>From 2d5e6350805f81d19ce086e779f5712c3e9228ef Mon Sep 17 00:00:00 2001 From: Nathan Kinder Date: Mon, 26 Oct 2009 13:08:31 -0700 Subject: [PATCH] BZ 221905 - Add SMD5 password storage support. This adds SMD5 password storage support to the existing password storage plug-in. Add upgrade LDIF has been added to ensure that this new functionality will be available to servers that are upgraded from previous versions. --- Makefile.am | 3 + ldap/admin/src/scripts/50smd5pwdstorageplugin.ldif | 8 + ldap/ldif/template-dse.ldif.in | 9 + ldap/servers/plugins/pwdstorage/pwd_init.c | 26 +++ ldap/servers/plugins/pwdstorage/pwd_util.c | 69 ++++++++ ldap/servers/plugins/pwdstorage/pwdstorage.h | 7 + ldap/servers/plugins/pwdstorage/sha_pwd.c | 24 +--- ldap/servers/plugins/pwdstorage/smd5_pwd.c | 168 ++++++++++++++++++++ ldap/servers/plugins/pwdstorage/ssha_pwd.c | 17 +-- 9 files changed, 292 insertions(+), 39 deletions(-) create mode 100644 ldap/admin/src/scripts/50smd5pwdstorageplugin.ldif create mode 100644 ldap/servers/plugins/pwdstorage/pwd_util.c create mode 100644 ldap/servers/plugins/pwdstorage/smd5_pwd.c diff --git a/Makefile.am b/Makefile.am index c34e6fe..676b5cf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -418,6 +418,7 @@ update_DATA = ldap/admin/src/scripts/exampleupdate.pl \ ldap/admin/src/scripts/50guidesyntaxplugin.ldif \ ldap/admin/src/scripts/50linkedattrsplugin.ldif \ ldap/admin/src/scripts/50usnplugin.ldif \ + ldap/admin/src/scripts/50smd5pwdstorageplugin.ldif \ ldap/admin/src/scripts/60upgradeschemafiles.pl \ ldap/admin/src/scripts/dnaplugindepends.ldif @@ -902,7 +903,9 @@ libpwdstorage_plugin_la_SOURCES = ldap/servers/plugins/pwdstorage/clear_pwd.c \ ldap/servers/plugins/pwdstorage/md5c.c \ ldap/servers/plugins/pwdstorage/ns-mta-md5_pwd.c \ ldap/servers/plugins/pwdstorage/pwd_init.c \ + ldap/servers/plugins/pwdstorage/pwd_util.c \ ldap/servers/plugins/pwdstorage/sha_pwd.c \ + ldap/servers/plugins/pwdstorage/smd5_pwd.c \ ldap/servers/plugins/pwdstorage/ssha_pwd.c libpwdstorage_plugin_la_CPPFLAGS = $(PLUGIN_CPPFLAGS) diff --git a/ldap/admin/src/scripts/50smd5pwdstorageplugin.ldif b/ldap/admin/src/scripts/50smd5pwdstorageplugin.ldif new file mode 100644 index 0000000..5375c9f --- /dev/null +++ b/ldap/admin/src/scripts/50smd5pwdstorageplugin.ldif @@ -0,0 +1,8 @@ +dn: cn=SMD5,cn=Password Storage Schemes,cn=plugins,cn=config +objectclass: top +objectclass: nsSlapdPlugin +cn: SMD5 +nsslapd-pluginpath: libpwdstorage-plugin +nsslapd-plugininitfunc: smd5_pwd_storage_scheme_init +nsslapd-plugintype: pwdstoragescheme +nsslapd-pluginenabled: on diff --git a/ldap/ldif/template-dse.ldif.in b/ldap/ldif/template-dse.ldif.in index 72708a0..7bcb48a 100644 --- a/ldap/ldif/template-dse.ldif.in +++ b/ldap/ldif/template-dse.ldif.in @@ -155,6 +155,15 @@ nsslapd-plugininitfunc: md5_pwd_storage_scheme_init nsslapd-plugintype: pwdstoragescheme nsslapd-pluginenabled: on +dn: cn=SMD5,cn=Password Storage Schemes,cn=plugins,cn=config +objectclass: top +objectclass: nsSlapdPlugin +cn: SMD5 +nsslapd-pluginpath: libpwdstorage-plugin +nsslapd-plugininitfunc: smd5_pwd_storage_scheme_init +nsslapd-plugintype: pwdstoragescheme +nsslapd-pluginenabled: on + dn: cn=CLEAR,cn=Password Storage Schemes,cn=plugins,cn=config objectclass: top objectclass: nsSlapdPlugin diff --git a/ldap/servers/plugins/pwdstorage/pwd_init.c b/ldap/servers/plugins/pwdstorage/pwd_init.c index 6ae2669..1bd24fd 100644 --- a/ldap/servers/plugins/pwdstorage/pwd_init.c +++ b/ldap/servers/plugins/pwdstorage/pwd_init.c @@ -73,6 +73,8 @@ static Slapi_PluginDesc ns_mta_md5_pdesc = { "NS-MTA-MD5-password-storage-scheme static Slapi_PluginDesc md5_pdesc = { "md5-password-storage-scheme", VENDOR, PACKAGE_VERSION, "MD5 hash algorithm (MD5)" }; +static Slapi_PluginDesc smd5_pdesc = { "smd5-password-storage-scheme", VENDOR, PACKAGE_VERSION, "Salted MD5 hash algorithm (SMD5)" }; + static char *plugin_name = "NSPwdStoragePlugin"; int @@ -369,3 +371,27 @@ md5_pwd_storage_scheme_init( Slapi_PBlock *pb ) slapi_log_error( SLAPI_LOG_PLUGIN, plugin_name, "<= md5_pwd_storage_scheme_init %d\n\n", rc ); return( rc ); } + +int +smd5_pwd_storage_scheme_init( Slapi_PBlock *pb ) +{ + int rc; + char *name; + + slapi_log_error( SLAPI_LOG_PLUGIN, plugin_name, "=> smd5_pwd_storage_scheme_init\n" ); + + rc = slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, + (void *) SLAPI_PLUGIN_VERSION_01 ); + rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, + (void *)&smd5_pdesc ); + rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_ENC_FN, + (void *) smd5_pw_enc ); + rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_CMP_FN, + (void *) smd5_pw_cmp ); + name = slapi_ch_strdup("SMD5"); + rc |= slapi_pblock_set( pb, SLAPI_PLUGIN_PWD_STORAGE_SCHEME_NAME, + name ); + + slapi_log_error( SLAPI_LOG_PLUGIN, plugin_name, "<= smd5_pwd_storage_scheme_init %d\n\n", rc ); + return( rc ); +} diff --git a/ldap/servers/plugins/pwdstorage/pwd_util.c b/ldap/servers/plugins/pwdstorage/pwd_util.c new file mode 100644 index 0000000..c7b4fda --- /dev/null +++ b/ldap/servers/plugins/pwdstorage/pwd_util.c @@ -0,0 +1,69 @@ +/** BEGIN COPYRIGHT BLOCK + * This Program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; version 2 of the License. + * + * This Program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA. + * + * In addition, as a special exception, Red Hat, Inc. gives You the additional + * right to link the code of this Program with code not covered under the GNU + * General Public License ("Non-GPL Code") and to distribute linked combinations + * including the two, subject to the limitations in this paragraph. Non-GPL Code + * permitted under this exception must only link to the code of this Program + * through those well defined interfaces identified in the file named EXCEPTION + * found in the source code files (the "Approved Interfaces"). The files of + * Non-GPL Code may instantiate templates or use macros or inline functions from + * the Approved Interfaces without causing the resulting work to be covered by + * the GNU General Public License. Only Red Hat, Inc. may make changes or + * additions to the list of Approved Interfaces. You must obey the GNU General + * Public License in all respects for all of the Program code and other code used + * in conjunction with the Program except the Non-GPL Code covered by this + * exception. If you modify this file, you may extend this exception to your + * version of the file, but you are not obligated to do so. If you do not wish to + * provide this exception without modification, you must delete this exception + * statement from your version and license this file solely under the GPL without + * exception. + * + * + * Copyright (C) 2009 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include "pwdstorage.h" + +/* + * Utility functions for the Password Storage Scheme plugins. + */ + +/* + * calculate the number of bytes the base64 encoded encval + * will have when decoded, taking into account padding + */ +int +pwdstorage_base64_decode_len(const char *encval) +{ + int len = strlen(encval); + if (len && (0 == (len & 3))) { + if('=' == encval[len - 1]) { + if('=' == encval[len - 2]) { + len -= 2; + } else { + len -= 1; + } + } + } + + return ((len * 3) / 4); +} + diff --git a/ldap/servers/plugins/pwdstorage/pwdstorage.h b/ldap/servers/plugins/pwdstorage/pwdstorage.h index 895ac4e..f43e63e 100644 --- a/ldap/servers/plugins/pwdstorage/pwdstorage.h +++ b/ldap/servers/plugins/pwdstorage/pwdstorage.h @@ -80,6 +80,8 @@ #define CLEARTEXT_NAME_LEN 5 #define MD5_SCHEME_NAME "MD5" #define MD5_NAME_LEN 3 +#define SALTED_MD5_SCHEME_NAME "SMD5" +#define SALTED_MD5_NAME_LEN 4 SECStatus sha_salted_hash(char *hash_out, const char *pwd, struct berval *salt, unsigned int secOID); int sha_pw_cmp( const char *userpwd, const char *dbpwd, unsigned int shaLen ); @@ -107,5 +109,10 @@ char *crypt_pw_enc( const char *pwd ); int ns_mta_md5_pw_cmp( const char *userpwd, const char *dbpwd ); int md5_pw_cmp( const char *userpwd, const char *dbpwd ); char *md5_pw_enc( const char *pwd ); +int smd5_pw_cmp( const char *userpwd, const char *dbpwd ); +char *smd5_pw_enc( const char *pwd ); + +/* Utility functions */ +int pwdstorage_base64_decode_len(const char *encval); #endif /* _PWDSTORAGE_H */ diff --git a/ldap/servers/plugins/pwdstorage/sha_pwd.c b/ldap/servers/plugins/pwdstorage/sha_pwd.c index ecade7a..d7fb693 100644 --- a/ldap/servers/plugins/pwdstorage/sha_pwd.c +++ b/ldap/servers/plugins/pwdstorage/sha_pwd.c @@ -67,28 +67,6 @@ static char *plugin_name = "NSPwdStoragePlugin"; * It's obsolescent now, but we still handle such stored values. */ - -/* - calculate the number of bytes the base64 encoded encval - will have when decoded, taking into account padding -*/ -static int -base64_decode_len(const char *encval) -{ - int len = strlen(encval); - if (len && (0 == (len & 3))) { - if('=' == encval[len - 1]) { - if('=' == encval[len - 2]) { - len -= 2; - } else { - len -= 1; - } - } - } - - return ((len * 3) / 4); -} - int sha_pw_cmp (const char *userpwd, const char *dbpwd, unsigned int shaLen ) { @@ -132,7 +110,7 @@ sha_pw_cmp (const char *userpwd, const char *dbpwd, unsigned int shaLen ) /* * Decode hash stored in database. */ - hash_len = base64_decode_len(dbpwd); + hash_len = pwdstorage_base64_decode_len(dbpwd); if ( hash_len > sizeof(quick_dbhash) ) { /* get more space: */ dbhash = (char*) slapi_ch_calloc( hash_len, sizeof(char) ); if ( dbhash == NULL ) goto loser; diff --git a/ldap/servers/plugins/pwdstorage/smd5_pwd.c b/ldap/servers/plugins/pwdstorage/smd5_pwd.c new file mode 100644 index 0000000..f7689ed --- /dev/null +++ b/ldap/servers/plugins/pwdstorage/smd5_pwd.c @@ -0,0 +1,168 @@ +/** BEGIN COPYRIGHT BLOCK + * This Program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; version 2 of the License. + * + * This Program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple + * Place, Suite 330, Boston, MA 02111-1307 USA. + * + * In addition, as a special exception, Red Hat, Inc. gives You the additional + * right to link the code of this Program with code not covered under the GNU + * General Public License ("Non-GPL Code") and to distribute linked combinations + * including the two, subject to the limitations in this paragraph. Non-GPL Code + * permitted under this exception must only link to the code of this Program + * through those well defined interfaces identified in the file named EXCEPTION + * found in the source code files (the "Approved Interfaces"). The files of + * Non-GPL Code may instantiate templates or use macros or inline functions from + * the Approved Interfaces without causing the resulting work to be covered by + * the GNU General Public License. Only Red Hat, Inc. may make changes or + * additions to the list of Approved Interfaces. You must obey the GNU General + * Public License in all respects for all of the Program code and other code used + * in conjunction with the Program except the Non-GPL Code covered by this + * exception. If you modify this file, you may extend this exception to your + * version of the file, but you are not obligated to do so. If you do not wish to + * provide this exception without modification, you must delete this exception + * statement from your version and license this file solely under the GPL without + * exception. + * + * + * Copyright (C) 2005 Red Hat, Inc. + * All rights reserved. + * END COPYRIGHT BLOCK **/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +/* + * Based on MD5 Password Encryption/Comparison routines by David Irving, + * Fred Brittain, and Aaron Gagnon -- University of Maine Farmington + * Donated to the RedHat Directory Server Project 2005-06-10 + */ + +#include +#include +#include +#include +#include +#include +#include +#include "pwdstorage.h" + +#define MD5_DEFAULT_SALT_LENGTH 4 +#define MD5_MAX_SALT_LENGTH 16 +#define SALTED_MD5_SUBSYSTEM_NAME "Salted MD5 password hash" + +int +smd5_pw_cmp( const char *userpwd, const char *dbpwd ) +{ + int rc=-1; + PK11Context *ctx=NULL; + unsigned int outLen; + unsigned char userhash[MD5_LENGTH]; + int hash_len; + unsigned char quick_dbhash[MD5_LENGTH + MD5_DEFAULT_SALT_LENGTH + 1]; + unsigned char *dbhash = quick_dbhash; + struct berval salt; + char *hashresult = NULL; + SECItem binary_item; + + ctx = PK11_CreateDigestContext(SEC_OID_MD5); + if (ctx == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, SALTED_MD5_SUBSYSTEM_NAME, + "Could not create context for digest operation for password compare"); + goto loser; + } + + /* + * Decode hash stored in database. + */ + hash_len = pwdstorage_base64_decode_len(dbpwd); + if ( hash_len >= sizeof(quick_dbhash) ) { /* get more space: */ + dbhash = (char*) slapi_ch_calloc( hash_len + 1, sizeof(char) ); + if ( dbhash == NULL ) goto loser; + } else { + memset( quick_dbhash, 0, sizeof(quick_dbhash) ); + } + + hashresult = PL_Base64Decode( dbpwd, 0, dbhash ); + if (NULL == hashresult) { + slapi_log_error( SLAPI_LOG_PLUGIN, SALTED_MD5_SUBSYSTEM_NAME, + "smd5_pw_cmp: userPassword \"%s\" is the wrong length " + "or is not properly encoded BASE64\n", dbpwd ); + goto loser; + } + + salt.bv_val = (void*)(dbhash + MD5_LENGTH); /* salt starts after hash value */ + salt.bv_len = hash_len - MD5_LENGTH; /* remaining bytes must be salt */ + + /* create the hash */ + memset( userhash, 0, sizeof(userhash) ); + PK11_DigestBegin(ctx); + PK11_DigestOp(ctx, (const unsigned char *)userpwd, strlen(userpwd)); + PK11_DigestOp(ctx, (unsigned char*)(salt.bv_val), salt.bv_len); + PK11_DigestFinal(ctx, userhash, &outLen, sizeof userhash); + PK11_DestroyContext(ctx, 1); + + /* Compare everything up to the salt. */ + rc = memcmp( userhash, dbhash, MD5_LENGTH ); + +loser: + if ( dbhash && dbhash != quick_dbhash ) slapi_ch_free_string( (char **)&dbhash ); + return rc; +} + +char * +smd5_pw_enc( const char *pwd ) +{ + char * bver, *enc=NULL; + PK11Context *ctx=NULL; + unsigned int outLen; + unsigned char hash_out[MD5_LENGTH + MD5_DEFAULT_SALT_LENGTH]; + unsigned char b2a_out[(MD5_LENGTH*2) + (MD5_MAX_SALT_LENGTH*2)]; /* conservative */ + char *salt = hash_out + MD5_LENGTH; + struct berval saltval; + SECItem binary_item; + + ctx = PK11_CreateDigestContext(SEC_OID_MD5); + if (ctx == NULL) { + slapi_log_error(SLAPI_LOG_PLUGIN, SALTED_MD5_SUBSYSTEM_NAME, + "Could not create context for digest operation for password encoding"); + return NULL; + } + + /* prepare the hash output area */ + memset( hash_out, 0, sizeof(hash_out) ); + + /* generate a new random salt */ + slapi_rand_array( salt, MD5_DEFAULT_SALT_LENGTH ); + saltval.bv_val = (void*)salt; + saltval.bv_len = MD5_DEFAULT_SALT_LENGTH; + + /* create the hash */ + PK11_DigestBegin(ctx); + PK11_DigestOp(ctx, (const unsigned char *)pwd, strlen(pwd)); + PK11_DigestOp(ctx, (unsigned char*)(saltval.bv_val), saltval.bv_len); + PK11_DigestFinal(ctx, hash_out, &outLen, sizeof hash_out); + PK11_DestroyContext(ctx, 1); + + /* convert the binary hash to base64 */ + binary_item.data = hash_out; + binary_item.len = outLen + MD5_DEFAULT_SALT_LENGTH; + bver = NSSBase64_EncodeItem(NULL, (char *)b2a_out, sizeof b2a_out, &binary_item); + if (bver) { + enc = slapi_ch_smprintf("%c%s%c%s", PWD_HASH_PREFIX_START, SALTED_MD5_SCHEME_NAME, + PWD_HASH_PREFIX_END, bver ); + } else { + slapi_log_error(SLAPI_LOG_PLUGIN, SALTED_MD5_SUBSYSTEM_NAME, + "Could not base64 encode hashed value for password encoding"); + } + + return( enc ); +} + diff --git a/ldap/servers/plugins/pwdstorage/ssha_pwd.c b/ldap/servers/plugins/pwdstorage/ssha_pwd.c index 6f09d5e..2bd8ef1 100644 --- a/ldap/servers/plugins/pwdstorage/ssha_pwd.c +++ b/ldap/servers/plugins/pwdstorage/ssha_pwd.c @@ -58,21 +58,6 @@ #define SHA_SALT_LENGTH 8 /* number of bytes of data in salt */ -static void ssha_rand_array(void *randx, size_t len); - - -/* *************************************************** - Identical function to slapi_rand_array in util.c, but can't use - that here since this module is included in libds_admin, which doesn't - link to libslapd. - *************************************************** */ -static void -ssha_rand_array(void *randx, size_t len) -{ - PK11_RandomUpdate(randx, len); - PK11_GenerateRandom((unsigned char *)randx, (int)len); -} - SECStatus sha_salted_hash(char *hash_out, const char *pwd, struct berval *salt, unsigned int secOID) { @@ -168,7 +153,7 @@ salted_sha_pw_enc( const char *pwd, unsigned int shaLen ) saltval.bv_len = SHA_SALT_LENGTH; /* generate a new random salt */ - ssha_rand_array( salt, SHA_SALT_LENGTH ); + slapi_rand_array( salt, SHA_SALT_LENGTH ); /* hash the user's key */ if ( sha_salted_hash( hash, pwd, &saltval, secOID ) != SECSuccess ) { -- 1.6.2.5