1 From 35b08487213749c6da625a446f605b6e7f74d07f Mon Sep 17 00:00:00 2001
2 From: babak sarashki <babak.sarashki@windriver.com>
3 Date: Sun, 3 Nov 2019 15:24:11 -0800
4 Subject: [PATCH 18/20] openldap tlsmc
6 From Stx 1901: openldap-tlsmc.patch
9 doc/man/man3/ldap_get_option.3 | 13 +
10 doc/man/man5/ldap.conf.5 | 5 +
11 doc/man/man5/slapd-config.5 | 5 +
12 doc/man/man5/slapd.conf.5 | 5 +
14 libraries/libldap/Makefile.in | 4 +-
15 libraries/libldap/init.c | 7 +-
16 libraries/libldap/ldap-int.h | 3 +-
17 libraries/libldap/tls2.c | 47 ++
18 libraries/libldap/tls_mc.c | 1369 +++++++++++++++++++++++++++++++
19 libraries/libldap/tls_mc.h | 18 +
20 libraries/libldap/tls_mc_ossl.c | 95 +++
21 libraries/libldap/tls_mc_ossl.h | 12 +
22 libraries/libldap_r/Makefile.in | 4 +-
23 15 files changed, 1606 insertions(+), 6 deletions(-)
24 create mode 100644 libraries/libldap/tls_mc.c
25 create mode 100644 libraries/libldap/tls_mc.h
26 create mode 100644 libraries/libldap/tls_mc_ossl.c
27 create mode 100644 libraries/libldap/tls_mc_ossl.h
29 diff --git a/configure.in b/configure.in
30 index caa49a1..949a1b2 100644
33 @@ -237,6 +237,7 @@ dnl OL_ARG_ENABLE(referrals,[ --enable-referrals enable LDAPv2+ Referrals (ex
34 ol_enable_referrals=${ol_enable_referrals-no}
35 OL_ARG_ENABLE(ipv6,[ --enable-ipv6 enable IPv6 support], auto)dnl
36 OL_ARG_ENABLE(local,[ --enable-local enable AF_LOCAL (AF_UNIX) socket support], auto)dnl
37 +OL_ARG_ENABLE(moznss_compatibility,[ --enable-moznss-compatibility enable MozNSS compatibility], no)dnl
39 dnl ----------------------------------------------------------------
40 dnl General "with" options
41 @@ -1256,6 +1257,26 @@ if test $ol_link_tls = no ; then
45 +if test $ol_enable_moznss_compatibility = yes ; then
46 + if test $have_openssl = yes ; then
47 + if test $ol_with_tls = openssl ; then
48 + PKG_CHECK_MODULES(MOZNSS, [nss nspr], [have_moznss_libs=yes], [have_moznss_libs=no])
49 + if test $have_moznss_libs = yes ; then
50 + AC_DEFINE(HAVE_MOZNSS_COMPATIBILITY, 1,
51 + [define if you want to support MozNSS databases when compiled with OpenSSL])
52 + TLS_LIBS="$TLS_LIBS $MOZNSS_LIBS"
53 + CFLAGS="$CFLAGS $MOZNSS_CFLAGS"
55 + AC_MSG_ERROR([For MOZNSS_COMPATIBILITY you have to have MozNSS present.])
58 + AC_MSG_ERROR([For MOZNSS_COMPATIBILITY you have to compile with OpenSSL for crypto. (see --with-tls flag)])
61 + AC_MSG_ERROR([For MOZNSS_COMPATIBILITY you have to have OpenSSL devel available. (unable to build with OpenSSL)])
66 if test $ol_link_tls = yes ; then
67 AC_DEFINE(HAVE_TLS, 1, [define if you have TLS])
68 diff --git a/doc/man/man3/ldap_get_option.3 b/doc/man/man3/ldap_get_option.3
69 index af5ede1..e4cf843 100644
70 --- a/doc/man/man3/ldap_get_option.3
71 +++ b/doc/man/man3/ldap_get_option.3
72 @@ -772,6 +772,19 @@ must be
73 When using the OpenSSL library this is an SSL*. When using other
74 crypto libraries this is a pointer to an OpenLDAP private structure.
75 Applications generally should not use this option.
77 +.B LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY
78 +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.
79 +This option defaults to be on.
88 +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.
90 On success, the functions return
91 .BR LDAP_OPT_SUCCESS ,
92 diff --git a/doc/man/man5/ldap.conf.5 b/doc/man/man5/ldap.conf.5
93 index 904bf24..24c0155 100644
94 --- a/doc/man/man5/ldap.conf.5
95 +++ b/doc/man/man5/ldap.conf.5
96 @@ -480,6 +480,11 @@ Check the CRL for a whole certificate chain
97 Specifies the file containing a Certificate Revocation List to be used
98 to verify if the server certificates have not been revoked. This
99 parameter is only supported with GnuTLS and Mozilla NSS.
102 +.B TLS_MOZNSS_COMPATIBILITY <on/true/yes/off/false/no>
103 +Specifies whether the MozNSS database compatibility layer for TLS options should be enabled. This options is available only if OpenLDAP is compiled with OpenSSL.
104 +This option defaults to be on.
105 .SH "ENVIRONMENT VARIABLES"
108 diff --git a/doc/man/man5/slapd-config.5 b/doc/man/man5/slapd-config.5
109 index 9c72e82..42032d4 100644
110 --- a/doc/man/man5/slapd-config.5
111 +++ b/doc/man/man5/slapd-config.5
112 @@ -1004,6 +1004,11 @@ Check the CRL for a whole certificate chain
113 Specifies a file containing a Certificate Revocation List to be used
114 for verifying that certificates have not been revoked. This parameter
115 is only valid when using GnuTLS or Mozilla NSS.
118 +.B olcTLSMozNSSCompatibility <on/true/yes/off/false/no>
119 +Specifies whether the MozNSS database compatibility layer for TLS options should be enabled. This options is available only if OpenLDAP is compiled with OpenSSL.
120 +This option defaults to be on.
121 .SH DYNAMIC MODULE OPTIONS
124 diff --git a/doc/man/man5/slapd.conf.5 b/doc/man/man5/slapd.conf.5
125 index f504adc..2d4431f 100644
126 --- a/doc/man/man5/slapd.conf.5
127 +++ b/doc/man/man5/slapd.conf.5
128 @@ -1235,6 +1235,11 @@ Check the CRL for a whole certificate chain
129 Specifies a file containing a Certificate Revocation List to be used
130 for verifying that certificates have not been revoked. This directive is
131 only valid when using GnuTLS and Mozilla NSS.
134 +.B TLSMozNSSCompatibility <on/true/yes/off/false/no>
135 +Specifies whether the MozNSS database compatibility layer for TLS options should be enabled. This options is available only if OpenLDAP is compiled with OpenSSL.
136 +This option defaults to be on.
137 .SH GENERAL BACKEND OPTIONS
138 Options in this section only apply to the configuration file section
139 for the specified backend. They are supported by every
140 diff --git a/include/ldap.h b/include/ldap.h
141 index c245651..7bc0644 100644
144 @@ -158,6 +158,10 @@ LDAP_BEGIN_DECL
145 #define LDAP_OPT_X_TLS_NEWCTX 0x600f
146 #define LDAP_OPT_X_TLS_CRLFILE 0x6010 /* GNUtls only */
147 #define LDAP_OPT_X_TLS_PACKAGE 0x6011
148 +#define LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY 0x6050
150 +#define LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY_DISABLED 0
151 +#define LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY_ENABLED 1
153 #define LDAP_OPT_X_TLS_NEVER 0
154 #define LDAP_OPT_X_TLS_HARD 1
155 diff --git a/libraries/libldap/Makefile.in b/libraries/libldap/Makefile.in
156 index 58d9cc7..80b81f0 100644
157 --- a/libraries/libldap/Makefile.in
158 +++ b/libraries/libldap/Makefile.in
159 @@ -26,7 +26,7 @@ SRCS = bind.c open.c result.c error.c compare.c search.c \
160 request.c os-ip.c url.c pagectrl.c sortctrl.c vlvctrl.c \
161 init.c options.c print.c string.c util-int.c schema.c \
162 charray.c os-local.c dnssrv.c utf-8.c utf-8-conv.c \
163 - tls2.c tls_o.c tls_g.c tls_m.c \
164 + tls2.c tls_o.c tls_g.c tls_m.c tls_mc.c tls_mc_ossl.c \
165 turn.c ppolicy.c dds.c txn.c ldap_sync.c stctrl.c \
166 assertion.c deref.c ldif.c fetch.c
168 @@ -39,7 +39,7 @@ OBJS = bind.lo open.lo result.lo error.lo compare.lo search.lo \
169 request.lo os-ip.lo url.lo pagectrl.lo sortctrl.lo vlvctrl.lo \
170 init.lo options.lo print.lo string.lo util-int.lo schema.lo \
171 charray.lo os-local.lo dnssrv.lo utf-8.lo utf-8-conv.lo \
172 - tls2.lo tls_o.lo tls_g.lo tls_m.lo \
173 + tls2.lo tls_o.lo tls_g.lo tls_m.lo tls_mc.lo tls_mc_ossl.lo \
174 turn.lo ppolicy.lo dds.lo txn.lo ldap_sync.lo stctrl.lo \
175 assertion.lo deref.lo ldif.lo fetch.lo
177 diff --git a/libraries/libldap/init.c b/libraries/libldap/init.c
178 index de16b60..d016bdc 100644
179 --- a/libraries/libldap/init.c
180 +++ b/libraries/libldap/init.c
181 @@ -137,7 +137,9 @@ static const struct ol_attribute {
183 {0, ATTR_TLS, "TLS_CRLFILE", NULL, LDAP_OPT_X_TLS_CRLFILE},
186 +#ifdef HAVE_MOZNSS_COMPATIBILITY
187 + {0, ATTR_TLS, "TLS_MOZNSS_COMPATIBILITY", NULL, LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY},
191 {0, ATTR_NONE, NULL, NULL, 0}
192 @@ -579,6 +581,9 @@ void ldap_int_initialize_global_options( struct ldapoptions *gopts, int *dbglvl
193 gopts->ldo_tls_connect_cb = NULL;
194 gopts->ldo_tls_connect_arg = NULL;
195 gopts->ldo_tls_require_cert = LDAP_OPT_X_TLS_DEMAND;
196 +#ifdef HAVE_MOZNSS_COMPATIBILITY
197 + gopts->ldo_tls_moznss_compatibility = LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY_ENABLED;
200 gopts->ldo_keepalive_probes = 0;
201 gopts->ldo_keepalive_interval = 0;
202 diff --git a/libraries/libldap/ldap-int.h b/libraries/libldap/ldap-int.h
203 index 66e04ae..15092c1 100644
204 --- a/libraries/libldap/ldap-int.h
205 +++ b/libraries/libldap/ldap-int.h
206 @@ -260,7 +260,8 @@ struct ldapoptions {
207 int ldo_tls_require_cert;
209 int ldo_tls_crlcheck;
210 -#define LDAP_LDO_TLS_NULLARG ,0,0,0,{0,0,0,0,0,0,0,0,0},0,0,0,0
211 + int ldo_tls_moznss_compatibility;
212 +#define LDAP_LDO_TLS_NULLARG ,0,0,0,{0,0,0,0,0,0,0,0,0},0,0,0,0,0
214 #define LDAP_LDO_TLS_NULLARG
216 diff --git a/libraries/libldap/tls2.c b/libraries/libldap/tls2.c
217 index 46ccefa..198d0b1 100644
218 --- a/libraries/libldap/tls2.c
219 +++ b/libraries/libldap/tls2.c
222 #include "ldap-tls.h"
226 static tls_impl *tls_imp = &ldap_int_tls_impl;
227 #define HAS_TLS( sb ) ber_sockbuf_ctrl( sb, LBER_SB_OPT_HAS_IO, \
228 (void *)tls_imp->ti_sbio )
229 @@ -96,6 +98,7 @@ tls_ctx_ref( tls_ctx *ctx )
230 * an extra mutex for the default ctx.
232 static ldap_pvt_thread_mutex_t tls_def_ctx_mutex;
233 +ldap_pvt_thread_mutex_t tlsmc_mutex;
237 @@ -161,6 +164,7 @@ tls_init(tls_impl *impl )
238 if ( !tls_initialized++ ) {
239 #ifdef LDAP_R_COMPILE
240 ldap_pvt_thread_mutex_init( &tls_def_ctx_mutex );
241 + ldap_pvt_thread_mutex_init( &tlsmc_mutex );
245 @@ -196,6 +200,27 @@ ldap_int_tls_init_ctx( struct ldapoptions *lo, int is_server )
249 +#ifdef HAVE_MOZNSS_COMPATIBILITY
250 + if ( LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY_ENABLED == lo->ldo_tls_moznss_compatibility ) {
251 + Debug( LDAP_DEBUG_ANY,
252 + "TLSMC: MozNSS compatibility interception begins.\n",
254 + if ( 0 == tlsmc_intercept_initialization( lo, is_server ) ) {
255 + Debug( LDAP_DEBUG_TRACE,
256 + "TLSMC: ERROR: MozNSS compatibility layer failed.\n",
259 + lts = lo->ldo_tls_info;
260 + Debug( LDAP_DEBUG_ANY,
261 + "TLSMC: MozNSS compatibility interception ends.\n",
264 + Debug( LDAP_DEBUG_TRACE,
265 + "TLS: MozNSS compatibility layer disabled.\n",
270 if ( is_server && !lts.lt_certfile && !lts.lt_keyfile &&
271 !lts.lt_cacertfile && !lts.lt_cacertdir ) {
272 /* minimum configuration not provided */
273 @@ -584,6 +609,21 @@ ldap_int_tls_config( LDAP *ld, int option, const char *arg )
274 return ldap_pvt_tls_set_option( ld, option, &i );
278 +#ifdef HAVE_MOZNSS_COMPATIBILITY
279 + case LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY:
281 + if ( (strcasecmp( arg, "yes" ) == 0) ||
282 + (strcasecmp( arg, "true" ) == 0) ||
283 + (strcasecmp( arg, "on" ) == 0) ) {
284 + i = LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY_ENABLED;
286 + if ( (strcasecmp( arg, "no" ) == 0) ||
287 + (strcasecmp( arg, "false" ) == 0) ||
288 + (strcasecmp( arg, "off" ) == 0) ) {
289 + i = LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY_DISABLED;
291 + return ldap_pvt_tls_set_option( ld, option, &i );
295 @@ -687,6 +727,9 @@ ldap_pvt_tls_get_option( LDAP *ld, int option, void *arg )
296 case LDAP_OPT_X_TLS_CONNECT_ARG:
297 *(void **)arg = lo->ldo_tls_connect_arg;
299 + case LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY:
300 + *(int *)arg = lo->ldo_tls_moznss_compatibility;
305 @@ -814,6 +857,10 @@ ldap_pvt_tls_set_option( LDAP *ld, int option, void *arg )
306 ldap_pvt_tls_ctx_free( lo->ldo_tls_ctx );
307 lo->ldo_tls_ctx = NULL;
308 return ldap_int_tls_init_ctx( lo, *(int *)arg );
309 + case LDAP_OPT_X_TLS_MOZNSS_COMPATIBILITY:
310 + if ( !arg ) return -1;
311 + lo->ldo_tls_moznss_compatibility = *(int *)arg;
316 diff --git a/libraries/libldap/tls_mc.c b/libraries/libldap/tls_mc.c
318 index 0000000..7ba2a07
320 +++ b/libraries/libldap/tls_mc.c
322 +#include "portable.h"
324 +#ifdef HAVE_MOZNSS_COMPATIBILITY
326 +#include <sys/stat.h>
327 +#include <sys/types.h>
328 +#include <ac/unistd.h>
329 +#include <ac/errno.h>
330 +#include <ac/termios.h>
334 +#include <nspr/nspr.h>
335 +#include <nspr/private/pprio.h>
337 +#include <nss/nss.h>
338 +#include <nss/pk11pub.h>
339 +#include <nss/cert.h>
341 +#include <nss/base64.h>
342 +#include <nss/key.h>
343 +#include <nss/keyt.h>
344 +#include <nss/blapi.h>
345 +#include <nss/certdb.h>
347 +#include "ldap-int.h"
348 +#include "ldap-tls.h"
349 +#include "ldap_pvt_thread.h"
352 +#include "tls_mc_ossl.h"
354 +#define TLSMC_CHECKSUM_LEN 32
356 +#define TLSMC_CACERTS_DIR_NAME "cacerts"
357 +#define TLSMC_CERT_FILE_NAME "cert.pem"
358 +#define TLSMC_KEY_FILE_NAME "key.pem"
359 +#define TLSMC_README_FILE_NAME "README"
361 +#define TLSM_CERTDB_DESC "ldap"
362 +#define DEFAULT_TOKEN_NAME "default"
364 +#define TLSMC_IS_INPLACE 1
365 +#define TLSMC_IS_TEMPORARY 2
368 + "This file is auto-generated by the MozNSS Compatibility Layer of OpenLDAP software.\n" \
369 + "This layer is not part of the OpenLDAP software distributed in upstream.\n" \
370 + "Please file any related bugs with your GNU/Linux distribution.\n" \
371 + "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" \
372 + "Please refer to the slapd.conf(5), slapd-config(5), and ldap.conf(5) man pages for more information."
373 +#define PEM_CERT_HEADER "-----BEGIN CERTIFICATE-----"
374 +#define PEM_CERT_FOOTER "-----END CERTIFICATE-----"
375 +#define PEM_KEY_HEADER "-----BEGIN PRIVATE KEY-----"
376 +#define PEM_KEY_FOOTER "-----END PRIVATE KEY-----"
377 +#define README_HEADER "The contents of this file are hashed and used as part of the name of the parent directory.\n"
379 +#ifdef LDAP_R_COMPILE
380 +ldap_pvt_thread_mutex_t tlsmc_mutex;
382 +/*******************************************************************/
386 +tlsmc_path2name( char *path )
388 + if ( NULL == path ) return NULL;
389 + char *last_slash = strrchr( path, '/' );
390 + if ( NULL == last_slash )
393 + return last_slash + 1;
398 +tlsmc_write_file( char *filename, char *buf, mode_t final_mode )
402 + if ( 0 > ( fd = open( filename, O_WRONLY | O_CREAT | O_EXCL, S_IWUSR ) ) ) {
403 + Debug( LDAP_DEBUG_ANY,
404 + "tlsmc_write_file: ERROR: could not open file: `%s': %s.\n",
405 + filename, strerror(errno), 0 );
408 + if ( -1 >= write( fd, buf, strlen(buf) ) ) {
409 + Debug( LDAP_DEBUG_ANY,
410 + "tlsmc_write_file: ERROR: could not write file: `%s': %s.\n",
411 + filename, strerror(errno), 0 );
414 + if ( -1 >= fchmod( fd, final_mode ) ) {
415 + Debug( LDAP_DEBUG_ANY,
416 + "tlsmc_write_file: ERROR: could not set file mode: `%s': %s.\n",
417 + filename, strerror(errno), 0 );
420 + if ( -1 >= fsync( fd ) ) {
421 + Debug( LDAP_DEBUG_ANY,
422 + "tlsmc_write_file: ERROR: could not fsync file: `%s': %s.\n",
423 + filename, strerror(errno), 0 );
426 + if ( 0 > close( fd ) ) {
427 + Debug( LDAP_DEBUG_ANY,
428 + "tlsmc_write_file: ERROR: could not close file: `%s': %s.\n",
429 + filename, strerror(errno), 0 );
436 + if ( fd > -1 ) close( fd );
442 +tlsmc_remove_dir_recursively( const char *dir_name )
446 + struct dirent *entry = NULL;
447 + char *full_path = NULL;
449 + Debug( LDAP_DEBUG_TRACE,
450 + "tlsmc_remove_dir_recursively: INFO: starting recursively removing directory `%s'.\n",
452 + if ( NULL == ( dir = opendir( dir_name ) ) ) {
453 + Debug( LDAP_DEBUG_ANY,
454 + "tlsmc_remove_dir_recursively: ERROR: could not open the directory (errno %d: %s).\n",
455 + errno, strerror( errno ), 0 );
459 + while ( NULL != ( entry = readdir( dir ) ) ) {
463 + full_path = PR_smprintf( "%s/%s", dir_name, entry->d_name );
465 + if ( 0 != strcmp( entry->d_name, "." ) && 0 != strcmp( entry->d_name, ".." ) ) {
466 + if ( 0 == lstat( full_path, &info ) ) {
467 + if ( S_ISDIR( info.st_mode ) ) {
468 + Debug( LDAP_DEBUG_TRACE,
469 + "tlsmc_remove_dir_recursively: INFO: stepping into directory `%s'.\n",
470 + entry->d_name, 0, 0 );
471 + if ( 0 == tlsmc_remove_dir_recursively( full_path ) ) {
472 + goto bail_and_close_dir;
475 + Debug( LDAP_DEBUG_TRACE,
476 + "tlsmc_remove_dir_recursively: INFO: removing file `%s'.\n",
477 + entry->d_name, 0, 0 );
478 + if ( 0 != remove( full_path ) ) {
479 + Debug( LDAP_DEBUG_ANY,
480 + "tlsmc_remove_dir_recursively: ERROR: could not remove the file (errno %d: %s).\n",
481 + errno, strerror( errno ), 0 );
482 + goto bail_and_close_dir;
486 + Debug( LDAP_DEBUG_ANY,
487 + "tlsmc_remove_dir_recursively: ERROR: could not stat `%s', (errno %d: %s).\n",
488 + full_path, errno, strerror( errno ) );
489 + goto bail_and_close_dir;
494 + PR_smprintf_free( full_path );
499 + Debug( LDAP_DEBUG_TRACE,
500 + "tlsmc_remove_dir_recursively: INFO: stepping out of the directory.\n",
502 + if ( 0 != closedir( dir ) ) {
503 + Debug( LDAP_DEBUG_ANY,
504 + "tlsmc_remove_dir_recursively: WARN: could not close the directory (errno %d: %s).\n",
505 + errno, strerror( errno ), 0 );
509 + Debug( LDAP_DEBUG_TRACE,
510 + "tlsmc_remove_dir_recursively: INFO: removing the directory itself.\n",
512 + if ( 0 != remove( dir_name ) ) {
513 + PRErrorCode errcode = PR_GetError();
514 + Debug( LDAP_DEBUG_ANY,
515 + "tlsmc_remove_dir_recursively: ERROR: could not remove the directory (errno %d: %s).\n",
516 + errno, strerror( errno ), 0 );
525 + if ( full_path ) PR_smprintf_free( full_path );
530 +/* BORROWED FROM tls_m.c */
532 +tlsmc_get_certdb_prefix( const char *certdir, char **nsscertdir, char **realcertdir, char **prefix )
534 + char sep = PR_GetDirectorySeparator();
536 + char *chkpath = NULL;
537 + struct PRFileInfo prfi;
540 + *realcertdir = (char *)certdir; /* default is the one passed in */
542 + /* if certdir is not given, just return */
543 + if ( !certdir ) return;
545 + *nsscertdir = certdir;
547 + /* ignore database type prefix (e.g. sql:, dbm:) if provided */
548 + if ( NULL != ( chkpath = strchr( certdir, ':' ) ) ) {
549 + *realcertdir = chkpath + 1;
552 + /* if certdir exists (file or directory) then it cannot specify a prefix */
553 + prc = PR_GetFileInfo( *realcertdir, &prfi );
554 + if ( prc == PR_SUCCESS ) {
558 + /* if certdir was given, and there is a '/' in certdir, see if there
559 + is anything after the last '/' - if so, assume it is the prefix */
560 + /* if ( ( ( ptr = strrchr( *realcertdir, sep ) ) ) && *(ptr + 1) ) { */
561 + /* *realcertdir = PL_strndup( *realcertdir, ptr - (*realcertdir) ); */
562 + /* *prefix = PL_strdup( ptr + 1 ); */
566 + if ( ptr = strrchr( *realcertdir, sep ) ) {
567 + if ( *(ptr + 1) ) {
571 + *prefix = *realcertdir + strlen( *realcertdir ); // empty string
574 + *prefix = *realcertdir;
575 + *realcertdir = *prefix + strlen( *prefix ); // empty string
578 + /* drop potential last '/' from realcertdir */
580 + ptr = strrchr( *realcertdir, sep );
581 + if ( ptr && (! *(ptr+1) ) ) {
592 +/* BORROWED FROM tls_m.c */
594 +tlsmc_get_pin_from_file(const char *token_name, char *filename)
596 + char *pwdstr = NULL;
597 + char *contents = NULL;
598 + char *lasts = NULL;
600 + char *candidate = NULL;
601 + PRFileInfo file_info;
602 + PRFileDesc *pwd_fileptr = NULL;
603 + pwd_fileptr = PR_Open( filename, PR_RDONLY, 00400 );
605 + /* open the password file */
606 + if ( !pwd_fileptr ) {
607 + PRErrorCode errcode = PR_GetError();
608 + Debug( LDAP_DEBUG_ANY,
609 + "tlsmc_get_pin_from_file: could not open security pin file %s - error %d:%s.\n",
611 + PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
615 + /* get the file size */
616 + if ( PR_SUCCESS != PR_GetFileInfo( filename, &file_info ) ) {
617 + PRErrorCode errcode = PR_GetError();
618 + Debug( LDAP_DEBUG_ANY,
619 + "tlsmc_get_pin_from_file: could not get file info from pin file %s - error %d:%s.\n",
621 + PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
625 + /* create a buffer to hold the file contents */
626 + if ( !( contents = PR_CALLOC( file_info.size + 1 ) ) ) {
627 + PRErrorCode errcode = PR_GetError();
628 + Debug( LDAP_DEBUG_ANY,
629 + "tlsmc_get_pin_from_file: could not alloc a buffer for contents of pin file %s - error %d:%s.\n",
630 + filename, errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
634 + /* read file into the buffer */
635 + if( PR_Read( pwd_fileptr, contents, file_info.size ) <= 0 ) {
636 + PRErrorCode errcode = PR_GetError();
637 + Debug( LDAP_DEBUG_ANY,
638 + "tlsmc_get_pin_from_file: could not read the file contents from pin file %s - error %d:%s.\n",
639 + filename, errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ) );
643 + /* format is [tokenname:]password EOL [tokenname:]password EOL ... */
644 + /* if you want to use a password containing a colon character, use
645 + the special tokenname "default" */
646 + for ( line = PL_strtok_r( contents, "\r\n", &lasts ); line;
647 + line = PL_strtok_r( NULL, "\r\n", &lasts ) ) {
651 + continue; /* skip blank lines */
653 + colon = PL_strchr( line, ':' );
655 + if ( *(colon + 1) && token_name &&
656 + !PL_strncmp( token_name, line, colon-line ) ) {
657 + candidate = colon + 1; /* found a definite match */
659 + } else if ( !PL_strncmp( DEFAULT_TOKEN_NAME, line, colon-line ) ) {
660 + candidate = colon + 1; /* found possible match */
662 + } else { /* no token name */
667 + if ( pwd_fileptr ) {
668 + PR_Close( pwd_fileptr );
671 + pwdstr = PL_strdup( candidate );
673 + PL_strfree( contents );
679 +/* BORROWED FROM tls_m.c */
681 + * Turn the echoing off on a tty.
686 + if ( isatty( fd ) ) {
687 + struct termios tio;
688 + tcgetattr( fd, &tio );
689 + tio.c_lflag &= ~ECHO;
690 + tcsetattr( fd, TCSAFLUSH, &tio );
694 +/* BORROWED FROM tls_m.c */
696 + * Turn the echoing on on a tty.
701 + if ( isatty( fd ) ) {
702 + struct termios tio;
703 + tcgetattr( fd, &tio );
704 + tio.c_lflag |= ECHO;
705 + tcsetattr( fd, TCSAFLUSH, &tio );
706 + tcsetattr( fd, TCSAFLUSH, &tio );
711 +/* BORROWED FROM tls_m.c */
713 +tlsmc_get_pin( PK11SlotInfo *slot, PRBool retry, void * filename)
715 + char *token_name = NULL;
716 + char *pwdstr = NULL;
718 + token_name = PK11_GetTokenName( slot );
719 + /* Try to get the passwords from the password file if it exists.
720 + * THIS IS UNSAFE and is provided for convenience only. Without this
721 + * capability the server would have to be started in foreground mode
722 + * if using an encrypted key.
726 + "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");
727 + pwdstr = tlsmc_get_pin_from_file( token_name, (char *)filename );
728 + if ( retry && pwdstr != NULL )
732 + int infd = PR_FileDesc2NativeHandle( PR_STDIN );
733 + int isTTY = isatty( infd );
734 + unsigned char phrase[200];
735 + /* Prompt for password */
738 + "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");
740 + "Please enter pin, password, or pass phrase for security token '%s': ",
741 + token_name ? token_name : DEFAULT_TOKEN_NAME );
744 + fgets( (char*)phrase, sizeof(phrase), stdin );
746 + fprintf( stdout, "\n" );
749 + /* stomp on newline */
750 + phrase[strlen((char*)phrase)-1] = 0;
752 + pwdstr = PL_strdup( (char*)phrase );
760 +tlsmc_hash( char **dest, const char *src )
763 + unsigned char fp[SHA256_LENGTH];
766 + if ( SECSuccess != ( rv = PK11_HashBuf( SEC_OID_SHA256, fp, src, strlen(src) ) ) ) {
767 + Debug( LDAP_DEBUG_ANY,
768 + "tlsmc_hash: could not hash a buffer",
769 + NULL, NULL, NULL );
773 + fpItem.len = SHA256_LENGTH;
774 + if ( NULL == ( *dest = CERT_Hexify( &fpItem, 0 ) ) ) {
775 + Debug( LDAP_DEBUG_ANY,
776 + "tlsmc_hash: could not hexify the hash",
777 + NULL, NULL, NULL );
787 +/* BORROWED FROM tls_m.c */
789 +tlsmc_open_nssdb( char *ld_cacertdir, NSSInitContext **out_initctx, char **out_nssdb_dir, char **out_nssdb_prefix )
791 +#define SECURITYDIRS_COUNT 3
793 + PRErrorCode errcode = 1;
795 + /* restart secmod modules */
796 +#ifdef HAVE_SECMOD_RESTARTMODULES
797 + /* NSS enforces the pkcs11 requirement that modules should be unloaded after
798 + a fork() - since there is no portable way to determine if NSS has been
799 + already initialized in a parent process, we just call SECMOD_RestartModules
800 + with force == FALSE - if the module has been unloaded due to a fork, it will
801 + be reloaded, otherwise, it is a no-op */
802 + if ( 0 == ( rc = SECMOD_RestartModules(PR_FALSE /* do not force */) ) ) {
803 + errcode = PORT_GetError();
804 + if ( errcode != SEC_ERROR_NOT_INITIALIZED ) {
805 + Debug( LDAP_DEBUG_TRACE,
806 + "tlsmc_open_nssdb: WARN: could not restart the security modules: %d:%s.\n",
807 + errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ), 0 );
815 + /* context and certdb */
816 + char *dir_moznss = PR_GetEnv( "MOZNSS_DIR" );
817 + char *dir_default_moznss = PR_GetEnv( "DEFAULT_MOZNSS_DIR" );
818 + const char *securitydirs[SECURITYDIRS_COUNT] = { dir_moznss, ld_cacertdir, dir_default_moznss };
821 + for ( ii = 0; !done && ( ii < SECURITYDIRS_COUNT ); ++ii ) {
822 + // get certdb prefix
823 + const char *securitydir = securitydirs[ii];
824 + char *nsscertdir = NULL;
825 + char *realcertdir = NULL;
826 + const char *defprefix = "";
827 + char *prefix = (char *)defprefix;
828 + if ( securitydir == NULL ) continue;
829 + tlsmc_get_certdb_prefix( securitydir, &nsscertdir, &realcertdir, &prefix );
830 + *out_nssdb_dir = strdup( realcertdir );
831 + *out_nssdb_prefix = strdup( prefix );
833 + Debug( LDAP_DEBUG_TRACE,
834 + "tlsmc_open_nssdb: INFO: trying to initialize moznss using security dir `%s` prefix `%s`.\n",
835 + nsscertdir, prefix, NULL);
838 + NSSInitContext *initctx = NULL;
839 + NSSInitParameters initparams;
840 + memset( &initparams, 0, sizeof( initparams ) );
841 + initparams.length = sizeof( initparams );
843 + initctx = NSS_InitContext( nsscertdir,
848 + NSS_INIT_READONLY // | NSS_INIT_NOCERTDB
850 + rc = initctx ? 1 : 0;
852 + *out_initctx = initctx;
855 + errcode = PORT_GetError();
856 + Debug( LDAP_DEBUG_ANY,
857 + "tlsmc_open_nssdb: WARN: could not initialize MozNSS context - error %d.\n",
860 + Debug( LDAP_DEBUG_TRACE,
861 + "tlsmc_open_nssdb: INFO: initialized MozNSS context.\n",
868 + PR_Free( dir_moznss );
869 + PR_Free( dir_default_moznss );
871 + PK11_SetPasswordFunc( tlsmc_get_pin );
873 + if ( done && (errcode == 0) && out_initctx ) {
882 +tlsmc_filestamp( char **filestamp, char *path )
888 + if ( 0 != stat( path, &attr ) ) {
892 + if ( 0 == strftime(stime, sizeof(stime), "%FT%T", localtime(&attr.st_mtime)) ) {
893 + Debug( LDAP_DEBUG_ANY,
894 + "tlsmc_filestamp: ERROR: could not format mtime: %s.\n",
895 + strerror(errno), 0, 0 );
899 + *filestamp = PR_smprintf("mtime %s.%d, size %lld",
900 + stime, attr.st_mtim.tv_nsec, (long long) attr.st_size);
909 +tlsmc_compute_checksum( char *nssdb_dir, char *nssdb_prefix,
910 + char *ld_cacertdir, char *ld_cert, char *ld_key,
915 + char *checksum = NULL;
918 + data = PR_sprintf_append( data,
923 + "nssdb_prefix: %s\n"
924 + "ld_cacertdir: %s\n"
929 + nssdb_dir, nssdb_prefix, ld_cacertdir, ld_cert, ld_key, geteuid() );
931 + char *files[] = { "cert8.db", "cert9.db", "key3.db", "key4.db", "secmod.db", NULL };
932 + char **filep = NULL;
933 + for ( filep = files; NULL != *filep; filep++ ) {
934 + char *filestamp = NULL;
936 + path = PR_smprintf( "%s/%s%s", nssdb_dir, nssdb_prefix, *filep );
937 + if ( 0 == tlsmc_filestamp( &filestamp, path ) ) {
938 + Debug( LDAP_DEBUG_ANY,
939 + "tlsmc_compute_checksum: INFO: could not check file `%s'.\n",
943 + data = PR_sprintf_append( data, "%s: %s\n", *filep, filestamp );
945 + if ( filestamp ) PR_smprintf_free( filestamp );
946 + if ( path ) PR_smprintf_free( path );
947 + if ( -1 == rv ) goto bail;
950 + /* compute data checksum */
951 + if ( 1 != tlsmc_hash( &checksum, (const char*) data ) ) {
956 + /* possibly supply data */
958 + *out_data = strdup( data );
961 + /* return checksum */
963 + if ( data ) PR_smprintf_free( data );
969 +tlsmc_prepare_dir( char *dir )
972 + char *cacerts_dir = NULL;
974 + Debug( LDAP_DEBUG_TRACE,
975 + "tlsmc_prepare_dir: INFO: preparing PEM directory `%s'.\n",
978 + if ( 0 != mkdir( dir, S_IRWXU /* u+rwx */ ) ) {
979 + Debug( LDAP_DEBUG_ANY,
980 + "tlsmc_prepare_dir: WARN: could not create the PEM directory.\n",
986 + Debug( LDAP_DEBUG_TRACE,
987 + "tlsmc_prepare_dir: INFO: creating a subdirectory `%s'.\n",
988 + TLSMC_CACERTS_DIR_NAME, 0, 0 );
989 + cacerts_dir = PR_smprintf( "%s/%s", dir, TLSMC_CACERTS_DIR_NAME );
990 + if ( 0 != mkdir( cacerts_dir, S_IRWXU /* u+rwx */ ) ) {
991 + Debug( LDAP_DEBUG_ANY,
992 + "tlsmc_prepare_dir: WARN: could not create the subdirectory.\n",
998 + Debug( LDAP_DEBUG_TRACE,
999 + "tlsmc_prepare_dir: INFO: successfully created PEM directory structure.\n",
1000 + TLSMC_CACERTS_DIR_NAME, 0, 0 );
1004 + if ( cacerts_dir ) PR_smprintf_free( cacerts_dir );
1009 +/* BORROWED FROM 389ds: ssl.c */
1011 +tlsmc_extract_cert_to_file(CERTCertDBHandle *certdb_handle, CERTCertificate *cert, char *file_path)
1016 + char *output = NULL;
1019 + Debug( LDAP_DEBUG_ANY,
1020 + "tlsmc_extract_cert_to_file: ERROR: cert empty.\n",
1025 + data.data = cert->derCert.data;
1026 + data.len = cert->derCert.len;
1027 + b64 = BTOA_DataToAscii(data.data, data.len);
1029 + Debug( LDAP_DEBUG_ANY,
1030 + "tlsmc_extract_cert_to_file: ERROR: could not base64 encode.\n",
1035 + output = PR_smprintf( DONOTEDIT "\n\n"
1036 + "NSS nickname: %s\n"
1039 + PEM_CERT_HEADER "\n"
1041 + PEM_CERT_FOOTER "\n",
1044 + cert->subjectName,
1047 + if ( 0 == tlsmc_write_file( file_path, output, S_IRUSR ) ) {
1048 + Debug( LDAP_DEBUG_ANY,
1049 + "tlsmc_extract_cert_to_file: ERROR: could not write certificate.\n",
1056 + if ( output ) PR_smprintf_free( output );
1057 + if ( b64 ) PORT_Free( b64 );
1062 +/* BORROWED FROM 389ds: ssl.c */
1064 +tlsmc_decrypt_key(SECKEYEncryptedPrivateKeyInfo *epki,
1070 + SECItem *cryptoParam = NULL;
1071 + PK11SymKey *symKey = NULL;
1072 + PK11Context *ctx = NULL;
1075 + if (!pwitem) return rv;
1078 + SECAlgorithmID algid = epki->algorithm;
1079 + CK_MECHANISM_TYPE cryptoMechType;
1080 + CK_ATTRIBUTE_TYPE operation = CKA_DECRYPT;
1081 + PK11SlotInfo *slot = NULL;
1083 + cryptoMechType = PK11_GetPBECryptoMechanism(&algid, &cryptoParam, pwitem);
1084 + if (cryptoMechType == CKM_INVALID_MECHANISM) {
1088 + if ( NULL == ( slot = PK11_GetBestSlot(cryptoMechType, NULL) ) ) {
1092 + if ( NULL == ( symKey = PK11_PBEKeyGen(slot, &algid, pwitem, PR_FALSE, pin_arg) ) ) {
1097 + if ( NULL == ( ctx = PK11_CreateContextBySymKey(
1098 + cryptoMechType, operation, symKey, cryptoParam) ) ) {
1103 + if ( SECSuccess != PK11_CipherOp(ctx,
1104 + derPKI->data, /* out */
1105 + (int *)(&derPKI->len), /* out len */
1106 + (int)epki->encryptedData.len, /* max out */
1107 + epki->encryptedData.data, /* in */
1108 + (int)epki->encryptedData.len /* in len */ ) ) {
1113 + if ( derPKI->len != epki->encryptedData.len ) goto bail_one;
1114 + if ( SECSuccess != PK11_Finalize(ctx) ) goto bail_one;
1116 + if (slot) PK11_FreeSlot(slot);
1119 + rv = ((rv == 0) ? 1 : 0);
1121 + if (symKey) PK11_FreeSymKey(symKey);
1122 + if (cryptoParam) {
1123 + SECITEM_ZfreeItem(cryptoParam, PR_TRUE);
1124 + cryptoParam = NULL;
1126 + if (ctx) PK11_DestroyContext(ctx, PR_TRUE);
1132 +/* BORROWED FROM 389ds: ssl.c */
1134 +tlsmc_extract_key_of_cert_to_file(CERTCertificate *cert,
1135 + char *pin_filename,
1139 + SECKEYPrivateKey *key = NULL;
1141 + SECKEYEncryptedPrivateKeyInfo *epki = NULL;
1142 + PLArenaPool *arenaForPKI = NULL;
1143 + SECItem clearKeyDER;
1145 + char *output = NULL;
1147 + // establish password
1148 + pwitem.data = "secretpw"; // FIXME use pin_filename
1149 + pwitem.len = strlen(pwitem.data);
1150 + pwitem.type = siBuffer;
1153 + if ( NULL == ( key = PK11_FindKeyByAnyCert(cert, (void *)pin_filename) ) ) {
1154 + Debug( LDAP_DEBUG_ANY,
1155 + "tlsmc_extract_key_of_cert_to_file: ERROR: PK11_FindKeyByAnyCert failed.\n",
1161 + if ( NULL == ( epki = PK11_ExportEncryptedPrivKeyInfo(
1162 + NULL, SEC_OID_DES_EDE3_CBC, &pwitem, key, 1000, (void *)pin_filename) ) ) {
1163 + Debug( LDAP_DEBUG_ANY,
1164 + "tlsmc_extract_key_of_cert_to_file: ERROR: PK11_ExportEncryptedPrivKeyInfo returned NULL.\n",
1170 + if ( NULL == ( arenaForPKI = PORT_NewArena(2048) ) ) {
1171 + Debug( LDAP_DEBUG_ANY,
1172 + "tlsmc_extract_key_of_cert_to_file: ERROR: PORT_NewArena failed.\n",
1177 + clearKeyDER.data = PORT_ArenaAlloc(arenaForPKI, epki->encryptedData.len);
1178 + clearKeyDER.len = epki->encryptedData.len;
1179 + clearKeyDER.type = siBuffer;
1181 + if ( 0 == tlsmc_decrypt_key(epki, SEC_OID_DES_EDE3_CBC, &pwitem,
1182 + (void *)pin_filename, &clearKeyDER) ) {
1183 + Debug( LDAP_DEBUG_ANY,
1184 + "tlsmc_extract_key_of_cert_to_file: ERROR: could not decrypt the key.\n",
1190 + if ( NULL == ( b64 = BTOA_ConvertItemToAscii(&clearKeyDER) ) ) {
1191 + Debug( LDAP_DEBUG_ANY,
1192 + "tlsmc_extract_key_of_cert_to_file: ERROR: could not base64 encode.\n",
1198 + output = PR_smprintf( DONOTEDIT "\n"
1199 + PEM_KEY_HEADER "\n"
1201 + PEM_KEY_FOOTER "\n",
1204 + if ( 0 == tlsmc_write_file( filename, output, S_IRUSR ) ) {
1205 + Debug( LDAP_DEBUG_ANY,
1206 + "tlsmc_extract_key_of_cert_to_file: ERROR: could not write PK.\n",
1214 + if (b64) PORT_Free(b64);
1215 + if (arenaForPKI) PORT_FreeArena(arenaForPKI, PR_FALSE);
1216 + if (epki) SECKEY_DestroyEncryptedPrivateKeyInfo(epki, PR_TRUE);
1217 + if (key) SECKEY_DestroyPrivateKey(key);
1222 +/* BORROWED FROM 389ds: ssl.c */
1224 +tlsmc_extract_cert_key_pair(char *nickname, char *pin_filename, char *dir_name)
1227 + CERTCertDBHandle *certHandle = NULL;
1228 + CERTCertificate *cert = NULL;
1229 + char *cert_file_path = NULL;
1230 + char *key_file_path = NULL;
1231 + char *file_realpath = NULL;
1234 + cert_file_path = PR_smprintf( "%s/cert.pem", dir_name );
1235 + key_file_path = PR_smprintf( "%s/key.pem", dir_name );
1237 + if ( NULL == nickname ) {
1238 + Debug( LDAP_DEBUG_ANY,
1239 + "tlsmc_extract_cert_key_pair: WARN: supplied nickname is empty (NULL).\n",
1244 + if ( NULL == ( certHandle = CERT_GetDefaultCertDB() ) ) {
1245 + // FIXME see same in tlsmc_extract_cacerts()
1246 + Debug( LDAP_DEBUG_ANY,
1247 + "tlsmc_extract_cert_key_pair: ERROR: could not get certificate handle.\n",
1251 + if ( NULL != ( cert = PK11_FindCertFromNickname(nickname, NULL) ) ) {
1252 + /* extract cert/key from NSS db */
1254 + Debug( LDAP_DEBUG_TRACE,
1255 + "tlsmc_extract_cert_key_pair: INFO: extracting certificate `%s' to file `%s'.\n",
1256 + nickname, cert_file_path, 0 );
1257 + if ( 0 == tlsmc_extract_cert_to_file(certHandle, cert, cert_file_path) ) {
1258 + Debug( LDAP_DEBUG_ANY,
1259 + "tlsmc_extract_cert_key_pair: ERROR: could not extract certificate.\n",
1264 + Debug( LDAP_DEBUG_TRACE,
1265 + "tlsmc_extract_cert_key_pair: INFO: extracting associated PK to file `%s'.\n",
1266 + key_file_path, 0, 0 );
1267 + if ( 0 == tlsmc_extract_key_of_cert_to_file( cert, pin_filename, key_file_path ) ) {
1268 + Debug( LDAP_DEBUG_ANY,
1269 + "tlsmc_extract_cert_key_pair: ERROR: could not extract PK.\n",
1274 + /* symlink PEM cert/key PEM files */
1276 + Debug( LDAP_DEBUG_ANY,
1277 + "tlsmc_extract_cert_key_pair: INFO: could not find certificate with nickname `%s', expecting a PEM file.\n",
1280 + Debug( LDAP_DEBUG_TRACE,
1281 + "tlsmc_extract_cert_key_pair: INFO: symlinking certificate file `%s' to file `%s'.\n",
1282 + nickname, cert_file_path, 0 );
1283 + if ( NULL == ( file_realpath = realpath( nickname, NULL ) ) ) {
1284 + Debug( LDAP_DEBUG_ANY,
1285 + "tlsmc_extract_cert_key_pair: ERROR: could not get realpath of certificate file `%s'.\n",
1289 + if ( -1 == symlink( file_realpath, cert_file_path ) ) {
1290 + Debug( LDAP_DEBUG_ANY,
1291 + "tlsmc_extract_cert_key_pair: ERROR: could not create a symlink of `%s' to `%s'.\n",
1292 + file_realpath, cert_file_path, 0 );
1295 + if ( file_realpath ) free( file_realpath );
1297 + Debug( LDAP_DEBUG_TRACE,
1298 + "tlsmc_extract_cert_key_pair: INFO: symlinking PK file `%s' to file `%s'.\n",
1299 + pin_filename, key_file_path, 0 );
1300 + if ( NULL == ( file_realpath = realpath( pin_filename, NULL ) ) ) {
1301 + Debug( LDAP_DEBUG_ANY,
1302 + "tlsmc_extract_cert_key_pair: ERROR: could not get realpath of PK file `%s'.\n",
1303 + pin_filename, 0, 0 );
1306 + if ( -1 == symlink( file_realpath, key_file_path ) ) {
1307 + Debug( LDAP_DEBUG_ANY,
1308 + "tlsmc_extract_cert_key_pair: ERROR: could not create a symlink of `%s' to `%s'.\n",
1309 + file_realpath, key_file_path, 0 );
1317 + if (file_realpath) free(file_realpath);
1318 + if (key_file_path) PR_smprintf_free(key_file_path);
1319 + if (cert_file_path) PR_smprintf_free(cert_file_path);
1320 + if (cert) CERT_DestroyCertificate(cert);
1325 +/* BORROWED FROM 389ds: ssl.c */
1327 +tlsmc_extract_cacerts( char *dir_name )
1330 + CERTCertDBHandle *certHandle = NULL;
1331 + CERTCertListNode *node = NULL;
1332 + CERTCertList *list = NULL;
1333 + char *cacerts_dir = NULL;
1336 + cacerts_dir = PR_smprintf( "%s/" TLSMC_CACERTS_DIR_NAME, dir_name );
1338 + certHandle = CERT_GetDefaultCertDB(); // FIXME maybe we should really use certdb_slot?
1339 + if ( ! certHandle ) {
1340 + Debug( LDAP_DEBUG_TRACE,
1341 + "tlsmc_extract_cacerts: could not get certificate database handle.\n",
1346 + list = PK11_ListCerts(PK11CertListAll, NULL);
1348 + Debug( LDAP_DEBUG_TRACE,
1349 + "tlsmc_extract_cacerts: could not get list of certificates.\n",
1353 + for ( node = CERT_LIST_HEAD(list);
1354 + !CERT_LIST_END(node, list);
1355 + node = CERT_LIST_NEXT(node)) {
1357 + CERTCertificate *cert = NULL;
1358 + CERTCertTrust trust;
1359 + char *cert_file_path = NULL;
1362 + cert = node->cert;
1363 + if ( SECFailure == CERT_GetCertTrust( cert, &trust ) ) {
1364 + Debug( LDAP_DEBUG_ANY,
1365 + "tlsmc_extract_cacerts: ERROR: could not get trust flags of certificate nick=`%s'.\n",
1366 + cert->nickname, 0, 0);
1369 + if (trust.sslFlags &
1370 + (CERTDB_VALID_CA | CERTDB_TRUSTED_CA | CERTDB_TRUSTED_CLIENT_CA)) {
1374 + Debug(LDAP_DEBUG_TRACE,
1375 + "tlsmc_extract_cacerts: INFO: found cert nick=`%s'%s.\n",
1376 + cert->nickname, is_ca ? ", a trusted CA" : ", _not_ a trusted CA, skipping", 0);
1377 + if ( ! is_ca ) continue;
1379 + cert_file_path = PR_smprintf( "%s/cert%d.pem", cacerts_dir, cert_cnt );
1381 + Debug(LDAP_DEBUG_TRACE,
1382 + "tlsmc_extract_cacerts: INFO: extracting cert nick=`%s' to file `%s'.\n",
1383 + cert->nickname, cert_file_path, 0);
1384 + if ( 0 == tlsmc_extract_cert_to_file( certHandle, cert, cert_file_path ) ) {
1385 + Debug( LDAP_DEBUG_ANY,
1386 + "tlsmc_extract_cacerts: ERROR: could not extract the certificate.\n",
1390 + if ( 0 == tlsmc_cert_create_hash_symlink( cert_file_path, cacerts_dir ) ) {
1391 + Debug( LDAP_DEBUG_ANY,
1392 + "tlsmc_extract_cacerts: ERROR: could not rehash the certificate.\n",
1396 + if ( cert_file_path ) PR_smprintf_free( cert_file_path );
1399 + if ( cert_file_path ) PR_smprintf_free( cert_file_path );
1405 + if ( cacerts_dir ) PR_smprintf_free( cacerts_dir );
1406 + if ( list ) CERT_DestroyCertList( list );
1411 +/* returns 1 if successfull;
1412 + returns -1 if only cert-key pair is NULL or could not be extracted;
1413 + returns 0 if any other error
1416 +tlsmc_extract_nssdb( char *dir_name, char **ld_cacertdir, char **ld_cert, char **ld_key )
1420 + if ( ! dir_name ) {
1421 + Debug( LDAP_DEBUG_ANY,
1422 + "tlsmc_extract_nssdb: FATAL: target dir name empty.\n",
1427 + if ( 0 == tlsmc_extract_cacerts( dir_name ) ) {
1428 + Debug( LDAP_DEBUG_ANY,
1429 + "tlsmc_extract_nssdb: ERROR: could not export CA certificates.\n",
1434 + if ( 0 == tlsmc_extract_cert_key_pair( *ld_cert, *ld_key, dir_name ) ) {
1435 + Debug( LDAP_DEBUG_ANY,
1436 + "tlsmc_extract_nssdb: ERROR: could not export user cert and/or key.\n",
1447 +/* BORROWED FROM tls_m.c */
1449 +tlsmc_close_nssdb(NSSInitContext **initctx)
1451 + if ( *initctx && NSS_ShutdownContext( *initctx ) ) {
1452 + PRErrorCode errcode = PR_GetError();
1453 + Debug( LDAP_DEBUG_ANY,
1454 + "tlsmc_close_nssdb: ERROR: could not shutdown NSS - error %d:%s.\n",
1455 + errcode, PR_ErrorToString( errcode, PR_LANGUAGE_I_DEFAULT ), 0 );
1464 +tlsmc_convert( char **ld_cacertdir, char **ld_cert, char **ld_key )
1469 + NSSInitContext *nss_ctx = NULL;
1470 + char *nssdb_dir_path = NULL;
1471 + char *nssdb_prefix = NULL;
1472 + char *pem_dir = NULL;
1473 + char *readme_path = NULL;
1474 + char *data = NULL; // data before checksum
1475 + char *checksum = NULL; // checksummed data
1476 + struct stat stat_buf;
1478 +#ifdef LDAP_R_COMPILE
1479 + ldap_pvt_thread_mutex_lock( &tlsmc_mutex );
1482 + Debug( LDAP_DEBUG_TRACE,
1483 + "tlsmc_convert: INFO: trying to open NSS DB with CACertDir = `%s'.\n",
1484 + *ld_cacertdir, 0, 0 );
1485 + if ( NULL == ld_cacertdir || NULL == ld_cert || NULL == ld_key ) {
1486 + Debug( LDAP_DEBUG_ANY,
1487 + "tlsmc_convert: ERROR: cannot proceed, some of the arguments are NULL.\n",
1492 + if ( 0 == tlsmc_open_nssdb( *ld_cacertdir, &nss_ctx, &nssdb_dir_path, &nssdb_prefix ) ) {
1493 + Debug( LDAP_DEBUG_ANY,
1494 + "tlsmc_convert: INFO: cannot open the NSS DB, expecting PEM configuration is present.\n",
1500 + if ( NULL == ( checksum = tlsmc_compute_checksum( nssdb_dir_path, nssdb_prefix,
1501 + *ld_cacertdir, *ld_cert, *ld_key,
1503 + Debug( LDAP_DEBUG_ANY,
1504 + "tlsmc_convert: ERROR: could not compute checksum.\n",
1509 + if ( NULL == ( pem_dir = PR_smprintf( "/tmp/openldap-tlsmc-%s-%s-%s",
1510 + tlsmc_path2name( nssdb_dir_path ),
1513 + Debug( LDAP_DEBUG_ANY,
1514 + "tlsmc_convert: FATAL: could not allocate memory.\n",
1518 + if ( NULL == ( readme_path = PR_smprintf( "%s/" TLSMC_README_FILE_NAME, pem_dir ) ) ) {
1519 + Debug( LDAP_DEBUG_ANY,
1520 + "tlsmc_convert: FATAL: could not allocate memory.\n",
1524 + Debug( LDAP_DEBUG_TRACE,
1525 + "tlsmc_convert: INFO: trying with PEM dir = `%s'.\n",
1527 + if ( 0 == stat( pem_dir, &stat_buf ) ) {
1528 + if ( S_ISDIR(stat_buf.st_mode) ) {
1529 + Debug( LDAP_DEBUG_TRACE,
1530 + "tlsmc_convert: INFO: using the existing PEM dir.\n",
1532 + if ( 0 == stat( readme_path, &stat_buf ) ) {
1533 + goto pem_dir_exists;
1535 + Debug( LDAP_DEBUG_ANY,
1536 + "tlsmc_convert: ERROR: the PEM dir found does not contain README file. Will remove the PEM dir and try to recreate it.\n",
1538 + if ( 0 == tlsmc_remove_dir_recursively( pem_dir ) ) {
1539 + Debug( LDAP_DEBUG_ANY,
1540 + "tlsmc_convert: FATAL: could not remove the PEM dir. Cannot properly set TLS.\n",
1546 + Debug( LDAP_DEBUG_ANY,
1547 + "tlsmc_convert: FATAL: tried to stat the PEM dir but it is not a directory.\n",
1552 + Debug( LDAP_DEBUG_TRACE,
1553 + "tlsmc_convert: WARN: will try to create PEM dir.\n",
1555 + if ( 0 == tlsmc_prepare_dir( pem_dir ) ) {
1556 + Debug( LDAP_DEBUG_ANY,
1557 + "tlsmc_convert: FATAL: cannot prepare the PEM dir.\n",
1561 + if ( 0 == tlsmc_extract_nssdb( pem_dir, ld_cacertdir, ld_cert, ld_key ) ) {
1562 + Debug( LDAP_DEBUG_ANY,
1563 + "tlsmc_convert: FATAL: could not extract from the NSS DB.\n",
1567 + if ( 0 == tlsmc_write_file( readme_path, data, S_IRUSR ) ) {
1568 + Debug( LDAP_DEBUG_ANY,
1569 + "tlsmc_convert: ERROR: could not create README file.\n",
1574 + if (*ld_cacertdir) free(*ld_cacertdir);
1575 + *ld_cacertdir = PR_smprintf( "%s/" TLSMC_CACERTS_DIR_NAME, pem_dir );
1576 + if ( ! ( ( 0 == stat( *ld_cacertdir, &stat_buf ) )
1577 + && S_ISDIR(stat_buf.st_mode) ) ) {
1578 + Debug( LDAP_DEBUG_ANY,
1579 + "tlsmc_convert: WARN: extracted cacerts dir is not present.\n",
1581 + if ( *ld_cacertdir ) PR_smprintf_free( *ld_cacertdir );
1582 + *ld_cacertdir = NULL;
1585 + if (*ld_cert) free(*ld_cert);
1586 + *ld_cert = PR_smprintf( "%s/" TLSMC_CERT_FILE_NAME, pem_dir );
1587 + if ( ! ( ( 0 == stat( *ld_cert, &stat_buf ) )
1588 + && ( S_ISREG(stat_buf.st_mode)
1589 + || S_ISLNK(stat_buf.st_mode) ) ) ) {
1590 + Debug( LDAP_DEBUG_ANY,
1591 + "tlsmc_convert: WARN: extracted cert file is not present.\n",
1593 + if ( *ld_cert ) PR_smprintf_free( *ld_cert );
1597 + if (*ld_key) free(*ld_key);
1598 + *ld_key = PR_smprintf( "%s/" TLSMC_KEY_FILE_NAME, pem_dir );
1599 + if ( ! ( ( 0 == stat( *ld_key, &stat_buf ) )
1600 + && ( S_ISREG(stat_buf.st_mode)
1601 + || S_ISLNK(stat_buf.st_mode) ) ) ) {
1602 + Debug( LDAP_DEBUG_ANY,
1603 + "tlsmc_convert: WARN: extracted key file is not present.\n",
1605 + if ( *ld_key ) PR_smprintf_free( *ld_key );
1612 + if ( checksum ) free( checksum );
1613 + if ( pem_dir ) PR_smprintf_free( pem_dir );
1614 + if ( readme_path ) PR_smprintf_free( readme_path );
1615 + if ( data ) free( data );
1616 + if ( nssdb_prefix ) free( nssdb_prefix );
1617 + if ( nssdb_dir_path ) free( nssdb_dir_path );
1618 + if ( nss_ctx ) tlsmc_close_nssdb( &nss_ctx );
1620 +#ifdef LDAP_R_COMPILE
1621 + ldap_pvt_thread_mutex_unlock( &tlsmc_mutex );
1628 +// returns 0 when successful
1630 +tlsmc_intercept_initialization( struct ldapoptions *lo, int is_server )
1633 + char *ld_cacertdir = NULL;
1634 + char *ld_cert = NULL;
1635 + char *ld_key = NULL;
1637 + ld_cacertdir = lo->ldo_tls_cacertdir ? LDAP_STRDUP( (char *) lo->ldo_tls_cacertdir ) : NULL;
1638 + ld_cert = lo->ldo_tls_certfile ? LDAP_STRDUP( (char *) lo->ldo_tls_certfile ) : NULL;
1639 + ld_key = lo->ldo_tls_keyfile ? LDAP_STRDUP( (char *) lo->ldo_tls_keyfile ) : NULL;
1641 + Debug( LDAP_DEBUG_TRACE,
1642 + "tlsmc_intercept_initialization: INFO: entry options follow:\n"
1643 + "tlsmc_intercept_initialization: INFO: cacertdir = `%s'\n"
1644 + "tlsmc_intercept_initialization: INFO: certfile = `%s'\n"
1645 + "tlsmc_intercept_initialization: INFO: keyfile = `%s'\n",
1646 + lo->ldo_tls_cacertdir, lo->ldo_tls_certfile, lo->ldo_tls_keyfile );
1648 + if ( 0 == tlsmc_convert( &ld_cacertdir, &ld_cert, &ld_key ) ) {
1649 + Debug( LDAP_DEBUG_ANY,
1650 + "tlsmc_intercept_initialization: FATAL: could not intercept TLS initialization. TLS will not work!\n",
1655 + if ( lo->ldo_tls_cacertdir ) LDAP_FREE( lo->ldo_tls_cacertdir );
1656 + lo->ldo_tls_cacertdir = ld_cacertdir ? LDAP_STRDUP( (char *) ld_cacertdir ) : NULL;
1658 + if ( lo->ldo_tls_certfile ) LDAP_FREE( lo->ldo_tls_certfile );
1659 + lo->ldo_tls_certfile = ld_cert ? LDAP_STRDUP( (char *) ld_cert ) : NULL;
1661 + if ( lo->ldo_tls_keyfile ) LDAP_FREE( lo->ldo_tls_keyfile );
1662 + lo->ldo_tls_keyfile = ld_key ? LDAP_STRDUP( (char *) ld_key ) : NULL;
1664 + Debug( LDAP_DEBUG_TRACE,
1665 + "tlsmc_intercept_initialization: INFO: altered options follow:\n"
1666 + "tlsmc_intercept_initialization: INFO: cacertdir = `%s'\n"
1667 + "tlsmc_intercept_initialization: INFO: certfile = `%s'\n"
1668 + "tlsmc_intercept_initialization: INFO: keyfile = `%s'\n",
1669 + lo->ldo_tls_cacertdir, lo->ldo_tls_certfile, lo->ldo_tls_keyfile );
1671 + Debug( LDAP_DEBUG_ANY,
1672 + "tlsmc_intercept_initialization: INFO: successfully intercepted TLS initialization. Continuing with OpenSSL only.\n",
1676 + if ( ld_cacertdir ) LDAP_FREE( ld_cacertdir );
1677 + if ( ld_cert ) LDAP_FREE( ld_cert );
1678 + if ( ld_key ) LDAP_FREE( ld_key );
1683 +#endif /* HAVE_MOZNSS_COMPATIBILITY */
1687 + indent-tabs-mode: t
1691 diff --git a/libraries/libldap/tls_mc.h b/libraries/libldap/tls_mc.h
1692 new file mode 100644
1693 index 0000000..2e6e567
1695 +++ b/libraries/libldap/tls_mc.h
1697 +#ifndef _LDAP_TLSMC_H
1698 +#define _LDAP_TLSMC_H
1700 +#include "portable.h"
1702 +#ifdef HAVE_MOZNSS_COMPATIBILITY
1704 +#include "ldap-int.h"
1707 +tlsmc_convert( char **ld_cacertdir, char **ld_cert, char **ld_key );
1710 +tlsmc_intercept_initialization( struct ldapoptions *lo, int is_server );
1713 +#endif /* HAVE_MOZNSS_COMPATIBILITY */
1714 +#endif /* _LDAP_TLSMC_H */
1715 diff --git a/libraries/libldap/tls_mc_ossl.c b/libraries/libldap/tls_mc_ossl.c
1716 new file mode 100644
1717 index 0000000..62ff54d
1719 +++ b/libraries/libldap/tls_mc_ossl.c
1721 +#include "portable.h"
1723 +/* This file contains functions that require OpenSSL headers due to some
1724 + conflicts with what MozNSS defines.
1727 +#ifdef HAVE_MOZNSS_COMPATIBILITY
1729 +#include <openssl/x509.h>
1730 +#include <openssl/pem.h>
1731 +#include "ldap-int.h"
1732 +#include <nspr/nspr.h>
1733 +#include <unistd.h>
1738 +tlsmc_cert_create_hash_symlink( char *cert_path, char *cacerts_dir )
1741 + X509 *cert = NULL;
1743 + unsigned long hash = 0;
1744 + char *cert_filename_p = NULL;
1745 + char *last_slash_p = NULL;
1746 + char *symlink_path = NULL;
1749 + if ( NULL == ( fp = fopen( cert_path, "r" ) ) ) {
1750 + Debug( LDAP_DEBUG_ANY,
1751 + "tlsmc_cert_create_hash_symlink: ERROR: could not open the cert file.\n",
1753 + perror( "tlsmc_cert_create_hash_symlink: ERROR: OS error" );
1756 + if ( NULL == PEM_read_X509( fp, &cert, NULL, NULL ) ) {
1757 + Debug( LDAP_DEBUG_ANY,
1758 + "tlsmc_cert_create_hash_symlink: ERROR: could not read PEM data.\n",
1762 + if ( 0 == ( hash = X509_subject_name_hash( cert ) ) ) {
1763 + Debug( LDAP_DEBUG_ANY,
1764 + "tlsmc_cert_create_hash_symlink: ERROR: could not hash subject.\n",
1769 + last_slash_p = strrchr( cert_path, '/' );
1770 + cert_filename_p = last_slash_p ? last_slash_p + 1 : cert_path;
1771 + for ( cnt = 0; cnt < 10; cnt++ ) {
1772 + if ( NULL == ( symlink_path = PR_smprintf( "%s/%08lx.%d", cacerts_dir, hash, cnt ) ) ) {
1773 + Debug( LDAP_DEBUG_ANY,
1774 + "tlsmc_cert_create_hash_symlink: ERROR: memory allocation error.\n",
1778 + if ( 0 != symlink( cert_filename_p, symlink_path ) ) {
1779 + if ( errno == EEXIST ) {
1780 + Debug( LDAP_DEBUG_ANY,
1781 + "tlsmc_cert_create_hash_symlink: INFO: symlink `%s' already exists.\n",
1782 + symlink_path, 0, 0 );
1783 + if ( symlink_path ) PR_smprintf( symlink_path );
1786 + Debug( LDAP_DEBUG_ANY,
1787 + "tlsmc_cert_create_hash_symlink: ERROR: could not create symlink.\n",
1789 + perror( "tlsmc_cert_create_hash_symlink: ERROR: OS error" );
1792 + Debug( LDAP_DEBUG_TRACE,
1793 + "tlsmc_cert_create_hash_symlink: INFO: the cert is now symlinked to %s.\n",
1794 + symlink_path, 0, 0 );
1798 + Debug( LDAP_DEBUG_ANY,
1799 + "tlsmc_cert_create_hash_symlink: INFO: could not create symlink (all possible file names taken).\n",
1802 + if ( symlink_path ) PR_smprintf_free( symlink_path );
1803 + if ( cert ) X509_free( cert );
1804 + if ( fp ) fclose( fp );
1808 +#endif /* HAVE_MOZNSS_COMPATIBILITY */
1812 + indent-tabs-mode: t
1816 diff --git a/libraries/libldap/tls_mc_ossl.h b/libraries/libldap/tls_mc_ossl.h
1817 new file mode 100644
1818 index 0000000..1b42845
1820 +++ b/libraries/libldap/tls_mc_ossl.h
1822 +#ifndef _LDAP_TLSMC_OSSL_H
1823 +#define _LDAP_TLSMC_OSSL_H
1825 +#include "portable.h"
1827 +#ifdef HAVE_MOZNSS_COMPATIBILITY
1830 +tlsmc_cert_create_hash_symlink( char *cert_path, char *cacerts_dir );
1834 diff --git a/libraries/libldap_r/Makefile.in b/libraries/libldap_r/Makefile.in
1835 index a43ce6e..6615f6c 100644
1836 --- a/libraries/libldap_r/Makefile.in
1837 +++ b/libraries/libldap_r/Makefile.in
1838 @@ -28,7 +28,7 @@ XXSRCS = apitest.c test.c \
1839 request.c os-ip.c url.c pagectrl.c sortctrl.c vlvctrl.c \
1840 init.c options.c print.c string.c util-int.c schema.c \
1841 charray.c os-local.c dnssrv.c utf-8.c utf-8-conv.c \
1842 - tls2.c tls_o.c tls_g.c tls_m.c \
1843 + tls2.c tls_o.c tls_g.c tls_m.c tls_mc.c tls_mc_ossl.c \
1844 turn.c ppolicy.c dds.c txn.c ldap_sync.c stctrl.c \
1845 assertion.c deref.c ldif.c fetch.c
1846 SRCS = threads.c rdwr.c rmutex.c tpool.c rq.c \
1847 @@ -46,7 +46,7 @@ OBJS = threads.lo rdwr.lo rmutex.lo tpool.lo rq.lo \
1848 request.lo os-ip.lo url.lo pagectrl.lo sortctrl.lo vlvctrl.lo \
1849 init.lo options.lo print.lo string.lo util-int.lo schema.lo \
1850 charray.lo os-local.lo dnssrv.lo utf-8.lo utf-8-conv.lo \
1851 - tls2.lo tls_o.lo tls_g.lo tls_m.lo \
1852 + tls2.lo tls_o.lo tls_g.lo tls_m.lo tls_mc.lo tls_mc_ossl.lo \
1853 turn.lo ppolicy.lo dds.lo txn.lo ldap_sync.lo stctrl.lo \
1854 assertion.lo deref.lo ldif.lo fetch.lo