Blame SOURCES/openldap-tlsmc.patch

edf356
MozNSS Interception Code
edf356
edf356
Author: Matus Honek <mhonek@redhat.com>
02ade0
Date: Thu Jun 21 20:37:25 CEST 2018
edf356
diff --git a/configure.in b/configure.in
edf356
--- a/configure.in
edf356
+++ b/configure.in
edf356
@@ -237,6 +237,7 @@ dnl OL_ARG_ENABLE(referrals,[  --enable-referrals	  enable LDAPv2+ Referrals (ex
edf356
 ol_enable_referrals=${ol_enable_referrals-no}
edf356
 OL_ARG_ENABLE(ipv6,[  --enable-ipv6 	  enable IPv6 support], auto)dnl
edf356
 OL_ARG_ENABLE(local,[  --enable-local	  enable AF_LOCAL (AF_UNIX) socket support], auto)dnl
edf356
+OL_ARG_ENABLE(moznss_compatibility,[  --enable-moznss-compatibility	  enable MozNSS compatibility], no)dnl
edf356
 
edf356
 dnl ----------------------------------------------------------------
edf356
 dnl General "with" options
edf356
@@ -1256,6 +1257,26 @@ if test $ol_link_tls = no ; then
edf356
 	fi
edf356
 fi
edf356
 
edf356
+if test $ol_enable_moznss_compatibility = yes ; then
edf356
+	if test $have_openssl = yes ; then
edf356
+		if test $ol_with_tls = openssl ; then
edf356
+			PKG_CHECK_MODULES(MOZNSS, [nss nspr], [have_moznss_libs=yes], [have_moznss_libs=no])
edf356
+			if test $have_moznss_libs = yes ; then
edf356
+				AC_DEFINE(HAVE_MOZNSS_COMPATIBILITY, 1,
edf356
+					[define if you want to support MozNSS databases when compiled with OpenSSL])
edf356
+				TLS_LIBS="$TLS_LIBS $MOZNSS_LIBS"
edf356
+				CFLAGS="$CFLAGS $MOZNSS_CFLAGS"
edf356
+			else
edf356
+				AC_MSG_ERROR([For MOZNSS_COMPATIBILITY you have to have MozNSS present.])
edf356
+			fi
edf356
+		else
edf356
+			AC_MSG_ERROR([For MOZNSS_COMPATIBILITY you have to compile with OpenSSL for crypto. (see --with-tls flag)])
edf356
+		fi
edf356
+	else
edf356
+		AC_MSG_ERROR([For MOZNSS_COMPATIBILITY you have to have OpenSSL devel available. (unable to build with OpenSSL)])
edf356
+	fi
edf356
+fi
edf356
+
edf356
 WITH_TLS=no
edf356
 if test $ol_link_tls = yes ; then
edf356
 	AC_DEFINE(HAVE_TLS, 1, [define if you have TLS])
edf356
diff --git a/doc/man/man3/ldap_get_option.3 b/doc/man/man3/ldap_get_option.3
edf356
--- a/doc/man/man3/ldap_get_option.3
edf356
+++ b/doc/man/man3/ldap_get_option.3
edf356
@@ -772,6 +772,19 @@ must be
edf356
 When using the OpenSSL library this is an SSL*. When using other
edf356
 crypto libraries this is a pointer to an OpenLDAP private structure.
edf356
 Applications generally should not use this option.
edf356
+.TP
edf356
+.B LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY
edf356
+Sets/gets the status of the MozNSS database compatibility layer for TLS options. This options is available only if OpenLDAP is compiled with OpenSSL. This has effect only before the TLS context is established.
edf356
+This option defaults to be on.
edf356
+.BR invalue
edf356
+should either be
edf356
+.BR LDAP_OPT_OFF
edf356
+or
edf356
+.BR LDAP_OPT_ON ;
edf356
+.BR outvalue
edf356
+must be
edf356
+.BR "int *" .
edf356
+When set, just before TLS context initialization the library will first check if TLS settings are MozNSS-specific and if so it will export certificates from a preconfigured database and set other TLS settings so that OpenSSL can continue without issues.
edf356
 .SH ERRORS
edf356
 On success, the functions return
edf356
 .BR LDAP_OPT_SUCCESS ,
edf356
diff --git a/doc/man/man5/ldap.conf.5 b/doc/man/man5/ldap.conf.5
edf356
--- a/doc/man/man5/ldap.conf.5
edf356
+++ b/doc/man/man5/ldap.conf.5
edf356
@@ -483,6 +483,11 @@ Check the CRL for a whole certificate chain
edf356
 Specifies the file containing a Certificate Revocation List to be used
edf356
 to verify if the server certificates have not been revoked. This
edf356
 parameter is only supported with GnuTLS and Mozilla NSS.
edf356
+.RE
edf356
+.TP
edf356
+.B TLS_MOZNSS_COMPATIBILITY <on/true/yes/off/false/no>
edf356
+Specifies whether the MozNSS database compatibility layer for TLS options should be enabled. This options is available only if OpenLDAP is compiled with OpenSSL.
edf356
+This option defaults to be on.
edf356
 .SH "ENVIRONMENT VARIABLES"
edf356
 .TP
edf356
 LDAPNOINIT
edf356
diff --git a/doc/man/man5/slapd-config.5 b/doc/man/man5/slapd-config.5
edf356
--- a/doc/man/man5/slapd-config.5
edf356
+++ b/doc/man/man5/slapd-config.5
edf356
@@ -1004,6 +1004,11 @@ Check the CRL for a whole certificate chain
edf356
 Specifies a file containing a Certificate Revocation List to be used
edf356
 for verifying that certificates have not been revoked. This parameter
edf356
 is only valid when using GnuTLS or Mozilla NSS.
edf356
+.RE
edf356
+.TP
edf356
+.B olcTLSMozNSSCompatibility <on/true/yes/off/false/no>
edf356
+Specifies whether the MozNSS database compatibility layer for TLS options should be enabled. This options is available only if OpenLDAP is compiled with OpenSSL.
edf356
+This option defaults to be on.
edf356
 .SH DYNAMIC MODULE OPTIONS
edf356
 If
edf356
 .B slapd
edf356
diff --git a/doc/man/man5/slapd.conf.5 b/doc/man/man5/slapd.conf.5
edf356
--- a/doc/man/man5/slapd.conf.5
edf356
+++ b/doc/man/man5/slapd.conf.5
edf356
@@ -1235,6 +1235,11 @@ Check the CRL for a whole certificate chain
edf356
 Specifies a file containing a Certificate Revocation List to be used
edf356
 for verifying that certificates have not been revoked. This directive is
edf356
 only valid when using GnuTLS and Mozilla NSS.
edf356
+.RE
edf356
+.TP
edf356
+.B TLSMozNSSCompatibility <on/true/yes/off/false/no>
edf356
+Specifies whether the MozNSS database compatibility layer for TLS options should be enabled. This options is available only if OpenLDAP is compiled with OpenSSL.
edf356
+This option defaults to be on.
edf356
 .SH GENERAL BACKEND OPTIONS
edf356
 Options in this section only apply to the configuration file section
edf356
 for the specified backend.  They are supported by every
edf356
diff --git a/include/ldap.h b/include/ldap.h
edf356
--- a/include/ldap.h
edf356
+++ b/include/ldap.h
edf356
@@ -158,6 +158,10 @@ LDAP_BEGIN_DECL
edf356
 #define LDAP_OPT_X_TLS_NEWCTX		0x600f
edf356
 #define LDAP_OPT_X_TLS_CRLFILE		0x6010	/* GNUtls only */
edf356
 #define LDAP_OPT_X_TLS_PACKAGE		0x6011
edf356
+#define LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY  0x6050
edf356
+
edf356
+#define LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY_DISABLED	0
edf356
+#define LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY_ENABLED	1
edf356
 
edf356
 #define LDAP_OPT_X_TLS_NEVER	0
edf356
 #define LDAP_OPT_X_TLS_HARD		1
edf356
diff --git a/libraries/libldap/Makefile.in b/libraries/libldap/Makefile.in
edf356
--- a/libraries/libldap/Makefile.in
edf356
+++ b/libraries/libldap/Makefile.in
edf356
@@ -26,7 +26,7 @@ SRCS	= bind.c open.c result.c error.c compare.c search.c \
edf356
 	request.c os-ip.c url.c pagectrl.c sortctrl.c vlvctrl.c \
edf356
 	init.c options.c print.c string.c util-int.c schema.c \
edf356
 	charray.c os-local.c dnssrv.c utf-8.c utf-8-conv.c \
edf356
-	tls2.c tls_o.c tls_g.c tls_m.c \
edf356
+	tls2.c tls_o.c tls_g.c tls_m.c tls_mc.c tls_mc_ossl.c \
edf356
 	turn.c ppolicy.c dds.c txn.c ldap_sync.c stctrl.c \
edf356
 	assertion.c deref.c ldif.c fetch.c
edf356
 
edf356
@@ -39,7 +39,7 @@ OBJS	= bind.lo open.lo result.lo error.lo compare.lo search.lo \
edf356
 	request.lo os-ip.lo url.lo pagectrl.lo sortctrl.lo vlvctrl.lo \
edf356
 	init.lo options.lo print.lo string.lo util-int.lo schema.lo \
edf356
 	charray.lo os-local.lo dnssrv.lo utf-8.lo utf-8-conv.lo \
edf356
-	tls2.lo tls_o.lo tls_g.lo tls_m.lo \
edf356
+	tls2.lo tls_o.lo tls_g.lo tls_m.lo tls_mc.lo tls_mc_ossl.lo \
edf356
 	turn.lo ppolicy.lo dds.lo txn.lo ldap_sync.lo stctrl.lo \
edf356
 	assertion.lo deref.lo ldif.lo fetch.lo
edf356
 
edf356
diff --git a/libraries/libldap/init.c b/libraries/libldap/init.c
edf356
--- a/libraries/libldap/init.c
edf356
+++ b/libraries/libldap/init.c
edf356
@@ -137,7 +137,9 @@ static const struct ol_attribute {
edf356
 #ifdef HAVE_GNUTLS
edf356
 	{0, ATTR_TLS,	"TLS_CRLFILE",			NULL,	LDAP_OPT_X_TLS_CRLFILE},
edf356
 #endif
edf356
-        
edf356
+#ifdef HAVE_MOZNSS_COMPATIBILITY
edf356
+	{0, ATTR_TLS,   "TLS_MOZNSS_COMPATIBILITY",     NULL,   LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY},
edf356
+#endif
edf356
 #endif
edf356
 
edf356
 	{0, ATTR_NONE,		NULL,		NULL,	0}
edf356
@@ -582,6 +584,9 @@ void ldap_int_initialize_global_options( struct ldapoptions *gopts, int *dbglvl
edf356
 	gopts->ldo_tls_connect_cb = NULL;
edf356
 	gopts->ldo_tls_connect_arg = NULL;
edf356
 	gopts->ldo_tls_require_cert = LDAP_OPT_X_TLS_DEMAND;
edf356
+#ifdef HAVE_MOZNSS_COMPATIBILITY
edf356
+	gopts->ldo_tls_moznss_compatibility = LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY_ENABLED;
edf356
+#endif
edf356
 #endif
edf356
 	gopts->ldo_keepalive_probes = 0;
edf356
 	gopts->ldo_keepalive_interval = 0;
edf356
diff --git a/libraries/libldap/ldap-int.h b/libraries/libldap/ldap-int.h
edf356
--- a/libraries/libldap/ldap-int.h
edf356
+++ b/libraries/libldap/ldap-int.h
edf356
@@ -260,7 +260,8 @@ struct ldapoptions {
edf356
    	int			ldo_tls_require_cert;
edf356
 	int			ldo_tls_impl;
edf356
    	int			ldo_tls_crlcheck;
edf356
-#define LDAP_LDO_TLS_NULLARG ,0,0,0,{0,0,0,0,0,0,0,0,0},0,0,0,0
edf356
+   	int			ldo_tls_moznss_compatibility;
edf356
+#define LDAP_LDO_TLS_NULLARG ,0,0,0,{0,0,0,0,0,0,0,0,0},0,0,0,0,0
edf356
 #else
edf356
 #define LDAP_LDO_TLS_NULLARG
edf356
 #endif
edf356
diff --git a/libraries/libldap/tls2.c b/libraries/libldap/tls2.c
edf356
--- a/libraries/libldap/tls2.c
edf356
+++ b/libraries/libldap/tls2.c
edf356
@@ -37,6 +37,8 @@
edf356
 
edf356
 #include "ldap-tls.h"
edf356
 
edf356
+#include "tls_mc.h"
edf356
+
edf356
 static tls_impl *tls_imp = &ldap_int_tls_impl;
edf356
 #define HAS_TLS( sb )	ber_sockbuf_ctrl( sb, LBER_SB_OPT_HAS_IO, \
edf356
 				(void *)tls_imp->ti_sbio )
edf356
@@ -96,6 +98,7 @@ tls_ctx_ref( tls_ctx *ctx )
edf356
  * an extra mutex for the default ctx.
edf356
  */
edf356
 static ldap_pvt_thread_mutex_t tls_def_ctx_mutex;
edf356
+ldap_pvt_thread_mutex_t tlsmc_mutex;
edf356
 #endif
edf356
 
edf356
 void
edf356
@@ -161,6 +164,7 @@ tls_init(tls_impl *impl )
edf356
 	if ( !tls_initialized++ ) {
edf356
 #ifdef LDAP_R_COMPILE
edf356
 		ldap_pvt_thread_mutex_init( &tls_def_ctx_mutex );
edf356
+		ldap_pvt_thread_mutex_init( &tlsmc_mutex );
edf356
 #endif
edf356
 	}
edf356
 
edf356
@@ -196,6 +200,27 @@ ldap_int_tls_init_ctx( struct ldapoptions *lo, int is_server )
edf356
 
edf356
 	tls_init( ti );
edf356
 
edf356
+#ifdef HAVE_MOZNSS_COMPATIBILITY
edf356
+	if ( LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY_ENABLED == lo->ldo_tls_moznss_compatibility ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "TLSMC: MozNSS compatibility interception begins.\n",
edf356
+		       0, 0, 0 );
edf356
+		if ( 0 == tlsmc_intercept_initialization( lo, is_server ) ) {
edf356
+			Debug( LDAP_DEBUG_TRACE,
edf356
+			       "TLSMC: ERROR: MozNSS compatibility layer failed.\n",
edf356
+			       0, 0, 0 );
edf356
+		}
edf356
+		lts = lo->ldo_tls_info;
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "TLSMC: MozNSS compatibility interception ends.\n",
edf356
+		       0, 0, 0 );
edf356
+	} else {
edf356
+		Debug( LDAP_DEBUG_TRACE,
edf356
+		       "TLS: MozNSS compatibility layer disabled.\n",
edf356
+		       0, 0, 0 );
edf356
+	}
edf356
+#endif
edf356
+
edf356
 	if ( is_server && !lts.lt_certfile && !lts.lt_keyfile &&
edf356
 		!lts.lt_cacertfile && !lts.lt_cacertdir ) {
edf356
 		/* minimum configuration not provided */
edf356
@@ -572,6 +597,21 @@ ldap_int_tls_config( LDAP *ld, int option, const char *arg )
edf356
 			return ldap_pvt_tls_set_option( ld, option, &i );
edf356
 		}
edf356
 		return -1;
edf356
+#endif
edf356
+#ifdef HAVE_MOZNSS_COMPATIBILITY
edf356
+	case LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY:
edf356
+		i = -1;
edf356
+		if ( (strcasecmp( arg, "yes" ) == 0) ||
edf356
+		     (strcasecmp( arg, "true" ) == 0) ||
edf356
+		     (strcasecmp( arg, "on" ) == 0) ) {
edf356
+			i = LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY_ENABLED;
edf356
+		}
edf356
+		if ( (strcasecmp( arg, "no" ) == 0) ||
edf356
+		     (strcasecmp( arg, "false" ) == 0) ||
edf356
+		     (strcasecmp( arg, "off" ) == 0)  ) {
edf356
+			i = LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY_DISABLED;
edf356
+		}
edf356
+		return ldap_pvt_tls_set_option( ld, option, &i );
edf356
 #endif
edf356
 	}
edf356
 	return -1;
edf356
@@ -675,6 +715,9 @@ ldap_pvt_tls_get_option( LDAP *ld, int option, void *arg )
edf356
 	case LDAP_OPT_X_TLS_CONNECT_ARG:
edf356
 		*(void **)arg = lo->ldo_tls_connect_arg;
edf356
 		break;
edf356
+	case LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY:
edf356
+		*(int *)arg = lo->ldo_tls_moznss_compatibility;
edf356
+		break;
edf356
 	default:
edf356
 		return -1;
edf356
 	}
edf356
@@ -802,6 +845,10 @@ ldap_pvt_tls_set_option( LDAP *ld, int option, void *arg )
edf356
 			ldap_pvt_tls_ctx_free( lo->ldo_tls_ctx );
edf356
 		lo->ldo_tls_ctx = NULL;
edf356
 		return ldap_int_tls_init_ctx( lo, *(int *)arg );
edf356
+	case LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY:
edf356
+		if ( !arg ) return -1;
edf356
+		lo->ldo_tls_moznss_compatibility = *(int *)arg;
edf356
+		break;
edf356
 	default:
edf356
 		return -1;
edf356
 	}
