diff options
Diffstat (limited to 'contrib/slapd-modules/passwd')
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, ×tamp ); + + 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], ×tamp ); + ber_dupbv( &m->sml_nvalues[0], ×tamp ); + 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. |