From bdd2463142e63df846f091e884304b77e1fab7f2 Mon Sep 17 00:00:00 2001 From: Noriko Hosoi Date: Thu, 14 Apr 2016 13:42:34 -0700 Subject: [PATCH] Ticket #47536 - Allow usage of OpenLDAP libraries that don't use NSS for crypto Design Doc: http://www.port389.org/docs/389ds/design/allow-usage-of-openldap-lib-w-openssl.html This patch also addresses the issue described in Ticket #48756 - if startTLS is enabled, perl utilities fail to start. The ticket #48756 is closed as dup of Ticket #47536. Note: Instead of checking with "OpenSSL" for the openldap client library, this patch checks with "Not MozNSS" for non-Fedora/RHEL platform support. --- ldap/admin/src/scripts/DSUtil.pm.in | 13 + ldap/schema/01core389.ldif | 7 +- .../servers/plugins/replication/repl5_connection.c | 8 +- .../plugins/replication/windows_connection.c | 8 +- ldap/servers/slapd/ldaputil.c | 120 ++- ldap/servers/slapd/libglobs.c | 26 + ldap/servers/slapd/proto-slap.h | 2 + ldap/servers/slapd/slap.h | 4 +- ldap/servers/slapd/slapi-plugin.h | 23 +- ldap/servers/slapd/slapi-private.h | 3 + ldap/servers/slapd/ssl.c | 919 ++++++++++++++++++--- ldap/servers/slapd/util.c | 123 +-- 12 files changed, 1054 insertions(+), 202 deletions(-) diff --git a/ldap/admin/src/scripts/DSUtil.pm.in b/ldap/admin/src/scripts/DSUtil.pm.in index 9830703..3476d67 100644 --- a/ldap/admin/src/scripts/DSUtil.pm.in +++ b/ldap/admin/src/scripts/DSUtil.pm.in @@ -1251,6 +1251,19 @@ sub get_info { $info{ldapiURL} = "ldapi://" . $value; } + while($entry = readOneEntry $ldif){ + if($entry->getDN() eq "cn=encryption,cn=config"){ + $foundcfg = "yes"; + last; + } + } + if($foundcfg eq "yes"){ + $info{cacertfile} = $entry->getValues("CACertExtractFile"); + if ($info{cacertfile}) { + $ENV{LDAPTLS_CACERT}=$info{cacertfile}; + } + } + close (DSE); return %info; } diff --git a/ldap/schema/01core389.ldif b/ldap/schema/01core389.ldif index 5628e99..e620e74 100644 --- a/ldap/schema/01core389.ldif +++ b/ldap/schema/01core389.ldif @@ -103,6 +103,9 @@ attributeTypes: ( allowWeakCipher-oid NAME 'allowWeakCipher' DESC 'Netscape defi attributeTypes: ( nsSSLToken-oid NAME 'nsSSLToken' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape' ) attributeTypes: ( nsSSLPersonalitySSL-oid NAME 'nsSSLPersonalitySSL' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape' ) attributeTypes: ( nsSSLActivation-oid NAME 'nsSSLActivation' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape' ) +attributeTypes: ( CACertExtractFile-oid NAME 'CACertExtractFile' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape' ) +attributeTypes: ( ServerKeyExtractFile-oid NAME 'ServerKeyExtractFile' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape' ) +attributeTypes: ( ServerCertExtractFile-oid NAME 'ServerCertExtractFile' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape' ) attributeTypes: ( 2.16.840.1.113730.3.1.2091 NAME 'nsslapd-suffix' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 X-ORIGIN 'Netscape' ) attributeTypes: ( 2.16.840.1.113730.3.1.2092 NAME 'nsslapd-ldapiautodnsuffix' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 X-ORIGIN 'Netscape' ) attributeTypes: ( 2.16.840.1.113730.3.1.2095 NAME 'connection' DESC 'Netscape defined attribute type' SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'Netscape' ) @@ -308,8 +311,8 @@ objectClasses: ( 2.16.840.1.113730.3.2.103 NAME 'nsDS5ReplicationAgreement' DESC objectClasses: ( 2.16.840.1.113730.3.2.39 NAME 'nsslapdConfig' DESC 'Netscape defined objectclass' SUP top MAY ( cn ) X-ORIGIN 'Netscape Directory Server' ) objectClasses: ( 2.16.840.1.113730.3.2.317 NAME 'nsSaslMapping' DESC 'Netscape defined objectclass' SUP top MUST ( cn $ nsSaslMapRegexString $ nsSaslMapBaseDNTemplate $ nsSaslMapFilterTemplate ) MAY ( nsSaslMapPriority ) X-ORIGIN 'Netscape Directory Server' ) objectClasses: ( 2.16.840.1.113730.3.2.43 NAME 'nsSNMP' DESC 'Netscape defined objectclass' SUP top MUST ( cn $ nsSNMPEnabled ) MAY ( nsSNMPOrganization $ nsSNMPLocation $ nsSNMPContact $ nsSNMPDescription $ nsSNMPName $ nsSNMPMasterHost $ nsSNMPMasterPort ) X-ORIGIN 'Netscape Directory Server' ) -objectClasses: ( nsEncryptionConfig-oid NAME 'nsEncryptionConfig' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( nsCertfile $ nsKeyfile $ nsSSL2 $ nsSSL3 $ nsTLS1 $ sslVersionMin $ sslVersionMax $ nsSSLSessionTimeout $ nsSSL3SessionTimeout $ nsSSLClientAuth $ nsSSL2Ciphers $ nsSSL3Ciphers $ nsSSLSupportedCiphers $ allowWeakCipher) X-ORIGIN 'Netscape' ) -objectClasses: ( nsEncryptionModule-oid NAME 'nsEncryptionModule' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( nsSSLToken $ nsSSLPersonalityssl $ nsSSLActivation ) X-ORIGIN 'Netscape' ) +objectClasses: ( nsEncryptionConfig-oid NAME 'nsEncryptionConfig' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( nsCertfile $ nsKeyfile $ nsSSL2 $ nsSSL3 $ nsTLS1 $ sslVersionMin $ sslVersionMax $ nsSSLSessionTimeout $ nsSSL3SessionTimeout $ nsSSLClientAuth $ nsSSL2Ciphers $ nsSSL3Ciphers $ nsSSLSupportedCiphers $ allowWeakCipher $ CACertExtractFile ) X-ORIGIN 'Netscape' ) +objectClasses: ( nsEncryptionModule-oid NAME 'nsEncryptionModule' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( nsSSLToken $ nsSSLPersonalityssl $ nsSSLActivation $ ServerKeyExtractFile $ ServerCertExtractFile ) X-ORIGIN 'Netscape' ) objectClasses: ( 2.16.840.1.113730.3.2.327 NAME 'rootDNPluginConfig' DESC 'Netscape defined objectclass' SUP top MUST ( cn ) MAY ( rootdn-open-time $ rootdn-close-time $ rootdn-days-allowed $ rootdn-allow-host $ rootdn-deny-host $ rootdn-allow-ip $ rootdn-deny-ip ) X-ORIGIN 'Netscape' ) objectClasses: ( 2.16.840.1.113730.3.2.328 NAME 'nsSchemaPolicy' DESC 'Netscape defined objectclass' SUP top MAY ( cn $ schemaUpdateObjectclassAccept $ schemaUpdateObjectclassReject $ schemaUpdateAttributeAccept $ schemaUpdateAttributeReject) X-ORIGIN 'Netscape Directory Server' ) diff --git a/ldap/servers/plugins/replication/repl5_connection.c b/ldap/servers/plugins/replication/repl5_connection.c index d193938..88f2a1d 100644 --- a/ldap/servers/plugins/replication/repl5_connection.c +++ b/ldap/servers/plugins/replication/repl5_connection.c @@ -1234,9 +1234,9 @@ conn_connect(Repl_Connection *conn) * initialisation should be done before ever trying to open any connection at all. */ if (conn->transport_flags == TRANSPORT_FLAG_TLS) { - secure = 2; + secure = SLAPI_LDAP_INIT_FLAG_startTLS; } else if (conn->transport_flags == TRANSPORT_FLAG_SSL) { - secure = 1; + secure = SLAPI_LDAP_INIT_FLAG_SSL; } if (secure > 0) { @@ -1261,7 +1261,7 @@ conn_connect(Repl_Connection *conn) "%s: Trying %s%s slapi_ldap_init_ext\n", agmt_get_long_name(conn->agmt), secure ? "secure" : "non-secure", - (secure == 2) ? " startTLS" : ""); + (secure == SLAPI_LDAP_INIT_FLAG_startTLS) ? " startTLS" : ""); /* shared = 1 because we will read results from a second thread */ if (conn->ld) { /* Since we call slapi_ldap_init, we must call slapi_ldap_unbind */ @@ -1279,7 +1279,7 @@ conn_connect(Repl_Connection *conn) "%s: Failed to establish %s%sconnection to the consumer\n", agmt_get_long_name(conn->agmt), secure ? "secure " : "", - (secure == 2) ? "startTLS " : ""); + (secure == SLAPI_LDAP_INIT_FLAG_startTLS) ? "startTLS " : ""); goto done; } diff --git a/ldap/servers/plugins/replication/windows_connection.c b/ldap/servers/plugins/replication/windows_connection.c index a06a07e..cab3715 100644 --- a/ldap/servers/plugins/replication/windows_connection.c +++ b/ldap/servers/plugins/replication/windows_connection.c @@ -1313,9 +1313,9 @@ windows_conn_connect(Repl_Connection *conn) * initialisation should be done before ever trying to open any connection at all. */ if (conn->transport_flags == TRANSPORT_FLAG_TLS) { - secure = 2; + secure = SLAPI_LDAP_INIT_FLAG_startTLS; } else if (conn->transport_flags == TRANSPORT_FLAG_SSL) { - secure = 1; + secure = SLAPI_LDAP_INIT_FLAG_SSL; } if (secure > 0) { @@ -1340,7 +1340,7 @@ windows_conn_connect(Repl_Connection *conn) "%s: Trying %s%s slapi_ldap_init_ext\n", agmt_get_long_name(conn->agmt), secure ? "secure" : "non-secure", - (secure == 2) ? " startTLS" : ""); + (secure == SLAPI_LDAP_INIT_FLAG_startTLS) ? " startTLS" : ""); conn->ld = slapi_ldap_init_ext(NULL, conn->hostname, conn->port, secure, 0, NULL); if (NULL == conn->ld) @@ -1353,7 +1353,7 @@ windows_conn_connect(Repl_Connection *conn) "%s: Failed to establish %s%sconnection to the consumer\n", agmt_get_long_name(conn->agmt), secure ? "secure " : "", - (secure == 2) ? "startTLS " : ""); + (secure == SLAPI_LDAP_INIT_FLAG_startTLS) ? "startTLS " : ""); goto done; } diff --git a/ldap/servers/slapd/ldaputil.c b/ldap/servers/slapd/ldaputil.c index 8a54cb9..138be1e 100644 --- a/ldap/servers/slapd/ldaputil.c +++ b/ldap/servers/slapd/ldaputil.c @@ -575,6 +575,7 @@ setup_ol_tls_conn(LDAP *ld, int clientauth) int optval = 0; int ssl_strength = 0; int rc = 0; + const char *cacert = NULL; if (config_get_ssl_check_hostname()) { ssl_strength = LDAP_OPT_X_TLS_HARD; @@ -587,7 +588,29 @@ setup_ol_tls_conn(LDAP *ld, int clientauth) slapi_log_error(SLAPI_LOG_FATAL, "setup_ol_tls_conn", "failed: unable to set REQUIRE_CERT option to %d\n", ssl_strength); } - /* tell it where our cert db is */ + if (slapi_client_uses_non_nss(ld)) { + cacert = slapi_get_cacertfile(); + if (cacert) { + /* CA Cert PEM file exists. Set the path to openldap option. */ + rc = ldap_set_option(ld, LDAP_OPT_X_TLS_CACERTFILE, cacert); + if (rc) { + slapi_log_error(SLAPI_LOG_FATAL, "setup_ol_tls_conn", + "Could not set CA cert path [%s]: %d:%s\n", + cacert, rc, ldap_err2string(rc)); + } + } + if (slapi_client_uses_openssl(ld)) { + const int crlcheck = LDAP_OPT_X_TLS_CRL_ALL; + /* Sets the CRL evaluation strategy. */ + rc = ldap_set_option(ld, LDAP_OPT_X_TLS_CRLCHECK, &crlcheck); + if (rc) { + slapi_log_error(SLAPI_LOG_FATAL, "setup_ol_tls_conn", + "Could not set CRLCHECK [%d]: %d:%s\n", + crlcheck, rc, ldap_err2string(rc)); + } + } + } + /* tell it where our cert db/file is */ if ((rc = ldap_set_option(ld, LDAP_OPT_X_TLS_CACERTDIR, certdir))) { slapi_log_error(SLAPI_LOG_FATAL, "setup_ol_tls_conn", "failed: unable to set CACERTDIR option to %s\n", certdir); @@ -635,8 +658,8 @@ setup_ol_tls_conn(LDAP *ld, int clientauth) on the secure setting (389 for ldap, 636 for ldaps, 389 for starttls) secure takes 1 of 3 values - 0 means regular ldap, 1 means ldaps, 2 means regular ldap with starttls. - filename is the ldapi file name - if this is given, and no other options - are given, ldapi is assumed. + ldapi_socket is the ldapi file name + if this is given, and no other options are given, ldapi is assumed. */ /* util_sasl_path: the string argument for putenv. It must be a global or a static */ @@ -646,12 +669,12 @@ LDAP * slapi_ldap_init_ext( const char *ldapurl, /* full ldap url */ const char *hostname, /* can also use this to override - host in url */ + host in url */ int port, /* can also use this to override port in url */ int secure, /* 0 for ldap, 1 for ldaps, 2 for starttls - - override proto in url */ + override proto in url */ int shared, /* if true, LDAP* will be shared among multiple threads */ - const char *filename /* for ldapi */ + const char *ldapi_socket /* for ldapi */ ) { LDAPURLDesc *ludp = NULL; @@ -705,16 +728,16 @@ slapi_ldap_init_ext( /* use secure setting from url if none given */ if (!secure && ludp) { if (secureurl) { - secure = 1; + secure = SLAPI_LDAP_INIT_FLAG_SSL; } else if (0/* starttls option - not supported yet in LDAP URLs */) { - secure = 2; + secure = SLAPI_LDAP_INIT_FLAG_startTLS; } } /* ldap_url_parse doesn't yet handle ldapi */ /* - if (!filename && ludp && ludp->lud_file) { - filename = ludp->lud_file; + if (!ldapi_socket && ludp && ludp->lud_file) { + ldapi_socket = ludp->lud_file; } */ @@ -762,10 +785,11 @@ slapi_ldap_init_ext( } else { char *makeurl = NULL; - if (filename) { - makeurl = slapi_ch_smprintf("ldapi://%s/", filename); + if (ldapi_socket) { + makeurl = slapi_ch_smprintf("ldapi://%s/", ldapi_socket); } else { /* host port */ - makeurl = convert_to_openldap_uri(hostname, port, (secure == 1 ? "ldaps" : "ldap")); + makeurl = convert_to_openldap_uri(hostname, port, + (secure == SLAPI_LDAP_INIT_FLAG_SSL ? "ldaps" : "ldap")); } if (PR_SUCCESS != PR_CallOnce(&ol_init_callOnce, internal_ol_init_init)) { slapi_log_error(SLAPI_LOG_FATAL, "slapi_ldap_init_ext", @@ -796,15 +820,15 @@ slapi_ldap_init_ext( * hostname (such as localhost.localdomain). */ if((rc = ldap_set_option(ld, LDAP_OPT_X_SASL_NOCANON, LDAP_OPT_ON))){ - slapi_log_error(SLAPI_LOG_FATAL, "slapi_ldap_init_ext", + slapi_log_error(SLAPI_LOG_FATAL, "slapi_ldap_init_ext", "Could not set ldap option LDAP_OPT_X_SASL_NOCANON for (%s), error %d (%s)\n", ldapurl, rc, ldap_err2string(rc) ); } } #else /* !USE_OPENLDAP */ - if (filename) { + if (ldapi_socket) { /* ldapi in mozldap client is not yet supported */ - } else if (secure == 1) { + } else if (secure == SLAPI_LDAP_INIT_FLAG_SSL) { ld = ldapssl_init(hostname, port, secure); } else { /* regular ldap and/or starttls */ /* @@ -828,7 +852,7 @@ slapi_ldap_init_ext( } } - if ((ld != NULL) && !filename) { + if (ld && !ldapi_socket) { /* * Set the outbound LDAP I/O timeout based on the server config. */ @@ -876,7 +900,7 @@ slapi_ldap_init_ext( * LDAP* if it has already gone through ldapssl_init - * so, use NULL if using starttls */ - if (secure == 1) { + if (secure == SLAPI_LDAP_INIT_FLAG_SSL) { myld = ld; } @@ -900,7 +924,7 @@ slapi_ldap_init_ext( SLAPI_COMPONENT_NAME_NSPR " error %d - %s)\n", prerr, slapd_pr_strerror(prerr)); } - if (secure == 1) { + if (secure == SLAPI_LDAP_INIT_FLAG_SSL) { /* tell bind code we are using SSL */ ldap_set_option(ld, LDAP_OPT_SSL, LDAP_OPT_ON); } @@ -908,7 +932,7 @@ slapi_ldap_init_ext( } } - if (ld && (secure == 2)) { + if (ld && (secure == SLAPI_LDAP_INIT_FLAG_startTLS)) { /* * We don't have a way to stash context data with the LDAP*, so we * stash the information in the client controls (currently unused). @@ -938,8 +962,8 @@ slapi_ldap_init_ext( slapi_log_error(SLAPI_LOG_SHELL, "slapi_ldap_init_ext", "Success: set up conn to [%s:%d]%s\n", hostname, port, - (secure == 2) ? " using startTLS" : - ((secure == 1) ? " using SSL" : "")); + (secure == SLAPI_LDAP_INIT_FLAG_startTLS) ? " using startTLS" : + ((secure == SLAPI_LDAP_INIT_FLAG_SSL) ? " using SSL" : "")); done: ldap_free_urldesc(ludp); @@ -993,7 +1017,7 @@ ldaputil_get_saslpath() LDAP * slapi_ldap_init( char *ldaphost, int ldapport, int secure, int shared ) { - return slapi_ldap_init_ext(NULL, ldaphost, ldapport, secure, shared, NULL); + return slapi_ldap_init_ext(NULL, ldaphost, ldapport, secure, shared, NULL/*, NULL*/); } /* @@ -1030,7 +1054,7 @@ slapi_ldap_bind( ldap_get_option(ld, LDAP_OPT_CLIENT_CONTROLS, &clientctrls); if (clientctrls && clientctrls[0] && slapi_control_present(clientctrls, START_TLS_OID, NULL, NULL)) { - secure = 2; + secure = SLAPI_LDAP_INIT_FLAG_startTLS; } else { #if defined(USE_OPENLDAP) /* openldap doesn't have a SSL/TLS yes/no flag - so grab the @@ -1039,7 +1063,7 @@ slapi_ldap_bind( ldap_get_option(ld, LDAP_OPT_URI, &ldapurl); if (ldapurl && !PL_strncasecmp(ldapurl, "ldaps", 5)) { - secure = 1; + secure = SLAPI_LDAP_INIT_FLAG_SSL; } slapi_ch_free_string(&ldapurl); #else /* !USE_OPENLDAP */ @@ -1077,7 +1101,7 @@ slapi_ldap_bind( bvcreds.bv_len = creds ? strlen(creds) : 0; } - if (secure == 2) { /* send start tls */ + if (secure == SLAPI_LDAP_INIT_FLAG_startTLS) { /* send start tls */ rc = ldap_start_tls_s(ld, NULL /* serverctrls?? */, NULL); if (LDAP_SUCCESS != rc) { slapi_log_error(SLAPI_LOG_FATAL, "slapi_ldap_bind", @@ -2386,3 +2410,47 @@ slapi_berval_get_msg_len(struct berval *bv, int strict) return len; } + +int +slapi_client_uses_non_nss(LDAP *ld) +{ + static int not_nss = 0; +#if defined(USE_OPENLDAP) + static int initialized = 0; + char *package_name = NULL; + int rc; + + if (initialized) { + return not_nss; + } + rc = ldap_get_option(ld, LDAP_OPT_X_TLS_PACKAGE, &package_name); + if (!rc && PL_strcasecmp(package_name, "MozNSS")) { + not_nss = 1; + slapi_ch_free_string(&package_name); + } + initialized = 1; +#endif + return not_nss; +} + +int +slapi_client_uses_openssl(LDAP *ld) +{ + static int is_openssl = 0; +#if defined(USE_OPENLDAP) + static int initialized = 0; + char *package_name = NULL; + int rc; + + if (initialized) { + return is_openssl; + } + rc = ldap_get_option(ld, LDAP_OPT_X_TLS_PACKAGE, &package_name); + if (!rc && !PL_strcasecmp(package_name, "OpenSSL")) { + is_openssl = 1; + slapi_ch_free_string(&package_name); + } + initialized = 1; +#endif + return is_openssl; +} diff --git a/ldap/servers/slapd/libglobs.c b/ldap/servers/slapd/libglobs.c index 33f2f92..7bbf10e 100644 --- a/ldap/servers/slapd/libglobs.c +++ b/ldap/servers/slapd/libglobs.c @@ -253,6 +253,7 @@ slapi_int_t init_malloc_mmap_threshold; #ifdef MEMPOOL_EXPERIMENTAL slapi_onoff_t init_mempool_switch; #endif +slapi_onoff_t init_extract_pem; #define DEFAULT_SSLCLIENTAPTH "off" #define DEFAULT_ALLOW_ANON_ACCESS "on" @@ -1197,6 +1198,10 @@ static struct config_get_and_set { (void**)&global_slapdFrontendConfig.logging_hr_timestamps, CONFIG_ON_OFF, NULL, &init_logging_hr_timestamps}, #endif + {CONFIG_EXTRACT_PEM, config_set_extract_pem, + NULL, 0, + (void**)&global_slapdFrontendConfig.extract_pem, + CONFIG_ON_OFF, (ConfigGetFunc)config_get_extract_pem, &init_extract_pem}, {CONFIG_LOGGING_BACKEND, NULL, log_set_backend, 0, (void**)&global_slapdFrontendConfig.logging_backend, @@ -1680,6 +1685,7 @@ FrontendConfig_init () { } } #endif /* MEMPOOL_EXPERIMENTAL */ + init_extract_pem = cfg->extract_pem = LDAP_OFF; init_config_get_and_set(); } @@ -8074,6 +8080,26 @@ config_get_maxsimplepaged_per_conn() return retVal; } +int +config_set_extract_pem(const char *attrname, char *value, char *errorbuf, int apply) +{ + slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig(); + int retVal = LDAP_SUCCESS; + + retVal = config_set_onoff(attrname, value, &(slapdFrontendConfig->extract_pem), errorbuf, apply); + return retVal; +} + +int +config_get_extract_pem() +{ + slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig(); + int retVal; + + retVal = slapdFrontendConfig->extract_pem; + return retVal; +} + #if defined(LINUX) int config_set_malloc_mxfast(const char *attrname, char *value, char *errorbuf, int apply) diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h index e9b4618..255e4bd 100644 --- a/ldap/servers/slapd/proto-slap.h +++ b/ldap/servers/slapd/proto-slap.h @@ -581,6 +581,7 @@ int config_get_cn_uses_dn_syntax_in_dns(); int config_get_enable_nunc_stans(void); int config_set_enable_nunc_stans(const char *attrname, char *value, char *errorbuf, int apply); #endif +int config_set_extract_pem(const char *attrname, char *value, char *errorbuf, int apply); PLHashNumber hashNocaseString(const void *key); PRIntn hashNocaseCompare(const void *v1, const void *v2); @@ -594,6 +595,7 @@ int config_get_malloc_mmap_threshold(); #endif int config_get_maxsimplepaged_per_conn(); +int config_get_extract_pem(); int is_abspath(const char *); char* rel2abspath( char * ); diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h index 0019c68..c6763e4 100644 --- a/ldap/servers/slapd/slap.h +++ b/ldap/servers/slapd/slap.h @@ -2129,6 +2129,8 @@ typedef struct _slapdEntryPoints { #define CONFIG_MAXSIMPLEPAGED_PER_CONN_ATTRIBUTE "nsslapd-maxsimplepaged-per-conn" #define CONFIG_LOGGING_BACKEND "nsslapd-logging-backend" +#define CONFIG_EXTRACT_PEM "nsslapd-extract-pemfiles" + #ifdef HAVE_CLOCK_GETTIME #define CONFIG_LOGGING_HR_TIMESTAMPS "nsslapd-logging-hr-timestamps-enabled" #endif @@ -2331,7 +2333,6 @@ typedef struct _slapdFrontendConfig { #ifdef HAVE_CLOCK_GETTIME slapi_onoff_t logging_hr_timestamps; #endif - slapi_onoff_t return_exact_case; /* Return attribute names with the same case as they appear in at.conf */ @@ -2427,6 +2428,7 @@ typedef struct _slapdFrontendConfig { int malloc_trim_threshold; /* mallopt M_TRIM_THRESHOLD */ int malloc_mmap_threshold; /* mallopt M_MMAP_THRESHOLD */ #endif + slapi_onoff_t extract_pem; /* If "on", export key/cert as pem files */ } slapdFrontendConfig_t; /* possible values for slapdFrontendConfig_t.schemareplace */ diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h index 0dd10d9..d13aae9 100644 --- a/ldap/servers/slapd/slapi-plugin.h +++ b/ldap/servers/slapd/slapi-plugin.h @@ -6160,12 +6160,14 @@ int slapi_rwlock_get_size( void ); /* * thread-safe LDAP connections */ +#define SLAPI_LDAP_INIT_FLAG_SSL 1 /* SSL */ +#define SLAPI_LDAP_INIT_FLAG_startTLS 2 /* startTLS */ /** * Initializes an LDAP connection, and returns a handle to the connection. * * \param ldaphost Hostname or IP address - NOTE: for TLS or GSSAPI, should be the FQDN * \param ldapport LDAP server port number (default 389) - * \param secure \c 0 - LDAP \c 1 - LDAPS \c 2 - startTLS + * \param secure \c 0 - LDAP \c SLAPI_LDAP_INIT_FLAG_SSL - LDAPS \c SLAPI_LDAP_INIT_FLAG_startTLS - startTLS * \param shared \c 0 - single thread access \c 1 - LDAP* will be shared among multiple threads * \return A pointer to an LDAP* handle * @@ -6184,6 +6186,7 @@ LDAP *slapi_ldap_init( char *ldaphost, int ldapport, int secure, int shared ); * \see slapi_ldap_init_ext() */ void slapi_ldap_unbind( LDAP *ld ); + /** * Initializes an LDAP connection, and returns a handle to the connection. * @@ -6191,9 +6194,9 @@ void slapi_ldap_unbind( LDAP *ld ); * ldapi://path - if \c NULL, #hostname, #port, and #secure must be provided * \param hostname Hostname or IP address - NOTE: for TLS or GSSAPI, should be the FQDN * \param port LDAP server port number (default 389) - * \param secure \c 0 - LDAP \c 1 - LDAPS \c 2 - startTLS + * \param secure \c 0 - LDAP \c SLAPI_LDAP_INIT_FLAG_SSL - LDAPS \c SLAPI_LDAP_INIT_FLAG_startTLS - startTLS * \param shared \c 0 - single thread access \c 1 - LDAP* will be shared among multiple threads - * \param filename - currently not supported + * \param ldapi_socket - ldapi socket path * \return A pointer to an LDAP* handle * * \note Use #slapi_ldap_unbind() to close and free the handle @@ -6209,7 +6212,7 @@ LDAP *slapi_ldap_init_ext( int secure, /* 0 for ldap, 1 for ldaps, 2 for starttls - override proto in url */ int shared, /* if true, LDAP* will be shared among multiple threads */ - const char *filename /* for ldapi */ + const char *ldap_socket /* ldapi socket path */ ); /** * The LDAP bind request - this function handles all of the different types of mechanisms @@ -6246,6 +6249,18 @@ int slapi_ldap_bind( ); /** + * Return the full path of PEM format CA Cert + * + * \return the full path of PEM format CA Cert + */ +const char * slapi_get_cacertfile(); + +/** + * Set the full path of PEM format CA Cert + */ +void slapi_set_cacertfile(char *certfile); + +/** * Create either a v1 Proxy Auth Control or a v2 Proxied Auth Control * * \param ld the LDAP connection handle diff --git a/ldap/servers/slapd/slapi-private.h b/ldap/servers/slapd/slapi-private.h index fb7b5f8..9034869 100644 --- a/ldap/servers/slapd/slapi-private.h +++ b/ldap/servers/slapd/slapi-private.h @@ -1160,6 +1160,7 @@ char* slapd_get_tmp_dir( void ); #include /* GGOODREPL - For BUFSIZ, below, gak */ const char* escape_string (const char* str, char buf[BUFSIZ]); const char* escape_string_with_punctuation(const char* str, char buf[BUFSIZ]); +const char* escape_string_for_filename(const char* str); void strcpy_unescape_value( char *d, const char *s ); char *slapi_berval_get_string_copy(const struct berval *bval); @@ -1304,6 +1305,8 @@ void add_internal_modifiersname(Slapi_PBlock *pb, Slapi_Entry *e); /* ldaputil.c */ char *ldaputil_get_saslpath(); +int slapi_client_uses_non_nss(LDAP *ld); +int slapi_client_uses_openssl(LDAP *ld); /* ssl.c */ /* diff --git a/ldap/servers/slapd/ssl.c b/ldap/servers/slapd/ssl.c index 544c9bc..85c2c6f 100644 --- a/ldap/servers/slapd/ssl.c +++ b/ldap/servers/slapd/ssl.c @@ -231,6 +231,19 @@ PRBool enableSSL3 = PR_FALSE; */ PRBool enableTLS1 = PR_TRUE; +/* + * OpenLDAP client library with OpenSSL (ticket 47536) + */ +#define PEMEXT ".pem" +/* CA cert pem file */ +static char *CACertPemFile = NULL; + +/* helper functions for openldap update. */ +static int slapd_extract_cert(Slapi_Entry *entry, int isCA); +static int slapd_extract_key(Slapi_Entry *entry, char *token, PK11SlotInfo *slot); +static void entrySetValue(Slapi_DN *sdn, char *type, char *value); +static char *gen_pem_path(char *filename); + static void slapd_SSL_report(int degree, char *fmt, va_list args) { @@ -277,7 +290,7 @@ getSupportedCiphers() SSL_GetCipherSuiteInfo((PRUint16)_conf_ciphers[i].num,&info,sizeof(info)); /* only support FIPS approved ciphers in FIPS mode */ if (!isFIPS || info.isFIPS) { - cipher_names[idx++] = PR_smprintf("%s%s%s%s%s%s%d", + cipher_names[idx++] = slapi_ch_smprintf("%s%s%s%s%s%s%d", _conf_ciphers[i].name,sep, info.symCipherName,sep, info.macAlgorithmName,sep, @@ -315,7 +328,7 @@ getEnabledCiphers() SSL_CipherPrefGetDefault(_conf_ciphers[x].num, &enabled); if (enabled) { SSL_GetCipherSuiteInfo((PRUint16)_conf_ciphers[x].num,&info,sizeof(info)); - enabled_cipher_names[idx++] = PR_smprintf("%s%s%s%s%s%s%d", + enabled_cipher_names[idx++] = slapi_ch_smprintf("%s%s%s%s%s%s%d", _conf_ciphers[x].name,sep, info.symCipherName,sep, info.macAlgorithmName,sep, @@ -575,7 +588,7 @@ charray2str(char **ary, const char *delim) if (str) { str = PR_sprintf_append(str, "%s%s", delim, *ary++); } else { - str = PR_smprintf("%s", *ary++); + str = slapi_ch_smprintf("%s", *ary++); } } @@ -757,7 +770,7 @@ _conf_setciphers(char *ciphers, int flags) slapi_ch_free((void **)&unsuplist); /* strings inside are static */ if (!enabledOne) { - char *nocipher = PR_smprintf("No active cipher suite is available."); + char *nocipher = slapi_ch_smprintf("No active cipher suite is available."); return nocipher; } _conf_dumpciphers(); @@ -856,6 +869,31 @@ freeChildren( char **list ) { } } +static void +entrySetValue(Slapi_DN *sdn, char *type, char *value) +{ + Slapi_PBlock mypb; + LDAPMod attr; + LDAPMod *mods[2]; + char *values[2]; + + values[0] = value; + values[1] = NULL; + + /* modify the attribute */ + attr.mod_type = type; + attr.mod_op = LDAP_MOD_REPLACE; + attr.mod_values = values; + + mods[0] = &attr; + mods[1] = NULL; + + pblock_init(&mypb); + slapi_modify_internal_set_pb_ext(&mypb, sdn, mods, NULL, NULL, (void *)plugin_get_default_component_id(), 0); + slapi_modify_internal_pb(&mypb); + pblock_done(&mypb); +} + /* Logs a warning and returns 1 if cert file doesn't exist. You * can skip the warning log message by setting no_log to 1.*/ static int @@ -863,8 +901,8 @@ warn_if_no_cert_file(const char *dir, int no_log) { int ret = 0; char *filename = slapi_ch_smprintf("%s/cert8.db", dir); - PRStatus status = PR_Access(filename, PR_ACCESS_READ_OK); - if (PR_SUCCESS != status) { + PRStatus status = PR_Access(filename, PR_ACCESS_READ_OK); + if (PR_SUCCESS != status) { slapi_ch_free_string(&filename); filename = slapi_ch_smprintf("%s/cert7.db", dir); status = PR_Access(filename, PR_ACCESS_READ_OK); @@ -1148,7 +1186,7 @@ slapd_nss_init(int init_ssl, int config_available) slapd_pk11_configurePKCS11(NULL, NULL, tokPBE, ptokPBE, NULL, NULL, NULL, NULL, 0, 0 ); secStatus = NSS_Initialize(certdir, NULL, NULL, "secmod.db", nssFlags); - dongle_file_name = PR_smprintf("%s/pin.txt", certdir); + dongle_file_name = slapi_ch_smprintf("%s/pin.txt", certdir); if (secStatus != SECSuccess) { errorCode = PR_GetError(); @@ -1280,10 +1318,16 @@ slapd_ssl_init() freeConfigEntry( &entry ); return -1; } + if (config_get_extract_pem()) { + /* extract cert file and convert it to a pem file. */ + slapd_extract_cert(entry, PR_TRUE); + } + if ((family_list = getChildren(configDN))) { char **family; char *token; char *activation; + int isinternal = 0; for (family = family_list; *family; family++) { @@ -1311,6 +1355,7 @@ slapd_ssl_init() if (!PL_strcasecmp(token, "internal") || !PL_strcasecmp(token, "internal (software)")) { slot = slapd_pk11_getInternalKeySlot(); + isinternal = 1; } else { slot = slapd_pk11_findSlotByName(token); } @@ -1324,8 +1369,6 @@ slapd_ssl_init() return -1; } - slapi_ch_free((void **) &token); - if (!slot) { errorCode = PR_GetError(); slapd_SSL_warn("Security Initialization: Unable to find slot (" @@ -1333,6 +1376,7 @@ slapd_ssl_init() errorCode, slapd_pr_strerror(errorCode)); freeChildren(family_list); freeConfigEntry( &entry ); + slapi_ch_free((void **) &token); return -1; } /* authenticate */ @@ -1342,13 +1386,20 @@ slapd_ssl_init() #endif if (slapd_pk11_authenticate(slot, PR_TRUE, NULL) != SECSuccess) { errorCode = PR_GetError(); - slapd_SSL_warn("Security Initialization: Unable to authenticate (" - SLAPI_COMPONENT_NAME_NSPR " error %d - %s)", - errorCode, slapd_pr_strerror(errorCode)); + slapi_log_error(SLAPI_LOG_FATAL, "slapd_ssl_init", + "Unable to authenticate (" SLAPI_COMPONENT_NAME_NSPR " error %d - %s)", + errorCode, slapd_pr_strerror(errorCode)); freeChildren(family_list); freeConfigEntry( &entry ); + slapi_ch_free((void **) &token); return -1; } + if (config_get_extract_pem()) { + /* Get Server{Key,Cert}ExtractFile from cn=Cipher,cn=encryption entry if any. */ + slapd_extract_cert(entry, PR_FALSE); + slapd_extract_key(entry, isinternal?internalTokenName:token, slot); + } + slapi_ch_free((void **) &token); } freeChildren( family_list ); freeConfigEntry( &entry ); @@ -1669,9 +1720,9 @@ slapd_ssl_init2(PRFileDesc **fd, int startTLS) if(slapd_pk11_isFIPS()) { if(slapd_pk11_authenticate(slot, PR_TRUE, NULL) != SECSuccess) { errorCode = PR_GetError(); - slapd_SSL_warn("Security Initialization: Unable to authenticate (" - SLAPI_COMPONENT_NAME_NSPR " error %d - %s)", - errorCode, slapd_pr_strerror(errorCode)); + slapi_log_error(SLAPI_LOG_FATAL, "slapd_ssl_init2", + "Unable to authenticate (" SLAPI_COMPONENT_NAME_NSPR " error %d - %s)\n", + errorCode, slapd_pr_strerror(errorCode)); return -1; } fipsMode = PR_TRUE; @@ -2103,111 +2154,117 @@ slapd_SSL_client_auth (LDAP* ld) char *token = NULL; SVRCOREStdPinObj *StdPinObj; SVRCOREError err = SVRCORE_Success; + char *finalpersonality = NULL; + char *CertExtractFile = NULL; + char *KeyExtractFile = NULL; - if((family_list = getChildren(configDN))) { + if ((family_list = getChildren(configDN))) { char **family; - char *personality = NULL; char *activation = NULL; char *cipher = NULL; + char *personality = NULL; for (family = family_list; *family; family++) { getConfigEntry( *family, &entry ); if ( entry == NULL ) { - continue; + continue; } activation = slapi_entry_attr_get_charptr( entry, "nssslactivation" ); - if((!activation) || (!PL_strcasecmp(activation, "off"))) { - /* this family was turned off, goto next */ - slapi_ch_free((void **) &activation); - freeConfigEntry( &entry ); - continue; + if ((!activation) || (!PL_strcasecmp(activation, "off"))) { + /* this family was turned off, goto next */ + slapi_ch_free((void **) &activation); + freeConfigEntry( &entry ); + continue; } - - slapi_ch_free((void **) &activation); + slapi_ch_free((void **) &activation); personality = slapi_entry_attr_get_charptr( entry, "nssslpersonalityssl" ); cipher = slapi_entry_attr_get_charptr( entry, "cn" ); - if ( cipher && !PL_strcasecmp(cipher, "RSA" )) { - char *ssltoken; - - /* If there already is a token name, use it */ - if (token) { - slapi_ch_free((void **) &personality); - slapi_ch_free((void **) &cipher); - freeConfigEntry( &entry ); - continue; - } + if ( cipher && !PL_strcasecmp(cipher, "RSA" )) { + char *ssltoken; + + /* If there already is a token name, use it */ + if (token) { + slapi_ch_free_string(&personality); + slapi_ch_free_string(&cipher); + freeConfigEntry( &entry ); + continue; + } - ssltoken = slapi_entry_attr_get_charptr( entry, "nsssltoken" ); - if( ssltoken && personality ) { - if( !PL_strcasecmp(ssltoken, "internal") || - !PL_strcasecmp(ssltoken, "internal (software)") ) { + ssltoken = slapi_entry_attr_get_charptr( entry, "nsssltoken" ); + if( ssltoken && personality ) { + if (!PL_strcasecmp(ssltoken, "internal") || + !PL_strcasecmp(ssltoken, "internal (software)")) { - /* Translate config internal name to more - * readable form. Certificate name is just - * the personality for internal tokens. - */ - token = slapi_ch_strdup(internalTokenName); + /* Translate config internal name to more + * readable form. Certificate name is just + * the personality for internal tokens. + */ + token = slapi_ch_strdup(internalTokenName); #if defined(USE_OPENLDAP) - /* openldap needs tokenname:certnick */ - PR_snprintf(cert_name, sizeof(cert_name), "%s:%s", token, personality); + /* openldap needs tokenname:certnick */ + PR_snprintf(cert_name, sizeof(cert_name), "%s:%s", token, personality); #else - PL_strncpyz(cert_name, personality, sizeof(cert_name)); + PL_strncpyz(cert_name, personality, sizeof(cert_name)); #endif - slapi_ch_free((void **) &ssltoken); - } else { - /* external PKCS #11 token - attach token name */ - /*ssltoken was already dupped and we don't need it anymore*/ - token = ssltoken; - PR_snprintf(cert_name, sizeof(cert_name), "%s:%s", token, personality); - } - } else { - errorCode = PR_GetError(); - slapd_SSL_warn("Security Initialization: Failed to get cipher " - "family information. Missing nsssltoken or" - "nssslpersonalityssl in %s (" - SLAPI_COMPONENT_NAME_NSPR " error %d - %s)", - *family, errorCode, slapd_pr_strerror(errorCode)); - slapi_ch_free((void **) &ssltoken); - slapi_ch_free((void **) &personality); - slapi_ch_free((void **) &cipher); - freeConfigEntry( &entry ); - continue; - } - } else { /* external PKCS #11 cipher */ - char *ssltoken; - - ssltoken = slapi_entry_attr_get_charptr( entry, "nsssltoken" ); - if( token && personality ) { - - /* free the old token and remember the new one */ - if (token) slapi_ch_free((void **)&token); - token = ssltoken; /*ssltoken was already dupped and we don't need it anymore*/ - - /* external PKCS #11 token - attach token name */ - PR_snprintf(cert_name, sizeof(cert_name), "%s:%s", token, personality); - } else { - errorCode = PR_GetError(); - slapd_SSL_warn("Security Initialization: Failed to get cipher " - "family information. Missing nsssltoken or" - "nssslpersonalityssl in %s (" - SLAPI_COMPONENT_NAME_NSPR " error %d - %s)", - *family, errorCode, slapd_pr_strerror(errorCode)); - slapi_ch_free((void **) &ssltoken); - slapi_ch_free((void **) &personality); - slapi_ch_free((void **) &cipher); - freeConfigEntry( &entry ); - continue; - } + slapi_ch_free_string(&ssltoken); + } else { + /* external PKCS #11 token - attach token name */ + token = ssltoken; /*ssltoken was already dupped */ + PR_snprintf(cert_name, sizeof(cert_name), "%s:%s", token, personality); + } + } else { + errorCode = PR_GetError(); + slapd_SSL_warn("Security Initialization: Failed to get cipher " + "family information. Missing nsssltoken or" + "nssslpersonalityssl in %s (" + SLAPI_COMPONENT_NAME_NSPR " error %d - %s)", + *family, errorCode, slapd_pr_strerror(errorCode)); + slapi_ch_free_string(&ssltoken); + slapi_ch_free_string(&personality); + slapi_ch_free_string(&cipher); + freeConfigEntry( &entry ); + continue; + } + } else { /* external PKCS #11 cipher */ + char *ssltoken; + + ssltoken = slapi_entry_attr_get_charptr( entry, "nsssltoken" ); + if( ssltoken && personality ) { - } - slapi_ch_free((void **) &personality); - slapi_ch_free((void **) &cipher); - freeConfigEntry( &entry ); + /* free the old token and remember the new one */ + if (token) slapi_ch_free_string(&token); + token = ssltoken; /*ssltoken was already dupped */ + + /* external PKCS #11 token - attach token name */ + PR_snprintf(cert_name, sizeof(cert_name), "%s:%s", token, personality); + } else { + errorCode = PR_GetError(); + slapd_SSL_warn("Security Initialization: Failed to get cipher " + "family information. Missing nsssltoken or" + "nssslpersonalityssl in %s (" + SLAPI_COMPONENT_NAME_NSPR " error %d - %s)", + * family, errorCode, slapd_pr_strerror(errorCode)); + slapi_ch_free_string(&ssltoken); + slapi_ch_free_string(&personality); + slapi_ch_free_string(&cipher); + freeConfigEntry( &entry ); + continue; + } + } + slapi_ch_free_string(&finalpersonality); + finalpersonality = personality; + slapi_ch_free_string(&cipher); + /* Get ServerCert/KeyExtractFile from given entry if any. */ + slapi_ch_free_string(&CertExtractFile); + CertExtractFile = slapi_entry_attr_get_charptr(entry, "ServerCertExtractFile"); + slapi_ch_free_string(&KeyExtractFile); + KeyExtractFile = slapi_entry_attr_get_charptr(entry, "ServerKeyExtractFile"); + freeConfigEntry( &entry ); } /* end of for */ - freeChildren( family_list ); + freeChildren( family_list ); } /* Free config data */ @@ -2226,15 +2283,69 @@ slapd_SSL_client_auth (LDAP* ld) errorCode, slapd_pr_strerror(errorCode)); } else { #if defined(USE_OPENLDAP) - rc = ldap_set_option(ld, LDAP_OPT_X_TLS_KEYFILE, SERVER_KEY_NAME); - if (rc) { - slapd_SSL_warn("SSL client authentication cannot be used " - "unable to set the key to use to %s", SERVER_KEY_NAME); - } - rc = ldap_set_option(ld, LDAP_OPT_X_TLS_CERTFILE, cert_name); - if (rc) { - slapd_SSL_warn("SSL client authentication cannot be used " - "unable to set the cert to use to %s", cert_name); + if (slapi_client_uses_non_nss(ld)) { + char *certdir = config_get_certdir(); + char *keyfile = NULL; + char *certfile = NULL; + if (KeyExtractFile) { + if ('/' == *KeyExtractFile) { + keyfile = KeyExtractFile; + } else { + keyfile = slapi_ch_smprintf("%s/%s", certdir, KeyExtractFile); + slapi_ch_free_string(&KeyExtractFile); + } + } else { + keyfile = slapi_ch_smprintf("%s/%s-Key%s", certdir, finalpersonality, PEMEXT); + } + if (CertExtractFile) { + if ('/' == *CertExtractFile) { + certfile = CertExtractFile; + } else { + certfile = slapi_ch_smprintf("%s/%s", certdir, CertExtractFile); + slapi_ch_free_string(&CertExtractFile); + } + } else { + certfile = slapi_ch_smprintf("%s/%s%s", certdir, finalpersonality, PEMEXT); + } + slapi_ch_free_string(&certdir); + if (PR_SUCCESS != PR_Access(keyfile, PR_ACCESS_EXISTS)) { + slapi_ch_free_string(&keyfile); + slapd_SSL_warn("SSL key file (%s) for client authentication does not exist. " + "Using %s", keyfile, SERVER_KEY_NAME); + keyfile = slapi_ch_strdup(SERVER_KEY_NAME); + } + rc = ldap_set_option(ld, LDAP_OPT_X_TLS_KEYFILE, keyfile); + if (rc) { + slapd_SSL_warn("SSL client authentication cannot be used " + "unable to set the key to use to %s", keyfile); + } + slapi_ch_free_string(&keyfile); + rc = PR_Access(certfile, PR_ACCESS_EXISTS); + if (rc) { + slapi_ch_free_string(&certfile); + slapd_SSL_warn("SSL cert file (%s) for client authentication does not exist. " + "Using %s", certfile, cert_name); + certfile = cert_name; + } + rc = ldap_set_option(ld, LDAP_OPT_X_TLS_CERTFILE, certfile); + if (rc) { + slapd_SSL_warn("SSL client authentication cannot be used " + "unable to set the cert to use to %s", certfile); + } + if (certfile != cert_name) { + slapi_ch_free_string(&certfile); + } + } else { + rc = ldap_set_option(ld, LDAP_OPT_X_TLS_KEYFILE, SERVER_KEY_NAME); + if (rc) { + slapd_SSL_warn("SSL client authentication cannot be used " + "unable to set the key to use to %s", SERVER_KEY_NAME); + } + rc = ldap_set_option(ld, LDAP_OPT_X_TLS_CERTFILE, cert_name); + if (rc) { + slapd_SSL_warn("SSL client authentication cannot be used " + "unable to set the cert to use to %s", cert_name); + } } /* * not sure what else needs to be done for client auth - don't @@ -2265,6 +2376,7 @@ slapd_SSL_client_auth (LDAP* ld) slapi_ch_free_string(&token); slapi_ch_free_string(&pw); + slapi_ch_free_string(&finalpersonality); LDAPDebug (LDAP_DEBUG_TRACE, "slapd_SSL_client_auth() %i\n", rc, 0, 0); return rc; @@ -2365,9 +2477,10 @@ slapd_get_unlocked_key_for_cert(CERTCertificate *cert, void *pin_arg) slotname, tokenname, certsubject); break; } else { - slapi_log_error(SLAPI_LOG_TRACE, "slapd_get_unlocked_key_for_cert", - "Skipping locked slot [%s] token [%s] for certificate [%s]\n", - slotname, tokenname, certsubject); + PRErrorCode errcode = PR_GetError(); + slapi_log_error(SLAPI_LOG_FATAL, "slapd_get_unlocked_key_for_cert", + "Skipping locked slot [%s] token [%s] for certificate [%s] (%d - %s)\n", + slotname, tokenname, certsubject, errcode, slapd_pr_strerror(errcode)); } } @@ -2391,3 +2504,591 @@ slapd_get_unlocked_key_for_cert(CERTCertificate *cert, void *pin_arg) return key; } +/* + * Functions to extract key and cert from the NSS cert db. + */ +#include +#include +#include +#include +#include +#define DONOTEDIT "This file is auto-generated by 389-ds-base.\nDo not edit directly.\n" +#define NS_CERT_HEADER "-----BEGIN CERTIFICATE-----" +#define NS_CERT_TRAILER "-----END CERTIFICATE-----" +#define KEY_HEADER "-----BEGIN PRIVATE KEY-----" +#define KEY_TRAILER "-----END PRIVATE KEY-----" +#define ENCRYPTED_KEY_HEADER "-----BEGIN ENCRYPTED PRIVATE KEY-----" +#define ENCRYPTED_KEY_TRAILER "-----END ENCRYPTED PRIVATE KEY-----" + +typedef struct { + enum { + PW_NONE = 0, + PW_FROMFILE = 1, + PW_PLAINTEXT = 2, + PW_EXTERNAL = 3 + } source; + char *data; +} secuPWData; + +static SECStatus +listCerts(CERTCertDBHandle *handle, CERTCertificate *cert, PK11SlotInfo *slot, + PRFileDesc *outfile, void *pwarg) +{ + SECItem data; + SECStatus rv = SECFailure; + CERTCertList *certs; + CERTCertListNode *node; + CERTCertificate *the_cert = NULL; + char *name = NULL; + + if (!cert) { + slapi_log_error(SLAPI_LOG_FATAL, "listCerts", "No cert given\n"); + return rv; + } + name = cert->nickname; + + if (!name) { + slapi_log_error(SLAPI_LOG_FATAL, "listCerts", "No cert nickname\n"); + return rv; + } + the_cert = CERT_FindCertByNicknameOrEmailAddr(handle, name); + if (!the_cert) { + slapi_log_error(SLAPI_LOG_FATAL, "listCerts", "Could not find cert: %s\n", name); + return SECFailure; + } + + PR_fprintf(outfile, "%s\n", DONOTEDIT); + /* Here, we have one cert with the desired nickname or email + * address. Now, we will attempt to get a list of ALL certs + * with the same subject name as the cert we have. That list + * should contain, at a minimum, the one cert we have already found. + * If the list of certs is empty (NULL), the libraries have failed. + */ + certs = CERT_CreateSubjectCertList(NULL, handle, &the_cert->derSubject, + PR_Now(), PR_FALSE); + CERT_DestroyCertificate(the_cert); + if (!certs) { + slapi_log_error(SLAPI_LOG_FATAL, "listCerts", "problem printing certificates"); + return SECFailure; + } + for (node = CERT_LIST_HEAD(certs); !CERT_LIST_END(node,certs); node = CERT_LIST_NEXT(node)) { + the_cert = node->cert; + PR_fprintf(outfile, "Issuer: %s\n", the_cert->issuerName); + PR_fprintf(outfile, "Subject: %s\n", the_cert->subjectName); + /* now get the subjectList that matches this cert */ + data.data = the_cert->derCert.data; + data.len = the_cert->derCert.len; + PR_fprintf(outfile, "\n%s\n%s\n%s\n", NS_CERT_HEADER, + BTOA_DataToAscii(data.data, data.len), NS_CERT_TRAILER); + rv = SECSuccess; + } + if (certs) { + CERT_DestroyCertList(certs); + } + if (rv) { + slapi_log_error(SLAPI_LOG_FATAL, "listCerts", "problem printing certificate nicknames"); + return SECFailure; + } + + return rv; +} + +static char * +gen_pem_path(char *filename) +{ + char *pem = NULL; + char *pempath = NULL; + char *dname = NULL; + char *bname = NULL; + char *certdir = config_get_certdir(); + + if (!filename) { + goto bail; + } + pem = PL_strstr(filename, PEMEXT); + if (pem) { + *pem = '\0'; + } + bname = basename(filename); + dname = dirname(filename); + if (!PL_strcmp(dname, ".")) { + /* just a file name */ + pempath = slapi_ch_smprintf("%s/%s%s", certdir, bname, PEMEXT); + } else if (*dname == '/') { + /* full path */ + pempath = slapi_ch_smprintf("%s/%s%s", dname, bname, PEMEXT); + } else { + /* relative path */ + pempath = slapi_ch_smprintf("%s/%s/%s%s", certdir, dname, bname, PEMEXT); + } +bail: + return pempath; +} + +static int +slapd_extract_cert(Slapi_Entry *entry, int isCA) +{ + CERTCertDBHandle *certHandle; + char *certdir = config_get_certdir(); + CERTCertListNode *node; + CERTCertList *list = PK11_ListCerts(PK11CertListAll, NULL); + PRFileDesc *outFile = NULL; + SECStatus rv = SECFailure; + char *CertExtractFile = NULL; + char *certfile = NULL; + char *personality = NULL; + + if (!entry) { + slapi_log_error(SLAPI_LOG_FATAL, "slapd_extract_cert", + "No entry is given for %s Cert.\n", isCA?"CA":"Server"); + goto bail; + } + + /* Get CertExtractFile from given entry if any. */ + if (isCA) { + CertExtractFile = slapi_entry_attr_get_charptr(entry, "CACertExtractFile"); + } else { + CertExtractFile = slapi_entry_attr_get_charptr(entry, "ServerCertExtractFile"); + personality = slapi_entry_attr_get_charptr(entry, "nsSSLPersonalitySSL" ); + } + certfile = gen_pem_path(CertExtractFile); + if (isCA) { + slapi_ch_free_string(&CACertPemFile); + CACertPemFile = certfile; + } + + certHandle = CERT_GetDefaultCertDB(); + for (node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list); + node = CERT_LIST_NEXT(node)) { + CERTCertificate *cert = node->cert; + CERTCertTrust trust; + switch (isCA) { + case PR_TRUE: + if ((CERT_GetCertTrust(cert, &trust) == SECSuccess) && + (trust.sslFlags & (CERTDB_VALID_CA|CERTDB_TRUSTED_CA|CERTDB_TRUSTED_CLIENT_CA))) { + /* default token "internal" */ + PK11SlotInfo *slot = slapd_pk11_getInternalKeySlot(); + slapi_log_error(SLAPI_LOG_FATAL, "slapd_extract_cert", "CA CERT NAME: %s\n", cert->nickname); + if (!certfile) { + certfile = slapi_ch_smprintf("%s/%s%s", certdir, escape_string_for_filename(cert->nickname), PEMEXT); + entrySetValue(slapi_entry_get_sdn(entry), "CACertExtractFile", certfile); + slapi_set_cacertfile(certfile); + } + if (!outFile) { + outFile = PR_Open(certfile, PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE, 00660); + } + if (!outFile) { + slapi_log_error(SLAPI_LOG_FATAL, "slapd_extract_cert", + "Unable to open \"%s\" for writing (%d, %d).\n", + certfile, PR_GetError(), PR_GetOSError()); + goto bail; + } + rv = listCerts(certHandle, cert, slot, outFile, NULL); + if (rv) { + slapi_log_error(SLAPI_LOG_FATAL, "slapd_extract_cert", "listCerts failed\n"); + break; + } + } + break; + default: + if (!PL_strcmp(cert->nickname, personality)) { + PK11SlotInfo *slot = slapd_pk11_getInternalKeySlot(); + slapi_log_error(SLAPI_LOG_FATAL, "slapd_extract_cert", "SERVER CERT NAME: %s\n", cert->nickname); + if (!certfile) { + certfile = slapi_ch_smprintf("%s/%s%s", certdir, escape_string_for_filename(cert->nickname), PEMEXT); + } + if (!outFile) { + outFile = PR_Open(certfile, PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE, 00660); + } + if (!outFile) { + slapi_log_error(SLAPI_LOG_FATAL, "slapd_extract_cert", + "Unable to open \"%s\" for writing (%d, %d).\n", + certfile, PR_GetError(), PR_GetOSError()); + goto bail; + } + rv = listCerts(certHandle, cert, slot, outFile, NULL); + if (rv) { + slapi_log_error(SLAPI_LOG_FATAL, "slapd_extract_cert", "listCerts failed\n"); + } + PR_Close(outFile); + outFile = NULL; + break; /* One cert per one pem file. */ + } + break; + } + } + rv = SECSuccess; +bail: + CERT_DestroyCertList(list); + slapi_ch_free_string(&CertExtractFile); + if (CACertPemFile != certfile) { + slapi_ch_free_string(&certfile); + } + slapi_ch_free_string(&personality); + if (outFile) { + PR_Close(outFile); + } + return rv; +} + +/* + * Borrowed from keyutil.c (crypto-util) + * + * Extract the public and private keys and the subject + * distinguished from the cert with the given nickname + * in the given slot. + * + * @param nickname the certificate nickname + * @param slot the slot where keys it was loaded + * @param pwdat module authentication password + * @param privkey private key out + * @param pubkey public key out + * @param subject subject out + */ +static SECStatus +extractRSAKeysAndSubject( + const char *nickname, + PK11SlotInfo *slot, + secuPWData *pwdata, + SECKEYPrivateKey **privkey, + SECKEYPublicKey **pubkey, + CERTName **subject) +{ + PRErrorCode rv = SECFailure; + CERTCertificate *cert = PK11_FindCertFromNickname((char *)nickname, NULL); + if (!cert) { + rv = PR_GetError(); + slapi_log_error(SLAPI_LOG_FATAL, "extractRSAKeysAndSubject", + "Failed extract cert with %s, (%d-%s, %d).\n", + nickname, rv, slapd_pr_strerror(rv), PR_GetOSError()); + goto bail; + } + + *pubkey = CERT_ExtractPublicKey(cert); + if (!*pubkey) { + rv = PR_GetError(); + slapi_log_error(SLAPI_LOG_FATAL, "extractRSAKeysAndSubject", + "Could not get public key from cert for %s, (%d-%s, %d)\n", + nickname, rv, slapd_pr_strerror(rv), PR_GetOSError()); + goto bail; + } + + *privkey = PK11_FindKeyByDERCert(slot, cert, pwdata); + if (!*privkey) { + rv = PR_GetError(); + slapi_log_error(SLAPI_LOG_FATAL, "extractRSAKeysAndSubject", + "Unable to find the key with PK11_FindKeyByDERCert for %s, (%d-%s, %d)\n", + nickname, rv, slapd_pr_strerror(rv), PR_GetOSError()); + *privkey= PK11_FindKeyByAnyCert(cert, &pwdata); + if (!*privkey) { + rv = PR_GetError(); + slapi_log_error(SLAPI_LOG_FATAL, "extractRSAKeysAndSubject", + "Unable to find the key with PK11_FindKeyByAnyCert for %s, (%d-%s, %d)\n", + nickname, rv, slapd_pr_strerror(rv), PR_GetOSError()); + goto bail; + } + } + + PR_ASSERT(((*privkey)->keyType) == rsaKey); + *subject = CERT_AsciiToName(cert->subjectName); + + if (!*subject) { + slapi_log_error(SLAPI_LOG_FATAL, "extractRSAKeysAndSubject", + "Improperly formatted name: \"%s\"\n", + cert->subjectName); + goto bail; + } + rv = SECSuccess; +bail: + if (cert) + CERT_DestroyCertificate(cert); + return rv; +} + +/* + * Decrypt the private key + */ +SECStatus DecryptKey( + SECKEYEncryptedPrivateKeyInfo *epki, + SECOidTag algTag, + SECItem *pwitem, + secuPWData *pwdata, + SECItem *derPKI) +{ + SECItem *cryptoParam = NULL; + PK11SymKey *symKey = NULL; + PK11Context *ctx = NULL; + SECStatus rv = SECFailure; + + if (!pwitem) { + return rv; + } + + do { + SECAlgorithmID algid = epki->algorithm; + CK_MECHANISM_TYPE cryptoMechType; + CK_ATTRIBUTE_TYPE operation = CKA_DECRYPT; + PK11SlotInfo *slot = NULL; + + cryptoMechType = PK11_GetPBECryptoMechanism(&algid, &cryptoParam, pwitem); + if (cryptoMechType == CKM_INVALID_MECHANISM) { + break; + } + + slot = PK11_GetBestSlot(cryptoMechType, NULL); + if (!slot) { + break; + } + + symKey = PK11_PBEKeyGen(slot, &algid, pwitem, PR_FALSE, pwdata); + if (symKey == NULL) { + break; + } + + ctx = PK11_CreateContextBySymKey(cryptoMechType, operation, symKey, cryptoParam); + if (ctx == NULL) { + break; + } + + rv = PK11_CipherOp(ctx, + derPKI->data, /* out */ + (int *)(&derPKI->len), /* out len */ + (int)epki->encryptedData.len, /* max out */ + epki->encryptedData.data, /* in */ + (int)epki->encryptedData.len); /* in len */ + + PR_ASSERT(derPKI->len == epki->encryptedData.len); + PR_ASSERT(rv == SECSuccess); + rv = PK11_Finalize(ctx); + PR_ASSERT(rv == SECSuccess); + + } while (0); + + /* cleanup */ + if (symKey) { + PK11_FreeSymKey(symKey); + } + if (cryptoParam) { + SECITEM_ZfreeItem(cryptoParam, PR_TRUE); + cryptoParam = NULL; + } + if (ctx) { + PK11_DestroyContext(ctx, PR_TRUE); + } + + return rv; + +} + +/* #define ENCRYPTEDKEY 1 */ +#define RAND_PASS_LEN 32 +static int +slapd_extract_key(Slapi_Entry *entry, char *token, PK11SlotInfo *slot) +{ + char *KeyExtractFile = NULL; + char *personality = NULL; + char *keyfile = NULL; + unsigned char randomPassword[RAND_PASS_LEN] = {0}; + SECStatus rv = SECFailure; + SECItem pwitem = { 0, NULL, 0 }; + SECItem clearKeyDER = { 0, NULL, 0 }; + PRFileDesc *outFile = NULL; + SECKEYEncryptedPrivateKeyInfo *epki = NULL; + SECKEYPrivateKey *privkey = NULL; + SECKEYPublicKey *pubkey = NULL; + secuPWData pwdata = { PW_NONE, 0 }; + CERTName *subject = NULL; + PLArenaPool *arenaForPKI = NULL; + char *b64 = NULL; + PRUint32 total = 0; + PRUint32 numBytes = 0; + char *certdir = config_get_certdir(); +#if defined(ENCRYPTEDKEY) + char *keyEncPwd = NULL; + SVRCOREError err = SVRCORE_Success; + PRArenaPool *arenaForEPKI = NULL; + SVRCOREStdPinObj *StdPinObj; + SECItem *encryptedKeyDER = NULL; +#endif + + if (!entry) { + slapi_log_error(SLAPI_LOG_FATAL, "slapd_extract_key", + "No entry is given for Server Key.\n"); + goto bail; + } +#if defined(ENCRYPTEDKEY) + if (!token) { + slapi_log_error(SLAPI_LOG_FATAL, "slapd_extract_key", + "No token is given.\n"); + goto bail; + } + StdPinObj = (SVRCOREStdPinObj *)SVRCORE_GetRegisteredPinObj(); + if (!StdPinObj) { + slapi_log_error(SLAPI_LOG_FATAL, "slapd_extract_key", + "No entry is given for Server Key.\n"); + goto bail; + } + err = SVRCORE_StdPinGetPin(&keyEncPwd, StdPinObj, token); + if (err || !keyEncPwd) { + slapi_log_error(SLAPI_LOG_FATAL, "slapd_extract_key", + "Failed to extract pw with token %s.\n", token); + goto bail; + } + pwitem.data = (unsigned char *)keyEncPwd; + pwitem.len = (unsigned int)strlen(keyEncPwd); + pwitem.type = siBuffer; +#else + /* Caller wants clear keys. Make up a dummy + * password to get NSS to export an encrypted + * key which we will decrypt. + */ + rv = PK11_GenerateRandom(randomPassword, sizeof((const char *)randomPassword) - 1); + if (rv != SECSuccess) { + slapi_log_error(SLAPI_LOG_FATAL, "slapd_extract_key", "Failed to generate random.\n"); + goto bail; + } + pwitem.data = randomPassword; + pwitem.len = strlen((const char *)randomPassword); + pwitem.type = siBuffer; +#endif + + /* Get ServerKeyExtractFile from given entry if any. */ + KeyExtractFile = slapi_entry_attr_get_charptr(entry, "ServerKeyExtractFile"); + personality = slapi_entry_attr_get_charptr(entry, "nsSSLPersonalitySSL" ); + if (!personality) { + slapi_log_error(SLAPI_LOG_FATAL, "slapd_extract_key", + "nsSSLPersonalitySSL value not found.\n"); + goto bail; + } + keyfile = gen_pem_path(KeyExtractFile); + if (!keyfile) { + keyfile = slapi_ch_smprintf("%s/%s-Key%s", certdir, escape_string_for_filename(personality), PEMEXT); + } + outFile = PR_Open(keyfile, PR_CREATE_FILE | PR_RDWR | PR_TRUNCATE, 00660); + if (!outFile) { + slapi_log_error(SLAPI_LOG_FATAL, "slapd_extract_key", + "Unable to open \"%s\" for writing (%d, %d).\n", + keyfile, PR_GetError(), PR_GetOSError()); + goto bail; + } + rv = extractRSAKeysAndSubject(personality, slot, &pwdata, &privkey, &pubkey, &subject); + if (rv != SECSuccess) { +#if defined(ENCRYPTEDKEY) + slapi_log_error(SLAPI_LOG_FATAL, "slapd_extract_key", + "Failed to extract keys for \"%s\".\n", token); +#else + slapi_log_error(SLAPI_LOG_FATAL, "slapd_extract_key", "Failed to extract keys for %s.\n", personality); +#endif + goto bail; + } + + /* + * Borrowed the code from KeyOut in keyutil.c (crypto-util). + * Is it ok to hardcode the algorithm SEC_OID_DES_EDE3_CBC??? + */ + epki = PK11_ExportEncryptedPrivKeyInfo(NULL, SEC_OID_DES_EDE3_CBC, &pwitem, privkey, 1000, &pwdata); + if (!epki) { + slapi_log_error(SLAPI_LOG_FATAL, "slapd_extract_key", + "Unable to export encrypted private key (%d, %d).\n", + PR_GetError(), PR_GetOSError()); + goto bail; + } +#if defined(ENCRYPTEDKEY) + arenaForEPKI = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + /* NULL dest to let it allocate memory for us */ + encryptedKeyDER = SEC_ASN1EncodeItem(arenaForEPKI, NULL, epki, SECKEY_EncryptedPrivateKeyInfoTemplate); + if (!encryptedKeyDER) { + slapi_log_error(SLAPI_LOG_FATAL, "slapd_extract_key", + "SEC_ASN1EncodeItem failed. (%d, %d).\n", PR_GetError(), PR_GetOSError()); + goto bail; + } +#else + /* Make a decrypted key the one to write out. */ + arenaForPKI = PORT_NewArena(2048); + if (!arenaForPKI) { + slapi_log_error(SLAPI_LOG_FATAL, "slapd_extract_key", + "PORT_NewArena failed. (%d, %d).\n", PR_GetError(), PR_GetOSError()); + goto bail; + } + clearKeyDER.data = PORT_ArenaAlloc(arenaForPKI, epki->encryptedData.len); + clearKeyDER.len = epki->encryptedData.len; + clearKeyDER.type = siBuffer; + + rv = DecryptKey(epki, SEC_OID_DES_EDE3_CBC, &pwitem, &pwdata, &clearKeyDER); + if (rv != SECSuccess) { + slapi_log_error(SLAPI_LOG_FATAL, "slapd_extract_key", + "DekryptKey failed. (%d, %d).\n", PR_GetError(), PR_GetOSError()); + goto bail; + } +#endif + + /* we could be exporting a clear or encrypted key */ +#if defined(ENCRYPTEDKEY) + b64 = BTOA_ConvertItemToAscii(encryptedKeyDER); +#else + b64 = BTOA_ConvertItemToAscii(&clearKeyDER); +#endif + if (!b64) { + slapi_log_error(SLAPI_LOG_FATAL, "slapd_extract_key", + "Failed to conver to the ASCII (%d, %d).\n", + PR_GetError(), PR_GetOSError()); + goto bail; + } + + total = PL_strlen(b64); + PR_fprintf(outFile, "%s\n", DONOTEDIT); +#if defined(ENCRYPTEDKEY) + PR_fprintf(outFile, "%s\n", ENCRYPTED_KEY_HEADER); +#else + PR_fprintf(outFile, "%s\n", KEY_HEADER); +#endif + numBytes = PR_Write(outFile, b64, total); + if (numBytes != total) { + slapi_log_error(SLAPI_LOG_FATAL, "slapd_extract_key", + "Failed to write to the file (%d, %d).\n", + PR_GetError(), PR_GetOSError()); + goto bail; + } +#if defined(ENCRYPTEDKEY) + PR_fprintf(outFile, "\n%s\n", ENCRYPTED_KEY_TRAILER); +#else + PR_fprintf(outFile, "\n%s\n", KEY_TRAILER); +#endif + rv = SECSuccess; +bail: + slapi_ch_free_string(&certdir); + slapi_ch_free_string(&KeyExtractFile); + slapi_ch_free_string(&keyfile); + if (outFile) { + PR_Close(outFile); + } +#if defined(ENCRYPTEDKEY) + if (arenaForEPKI) { + PORT_FreeArena(arenaForEPKI, PR_FALSE); + } + if (pwitem.data) { + memset(pwitem.data, 0, pwitem.len); + PORT_Free(pwitem.data); + } + memset(&pwitem, 0, sizeof(SECItem)); +#else + if (arenaForPKI) { + PORT_FreeArena(arenaForPKI, PR_FALSE); + } + memset(randomPassword, 0, strlen((const char *)randomPassword)); +#endif + return rv; +} + +const char * +slapi_get_cacertfile() +{ + return CACertPemFile; +} + +void +slapi_set_cacertfile(char *certfile) +{ + slapi_ch_free_string(&CACertPemFile); + CACertPemFile = certfile; +} diff --git a/ldap/servers/slapd/util.c b/ldap/servers/slapd/util.c index 41e213e..b9d2ea5 100644 --- a/ldap/servers/slapd/util.c +++ b/ldap/servers/slapd/util.c @@ -55,8 +55,14 @@ #include #endif - - +static int special_filename(unsigned char c) +{ + if ((c < 45) || (c == '/') || ((c > 57) && (c < 65)) || + ((c > 90) && (c < 95)) || (c == 96) ||(c > 122) ) { + return UTIL_ESCAPE_HEX; + } + return UTIL_ESCAPE_NONE; +} static int special_np(unsigned char c) { @@ -118,12 +124,16 @@ special_attr_char(unsigned char c) c == '"'); } +/* No '\\' */ +#define DOESCAPE_FLAGS_HEX_NOESC 0x1 + static const char* do_escape_string ( const char* str, int len, /* -1 means str is nul-terminated */ char buf[BUFSIZ], - int (*special)(unsigned char) + int (*special)(unsigned char), + int flags ) { const char* s; @@ -140,54 +150,56 @@ do_escape_string ( last = str + len - 1; for (s = str; s <= last; ++s) { - if ( (esc = (*special)((unsigned char)*s))) { - const char* first = str; - char* bufNext = buf; - int bufSpace = BUFSIZ - 4; - while (1) { - if (bufSpace < (s - first)) s = first + bufSpace - 1; - if (s > first) { - memcpy (bufNext, first, s - first); - bufNext += (s - first); - bufSpace -= (s - first); - } - if (s > last) { - break; - } - do { - if (esc == UTIL_ESCAPE_BACKSLASH) { - /* *s is '\\' */ - /* If *(s+1) and *(s+2) are both hex digits, - * the char is already escaped. */ - if (isxdigit(*(s+1)) && isxdigit(*(s+2))) { - memcpy(bufNext, s, 3); - bufNext += 3; - bufSpace -= 3; - s += 2; - } else { - *bufNext++ = *s; --bufSpace; - } - } else { /* UTIL_ESCAPE_HEX */ - *bufNext++ = '\\'; --bufSpace; - if (bufSpace < 3) { - memcpy(bufNext, "..", 2); - bufNext += 2; - goto bail; - } - PR_snprintf(bufNext, 3, "%02x", *(unsigned char*)s); - bufNext += 2; bufSpace -= 2; - } - } while (++s <= last && + if ( (esc = (*special)((unsigned char)*s))) { + const char* first = str; + char* bufNext = buf; + int bufSpace = BUFSIZ - 4; + while (1) { + if (bufSpace < (s - first)) s = first + bufSpace - 1; + if (s > first) { + memcpy (bufNext, first, s - first); + bufNext += (s - first); + bufSpace -= (s - first); + } + if (s > last) { + break; + } + do { + if (esc == UTIL_ESCAPE_BACKSLASH) { + /* *s is '\\' */ + /* If *(s+1) and *(s+2) are both hex digits, + * the char is already escaped. */ + if (isxdigit(*(s+1)) && isxdigit(*(s+2))) { + memcpy(bufNext, s, 3); + bufNext += 3; + bufSpace -= 3; + s += 2; + } else { + *bufNext++ = *s; --bufSpace; + } + } else { /* UTIL_ESCAPE_HEX */ + if (!(flags & DOESCAPE_FLAGS_HEX_NOESC)) { + *bufNext++ = '\\'; --bufSpace; + } + if (bufSpace < 3) { + memcpy(bufNext, "..", 2); + bufNext += 2; + goto bail; + } + PR_snprintf(bufNext, 3, "%02x", *(unsigned char*)s); + bufNext += 2; bufSpace -= 2; + } + } while (++s <= last && (esc = (*special)((unsigned char)*s))); - if (s > last) break; - first = s; - while ( (esc = (*special)((unsigned char)*s)) == UTIL_ESCAPE_NONE && s <= last) ++s; - } - bail: - *bufNext = '\0'; - return buf; - } - } + if (s > last) break; + first = s; + while ( (esc = (*special)((unsigned char)*s)) == UTIL_ESCAPE_NONE && s <= last) ++s; + } +bail: + *bufNext = '\0'; + return buf; + } + } /* for */ return str; } @@ -204,13 +216,20 @@ do_escape_string ( const char* escape_string (const char* str, char buf[BUFSIZ]) { - return do_escape_string(str,-1,buf,special_np); + return do_escape_string(str,-1,buf,special_np, 0); } const char* escape_string_with_punctuation(const char* str, char buf[BUFSIZ]) { - return do_escape_string(str,-1,buf,special_np_and_punct); + return do_escape_string(str,-1,buf,special_np_and_punct, 0); +} + +const char* +escape_string_for_filename(const char *str) +{ + char buf[BUFSIZ]; + return do_escape_string(str,-1,buf,special_filename, DOESCAPE_FLAGS_HEX_NOESC); } #define ESCAPE_FILTER 1 -- 2.4.3