summaryrefslogtreecommitdiffstats
path: root/contrib/slapd-modules/passwd
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:35:32 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:35:32 +0000
commit5ea77a75dd2d2158401331879f3c8f47940a732c (patch)
treed89dc06e9f4850a900f161e25f84e922c4f86cc8 /contrib/slapd-modules/passwd
parentInitial commit. (diff)
downloadopenldap-upstream/2.5.13+dfsg.tar.xz
openldap-upstream/2.5.13+dfsg.zip
Adding upstream version 2.5.13+dfsg.upstream/2.5.13+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'contrib/slapd-modules/passwd')
-rw-r--r--contrib/slapd-modules/passwd/Makefile70
-rw-r--r--contrib/slapd-modules/passwd/README69
-rw-r--r--contrib/slapd-modules/passwd/apr1-atol.pl29
-rw-r--r--contrib/slapd-modules/passwd/apr1-ltoa.pl31
-rw-r--r--contrib/slapd-modules/passwd/apr1.c236
-rw-r--r--contrib/slapd-modules/passwd/kerberos.c211
-rw-r--r--contrib/slapd-modules/passwd/netscape.c83
-rw-r--r--contrib/slapd-modules/passwd/pbkdf2/Makefile62
-rw-r--r--contrib/slapd-modules/passwd/pbkdf2/README99
-rw-r--r--contrib/slapd-modules/passwd/pbkdf2/pw-pbkdf2.c451
-rw-r--r--contrib/slapd-modules/passwd/pbkdf2/slapd-pw-pbkdf2.5112
-rw-r--r--contrib/slapd-modules/passwd/radius.c149
-rw-r--r--contrib/slapd-modules/passwd/sha2/Makefile59
-rw-r--r--contrib/slapd-modules/passwd/sha2/README144
-rw-r--r--contrib/slapd-modules/passwd/sha2/sha2.c1070
-rw-r--r--contrib/slapd-modules/passwd/sha2/sha2.h236
-rw-r--r--contrib/slapd-modules/passwd/sha2/slapd-pw-sha2.5118
-rw-r--r--contrib/slapd-modules/passwd/sha2/slapd-sha2.c508
-rw-r--r--contrib/slapd-modules/passwd/slapd-pw-radius.5110
-rw-r--r--contrib/slapd-modules/passwd/totp/Makefile58
-rw-r--r--contrib/slapd-modules/passwd/totp/README87
-rw-r--r--contrib/slapd-modules/passwd/totp/slapd-totp.c1000
-rw-r--r--contrib/slapd-modules/passwd/totp/slapo-totp.5109
23 files changed, 5101 insertions, 0 deletions
diff --git a/contrib/slapd-modules/passwd/Makefile b/contrib/slapd-modules/passwd/Makefile
new file mode 100644
index 0000000..6346496
--- /dev/null
+++ b/contrib/slapd-modules/passwd/Makefile
@@ -0,0 +1,70 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = pw-kerberos.la pw-netscape.la pw-radius.la pw-apr1.la
+MANPAGES = slapd-pw-radius.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+pw-kerberos.la: kerberos.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? -lkrb5
+
+pw-netscape.la: netscape.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $?
+
+pw-radius.la: radius.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? -lradius
+
+pw-apr1.la: apr1.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $?
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/passwd/README b/contrib/slapd-modules/passwd/README
new file mode 100644
index 0000000..069555f
--- /dev/null
+++ b/contrib/slapd-modules/passwd/README
@@ -0,0 +1,69 @@
+This directory contains native slapd plugins for password mechanisms that
+are not actively supported by the project. Currently this includes the
+Kerberos, Netscape MTA-MD5 and RADIUS password mechanisms. The Apache
+APR1 MD5 and BSD/Paul Henning Kamp MD5 mechanisms are also included.
+
+To use the Kerberos plugin, add:
+
+moduleload pw-kerberos.so
+
+to your slapd configuration file.
+
+To use the Netscape plugin, add:
+
+moduleload pw-netscape.so
+
+to your slapd configuration file.
+
+To use the APR1/BSD/MD5 plugin, add:
+
+moduleload pw-apr1.so
+
+to your slapd configuration file.
+
+To use the RADIUS plugin, add:
+
+moduleload pw-radius.so
+
+to your slapd configuration file; optionally, the path to a configuration
+file can be appended in the form
+
+moduleload pw-radius.so config="/etc/radius.conf"
+
+Use Makefile to compile this plugin or use a command line similar to:
+
+gcc -shared -I../../../include -Wall -g -DHAVE_KRB5 -o pw-kerberos.so kerberos.c
+
+Replace HAVE_KRB5 with HAVE_KRB4 if you want to use Kerberos IV.
+If your Kerberos header files are not in the C compiler's
+default path, you will need to add a "-I" directive for that as well.
+
+The corresponding command for the Netscape plugin would be:
+
+gcc -shared -I../../../include -Wall -g -o pw-netscape.so netscape.c
+
+The corresponding command for the RADIUS plugin would be:
+
+gcc -shared -I../../../include -Wall -g -o pw-radius.so radius.c -lradius
+
+(Actually, you might want to statically link the RADIUS client library
+libradius.a into the module).
+
+The corresponding command for the APR1 plugin would be:
+
+gcc -shared -I../../../include -Wall -g -o pw-apr1.so apr1.c
+
+---
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 2004-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/contrib/slapd-modules/passwd/apr1-atol.pl b/contrib/slapd-modules/passwd/apr1-atol.pl
new file mode 100644
index 0000000..d6eaee7
--- /dev/null
+++ b/contrib/slapd-modules/passwd/apr1-atol.pl
@@ -0,0 +1,29 @@
+#!/usr/bin/perl -w
+
+# Apache $apr1$ to OpenLDAP {APR1} hash converter
+# (C) 2011 Devin J. Pohly
+# You may use this code freely. It would be nice to be credited.
+
+use MIME::Base64;
+
+while (<>) {
+ ($user, $hash) = split(/:/, $_);
+ unless ($hash =~ /^\$apr1\$/) {
+ print STDERR "Not an Apache MD5 hash\n";
+ exit 1;
+ }
+
+ chomp $hash;
+ ($_,$_,$salt,$hash) = split(/\$/, $hash);
+
+ $hash =~ tr|./0-9A-Za-z|A-Za-z0-9+/|;
+ $hash .= "AA";
+ $hash =~ s/(.)(.)(.)(.)/$4$3$2$1/gs;
+ $hash = decode_base64($hash);
+ $hash =~ s/(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)..(.)/$1$4$7$10$13$15$2$5$8$11$14$16$3$6$9$12/s;
+ $hash .= $salt;
+ $hash = encode_base64($hash);
+ chop $hash;
+
+ print "$user:{APR1}$hash\n";
+} \ No newline at end of file
diff --git a/contrib/slapd-modules/passwd/apr1-ltoa.pl b/contrib/slapd-modules/passwd/apr1-ltoa.pl
new file mode 100644
index 0000000..ee628ec
--- /dev/null
+++ b/contrib/slapd-modules/passwd/apr1-ltoa.pl
@@ -0,0 +1,31 @@
+#!/usr/bin/perl -w
+
+# OpenLDAP {APR1} to Apache $apr1$ hash converter
+# (C) 2011 Devin J. Pohly
+# You may use this code freely. It would be nice to be credited.
+
+use MIME::Base64;
+
+while (<>) {
+ ($user, $hash) = split(/:/, $_);
+ unless ($hash =~ /^{APR1}/) {
+ print STDERR "Not an Apache MD5 hash\n";
+ next;
+ }
+
+ chomp $hash;
+ $hash = decode_base64(substr($hash, 6));
+ ($hash, $salt) = (substr($hash, 0, 16), substr($hash, 16));
+ $hash = $hash;
+ $hash =~ s/(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)/$1$7$13$2$8$14$3$9$15$4$10$16$5$11$6\0\0$12/s;
+ $hash = encode_base64($hash);
+ chomp $hash;
+ $hash =~ s/(.)(.)(.)(.)/$4$3$2$1/gs;
+ unless ($hash =~ /AA$/) {
+ #print "Problem with hash\n";
+ next;
+ }
+ $hash =~ s/AA$//;
+ $hash =~ tr|A-Za-z0-9+/|./0-9A-Za-z|;
+ print "$user:\$apr1\$$salt\$$hash\n"
+} \ No newline at end of file
diff --git a/contrib/slapd-modules/passwd/apr1.c b/contrib/slapd-modules/passwd/apr1.c
new file mode 100644
index 0000000..36880f3
--- /dev/null
+++ b/contrib/slapd-modules/passwd/apr1.c
@@ -0,0 +1,236 @@
+/* $OpenLDAP$ */
+/*
+ * This file is derived from OpenLDAP Software. All of the modifications to
+ * OpenLDAP Software represented in the following file were developed by
+ * Devin J. Pohly <djpohly@gmail.com>. I have not assigned rights and/or
+ * interest in this work to any party.
+ *
+ * The extensions to OpenLDAP Software herein are subject to the following
+ * notice:
+ *
+ * Copyright 2011 Devin J. Pohly
+ * Portions Copyright 2011 Howard Chu
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP Public
+ * License.
+ *
+ * A portion of this code is used in accordance with the Beer-ware License,
+ * revision 42, as noted.
+ *
+ */
+
+#include "portable.h"
+
+#include <lber.h>
+#include <lber_pvt.h>
+#include "lutil.h"
+#include "lutil_md5.h"
+#include <ac/string.h>
+
+#include <assert.h>
+
+/* the only difference between this and straight PHK is the magic */
+static LUTIL_PASSWD_CHK_FUNC chk_apr1;
+static LUTIL_PASSWD_HASH_FUNC hash_apr1;
+static const struct berval scheme_apr1 = BER_BVC("{APR1}");
+static const struct berval magic_apr1 = BER_BVC("$apr1$");
+
+static LUTIL_PASSWD_CHK_FUNC chk_bsdmd5;
+static LUTIL_PASSWD_HASH_FUNC hash_bsdmd5;
+static const struct berval scheme_bsdmd5 = BER_BVC("{BSDMD5}");
+static const struct berval magic_bsdmd5 = BER_BVC("$1$");
+
+static const unsigned char apr64[] =
+ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+#define APR_SALT_SIZE 8
+
+/* The algorithm implemented in this function was created by Poul-Henning
+ * Kamp and released under the following license:
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.ORG> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ */
+static void do_phk_hash(
+ const struct berval *passwd,
+ const struct berval *salt,
+ const struct berval *magic,
+ unsigned char *digest)
+{
+ lutil_MD5_CTX ctx, ctx1;
+ int n;
+
+ /* Start hashing */
+ lutil_MD5Init(&ctx);
+ lutil_MD5Update(&ctx, (const unsigned char *) passwd->bv_val, passwd->bv_len);
+ lutil_MD5Update(&ctx, (const unsigned char *) magic->bv_val, magic->bv_len);
+ lutil_MD5Update(&ctx, (const unsigned char *) salt->bv_val, salt->bv_len);
+ /* Inner hash */
+ lutil_MD5Init(&ctx1);
+ lutil_MD5Update(&ctx1, (const unsigned char *) passwd->bv_val, passwd->bv_len);
+ lutil_MD5Update(&ctx1, (const unsigned char *) salt->bv_val, salt->bv_len);
+ lutil_MD5Update(&ctx1, (const unsigned char *) passwd->bv_val, passwd->bv_len);
+ lutil_MD5Final(digest, &ctx1);
+ /* Nom start mixing things up */
+ for (n = passwd->bv_len; n > 0; n -= LUTIL_MD5_BYTES)
+ lutil_MD5Update(&ctx, digest,
+ (n > LUTIL_MD5_BYTES ? LUTIL_MD5_BYTES : n));
+ memset(digest, 0, LUTIL_MD5_BYTES);
+ /* Curiouser and curiouser... */
+ for (n = passwd->bv_len; n; n >>= 1)
+ if (n & 1)
+ lutil_MD5Update(&ctx, digest, 1);
+ else
+ lutil_MD5Update(&ctx, (const unsigned char *) passwd->bv_val, 1);
+ lutil_MD5Final(digest, &ctx);
+ /*
+ * Repeatedly hash things into the final value. This was originally
+ * intended to slow the algorithm down.
+ */
+ for (n = 0; n < 1000; n++) {
+ lutil_MD5Init(&ctx1);
+ if (n & 1)
+ lutil_MD5Update(&ctx1,
+ (const unsigned char *) passwd->bv_val, passwd->bv_len);
+ else
+ lutil_MD5Update(&ctx1, digest, LUTIL_MD5_BYTES);
+
+ if (n % 3)
+ lutil_MD5Update(&ctx1,
+ (const unsigned char *) salt->bv_val, salt->bv_len);
+ if (n % 7)
+ lutil_MD5Update(&ctx1,
+ (const unsigned char *) passwd->bv_val, passwd->bv_len);
+
+ if (n & 1)
+ lutil_MD5Update(&ctx1, digest, LUTIL_MD5_BYTES);
+ else
+ lutil_MD5Update(&ctx1,
+ (const unsigned char *) passwd->bv_val, passwd->bv_len);
+ lutil_MD5Final(digest, &ctx1);
+ }
+}
+
+static int chk_phk(
+ const struct berval *magic,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ unsigned char digest[LUTIL_MD5_BYTES];
+ unsigned char *orig_pass;
+ int rc;
+ struct berval salt;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check */
+ if (decode_len <= sizeof(digest))
+ return LUTIL_PASSWD_ERR;
+
+ /* base64 un-encode password hash */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if (orig_pass == NULL)
+ return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+
+ if (rc <= (int) sizeof(digest)) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ salt.bv_val = (char *) &orig_pass[sizeof(digest)];
+ salt.bv_len = rc - sizeof(digest);
+
+ do_phk_hash(cred, &salt, magic, digest);
+
+ if (text)
+ *text = NULL;
+
+ /* compare */
+ rc = memcmp((char *) orig_pass, (char *) digest, sizeof(digest));
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+static int chk_apr1(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ return chk_phk(&magic_apr1, passwd, cred, text);
+}
+
+static int chk_bsdmd5(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ return chk_phk(&magic_bsdmd5, passwd, cred, text);
+}
+
+static int hash_phk(
+ const struct berval *scheme,
+ const struct berval *magic,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+ unsigned char digest_buf[LUTIL_MD5_BYTES];
+ char salt_buf[APR_SALT_SIZE];
+ struct berval digest;
+ struct berval salt;
+ int n;
+
+ digest.bv_val = (char *) digest_buf;
+ digest.bv_len = sizeof(digest_buf);
+ salt.bv_val = salt_buf;
+ salt.bv_len = APR_SALT_SIZE;
+
+ /* generate random salt */
+ if (lutil_entropy( (unsigned char *) salt.bv_val, salt.bv_len) < 0)
+ return LUTIL_PASSWD_ERR;
+ /* limit it to characters in the 64-char set */
+ for (n = 0; n < salt.bv_len; n++)
+ salt.bv_val[n] = apr64[salt.bv_val[n] % (sizeof(apr64) - 1)];
+
+ do_phk_hash(passwd, &salt, magic, digest_buf);
+
+ if (text)
+ *text = NULL;
+
+ return lutil_passwd_string64(scheme, &digest, hash, &salt);
+}
+
+static int hash_apr1(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+ return hash_phk(scheme, &magic_apr1, passwd, hash, text);
+}
+
+static int hash_bsdmd5(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+ return hash_phk(scheme, &magic_bsdmd5, passwd, hash, text);
+}
+
+int init_module(int argc, char *argv[]) {
+ int rc;
+ rc = lutil_passwd_add((struct berval *) &scheme_apr1, chk_apr1, hash_apr1);
+ if ( !rc )
+ rc = lutil_passwd_add((struct berval *) &scheme_bsdmd5,
+ chk_bsdmd5, hash_bsdmd5);
+ return rc;
+}
diff --git a/contrib/slapd-modules/passwd/kerberos.c b/contrib/slapd-modules/passwd/kerberos.c
new file mode 100644
index 0000000..bebcbd0
--- /dev/null
+++ b/contrib/slapd-modules/passwd/kerberos.c
@@ -0,0 +1,211 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-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"
+
+#include <unistd.h>
+
+#include <lber.h>
+#include <lber_pvt.h> /* BER_BVC definition */
+#include "lutil.h"
+#include <ac/string.h>
+
+#ifdef HAVE_KRB5
+#include <krb5.h>
+#elif defined(HAVE_KRB4)
+#include <krb.h>
+#endif
+
+/* From <ldap_pvt.h> */
+LDAP_F( char *) ldap_pvt_get_fqdn LDAP_P(( char * ));
+
+static LUTIL_PASSWD_CHK_FUNC chk_kerberos;
+static const struct berval scheme = BER_BVC("{KERBEROS}");
+
+static int chk_kerberos(
+ const struct berval *sc,
+ const struct berval * passwd,
+ const struct berval * cred,
+ const char **text )
+{
+ unsigned int i;
+ int rtn;
+
+ for( i=0; i<cred->bv_len; i++) {
+ if(cred->bv_val[i] == '\0') {
+ return LUTIL_PASSWD_ERR; /* NUL character in password */
+ }
+ }
+
+ if( cred->bv_val[i] != '\0' ) {
+ return LUTIL_PASSWD_ERR; /* cred must behave like a string */
+ }
+
+ for( i=0; i<passwd->bv_len; i++) {
+ if(passwd->bv_val[i] == '\0') {
+ return LUTIL_PASSWD_ERR; /* NUL character in password */
+ }
+ }
+
+ if( passwd->bv_val[i] != '\0' ) {
+ return LUTIL_PASSWD_ERR; /* passwd must behave like a string */
+ }
+
+ rtn = LUTIL_PASSWD_ERR;
+
+#ifdef HAVE_KRB5 /* HAVE_HEIMDAL_KRB5 */
+ {
+/* Portions:
+ * Copyright (c) 1997, 1998, 1999 Kungliga Tekniska H\xf6gskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+ krb5_context context;
+ krb5_error_code ret;
+ krb5_creds creds;
+ krb5_get_init_creds_opt get_options;
+ krb5_verify_init_creds_opt verify_options;
+ krb5_principal client, server;
+#ifdef notdef
+ krb5_preauthtype pre_auth_types[] = {KRB5_PADATA_ENC_TIMESTAMP};
+#endif
+
+ ret = krb5_init_context( &context );
+ if (ret) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+#ifdef notdef
+ krb5_get_init_creds_opt_set_preauth_list(&get_options,
+ pre_auth_types, 1);
+#endif
+
+ krb5_get_init_creds_opt_init( &get_options );
+
+ krb5_verify_init_creds_opt_init( &verify_options );
+
+ ret = krb5_parse_name( context, passwd->bv_val, &client );
+
+ if (ret) {
+ krb5_free_context( context );
+ return LUTIL_PASSWD_ERR;
+ }
+
+ ret = krb5_get_init_creds_password( context,
+ &creds, client, cred->bv_val, NULL,
+ NULL, 0, NULL, &get_options );
+
+ if (ret) {
+ krb5_free_principal( context, client );
+ krb5_free_context( context );
+ return LUTIL_PASSWD_ERR;
+ }
+
+ {
+ char *host = ldap_pvt_get_fqdn( NULL );
+
+ if( host == NULL ) {
+ krb5_free_principal( context, client );
+ krb5_free_context( context );
+ return LUTIL_PASSWD_ERR;
+ }
+
+ ret = krb5_sname_to_principal( context,
+ host, "ldap", KRB5_NT_SRV_HST, &server );
+
+ ber_memfree( host );
+ }
+
+ if (ret) {
+ krb5_free_principal( context, client );
+ krb5_free_context( context );
+ return LUTIL_PASSWD_ERR;
+ }
+
+ ret = krb5_verify_init_creds( context,
+ &creds, server, NULL, NULL, &verify_options );
+
+ krb5_free_principal( context, client );
+ krb5_free_principal( context, server );
+ krb5_free_cred_contents( context, &creds );
+ krb5_free_context( context );
+
+ rtn = ret ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+ }
+#elif defined(HAVE_KRB4)
+ {
+ /* Borrowed from Heimdal kpopper */
+/* Portions:
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved. The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+ int status;
+ char lrealm[REALM_SZ];
+ char tkt[MAXHOSTNAMELEN];
+
+ status = krb_get_lrealm(lrealm,1);
+ if (status == KFAILURE) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ snprintf(tkt, sizeof(tkt), "%s_slapd.%u",
+ TKT_ROOT, (unsigned)getpid());
+ krb_set_tkt_string (tkt);
+
+ status = krb_verify_user( passwd->bv_val, "", lrealm,
+ cred->bv_val, 1, "ldap");
+
+ dest_tkt(); /* no point in keeping the tickets */
+
+ return status == KFAILURE ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+ }
+#endif
+
+ return rtn;
+}
+
+int init_module(int argc, char *argv[]) {
+ return lutil_passwd_add( (struct berval *)&scheme, chk_kerberos, NULL );
+}
diff --git a/contrib/slapd-modules/passwd/netscape.c b/contrib/slapd-modules/passwd/netscape.c
new file mode 100644
index 0000000..8e2de7b
--- /dev/null
+++ b/contrib/slapd-modules/passwd/netscape.c
@@ -0,0 +1,83 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-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"
+
+#include <unistd.h>
+
+#include <lber.h>
+#include <lber_pvt.h>
+#include "lutil.h"
+#include "lutil_md5.h"
+#include <ac/string.h>
+
+static LUTIL_PASSWD_CHK_FUNC chk_ns_mta_md5;
+static const struct berval scheme = BER_BVC("{NS-MTA-MD5}");
+
+#define NS_MTA_MD5_PASSLEN 64
+static int chk_ns_mta_md5(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text )
+{
+ lutil_MD5_CTX MD5context;
+ unsigned char MD5digest[LUTIL_MD5_BYTES], c;
+ char buffer[LUTIL_MD5_BYTES*2];
+ int i;
+
+ if( passwd->bv_len != NS_MTA_MD5_PASSLEN ) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ lutil_MD5Init(&MD5context);
+ lutil_MD5Update(&MD5context,
+ (const unsigned char *) &passwd->bv_val[32],
+ 32 );
+
+ c = 0x59;
+ lutil_MD5Update(&MD5context,
+ (const unsigned char *) &c,
+ 1 );
+
+ lutil_MD5Update(&MD5context,
+ (const unsigned char *) cred->bv_val,
+ cred->bv_len );
+
+ c = 0xF7;
+ lutil_MD5Update(&MD5context,
+ (const unsigned char *) &c,
+ 1 );
+
+ lutil_MD5Update(&MD5context,
+ (const unsigned char *) &passwd->bv_val[32],
+ 32 );
+
+ lutil_MD5Final(MD5digest, &MD5context);
+
+ for( i=0; i < sizeof( MD5digest ); i++ ) {
+ buffer[i+i] = "0123456789abcdef"[(MD5digest[i]>>4) & 0x0F];
+ buffer[i+i+1] = "0123456789abcdef"[ MD5digest[i] & 0x0F];
+ }
+
+ /* compare */
+ return memcmp((char *)passwd->bv_val,
+ (char *)buffer, sizeof(buffer)) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+int init_module(int argc, char *argv[]) {
+ return lutil_passwd_add( (struct berval *)&scheme, chk_ns_mta_md5, NULL );
+}
diff --git a/contrib/slapd-modules/passwd/pbkdf2/Makefile b/contrib/slapd-modules/passwd/pbkdf2/Makefile
new file mode 100644
index 0000000..6279f50
--- /dev/null
+++ b/contrib/slapd-modules/passwd/pbkdf2/Makefile
@@ -0,0 +1,62 @@
+# $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/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+#DEFS = -DSLAPD_PBKDF2_DEBUG
+
+SSL_INC =
+SSL_LIB = -lcrypto
+
+INCS = $(LDAP_INC) $(SSL_INC)
+LIBS = $(LDAP_LIB) $(SSL_LIB)
+
+PROGRAMS = pw-pbkdf2.la
+MANPAGES = slapd-pw-pbkdf2.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+pw-pbkdf2.la: pw-pbkdf2.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/passwd/pbkdf2/README b/contrib/slapd-modules/passwd/pbkdf2/README
new file mode 100644
index 0000000..d4d99d2
--- /dev/null
+++ b/contrib/slapd-modules/passwd/pbkdf2/README
@@ -0,0 +1,99 @@
+PBKDF2 for OpenLDAP
+=======================
+
+pw-pbkdf2.c provides PBKDF2 key derivation functions in OpenLDAP.
+
+Schemes:
+
+ * {PBKDF2} - alias to {PBKDF2-SHA1}
+ * {PBKDF2-SHA1}
+ * {PBKDF2-SHA256}
+ * {PBKDF2-SHA512}
+
+# Requirements
+
+ * OpenSSL 1.0.0 or later
+
+# Installations
+
+First, You need to configure and build OpenLDAP.
+
+ $ cd <OPENLDAP_BUILD_DIR>/contrib/slapd-modules/passwd/
+ $ git clone https://github.com/hamano/openldap-pbkdf2.git
+ $ cd openldap-pbkdf2/
+ $ make
+ # make install
+
+# Configuration
+
+In slapd.conf:
+
+ moduleload pw-pbkdf2.so
+
+You can also tell OpenLDAP to use the schemes when processing LDAP
+Password Modify Extended Operations, thanks to the password-hash
+option in slapd.conf. For example:
+
+ password-hash {PBKDF2}
+or
+ password-hash {PBKDF2-SHA256}
+or
+ password-hash {PBKDF2-SHA512}
+
+# Testing
+
+You can get hash to use slappasswd.
+
+ $ slappasswd -o module-load=pw-pbkdf2.la -h {PBKDF2} -s secret
+ {PBKDF2}60000$Y6ZHtTTbeUgpIbIW0QDmDA$j/aU7jFKUSbH4UobNQDm9OEIwuw
+
+A quick way to test whether it's working is to customize the rootdn and
+rootpw in slapd.conf, eg:
+
+ rootdn "cn=Manager,dc=example,dc=com"
+ rootpw {PBKDF2}60000$Y6ZHtTTbeUgpIbIW0QDmDA$j/aU7jFKUSbH4UobNQDm9OEIwuw
+
+Then to test, run something like:
+
+ $ ldapsearch -x -b "dc=example,dc=com" -D "cn=Manager,dc=example,dc=com" -w secret
+
+# Debugging
+You can specify -DSLAPD_PBKDF2_DEBUG flag for debugging.
+
+# Message Format
+
+ {PBKDF2}<Iteration>$<Adapted Base64 Salt>$<Adapted Base64 DK>
+
+# References
+
+* [RFC 2898 Password-Based Cryptography][^1]
+[^1]: http://tools.ietf.org/html/rfc2898
+
+* [PKCS #5 PBKDF2 Test Vectors][^2]
+[^2]: http://tools.ietf.org/html/draft-josefsson-pbkdf2-test-vectors-06
+
+* [RFC 2307 Using LDAP as a Network Information Service][^3]
+[^3]: http://tools.ietf.org/html/rfc2307
+
+* [Python Passlib][^4]
+[^4]: http://pythonhosted.org/passlib/
+
+* [Adapted Base64 Encoding][^5]
+[^5]: http://pythonhosted.org/passlib/lib/passlib.utils.html#passlib.utils.ab64_encode
+
+# License
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 2009-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>.
+
+# ACKNOWLEDGEMENT
+This work was initially developed by HAMANO Tsukasa <hamano@osstech.co.jp>
diff --git a/contrib/slapd-modules/passwd/pbkdf2/pw-pbkdf2.c b/contrib/slapd-modules/passwd/pbkdf2/pw-pbkdf2.c
new file mode 100644
index 0000000..1cc2770
--- /dev/null
+++ b/contrib/slapd-modules/passwd/pbkdf2/pw-pbkdf2.c
@@ -0,0 +1,451 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2009-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>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ */
+
+#define _GNU_SOURCE
+
+#include "portable.h"
+#include <ac/string.h>
+#include "lber_pvt.h"
+#include "lutil.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef HAVE_OPENSSL
+#include <openssl/evp.h>
+#elif HAVE_GNUTLS
+#include <nettle/pbkdf2.h>
+#include <nettle/hmac.h>
+typedef void (*pbkdf2_hmac_update)(void *, unsigned, const uint8_t *);
+typedef void (*pbkdf2_hmac_digest)(void *, unsigned, uint8_t *);
+#else
+#error Unsupported crypto backend.
+#endif
+
+#define PBKDF2_ITERATION 10000
+#define PBKDF2_SALT_SIZE 16
+#define PBKDF2_SHA1_DK_SIZE 20
+#define PBKDF2_SHA256_DK_SIZE 32
+#define PBKDF2_SHA512_DK_SIZE 64
+#define PBKDF2_MAX_DK_SIZE 64
+
+const struct berval pbkdf2_scheme = BER_BVC("{PBKDF2}");
+const struct berval pbkdf2_sha1_scheme = BER_BVC("{PBKDF2-SHA1}");
+const struct berval pbkdf2_sha256_scheme = BER_BVC("{PBKDF2-SHA256}");
+const struct berval pbkdf2_sha512_scheme = BER_BVC("{PBKDF2-SHA512}");
+
+/*
+ * Converting base64 string to adapted base64 string.
+ * Adapted base64 encode is identical to general base64 encode except
+ * that it uses '.' instead of '+', and omits trailing padding '=' and
+ * whitespace.
+ * see http://pythonhosted.org/passlib/lib/passlib.utils.html
+ * This is destructive function.
+ */
+static int b64_to_ab64(char *str)
+{
+ char *p = str;
+ do {
+ if(*p == '+'){
+ *p = '.';
+ }
+ if(*p == '='){
+ *p = '\0';
+ }
+ } while(*p++);
+ return 0;
+}
+
+/*
+ * Converting adapted base64 string to base64 string.
+ * dstsize will require src length + 2, due to output string have
+ * potential to append "=" or "==".
+ * return -1 if few output buffer.
+ */
+static int ab64_to_b64(char *src, char *dst, size_t dstsize){
+ int i;
+ char *p = src;
+ for(i=0; p[i] && p[i] != '$'; i++){
+ if(i >= dstsize){
+ dst[0] = '\0';
+ return -1;
+ }
+ if(p[i] == '.'){
+ dst[i] = '+';
+ }else{
+ dst[i] = p[i];
+ }
+ }
+ for(;i%4;i++){
+ if(i >= dstsize){
+ dst[0] = '\0';
+ return -1;
+ }
+ dst[i] = '=';
+ }
+ dst[i] = '\0';
+ return 0;
+}
+
+static int pbkdf2_format(
+ const struct berval *sc,
+ int iteration,
+ const struct berval *salt,
+ const struct berval *dk,
+ struct berval *msg)
+{
+
+ int rc, msg_len;
+ char salt_b64[LUTIL_BASE64_ENCODE_LEN(PBKDF2_SALT_SIZE) + 1];
+ char dk_b64[LUTIL_BASE64_ENCODE_LEN(PBKDF2_MAX_DK_SIZE) + 1];
+
+ rc = lutil_b64_ntop((unsigned char *)salt->bv_val, salt->bv_len,
+ salt_b64, sizeof(salt_b64));
+ if(rc < 0){
+ return LUTIL_PASSWD_ERR;
+ }
+ b64_to_ab64(salt_b64);
+ rc = lutil_b64_ntop((unsigned char *)dk->bv_val, dk->bv_len,
+ dk_b64, sizeof(dk_b64));
+ if(rc < 0){
+ return LUTIL_PASSWD_ERR;
+ }
+ b64_to_ab64(dk_b64);
+ msg_len = asprintf(&msg->bv_val, "%s%d$%s$%s",
+ sc->bv_val, iteration,
+ salt_b64, dk_b64);
+ if(msg_len < 0){
+ msg->bv_len = 0;
+ return LUTIL_PASSWD_ERR;
+ }
+
+ msg->bv_len = msg_len;
+ return LUTIL_PASSWD_OK;
+}
+
+static int pbkdf2_encrypt(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *msg,
+ const char **text)
+{
+ unsigned char salt_value[PBKDF2_SALT_SIZE];
+ struct berval salt;
+ unsigned char dk_value[PBKDF2_MAX_DK_SIZE];
+ struct berval dk;
+ int iteration = PBKDF2_ITERATION;
+ int rc;
+#ifdef HAVE_OPENSSL
+ const EVP_MD *md;
+#elif HAVE_GNUTLS
+ struct hmac_sha1_ctx sha1_ctx;
+ struct hmac_sha256_ctx sha256_ctx;
+ struct hmac_sha512_ctx sha512_ctx;
+ void * current_ctx = NULL;
+ pbkdf2_hmac_update current_hmac_update = NULL;
+ pbkdf2_hmac_digest current_hmac_digest = NULL;
+#endif
+
+ salt.bv_val = (char *)salt_value;
+ salt.bv_len = sizeof(salt_value);
+ dk.bv_val = (char *)dk_value;
+
+#ifdef HAVE_OPENSSL
+ if(!ber_bvcmp(scheme, &pbkdf2_scheme)){
+ dk.bv_len = PBKDF2_SHA1_DK_SIZE;
+ md = EVP_sha1();
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha1_scheme)){
+ dk.bv_len = PBKDF2_SHA1_DK_SIZE;
+ md = EVP_sha1();
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha256_scheme)){
+ dk.bv_len = PBKDF2_SHA256_DK_SIZE;
+ md = EVP_sha256();
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha512_scheme)){
+ dk.bv_len = PBKDF2_SHA512_DK_SIZE;
+ md = EVP_sha512();
+ }else{
+ return LUTIL_PASSWD_ERR;
+ }
+#elif HAVE_GNUTLS
+ if(!ber_bvcmp(scheme, &pbkdf2_scheme)){
+ dk.bv_len = PBKDF2_SHA1_DK_SIZE;
+ current_ctx = &sha1_ctx;
+ current_hmac_update = (pbkdf2_hmac_update) &hmac_sha1_update;
+ current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha1_digest;
+ hmac_sha1_set_key(current_ctx, passwd->bv_len, (const uint8_t *) passwd->bv_val);
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha1_scheme)){
+ dk.bv_len = PBKDF2_SHA1_DK_SIZE;
+ current_ctx = &sha1_ctx;
+ current_hmac_update = (pbkdf2_hmac_update) &hmac_sha1_update;
+ current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha1_digest;
+ hmac_sha1_set_key(current_ctx, passwd->bv_len, (const uint8_t *) passwd->bv_val);
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha256_scheme)){
+ dk.bv_len = PBKDF2_SHA256_DK_SIZE;
+ current_ctx = &sha256_ctx;
+ current_hmac_update = (pbkdf2_hmac_update) &hmac_sha256_update;
+ current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha256_digest;
+ hmac_sha256_set_key(current_ctx, passwd->bv_len, (const uint8_t *) passwd->bv_val);
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha512_scheme)){
+ dk.bv_len = PBKDF2_SHA512_DK_SIZE;
+ current_ctx = &sha512_ctx;
+ current_hmac_update = (pbkdf2_hmac_update) &hmac_sha512_update;
+ current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha512_digest;
+ hmac_sha512_set_key(current_ctx, passwd->bv_len, (const uint8_t *) passwd->bv_val);
+ }else{
+ return LUTIL_PASSWD_ERR;
+ }
+#endif
+
+ if(lutil_entropy((unsigned char *)salt.bv_val, salt.bv_len) < 0){
+ return LUTIL_PASSWD_ERR;
+ }
+
+#ifdef HAVE_OPENSSL
+ if(!PKCS5_PBKDF2_HMAC(passwd->bv_val, passwd->bv_len,
+ (unsigned char *)salt.bv_val, salt.bv_len,
+ iteration, md, dk.bv_len, dk_value)){
+ return LUTIL_PASSWD_ERR;
+ }
+#elif HAVE_GNUTLS
+ PBKDF2(current_ctx, current_hmac_update, current_hmac_digest,
+ dk.bv_len, iteration,
+ salt.bv_len, (const uint8_t *) salt.bv_val,
+ dk.bv_len, dk_value);
+#endif
+
+#ifdef SLAPD_PBKDF2_DEBUG
+ printf("Encrypt for %s\n", scheme->bv_val);
+ printf(" Password:\t%s\n", passwd->bv_val);
+
+ printf(" Salt:\t\t");
+ int i;
+ for(i=0; i<salt.bv_len; i++){
+ printf("%02x", salt_value[i]);
+ }
+ printf("\n");
+ printf(" Iteration:\t%d\n", iteration);
+
+ printf(" DK:\t\t");
+ for(i=0; i<dk.bv_len; i++){
+ printf("%02x", dk_value[i]);
+ }
+ printf("\n");
+#endif
+
+ rc = pbkdf2_format(scheme, iteration, &salt, &dk, msg);
+
+#ifdef SLAPD_PBKDF2_DEBUG
+ printf(" Output:\t%s\n", msg->bv_val);
+#endif
+
+ return rc;
+}
+
+static int pbkdf2_check(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ int rc;
+ int iteration;
+
+ /* salt_value require PBKDF2_SALT_SIZE + 1 in lutil_b64_pton. */
+ unsigned char salt_value[PBKDF2_SALT_SIZE + 1];
+ char salt_b64[LUTIL_BASE64_ENCODE_LEN(PBKDF2_SALT_SIZE) + 1];
+ /* dk_value require PBKDF2_MAX_DK_SIZE + 1 in lutil_b64_pton. */
+ unsigned char dk_value[PBKDF2_MAX_DK_SIZE + 1];
+ char dk_b64[LUTIL_BASE64_ENCODE_LEN(PBKDF2_MAX_DK_SIZE) + 1];
+ unsigned char input_dk_value[PBKDF2_MAX_DK_SIZE];
+ size_t dk_len;
+#ifdef HAVE_OPENSSL
+ const EVP_MD *md;
+#elif HAVE_GNUTLS
+ struct hmac_sha1_ctx sha1_ctx;
+ struct hmac_sha256_ctx sha256_ctx;
+ struct hmac_sha512_ctx sha512_ctx;
+ void * current_ctx = NULL;
+ pbkdf2_hmac_update current_hmac_update = NULL;
+ pbkdf2_hmac_digest current_hmac_digest = NULL;
+#endif
+
+#ifdef SLAPD_PBKDF2_DEBUG
+ printf("Checking for %s\n", scheme->bv_val);
+ printf(" Stored Value:\t%s\n", passwd->bv_val);
+ printf(" Input Cred:\t%s\n", cred->bv_val);
+#endif
+
+#ifdef HAVE_OPENSSL
+ if(!ber_bvcmp(scheme, &pbkdf2_scheme)){
+ dk_len = PBKDF2_SHA1_DK_SIZE;
+ md = EVP_sha1();
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha1_scheme)){
+ dk_len = PBKDF2_SHA1_DK_SIZE;
+ md = EVP_sha1();
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha256_scheme)){
+ dk_len = PBKDF2_SHA256_DK_SIZE;
+ md = EVP_sha256();
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha512_scheme)){
+ dk_len = PBKDF2_SHA512_DK_SIZE;
+ md = EVP_sha512();
+ }else{
+ return LUTIL_PASSWD_ERR;
+ }
+#elif HAVE_GNUTLS
+ if(!ber_bvcmp(scheme, &pbkdf2_scheme)){
+ dk_len = PBKDF2_SHA1_DK_SIZE;
+ current_ctx = &sha1_ctx;
+ current_hmac_update = (pbkdf2_hmac_update) &hmac_sha1_update;
+ current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha1_digest;
+ hmac_sha1_set_key(current_ctx, cred->bv_len, (const uint8_t *) cred->bv_val);
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha1_scheme)){
+ dk_len = PBKDF2_SHA1_DK_SIZE;
+ current_ctx = &sha1_ctx;
+ current_hmac_update = (pbkdf2_hmac_update) &hmac_sha1_update;
+ current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha1_digest;
+ hmac_sha1_set_key(current_ctx, cred->bv_len, (const uint8_t *) cred->bv_val);
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha256_scheme)){
+ dk_len = PBKDF2_SHA256_DK_SIZE;
+ current_ctx = &sha256_ctx;
+ current_hmac_update = (pbkdf2_hmac_update) &hmac_sha256_update;
+ current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha256_digest;
+ hmac_sha256_set_key(current_ctx, cred->bv_len, (const uint8_t *) cred->bv_val);
+ }else if(!ber_bvcmp(scheme, &pbkdf2_sha512_scheme)){
+ dk_len = PBKDF2_SHA512_DK_SIZE;
+ current_ctx = &sha512_ctx;
+ current_hmac_update = (pbkdf2_hmac_update) &hmac_sha512_update;
+ current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha512_digest;
+ hmac_sha512_set_key(current_ctx, cred->bv_len, (const uint8_t *) cred->bv_val);
+ }else{
+ return LUTIL_PASSWD_ERR;
+ }
+#endif
+
+ iteration = atoi(passwd->bv_val);
+ if(iteration < 1){
+ return LUTIL_PASSWD_ERR;
+ }
+
+ char *ptr;
+ ptr = strchr(passwd->bv_val, '$');
+ if(!ptr){
+ return LUTIL_PASSWD_ERR;
+ }
+ ptr++; /* skip '$' */
+ rc = ab64_to_b64(ptr, salt_b64, sizeof(salt_b64));
+ if(rc < 0){
+ return LUTIL_PASSWD_ERR;
+ }
+
+ ptr = strchr(ptr, '$');
+ if(!ptr){
+ return LUTIL_PASSWD_ERR;
+ }
+ ptr++; /* skip '$' */
+ rc = ab64_to_b64(ptr, dk_b64, sizeof(dk_b64));
+ if(rc < 0){
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* The targetsize require PBKDF2_SALT_SIZE + 1 in lutil_b64_pton. */
+ rc = lutil_b64_pton(salt_b64, salt_value, PBKDF2_SALT_SIZE + 1);
+ if(rc < 0){
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* consistency check */
+ if(rc != PBKDF2_SALT_SIZE){
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* The targetsize require PBKDF2_MAX_DK_SIZE + 1 in lutil_b64_pton. */
+ rc = lutil_b64_pton(dk_b64, dk_value, sizeof(dk_value));
+ if(rc < 0){
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* consistency check */
+ if(rc != dk_len){
+ return LUTIL_PASSWD_ERR;
+ }
+
+#ifdef HAVE_OPENSSL
+ if(!PKCS5_PBKDF2_HMAC(cred->bv_val, cred->bv_len,
+ salt_value, PBKDF2_SALT_SIZE,
+ iteration, md, dk_len, input_dk_value)){
+ return LUTIL_PASSWD_ERR;
+ }
+#elif HAVE_GNUTLS
+ PBKDF2(current_ctx, current_hmac_update, current_hmac_digest,
+ dk_len, iteration,
+ PBKDF2_SALT_SIZE, salt_value,
+ dk_len, input_dk_value);
+#endif
+
+ rc = memcmp(dk_value, input_dk_value, dk_len);
+#ifdef SLAPD_PBKDF2_DEBUG
+ printf(" Iteration:\t%d\n", iteration);
+ printf(" Base64 Salt:\t%s\n", salt_b64);
+ printf(" Base64 DK:\t%s\n", dk_b64);
+ int i;
+ printf(" Stored Salt:\t");
+ for(i=0; i<PBKDF2_SALT_SIZE; i++){
+ printf("%02x", salt_value[i]);
+ }
+ printf("\n");
+
+ printf(" Stored DK:\t");
+ for(i=0; i<dk_len; i++){
+ printf("%02x", dk_value[i]);
+ }
+ printf("\n");
+
+ printf(" Input DK:\t");
+ for(i=0; i<dk_len; i++){
+ printf("%02x", input_dk_value[i]);
+ }
+ printf("\n");
+ printf(" Result:\t%d\n", rc);
+#endif
+ return rc?LUTIL_PASSWD_ERR:LUTIL_PASSWD_OK;
+}
+
+int init_module(int argc, char *argv[]) {
+ int rc;
+ rc = lutil_passwd_add((struct berval *)&pbkdf2_scheme,
+ pbkdf2_check, pbkdf2_encrypt);
+ if(rc) return rc;
+ rc = lutil_passwd_add((struct berval *)&pbkdf2_sha1_scheme,
+ pbkdf2_check, pbkdf2_encrypt);
+ if(rc) return rc;
+
+ rc = lutil_passwd_add((struct berval *)&pbkdf2_sha256_scheme,
+ pbkdf2_check, pbkdf2_encrypt);
+ if(rc) return rc;
+
+ rc = lutil_passwd_add((struct berval *)&pbkdf2_sha512_scheme,
+ pbkdf2_check, pbkdf2_encrypt);
+ return rc;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/contrib/slapd-modules/passwd/pbkdf2/slapd-pw-pbkdf2.5 b/contrib/slapd-modules/passwd/pbkdf2/slapd-pw-pbkdf2.5
new file mode 100644
index 0000000..3bacf62
--- /dev/null
+++ b/contrib/slapd-modules/passwd/pbkdf2/slapd-pw-pbkdf2.5
@@ -0,0 +1,112 @@
+.TH SLAPD-PW-PBKDF2 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2015-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd-pw-pbkdf2 \- PBKDF2 password module to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.RS
+.LP
+.B moduleload
+.B pw-pbkdf2
+.RE
+.SH DESCRIPTION
+.LP
+The
+.B pw-pbkdf2
+module to
+.BR slapd (8)
+provides support for the use of the key stretching function
+PBKDF2 (Password-Based Key Derivation Function 2) following RFC 2898
+in hashed passwords in OpenLDAP.
+.LP
+It does so by providing the following additional password schemes for use in slapd:
+.RS
+.TP
+.B {PBKDF2}
+alias to {PBKDF2-SHA1}
+.TP
+.B {PBKDF2-SHA1}
+PBKDF2 using HMAC-SHA-1 as the underlying pseudorandom function
+.TP
+.B {PBKDF2-SHA256}
+PBKDF2 using HMAC-SHA-256 as the underlying pseudorandom function
+.TP
+.B {PBKDF2-SHA512}
+PBKDF2 using HMAC-SHA-512 as the underlying pseudorandom function
+.RE
+
+.SH CONFIGURATION
+The
+.B pw-pbkdf2
+module does not need any configuration.
+.LP
+After loading the module, the password schemes
+{PBKDF2}, {PBKDF2-SHA1}, {PBKDF2-SHA256}, and {PBKDF2-SHA512}
+will be recognised in values of the
+.I userPassword
+attribute.
+.LP
+You can then instruct OpenLDAP to use these schemes when processing
+the LDAPv3 Password Modify (RFC 3062) extended operations by using the
+.BR password-hash
+option in
+.BR slapd.conf (5).
+
+.SH NOTES
+If you want to use the schemes described here with
+.BR slappasswd (8),
+remember to load the module using its command line options.
+The relevant option/value is:
+.RS
+.LP
+.B \-o
+.BR module\-load = pw-pbkdf2
+.LP
+.RE
+Depending on
+.BR pw-pbkdf2 's
+location, you may also need:
+.RS
+.LP
+.B \-o
+.BR module\-path = \fIpathspec\fP
+.RE
+
+.SH EXAMPLES
+All of the userPassword LDAP attributes below encode the password
+.RI ' secret '.
+.EX
+.LP
+userPassword: {PBKDF2-SHA512}10000$/oQ4xZi382mk7kvCd3ZdkA$2wqjpuyV2l0U/a1QwoQPOtlQL.UcJGNACj1O24balruqQb/NgPW6OCvvrrJP8.SzA3/5iYvLnwWPzeX8IK/bEQ
+.LP
+userPassword: {PBKDF2-SHA256}10000$jq40ImWtmpTE.aYDYV1GfQ$mpiL4ui02ACmYOAnCjp/MI1gQk50xLbZ54RZneU0fCg
+.LP
+userPassword: {PBKDF2-SHA1}10000$QJTEclnXgh9Cz3ChCWpdAg$9.s98jwFJM.NXJK9ca/oJ5AyoAQ
+.EE
+.LP
+To make {PBKDF2-SHA512} the password hash used in Password Modify extended operations,
+simply set this line in slapd.conf(5):
+.EX
+.LP
+password-hash {PBKDF2-SHA512}
+.EX
+
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR ldappasswd (1),
+.BR slappasswd (8),
+.BR ldap (3),
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.LP
+
+.SH ACKNOWLEDGEMENTS
+This manual page has been written by Peter Marschall based on the
+module's README file written by HAMANO Tsukasa <hamano@osstech.co.jp>
+.LP
+.B OpenLDAP
+is developed and maintained by The OpenLDAP Project (http://www.openldap.org/).
+.B OpenLDAP
+is derived from University of Michigan LDAP 3.3 Release.
diff --git a/contrib/slapd-modules/passwd/radius.c b/contrib/slapd-modules/passwd/radius.c
new file mode 100644
index 0000000..8474bf5
--- /dev/null
+++ b/contrib/slapd-modules/passwd/radius.c
@@ -0,0 +1,149 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1998-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"
+
+#include <stdio.h>
+
+#include <lber.h>
+#include <lber_pvt.h> /* BER_BVC definition */
+#include "lutil.h"
+#include <ldap_pvt_thread.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include <radlib.h>
+
+extern char *global_host; /* from slapd */
+static LUTIL_PASSWD_CHK_FUNC chk_radius;
+static const struct berval scheme = BER_BVC("{RADIUS}");
+static char *config_filename;
+static ldap_pvt_thread_mutex_t libradius_mutex;
+
+static int
+chk_radius(
+ const struct berval *sc,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text )
+{
+ unsigned int i;
+ int rc = LUTIL_PASSWD_ERR;
+
+ struct rad_handle *h = NULL;
+
+ for ( i = 0; i < cred->bv_len; i++ ) {
+ if ( cred->bv_val[ i ] == '\0' ) {
+ return LUTIL_PASSWD_ERR; /* NUL character in cred */
+ }
+ }
+
+ if ( cred->bv_val[ i ] != '\0' ) {
+ return LUTIL_PASSWD_ERR; /* cred must behave like a string */
+ }
+
+ for ( i = 0; i < passwd->bv_len; i++ ) {
+ if ( passwd->bv_val[ i ] == '\0' ) {
+ return LUTIL_PASSWD_ERR; /* NUL character in password */
+ }
+ }
+
+ if ( passwd->bv_val[ i ] != '\0' ) {
+ return LUTIL_PASSWD_ERR; /* passwd must behave like a string */
+ }
+
+ ldap_pvt_thread_mutex_lock( &libradius_mutex );
+
+ h = rad_auth_open();
+ if ( h == NULL ) {
+ ldap_pvt_thread_mutex_unlock( &libradius_mutex );
+ return LUTIL_PASSWD_ERR;
+ }
+
+ if ( rad_config( h, config_filename ) != 0 ) {
+ goto done;
+ }
+
+ if ( rad_create_request( h, RAD_ACCESS_REQUEST ) ) {
+ goto done;
+ }
+
+ if ( rad_put_string( h, RAD_USER_NAME, passwd->bv_val ) != 0 ) {
+ goto done;
+ }
+
+ if ( rad_put_string( h, RAD_USER_PASSWORD, cred->bv_val ) != 0 ) {
+ goto done;
+ }
+
+ if ( rad_put_string( h, RAD_NAS_IDENTIFIER, global_host ) != 0 ) {
+ goto done;
+ }
+
+ switch ( rad_send_request( h ) ) {
+ case RAD_ACCESS_ACCEPT:
+ rc = LUTIL_PASSWD_OK;
+ break;
+
+ case RAD_ACCESS_REJECT:
+ rc = LUTIL_PASSWD_ERR;
+ break;
+
+ case RAD_ACCESS_CHALLENGE:
+ rc = LUTIL_PASSWD_ERR;
+ break;
+
+ case -1:
+ /* no valid response is received */
+ break;
+ }
+
+done:;
+ rad_close( h );
+
+ ldap_pvt_thread_mutex_unlock( &libradius_mutex );
+ return rc;
+}
+
+int
+term_module()
+{
+ return ldap_pvt_thread_mutex_destroy( &libradius_mutex );
+}
+
+int
+init_module( int argc, char *argv[] )
+{
+ int i;
+
+ for ( i = 0; i < argc; i++ ) {
+ if ( strncasecmp( argv[ i ], "config=", STRLENOF( "config=" ) ) == 0 ) {
+ /* FIXME: what if multiple loads of same module?
+ * does it make sense (e.g. override an existing one)? */
+ if ( config_filename == NULL ) {
+ config_filename = ber_strdup( &argv[ i ][ STRLENOF( "config=" ) ] );
+ }
+
+ } else {
+ fprintf( stderr, "init_module(radius): unknown arg#%d=\"%s\".\n",
+ i, argv[ i ] );
+ return 1;
+ }
+ }
+
+ ldap_pvt_thread_mutex_init( &libradius_mutex );
+
+ return lutil_passwd_add( (struct berval *)&scheme, chk_radius, NULL );
+}
diff --git a/contrib/slapd-modules/passwd/sha2/Makefile b/contrib/slapd-modules/passwd/sha2/Makefile
new file mode 100644
index 0000000..2d20756
--- /dev/null
+++ b/contrib/slapd-modules/passwd/sha2/Makefile
@@ -0,0 +1,59 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS =
+#DEFS = -DSLAPD_SHA2_DEBUG
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = pw-sha2.la
+MANPAGES = slapd-pw-sha2.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+pw-sha2.la: slapd-sha2.lo sha2.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/passwd/sha2/README b/contrib/slapd-modules/passwd/sha2/README
new file mode 100644
index 0000000..bab1dcd
--- /dev/null
+++ b/contrib/slapd-modules/passwd/sha2/README
@@ -0,0 +1,144 @@
+SHA-2 OpenLDAP support
+----------------------
+
+slapd-sha2.c provides support for SSHA-512, SSHA-384, SSHA-256,
+SHA-512, SHA-384 and SHA-256 hashed passwords in OpenLDAP. For
+instance, one could have the LDAP attribute:
+
+userPassword: {SHA512}vSsar3708Jvp9Szi2NWZZ02Bqp1qRCFpbcTZPdBhnWgs5WtNZKnvCXdhztmeD2cmW192CF5bDufKRpayrW/isg==
+
+or:
+
+userPassword: {SHA384}WKd1ukESvjAFrkQHznV9iP2nHUBJe7gCbsrFTU4//HIyzo3jq1rLMK45dg/ufFPt
+
+or:
+
+userPassword: {SHA256}K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=
+
+all of which encode the password 'secret'.
+
+
+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_SHA2_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-sha2.so
+
+3) Copy slapd-sha2.so somewhere permanent.
+
+4) Edit your slapd.conf (eg. /etc/ldap/slapd.conf), and add:
+
+moduleload ...path/to/slapd-sha2.so
+
+5) Restart slapd.
+
+
+Configuring
+-----------
+
+The {SSHA256}, {SSHA384}, {SSHA512}, {SSHA256}, {SHA384} and {SHA512}
+password schemes should now be recognised.
+
+You can also tell OpenLDAP to use one of these new schemes when processing LDAP
+Password Modify Extended Operations, thanks to the password-hash option in
+slapd.conf. For example:
+
+password-hash {SSHA512}
+
+
+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 encrypts the string 'secret'
+
+rootpw {SHA256}K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=
+
+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 openssl:
+
+$ echo -n "secret" | openssl dgst -sha256 -binary | openssl enc -base64
+K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=
+$ echo -n "secret" | openssl dgst -sha384 -binary | openssl enc -base64
+WKd1ukESvjAFrkQHznV9iP2nHUBJe7gCbsrFTU4//HIyzo3jq1rLMK45dg/ufFPt
+$ echo -n "secret" | openssl dgst -sha512 -binary | openssl enc -base64
+vSsar3708Jvp9Szi2NWZZ02Bqp1qRCFpbcTZPdBhnWgs5WtNZKnvCXdhztmeD2cm
+W192CF5bDufKRpayrW/isg==
+
+(join those lines up to form the full hash)
+
+
+
+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
+
+
+Debugging (SHA-512, SHA-384 and SHA-256 only)
+---------------------------------------------
+
+To see what's going on, recompile with SLAPD_SHA2_DEBUG (use the
+commented-out DEFS in Makefile), and then run slapd from the console
+to see stderr:
+
+$ sudo /etc/init.d/slapd stop
+Stopping OpenLDAP: slapd.
+$ sudo /usr/sbin/slapd -f /etc/ldap/slapd.conf -h ldap://localhost:389 -d stats
+@(#) $OpenLDAP$
+ buildd@palmer:/build/buildd/openldap2.3-2.4.9/debian/build/servers/slapd
+slapd starting
+...
+Validating password
+ Hash scheme: {SHA256}
+ Password to validate: secret
+ Password hash: K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=
+ Stored password hash: K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=
+ Result: match
+conn=0 op=0 BIND dn="cn=admin,dc=example,dc=com" mech=SIMPLE ssf=0
+conn=0 op=0 RESULT tag=97 err=0 text=
+conn=0 op=1 SRCH base="dc=example,dc=com" scope=2 deref=0 filter="(objectClass=*)"
+conn=0 fd=12 closed (connection lost)
+
+---
+
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 2009-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>.
+
+---
+
+ACKNOWLEDGEMENT:
+This work was initially developed by Jeff Turner for inclusion in
+OpenLDAP Software, based upon the SHA-2 implementation independently
+developed by Aaron Gifford.
+
diff --git a/contrib/slapd-modules/passwd/sha2/sha2.c b/contrib/slapd-modules/passwd/sha2/sha2.c
new file mode 100644
index 0000000..047741a
--- /dev/null
+++ b/contrib/slapd-modules/passwd/sha2/sha2.c
@@ -0,0 +1,1070 @@
+/* $OpenLDAP$ */
+/*
+ * FILE: sha2.c
+ * AUTHOR: Aaron D. Gifford - http://www.aarongifford.com/
+ *
+ * Copyright (c) 2000-2001, Aaron D. Gifford
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: sha2.c,v 1.1 2001/11/08 00:01:51 adg Exp adg $
+ */
+
+#include <string.h> /* memcpy()/memset() or bcopy()/bzero() */
+#include <assert.h> /* assert() */
+#include "sha2.h"
+
+/*
+ * ASSERT NOTE:
+ * Some sanity checking code is included using assert(). On my FreeBSD
+ * system, this additional code can be removed by compiling with NDEBUG
+ * defined. Check your own systems manpage on assert() to see how to
+ * compile WITHOUT the sanity checking code on your system.
+ *
+ * UNROLLED TRANSFORM LOOP NOTE:
+ * You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform
+ * loop version for the hash transform rounds (defined using macros
+ * later in this file). Either define on the command line, for example:
+ *
+ * cc -DSHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c
+ *
+ * or define below:
+ *
+ * #define SHA2_UNROLL_TRANSFORM
+ *
+ */
+
+
+/*** SHA-256/384/512 Machine Architecture Definitions *****************/
+/*
+ * BYTE_ORDER NOTE:
+ *
+ * Please make sure that your system defines BYTE_ORDER. If your
+ * architecture is little-endian, make sure it also defines
+ * LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are
+ * equivalent.
+ *
+ * If your system does not define the above, then you can do so by
+ * hand like this:
+ *
+ * #define LITTLE_ENDIAN 1234
+ * #define BIG_ENDIAN 4321
+ *
+ * And for little-endian machines, add:
+ *
+ * #define BYTE_ORDER LITTLE_ENDIAN
+ *
+ * Or for big-endian machines:
+ *
+ * #define BYTE_ORDER BIG_ENDIAN
+ *
+ * The FreeBSD machine this was written on defines BYTE_ORDER
+ * appropriately by including <sys/types.h> (which in turn includes
+ * <machine/endian.h> where the appropriate definitions are actually
+ * made).
+ */
+#if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN)
+#error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN
+#endif
+
+/*
+ * Define the followingsha2_* types to types of the correct length on
+ * the native architecture. Most BSD systems and Linux define u_intXX_t
+ * types. Machines with very recent ANSI C headers, can use the
+ * uintXX_t definitions from inttypes.h by defining SHA2_USE_INTTYPES_H
+ * during compile or in the sha.h header file.
+ *
+ * Machines that support neither u_intXX_t nor inttypes.h's uintXX_t
+ * will need to define these three typedefs below (and the appropriate
+ * ones in sha.h too) by hand according to their system architecture.
+ *
+ * Thank you, Jun-ichiro itojun Hagino, for suggesting using u_intXX_t
+ * types and pointing out recent ANSI C support for uintXX_t in inttypes.h.
+ */
+#ifdef SHA2_USE_INTTYPES_H
+
+typedef uint8_t sha2_byte; /* Exactly 1 byte */
+typedef uint32_t sha2_word32; /* Exactly 4 bytes */
+typedef uint64_t sha2_word64; /* Exactly 8 bytes */
+
+#else /* SHA2_USE_INTTYPES_H */
+
+typedef u_int8_t sha2_byte; /* Exactly 1 byte */
+typedef u_int32_t sha2_word32; /* Exactly 4 bytes */
+typedef u_int64_t sha2_word64; /* Exactly 8 bytes */
+
+#endif /* SHA2_USE_INTTYPES_H */
+
+
+/*** SHA-256/384/512 Various Length Definitions ***********************/
+/* NOTE: Most of these are in sha2.h */
+#define SHA256_SHORT_BLOCK_LENGTH (SHA256_BLOCK_LENGTH - 8)
+#define SHA384_SHORT_BLOCK_LENGTH (SHA384_BLOCK_LENGTH - 16)
+#define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16)
+
+
+/*** ENDIAN REVERSAL MACROS *******************************************/
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define REVERSE32(w,x) { \
+ sha2_word32 tmp = (w); \
+ tmp = (tmp >> 16) | (tmp << 16); \
+ (x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \
+}
+#define REVERSE64(w,x) { \
+ sha2_word64 tmp = (w); \
+ tmp = (tmp >> 32) | (tmp << 32); \
+ tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \
+ ((tmp & 0x00ff00ff00ff00ffULL) << 8); \
+ (x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \
+ ((tmp & 0x0000ffff0000ffffULL) << 16); \
+}
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+
+/*
+ * Macro for incrementally adding the unsigned 64-bit integer n to the
+ * unsigned 128-bit integer (represented using a two-element array of
+ * 64-bit words):
+ */
+#define ADDINC128(w,n) { \
+ (w)[0] += (sha2_word64)(n); \
+ if ((w)[0] < (n)) { \
+ (w)[1]++; \
+ } \
+}
+
+/*
+ * Macros for copying blocks of memory and for zeroing out ranges
+ * of memory. Using these macros makes it easy to switch from
+ * using memset()/memcpy() and using bzero()/bcopy().
+ *
+ * Please define either SHA2_USE_MEMSET_MEMCPY or define
+ * SHA2_USE_BZERO_BCOPY depending on which function set you
+ * choose to use:
+ */
+#if !defined(SHA2_USE_MEMSET_MEMCPY) && !defined(SHA2_USE_BZERO_BCOPY)
+/* Default to memset()/memcpy() if no option is specified */
+#define SHA2_USE_MEMSET_MEMCPY 1
+#endif
+#if defined(SHA2_USE_MEMSET_MEMCPY) && defined(SHA2_USE_BZERO_BCOPY)
+/* Abort with an error if BOTH options are defined */
+#error Define either SHA2_USE_MEMSET_MEMCPY or SHA2_USE_BZERO_BCOPY, not both!
+#endif
+
+#ifdef SHA2_USE_MEMSET_MEMCPY
+#define MEMSET_BZERO(p,l) memset((p), 0, (l))
+#define MEMCPY_BCOPY(d,s,l) memcpy((d), (s), (l))
+#endif
+#ifdef SHA2_USE_BZERO_BCOPY
+#define MEMSET_BZERO(p,l) bzero((p), (l))
+#define MEMCPY_BCOPY(d,s,l) bcopy((s), (d), (l))
+#endif
+
+
+/*** THE SIX LOGICAL FUNCTIONS ****************************************/
+/*
+ * Bit shifting and rotation (used by the six SHA-XYZ logical functions:
+ *
+ * NOTE: The naming of R and S appears backwards here (R is a SHIFT and
+ * S is a ROTATION) because the SHA-256/384/512 description document
+ * (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this
+ * same "backwards" definition.
+ */
+/* Shift-right (used in SHA-256, SHA-384, and SHA-512): */
+#define R(b,x) ((x) >> (b))
+/* 32-bit Rotate-right (used in SHA-256): */
+#define S32(b,x) (((x) >> (b)) | ((x) << (32 - (b))))
+/* 64-bit Rotate-right (used in SHA-384 and SHA-512): */
+#define S64(b,x) (((x) >> (b)) | ((x) << (64 - (b))))
+
+/* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */
+#define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z)))
+#define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+
+/* Four of six logical functions used in SHA-256: */
+#define Sigma0_256(x) (S32(2, (x)) ^ S32(13, (x)) ^ S32(22, (x)))
+#define Sigma1_256(x) (S32(6, (x)) ^ S32(11, (x)) ^ S32(25, (x)))
+#define sigma0_256(x) (S32(7, (x)) ^ S32(18, (x)) ^ R(3 , (x)))
+#define sigma1_256(x) (S32(17, (x)) ^ S32(19, (x)) ^ R(10, (x)))
+
+/* Four of six logical functions used in SHA-384 and SHA-512: */
+#define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x)))
+#define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x)))
+#define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7, (x)))
+#define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ R( 6, (x)))
+
+/*** INTERNAL FUNCTION PROTOTYPES *************************************/
+/* NOTE: These should not be accessed directly from outside this
+ * library -- they are intended for private internal visibility/use
+ * only.
+ */
+static void SHA512_Last(SHA512_CTX*);
+static void SHA256_Transform(SHA256_CTX*, const sha2_word32*);
+static void SHA512_Transform(SHA512_CTX*, const sha2_word64*);
+
+
+/*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/
+/* Hash constant words K for SHA-256: */
+const static sha2_word32 K256[64] = {
+ 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,
+ 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
+ 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
+ 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
+ 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+ 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
+ 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,
+ 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
+ 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
+ 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+ 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,
+ 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
+ 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,
+ 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
+ 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+ 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+
+/* Initial hash value H for SHA-256: */
+const static sha2_word32 sha256_initial_hash_value[8] = {
+ 0x6a09e667UL,
+ 0xbb67ae85UL,
+ 0x3c6ef372UL,
+ 0xa54ff53aUL,
+ 0x510e527fUL,
+ 0x9b05688cUL,
+ 0x1f83d9abUL,
+ 0x5be0cd19UL
+};
+
+/* Hash constant words K for SHA-384 and SHA-512: */
+const static sha2_word64 K512[80] = {
+ 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
+ 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
+ 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
+ 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
+ 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
+ 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
+ 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
+ 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
+ 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
+ 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
+ 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
+ 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
+ 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
+ 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
+ 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
+ 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
+ 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
+ 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
+ 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
+ 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
+ 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
+ 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
+ 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
+ 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
+ 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
+ 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
+ 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
+ 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
+ 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
+ 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
+ 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
+ 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
+ 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
+ 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
+ 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
+ 0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
+ 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
+ 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
+ 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
+ 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL
+};
+
+/* Initial hash value H for SHA-384 */
+const static sha2_word64 sha384_initial_hash_value[8] = {
+ 0xcbbb9d5dc1059ed8ULL,
+ 0x629a292a367cd507ULL,
+ 0x9159015a3070dd17ULL,
+ 0x152fecd8f70e5939ULL,
+ 0x67332667ffc00b31ULL,
+ 0x8eb44a8768581511ULL,
+ 0xdb0c2e0d64f98fa7ULL,
+ 0x47b5481dbefa4fa4ULL
+};
+
+/* Initial hash value H for SHA-512 */
+const static sha2_word64 sha512_initial_hash_value[8] = {
+ 0x6a09e667f3bcc908ULL,
+ 0xbb67ae8584caa73bULL,
+ 0x3c6ef372fe94f82bULL,
+ 0xa54ff53a5f1d36f1ULL,
+ 0x510e527fade682d1ULL,
+ 0x9b05688c2b3e6c1fULL,
+ 0x1f83d9abfb41bd6bULL,
+ 0x5be0cd19137e2179ULL
+};
+
+/*
+ * Constant used by SHA256/384/512_End() functions for converting the
+ * digest to a readable hexadecimal character string:
+ */
+static const char *sha2_hex_digits = "0123456789abcdef";
+
+
+/*** SHA-256: *********************************************************/
+void SHA256_Init(SHA256_CTX* context) {
+ if (context == (SHA256_CTX*)0) {
+ return;
+ }
+ MEMCPY_BCOPY(context->state, sha256_initial_hash_value, SHA256_DIGEST_LENGTH);
+ MEMSET_BZERO(context->buffer, SHA256_BLOCK_LENGTH);
+ context->bitcount = 0;
+}
+
+#ifdef SHA2_UNROLL_TRANSFORM
+
+/* Unrolled SHA-256 round macros: */
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+
+#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \
+ REVERSE32(*data++, W256[j]); \
+ T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \
+ K256[j] + W256[j]; \
+ (d) += T1; \
+ (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
+ j++
+
+
+#else /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \
+ T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \
+ K256[j] + (W256[j] = *data++); \
+ (d) += T1; \
+ (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
+ j++
+
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#define ROUND256(a,b,c,d,e,f,g,h) \
+ s0 = W256[(j+1)&0x0f]; \
+ s0 = sigma0_256(s0); \
+ s1 = W256[(j+14)&0x0f]; \
+ s1 = sigma1_256(s1); \
+ T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + K256[j] + \
+ (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \
+ (d) += T1; \
+ (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \
+ j++
+
+void SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) {
+ sha2_word32 a, b, c, d, e, f, g, h, s0, s1;
+ sha2_word32 T1, *W256;
+ int j;
+
+ W256 = (sha2_word32*)context->buffer;
+
+ /* Initialize registers with the prev. intermediate value */
+ a = context->state[0];
+ b = context->state[1];
+ c = context->state[2];
+ d = context->state[3];
+ e = context->state[4];
+ f = context->state[5];
+ g = context->state[6];
+ h = context->state[7];
+
+ j = 0;
+ do {
+ /* Rounds 0 to 15 (unrolled): */
+ ROUND256_0_TO_15(a,b,c,d,e,f,g,h);
+ ROUND256_0_TO_15(h,a,b,c,d,e,f,g);
+ ROUND256_0_TO_15(g,h,a,b,c,d,e,f);
+ ROUND256_0_TO_15(f,g,h,a,b,c,d,e);
+ ROUND256_0_TO_15(e,f,g,h,a,b,c,d);
+ ROUND256_0_TO_15(d,e,f,g,h,a,b,c);
+ ROUND256_0_TO_15(c,d,e,f,g,h,a,b);
+ ROUND256_0_TO_15(b,c,d,e,f,g,h,a);
+ } while (j < 16);
+
+ /* Now for the remaining rounds to 64: */
+ do {
+ ROUND256(a,b,c,d,e,f,g,h);
+ ROUND256(h,a,b,c,d,e,f,g);
+ ROUND256(g,h,a,b,c,d,e,f);
+ ROUND256(f,g,h,a,b,c,d,e);
+ ROUND256(e,f,g,h,a,b,c,d);
+ ROUND256(d,e,f,g,h,a,b,c);
+ ROUND256(c,d,e,f,g,h,a,b);
+ ROUND256(b,c,d,e,f,g,h,a);
+ } while (j < 64);
+
+ /* Compute the current intermediate hash value */
+ context->state[0] += a;
+ context->state[1] += b;
+ context->state[2] += c;
+ context->state[3] += d;
+ context->state[4] += e;
+ context->state[5] += f;
+ context->state[6] += g;
+ context->state[7] += h;
+
+ /* Clean up */
+ a = b = c = d = e = f = g = h = T1 = 0;
+}
+
+#else /* SHA2_UNROLL_TRANSFORM */
+
+void SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) {
+ sha2_word32 a, b, c, d, e, f, g, h, s0, s1;
+ sha2_word32 T1, T2, *W256;
+ int j;
+
+ W256 = (sha2_word32*)context->buffer;
+
+ /* Initialize registers with the prev. intermediate value */
+ a = context->state[0];
+ b = context->state[1];
+ c = context->state[2];
+ d = context->state[3];
+ e = context->state[4];
+ f = context->state[5];
+ g = context->state[6];
+ h = context->state[7];
+
+ j = 0;
+ do {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ /* Copy data while converting to host byte order */
+ REVERSE32(*data++,W256[j]);
+ /* Apply the SHA-256 compression function to update a..h */
+ T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j];
+#else /* BYTE_ORDER == LITTLE_ENDIAN */
+ /* Apply the SHA-256 compression function to update a..h with copy */
+ T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + (W256[j] = *data++);
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+ T2 = Sigma0_256(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+
+ j++;
+ } while (j < 16);
+
+ do {
+ /* Part of the message block expansion: */
+ s0 = W256[(j+1)&0x0f];
+ s0 = sigma0_256(s0);
+ s1 = W256[(j+14)&0x0f];
+ s1 = sigma1_256(s1);
+
+ /* Apply the SHA-256 compression function to update a..h */
+ T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] +
+ (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0);
+ T2 = Sigma0_256(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+
+ j++;
+ } while (j < 64);
+
+ /* Compute the current intermediate hash value */
+ context->state[0] += a;
+ context->state[1] += b;
+ context->state[2] += c;
+ context->state[3] += d;
+ context->state[4] += e;
+ context->state[5] += f;
+ context->state[6] += g;
+ context->state[7] += h;
+
+ /* Clean up */
+ a = b = c = d = e = f = g = h = T1 = T2 = 0;
+}
+
+#endif /* SHA2_UNROLL_TRANSFORM */
+
+void SHA256_Update(SHA256_CTX* context, const sha2_byte *data, size_t len) {
+ unsigned int freespace, usedspace;
+
+ if (len == 0) {
+ /* Calling with no data is valid - we do nothing */
+ return;
+ }
+
+ /* Sanity check: */
+ assert(context != (SHA256_CTX*)0 && data != (sha2_byte*)0);
+
+ usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH;
+ if (usedspace > 0) {
+ /* Calculate how much free space is available in the buffer */
+ freespace = SHA256_BLOCK_LENGTH - usedspace;
+
+ if (len >= freespace) {
+ /* Fill the buffer completely and process it */
+ MEMCPY_BCOPY(&context->buffer[usedspace], data, freespace);
+ context->bitcount += freespace << 3;
+ len -= freespace;
+ data += freespace;
+ SHA256_Transform(context, (sha2_word32*)context->buffer);
+ } else {
+ /* The buffer is not yet full */
+ MEMCPY_BCOPY(&context->buffer[usedspace], data, len);
+ context->bitcount += len << 3;
+ /* Clean up: */
+ usedspace = freespace = 0;
+ return;
+ }
+ }
+ while (len >= SHA256_BLOCK_LENGTH) {
+ /* Process as many complete blocks as we can */
+ SHA256_Transform(context, (sha2_word32*)data);
+ context->bitcount += SHA256_BLOCK_LENGTH << 3;
+ len -= SHA256_BLOCK_LENGTH;
+ data += SHA256_BLOCK_LENGTH;
+ }
+ if (len > 0) {
+ /* There's left-overs, so save 'em */
+ MEMCPY_BCOPY(context->buffer, data, len);
+ context->bitcount += len << 3;
+ }
+ /* Clean up: */
+ usedspace = freespace = 0;
+}
+
+void SHA256_Final(sha2_byte digest[], SHA256_CTX* context) {
+ sha2_word32 *d = (sha2_word32*)digest;
+ sha2_word64 *p;
+ unsigned int usedspace;
+
+ /* Sanity check: */
+ assert(context != (SHA256_CTX*)0);
+
+ /* If no digest buffer is passed, we don't bother doing this: */
+ if (digest != (sha2_byte*)0) {
+ usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ /* Convert FROM host byte order */
+ REVERSE64(context->bitcount,context->bitcount);
+#endif
+ if (usedspace > 0) {
+ /* Begin padding with a 1 bit: */
+ context->buffer[usedspace++] = 0x80;
+
+ if (usedspace <= SHA256_SHORT_BLOCK_LENGTH) {
+ /* Set-up for the last transform: */
+ MEMSET_BZERO(&context->buffer[usedspace], SHA256_SHORT_BLOCK_LENGTH - usedspace);
+ } else {
+ if (usedspace < SHA256_BLOCK_LENGTH) {
+ MEMSET_BZERO(&context->buffer[usedspace], SHA256_BLOCK_LENGTH - usedspace);
+ }
+ /* Do second-to-last transform: */
+ SHA256_Transform(context, (sha2_word32*)context->buffer);
+
+ /* And set-up for the last transform: */
+ MEMSET_BZERO(context->buffer, SHA256_SHORT_BLOCK_LENGTH);
+ }
+ } else {
+ /* Set-up for the last transform: */
+ MEMSET_BZERO(context->buffer, SHA256_SHORT_BLOCK_LENGTH);
+
+ /* Begin padding with a 1 bit: */
+ *context->buffer = 0x80;
+ }
+ /* Set the bit count: */
+ p = (sha2_word64 *)&context->buffer[SHA256_SHORT_BLOCK_LENGTH];
+ *p = context->bitcount;
+
+ /* Final transform: */
+ SHA256_Transform(context, (sha2_word32*)context->buffer);
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+ {
+ /* Convert TO host byte order */
+ int j;
+ for (j = 0; j < 8; j++) {
+ REVERSE32(context->state[j],context->state[j]);
+ *d++ = context->state[j];
+ }
+ }
+#else
+ MEMCPY_BCOPY(d, context->state, SHA256_DIGEST_LENGTH);
+#endif
+ }
+
+ /* Clean up state data: */
+ MEMSET_BZERO(context, sizeof(*context));
+ usedspace = 0;
+}
+
+char *SHA256_End(SHA256_CTX* context, char buffer[]) {
+ sha2_byte digest[SHA256_DIGEST_LENGTH], *d = digest;
+ int i;
+
+ /* Sanity check: */
+ assert(context != (SHA256_CTX*)0);
+
+ if (buffer != (char*)0) {
+ SHA256_Final(digest, context);
+
+ for (i = 0; i < SHA256_DIGEST_LENGTH; i++) {
+ *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4];
+ *buffer++ = sha2_hex_digits[*d & 0x0f];
+ d++;
+ }
+ *buffer = (char)0;
+ } else {
+ MEMSET_BZERO(context, sizeof(*context));
+ }
+ MEMSET_BZERO(digest, SHA256_DIGEST_LENGTH);
+ return buffer;
+}
+
+char* SHA256_Data(const sha2_byte* data, size_t len, char digest[SHA256_DIGEST_STRING_LENGTH]) {
+ SHA256_CTX context;
+
+ SHA256_Init(&context);
+ SHA256_Update(&context, data, len);
+ return SHA256_End(&context, digest);
+}
+
+
+/*** SHA-512: *********************************************************/
+void SHA512_Init(SHA512_CTX* context) {
+ if (context == (SHA512_CTX*)0) {
+ return;
+ }
+ MEMCPY_BCOPY(context->state, sha512_initial_hash_value, SHA512_DIGEST_LENGTH);
+ MEMSET_BZERO(context->buffer, SHA512_BLOCK_LENGTH);
+ context->bitcount[0] = context->bitcount[1] = 0;
+}
+
+#ifdef SHA2_UNROLL_TRANSFORM
+
+/* Unrolled SHA-512 round macros: */
+#if BYTE_ORDER == LITTLE_ENDIAN
+
+#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \
+ REVERSE64(*data++, W512[j]); \
+ T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \
+ K512[j] + W512[j]; \
+ (d) += T1, \
+ (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)), \
+ j++
+
+
+#else /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \
+ T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \
+ K512[j] + (W512[j] = *data++); \
+ (d) += T1; \
+ (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \
+ j++
+
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#define ROUND512(a,b,c,d,e,f,g,h) \
+ s0 = W512[(j+1)&0x0f]; \
+ s0 = sigma0_512(s0); \
+ s1 = W512[(j+14)&0x0f]; \
+ s1 = sigma1_512(s1); \
+ T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + K512[j] + \
+ (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); \
+ (d) += T1; \
+ (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \
+ j++
+
+void SHA512_Transform(SHA512_CTX* context, const sha2_word64* data) {
+ sha2_word64 a, b, c, d, e, f, g, h, s0, s1;
+ sha2_word64 T1, *W512 = (sha2_word64*)context->buffer;
+ int j;
+
+ /* Initialize registers with the prev. intermediate value */
+ a = context->state[0];
+ b = context->state[1];
+ c = context->state[2];
+ d = context->state[3];
+ e = context->state[4];
+ f = context->state[5];
+ g = context->state[6];
+ h = context->state[7];
+
+ j = 0;
+ do {
+ ROUND512_0_TO_15(a,b,c,d,e,f,g,h);
+ ROUND512_0_TO_15(h,a,b,c,d,e,f,g);
+ ROUND512_0_TO_15(g,h,a,b,c,d,e,f);
+ ROUND512_0_TO_15(f,g,h,a,b,c,d,e);
+ ROUND512_0_TO_15(e,f,g,h,a,b,c,d);
+ ROUND512_0_TO_15(d,e,f,g,h,a,b,c);
+ ROUND512_0_TO_15(c,d,e,f,g,h,a,b);
+ ROUND512_0_TO_15(b,c,d,e,f,g,h,a);
+ } while (j < 16);
+
+ /* Now for the remaining rounds up to 79: */
+ do {
+ ROUND512(a,b,c,d,e,f,g,h);
+ ROUND512(h,a,b,c,d,e,f,g);
+ ROUND512(g,h,a,b,c,d,e,f);
+ ROUND512(f,g,h,a,b,c,d,e);
+ ROUND512(e,f,g,h,a,b,c,d);
+ ROUND512(d,e,f,g,h,a,b,c);
+ ROUND512(c,d,e,f,g,h,a,b);
+ ROUND512(b,c,d,e,f,g,h,a);
+ } while (j < 80);
+
+ /* Compute the current intermediate hash value */
+ context->state[0] += a;
+ context->state[1] += b;
+ context->state[2] += c;
+ context->state[3] += d;
+ context->state[4] += e;
+ context->state[5] += f;
+ context->state[6] += g;
+ context->state[7] += h;
+
+ /* Clean up */
+ a = b = c = d = e = f = g = h = T1 = 0;
+}
+
+#else /* SHA2_UNROLL_TRANSFORM */
+
+void SHA512_Transform(SHA512_CTX* context, const sha2_word64* data) {
+ sha2_word64 a, b, c, d, e, f, g, h, s0, s1;
+ sha2_word64 T1, T2, *W512 = (sha2_word64*)context->buffer;
+ int j;
+
+ /* Initialize registers with the prev. intermediate value */
+ a = context->state[0];
+ b = context->state[1];
+ c = context->state[2];
+ d = context->state[3];
+ e = context->state[4];
+ f = context->state[5];
+ g = context->state[6];
+ h = context->state[7];
+
+ j = 0;
+ do {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ /* Convert TO host byte order */
+ REVERSE64(*data++, W512[j]);
+ /* Apply the SHA-512 compression function to update a..h */
+ T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j];
+#else /* BYTE_ORDER == LITTLE_ENDIAN */
+ /* Apply the SHA-512 compression function to update a..h with copy */
+ T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + (W512[j] = *data++);
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+ T2 = Sigma0_512(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+
+ j++;
+ } while (j < 16);
+
+ do {
+ /* Part of the message block expansion: */
+ s0 = W512[(j+1)&0x0f];
+ s0 = sigma0_512(s0);
+ s1 = W512[(j+14)&0x0f];
+ s1 = sigma1_512(s1);
+
+ /* Apply the SHA-512 compression function to update a..h */
+ T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] +
+ (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0);
+ T2 = Sigma0_512(a) + Maj(a, b, c);
+ h = g;
+ g = f;
+ f = e;
+ e = d + T1;
+ d = c;
+ c = b;
+ b = a;
+ a = T1 + T2;
+
+ j++;
+ } while (j < 80);
+
+ /* Compute the current intermediate hash value */
+ context->state[0] += a;
+ context->state[1] += b;
+ context->state[2] += c;
+ context->state[3] += d;
+ context->state[4] += e;
+ context->state[5] += f;
+ context->state[6] += g;
+ context->state[7] += h;
+
+ /* Clean up */
+ a = b = c = d = e = f = g = h = T1 = T2 = 0;
+}
+
+#endif /* SHA2_UNROLL_TRANSFORM */
+
+void SHA512_Update(SHA512_CTX* context, const sha2_byte *data, size_t len) {
+ unsigned int freespace, usedspace;
+
+ if (len == 0) {
+ /* Calling with no data is valid - we do nothing */
+ return;
+ }
+
+ /* Sanity check: */
+ assert(context != (SHA512_CTX*)0 && data != (sha2_byte*)0);
+
+ usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
+ if (usedspace > 0) {
+ /* Calculate how much free space is available in the buffer */
+ freespace = SHA512_BLOCK_LENGTH - usedspace;
+
+ if (len >= freespace) {
+ /* Fill the buffer completely and process it */
+ MEMCPY_BCOPY(&context->buffer[usedspace], data, freespace);
+ ADDINC128(context->bitcount, freespace << 3);
+ len -= freespace;
+ data += freespace;
+ SHA512_Transform(context, (sha2_word64*)context->buffer);
+ } else {
+ /* The buffer is not yet full */
+ MEMCPY_BCOPY(&context->buffer[usedspace], data, len);
+ ADDINC128(context->bitcount, len << 3);
+ /* Clean up: */
+ usedspace = freespace = 0;
+ return;
+ }
+ }
+ while (len >= SHA512_BLOCK_LENGTH) {
+ /* Process as many complete blocks as we can */
+ SHA512_Transform(context, (sha2_word64*)data);
+ ADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3);
+ len -= SHA512_BLOCK_LENGTH;
+ data += SHA512_BLOCK_LENGTH;
+ }
+ if (len > 0) {
+ /* There's left-overs, so save 'em */
+ MEMCPY_BCOPY(context->buffer, data, len);
+ ADDINC128(context->bitcount, len << 3);
+ }
+ /* Clean up: */
+ usedspace = freespace = 0;
+}
+
+void SHA512_Last(SHA512_CTX* context) {
+ sha2_word64 *p;
+ unsigned int usedspace;
+
+ usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ /* Convert FROM host byte order */
+ REVERSE64(context->bitcount[0],context->bitcount[0]);
+ REVERSE64(context->bitcount[1],context->bitcount[1]);
+#endif
+ if (usedspace > 0) {
+ /* Begin padding with a 1 bit: */
+ context->buffer[usedspace++] = 0x80;
+
+ if (usedspace <= SHA512_SHORT_BLOCK_LENGTH) {
+ /* Set-up for the last transform: */
+ MEMSET_BZERO(&context->buffer[usedspace], SHA512_SHORT_BLOCK_LENGTH - usedspace);
+ } else {
+ if (usedspace < SHA512_BLOCK_LENGTH) {
+ MEMSET_BZERO(&context->buffer[usedspace], SHA512_BLOCK_LENGTH - usedspace);
+ }
+ /* Do second-to-last transform: */
+ SHA512_Transform(context, (sha2_word64*)context->buffer);
+
+ /* And set-up for the last transform: */
+ MEMSET_BZERO(context->buffer, SHA512_BLOCK_LENGTH - 2);
+ }
+ } else {
+ /* Prepare for final transform: */
+ MEMSET_BZERO(context->buffer, SHA512_SHORT_BLOCK_LENGTH);
+
+ /* Begin padding with a 1 bit: */
+ *context->buffer = 0x80;
+ }
+ /* Store the length of input data (in bits): */
+ p = (sha2_word64 *)&context->buffer[SHA512_SHORT_BLOCK_LENGTH];
+ p[0] = context->bitcount[1];
+ p[1] = context->bitcount[0];
+
+ /* Final transform: */
+ SHA512_Transform(context, (sha2_word64*)context->buffer);
+}
+
+void SHA512_Final(sha2_byte digest[], SHA512_CTX* context) {
+ sha2_word64 *d = (sha2_word64*)digest;
+
+ /* Sanity check: */
+ assert(context != (SHA512_CTX*)0);
+
+ /* If no digest buffer is passed, we don't bother doing this: */
+ if (digest != (sha2_byte*)0) {
+ SHA512_Last(context);
+
+ /* Save the hash data for output: */
+#if BYTE_ORDER == LITTLE_ENDIAN
+ {
+ /* Convert TO host byte order */
+ int j;
+ for (j = 0; j < 8; j++) {
+ REVERSE64(context->state[j],context->state[j]);
+ *d++ = context->state[j];
+ }
+ }
+#else
+ MEMCPY_BCOPY(d, context->state, SHA512_DIGEST_LENGTH);
+#endif
+ }
+
+ /* Zero out state data */
+ MEMSET_BZERO(context, sizeof(*context));
+}
+
+char *SHA512_End(SHA512_CTX* context, char buffer[]) {
+ sha2_byte digest[SHA512_DIGEST_LENGTH], *d = digest;
+ int i;
+
+ /* Sanity check: */
+ assert(context != (SHA512_CTX*)0);
+
+ if (buffer != (char*)0) {
+ SHA512_Final(digest, context);
+
+ for (i = 0; i < SHA512_DIGEST_LENGTH; i++) {
+ *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4];
+ *buffer++ = sha2_hex_digits[*d & 0x0f];
+ d++;
+ }
+ *buffer = (char)0;
+ } else {
+ MEMSET_BZERO(context, sizeof(*context));
+ }
+ MEMSET_BZERO(digest, SHA512_DIGEST_LENGTH);
+ return buffer;
+}
+
+char* SHA512_Data(const sha2_byte* data, size_t len, char digest[SHA512_DIGEST_STRING_LENGTH]) {
+ SHA512_CTX context;
+
+ SHA512_Init(&context);
+ SHA512_Update(&context, data, len);
+ return SHA512_End(&context, digest);
+}
+
+
+/*** SHA-384: *********************************************************/
+void SHA384_Init(SHA384_CTX* context) {
+ if (context == (SHA384_CTX*)0) {
+ return;
+ }
+ MEMCPY_BCOPY(context->state, sha384_initial_hash_value, SHA512_DIGEST_LENGTH);
+ MEMSET_BZERO(context->buffer, SHA384_BLOCK_LENGTH);
+ context->bitcount[0] = context->bitcount[1] = 0;
+}
+
+void SHA384_Update(SHA384_CTX* context, const sha2_byte* data, size_t len) {
+ SHA512_Update((SHA512_CTX*)context, data, len);
+}
+
+void SHA384_Final(sha2_byte digest[], SHA384_CTX* context) {
+ sha2_word64 *d = (sha2_word64*)digest;
+
+ /* Sanity check: */
+ assert(context != (SHA384_CTX*)0);
+
+ /* If no digest buffer is passed, we don't bother doing this: */
+ if (digest != (sha2_byte*)0) {
+ SHA512_Last((SHA512_CTX*)context);
+
+ /* Save the hash data for output: */
+#if BYTE_ORDER == LITTLE_ENDIAN
+ {
+ /* Convert TO host byte order */
+ int j;
+ for (j = 0; j < 6; j++) {
+ REVERSE64(context->state[j],context->state[j]);
+ *d++ = context->state[j];
+ }
+ }
+#else
+ MEMCPY_BCOPY(d, context->state, SHA384_DIGEST_LENGTH);
+#endif
+ }
+
+ /* Zero out state data */
+ MEMSET_BZERO(context, sizeof(*context));
+}
+
+char *SHA384_End(SHA384_CTX* context, char buffer[]) {
+ sha2_byte digest[SHA384_DIGEST_LENGTH], *d = digest;
+ int i;
+
+ /* Sanity check: */
+ assert(context != (SHA384_CTX*)0);
+
+ if (buffer != (char*)0) {
+ SHA384_Final(digest, context);
+
+ for (i = 0; i < SHA384_DIGEST_LENGTH; i++) {
+ *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4];
+ *buffer++ = sha2_hex_digits[*d & 0x0f];
+ d++;
+ }
+ *buffer = (char)0;
+ } else {
+ MEMSET_BZERO(context, sizeof(*context));
+ }
+ MEMSET_BZERO(digest, SHA384_DIGEST_LENGTH);
+ return buffer;
+}
+
+char* SHA384_Data(const sha2_byte* data, size_t len, char digest[SHA384_DIGEST_STRING_LENGTH]) {
+ SHA384_CTX context;
+
+ SHA384_Init(&context);
+ SHA384_Update(&context, data, len);
+ return SHA384_End(&context, digest);
+}
+
diff --git a/contrib/slapd-modules/passwd/sha2/sha2.h b/contrib/slapd-modules/passwd/sha2/sha2.h
new file mode 100644
index 0000000..7fff142
--- /dev/null
+++ b/contrib/slapd-modules/passwd/sha2/sha2.h
@@ -0,0 +1,236 @@
+/* $OpenLDAP$ */
+/*
+ * FILE: sha2.h
+ * AUTHOR: Aaron D. Gifford - http://www.aarongifford.com/
+ *
+ * Copyright (c) 2000-2001, Aaron D. Gifford
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the copyright holder nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: sha2.h,v 1.1 2001/11/08 00:02:01 adg Exp adg $
+ */
+
+#ifndef __SHA2_H__
+#define __SHA2_H__
+
+#include "portable.h"
+
+#ifdef HAVE_INTTYPES_H
+# define SHA2_USE_INTTYPES_H 1
+#endif
+
+#ifndef LITTLE_ENDIAN
+# define LITTLE_ENDIAN 1234
+#endif
+#ifndef BIG_ENDIAN
+# define BIG_ENDIAN 4321
+#endif
+#ifndef BYTE_ORDER
+# ifdef WORDS_BIGENDIAN
+# define BYTE_ORDER BIG_ENDIAN
+# else
+# define BYTE_ORDER LITTLE_ENDIAN
+# endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Import u_intXX_t size_t type definitions from system headers. You
+ * may need to change this, or define these things yourself in this
+ * file.
+ */
+#include <sys/types.h>
+
+#ifdef SHA2_USE_INTTYPES_H
+
+#include <inttypes.h>
+
+#endif /* SHA2_USE_INTTYPES_H */
+
+
+/*** SHA-256/384/512 Various Length Definitions ***********************/
+#define SHA256_BLOCK_LENGTH 64
+#define SHA256_DIGEST_LENGTH 32
+#define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2 + 1)
+#define SHA384_BLOCK_LENGTH 128
+#define SHA384_DIGEST_LENGTH 48
+#define SHA384_DIGEST_STRING_LENGTH (SHA384_DIGEST_LENGTH * 2 + 1)
+#define SHA512_BLOCK_LENGTH 128
+#define SHA512_DIGEST_LENGTH 64
+#define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1)
+
+
+/*** SHA-256/384/512 Context Structures *******************************/
+/* NOTE: If your architecture does not define either u_intXX_t types or
+ * uintXX_t (from inttypes.h), you may need to define things by hand
+ * for your system:
+ */
+#if 0
+typedef unsigned char u_int8_t; /* 1-byte (8-bits) */
+typedef unsigned int u_int32_t; /* 4-bytes (32-bits) */
+typedef unsigned long long u_int64_t; /* 8-bytes (64-bits) */
+#endif
+/*
+ * Most BSD systems already define u_intXX_t types, as does Linux.
+ * Some systems, however, like Compaq's Tru64 Unix instead can use
+ * uintXX_t types defined by very recent ANSI C standards and included
+ * in the file:
+ *
+ * #include <inttypes.h>
+ *
+ * If you choose to use <inttypes.h> then please define:
+ *
+ * #define SHA2_USE_INTTYPES_H
+ *
+ * Or on the command line during compile:
+ *
+ * cc -DSHA2_USE_INTTYPES_H ...
+ */
+#ifdef SHA2_USE_INTTYPES_H
+
+typedef struct _SHA256_CTX {
+ uint32_t state[8];
+ uint64_t bitcount;
+ uint8_t buffer[SHA256_BLOCK_LENGTH];
+} SHA256_CTX;
+typedef struct _SHA512_CTX {
+ uint64_t state[8];
+ uint64_t bitcount[2];
+ uint8_t buffer[SHA512_BLOCK_LENGTH];
+} SHA512_CTX;
+
+#else /* SHA2_USE_INTTYPES_H */
+
+typedef struct _SHA256_CTX {
+ u_int32_t state[8];
+ u_int64_t bitcount;
+ u_int8_t buffer[SHA256_BLOCK_LENGTH];
+} SHA256_CTX;
+typedef struct _SHA512_CTX {
+ u_int64_t state[8];
+ u_int64_t bitcount[2];
+ u_int8_t buffer[SHA512_BLOCK_LENGTH];
+} SHA512_CTX;
+
+#endif /* SHA2_USE_INTTYPES_H */
+
+typedef SHA512_CTX SHA384_CTX;
+
+
+/*** SHA-256/384/512 Function Prototypes ******************************/
+/* avoid symbol clash with other crypto libs */
+#define SHA256_Init pw_SHA256_Init
+#define SHA256_Update pw_SHA256_Update
+#define SHA256_Final pw_SHA256_Final
+#define SHA256_End pw_SHA256_End
+#define SHA256_Data pw_SHA256_Data
+
+#define SHA384_Init pw_SHA384_Init
+#define SHA384_Update pw_SHA384_Update
+#define SHA384_Final pw_SHA384_Final
+#define SHA384_End pw_SHA384_End
+#define SHA384_Data pw_SHA384_Data
+
+#define SHA512_Init pw_SHA512_Init
+#define SHA512_Update pw_SHA512_Update
+#define SHA512_Final pw_SHA512_Final
+#define SHA512_End pw_SHA512_End
+#define SHA512_Data pw_SHA512_Data
+
+#ifndef NOPROTO
+#ifdef SHA2_USE_INTTYPES_H
+
+void SHA256_Init(SHA256_CTX *);
+void SHA256_Update(SHA256_CTX*, const uint8_t*, size_t);
+void SHA256_Final(uint8_t[SHA256_DIGEST_LENGTH], SHA256_CTX*);
+char* SHA256_End(SHA256_CTX*, char[SHA256_DIGEST_STRING_LENGTH]);
+char* SHA256_Data(const uint8_t*, size_t, char[SHA256_DIGEST_STRING_LENGTH]);
+
+void SHA384_Init(SHA384_CTX*);
+void SHA384_Update(SHA384_CTX*, const uint8_t*, size_t);
+void SHA384_Final(uint8_t[SHA384_DIGEST_LENGTH], SHA384_CTX*);
+char* SHA384_End(SHA384_CTX*, char[SHA384_DIGEST_STRING_LENGTH]);
+char* SHA384_Data(const uint8_t*, size_t, char[SHA384_DIGEST_STRING_LENGTH]);
+
+void SHA512_Init(SHA512_CTX*);
+void SHA512_Update(SHA512_CTX*, const uint8_t*, size_t);
+void SHA512_Final(uint8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*);
+char* SHA512_End(SHA512_CTX*, char[SHA512_DIGEST_STRING_LENGTH]);
+char* SHA512_Data(const uint8_t*, size_t, char[SHA512_DIGEST_STRING_LENGTH]);
+
+#else /* SHA2_USE_INTTYPES_H */
+
+void SHA256_Init(SHA256_CTX *);
+void SHA256_Update(SHA256_CTX*, const u_int8_t*, size_t);
+void SHA256_Final(u_int8_t[SHA256_DIGEST_LENGTH], SHA256_CTX*);
+char* SHA256_End(SHA256_CTX*, char[SHA256_DIGEST_STRING_LENGTH]);
+char* SHA256_Data(const u_int8_t*, size_t, char[SHA256_DIGEST_STRING_LENGTH]);
+
+void SHA384_Init(SHA384_CTX*);
+void SHA384_Update(SHA384_CTX*, const u_int8_t*, size_t);
+void SHA384_Final(u_int8_t[SHA384_DIGEST_LENGTH], SHA384_CTX*);
+char* SHA384_End(SHA384_CTX*, char[SHA384_DIGEST_STRING_LENGTH]);
+char* SHA384_Data(const u_int8_t*, size_t, char[SHA384_DIGEST_STRING_LENGTH]);
+
+void SHA512_Init(SHA512_CTX*);
+void SHA512_Update(SHA512_CTX*, const u_int8_t*, size_t);
+void SHA512_Final(u_int8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*);
+char* SHA512_End(SHA512_CTX*, char[SHA512_DIGEST_STRING_LENGTH]);
+char* SHA512_Data(const u_int8_t*, size_t, char[SHA512_DIGEST_STRING_LENGTH]);
+
+#endif /* SHA2_USE_INTTYPES_H */
+
+#else /* NOPROTO */
+
+void SHA256_Init();
+void SHA256_Update();
+void SHA256_Final();
+char* SHA256_End();
+char* SHA256_Data();
+
+void SHA384_Init();
+void SHA384_Update();
+void SHA384_Final();
+char* SHA384_End();
+char* SHA384_Data();
+
+void SHA512_Init();
+void SHA512_Update();
+void SHA512_Final();
+char* SHA512_End();
+char* SHA512_Data();
+
+#endif /* NOPROTO */
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __SHA2_H__ */
+
diff --git a/contrib/slapd-modules/passwd/sha2/slapd-pw-sha2.5 b/contrib/slapd-modules/passwd/sha2/slapd-pw-sha2.5
new file mode 100644
index 0000000..f700b52
--- /dev/null
+++ b/contrib/slapd-modules/passwd/sha2/slapd-pw-sha2.5
@@ -0,0 +1,118 @@
+.TH SLAPD-PW-SHA2 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2015-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd-pw-sha2 \- SHA-2 password module to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.RS
+.LP
+.B moduleload
+.B pw-sha2
+.RE
+.SH DESCRIPTION
+.LP
+The
+.B pw-sha2
+module to
+.BR slapd (8)
+provides support for the use of SSHA-512, SSHA-384, SSHA-256, SHA-512,
+SHA-384 and SHA-256 from the SHA-2 family (FIPS 180-2) of hash functions
+in hashed passwords in OpenLDAP.
+.LP
+It does so by providing the following additional password schemes for use in slapd:
+.RS
+.TP
+.B {SSHA256}
+SHA-256 with salt, giving hash values of 256 bits length
+.TP
+.B {SHA256}
+plain SHA-256 giving hash values of 256 bits length
+.TP
+.B {SSHA384}
+SHA-384 with salt, giving hash values of 384 bits length
+.TP
+.B {SHA384}
+plain SHA-384 giving hash values of 384 bits length
+.TP
+.B {SSHA512}
+SHA-512 with salt, giving hash values of 512 bits length
+.TP
+.B {SHA512}
+plain SHA-512 giving hash values of 512 bits length
+.RE
+
+.SH CONFIGURATION
+The
+.B pw-sha2
+module does not need any configuration.
+.LP
+After loading the module, the password schemes
+{SSHA256}, {SSHA384}, {SSHA512}, {SSHA256}, {SHA384}, and {SHA512}
+will be recognised in values of the
+.I userPassword
+attribute.
+.LP
+You can then instruct OpenLDAP to use these schemes when processing
+the LDAPv3 Password Modify (RFC 3062) extended operations by using the
+.BR password-hash
+option in
+.BR slapd.conf (5).
+
+.SH NOTES
+If you want to use the schemes described here with
+.BR slappasswd (8),
+don't forget to load the module using its command line options.
+The relevant option/value is:
+.RS
+.LP
+.B \-o
+.BR module\-load = pw-sha2
+.LP
+.RE
+Depending on
+.BR pw-sha2 's
+location, you may also need:
+.RS
+.LP
+.B \-o
+.BR module\-path = \fIpathspec\fP
+.RE
+
+.SH EXAMPLES
+All of the userPassword LDAP attributes below encode the password
+.RI ' secret '.
+.EX
+.LP
+userPassword: {SHA512}vSsar3708Jvp9Szi2NWZZ02Bqp1qRCFpbcTZPdBhnWgs5WtNZKnvCXdhztmeD2cmW192CF5bDufKRpayrW/isg==
+.LP
+userPassword: {SHA384}WKd1ukESvjAFrkQHznV9iP2nHUBJe7gCbsrFTU4//HIyzo3jq1rLMK45dg/ufFPt
+.LP
+userPassword: {SHA256}K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=
+.EE
+.LP
+To make {SSHA512} the password hash used in Password Modify extended operations,
+simply set this line in slapd.conf(5):
+.EX
+.LP
+password-hash {SSHA512}
+.EX
+
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR ldappasswd (1),
+.BR slappasswd (8),
+.BR ldap (3),
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.LP
+
+.SH ACKNOWLEDGEMENTS
+This manual page has been written by Peter Marschall based on the
+module's README file written by Jeff Turner.
+.LP
+.B OpenLDAP
+is developed and maintained by The OpenLDAP Project (http://www.openldap.org/).
+.B OpenLDAP
+is derived from University of Michigan LDAP 3.3 Release.
diff --git a/contrib/slapd-modules/passwd/sha2/slapd-sha2.c b/contrib/slapd-modules/passwd/sha2/slapd-sha2.c
new file mode 100644
index 0000000..d67afda
--- /dev/null
+++ b/contrib/slapd-modules/passwd/sha2/slapd-sha2.c
@@ -0,0 +1,508 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2009-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>.
+ */
+/* ACKNOWLEDGEMENT:
+ * This work was initially developed by Jeff Turner for inclusion
+ * in OpenLDAP Software.
+ *
+ * Hash methods for passwords generation added by Cédric Delfosse.
+ *
+ * SSHA256 / SSHA384 / SSHA512 support added, and chk_sha*() replaced
+ * with libraries/liblutil/passwd.c:chk_sha1() implementation to
+ * fix a race by SATOH Fumiyasu @ OSS Technology, Inc.
+ */
+
+#include "portable.h"
+
+#include <ac/string.h>
+
+#include "lber_pvt.h"
+#include "lutil.h"
+#include "sha2.h"
+
+#ifdef SLAPD_SHA2_DEBUG
+#include <stdio.h>
+#endif
+
+#define SHA2_SALT_SIZE 8
+
+static int hash_ssha256(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ SHA256_CTX ct;
+ unsigned char hash256[SHA256_DIGEST_LENGTH];
+ char saltdata[SHA2_SALT_SIZE];
+ struct berval digest;
+ struct berval salt;
+
+ digest.bv_val = (char *) hash256;
+ digest.bv_len = sizeof(hash256);
+ salt.bv_val = saltdata;
+ salt.bv_len = sizeof(saltdata);
+
+ if (lutil_entropy((unsigned char *)salt.bv_val, salt.bv_len) < 0) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ SHA256_Init(&ct);
+ SHA256_Update(&ct, (const uint8_t*)passwd->bv_val, passwd->bv_len);
+ SHA256_Update(&ct, (const uint8_t*)salt.bv_val, salt.bv_len);
+ SHA256_Final(hash256, &ct);
+
+ return lutil_passwd_string64(scheme, &digest, hash, &salt);
+}
+
+static int hash_sha256(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ SHA256_CTX ct;
+ unsigned char hash256[SHA256_DIGEST_LENGTH];
+ struct berval digest;
+ digest.bv_val = (char *) hash256;
+ digest.bv_len = sizeof(hash256);
+
+ SHA256_Init(&ct);
+ SHA256_Update(&ct, (const uint8_t*)passwd->bv_val, passwd->bv_len);
+ SHA256_Final(hash256, &ct);
+
+ return lutil_passwd_string64(scheme, &digest, hash, NULL);
+}
+
+static int hash_ssha384(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ SHA384_CTX ct;
+ unsigned char hash384[SHA384_DIGEST_LENGTH];
+ char saltdata[SHA2_SALT_SIZE];
+ struct berval digest;
+ struct berval salt;
+
+ digest.bv_val = (char *) hash384;
+ digest.bv_len = sizeof(hash384);
+ salt.bv_val = saltdata;
+ salt.bv_len = sizeof(saltdata);
+
+ if (lutil_entropy((unsigned char *)salt.bv_val, salt.bv_len) < 0) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ SHA384_Init(&ct);
+ SHA384_Update(&ct, (const uint8_t*)passwd->bv_val, passwd->bv_len);
+ SHA384_Update(&ct, (const uint8_t*)salt.bv_val, salt.bv_len);
+ SHA384_Final(hash384, &ct);
+
+ return lutil_passwd_string64(scheme, &digest, hash, &salt);
+}
+
+static int hash_sha384(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ SHA384_CTX ct;
+ unsigned char hash384[SHA384_DIGEST_LENGTH];
+ struct berval digest;
+ digest.bv_val = (char *) hash384;
+ digest.bv_len = sizeof(hash384);
+
+ SHA384_Init(&ct);
+ SHA384_Update(&ct, (const uint8_t*)passwd->bv_val, passwd->bv_len);
+ SHA384_Final(hash384, &ct);
+
+ return lutil_passwd_string64(scheme, &digest, hash, NULL);
+}
+
+static int hash_ssha512(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ SHA512_CTX ct;
+ unsigned char hash512[SHA512_DIGEST_LENGTH];
+ char saltdata[SHA2_SALT_SIZE];
+ struct berval digest;
+ struct berval salt;
+
+ digest.bv_val = (char *) hash512;
+ digest.bv_len = sizeof(hash512);
+ salt.bv_val = saltdata;
+ salt.bv_len = sizeof(saltdata);
+
+ if (lutil_entropy((unsigned char *)salt.bv_val, salt.bv_len) < 0) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ SHA512_Init(&ct);
+ SHA512_Update(&ct, (const uint8_t*)passwd->bv_val, passwd->bv_len);
+ SHA512_Update(&ct, (const uint8_t*)salt.bv_val, salt.bv_len);
+ SHA512_Final(hash512, &ct);
+
+ return lutil_passwd_string64(scheme, &digest, hash, &salt);
+}
+
+static int hash_sha512(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text )
+{
+ SHA512_CTX ct;
+ unsigned char hash512[SHA512_DIGEST_LENGTH];
+ struct berval digest;
+ digest.bv_val = (char *) hash512;
+ digest.bv_len = sizeof(hash512);
+
+ SHA512_Init(&ct);
+ SHA512_Update(&ct, (const uint8_t*)passwd->bv_val, passwd->bv_len);
+ SHA512_Final(hash512, &ct);
+
+ return lutil_passwd_string64(scheme, &digest, hash, NULL);
+}
+
+#ifdef SLAPD_SHA2_DEBUG
+static void chk_sha_debug(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char *cred_hash,
+ size_t cred_len,
+ int cmp_rc)
+{
+ int rc;
+ struct berval cred_b64;
+
+ cred_b64.bv_len = LUTIL_BASE64_ENCODE_LEN(cred_len) + 1;
+ cred_b64.bv_val = ber_memalloc(cred_b64.bv_len + 1);
+
+ if( cred_b64.bv_val == NULL ) {
+ return;
+ }
+
+ rc = lutil_b64_ntop(
+ (unsigned char *) cred_hash, cred_len,
+ cred_b64.bv_val, cred_b64.bv_len );
+
+ if( rc < 0 ) {
+ ber_memfree(cred_b64.bv_val);
+ return;
+ }
+
+ fprintf(stderr, "Validating password\n");
+ fprintf(stderr, " Hash scheme:\t\t%s\n", scheme->bv_val);
+ fprintf(stderr, " Password to validate: %s\n", cred->bv_val);
+ fprintf(stderr, " Password hash:\t%s\n", cred_b64.bv_val);
+ fprintf(stderr, " Stored password hash:\t%s\n", passwd->bv_val);
+ fprintf(stderr, " Result:\t\t%s\n", cmp_rc ? "do not match" : "match");
+
+ ber_memfree(cred_b64.bv_val);
+}
+#endif
+
+static int chk_ssha256(
+ const struct berval *scheme, /* Scheme of hashed reference password */
+ const struct berval *passwd, /* Hashed reference password to check against */
+ const struct berval *cred, /* user-supplied password to check */
+ const char **text )
+{
+ SHA256_CTX SHAcontext;
+ unsigned char SHAdigest[SHA256_DIGEST_LENGTH];
+ int rc;
+ unsigned char *orig_pass = NULL;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check */
+ if (decode_len <= sizeof(SHAdigest)) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* base64 un-encode password */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if( orig_pass == NULL ) return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+
+ if( rc <= (int)(sizeof(SHAdigest)) ) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ SHA256_Init(&SHAcontext);
+ SHA256_Update(&SHAcontext,
+ (const unsigned char *) cred->bv_val, cred->bv_len);
+ SHA256_Update(&SHAcontext,
+ (const unsigned char *) &orig_pass[sizeof(SHAdigest)],
+ rc - sizeof(SHAdigest));
+ SHA256_Final(SHAdigest, &SHAcontext);
+
+ /* compare */
+ rc = memcmp((char *)orig_pass, (char *)SHAdigest, sizeof(SHAdigest));
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+static int chk_sha256(
+ const struct berval *scheme, /* Scheme of hashed reference password */
+ const struct berval *passwd, /* Hashed reference password to check against */
+ const struct berval *cred, /* user-supplied password to check */
+ const char **text )
+{
+ SHA256_CTX SHAcontext;
+ unsigned char SHAdigest[SHA256_DIGEST_LENGTH];
+ int rc;
+ unsigned char *orig_pass = NULL;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check */
+ if (decode_len < sizeof(SHAdigest)) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* base64 un-encode password */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if( orig_pass == NULL ) return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+
+ if( rc != sizeof(SHAdigest) ) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ SHA256_Init(&SHAcontext);
+ SHA256_Update(&SHAcontext,
+ (const unsigned char *) cred->bv_val, cred->bv_len);
+ SHA256_Final(SHAdigest, &SHAcontext);
+
+ /* compare */
+ rc = memcmp((char *)orig_pass, (char *)SHAdigest, sizeof(SHAdigest));
+#ifdef SLAPD_SHA2_DEBUG
+ chk_sha_debug(scheme, passwd, cred, (char *)SHAdigest, sizeof(SHAdigest), rc);
+#endif
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+static int chk_ssha384(
+ const struct berval *scheme, /* Scheme of hashed reference password */
+ const struct berval *passwd, /* Hashed reference password to check against */
+ const struct berval *cred, /* user-supplied password to check */
+ const char **text )
+{
+ SHA384_CTX SHAcontext;
+ unsigned char SHAdigest[SHA384_DIGEST_LENGTH];
+ int rc;
+ unsigned char *orig_pass = NULL;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check */
+ if (decode_len <= sizeof(SHAdigest)) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* base64 un-encode password */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if( orig_pass == NULL ) return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+
+ if( rc <= (int)(sizeof(SHAdigest)) ) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ SHA384_Init(&SHAcontext);
+ SHA384_Update(&SHAcontext,
+ (const unsigned char *) cred->bv_val, cred->bv_len);
+ SHA384_Update(&SHAcontext,
+ (const unsigned char *) &orig_pass[sizeof(SHAdigest)],
+ rc - sizeof(SHAdigest));
+ SHA384_Final(SHAdigest, &SHAcontext);
+
+ /* compare */
+ rc = memcmp((char *)orig_pass, (char *)SHAdigest, sizeof(SHAdigest));
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+static int chk_sha384(
+ const struct berval *scheme, /* Scheme of hashed reference password */
+ const struct berval *passwd, /* Hashed reference password to check against */
+ const struct berval *cred, /* user-supplied password to check */
+ const char **text )
+{
+ SHA384_CTX SHAcontext;
+ unsigned char SHAdigest[SHA384_DIGEST_LENGTH];
+ int rc;
+ unsigned char *orig_pass = NULL;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check */
+ if (decode_len < sizeof(SHAdigest)) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* base64 un-encode password */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if( orig_pass == NULL ) return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+
+ if( rc != sizeof(SHAdigest) ) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ SHA384_Init(&SHAcontext);
+ SHA384_Update(&SHAcontext,
+ (const unsigned char *) cred->bv_val, cred->bv_len);
+ SHA384_Final(SHAdigest, &SHAcontext);
+
+ /* compare */
+ rc = memcmp((char *)orig_pass, (char *)SHAdigest, sizeof(SHAdigest));
+#ifdef SLAPD_SHA2_DEBUG
+ chk_sha_debug(scheme, passwd, cred, (char *)SHAdigest, sizeof(SHAdigest), rc);
+#endif
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+static int chk_ssha512(
+ const struct berval *scheme, /* Scheme of hashed reference password */
+ const struct berval *passwd, /* Hashed reference password to check against */
+ const struct berval *cred, /* user-supplied password to check */
+ const char **text )
+{
+ SHA512_CTX SHAcontext;
+ unsigned char SHAdigest[SHA512_DIGEST_LENGTH];
+ int rc;
+ unsigned char *orig_pass = NULL;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check */
+ if (decode_len <= sizeof(SHAdigest)) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* base64 un-encode password */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if( orig_pass == NULL ) return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+
+ if( rc <= (int)(sizeof(SHAdigest)) ) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ SHA512_Init(&SHAcontext);
+ SHA512_Update(&SHAcontext,
+ (const unsigned char *) cred->bv_val, cred->bv_len);
+ SHA512_Update(&SHAcontext,
+ (const unsigned char *) &orig_pass[sizeof(SHAdigest)],
+ rc - sizeof(SHAdigest));
+ SHA512_Final(SHAdigest, &SHAcontext);
+
+ /* compare */
+ rc = memcmp((char *)orig_pass, (char *)SHAdigest, sizeof(SHAdigest));
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+static int chk_sha512(
+ const struct berval *scheme, /* Scheme of hashed reference password */
+ const struct berval *passwd, /* Hashed reference password to check against */
+ const struct berval *cred, /* user-supplied password to check */
+ const char **text )
+{
+ SHA512_CTX SHAcontext;
+ unsigned char SHAdigest[SHA512_DIGEST_LENGTH];
+ int rc;
+ unsigned char *orig_pass = NULL;
+ size_t decode_len = LUTIL_BASE64_DECODE_LEN(passwd->bv_len);
+
+ /* safety check */
+ if (decode_len < sizeof(SHAdigest)) {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* base64 un-encode password */
+ orig_pass = (unsigned char *) ber_memalloc(decode_len + 1);
+
+ if( orig_pass == NULL ) return LUTIL_PASSWD_ERR;
+
+ rc = lutil_b64_pton(passwd->bv_val, orig_pass, decode_len);
+
+ if( rc != sizeof(SHAdigest) ) {
+ ber_memfree(orig_pass);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* hash credentials with salt */
+ SHA512_Init(&SHAcontext);
+ SHA512_Update(&SHAcontext,
+ (const unsigned char *) cred->bv_val, cred->bv_len);
+ SHA512_Final(SHAdigest, &SHAcontext);
+
+ /* compare */
+ rc = memcmp((char *)orig_pass, (char *)SHAdigest, sizeof(SHAdigest));
+#ifdef SLAPD_SHA2_DEBUG
+ chk_sha_debug(scheme, passwd, cred, (char *)SHAdigest, sizeof(SHAdigest), rc);
+#endif
+ ber_memfree(orig_pass);
+ return rc ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+}
+
+const struct berval ssha256scheme = BER_BVC("{SSHA256}");
+const struct berval sha256scheme = BER_BVC("{SHA256}");
+const struct berval ssha384scheme = BER_BVC("{SSHA384}");
+const struct berval sha384scheme = BER_BVC("{SHA384}");
+const struct berval ssha512scheme = BER_BVC("{SSHA512}");
+const struct berval sha512scheme = BER_BVC("{SHA512}");
+
+int init_module(int argc, char *argv[]) {
+ int result = 0;
+ result = lutil_passwd_add( (struct berval *)&ssha256scheme, chk_ssha256, hash_ssha256 );
+ if (result != 0) return result;
+ result = lutil_passwd_add( (struct berval *)&sha256scheme, chk_sha256, hash_sha256 );
+ if (result != 0) return result;
+ result = lutil_passwd_add( (struct berval *)&ssha384scheme, chk_ssha384, hash_ssha384 );
+ if (result != 0) return result;
+ result = lutil_passwd_add( (struct berval *)&sha384scheme, chk_sha384, hash_sha384 );
+ if (result != 0) return result;
+ result = lutil_passwd_add( (struct berval *)&ssha512scheme, chk_ssha512, hash_ssha512 );
+ if (result != 0) return result;
+ result = lutil_passwd_add( (struct berval *)&sha512scheme, chk_sha512, hash_sha512 );
+ return result;
+}
diff --git a/contrib/slapd-modules/passwd/slapd-pw-radius.5 b/contrib/slapd-modules/passwd/slapd-pw-radius.5
new file mode 100644
index 0000000..9a74847
--- /dev/null
+++ b/contrib/slapd-modules/passwd/slapd-pw-radius.5
@@ -0,0 +1,110 @@
+.TH SLAPD-PW-RADIUS 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2015-2022 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd-pw-radius \- Radius backend password module to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.RS
+.LP
+.B moduleload
+.B pw-radius
+.I /path/to/radius.conf
+.RE
+.SH DESCRIPTION
+.LP
+The
+.B pw-radius
+module to
+.BR slapd (8)
+provides support for using a RADIUS infrastructure as backend to
+verify the password provided in Simple Bind operations to OpenLDAP.
+.LP
+It does so by providing an additional password scheme for use in slapd:
+.RS
+.TP
+.B {RADIUS}
+RADIUS password scheme
+.RE
+.LP
+Unlike in other password schemes, the value following the scheme is not
+a - potentially hashed - password, but the name of the corresponding
+RADIUS user in the RADIUS infrastructure.
+.LP
+This value, together with the password used in the Simple Bind operation,
+will be sent to the RADIUS server for authentication.
+.LP
+If the RADIUS server successfully authenticates the user,
+then the password verification succeeds, resulting in the LDAP Bind
+operation's success.
+.LP
+Conversely, failed RADIUS authentications leads to failing LDAP Binds.
+
+.SH CONFIGURATION
+The
+.B pw-radius
+module needs no configuration beyond the additional
+.I filename
+argument to
+.BR slapd.conf (5)'s
+.B moduleload
+directive.
+This filename is expected to point to a valid
+.BR radius.conf (5).
+file adhering to
+.BR libradius (3).
+.LP
+After loading the module, the password scheme
+.B {RADIUS}
+will be recognised in values of the
+.I userPassword
+attribute.
+
+.SH NOTES
+Owing to its construction, using the
+.B {RADIUS}
+scheme as argument to the
+.BR password-hash
+option in
+.BR slapd.conf (5)
+does not make much sense, because of the scheme's construction.
+.LP
+This also applies to the use of the
+.B {RADIUS}
+scheme in
+.B slappasswd
+or
+.BR ldappasswd .
+
+
+.SH EXAMPLES
+To indicate that Simple Bind operations shall use the RADIUS user
+.B johndoe
+when validating passwords against the RADIUS infrastructure,
+set a user's LDAP attribute userPassword to:
+.EX
+.LP
+userPassword: {RADIUS}johndoe
+.EE
+
+.SH LIMITATIONS
+Due to the way the configuration is loaded (additional argument
+to slapd.conf's moduleload directive), this module cannot be used
+with table-driven configuration.
+
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR libradius (3)
+.BR ldap (3),
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.LP
+
+.SH ACKNOWLEDGEMENTS
+This manual page has been written by Peter Marschall.
+.LP
+.B OpenLDAP
+is developed and maintained by The OpenLDAP Project (http://www.openldap.org/).
+.B OpenLDAP
+is derived from University of Michigan LDAP 3.3 Release.
diff --git a/contrib/slapd-modules/passwd/totp/Makefile b/contrib/slapd-modules/passwd/totp/Makefile
new file mode 100644
index 0000000..f7dff4b
--- /dev/null
+++ b/contrib/slapd-modules/passwd/totp/Makefile
@@ -0,0 +1,58 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+ $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS =
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = pw-totp.la
+MANPAGES = slapo-totp.5
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+.SUFFIXES: .c .o .lo
+
+.c.lo:
+ $(LIBTOOL) --mode=compile $(CC) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+pw-totp.la: slapd-totp.lo
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $? $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/passwd/totp/README b/contrib/slapd-modules/passwd/totp/README
new file mode 100644
index 0000000..e6867f2
--- /dev/null
+++ b/contrib/slapd-modules/passwd/totp/README
@@ -0,0 +1,87 @@
+TOTP OpenLDAP support
+----------------------
+
+slapd-totp.c provides support for RFC 6238 TOTP Time-based One
+Time Passwords in OpenLDAP using SHA-1, SHA-256, and SHA-512.
+For instance, one could have the LDAP attribute:
+
+userPassword: {TOTP1}GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ
+
+which encodes the key '12345678901234567890'.
+
+It can also encode credentials consisting of a TOTP and a static
+password. The format for this is:
+
+userPassword: {TOTP1ANDPW}GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ|<some_other_passwd>
+
+where <some_other_passwd> can be any scheme currently understood
+by OpenLDAP. For example, using '{SHA}5en6G6MezRroT3XKqkdPOmY/BfQ='
+would encode the above TOTP with a static password of 'secret'. To
+authenticate using this scheme, enter the static password immediately
+followed by the TOTP, for example 'secret123456'.
+
+
+Building
+--------
+
+1) Customize the LDAP_SRC variable in Makefile to point to the OpenLDAP
+source root.
+
+2) Run 'make' to produce slapd-totp.so
+
+3) Copy slapd-totp.so somewhere permanent.
+
+4) Edit your slapd.conf (eg. /etc/ldap/slapd.conf), and add:
+
+moduleload ...path/to/slapd-totp.so
+
+5) This module replaces the function of the slapo-lastbind overlay. You
+cannot use that overlay on the same database as this one.
+
+6) Restart slapd.
+
+
+Configuring
+-----------
+
+The {TOTP1}, {TOTP256}, {TOTP512}, {TOTP1ANDPW}, {TOTP256ANDPW},
+and {TOTP512ANDPW} password schemes should now be recognised.
+
+You can also tell OpenLDAP to use one of these new schemes when processing LDAP
+Password Modify Extended Operations, thanks to the password-hash option in
+slapd.conf. For example:
+
+password-hash {TOTP1}
+
+TOTP password schemes will only work on databases that have a rootdn and the
+totp overlay configured:
+
+database mdb
+rootdn "..."
+...
+
+overlay totp
+
+
+
+Testing
+-------
+
+The TOTP1 algorithm is compatible with Google Authenticator.
+
+---
+
+This work is part of OpenLDAP Software <http://www.openldap.org/>.
+
+Copyright 2015-2022 The OpenLDAP Foundation.
+Portions Copyright 2015 by Howard Chu, Symas Corp.
+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/contrib/slapd-modules/passwd/totp/slapd-totp.c b/contrib/slapd-modules/passwd/totp/slapd-totp.c
new file mode 100644
index 0000000..25081e1
--- /dev/null
+++ b/contrib/slapd-modules/passwd/totp/slapd-totp.c
@@ -0,0 +1,1000 @@
+/* slapd-totp.c - Password module and overlay for TOTP */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2015-2022 The OpenLDAP Foundation.
+ * Portions Copyright 2015 by Howard Chu, Symas Corp.
+ * 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>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work includes code from the lastbind overlay.
+ */
+
+#include <portable.h>
+
+#if HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+#include <lber.h>
+#include <lber_pvt.h>
+#include "lutil.h"
+#include <ac/stdlib.h>
+#include <ac/ctype.h>
+#include <ac/string.h>
+/* include socket.h to get sys/types.h and/or winsock2.h */
+#include <ac/socket.h>
+
+#if HAVE_OPENSSL
+#include <openssl/sha.h>
+#include <openssl/hmac.h>
+
+#define TOTP_SHA512_DIGEST_LENGTH SHA512_DIGEST_LENGTH
+#define TOTP_SHA1 EVP_sha1()
+#define TOTP_SHA256 EVP_sha256()
+#define TOTP_SHA512 EVP_sha512()
+#define TOTP_HMAC_CTX HMAC_CTX *
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+static HMAC_CTX *HMAC_CTX_new(void)
+{
+ HMAC_CTX *ctx = OPENSSL_malloc(sizeof(*ctx));
+ if (ctx != NULL) {
+ HMAC_CTX_init(ctx);
+ }
+ return ctx;
+}
+
+static void HMAC_CTX_free(HMAC_CTX *ctx)
+{
+ if (ctx != NULL) {
+ HMAC_CTX_cleanup(ctx);
+ OPENSSL_free(ctx);
+ }
+}
+#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
+
+#define HMAC_setup(ctx, key, len, hash) \
+ ctx = HMAC_CTX_new(); \
+ HMAC_Init_ex(ctx, key, len, hash, 0)
+#define HMAC_crunch(ctx, buf, len) HMAC_Update(ctx, buf, len)
+#define HMAC_finish(ctx, dig, dlen) \
+ HMAC_Final(ctx, dig, &dlen); \
+ HMAC_CTX_free(ctx)
+
+#elif HAVE_GNUTLS
+#include <nettle/hmac.h>
+
+#define TOTP_SHA512_DIGEST_LENGTH SHA512_DIGEST_SIZE
+#define TOTP_SHA1 &nettle_sha1
+#define TOTP_SHA256 &nettle_sha256
+#define TOTP_SHA512 &nettle_sha512
+#define TOTP_HMAC_CTX struct hmac_sha512_ctx
+
+#define HMAC_setup(ctx, key, len, hash) \
+ const struct nettle_hash *h=hash;\
+ hmac_set_key(&ctx.outer, &ctx.inner, &ctx.state, h, len, key)
+#define HMAC_crunch(ctx, buf, len) hmac_update(&ctx.state, h, len, buf)
+#define HMAC_finish(ctx, dig, dlen) \
+ hmac_digest(&ctx.outer, &ctx.inner, &ctx.state, h, h->digest_size, dig);\
+ dlen = h->digest_size
+
+#else
+# error Unsupported crypto backend.
+#endif
+
+#include "slap.h"
+#include "slap-config.h"
+
+static LUTIL_PASSWD_CHK_FUNC chk_totp1, chk_totp256, chk_totp512,
+ chk_totp1andpw, chk_totp256andpw, chk_totp512andpw;
+static LUTIL_PASSWD_HASH_FUNC hash_totp1, hash_totp256, hash_totp512,
+ hash_totp1andpw, hash_totp256andpw, hash_totp512andpw;
+static const struct berval scheme_totp1 = BER_BVC("{TOTP1}");
+static const struct berval scheme_totp256 = BER_BVC("{TOTP256}");
+static const struct berval scheme_totp512 = BER_BVC("{TOTP512}");
+static const struct berval scheme_totp1andpw = BER_BVC("{TOTP1ANDPW}");
+static const struct berval scheme_totp256andpw = BER_BVC("{TOTP256ANDPW}");
+static const struct berval scheme_totp512andpw = BER_BVC("{TOTP512ANDPW}");
+
+static AttributeDescription *ad_authTimestamp;
+
+/* This is the definition used by ISODE, as supplied to us in
+ * ITS#6238 Followup #9
+ */
+static struct schema_info {
+ char *def;
+ AttributeDescription **ad;
+} totp_OpSchema[] = {
+ { "( 1.3.6.1.4.1.453.16.2.188 "
+ "NAME 'authTimestamp' "
+ "DESC 'last successful authentication using any method/mech' "
+ "EQUALITY generalizedTimeMatch "
+ "ORDERING generalizedTimeOrderingMatch "
+ "SYNTAX 1.3.6.1.4.1.1466.115.121.1.24 "
+ "SINGLE-VALUE NO-USER-MODIFICATION USAGE dsaOperation )",
+ &ad_authTimestamp},
+ { NULL, NULL }
+};
+
+/* RFC3548 base32 encoding/decoding */
+
+static const char Base32[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
+static const char Pad32 = '=';
+
+static int
+totp_b32_ntop(
+ u_char const *src,
+ size_t srclength,
+ char *target,
+ size_t targsize)
+{
+ size_t datalength = 0;
+ u_char input0;
+ u_int input1; /* assumed to be at least 32 bits */
+ u_char output[8];
+ int i;
+
+ while (4 < srclength) {
+ if (datalength + 8 > targsize)
+ return (-1);
+ input0 = *src++;
+ input1 = *src++;
+ input1 <<= 8;
+ input1 |= *src++;
+ input1 <<= 8;
+ input1 |= *src++;
+ input1 <<= 8;
+ input1 |= *src++;
+ srclength -= 5;
+
+ for (i=7; i>1; i--) {
+ output[i] = input1 & 0x1f;
+ input1 >>= 5;
+ }
+ output[0] = input0 >> 3;
+ output[1] = (input0 & 0x07) << 2 | input1;
+
+ for (i=0; i<8; i++)
+ target[datalength++] = Base32[output[i]];
+ }
+
+ /* Now we worry about padding. */
+ if (0 != srclength) {
+ static const int outlen[] = { 2,4,5,7 };
+ int n;
+ if (datalength + 8 > targsize)
+ return (-1);
+
+ /* Get what's left. */
+ input1 = *src++;
+ for (i = 1; i < srclength; i++) {
+ input1 <<= 8;
+ input1 |= *src++;
+ }
+ input1 <<= 8 * (4-srclength);
+ n = outlen[srclength-1];
+ for (i=0; i<n; i++) {
+ target[datalength++] = Base32[(input1 & 0xf8000000) >> 27];
+ input1 <<= 5;
+ }
+ for (; i<8; i++)
+ target[datalength++] = Pad32;
+ }
+ if (datalength >= targsize)
+ return (-1);
+ target[datalength] = '\0'; /* Returned value doesn't count \0. */
+ return (datalength);
+}
+
+/* converts characters, eight at a time, starting at src
+ from base - 32 numbers into five 8 bit bytes in the target area.
+ it returns the number of data bytes stored at the target, or -1 on error.
+ */
+
+static int
+totp_b32_pton(
+ char const *src,
+ u_char *target,
+ size_t targsize)
+{
+ int tarindex, state, ch;
+ char *pos;
+
+ state = 0;
+ tarindex = 0;
+
+ while ((ch = *src++) != '\0') {
+ if (ch == Pad32)
+ break;
+
+ pos = strchr(Base32, ch);
+ if (pos == 0) /* A non-base32 character. */
+ return (-1);
+
+ switch (state) {
+ case 0:
+ if (target) {
+ if ((size_t)tarindex >= targsize)
+ return (-1);
+ target[tarindex] = (pos - Base32) << 3;
+ }
+ state = 1;
+ break;
+ case 1:
+ if (target) {
+ if ((size_t)tarindex + 1 >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base32) >> 2;
+ target[tarindex+1] = ((pos - Base32) & 0x3)
+ << 6 ;
+ }
+ tarindex++;
+ state = 2;
+ break;
+ case 2:
+ if (target) {
+ target[tarindex] |= (pos - Base32) << 1;
+ }
+ state = 3;
+ break;
+ case 3:
+ if (target) {
+ if ((size_t)tarindex + 1 >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base32) >> 4;
+ target[tarindex+1] = ((pos - Base32) & 0xf)
+ << 4 ;
+ }
+ tarindex++;
+ state = 4;
+ break;
+ case 4:
+ if (target) {
+ if ((size_t)tarindex + 1 >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base32) >> 1;
+ target[tarindex+1] = ((pos - Base32) & 0x1)
+ << 7 ;
+ }
+ tarindex++;
+ state = 5;
+ break;
+ case 5:
+ if (target) {
+ target[tarindex] |= (pos - Base32) << 2;
+ }
+ state = 6;
+ break;
+ case 6:
+ if (target) {
+ if ((size_t)tarindex + 1 >= targsize)
+ return (-1);
+ target[tarindex] |= (pos - Base32) >> 3;
+ target[tarindex+1] = ((pos - Base32) & 0x7)
+ << 5 ;
+ }
+ tarindex++;
+ state = 7;
+ break;
+ case 7:
+ if (target) {
+ target[tarindex] |= (pos - Base32);
+ }
+ state = 0;
+ tarindex++;
+ break;
+
+ default:
+ abort();
+ }
+ }
+
+ /*
+ * We are done decoding Base-32 chars. Let's see if we ended
+ * on a byte boundary, and/or with erroneous trailing characters.
+ */
+
+ if (ch == Pad32) { /* We got a pad char. */
+ int i = 0;
+
+ /* count pad chars */
+ for (; ch; ch = *src++) {
+ if (ch != Pad32)
+ return (-1);
+ i++;
+ }
+ /* there are only 4 valid ending states with a
+ * pad character, make sure the number of pads is valid.
+ */
+ switch(state) {
+ case 2: if (i != 6) return -1;
+ break;
+ case 4: if (i != 4) return -1;
+ break;
+ case 5: if (i != 3) return -1;
+ break;
+ case 7: if (i != 1) return -1;
+ break;
+ default:
+ return -1;
+ }
+ /*
+ * Now make sure that the "extra" bits that slopped past
+ * the last full byte were zeros. If we don't check them,
+ * they become a subliminal channel.
+ */
+ if (target && target[tarindex] != 0)
+ return (-1);
+ } else {
+ /*
+ * We ended by seeing the end of the string. Make sure we
+ * have no partial bytes lying around.
+ */
+ if (state != 0)
+ return (-1);
+ }
+
+ return (tarindex);
+}
+
+/* RFC6238 TOTP */
+
+
+typedef struct myval {
+ ber_len_t mv_len;
+ void *mv_val;
+} myval;
+
+static void do_hmac(
+ const void *hash,
+ myval *key,
+ myval *data,
+ myval *out)
+{
+ TOTP_HMAC_CTX ctx;
+ unsigned int digestLen;
+
+ HMAC_setup(ctx, key->mv_val, key->mv_len, hash);
+ HMAC_crunch(ctx, data->mv_val, data->mv_len);
+ HMAC_finish(ctx, out->mv_val, digestLen);
+ out->mv_len = digestLen;
+}
+
+static const int DIGITS_POWER[] = {
+ 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
+
+static void generate(
+ myval *key,
+ uint64_t tval,
+ int digits,
+ myval *out,
+ const void *mech)
+{
+ unsigned char digest[TOTP_SHA512_DIGEST_LENGTH];
+ myval digval;
+ myval data;
+ unsigned char msg[8];
+ int i, offset, res, otp;
+
+#if WORDS_BIGENDIAN
+ *(uint64_t *)msg = tval;
+#else
+ for (i=7; i>=0; i--) {
+ msg[i] = tval & 0xff;
+ tval >>= 8;
+ }
+#endif
+
+ data.mv_val = msg;
+ data.mv_len = sizeof(msg);
+
+ digval.mv_val = digest;
+ digval.mv_len = sizeof(digest);
+ do_hmac(mech, key, &data, &digval);
+
+ offset = digest[digval.mv_len-1] & 0xf;
+ res = ((digest[offset] & 0x7f) << 24) |
+ ((digest[offset+1] & 0xff) << 16) |
+ ((digest[offset+2] & 0xff) << 8) |
+ (digest[offset+3] & 0xff);
+
+ otp = res % DIGITS_POWER[digits];
+ out->mv_len = snprintf(out->mv_val, out->mv_len, "%0*d", digits, otp);
+}
+
+static int totp_op_cleanup( Operation *op, SlapReply *rs );
+static int totp_bind_response( Operation *op, SlapReply *rs );
+
+#define TIME_STEP 30
+#define DIGITS 6
+#define DELIM '|' /* a single character */
+#define TOTP_AND_PW_HASH_SCHEME "{SSHA}"
+
+static int chk_totp(
+ const struct berval *passwd,
+ const struct berval *cred,
+ const void *mech,
+ const char **text)
+{
+ void *ctx, *op_tmp;
+ Operation *op;
+ Entry *e;
+ Attribute *a;
+ long t, told = 0;
+ int rc;
+ myval out, key;
+ char outbuf[32];
+
+ /* Find our thread context, find our Operation */
+ ctx = ldap_pvt_thread_pool_context();
+ if (ldap_pvt_thread_pool_getkey(ctx, totp_op_cleanup, &op_tmp, NULL) ||
+ !op_tmp)
+ return LUTIL_PASSWD_ERR;
+ op = op_tmp;
+
+ rc = be_entry_get_rw(op, &op->o_req_ndn, NULL, NULL, 0, &e);
+ if (rc != LDAP_SUCCESS) return LUTIL_PASSWD_ERR;
+
+ /* Make sure previous login is older than current time */
+ t = op->o_time / TIME_STEP;
+ a = attr_find(e->e_attrs, ad_authTimestamp);
+ if (a) {
+ struct lutil_tm tm;
+ struct lutil_timet tt;
+ if (lutil_parsetime(a->a_vals[0].bv_val, &tm) == 0 &&
+ lutil_tm2time(&tm, &tt) == 0) {
+ told = tt.tt_sec / TIME_STEP;
+ if (told >= t)
+ rc = LUTIL_PASSWD_ERR;
+ }
+ if (!rc) { /* seems OK, remember old stamp */
+ slap_callback *sc;
+ for (sc = op->o_callback; sc; sc = sc->sc_next) {
+ if (sc->sc_response == totp_bind_response) {
+ sc->sc_private = ber_dupbv_x(NULL, &a->a_vals[0], op->o_tmpmemctx);
+ break;
+ }
+ }
+ }
+ } /* else no previous login, 1st use is OK */
+
+ be_entry_release_r(op, e);
+ if (rc) return rc;
+
+ /* Key is stored in base32 */
+ key.mv_len = passwd->bv_len * 5 / 8;
+ key.mv_val = ber_memalloc(key.mv_len+1);
+
+ if (!key.mv_val)
+ return LUTIL_PASSWD_ERR;
+
+ rc = totp_b32_pton(passwd->bv_val, key.mv_val, key.mv_len);
+ if (rc < 1) {
+ rc = LUTIL_PASSWD_ERR;
+ goto out;
+ }
+
+ out.mv_val = outbuf;
+ out.mv_len = sizeof(outbuf);
+ generate(&key, t, DIGITS, &out, mech);
+
+ /* compare */
+ if (out.mv_len != cred->bv_len) {
+ rc = LUTIL_PASSWD_ERR;
+ goto out;
+ }
+
+ rc = memcmp(out.mv_val, cred->bv_val, out.mv_len) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+
+ /* If current value doesn't match, try again with previous value
+ * but only if the most recent login is older than the previous
+ * time step but still set */
+ if (rc == LUTIL_PASSWD_ERR && told < t - 1 && told > 0) {
+ out.mv_val = outbuf;
+ out.mv_len = sizeof(outbuf);
+ generate(&key, t - 1, DIGITS, &out, mech);
+ /* compare */
+ if (out.mv_len != cred->bv_len)
+ goto out;
+ rc = memcmp(out.mv_val, cred->bv_val, out.mv_len) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
+ }
+
+out:
+ memset(key.mv_val, 0, key.mv_len);
+ ber_memfree(key.mv_val);
+ return rc;
+}
+
+static int chk_totp_and_pw(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text,
+ const void *mech)
+{
+ char *s;
+ int rc = LUTIL_PASSWD_ERR, rc_pass, rc_otp;
+ ber_len_t len;
+ struct berval cred_pass, cred_otp, passwd_pass, passwd_otp;
+
+ /* Check credential length, no point to continue if too short */
+ if (cred->bv_len <= DIGITS)
+ return rc;
+
+ /* The OTP seed of the stored password */
+ s = strchr(passwd->bv_val, DELIM);
+ if (s) {
+ len = s - passwd->bv_val;
+ } else {
+ return rc;
+ }
+ if (!ber_str2bv(passwd->bv_val, len, 1, &passwd_otp))
+ return rc;
+
+ /* The password part of the stored password */
+ s++;
+ ber_str2bv(s, 0, 0, &passwd_pass);
+
+ /* The OTP part of the entered credential */
+ ber_str2bv(&cred->bv_val[cred->bv_len - DIGITS], DIGITS, 0, &cred_otp);
+
+ /* The password part of the entered credential */
+ if (!ber_str2bv(cred->bv_val, cred->bv_len - DIGITS, 0, &cred_pass)) {
+ /* Cleanup */
+ memset(passwd_otp.bv_val, 0, passwd_otp.bv_len);
+ ber_memfree(passwd_otp.bv_val);
+ return rc;
+ }
+
+ rc_otp = chk_totp(&passwd_otp, &cred_otp, mech, text);
+ rc_pass = lutil_passwd(&passwd_pass, &cred_pass, NULL, text);
+ if (rc_otp == LUTIL_PASSWD_OK && rc_pass == LUTIL_PASSWD_OK)
+ rc = LUTIL_PASSWD_OK;
+
+ /* Cleanup and return */
+ memset(passwd_otp.bv_val, 0, passwd_otp.bv_len);
+ ber_memfree(passwd_otp.bv_val);
+
+ return rc;
+}
+
+static int chk_totp1(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ return chk_totp(passwd, cred, TOTP_SHA1, text);
+}
+
+static int chk_totp256(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ return chk_totp(passwd, cred, TOTP_SHA256, text);
+}
+
+static int chk_totp512(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ return chk_totp(passwd, cred, TOTP_SHA512, text);
+}
+
+static int chk_totp1andpw(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ return chk_totp_and_pw(scheme, passwd, cred, text, TOTP_SHA1);
+}
+
+static int chk_totp256andpw(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ return chk_totp_and_pw(scheme, passwd, cred, text, TOTP_SHA256);
+}
+
+static int chk_totp512andpw(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ const struct berval *cred,
+ const char **text)
+{
+ return chk_totp_and_pw(scheme, passwd, cred, text, TOTP_SHA512);
+}
+
+static int passwd_string32(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash)
+{
+ int b32len = (passwd->bv_len + 4)/5 * 8;
+ int rc;
+ hash->bv_len = scheme->bv_len + b32len;
+ hash->bv_val = ber_memalloc(hash->bv_len + 1);
+ AC_MEMCPY(hash->bv_val, scheme->bv_val, scheme->bv_len);
+ rc = totp_b32_ntop((unsigned char *)passwd->bv_val, passwd->bv_len,
+ hash->bv_val + scheme->bv_len, b32len+1);
+ if (rc < 0) {
+ ber_memfree(hash->bv_val);
+ hash->bv_val = NULL;
+ return LUTIL_PASSWD_ERR;
+ }
+ return LUTIL_PASSWD_OK;
+}
+
+static int hash_totp_and_pw(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+ struct berval otp, pass, hash_otp, hash_pass;
+ ber_len_t len;
+ char *s;
+ int rc = LUTIL_PASSWD_ERR;
+
+ /* The OTP seed part */
+ s = strchr(passwd->bv_val, DELIM);
+ if (s) {
+ len = s - passwd->bv_val;
+ } else {
+ return rc;
+ }
+ if (!ber_str2bv(passwd->bv_val, len, 0, &otp))
+ return rc;
+
+ /* The static password part */
+ s++;
+ ber_str2bv(s, 0, 0, &pass);
+
+ /* Hash the OTP seed */
+ rc = passwd_string32(scheme, &otp, &hash_otp);
+
+ /* If successful, hash the static password, else cleanup and return */
+ if (rc == LUTIL_PASSWD_OK) {
+ rc = lutil_passwd_hash(&pass, TOTP_AND_PW_HASH_SCHEME,
+ &hash_pass, text);
+ } else {
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* If successful, allocate memory to combine them, else cleanup
+ * and return */
+ if (rc == LUTIL_PASSWD_OK) {
+ /* Add 1 character to bv_len to hold DELIM */
+ hash->bv_len = hash_pass.bv_len + hash_otp.bv_len + 1;
+ hash->bv_val = ber_memalloc(hash->bv_len + 1);
+ if (!hash->bv_val)
+ rc = LUTIL_PASSWD_ERR;
+ } else {
+ memset(hash_otp.bv_val, 0, hash_otp.bv_len);
+ ber_memfree(hash_otp.bv_val);
+ return LUTIL_PASSWD_ERR;
+ }
+
+ /* If successful, combine the two hashes with the delimiter */
+ if (rc == LUTIL_PASSWD_OK) {
+ AC_MEMCPY(hash->bv_val, hash_otp.bv_val, hash_otp.bv_len);
+ hash->bv_val[hash_otp.bv_len] = DELIM;
+ AC_MEMCPY(hash->bv_val + hash_otp.bv_len + 1,
+ hash_pass.bv_val, hash_pass.bv_len);
+ hash->bv_val[hash->bv_len] = '\0';
+ }
+
+ /* Cleanup and return */
+ memset(hash_otp.bv_val, 0, hash_otp.bv_len);
+ memset(hash_pass.bv_val, 0, hash_pass.bv_len);
+ ber_memfree(hash_otp.bv_val);
+ ber_memfree(hash_pass.bv_val);
+
+ return rc;
+}
+
+static int hash_totp1(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+#if 0
+ if (passwd->bv_len != SHA_DIGEST_LENGTH) {
+ *text = "invalid key length";
+ return LUTIL_PASSWD_ERR;
+ }
+#endif
+ return passwd_string32(scheme, passwd, hash);
+}
+
+static int hash_totp256(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+#if 0
+ if (passwd->bv_len != SHA256_DIGEST_LENGTH) {
+ *text = "invalid key length";
+ return LUTIL_PASSWD_ERR;
+ }
+#endif
+ return passwd_string32(scheme, passwd, hash);
+}
+
+static int hash_totp512(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+#if 0
+ if (passwd->bv_len != SHA512_DIGEST_LENGTH) {
+ *text = "invalid key length";
+ return LUTIL_PASSWD_ERR;
+ }
+#endif
+ return passwd_string32(scheme, passwd, hash);
+}
+
+static int hash_totp1andpw(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+#if 0
+ if (passwd->bv_len != SHA_DIGEST_LENGTH) {
+ *text = "invalid key length";
+ return LUTIL_PASSWD_ERR;
+ }
+#endif
+ return hash_totp_and_pw(scheme, passwd, hash, text);
+}
+
+static int hash_totp256andpw(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+#if 0
+ if (passwd->bv_len != SHA256_DIGEST_LENGTH) {
+ *text = "invalid key length";
+ return LUTIL_PASSWD_ERR;
+ }
+#endif
+ return hash_totp_and_pw(scheme, passwd, hash, text);
+}
+
+static int hash_totp512andpw(
+ const struct berval *scheme,
+ const struct berval *passwd,
+ struct berval *hash,
+ const char **text)
+{
+#if 0
+ if (passwd->bv_len != SHA512_DIGEST_LENGTH) {
+ *text = "invalid key length";
+ return LUTIL_PASSWD_ERR;
+ }
+#endif
+ return hash_totp_and_pw(scheme, passwd, hash, text);
+}
+
+static int totp_op_cleanup(
+ Operation *op,
+ SlapReply *rs )
+{
+ slap_callback *cb;
+
+ /* clear out the current key */
+ ldap_pvt_thread_pool_setkey( op->o_threadctx, totp_op_cleanup,
+ NULL, 0, NULL, NULL );
+
+ /* free the callback */
+ cb = op->o_callback;
+ op->o_callback = cb->sc_next;
+ if (cb->sc_private)
+ ber_bvfree_x(cb->sc_private, op->o_tmpmemctx);
+ op->o_tmpfree( cb, op->o_tmpmemctx );
+ return 0;
+}
+
+static int
+totp_bind_response( Operation *op, SlapReply *rs )
+{
+ Modifications *mod = NULL;
+ BackendInfo *bi = op->o_bd->bd_info;
+ Entry *e;
+ int rc;
+
+ /* we're only interested if the bind was successful */
+ if ( rs->sr_err != LDAP_SUCCESS )
+ return SLAP_CB_CONTINUE;
+
+ rc = be_entry_get_rw( op, &op->o_req_ndn, NULL, NULL, 0, &e );
+ op->o_bd->bd_info = bi;
+
+ if ( rc != LDAP_SUCCESS ) {
+ return SLAP_CB_CONTINUE;
+ }
+
+ {
+ time_t now;
+ Attribute *a;
+ Modifications *m;
+ char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ];
+ struct berval timestamp;
+
+ /* get the current time */
+ now = op->o_time;
+
+ /* update the authTimestamp in the user's entry with the current time */
+ timestamp.bv_val = nowstr;
+ timestamp.bv_len = sizeof(nowstr);
+ slap_timestamp( &now, &timestamp );
+
+ m = ch_calloc( sizeof(Modifications), 1 );
+ m->sml_op = LDAP_MOD_REPLACE;
+ m->sml_flags = 0;
+ m->sml_type = ad_authTimestamp->ad_cname;
+ m->sml_desc = ad_authTimestamp;
+ m->sml_numvals = 1;
+ m->sml_values = ch_calloc( sizeof(struct berval), 2 );
+ m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
+
+ ber_dupbv( &m->sml_values[0], &timestamp );
+ ber_dupbv( &m->sml_nvalues[0], &timestamp );
+ m->sml_next = mod;
+ mod = m;
+
+ /* get authTimestamp attribute, if it exists */
+ if ((a = attr_find( e->e_attrs, ad_authTimestamp)) != NULL && op->o_callback->sc_private) {
+ struct berval *bv = op->o_callback->sc_private;
+ m = ch_calloc( sizeof(Modifications), 1 );
+ m->sml_op = LDAP_MOD_DELETE;
+ m->sml_flags = 0;
+ m->sml_type = ad_authTimestamp->ad_cname;
+ m->sml_desc = ad_authTimestamp;
+ m->sml_numvals = 1;
+ m->sml_values = ch_calloc( sizeof(struct berval), 2 );
+ m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
+
+ ber_dupbv( &m->sml_values[0], bv );
+ ber_dupbv( &m->sml_nvalues[0], bv );
+ m->sml_next = mod;
+ mod = m;
+ }
+ }
+
+ be_entry_release_r( op, e );
+
+ /* perform the update */
+ if ( mod ) {
+ Operation op2 = *op;
+ SlapReply r2 = { REP_RESULT };
+ slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
+
+ /* This is a DSA-specific opattr, it never gets replicated. */
+ op2.o_tag = LDAP_REQ_MODIFY;
+ op2.o_callback = &cb;
+ op2.orm_modlist = mod;
+ op2.o_dn = op->o_bd->be_rootdn;
+ op2.o_ndn = op->o_bd->be_rootndn;
+ op2.o_dont_replicate = 1;
+ rc = op->o_bd->be_modify( &op2, &r2 );
+ slap_mods_free( mod, 1 );
+ if (rc != LDAP_SUCCESS) {
+ /* slapd has logged this as a success already, but we
+ * need to fail it because the authTimestamp changed
+ * out from under us.
+ */
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ connection2anonymous(op->o_conn);
+ op2 = *op;
+ op2.o_callback = NULL;
+ send_ldap_result(&op2, rs);
+ op->o_bd->bd_info = bi;
+ return rs->sr_err;
+ }
+ }
+
+ op->o_bd->bd_info = bi;
+ return SLAP_CB_CONTINUE;
+}
+
+static int totp_op_bind(
+ Operation *op,
+ SlapReply *rs )
+{
+ /* If this is a simple Bind, stash the Op pointer so our chk
+ * function can find it. Set a cleanup callback to clear it
+ * out when the Bind completes.
+ */
+ if ( op->oq_bind.rb_method == LDAP_AUTH_SIMPLE ) {
+ slap_callback *cb;
+ ldap_pvt_thread_pool_setkey( op->o_threadctx,
+ totp_op_cleanup, op, 0, NULL, NULL );
+ cb = op->o_tmpcalloc( 1, sizeof(slap_callback), op->o_tmpmemctx );
+ cb->sc_response = totp_bind_response;
+ cb->sc_cleanup = totp_op_cleanup;
+ cb->sc_next = op->o_callback;
+ op->o_callback = cb;
+ }
+ return SLAP_CB_CONTINUE;
+}
+
+static int totp_db_open(
+ BackendDB *be,
+ ConfigReply *cr
+)
+{
+ int rc = 0;
+
+ if (!ad_authTimestamp) {
+ const char *text = NULL;
+ rc = slap_str2ad("authTimestamp", &ad_authTimestamp, &text);
+ if (rc) {
+ rc = register_at(totp_OpSchema[0].def, totp_OpSchema[0].ad, 0 );
+ if (rc) {
+ snprintf(cr->msg, sizeof(cr->msg), "unable to find or register authTimestamp attribute: %s (%d)",
+ text, rc);
+ Debug(LDAP_DEBUG_ANY, "totp: %s.\n", cr->msg );
+ }
+ ad_authTimestamp->ad_type->sat_flags |= SLAP_AT_MANAGEABLE;
+ }
+ }
+ return rc;
+}
+
+static slap_overinst totp;
+
+int
+totp_initialize(void)
+{
+ int rc;
+
+ totp.on_bi.bi_type = "totp";
+
+ totp.on_bi.bi_db_open = totp_db_open;
+ totp.on_bi.bi_op_bind = totp_op_bind;
+
+ rc = lutil_passwd_add((struct berval *) &scheme_totp1, chk_totp1, hash_totp1);
+ if (!rc)
+ rc = lutil_passwd_add((struct berval *) &scheme_totp256, chk_totp256, hash_totp256);
+ if (!rc)
+ rc = lutil_passwd_add((struct berval *) &scheme_totp512, chk_totp512, hash_totp512);
+ if (!rc)
+ rc = lutil_passwd_add((struct berval *) &scheme_totp1andpw, chk_totp1andpw, hash_totp1andpw);
+ if (!rc)
+ rc = lutil_passwd_add((struct berval *) &scheme_totp256andpw, chk_totp256andpw, hash_totp256andpw);
+ if (!rc)
+ rc = lutil_passwd_add((struct berval *) &scheme_totp512andpw, chk_totp512andpw, hash_totp512andpw);
+ if (rc)
+ return rc;
+
+ return overlay_register(&totp);
+}
+
+int init_module(int argc, char *argv[]) {
+ return totp_initialize();
+}
diff --git a/contrib/slapd-modules/passwd/totp/slapo-totp.5 b/contrib/slapd-modules/passwd/totp/slapo-totp.5
new file mode 100644
index 0000000..7c99bf1
--- /dev/null
+++ b/contrib/slapd-modules/passwd/totp/slapo-totp.5
@@ -0,0 +1,109 @@
+.TH PW-TOTP 5 "2015/7/2" "PW-TOTP"
+.\" Copyright 2015-2022 The OpenLDAP Foundation.
+.\" Portions Copyright 2015 by Howard Chu, Symas Corp. All rights reserved.
+.\" Copying restrictions apply. See COPYRIGHT/LICENSE.
+.SH NAME
+pw-totp \- TOTP Password handling module
+.SH SYNOPSIS
+.B moduleload
+.I pw-totp.la
+.SH DESCRIPTION
+The
+.B pw-totp
+module allows time-based one-time password, AKA "authenticator-style",
+authentication to be added to applications that use LDAP for
+authentication. In most cases no changes to the applications are needed to switch
+to this type of authentication.
+
+With this module, the password needed for a user to authenticate is calculated
+based on the current time and a key that is stored in the user's LDAP entry. Since
+the password is based on the time, it changes periodically. Once used, it cannot be
+used again so keyloggers and shoulder-surfers are thwarted. A mobile
+phone application, such as the Google Authenticator (a 'prover'), can be used
+to calculate the user's current password, which is expressed as a six-digit
+number.
+Alternatively, the value can be calculated by some other application with access
+to the user's key and delivered to the user through SMS or some other channel.
+When prompted to authenticate, the user merely enters the six-digit code provided by
+the prover.
+
+Additionally, the overlay can also authenticate TOTP passwords
+combined with a static password. To do this, utilize one of the
+{TOTP1ANDPW}, {TOTP256ANDPW}, or {TOTP512ANDPW} password schemes
+and append the static password scheme value to the end of the
+userPassword attribute, separated by a pipe (|) character.
+
+This implementation complies with
+.B RFC 6238 TOTP Time-based One Time Passwords
+and includes support for the SHA-1, SHA-256, and SHA-512 HMAC
+algorithms.
+
+The HMAC key used in the TOTP computation is stored in the userPassword attribute
+of the user's LDAP entry and the LDAP Password Modify Extended Operation is used to
+set and change the value. The
+value should correspond to that used by the the prover (authenticator).
+
+.SH CONFIGURATION
+Once the module is loaded with the moduleload command from the synopsis,
+the {TOTP1}, {TOTP256}, {TOTP512}
+{TOTP1ANDPW}, {TOTP256ANDPW}, and {TOTP512ANDPW}
+password schemes will be recognized.
+
+On the databases where your users reside you must configure the
+totp overlay:
+
+.nf
+ database mdb
+ \...
+ overlay totp
+ \...
+.fi
+
+You can tell OpenLDAP to use one of these new schemes when processing LDAP
+Password Modify Extended Operations, thanks to the password-hash option in
+slapd.conf. For example:
+
+.nf
+ password-hash {TOTP256}
+.fi
+
+.SH NOTES
+This module includes functionality implemented by the slapo-lastbind overlay
+and cannot coexist with it in the same database. Also note
+that since the time that the last bind occurred
+is needed to properly implement TOTP, provisions need to be made to propagate
+the authTimestamp attribute to other servers that are providing authentication
+services.
+
+The hash functions for the {TOTP1ANDPW}, {TOTP256ANDPW}, and {TOTP512ANDPW}
+schemes expect the secret to be entered in the form:
+<OTP seed><DELIM><static password>, where DELIM is currently defined
+as the pipe character (|).
+
+.SH BUGS
+The time step is hard-coded to thirty seconds. This should be OK for many use cases,
+but it would be nice if the value
+could be changed with a configuration keyword or in an attribute value.
+However, after one successful initial authentication (to verify
+the clocks on the server and the user's prover are in sync) the TOTP
+value of the previous time window may also be used to successfully
+authenticate, provided no successful bind has been performed already
+in the current or previous time window. This eliminates false
+negatives caused by user or network delays
+entering or transmitting the TOTP value.
+
+The authenticator code that is generated is hard-coded to a length of six digits.
+While in most cases
+this is probably better than the alternative length of four digits, there may be
+cases where a four-digit value is preferred.
+
+In cases where password-hash lists multiple mechanisms, the TOTP key will also
+be changed at the same time. This is likely to be undesirable behavior.
+
+.SH "SEE ALSO"
+.BR slapd.conf (5) ldappasswd (1)
+.SH ACKNOWLEDGEMENT
+This work was developed by Howard Chu of Symas Corporation for inclusion in
+OpenLDAP Software.
+
+Password + TOTP support added by Greg Veldman on behalf of SCinet.