>From 2d5e6350805f81d19ce086e779f5712c3e9228ef Mon Sep 17 00:00:00 2001
From: Nathan Kinder <nkinder@redhat.com>
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 <config.h>
+#endif
+
+#include <string.h>
+#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 <config.h>
+#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 <string.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <pk11func.h>
+#include <nss.h>
+#include <nssb64.h>
+#include <sechash.h>
+#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