edf356
diff --git a/libraries/libldap/tls_mc.c b/libraries/libldap/tls_mc.c
edf356
new file mode 100644
edf356
--- /dev/null
edf356
+++ b/libraries/libldap/tls_mc.c
02ade0
@@ -0,0 +1,1369 @@
edf356
+#include "portable.h"
edf356
+
edf356
+#ifdef HAVE_MOZNSS_COMPATIBILITY
edf356
+
edf356
+#include <sys/stat.h>
edf356
+#include <sys/types.h>
edf356
+#include <ac/unistd.h>
edf356
+#include <ac/errno.h>
edf356
+#include <ac/termios.h>
edf356
+#include <fcntl.h>
edf356
+#include <dirent.h>
edf356
+
edf356
+#include <nspr/nspr.h>
edf356
+#include <nspr/private/pprio.h>
edf356
+
edf356
+#include <nss/nss.h>
edf356
+#include <nss/pk11pub.h>
edf356
+#include <nss/cert.h>
edf356
+
edf356
+#include <nss/base64.h>
edf356
+#include <nss/key.h>
edf356
+#include <nss/keyt.h>
edf356
+#include <nss/blapi.h>
edf356
+#include <nss/certdb.h>
edf356
+
edf356
+#include "ldap-int.h"
edf356
+#include "ldap-tls.h"
edf356
+#include "ldap_pvt_thread.h"
edf356
+
edf356
+#include "tls_mc.h"
edf356
+#include "tls_mc_ossl.h"
edf356
+
edf356
+#define TLSMC_CHECKSUM_LEN 32
edf356
+
edf356
+#define TLSMC_CACERTS_DIR_NAME "cacerts"
edf356
+#define TLSMC_CERT_FILE_NAME "cert.pem"
edf356
+#define TLSMC_KEY_FILE_NAME "key.pem"
edf356
+#define TLSMC_README_FILE_NAME "README"
edf356
+
edf356
+#define TLSM_CERTDB_DESC "ldap"
edf356
+#define DEFAULT_TOKEN_NAME "default"
edf356
+
edf356
+#define TLSMC_IS_INPLACE 1
edf356
+#define TLSMC_IS_TEMPORARY 2
edf356
+
edf356
+#define DONOTEDIT \
02ade0
+	"This file is auto-generated by the MozNSS Compatibility Layer of OpenLDAP software.\n" \
02ade0
+	"This layer is not part of the OpenLDAP software distributed in upstream.\n" \
02ade0
+	"Please file any related bugs with your GNU/Linux distribution.\n" \
02ade0
+	"Do not edit contents of this directory structure directly. Rather, change your configuration to use the OpenSSL-style TLS options, so that this directory structure does not need to be generated anymore.\n" \
02ade0
+	"Please refer to the slapd.conf(5), slapd-config(5), and ldap.conf(5) man pages for more information."
edf356
+#define PEM_CERT_HEADER "-----BEGIN CERTIFICATE-----"
edf356
+#define PEM_CERT_FOOTER "-----END CERTIFICATE-----"
edf356
+#define PEM_KEY_HEADER  "-----BEGIN PRIVATE KEY-----"
edf356
+#define PEM_KEY_FOOTER "-----END PRIVATE KEY-----"
02ade0
+#define README_HEADER "The contents of this file are hashed and used as part of the name of the parent directory.\n"
edf356
+
edf356
+#ifdef LDAP_R_COMPILE
edf356
+ldap_pvt_thread_mutex_t tlsmc_mutex;
edf356
+#endif
edf356
+/*******************************************************************/
edf356
+
edf356
+
edf356
+char *
edf356
+tlsmc_path2name( char *path )
edf356
+{
edf356
+    if ( NULL == path ) return NULL;
edf356
+    char *last_slash = strrchr( path, '/' );
edf356
+    if ( NULL == last_slash )
edf356
+        return path;
edf356
+    else
edf356
+        return last_slash + 1;
edf356
+}
edf356
+
edf356
+
edf356
+int
edf356
+tlsmc_write_file( char *filename, char *buf, mode_t final_mode )
edf356
+{
edf356
+	int rv = 0;
edf356
+	int fd = -1;
edf356
+	if ( 0 > ( fd = open( filename, O_WRONLY | O_CREAT | O_EXCL, S_IWUSR ) ) ) {
02ade0
+		Debug( LDAP_DEBUG_ANY,
02ade0
+		       "tlsmc_write_file: ERROR: could not open file: `%s': %s.\n",
02ade0
+		       filename, strerror(errno), 0 );
edf356
+		goto bail;
edf356
+	}
edf356
+	if ( -1 >= write( fd, buf, strlen(buf) ) ) {
02ade0
+		Debug( LDAP_DEBUG_ANY,
02ade0
+		       "tlsmc_write_file: ERROR: could not write file: `%s': %s.\n",
02ade0
+		       filename, strerror(errno), 0 );
edf356
+		goto bail;
edf356
+	}
edf356
+	if ( -1 >= fchmod( fd, final_mode ) ) {
02ade0
+		Debug( LDAP_DEBUG_ANY,
02ade0
+		       "tlsmc_write_file: ERROR: could not set file mode: `%s': %s.\n",
02ade0
+		       filename, strerror(errno), 0 );
edf356
+		goto bail;
edf356
+	}
edf356
+	if ( -1 >= fsync( fd ) ) {
02ade0
+		Debug( LDAP_DEBUG_ANY,
02ade0
+		       "tlsmc_write_file: ERROR: could not fsync file: `%s': %s.\n",
02ade0
+		       filename, strerror(errno), 0 );
edf356
+		goto bail;
edf356
+	}
edf356
+	if ( 0 > close( fd ) ) {
02ade0
+		Debug( LDAP_DEBUG_ANY,
02ade0
+		       "tlsmc_write_file: ERROR: could not close file: `%s': %s.\n",
02ade0
+		       filename, strerror(errno), 0 );
edf356
+		fd = -1;
edf356
+		goto bail;
edf356
+	}
edf356
+	fd = -1;
edf356
+	rv = 1;
edf356
+bail:
edf356
+	if ( fd > -1 ) close( fd );
edf356
+	return rv;
edf356
+}
edf356
+
edf356
+
edf356
+static int
edf356
+tlsmc_remove_dir_recursively( const char *dir_name )
edf356
+{
edf356
+	int rv = 0;
edf356
+	DIR *dir = NULL;
edf356
+	struct dirent *entry = NULL;
edf356
+	char *full_path = NULL;
edf356
+
edf356
+	Debug( LDAP_DEBUG_TRACE,
edf356
+	       "tlsmc_remove_dir_recursively: INFO: starting recursively removing directory `%s'.\n",
edf356
+	       dir_name, 0, 0 );
edf356
+	if ( NULL == ( dir = opendir( dir_name ) ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_remove_dir_recursively: ERROR: could not open the directory (errno %d: %s).\n",
edf356
+		       errno, strerror( errno ), 0 );
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	while ( NULL != ( entry = readdir( dir ) ) ) {
edf356
+		struct stat info;
edf356
+
edf356
+		full_path = NULL;
edf356
+		full_path = PR_smprintf( "%s/%s", dir_name, entry->d_name );
edf356
+
edf356
+		if ( 0 != strcmp( entry->d_name, "." ) && 0 != strcmp( entry->d_name, ".." ) ) {
edf356
+			if ( 0 == lstat( full_path, &info ) ) {
edf356
+				if ( S_ISDIR( info.st_mode ) ) {
edf356
+					Debug( LDAP_DEBUG_TRACE,
edf356
+					       "tlsmc_remove_dir_recursively: INFO: stepping into directory `%s'.\n",
edf356
+					       entry->d_name, 0, 0 );
edf356
+					if ( 0 == tlsmc_remove_dir_recursively( full_path ) ) {
edf356
+						goto bail_and_close_dir;
edf356
+					}
edf356
+				} else {
edf356
+					Debug( LDAP_DEBUG_TRACE,
edf356
+					       "tlsmc_remove_dir_recursively: INFO: removing file `%s'.\n",
edf356
+					       entry->d_name, 0, 0 );
edf356
+					if ( 0 != remove( full_path ) ) {
edf356
+						Debug( LDAP_DEBUG_ANY,
edf356
+						       "tlsmc_remove_dir_recursively: ERROR: could not remove the file (errno %d: %s).\n",
edf356
+						       errno, strerror( errno ), 0 );
edf356
+						goto bail_and_close_dir;
edf356
+					}
edf356
+				}
edf356
+			} else {
edf356
+				Debug( LDAP_DEBUG_ANY,
edf356
+				       "tlsmc_remove_dir_recursively: ERROR: could not stat `%s', (errno %d: %s).\n",
edf356
+				       full_path, errno, strerror( errno ) );
edf356
+				goto bail_and_close_dir;
edf356
+			}
edf356
+		}
edf356
+
edf356
+		if ( full_path ) {
edf356
+			PR_smprintf_free( full_path );
edf356
+			full_path = NULL;
edf356
+		}
edf356
+
edf356
+	}
edf356
+	Debug( LDAP_DEBUG_TRACE,
edf356
+	       "tlsmc_remove_dir_recursively: INFO: stepping out of the directory.\n",
edf356
+	       0, 0, 0 );
edf356
+	if ( 0 != closedir( dir ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_remove_dir_recursively: WARN: could not close the directory (errno %d: %s).\n",
edf356
+		       errno, strerror( errno ), 0 );
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	Debug( LDAP_DEBUG_TRACE,
edf356
+	       "tlsmc_remove_dir_recursively: INFO: removing the directory itself.\n",
edf356
+	       0, 0, 0 );
edf356
+	if ( 0 != remove( dir_name ) ) {
edf356
+		PRErrorCode errcode = PR_GetError();
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_remove_dir_recursively: ERROR: could not remove the directory (errno %d: %s).\n",
edf356
+		       errno, strerror( errno ), 0 );
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	rv = 1;
edf356
+	goto bail;
edf356
+bail_and_close_dir:
edf356
+	closedir( dir );
edf356
+bail:
edf356
+	if ( full_path ) PR_smprintf_free( full_path );
edf356
+	return rv;
edf356
+}
edf356
+
edf356
+
edf356
+/* BORROWED FROM tls_m.c */
edf356
+static void
edf356
+tlsmc_get_certdb_prefix( const char *certdir, char **nsscertdir, char **realcertdir, char **prefix )
edf356
+{
edf356
+	char sep = PR_GetDirectorySeparator();
edf356
+	char *ptr = NULL;
edf356
+	char *chkpath = NULL;
edf356
+	struct PRFileInfo prfi;
edf356
+	PRStatus prc;
edf356
+
edf356
+	*realcertdir = (char *)certdir; /* default is the one passed in */
edf356
+
edf356
+	/* if certdir is not given, just return */
edf356
+	if ( !certdir ) return;
edf356
+
edf356
+	*nsscertdir = certdir;
edf356
+
edf356
+	/* ignore database type prefix (e.g. sql:, dbm:) if provided */
edf356
+	if ( NULL != ( chkpath = strchr( certdir, ':' ) ) ) {
edf356
+		*realcertdir = chkpath + 1;
edf356
+	}
edf356
+
edf356
+	/* if certdir exists (file or directory) then it cannot specify a prefix */
edf356
+	prc = PR_GetFileInfo( *realcertdir, &prfi );
edf356
+	if ( prc == PR_SUCCESS ) {
edf356
+		goto finish;
edf356
+	}
edf356
+
edf356
+	/* if certdir was given, and there is a '/' in certdir, see if there
edf356
+	   is anything after the last '/' - if so, assume it is the prefix */
edf356
+	/* if ( ( ( ptr = strrchr( *realcertdir, sep ) ) ) && *(ptr + 1) ) { */
edf356
+	/* 	*realcertdir = PL_strndup( *realcertdir, ptr - (*realcertdir) ); */
edf356
+	/* 	*prefix = PL_strdup( ptr + 1 ); */
edf356
+	/* } */
edf356
+
edf356
+
edf356
+	if ( ptr = strrchr( *realcertdir, sep ) ) {
edf356
+		if ( *(ptr + 1) ) {
edf356
+			*ptr = '\0';
edf356
+			*prefix = ptr + 1;
edf356
+		} else {
edf356
+			*prefix = *realcertdir + strlen( *realcertdir );  // empty string
edf356
+		}
edf356
+	} else {
edf356
+		*prefix = *realcertdir;
edf356
+		*realcertdir = *prefix + strlen( *prefix );  //  empty string
edf356
+	}
edf356
+finish:
edf356
+	/* drop potential last '/' from realcertdir */
edf356
+	do {
edf356
+		ptr = strrchr( *realcertdir, sep );
edf356
+		if ( ptr && (! *(ptr+1) ) ) {
edf356
+			*ptr = '\0';
edf356
+		} else {
edf356
+			break;
edf356
+		}
edf356
+	} while (1);
edf356
+
edf356
+	return;
edf356
+}
edf356
+
edf356
+
edf356
+/* BORROWED FROM tls_m.c */
edf356
+static char *
edf356
+tlsmc_get_pin_from_file(const char *token_name, char *filename)
edf356
+{
edf356
+	char *pwdstr = NULL;
edf356
+	char *contents = NULL;
edf356
+	char *lasts = NULL;
edf356
+	char *line = NULL;
edf356
+	char *candidate = NULL;
edf356
+	PRFileInfo file_info;
edf356
+	PRFileDesc *pwd_fileptr = NULL;
edf356
+	pwd_fileptr = PR_Open( filename, PR_RDONLY, 00400 );
edf356
+
edf356
+	/* open the password file */
edf356
+	if ( !pwd_fileptr ) {
edf356
+		PRErrorCode errcode = PR_GetError();
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_get_pin_from_file: could not open security pin file %s - error %d:%s.\n",
edf356
+		       filename, errcode,
edf356
+		       PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
edf356
+		goto done;
edf356
+	}
edf356
+
edf356
+	/* get the file size */
edf356
+	if ( PR_SUCCESS != PR_GetFileInfo( filename, &file_info ) ) {
edf356
+		PRErrorCode errcode = PR_GetError();
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_get_pin_from_file: could not get file info from pin file %s - error %d:%s.\n",
edf356
+		       filename, errcode,
edf356
+		       PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
edf356
+		goto done;
edf356
+	}
edf356
+
edf356
+	/* create a buffer to hold the file contents */
edf356
+	if ( !( contents = PR_CALLOC( file_info.size + 1 ) ) ) {
edf356
+		PRErrorCode errcode = PR_GetError();
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_get_pin_from_file: could not alloc a buffer for contents of pin file %s - error %d:%s.\n",
edf356
+		       filename, errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
edf356
+		goto done;
edf356
+	}
edf356
+
edf356
+	/* read file into the buffer */
edf356
+	if( PR_Read( pwd_fileptr, contents, file_info.size ) <= 0 ) {
edf356
+		PRErrorCode errcode = PR_GetError();
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_get_pin_from_file: could not read the file contents from pin file %s - error %d:%s.\n",
edf356
+		       filename, errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
edf356
+		goto done;
edf356
+	}
edf356
+
edf356
+	/* format is [tokenname:]password EOL [tokenname:]password EOL ... */
edf356
+	/* if you want to use a password containing a colon character, use
edf356
+	   the special tokenname "default" */
edf356
+	for ( line = PL_strtok_r( contents, "\r\n", &lasts ); line;
edf356
+	      line = PL_strtok_r( NULL, "\r\n", &lasts ) ) {
edf356
+		char *colon;
edf356
+
edf356
+		if ( !*line ) {
edf356
+			continue; /* skip blank lines */
edf356
+		}
edf356
+		colon = PL_strchr( line, ':' );
edf356
+		if ( colon ) {
edf356
+			if ( *(colon + 1) && token_name &&
edf356
+			     !PL_strncmp( token_name, line, colon-line ) ) {
edf356
+				candidate = colon + 1; /* found a definite match */
edf356
+				break;
edf356
+			} else if ( !PL_strncmp( DEFAULT_TOKEN_NAME, line, colon-line ) ) {
edf356
+				candidate = colon + 1; /* found possible match */
edf356
+			}
edf356
+		} else { /* no token name */
edf356
+			candidate = line;
edf356
+		}
edf356
+	}
edf356
+done:
edf356
+	if ( pwd_fileptr ) {
edf356
+		PR_Close( pwd_fileptr );
edf356
+	}
edf356
+	if ( candidate ) {
edf356
+		pwdstr = PL_strdup( candidate );
edf356
+	}
edf356
+	PL_strfree( contents );
edf356
+
edf356
+	return pwdstr;
edf356
+}
edf356
+
edf356
+
edf356
+/* BORROWED FROM tls_m.c */
edf356
+/*
edf356
+ * Turn the echoing off on a tty.
edf356
+ */
edf356
+static void
edf356
+echoOff(int fd)
edf356
+{
edf356
+	if ( isatty( fd ) ) {
edf356
+		struct termios tio;
edf356
+		tcgetattr( fd, &tio );
edf356
+		tio.c_lflag &= ~ECHO;
edf356
+		tcsetattr( fd, TCSAFLUSH, &tio );
edf356
+	}
edf356
+}
edf356
+
edf356
+/* BORROWED FROM tls_m.c */
edf356
+/*
edf356
+ * Turn the echoing on on a tty.
edf356
+ */
edf356
+static void
edf356
+echoOn(int fd)
edf356
+{
edf356
+	if ( isatty( fd ) ) {
edf356
+		struct termios tio;
edf356
+		tcgetattr( fd, &tio );
edf356
+		tio.c_lflag |= ECHO;
edf356
+		tcsetattr( fd, TCSAFLUSH, &tio );
edf356
+		tcsetattr( fd, TCSAFLUSH, &tio );
edf356
+	}
edf356
+}
edf356
+
edf356
+
edf356
+/* BORROWED FROM tls_m.c */
edf356
+char *
edf356
+tlsmc_get_pin( PK11SlotInfo *slot, PRBool retry, void * filename)
edf356
+{
edf356
+	char *token_name = NULL;
edf356
+	char *pwdstr = NULL;
edf356
+
edf356
+	token_name = PK11_GetTokenName( slot );
edf356
+	/* Try to get the passwords from the password file if it exists.
edf356
+	 * THIS IS UNSAFE and is provided for convenience only. Without this
edf356
+	 * capability the server would have to be started in foreground mode
edf356
+	 * if using an encrypted key.
edf356
+	 */
edf356
+	if ( filename ) {
edf356
+		fprintf( stderr,
edf356
+		         "tlsmc_get_pin: INFO: Please note the extracted key file will not be protected with a PIN any more, however it will be still protected at least by file permissions.\n");
edf356
+		pwdstr = tlsmc_get_pin_from_file( token_name, (char *)filename );
edf356
+		if ( retry && pwdstr != NULL )
edf356
+			return NULL;
edf356
+	}
edf356
+	if ( !pwdstr ) {
edf356
+		int infd = PR_FileDesc2NativeHandle( PR_STDIN );
edf356
+		int isTTY = isatty( infd );
edf356
+		unsigned char phrase[200];
edf356
+		/* Prompt for password */
edf356
+		if ( isTTY ) {
edf356
+			fprintf( stderr,
edf356
+			         "tlsmc_get_pin: INFO: Please note the extracted key file will not be protected with a PIN any more, however it will be still protected at least by file permissions.\n");
edf356
+			fprintf( stdout,
edf356
+				 "Please enter pin, password, or pass phrase for security token '%s': ",
edf356
+				 token_name ? token_name : DEFAULT_TOKEN_NAME );
edf356
+			echoOff( infd );
edf356
+		}
edf356
+		fgets( (char*)phrase, sizeof(phrase), stdin );
edf356
+		if ( isTTY ) {
edf356
+			fprintf( stdout, "\n" );
edf356
+			echoOn( infd );
edf356
+		}
edf356
+		/* stomp on newline */
edf356
+		phrase[strlen((char*)phrase)-1] = 0;
edf356
+
edf356
+		pwdstr = PL_strdup( (char*)phrase );
edf356
+	}
edf356
+
edf356
+	return pwdstr;
edf356
+}
edf356
+
edf356
+
edf356
+int
edf356
+tlsmc_hash( char **dest, const char *src )
edf356
+{
edf356
+	int rv = 0;
edf356
+	unsigned char fp[SHA256_LENGTH];
edf356
+	SECItem fpItem;
edf356
+
edf356
+	if ( SECSuccess != ( rv = PK11_HashBuf( SEC_OID_SHA256, fp, src, strlen(src) ) ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_hash: could not hash a buffer",
edf356
+		       NULL, NULL, NULL );
edf356
+		goto bail;
edf356
+	}
edf356
+	fpItem.data = fp;
edf356
+	fpItem.len = SHA256_LENGTH;
edf356
+	if ( NULL == ( *dest = CERT_Hexify( &fpItem, 0 ) ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_hash: could not hexify the hash",
edf356
+		       NULL, NULL, NULL );
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	rv = 1;
edf356
+bail:
edf356
+	return rv;
edf356
+}
edf356
+
edf356
+
edf356
+/* BORROWED FROM tls_m.c */
edf356
+int
edf356
+tlsmc_open_nssdb( char *ld_cacertdir, NSSInitContext **out_initctx, char **out_nssdb_dir, char **out_nssdb_prefix )
edf356
+{
edf356
+#define SECURITYDIRS_COUNT 3
edf356
+	int rc = 0;
edf356
+	PRErrorCode errcode = 1;
edf356
+
edf356
+	/* restart secmod modules */
edf356
+#ifdef HAVE_SECMOD_RESTARTMODULES
edf356
+	/* NSS enforces the pkcs11 requirement that modules should be unloaded after
edf356
+	   a fork() - since there is no portable way to determine if NSS has been
edf356
+	   already initialized in a parent process, we just call SECMOD_RestartModules
edf356
+	   with force == FALSE - if the module has been unloaded due to a fork, it will
edf356
+	   be reloaded, otherwise, it is a no-op */
edf356
+	if ( 0 == ( rc = SECMOD_RestartModules(PR_FALSE /* do not force */) ) ) {
edf356
+		errcode = PORT_GetError();
edf356
+		if ( errcode != SEC_ERROR_NOT_INITIALIZED ) {
edf356
+			Debug( LDAP_DEBUG_TRACE,
edf356
+			       "tlsmc_open_nssdb: WARN: could not restart the security modules: %d:%s.\n",
edf356
+			       errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ), 0 );
edf356
+		} else {
edf356
+			errcode = 1;
edf356
+		}
edf356
+	}
edf356
+#endif
edf356
+
edf356
+
edf356
+	/* context and certdb */
edf356
+	char *dir_moznss = PR_GetEnv( "MOZNSS_DIR" );
edf356
+	char *dir_default_moznss = PR_GetEnv( "DEFAULT_MOZNSS_DIR" );
edf356
+	const char *securitydirs[SECURITYDIRS_COUNT] = { dir_moznss, ld_cacertdir, dir_default_moznss };
edf356
+	int done = 0;
edf356
+	int ii = 0;
edf356
+	for ( ii = 0; !done && ( ii < SECURITYDIRS_COUNT ); ++ii ) {
edf356
+		// get certdb prefix
edf356
+		const char *securitydir = securitydirs[ii];
edf356
+		char *nsscertdir = NULL;
edf356
+		char *realcertdir = NULL;
edf356
+		const char *defprefix = "";
edf356
+		char *prefix = (char *)defprefix;
edf356
+		if ( securitydir == NULL ) continue;
edf356
+		tlsmc_get_certdb_prefix( securitydir, &nsscertdir, &realcertdir, &prefix );
edf356
+		*out_nssdb_dir = strdup( realcertdir );
edf356
+		*out_nssdb_prefix = strdup( prefix );
edf356
+
edf356
+		Debug( LDAP_DEBUG_TRACE,
edf356
+		       "tlsmc_open_nssdb: INFO: trying to initialize moznss using security dir `%s` prefix `%s`.\n",
edf356
+		       nsscertdir, prefix, NULL);
edf356
+
edf356
+		// init context
edf356
+		NSSInitContext *initctx = NULL;
edf356
+		NSSInitParameters initparams;
edf356
+		memset( &initparams, 0, sizeof( initparams ) );
edf356
+		initparams.length = sizeof( initparams );
edf356
+
edf356
+		initctx = NSS_InitContext( nsscertdir,
edf356
+		                           prefix,
edf356
+		                           prefix,
edf356
+		                           SECMOD_DB,
edf356
+		                           &initparams,
edf356
+		                           NSS_INIT_READONLY // | NSS_INIT_NOCERTDB
edf356
+			);
edf356
+		rc = initctx ? 1 : 0;
edf356
+
edf356
+		*out_initctx = initctx;
edf356
+
edf356
+		if ( rc != 1 ) {
edf356
+			errcode = PORT_GetError();
edf356
+			Debug( LDAP_DEBUG_ANY,
edf356
+			       "tlsmc_open_nssdb: WARN: could not initialize MozNSS context - error %d.\n",
edf356
+			       errcode, 0, 0 );
edf356
+		} else {
edf356
+			Debug( LDAP_DEBUG_TRACE,
edf356
+			       "tlsmc_open_nssdb: INFO: initialized MozNSS context.\n",
edf356
+			       0, 0, 0 );
edf356
+			errcode = 0;
edf356
+			done = 1;
edf356
+		}
edf356
+	}
edf356
+
edf356
+	PR_Free( dir_moznss );
edf356
+	PR_Free( dir_default_moznss );
edf356
+
edf356
+	PK11_SetPasswordFunc( tlsmc_get_pin );
edf356
+
edf356
+	if ( done && (errcode == 0) && out_initctx ) {
edf356
+		return 1;
edf356
+	} else {
edf356
+		return 0;
edf356
+	}
edf356
+}
edf356
+
edf356
+
edf356
+int
edf356
+tlsmc_filestamp( char **filestamp, char *path )
edf356
+{
edf356
+	int rv = 0;
edf356
+	struct stat attr;
edf356
+	char stime[20];
edf356
+
edf356
+	if ( 0 != stat( path, &attr ) ) {
edf356
+		rv = -1;
edf356
+		goto bail;
edf356
+	}
edf356
+	if ( 0 == strftime(stime, sizeof(stime), "%FT%T", localtime(&attr.st_mtime)) ) {
02ade0
+		Debug( LDAP_DEBUG_ANY,
02ade0
+		       "tlsmc_filestamp: ERROR: could not format mtime: %s.\n",
02ade0
+		       strerror(errno), 0, 0 );
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	*filestamp = PR_smprintf("mtime %s.%d, size %lld",
edf356
+	                         stime, attr.st_mtim.tv_nsec, (long long) attr.st_size);
edf356
+
edf356
+	rv = 1;
edf356
+bail:
edf356
+	return rv;
edf356
+}
edf356
+
edf356
+
edf356
+char *
edf356
+tlsmc_compute_checksum( char *nssdb_dir, char *nssdb_prefix,
edf356
+                        char *ld_cacertdir, char *ld_cert, char *ld_key,
edf356
+                        char **out_data)
edf356
+{
edf356
+	int rv = 0;
edf356
+	char *data = NULL;
edf356
+	char *checksum = NULL;
edf356
+
edf356
+	/* gather data */
edf356
+	data = PR_sprintf_append( data,
edf356
+	                          DONOTEDIT "\n"
edf356
+	                          README_HEADER "\n"
edf356
+	                          "PARAMETERS:\n"
edf356
+	                          "nssdb_dir: %s\n"
edf356
+	                          "nssdb_prefix: %s\n"
edf356
+	                          "ld_cacertdir: %s\n"
edf356
+	                          "ld_cert: %s\n"
edf356
+	                          "ld_key: %s\n"
edf356
+	                          "euid: %d\n\n"
edf356
+	                          "FILES:\n",
edf356
+	                          nssdb_dir, nssdb_prefix, ld_cacertdir, ld_cert, ld_key, geteuid() );
edf356
+
edf356
+	char *files[] = { "cert8.db", "cert9.db", "key3.db", "key4.db", "secmod.db", NULL };
edf356
+	char **filep = NULL;
edf356
+	for ( filep = files; NULL != *filep; filep++ ) {
edf356
+		char *filestamp = NULL;
edf356
+		char *path = NULL;
edf356
+		path = PR_smprintf( "%s/%s%s", nssdb_dir, nssdb_prefix, *filep );
edf356
+		if ( 0 == tlsmc_filestamp( &filestamp, path ) ) {
edf356
+			Debug( LDAP_DEBUG_ANY,
edf356
+			       "tlsmc_compute_checksum: INFO: could not check file `%s'.\n",
edf356
+			       path, 0, 0 );
edf356
+			rv = -1;
edf356
+		} else {
edf356
+			data = PR_sprintf_append( data, "%s: %s\n", *filep, filestamp );
edf356
+		}
edf356
+		if ( filestamp ) PR_smprintf_free( filestamp );
edf356
+		if ( path ) PR_smprintf_free( path );
edf356
+		if ( -1 == rv ) goto bail;
edf356
+	}
edf356
+
edf356
+	/* compute data checksum */
edf356
+	if ( 1 != tlsmc_hash( &checksum, (const char*) data ) ) {
edf356
+		checksum = NULL;
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	/* possibly supply data */
edf356
+	if ( out_data ) {
edf356
+		*out_data = strdup( data );
edf356
+	}
edf356
+
edf356
+	/* return checksum */
edf356
+bail:
edf356
+	if ( data ) PR_smprintf_free( data );
edf356
+	return checksum;
edf356
+}
edf356
+
edf356
+
edf356
+int
edf356
+tlsmc_prepare_dir( char *dir )
edf356
+{
edf356
+	int rv = 0;
edf356
+	char *cacerts_dir = NULL;
edf356
+
edf356
+	Debug( LDAP_DEBUG_TRACE,
edf356
+	       "tlsmc_prepare_dir: INFO: preparing PEM directory `%s'.\n",
edf356
+	       dir, 0, 0 );
edf356
+
edf356
+	if ( 0 != mkdir( dir, S_IRWXU /* u+rwx */ ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
02ade0
+		       "tlsmc_prepare_dir: WARN: could not create the PEM directory.\n",
edf356
+		       0, 0, 0 );
edf356
+		rv = 0;
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	Debug( LDAP_DEBUG_TRACE,
edf356
+	       "tlsmc_prepare_dir: INFO: creating a subdirectory `%s'.\n",
edf356
+	       TLSMC_CACERTS_DIR_NAME, 0, 0 );
edf356
+	cacerts_dir = PR_smprintf( "%s/%s", dir, TLSMC_CACERTS_DIR_NAME );
edf356
+	if ( 0 != mkdir( cacerts_dir, S_IRWXU /* u+rwx */ ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
02ade0
+		       "tlsmc_prepare_dir: WARN: could not create the subdirectory.\n",
edf356
+		       0, 0, 0 );
edf356
+		rv = 0;
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	Debug( LDAP_DEBUG_TRACE,
edf356
+	       "tlsmc_prepare_dir: INFO: successfully created PEM directory structure.\n",
edf356
+	       TLSMC_CACERTS_DIR_NAME, 0, 0 );
edf356
+	rv = 1;
edf356
+
edf356
+bail:
edf356
+	if ( cacerts_dir ) PR_smprintf_free( cacerts_dir );
edf356
+	return rv;
edf356
+}
edf356
+
edf356
+
edf356
+/* BORROWED FROM 389ds: ssl.c */
edf356
+int
edf356
+tlsmc_extract_cert_to_file(CERTCertDBHandle *certdb_handle, CERTCertificate *cert, char *file_path)
edf356
+{
edf356
+	int rv = 0;
edf356
+	SECItem data;
edf356
+	char *b64 = NULL;
edf356
+	char *output = NULL;
edf356
+
edf356
+	if ( ! cert ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_extract_cert_to_file: ERROR: cert empty.\n",
edf356
+		       0, 0, 0 );
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	data.data = cert->derCert.data;
edf356
+	data.len = cert->derCert.len;
edf356
+	b64 = BTOA_DataToAscii(data.data, data.len);
edf356
+	if ( ! b64 ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_extract_cert_to_file: ERROR: could not base64 encode.\n",
edf356
+		       0, 0, 0 );
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	output = PR_smprintf( DONOTEDIT "\n\n"
edf356
+	                      "NSS nickname: %s\n"
edf356
+	                      "Issuer: %s\n"
edf356
+	                      "Subject: %s\n"
edf356
+	                      PEM_CERT_HEADER "\n"
edf356
+	                      "%s\n"
edf356
+	                      PEM_CERT_FOOTER "\n",
edf356
+	                      cert->nickname,
edf356
+	                      cert->issuerName,
edf356
+	                      cert->subjectName,
edf356
+	                      b64 );
edf356
+
edf356
+	if ( 0 == tlsmc_write_file( file_path, output, S_IRUSR ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_extract_cert_to_file: ERROR: could not write certificate.\n",
edf356
+		       0, 0, 0 );
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	rv = 1;
edf356
+bail:
edf356
+	if ( output ) PR_smprintf_free( output );
edf356
+	if ( b64 ) PORT_Free( b64 );
edf356
+	return rv;
edf356
+}
edf356
+
edf356
+
edf356
+/* BORROWED FROM 389ds: ssl.c */
edf356
+int
edf356
+tlsmc_decrypt_key(SECKEYEncryptedPrivateKeyInfo *epki,
edf356
+                  SECOidTag algTag,
edf356
+                  SECItem *pwitem,
edf356
+                  void *pin_arg,
edf356
+                  SECItem *derPKI)
edf356
+{
edf356
+	SECItem  *cryptoParam = NULL;
edf356
+	PK11SymKey *symKey = NULL;
edf356
+	PK11Context *ctx = NULL;
edf356
+	int rv = 0;
edf356
+
edf356
+	if (!pwitem) return rv;
edf356
+
edf356
+	do {
edf356
+		SECAlgorithmID algid = epki->algorithm;
edf356
+		CK_MECHANISM_TYPE cryptoMechType;
edf356
+		CK_ATTRIBUTE_TYPE operation = CKA_DECRYPT;
edf356
+		PK11SlotInfo *slot = NULL;
edf356
+
edf356
+		cryptoMechType = PK11_GetPBECryptoMechanism(&algid, &cryptoParam, pwitem);
edf356
+		if (cryptoMechType == CKM_INVALID_MECHANISM)  {
edf356
+			goto bail;
edf356
+		}
edf356
+
edf356
+		if ( NULL == ( slot = PK11_GetBestSlot(cryptoMechType, NULL) ) ) {
edf356
+			goto bail;
edf356
+		}
edf356
+
edf356
+		if ( NULL == ( symKey = PK11_PBEKeyGen(slot, &algid, pwitem, PR_FALSE, pin_arg) ) ) {
edf356
+			rv = -1;
edf356
+			goto bail_one;
edf356
+		}
edf356
+
edf356
+		if ( NULL == ( ctx = PK11_CreateContextBySymKey(
edf356
+			               cryptoMechType, operation, symKey, cryptoParam) ) ) {
edf356
+			rv = -1;
edf356
+			goto bail_one;
edf356
+		}
edf356
+
edf356
+		if ( SECSuccess != PK11_CipherOp(ctx,
edf356
+		                                 derPKI->data, /* out */
edf356
+		                                 (int *)(&derPKI->len), /* out len */
edf356
+		                                 (int)epki->encryptedData.len, /* max out */
edf356
+		                                 epki->encryptedData.data,      /* in */
edf356
+		                                 (int)epki->encryptedData.len /* in len */ ) ) {
edf356
+			rv = -1;
edf356
+			goto bail_one;
edf356
+		}
edf356
+
edf356
+		if ( derPKI->len != epki->encryptedData.len ) goto bail_one;
edf356
+		if ( SECSuccess != PK11_Finalize(ctx) ) goto bail_one;
edf356
+	bail_one:
edf356
+		if (slot) PK11_FreeSlot(slot);
edf356
+	} while (0);
edf356
+
edf356
+	rv = ((rv == 0) ? 1 : 0);
edf356
+bail:
edf356
+	if (symKey) PK11_FreeSymKey(symKey);
edf356
+	if (cryptoParam) {
edf356
+		SECITEM_ZfreeItem(cryptoParam, PR_TRUE);
edf356
+		cryptoParam = NULL;
edf356
+	}
edf356
+	if (ctx) PK11_DestroyContext(ctx, PR_TRUE);
edf356
+
edf356
+	return rv;
edf356
+}
edf356
+
edf356
+
edf356
+/* BORROWED FROM 389ds: ssl.c */
edf356
+int
edf356
+tlsmc_extract_key_of_cert_to_file(CERTCertificate *cert,
edf356
+                                  char *pin_filename,
edf356
+                                  char *filename)
edf356
+{
edf356
+	int rv = 0;
edf356
+	SECKEYPrivateKey *key = NULL;
edf356
+	SECItem pwitem;
edf356
+	SECKEYEncryptedPrivateKeyInfo *epki = NULL;
edf356
+	PLArenaPool *arenaForPKI = NULL;
edf356
+	SECItem clearKeyDER;
edf356
+	char *b64 = NULL;
edf356
+	char *output = NULL;
edf356
+
edf356
+	// establish password
edf356
+	pwitem.data = "secretpw";  // FIXME use pin_filename
edf356
+	pwitem.len = strlen(pwitem.data);
edf356
+	pwitem.type = siBuffer;
edf356
+
edf356
+	// get key
edf356
+	if ( NULL == ( key = PK11_FindKeyByAnyCert(cert, (void *)pin_filename) ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_extract_key_of_cert_to_file: ERROR: PK11_FindKeyByAnyCert failed.\n",
edf356
+		       0,0,0);
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	// get key info
edf356
+	if ( NULL == ( epki = PK11_ExportEncryptedPrivKeyInfo(
edf356
+		               NULL, SEC_OID_DES_EDE3_CBC, &pwitem, key, 1000, (void *)pin_filename) ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_extract_key_of_cert_to_file: ERROR: PK11_ExportEncryptedPrivKeyInfo returned NULL.\n",
edf356
+		       0,0,0);
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	// get clear DER
edf356
+	if ( NULL == ( arenaForPKI = PORT_NewArena(2048) ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_extract_key_of_cert_to_file: ERROR: PORT_NewArena failed.\n",
edf356
+		       0,0,0);
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	clearKeyDER.data = PORT_ArenaAlloc(arenaForPKI, epki->encryptedData.len);
edf356
+	clearKeyDER.len = epki->encryptedData.len;
edf356
+	clearKeyDER.type = siBuffer;
edf356
+
edf356
+	if ( 0 == tlsmc_decrypt_key(epki, SEC_OID_DES_EDE3_CBC, &pwitem,
edf356
+	                                     (void *)pin_filename, &clearKeyDER) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_extract_key_of_cert_to_file: ERROR: could not decrypt the key.\n",
edf356
+		       0,0,0);
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	// base64 encode
edf356
+	if ( NULL == ( b64 = BTOA_ConvertItemToAscii(&clearKeyDER) ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_extract_key_of_cert_to_file: ERROR: could not base64 encode.\n",
edf356
+		       0,0,0);
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	// print out
edf356
+	output = PR_smprintf( DONOTEDIT "\n"
edf356
+	                      PEM_KEY_HEADER "\n"
edf356
+	                      "%s\n"
edf356
+	                      PEM_KEY_FOOTER "\n",
edf356
+	                      b64 );
edf356
+
edf356
+	if ( 0 == tlsmc_write_file( filename, output, S_IRUSR ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_extract_key_of_cert_to_file: ERROR: could not write PK.\n",
edf356
+		       0, 0, 0 );
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	rv = 1;
edf356
+
edf356
+bail:
edf356
+	if (b64) PORT_Free(b64);
edf356
+	if (arenaForPKI) PORT_FreeArena(arenaForPKI, PR_FALSE);
edf356
+	if (epki) SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE);
edf356
+	if (key) SECKEY_DestroyPrivateKey(key);
edf356
+	return rv;
edf356
+}
edf356
+
edf356
+
edf356
+/* BORROWED FROM 389ds: ssl.c */
edf356
+int
edf356
+tlsmc_extract_cert_key_pair(char *nickname, char *pin_filename, char *dir_name)
edf356
+{
edf356
+	int rv = 0;
edf356
+	CERTCertDBHandle *certHandle = NULL;
edf356
+	CERTCertificate *cert = NULL;
edf356
+	char *cert_file_path = NULL;
edf356
+	char *key_file_path = NULL;
edf356
+	char *file_realpath = NULL;
edf356
+
edf356
+
edf356
+	cert_file_path = PR_smprintf( "%s/cert.pem", dir_name );
edf356
+	key_file_path = PR_smprintf( "%s/key.pem", dir_name );
edf356
+
edf356
+	if ( NULL == nickname ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_extract_cert_key_pair: WARN: supplied nickname is empty (NULL).\n",
edf356
+		       0, 0, 0 );
edf356
+		rv = 1;
edf356
+		goto bail;
edf356
+	}
edf356
+	if ( NULL == ( certHandle = CERT_GetDefaultCertDB() ) ) {
edf356
+		// FIXME see same in tlsmc_extract_cacerts()
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_extract_cert_key_pair: ERROR: could not get certificate handle.\n",
edf356
+		       0, 0, 0 );
edf356
+		goto bail;
edf356
+	}
edf356
+	if ( NULL != ( cert = PK11_FindCertFromNickname(nickname, NULL) ) ) {
edf356
+		/* extract cert/key from NSS db */
edf356
+
edf356
+		Debug( LDAP_DEBUG_TRACE,
edf356
+		       "tlsmc_extract_cert_key_pair: INFO: extracting certificate `%s' to file `%s'.\n",
edf356
+		       nickname, cert_file_path, 0 );
edf356
+		if ( 0 == tlsmc_extract_cert_to_file(certHandle, cert, cert_file_path) ) {
edf356
+			Debug( LDAP_DEBUG_ANY,
edf356
+			       "tlsmc_extract_cert_key_pair: ERROR: could not extract certificate.\n",
edf356
+			       0, 0, 0 );
edf356
+			goto bail;
edf356
+		}
edf356
+
edf356
+		Debug( LDAP_DEBUG_TRACE,
edf356
+		       "tlsmc_extract_cert_key_pair: INFO: extracting associated PK to file `%s'.\n",
edf356
+		       key_file_path, 0, 0 );
edf356
+		if ( 0 == tlsmc_extract_key_of_cert_to_file( cert, pin_filename, key_file_path ) ) {
edf356
+			Debug( LDAP_DEBUG_ANY,
edf356
+			       "tlsmc_extract_cert_key_pair: ERROR: could not extract PK.\n",
edf356
+			       0, 0, 0 );
edf356
+			goto bail;
edf356
+		}
edf356
+	} else {
edf356
+		/* symlink PEM cert/key PEM files */
edf356
+
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_extract_cert_key_pair: INFO: could not find certificate with nickname `%s', expecting a PEM file.\n",
edf356
+		       nickname, 0, 0 );
edf356
+
edf356
+		Debug( LDAP_DEBUG_TRACE,
edf356
+		       "tlsmc_extract_cert_key_pair: INFO: symlinking certificate file `%s' to file `%s'.\n",
edf356
+		       nickname, cert_file_path, 0 );
edf356
+		if ( NULL == ( file_realpath = realpath( nickname, NULL ) ) ) {
02ade0
+			Debug( LDAP_DEBUG_ANY,
02ade0
+			       "tlsmc_extract_cert_key_pair: ERROR: could not get realpath of certificate file `%s'.\n",
02ade0
+			       nickname, 0, 0 );
edf356
+			goto bail;
edf356
+		}
edf356
+		if ( -1 == symlink( file_realpath, cert_file_path ) ) {
02ade0
+			Debug( LDAP_DEBUG_ANY,
02ade0
+			       "tlsmc_extract_cert_key_pair: ERROR: could not create a symlink of `%s' to `%s'.\n",
02ade0
+			       file_realpath, cert_file_path, 0 );
edf356
+			goto bail;
edf356
+		}
edf356
+		if ( file_realpath ) free( file_realpath );
edf356
+
edf356
+		Debug( LDAP_DEBUG_TRACE,
edf356
+		       "tlsmc_extract_cert_key_pair: INFO: symlinking PK file `%s' to file `%s'.\n",
edf356
+		       pin_filename, key_file_path, 0 );
edf356
+		if ( NULL == ( file_realpath = realpath( pin_filename, NULL ) ) ) {
02ade0
+			Debug( LDAP_DEBUG_ANY,
02ade0
+			       "tlsmc_extract_cert_key_pair: ERROR: could not get realpath of PK file `%s'.\n",
02ade0
+			       pin_filename, 0, 0 );
edf356
+			goto bail;
edf356
+		}
edf356
+		if ( -1 == symlink( file_realpath, key_file_path ) ) {
02ade0
+			Debug( LDAP_DEBUG_ANY,
02ade0
+			       "tlsmc_extract_cert_key_pair: ERROR: could not create a symlink of `%s' to `%s'.\n",
02ade0
+			       file_realpath, key_file_path, 0 );
edf356
+			goto bail;
edf356
+		}
edf356
+	}
edf356
+
edf356
+	rv = 1;
edf356
+
edf356
+bail:
edf356
+	if (file_realpath) free(file_realpath);
edf356
+	if (key_file_path) PR_smprintf_free(key_file_path);
edf356
+	if (cert_file_path) PR_smprintf_free(cert_file_path);
edf356
+	if (cert) CERT_DestroyCertificate(cert);
edf356
+	return rv;
edf356
+}
edf356
+
edf356
+
edf356
+/* BORROWED FROM 389ds: ssl.c */
edf356
+int
edf356
+tlsmc_extract_cacerts( char *dir_name )
edf356
+{
edf356
+	int rv = 0;
edf356
+	CERTCertDBHandle *certHandle = NULL;
edf356
+	CERTCertListNode *node = NULL;
edf356
+	CERTCertList *list = NULL;
edf356
+	char *cacerts_dir = NULL;
edf356
+	int cert_cnt = 0;
edf356
+
edf356
+	cacerts_dir = PR_smprintf( "%s/" TLSMC_CACERTS_DIR_NAME, dir_name );
edf356
+
edf356
+	certHandle = CERT_GetDefaultCertDB();  // FIXME maybe we should really use certdb_slot?
edf356
+	if ( ! certHandle ) {
edf356
+		Debug( LDAP_DEBUG_TRACE,
edf356
+		       "tlsmc_extract_cacerts: could not get certificate database handle.\n",
edf356
+		       0, 0, 0);
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	list = PK11_ListCerts(PK11CertListAll, NULL);
edf356
+	if ( ! list ) {
edf356
+		Debug( LDAP_DEBUG_TRACE,
edf356
+		       "tlsmc_extract_cacerts: could not get list of certificates.\n",
edf356
+		       0, 0, 0);
edf356
+		goto bail;
edf356
+	}
edf356
+	for ( node = CERT_LIST_HEAD(list);
edf356
+	      !CERT_LIST_END(node, list);
edf356
+	      node = CERT_LIST_NEXT(node)) {
edf356
+
edf356
+		CERTCertificate *cert = NULL;
edf356
+		CERTCertTrust trust;
edf356
+		char *cert_file_path = NULL;
edf356
+		int is_ca = 0;
edf356
+
edf356
+		cert = node->cert;
edf356
+		if ( SECFailure == CERT_GetCertTrust( cert, &trust ) ) {
edf356
+			Debug( LDAP_DEBUG_ANY,
edf356
+			       "tlsmc_extract_cacerts: ERROR: could not get trust flags of certificate nick=`%s'.\n",
edf356
+			       cert->nickname, 0, 0);
edf356
+			goto bail;
edf356
+		}
edf356
+		if (trust.sslFlags &
edf356
+		    (CERTDB_VALID_CA | CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA)) {
edf356
+			is_ca = 1;
edf356
+		}
edf356
+
edf356
+		Debug(LDAP_DEBUG_TRACE,
edf356
+		      "tlsmc_extract_cacerts: INFO: found cert nick=`%s'%s.\n",
edf356
+		      cert->nickname, is_ca ? ", a trusted CA" : ", _not_ a trusted CA, skipping", 0);
edf356
+		if ( ! is_ca ) continue;
edf356
+
edf356
+		cert_file_path = PR_smprintf( "%s/cert%d.pem", cacerts_dir, cert_cnt );
edf356
+		cert_cnt++;
edf356
+		Debug(LDAP_DEBUG_TRACE,
edf356
+		      "tlsmc_extract_cacerts: INFO: extracting cert nick=`%s' to file `%s'.\n",
edf356
+		      cert->nickname, cert_file_path, 0);
edf356
+		if ( 0 == tlsmc_extract_cert_to_file( certHandle, cert, cert_file_path ) ) {
edf356
+			Debug( LDAP_DEBUG_ANY,
edf356
+			       "tlsmc_extract_cacerts: ERROR: could not extract the certificate.\n",
edf356
+			       0, 0, 0);
edf356
+			goto bail_one;
edf356
+		}
edf356
+		if ( 0 == tlsmc_cert_create_hash_symlink( cert_file_path, cacerts_dir ) ) {
edf356
+			Debug( LDAP_DEBUG_ANY,
edf356
+			       "tlsmc_extract_cacerts: ERROR: could not rehash the certificate.\n",
edf356
+			       0, 0, 0);
edf356
+			goto bail_one;
edf356
+		}
edf356
+		if ( cert_file_path ) PR_smprintf_free( cert_file_path );
edf356
+		continue;
edf356
+	bail_one:
edf356
+		if ( cert_file_path ) PR_smprintf_free( cert_file_path );
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	rv = 1;
edf356
+bail:
edf356
+	if ( cacerts_dir ) PR_smprintf_free( cacerts_dir );
edf356
+	if ( list ) CERT_DestroyCertList( list );
edf356
+	return rv;
edf356
+}
edf356
+
edf356
+
edf356
+/* returns 1 if successfull;
edf356
+   returns -1 if only cert-key pair is NULL or could not be extracted;
edf356
+   returns 0 if any other error
edf356
+*/
edf356
+int
edf356
+tlsmc_extract_nssdb( char *dir_name, char **ld_cacertdir, char **ld_cert, char **ld_key )
edf356
+{
edf356
+	int rv = 0;
edf356
+
edf356
+	if ( ! dir_name ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_extract_nssdb: FATAL: target dir name empty.\n",
edf356
+		       0, 0, 0 );
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	if ( 0 == tlsmc_extract_cacerts( dir_name ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_extract_nssdb: ERROR: could not export CA certificates.\n",
edf356
+		       0, 0, 0 );
d0f00d
+		goto bail;
edf356
+	}
edf356
+
edf356
+	if ( 0 == tlsmc_extract_cert_key_pair( *ld_cert, *ld_key, dir_name ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_extract_nssdb: ERROR: could not export user cert and/or key.\n",
edf356
+		       0, 0, 0 );
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	rv = 1;
edf356
+bail:
edf356
+	return rv;
edf356
+}
edf356
+
edf356
+
edf356
+/* BORROWED FROM tls_m.c */
edf356
+int
edf356
+tlsmc_close_nssdb(NSSInitContext **initctx)
edf356
+{
edf356
+	if ( *initctx && NSS_ShutdownContext( *initctx ) ) {
edf356
+		PRErrorCode errcode = PR_GetError();
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_close_nssdb: ERROR: could not shutdown NSS - error %d:%s.\n",
edf356
+		       errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ), 0 );
edf356
+		return 0;
edf356
+	} else {
edf356
+		return 1;
edf356
+	}
edf356
+}
edf356
+
edf356
+
edf356
+int
edf356
+tlsmc_convert( char **ld_cacertdir, char **ld_cert, char **ld_key )
edf356
+{
edf356
+
edf356
+	int rv = 0;
edf356
+
edf356
+	NSSInitContext *nss_ctx = NULL;
edf356
+	char *nssdb_dir_path = NULL;
edf356
+	char *nssdb_prefix = NULL;
edf356
+	char *pem_dir = NULL;
edf356
+	char *readme_path = NULL;
edf356
+	char *data = NULL;  // data before checksum
edf356
+	char *checksum = NULL;  // checksummed data
edf356
+	struct stat stat_buf;
edf356
+
edf356
+#ifdef LDAP_R_COMPILE
edf356
+	ldap_pvt_thread_mutex_lock( &tlsmc_mutex );
edf356
+#endif
edf356
+
edf356
+	Debug( LDAP_DEBUG_TRACE,
edf356
+	       "tlsmc_convert: INFO: trying to open NSS DB with CACertDir = `%s'.\n",
edf356
+	       *ld_cacertdir, 0, 0 );
edf356
+	if ( NULL == ld_cacertdir || NULL == ld_cert || NULL == ld_key ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_convert: ERROR: cannot proceed, some of the arguments are NULL.\n",
edf356
+		       0, 0, 0 );
edf356
+		rv = 1;
edf356
+		goto bail;
edf356
+	}
edf356
+	if ( 0 == tlsmc_open_nssdb( *ld_cacertdir, &nss_ctx, &nssdb_dir_path, &nssdb_prefix ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_convert: INFO: cannot open the NSS DB, expecting PEM configuration is present.\n",
edf356
+		       0, 0, 0 );
edf356
+		rv = 1;
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	if ( NULL == ( checksum = tlsmc_compute_checksum( nssdb_dir_path, nssdb_prefix,
edf356
+	                                                  *ld_cacertdir, *ld_cert, *ld_key,
edf356
+	                                                  &data ) ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_convert: ERROR: could not compute checksum.\n",
edf356
+		       0, 0, 0 );
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	if ( NULL == ( pem_dir = PR_smprintf( "/tmp/openldap-tlsmc-%s-%s-%s",
edf356
+	                                      tlsmc_path2name( nssdb_dir_path ),
edf356
+	                                      nssdb_prefix,
edf356
+	                                      checksum) ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_convert: FATAL: could not allocate memory.\n",
edf356
+		       0, 0, 0 );
edf356
+		goto bail;
edf356
+	}
edf356
+	if ( NULL == ( readme_path = PR_smprintf( "%s/" TLSMC_README_FILE_NAME, pem_dir ) ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_convert: FATAL: could not allocate memory.\n",
edf356
+		       0, 0, 0 );
edf356
+		goto bail;
edf356
+	}
edf356
+	Debug( LDAP_DEBUG_TRACE,
edf356
+	       "tlsmc_convert: INFO: trying with PEM dir = `%s'.\n",
edf356
+	       pem_dir, 0, 0 );
edf356
+	if ( 0 == stat( pem_dir, &stat_buf ) ) {
edf356
+		if ( S_ISDIR(stat_buf.st_mode) ) {
edf356
+			Debug( LDAP_DEBUG_TRACE,
edf356
+			       "tlsmc_convert: INFO: using the existing PEM dir.\n",
edf356
+			       0, 0, 0 );
edf356
+			if ( 0 == stat( readme_path, &stat_buf ) ) {
edf356
+				goto pem_dir_exists;
edf356
+			} else {
edf356
+				Debug( LDAP_DEBUG_ANY,
edf356
+				       "tlsmc_convert: ERROR: the PEM dir found does not contain README file. Will remove the PEM dir and try to recreate it.\n",
edf356
+				       0, 0, 0 );
edf356
+				if ( 0 == tlsmc_remove_dir_recursively( pem_dir ) ) {
edf356
+					Debug( LDAP_DEBUG_ANY,
edf356
+					       "tlsmc_convert: FATAL: could not remove the PEM dir. Cannot properly set TLS.\n",
edf356
+					       0, 0, 0 );
edf356
+					goto bail;
edf356
+				}
edf356
+			}
edf356
+		} else {
edf356
+			Debug( LDAP_DEBUG_ANY,
edf356
+			       "tlsmc_convert: FATAL: tried to stat the PEM dir but it is not a directory.\n",
edf356
+			       0, 0, 0 );
edf356
+			goto bail;
edf356
+		}
edf356
+	}
edf356
+	Debug( LDAP_DEBUG_TRACE,
edf356
+	       "tlsmc_convert: WARN: will try to create PEM dir.\n",
edf356
+	       0, 0, 0 );
edf356
+	if ( 0 == tlsmc_prepare_dir( pem_dir ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_convert: FATAL: cannot prepare the PEM dir.\n",
edf356
+		       0, 0, 0 );
edf356
+		goto bail;
edf356
+	}
edf356
+	if ( 0 == tlsmc_extract_nssdb( pem_dir, ld_cacertdir, ld_cert, ld_key ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_convert: FATAL: could not extract from the NSS DB.\n",
edf356
+		       0, 0, 0 );
edf356
+		goto bail;
edf356
+	}
edf356
+	if ( 0 == tlsmc_write_file( readme_path, data, S_IRUSR ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_convert: ERROR: could not create README file.\n",
edf356
+		       0, 0, 0 );
edf356
+	}
edf356
+
edf356
+pem_dir_exists:
edf356
+	if (*ld_cacertdir) free(*ld_cacertdir);
edf356
+	*ld_cacertdir = PR_smprintf( "%s/" TLSMC_CACERTS_DIR_NAME, pem_dir );
edf356
+	if ( ! ( ( 0 == stat( *ld_cacertdir, &stat_buf ) )
edf356
+	         && S_ISDIR(stat_buf.st_mode) ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_convert: WARN: extracted cacerts dir is not present.\n",
edf356
+		       0, 0, 0 );
02ade0
+		if ( *ld_cacertdir ) PR_smprintf_free( *ld_cacertdir );
edf356
+		*ld_cacertdir = NULL;
edf356
+	}
edf356
+
edf356
+	if (*ld_cert) free(*ld_cert);
edf356
+	*ld_cert = PR_smprintf( "%s/" TLSMC_CERT_FILE_NAME, pem_dir );
edf356
+	if ( ! ( ( 0 == stat( *ld_cert, &stat_buf ) )
edf356
+	         && ( S_ISREG(stat_buf.st_mode)
edf356
+	              || S_ISLNK(stat_buf.st_mode) ) ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_convert: WARN: extracted cert file is not present.\n",
edf356
+		       0, 0, 0 );
02ade0
+		if ( *ld_cert ) PR_smprintf_free( *ld_cert );
edf356
+		*ld_cert = NULL;
edf356
+	}
edf356
+
edf356
+	if (*ld_key) free(*ld_key);
edf356
+	*ld_key = PR_smprintf( "%s/" TLSMC_KEY_FILE_NAME, pem_dir );
edf356
+	if ( ! ( ( 0 == stat( *ld_key, &stat_buf ) )
edf356
+	         && ( S_ISREG(stat_buf.st_mode)
edf356
+	              || S_ISLNK(stat_buf.st_mode) ) ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_convert: WARN: extracted key file is not present.\n",
edf356
+		       0, 0, 0 );
02ade0
+		if ( *ld_key ) PR_smprintf_free( *ld_key );
edf356
+		*ld_key = NULL;
edf356
+	}
edf356
+
edf356
+	rv = 1;
edf356
+
edf356
+bail:
02ade0
+	if ( checksum ) free( checksum );
edf356
+	if ( pem_dir ) PR_smprintf_free( pem_dir );
02ade0
+	if ( readme_path ) PR_smprintf_free( readme_path );
edf356
+	if ( data ) free( data );
edf356
+	if ( nssdb_prefix ) free( nssdb_prefix );
edf356
+	if ( nssdb_dir_path ) free( nssdb_dir_path );
edf356
+	if ( nss_ctx ) tlsmc_close_nssdb( &nss_ctx );
edf356
+
edf356
+#ifdef LDAP_R_COMPILE
edf356
+	ldap_pvt_thread_mutex_unlock( &tlsmc_mutex );
edf356
+#endif
edf356
+
edf356
+	return rv;
edf356
+}
edf356
+
edf356
+
edf356
+// returns 0 when successful
edf356
+int
edf356
+tlsmc_intercept_initialization( struct ldapoptions *lo, int is_server )
edf356
+{
edf356
+	int rv = 0;
edf356
+	char *ld_cacertdir = NULL;
edf356
+	char *ld_cert = NULL;
edf356
+	char *ld_key = NULL;
edf356
+
edf356
+	ld_cacertdir = lo->ldo_tls_cacertdir ? LDAP_STRDUP( (char *) lo->ldo_tls_cacertdir ) : NULL;
edf356
+	ld_cert = lo->ldo_tls_certfile ? LDAP_STRDUP( (char *) lo->ldo_tls_certfile ) : NULL;
edf356
+	ld_key = lo->ldo_tls_keyfile ? LDAP_STRDUP( (char *) lo->ldo_tls_keyfile ) : NULL;
edf356
+
edf356
+	Debug( LDAP_DEBUG_TRACE,
edf356
+	       "tlsmc_intercept_initialization: INFO: entry options follow:\n"
edf356
+	       "tlsmc_intercept_initialization: INFO: cacertdir = `%s'\n"
edf356
+	       "tlsmc_intercept_initialization: INFO: certfile = `%s'\n"
edf356
+	       "tlsmc_intercept_initialization: INFO: keyfile = `%s'\n",
edf356
+	       lo->ldo_tls_cacertdir, lo->ldo_tls_certfile, lo->ldo_tls_keyfile );
edf356
+
edf356
+	if ( 0 == tlsmc_convert( &ld_cacertdir, &ld_cert, &ld_key ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_intercept_initialization: FATAL: could not intercept TLS initialization. TLS will not work!\n",
edf356
+		       0, 0, 0 );
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	if ( lo->ldo_tls_cacertdir ) LDAP_FREE( lo->ldo_tls_cacertdir );
edf356
+	lo->ldo_tls_cacertdir = ld_cacertdir ? LDAP_STRDUP( (char *) ld_cacertdir ) : NULL;
edf356
+
edf356
+	if ( lo->ldo_tls_certfile ) LDAP_FREE( lo->ldo_tls_certfile );
edf356
+	lo->ldo_tls_certfile = ld_cert ? LDAP_STRDUP( (char *) ld_cert ) : NULL;
edf356
+
edf356
+	if ( lo->ldo_tls_keyfile ) LDAP_FREE( lo->ldo_tls_keyfile );
edf356
+	lo->ldo_tls_keyfile = ld_key ? LDAP_STRDUP( (char *) ld_key ) : NULL;
edf356
+
edf356
+	Debug( LDAP_DEBUG_TRACE,
edf356
+	       "tlsmc_intercept_initialization: INFO: altered options follow:\n"
edf356
+	       "tlsmc_intercept_initialization: INFO: cacertdir = `%s'\n"
edf356
+	       "tlsmc_intercept_initialization: INFO: certfile = `%s'\n"
edf356
+	       "tlsmc_intercept_initialization: INFO: keyfile = `%s'\n",
edf356
+	       lo->ldo_tls_cacertdir, lo->ldo_tls_certfile, lo->ldo_tls_keyfile );
edf356
+
edf356
+	Debug( LDAP_DEBUG_ANY,
edf356
+	       "tlsmc_intercept_initialization: INFO: successfully intercepted TLS initialization. Continuing with OpenSSL only.\n",
edf356
+	       0, 0, 0 );
edf356
+	rv = 1;
edf356
+bail:
edf356
+	if ( ld_cacertdir ) LDAP_FREE( ld_cacertdir );
edf356
+	if ( ld_cert ) LDAP_FREE( ld_cert );
edf356
+	if ( ld_key ) LDAP_FREE( ld_key );
edf356
+	return rv;
edf356
+}
edf356
+
edf356
+
edf356
+#endif /* HAVE_MOZNSS_COMPATIBILITY */
edf356
+/*
edf356
+  emacs settings
edf356
+  Local Variables:
edf356
+  indent-tabs-mode: t
edf356
+  tab-width: 4
edf356
+  End:
edf356
+*/
edf356
diff --git a/libraries/libldap/tls_mc.h b/libraries/libldap/tls_mc.h
edf356
new file mode 100644
edf356
--- /dev/null
edf356
+++ b/libraries/libldap/tls_mc.h
edf356
@@ -0,0 +1,18 @@
edf356
+#ifndef _LDAP_TLSMC_H
edf356
+#define _LDAP_TLSMC_H
edf356
+
edf356
+#include "portable.h"
edf356
+
edf356
+#ifdef HAVE_MOZNSS_COMPATIBILITY
edf356
+
edf356
+#include "ldap-int.h"
edf356
+
edf356
+int
edf356
+tlsmc_convert( char **ld_cacertdir, char **ld_cert, char **ld_key );
edf356
+
edf356
+int
edf356
+tlsmc_intercept_initialization( struct ldapoptions *lo, int is_server );
edf356
+
edf356
+
edf356
+#endif /* HAVE_MOZNSS_COMPATIBILITY */
edf356
+#endif /* _LDAP_TLSMC_H */
edf356
diff --git a/libraries/libldap/tls_mc_ossl.c b/libraries/libldap/tls_mc_ossl.c
edf356
new file mode 100644
edf356
--- /dev/null
edf356
+++ b/libraries/libldap/tls_mc_ossl.c
edf356
@@ -0,0 +1,95 @@
edf356
+#include "portable.h"
edf356
+
edf356
+/* This file contains functions that require OpenSSL headers due to some
edf356
+   conflicts with what MozNSS defines.
edf356
+*/
edf356
+
edf356
+#ifdef HAVE_MOZNSS_COMPATIBILITY
edf356
+
edf356
+#include <openssl/x509.h>
edf356
+#include <openssl/pem.h>
edf356
+#include "ldap-int.h"
edf356
+#include <nspr/nspr.h>
edf356
+#include <unistd.h>
edf356
+#include <errno.h>
edf356
+
edf356
+
edf356
+int
edf356
+tlsmc_cert_create_hash_symlink( char *cert_path, char *cacerts_dir )
edf356
+{
edf356
+	int rv = 0;
edf356
+	X509 *cert = NULL;
edf356
+	FILE *fp = NULL;
edf356
+	unsigned long hash = 0;
edf356
+	char *cert_filename_p = NULL;
edf356
+	char *last_slash_p = NULL;
edf356
+	char *symlink_path = NULL;
edf356
+	int cnt = 0;
edf356
+
edf356
+	if ( NULL == ( fp = fopen( cert_path, "r" ) ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_cert_create_hash_symlink: ERROR: could not open the cert file.\n",
edf356
+		       0, 0, 0 );
edf356
+		perror( "tlsmc_cert_create_hash_symlink: ERROR: OS error" );
edf356
+		goto bail;
edf356
+	}
edf356
+	if ( NULL == PEM_read_X509( fp, &cert, NULL, NULL ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_cert_create_hash_symlink: ERROR: could not read PEM data.\n",
edf356
+		       0, 0, 0 );
edf356
+		goto bail;
edf356
+	}
edf356
+	if ( 0 == ( hash = X509_subject_name_hash( cert ) ) ) {
edf356
+		Debug( LDAP_DEBUG_ANY,
edf356
+		       "tlsmc_cert_create_hash_symlink: ERROR: could not hash subject.\n",
edf356
+		       0, 0, 0 );
edf356
+		goto bail;
edf356
+	}
edf356
+
edf356
+	last_slash_p = strrchr( cert_path, '/' );
edf356
+	cert_filename_p = last_slash_p ? last_slash_p + 1 : cert_path;
edf356
+	for ( cnt = 0; cnt < 10; cnt++ ) {
edf356
+		if ( NULL == ( symlink_path = PR_smprintf( "%s/%08lx.%d", cacerts_dir, hash, cnt ) ) ) {
edf356
+			Debug( LDAP_DEBUG_ANY,
edf356
+			       "tlsmc_cert_create_hash_symlink: ERROR: memory allocation error.\n",
edf356
+			       0, 0, 0 );
edf356
+			continue;
edf356
+		}
edf356
+		if ( 0 != symlink( cert_filename_p, symlink_path ) ) {
edf356
+			if ( errno == EEXIST ) {
edf356
+				Debug( LDAP_DEBUG_ANY,
edf356
+				       "tlsmc_cert_create_hash_symlink: INFO: symlink `%s' already exists.\n",
edf356
+				       symlink_path, 0, 0 );
edf356
+				if ( symlink_path ) PR_smprintf( symlink_path );
edf356
+				continue;
edf356
+			}
edf356
+			Debug( LDAP_DEBUG_ANY,
edf356
+			       "tlsmc_cert_create_hash_symlink: ERROR: could not create symlink.\n",
edf356
+			       0, 0, 0 );
edf356
+			perror( "tlsmc_cert_create_hash_symlink: ERROR: OS error" );
edf356
+			goto bail;
edf356
+		}
edf356
+		Debug( LDAP_DEBUG_TRACE,
edf356
+		       "tlsmc_cert_create_hash_symlink: INFO: the cert is now symlinked to %s.\n",
edf356
+		       symlink_path, 0, 0 );
edf356
+		rv = 1;
edf356
+		goto bail;
edf356
+	}
edf356
+	Debug( LDAP_DEBUG_ANY,
edf356
+	       "tlsmc_cert_create_hash_symlink: INFO: could not create symlink (all possible file names taken).\n",
edf356
+	       0, 0, 0 );
edf356
+bail:
edf356
+	if ( symlink_path ) PR_smprintf_free( symlink_path );
edf356
+	if ( cert ) X509_free( cert );
edf356
+	if ( fp ) fclose( fp );
edf356
+	return rv;
edf356
+}
edf356
+
edf356
+#endif /* HAVE_MOZNSS_COMPATIBILITY */
edf356
+/*
edf356
+  emacs settings
edf356
+  Local Variables:
edf356
+  indent-tabs-mode: t
edf356
+  tab-width: 4
edf356
+  End:
edf356
+*/
edf356
diff --git a/libraries/libldap/tls_mc_ossl.h b/libraries/libldap/tls_mc_ossl.h
edf356
new file mode 100644
edf356
--- /dev/null
edf356
+++ b/libraries/libldap/tls_mc_ossl.h
edf356
@@ -0,0 +1,12 @@
edf356
+#ifndef _LDAP_TLSMC_OSSL_H
edf356
+#define _LDAP_TLSMC_OSSL_H
edf356
+
edf356
+#include "portable.h"
edf356
+
edf356
+#ifdef HAVE_MOZNSS_COMPATIBILITY
edf356
+
edf356
+int
edf356
+tlsmc_cert_create_hash_symlink( char *cert_path, char *cacerts_dir );
edf356
+
edf356
+#endif
edf356
+#endif
edf356
diff --git a/libraries/libldap_r/Makefile.in b/libraries/libldap_r/Makefile.in
edf356
--- a/libraries/libldap_r/Makefile.in
edf356
+++ b/libraries/libldap_r/Makefile.in
edf356
@@ -28,7 +28,7 @@ XXSRCS    = apitest.c test.c \
edf356
 	request.c os-ip.c url.c pagectrl.c sortctrl.c vlvctrl.c \
edf356
 	init.c options.c print.c string.c util-int.c schema.c \
edf356
 	charray.c os-local.c dnssrv.c utf-8.c utf-8-conv.c \
edf356
-	tls2.c tls_o.c tls_g.c tls_m.c \
edf356
+	tls2.c tls_o.c tls_g.c tls_m.c tls_mc.c tls_mc_ossl.c \
edf356
 	turn.c ppolicy.c dds.c txn.c ldap_sync.c stctrl.c \
edf356
 	assertion.c deref.c ldif.c fetch.c
edf356
 SRCS	= threads.c rdwr.c rmutex.c tpool.c rq.c \
edf356
@@ -46,7 +46,7 @@ OBJS	= threads.lo rdwr.lo rmutex.lo tpool.lo  rq.lo \
edf356
 	request.lo os-ip.lo url.lo pagectrl.lo sortctrl.lo vlvctrl.lo \
edf356
 	init.lo options.lo print.lo string.lo util-int.lo schema.lo \
edf356
 	charray.lo os-local.lo dnssrv.lo utf-8.lo utf-8-conv.lo \
edf356
-	tls2.lo tls_o.lo tls_g.lo tls_m.lo \
edf356
+	tls2.lo tls_o.lo tls_g.lo tls_m.lo tls_mc.lo tls_mc_ossl.lo \
edf356
 	turn.lo ppolicy.lo dds.lo txn.lo ldap_sync.lo stctrl.lo \
edf356
 	assertion.lo deref.lo ldif.lo fetch.lo
edf356