From 0fbabb115b875a81b3a0e9d95b62f01a11c1d050 Mon Sep 17 00:00:00 2001 From: HAMANO Tsukasa Date: Fri, 8 Nov 2013 17:58:00 +0900 Subject: [PATCH] ITS#7742 New: PBKDF2 module Signed-off-by: HAMANO Tsukasa --- contrib/slapd-modules/passwd/pbkdf2/Makefile | 48 +++ contrib/slapd-modules/passwd/pbkdf2/README | 87 ++++++ .../slapd-modules/passwd/pbkdf2/pw-pbkdf2.c | 295 ++++++++++++++++++ 3 files changed, 430 insertions(+) create mode 100644 contrib/slapd-modules/passwd/pbkdf2/Makefile create mode 100644 contrib/slapd-modules/passwd/pbkdf2/README create mode 100644 contrib/slapd-modules/passwd/pbkdf2/pw-pbkdf2.c diff --git a/contrib/slapd-modules/passwd/pbkdf2/Makefile b/contrib/slapd-modules/passwd/pbkdf2/Makefile new file mode 100644 index 0000000000..64ad97c9a5 --- /dev/null +++ b/contrib/slapd-modules/passwd/pbkdf2/Makefile @@ -0,0 +1,48 @@ +# $OpenLDAP$ + +LDAP_SRC = ../../../.. +LDAP_BUILD = ../../../.. +LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd +LDAP_LIB = $(LDAP_BUILD)/libraries/libldap_r/libldap_r.la \ + $(LDAP_BUILD)/libraries/liblber/liblber.la + +LIBTOOL = $(LDAP_BUILD)/libtool +CC = gcc +OPT = -g -O2 -Wall +#DEFS = -DSLAPD_PBKDF2_DEBUG + +INCS = $(LDAP_INC) +LIBS = $(LDAP_LIB) -lcrypto + +PROGRAMS = pw-pbkdf2.la +LTVER = 0:0:0 + +#prefix=/usr/local +prefix=`grep -e "^prefix =" $(LDAP_BUILD)/Makefile | cut -d= -f2` + +exec_prefix=$(prefix) +ldap_subdir=/openldap + +libdir=$(exec_prefix)/lib +libexecdir=$(exec_prefix)/libexec +moduledir = $(libexecdir)$(ldap_subdir) + +.SUFFIXES: .c .o .lo + +.c.lo: + $(LIBTOOL) --mode=compile $(CC) $(OPT) $(DEFS) $(INCS) -c $< + +all: $(PROGRAMS) + +pw-pbkdf2.la: pw-pbkdf2.lo + $(LIBTOOL) --mode=link $(CC) $(OPT) -version-info $(LTVER) \ + -rpath $(moduledir) -module -o $@ $? $(LIBS) + +clean: + rm -rf *.o *.lo *.la .libs + +install: $(PROGRAMS) + mkdir -p $(DESTDIR)$(moduledir) + for p in $(PROGRAMS) ; do \ + $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \ + done diff --git a/contrib/slapd-modules/passwd/pbkdf2/README b/contrib/slapd-modules/passwd/pbkdf2/README new file mode 100644 index 0000000000..348580be0f --- /dev/null +++ b/contrib/slapd-modules/passwd/pbkdf2/README @@ -0,0 +1,87 @@ +PBKDF2 for OpenLDAP +======================= + +pw-pbkdf2.c provides support for PBKDF2 key derivation functions in +OpenLDAP. + +# Requirements + + * OpenSSL 1.0.0 or later + +# Installations + + $ cd /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} + +# 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}$$ + +# 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 . + +Copyright 2009-2013 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 +. + +# ACKNOWLEDGEMENT +This work was initially developed by HAMANO Tsukasa 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 0000000000..6070013a58 --- /dev/null +++ b/contrib/slapd-modules/passwd/pbkdf2/pw-pbkdf2.c @@ -0,0 +1,295 @@ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * Copyright 2009-2013 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 + * . + */ +/* ACKNOWLEDGEMENT: + * This work was initially developed by HAMANO Tsukasa + */ + +#define _GNU_SOURCE + +#include "portable.h" +#include +#include "lber_pvt.h" +#include "lutil.h" + +#include + +#define PBKDF2_SALT_SIZE 16 +#define PBKDF2_DK_SIZE 20 +#define PBKDF2_ITERATION 60000 + +const struct berval pbkdf2scheme = BER_BVC("{PBKDF2}"); + +/* + * 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 + * whitepsace. + * see http://pythonhosted.org/passlib/lib/passlib.utils.html + * This is destructive function. + */ +static int b64_to_ab64(char *str) +{ + char *p = str; + while(*p++){ + if(*p == '+'){ + *p = '.'; + } + if(*p == '='){ + *p = '\0'; + break; + } + } + 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; + char salt_b64[LUTIL_BASE64_ENCODE_LEN(PBKDF2_SALT_SIZE) + 1]; + char dk_b64[LUTIL_BASE64_ENCODE_LEN(PBKDF2_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->bv_len = asprintf(&msg->bv_val, "%s%d$%s$%s", + sc->bv_val, iteration, + salt_b64, dk_b64); + if(msg->bv_len < 0){ + return LUTIL_PASSWD_ERR; + } + +#ifdef SLAPD_PBKDF2_DEBUG + printf(" Output:\t%s\n", msg->bv_val); +#endif + 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_DK_SIZE]; + struct berval dk; + int iteration = PBKDF2_ITERATION; + + dk.bv_val = (char *)dk_value; + dk.bv_len = PBKDF2_DK_SIZE; + salt.bv_val = (char *)salt_value; + salt.bv_len = sizeof(salt_value); + + if(lutil_entropy((unsigned char *)salt.bv_val, salt.bv_len) < 0){ + return LUTIL_PASSWD_ERR; + } + + if(!PKCS5_PBKDF2_HMAC_SHA1(passwd->bv_val, passwd->bv_len, + (unsigned char *)salt.bv_val, salt.bv_len, + iteration, PBKDF2_DK_SIZE, dk_value)){ + return LUTIL_PASSWD_ERR; + } + +#ifdef SLAPD_PBKDF2_DEBUG + printf("DEBUG pbkdf2_encrypt()\n"); + printf(" Password:\t%s\n", passwd->bv_val); + + printf(" Salt:\t\t"); + int i; + for(i=0; ibv_val); + printf(" Input Cred:\t%s\n", cred->bv_val); +#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_DK_SIZE + 1 in lutil_b64_pton. */ + rc = lutil_b64_pton(dk_b64, dk_value, PBKDF2_DK_SIZE + 1); + if(rc < 0){ + return LUTIL_PASSWD_ERR; + } + + /* consistency check */ + if(rc != PBKDF2_DK_SIZE){ + return LUTIL_PASSWD_ERR; + } + + if(!PKCS5_PBKDF2_HMAC_SHA1(cred->bv_val, cred->bv_len, + salt_value, PBKDF2_SALT_SIZE, + iteration, PBKDF2_DK_SIZE, input_dk_value)){ + return LUTIL_PASSWD_ERR; + } + + rc = memcmp(dk_value, input_dk_value, PBKDF2_DK_SIZE); +#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