summaryrefslogtreecommitdiffstats
path: root/ldap/servers/slapd/start_tls_extop.c
diff options
context:
space:
mode:
authorcvsadm <cvsadm>2005-01-21 00:44:34 +0000
committercvsadm <cvsadm>2005-01-21 00:44:34 +0000
commitb2093e3016027d6b5cf06b3f91f30769bfc099e2 (patch)
treecf58939393a9032182c4fbc4441164a9456e82f8 /ldap/servers/slapd/start_tls_extop.c
downloadds-b2093e3016027d6b5cf06b3f91f30769bfc099e2.tar.gz
ds-b2093e3016027d6b5cf06b3f91f30769bfc099e2.tar.xz
ds-b2093e3016027d6b5cf06b3f91f30769bfc099e2.zip
Moving NSCP Directory Server from DirectoryBranch to TRUNK, initial drop. (foxworth)ldapserver7x
Diffstat (limited to 'ldap/servers/slapd/start_tls_extop.c')
-rw-r--r--ldap/servers/slapd/start_tls_extop.c479
1 files changed, 479 insertions, 0 deletions
diff --git a/ldap/servers/slapd/start_tls_extop.c b/ldap/servers/slapd/start_tls_extop.c
new file mode 100644
index 00000000..523805f0
--- /dev/null
+++ b/ldap/servers/slapd/start_tls_extop.c
@@ -0,0 +1,479 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+/*
+ * Start TLS - LDAP Extended Operation.
+ *
+ *
+ * This plugin implements the "Start TLS (Transport Layer Security)"
+ * extended operation for LDAP. The plugin function is called by
+ * the server if an LDAP client request contains the OID:
+ * "1.3.6.1.4.1.1466.20037".
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <private/pprio.h>
+
+
+#include <prio.h>
+#include <ssl.h>
+#include "slap.h"
+#include "slapi-plugin.h"
+#include "fe.h"
+
+
+
+
+/* OID of the extended operation handled by this plug-in */
+/* #define START_TLS_OID "1.3.6.1.4.1.1466.20037" */
+
+
+Slapi_PluginDesc exopdesc = { "start_tls_plugin", "Netscape", "0.1",
+ "Start TLS extended operation plugin" };
+
+
+
+
+/* Start TLS Extended operation plugin function */
+int
+start_tls( Slapi_PBlock *pb )
+{
+
+ char *oid;
+ Connection *conn;
+ PRFileDesc *oldsocket, *newsocket;
+ int secure;
+ int ns, oldnativesocket;
+ int rv;
+
+ /* Get the pb ready for sending Start TLS Extended Responses back to the client.
+ * The only requirement is to set the LDAP OID of the extended response to the START_TLS_OID. */
+
+ if ( slapi_pblock_set( pb, SLAPI_EXT_OP_RET_OID, START_TLS_OID ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Could not set extended response oid.\n" );
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "Could not set extended response oid.", 0, NULL );
+ return( SLAPI_PLUGIN_EXTENDED_SENT_RESULT );
+ }
+
+
+ /* Before going any further, we'll make sure that the right extended operation plugin
+ * has been called: i.e., the OID shipped whithin the extended operation request must
+ * match this very plugin's OID: START_TLS_OID. */
+
+ if ( slapi_pblock_get( pb, SLAPI_EXT_OP_REQ_OID, &oid ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Could not get OID value from request.\n" );
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "Could not get OID value from request.", 0, NULL );
+ return( SLAPI_PLUGIN_EXTENDED_SENT_RESULT );
+ } else {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Received extended operation request with OID %s\n", oid );
+ }
+
+ if ( strcasecmp( oid, START_TLS_OID ) != 0) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Request OID does not match Start TLS OID.\n" );
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "Request OID does not match Start TLS OID.", 0, NULL );
+ return( SLAPI_PLUGIN_EXTENDED_SENT_RESULT );
+ } else {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Start TLS extended operation request confirmed.\n" );
+ }
+
+
+ /* At least we know that the request was indeed an Start TLS one. */
+
+ conn = pb->pb_conn;
+ PR_Lock( conn->c_mutex );
+#ifndef _WIN32
+ oldsocket = conn->c_prfd;
+ if ( oldsocket == (PRFileDesc *) NULL ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Connection socket not available.\n" );
+ slapi_send_ldap_result( pb, LDAP_UNAVAILABLE, NULL,
+ "Connection socket not available.", 0, NULL );
+ goto unlock_and_return;
+ }
+#else
+ oldnativesocket = conn->c_sd;
+ oldsocket = PR_ImportTCPSocket(oldnativesocket);
+ if ( oldsocket == (PRFileDesc *) NULL ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Failed to import NT native socket into NSPR.\n" );
+ slapi_send_ldap_result( pb, LDAP_UNAVAILABLE, NULL,
+ "Failed to import NT native socket into NSPR.", 0, NULL );
+ goto unlock_and_return;
+ }
+#endif
+
+ /* Check whether the Start TLS request can be accepted. */
+
+ if ( connection_operations_pending( conn, pb->pb_op,
+ 1 /* check for ops where result not yet sent */ )) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Other operations are still pending on the connection.\n" );
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "Other operations are still pending on the connection.", 0, NULL );
+ goto unlock_and_return;
+ }
+
+
+ if ( !config_get_security() ) {
+ /* if any, here is where the referral to another SSL supporting server should be done: */
+ /* slapi_send_ldap_result( pb, LDAP_REFERRAL, NULL, msg, 0, url ); */
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "SSL not supported by this server.\n" );
+ slapi_send_ldap_result( pb, LDAP_PROTOCOL_ERROR, NULL,
+ "SSL not supported by this server.", 0, NULL );
+ goto unlock_and_return;
+ }
+
+
+ if ( conn->c_flags & CONN_FLAG_SSL ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "SSL connection already established.\n" );
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "SSL connection already established.", 0, NULL );
+ goto unlock_and_return;
+ }
+
+ if ( conn->c_flags & CONN_FLAG_SASL_CONTINUE ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "SASL multi-stage bind in progress.\n" );
+ slapi_send_ldap_result( pb, LDAP_OPERATIONS_ERROR, NULL,
+ "SASL multi-stage bind in progress.", 0, NULL );
+ goto unlock_and_return;
+ }
+
+
+ if ( conn->c_flags & CONN_FLAG_CLOSING ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Connection being closed at this moment.\n" );
+ slapi_send_ldap_result( pb, LDAP_UNAVAILABLE, NULL,
+ "Connection being closed at this moment.", 0, NULL );
+ goto unlock_and_return;
+ }
+
+
+
+ /* At first sight, there doesn't seem to be any major impediment to start TLS.
+ * So, we may as well try initialising SSL. */
+
+ if ( slapd_security_library_is_initialized() == 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "NSS libraries not initialised.\n" );
+ slapi_send_ldap_result( pb, LDAP_UNAVAILABLE, NULL,
+ "NSS libraries not initialised.", 0, NULL );
+ goto unlock_and_return;
+ }
+
+
+
+ /* Since no specific argument for denying the Start TLS request has been found,
+ * we send a success response back to the client. */
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Start TLS request accepted.Server willing to negotiate SSL.\n" );
+ slapi_send_ldap_result( pb, LDAP_SUCCESS, NULL,
+ "Start TLS request accepted.Server willing to negotiate SSL.", 0, NULL );
+
+
+ /* So far we have set up the environment for deploying SSL. It's now time to import the socket
+ * into SSL and to configure it consequently. */
+
+ if ( slapd_ssl_listener_is_initialized() != 0 ) {
+ PRFileDesc * ssl_listensocket;
+
+ ssl_listensocket = get_ssl_listener_fd();
+ if ( ssl_listensocket == (PRFileDesc *) NULL ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "SSL listener socket not found.\n" );
+ slapi_send_ldap_result( pb, LDAP_UNAVAILABLE, NULL,
+ "SSL listener socket not found.", 0, NULL );
+ goto unlock_and_return;
+ }
+ newsocket = slapd_ssl_importFD( ssl_listensocket, oldsocket );
+ if ( newsocket == (PRFileDesc *) NULL ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "SSL socket import failed.\n" );
+ slapi_send_ldap_result( pb, LDAP_UNAVAILABLE, NULL,
+ "SSL socket import failed.", 0, NULL );
+ goto unlock_and_return;
+ }
+ } else {
+ if ( slapd_ssl_init2( &oldsocket, 1 ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "SSL socket import or configuration failed.\n" );
+ slapi_send_ldap_result( pb, LDAP_UNAVAILABLE, NULL,
+ "SSL socket import or configuration failed.", 0, NULL );
+ goto unlock_and_return;
+ }
+ newsocket = oldsocket;
+ }
+
+
+ rv = slapd_ssl_resetHandshake( newsocket, 1 );
+ if ( rv != SECSuccess ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Unable to set socket ready for SSL handshake.\n" );
+ slapi_send_ldap_result( pb, LDAP_UNAVAILABLE, NULL,
+ "Unable to set socket ready for SSL handshake.", 0, NULL );
+ goto unlock_and_return;
+ }
+
+
+
+ /* From here on, messages will be sent through the SSL layer, so we need to get our
+ * connection ready. */
+
+ secure = 1;
+ ns = configure_pr_socket( &newsocket, secure );
+
+ /*
+ ber_sockbuf_set_option( conn->c_sb, LBER_SOCKBUF_OPT_DESC, &newsocket );
+ ber_sockbuf_set_option( conn->c_sb, LBER_SOCKBUF_OPT_READ_FN, (void *)secure_read_function );
+ ber_sockbuf_set_option( conn->c_sb, LBER_SOCKBUF_OPT_WRITE_FN, (void *)secure_write_function );
+ */
+
+ /*changed to */
+ {
+ struct lber_x_ext_io_fns *func_pointers = malloc(LBER_X_EXTIO_FNS_SIZE);
+ func_pointers->lbextiofn_size = LBER_X_EXTIO_FNS_SIZE;
+ func_pointers->lbextiofn_read = secure_read_function;
+ func_pointers->lbextiofn_write = secure_write_function;
+ func_pointers->lbextiofn_writev = NULL;
+ func_pointers->lbextiofn_socket_arg = (struct lextiof_socket_private *) newsocket;
+ ber_sockbuf_set_option( conn->c_sb,
+ LBER_SOCKBUF_OPT_EXT_IO_FNS, func_pointers);
+ free(func_pointers);
+ }
+ conn->c_flags |= CONN_FLAG_SSL;
+ conn->c_flags |= CONN_FLAG_START_TLS;
+ conn->c_sd = ns;
+ conn->c_prfd = newsocket;
+
+
+ rv = slapd_ssl_handshakeCallback (conn->c_prfd, (void *)handle_handshake_done, conn);
+
+ if ( rv < 0 ) {
+ PRErrorCode prerr = PR_GetError();
+ slapi_log_error( SLAPI_LOG_FATAL, "start_tls",
+ "SSL_HandshakeCallback() %d " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ rv, prerr, slapd_pr_strerror( prerr ) );
+ }
+
+ if ( config_get_SSLclientAuth() != SLAPD_SSLCLIENTAUTH_OFF ) {
+ rv = slapd_ssl_badCertHook (conn->c_prfd, (void *)handle_bad_certificate, conn);
+ if ( rv < 0 ) {
+ PRErrorCode prerr = PR_GetError();
+ slapi_log_error( SLAPI_LOG_FATAL, "start_tls",
+ "SSL_BadCertHook(%i) %i " SLAPI_COMPONENT_NAME_NSPR " error %d (%s)\n",
+ conn->c_sd, rv, prerr, slapd_pr_strerror( prerr ) );
+ }
+ }
+
+
+ /* Once agreed in starting TLS, the handshake must be carried out. */
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Starting SSL Handshake.\n" );
+
+ unlock_and_return:
+ PR_Unlock( conn->c_mutex );
+
+ return( SLAPI_PLUGIN_EXTENDED_SENT_RESULT );
+
+}/* start_tls */
+
+
+/* TLS Graceful Closure function.
+ * The function below kind of "resets" the connection to its state previous
+ * to receiving the Start TLS operation request. But it also sets the
+ * authorization and authentication credentials to "anonymous". Finally,
+ * it sends a closure alert message to the client.
+ *
+ * Note: start_tls_graceful_closure() must be called with c->c_mutex locked.
+ */
+int
+start_tls_graceful_closure( Connection *c, Slapi_PBlock * pb, int is_initiator )
+{
+ int ns;
+ Slapi_PBlock *pblock = pb;
+ struct slapdplugin *plugin;
+ int secure = 0;
+ PRFileDesc *ssl_fd;
+
+ if ( pblock == NULL ) {
+ pblock = slapi_pblock_new();
+ plugin = (struct slapdplugin *) slapi_ch_calloc( 1, sizeof( struct slapdplugin ) );
+ pblock->pb_plugin = plugin;
+ pblock->pb_conn = c;
+ pblock->pb_op = c->c_ops;
+ set_db_default_result_handlers( pblock );
+ if ( slapi_pblock_set( pblock, SLAPI_EXT_OP_RET_OID, START_TLS_OID ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Could not set extended response oid.\n" );
+ slapi_send_ldap_result( pblock, LDAP_OPERATIONS_ERROR, NULL,
+ "Could not set extended response oid.", 0, NULL );
+ return( SLAPI_PLUGIN_EXTENDED_SENT_RESULT );
+ }
+ slapi_ch_free( (void **) &plugin );
+ }
+
+ /* First thing to do is to finish with whatever operation may be hanging on the
+ * encrypted session.
+ */
+
+ while ( connection_operations_pending( c, pblock->pb_op,
+ 0 /* wait for all other ops to full complete */ )) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls",
+ "Still %d operations to be completed before closing the SSL connection.\n",
+ c->c_refcnt - 1 );
+ }
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls_graceful_closure", "SSL_CLOSE_NOTIFY_ALERT\n" );
+
+ /* An SSL close_notify alert should be sent to the client. However, the NSS API
+ * doesn't provide us with anything alike.
+ */
+ slapi_send_ldap_result( pblock, LDAP_OPERATIONS_ERROR, NULL,
+ "SSL_CLOSE_NOTIFY_ALERT", 0, NULL );
+
+
+ if ( is_initiator ) {
+ /* if this call belongs to the initiator of the SSL connection closure, it must first
+ * wait for the peer to send another close_notify alert back.
+ */
+ }
+
+
+ PR_Lock( c->c_mutex );
+
+ /* "Unimport" the socket from SSL, i.e. get rid of the upper layer of the
+ * file descriptor stack, which represents SSL.
+ * The ssl socket assigned to c->c_prfd should also be closed and destroyed.
+ * Should find a way of getting that ssl socket.
+ */
+ /*
+ rc = strcasecmp( "SSL", PR_GetNameForIdentity( c->c_prfd->identity ) );
+ if ( rc == 0 ) {
+ sslSocket * ssl_socket;
+
+ ssl_socket = (sslSocket *) c->c_prfd->secret;
+ ssl_socket->fd = NULL;
+ }
+ */
+ ssl_fd = PR_PopIOLayer( c->c_prfd, PR_TOP_IO_LAYER );
+ ssl_fd->dtor( ssl_fd );
+
+
+#ifndef _WIN32
+ secure = 0;
+ ns = configure_pr_socket( &(c->c_prfd), secure );
+
+ ber_sockbuf_set_option( c->c_sb, LBER_SOCKBUF_OPT_DESC, &(c->c_prfd) );
+
+#else
+ ns = PR_FileDesc2NativeHandle( c->c_prfd );
+ c->c_prfd = NULL;
+
+ configure_ns_socket( &ns );
+
+ ber_sockbuf_set_option( c->c_sb, LBER_SOCKBUF_OPT_DESC, &ns );
+
+#endif
+
+ c->c_sd = ns;
+ c->c_flags &= ~CONN_FLAG_SSL;
+ c->c_flags &= ~CONN_FLAG_START_TLS;
+
+ ber_sockbuf_set_option( c->c_sb, LBER_SOCKBUF_OPT_READ_FN, (void *)read_function );
+ ber_sockbuf_set_option( c->c_sb, LBER_SOCKBUF_OPT_WRITE_FN, (void *)write_function );
+
+
+ /* authentication & authorization credentials must be set to "anonymous". */
+
+ bind_credentials_clear( c, PR_FALSE, PR_TRUE );
+
+ PR_Unlock( c->c_mutex );
+
+
+
+ return ( SLAPI_PLUGIN_EXTENDED_SENT_RESULT );
+}
+
+
+static char *start_tls_oid_list[] = {
+ START_TLS_OID,
+ NULL
+};
+static char *start_tls_name_list[] = {
+ "startTLS",
+ NULL
+};
+
+int start_tls_register_plugin()
+{
+ slapi_register_plugin( "extendedop", 1 /* Enabled */, "start_tls_init",
+ start_tls_init, "Start TLS extended operation",
+ start_tls_oid_list, NULL );
+
+ return 0;
+}
+
+
+/* Initialization function */
+
+int start_tls_init( Slapi_PBlock *pb )
+{
+ char **argv;
+ char *oid;
+
+ /* Get the arguments appended to the plugin extendedop directive. The first argument
+ * (after the standard arguments for the directive) should contain the OID of the
+ * extended operation.
+ */
+
+ if ( slapi_pblock_get( pb, SLAPI_PLUGIN_ARGV, &argv ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls_init", "Could not get argv\n" );
+ return( -1 );
+ }
+
+ /* Compare the OID specified in the configuration file against the Start TLS OID. */
+
+ if ( argv == NULL || strcmp( argv[0], START_TLS_OID ) != 0 ) {
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls_init",
+ "OID is missing or is not %s\n", START_TLS_OID );
+ return( -1 );
+ } else {
+ oid = slapi_ch_strdup( argv[0] );
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls_init",
+ "Registering plug-in for Start TLS extended op %s.\n", oid );
+ }
+
+ /* Register the plug-in function as an extended operation
+ * plug-in function that handles the operation identified by
+ * OID 1.3.6.1.4.1.1466.20037. Also specify the version of the server
+ * plug-in */
+ if ( slapi_pblock_set( pb, SLAPI_PLUGIN_VERSION, SLAPI_PLUGIN_VERSION_01 ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_DESCRIPTION, (void *)&exopdesc ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_FN, (void *) start_tls ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_OIDLIST, start_tls_oid_list ) != 0 ||
+ slapi_pblock_set( pb, SLAPI_PLUGIN_EXT_OP_NAMELIST, start_tls_name_list ) != 0 ) {
+
+ slapi_log_error( SLAPI_LOG_PLUGIN, "start_tls_init",
+ "Failed to set plug-in version, function, and OID.\n" );
+ return( -1 );
+ }
+
+ return( 0 );
+}
+