Patchwork [05/11] libnsdb: New mechanism for setting NSDB connection security

login
register
mail settings
Submitter Chuck Lever
Date Jan. 16, 2013, 9:52 p.m.
Message ID <20130116215226.21683.36703.stgit@seurat.1015granger.net>
Download mbox | patch
Permalink /patch/213073/
State Accepted
Headers show

Comments

Chuck Lever - Jan. 16, 2013, 9:52 p.m.
The nsdbparams "update" subcommand shouldn't wipe security data when
it is invoked.  Every time an NSDB connection parameters update is
done to change other connection parameters, the previous security
settings for that NSDB are wiped.

It's always worked this way, but until now, fedfs-utils has
supported only one connection security type, a type which had no
security data associated with it.

Also, the LDAP library wants certificate files in PEM format, but
the ADMIN protocol states that certificate material transmitted on
the network (via FEDFS_SET_NSDB_PARAMS, for instance) must be DER
encoded.

In addition to transforming certificate material between PEM and
DER encoding, fedfsd and the ADMIN client tools ought to be doing
some level of validation of user-provided certificate material
before storage and use.

To address these issues, introduce a new API for changing NSDB
connection security settings.  The old API used the
nsdb_update_nsdb() function to both create new entries in the local
NSDB connection parameters database, and to change/update their
security settings.  The new API breaks nsdb_update_nsdb() into two
phases:

  o  The new nsdb_create_nsdb() function creates a new entry in the
     local NSDB connection parameters database

  o  The new nsdb_connsec_set_foo() functions replace the
     security settings in an entry

The purpose of the new arrangement is to split out the details of
certificate management from the details of creating an entry in the
database.  Setting the connection security mode is the same class of
operation as updating any of the other per-NSDB settings, with the
additional complexity of having to deal with the private certificate
files.

To manage certificate encoding transformations, new APIs are
introduced that allow callers to pass just the pathname of the
certificate file to libnsdb, which handles the details of validating
and storing the certificate material.

In subsequent commits, I'll update the various tools and daemons to
use the new API, then finally remove the old API.

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---

 configure.ac                |   10 +
 src/fedfsd/Makefile.am      |    2 
 src/include/nsdb.h          |   46 ++++
 src/libnsdb/connsec.c       |  485 +++++++++++++++++++++++++++++++++++++++++++
 src/libnsdb/nsdb-internal.h |   13 +
 src/libnsdb/nsdb.c          |  228 ++++++++++++++++++++
 src/plug-ins/Makefile.am    |    2 
 7 files changed, 781 insertions(+), 5 deletions(-)

Patch

diff --git a/configure.ac b/configure.ac
index 2ae2031..8859eb6 100644
--- a/configure.ac
+++ b/configure.ac
@@ -128,6 +128,16 @@  AC_CHECK_LIB([uriparser], [uriParseUriA],
 		 AC_DEFINE([HAVE_LIBURIPARSER], [1],
 			   [Define if you have liburiparser])],
 		[AC_MSG_ERROR([liburiparser not found.])])
+AC_CHECK_LIB([crypto], [X509_LOOKUP_file],
+		[AC_SUBST([LIBCRYPTO], ["-lcrypto"])
+		 AC_DEFINE([HAVE_LIBCRYPTO], [1],
+			   [Define if you have libcrypto])],
+		[AC_MSG_ERROR([libcrypto not found.])])
+AC_CHECK_LIB([ssl], [SSL_CTX_new],
+		[AC_SUBST([LIBSSL], ["-lssl"])
+		 AC_DEFINE([HAVE_LIBSSL], [1],
+			   [Define if you have libssl])],
+		[AC_MSG_ERROR([libssl not found.])])
 
 # Checks for header files.
 AC_CHECK_HEADERS([fcntl.h langinfo.h locale.h memory.h netdb.h netinet/in.h stdint.h stdlib.h string.h sys/socket.h syslog.h termios.h unistd.h wchar.h])
diff --git a/src/fedfsd/Makefile.am b/src/fedfsd/Makefile.am
index 29050c2..0705a3b 100644
--- a/src/fedfsd/Makefile.am
+++ b/src/fedfsd/Makefile.am
@@ -29,7 +29,7 @@  sbin_PROGRAMS		= fedfsd
 fedfsd_SOURCES		= listen.c main.c privilege.c svc.c
 fedfsd_LDADD		= $(LIBTIRPC) $(LIBLDAP) $(LIBLBER) $(LIBXML2) \
 			  $(LIBSQLITE3) $(LIBIDN) $(LIBUUID) $(LIBCAP) \
-			  $(LIBURIPARSER) \
+			  $(LIBURIPARSER) $(LIBCRYPTO) $(LIBSSL) \
 			  $(top_builddir)/src/libadmin/libadmin.la \
 			  $(top_builddir)/src/libnsdb/libnsdb.la \
 			  $(top_builddir)/src/libjunction/libjunction.la \
diff --git a/src/include/nsdb.h b/src/include/nsdb.h
index cd81874..1bbe947 100644
--- a/src/include/nsdb.h
+++ b/src/include/nsdb.h
@@ -185,6 +185,22 @@  FedFsStatus	 nsdb_lookup_nsdb(const char *hostname,
 FedFsStatus	 nsdb_lookup_nsdb_by_uri(const char *uri, nsdb_t *host);
 
 /**
+ * Create connection parameters entry for an NSDB
+ */
+FedFsStatus	 nsdb_create_nsdb(const char *hostname,
+				const unsigned short port);
+
+/**
+ * Initialize libcrypto
+ */
+void		 nsdb_connsec_crypto_startup(void);
+
+/**
+ * Shut down libcrypto
+ */
+void		 nsdb_connsec_crypto_shutdown(void);
+
+/**
  * Retrieve NSDB certificate data for "host"
  */
 FedFsStatus	 nsdb_connsec_get_cert_data(nsdb_t host,
@@ -198,6 +214,28 @@  FedFsStatus	 nsdb_update_nsdb(const char *hostname,
 				const struct fedfs_secdata *sec);
 
 /**
+ * Set connection security parameters for an NSDB to "NONE"
+ */
+FedFsStatus	 nsdb_connsec_set_none(const char *hostname,
+				const unsigned short port);
+
+/**
+ * Set connection security parameters for an NSDB to "TLS"
+ * Certificate material provided in a buffer
+ */
+FedFsStatus	 nsdb_connsec_set_tls_buf(const char *hostname,
+				const unsigned short port, char *data,
+				unsigned int len);
+
+/**
+ * Set connection security parameters for an NSDB to "TLS"
+ * Certificate material provided in a local file
+ */
+FedFsStatus	 nsdb_connsec_set_tls_file(const char *hostname,
+				const unsigned short port,
+				const char *certfile);
+
+/**
  * Update stored default bind DN for an NSDB
  */
 FedFsStatus	 nsdb_update_default_binddn(const char *hostname,
@@ -443,4 +481,12 @@  FedFsStatus	 nsdb_path_array_to_uri_pathname(char * const *path_array,
 FedFsStatus	 nsdb_uri_pathname_to_path_array(const UriUriA *uri,
 				char ***path_array);
 
+/**
+ ** x.509 certificate utilities
+ **/
+FedFsStatus	 nsdb_connsec_read_pem_file(const char *certfile,
+				char **data, unsigned int *len);
+FedFsStatus	 nsdb_connsec_write_pem_file(const char *certfile,
+				const char *data, const unsigned int len);
+
 #endif	/* !_FEDFS_NSDB_H_ */
diff --git a/src/libnsdb/connsec.c b/src/libnsdb/connsec.c
index fb708cb..5272683 100644
--- a/src/libnsdb/connsec.c
+++ b/src/libnsdb/connsec.c
@@ -23,12 +23,366 @@ 
  *	http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
  */
 
+#include <stdbool.h>
+#include <unistd.h>
+
+#include <openssl/ssl.h>
+#include <openssl/engine.h>
+#include <openssl/err.h>
+
 #include "fedfs.h"
 #include "nsdb.h"
 #include "nsdb-internal.h"
 #include "xlog.h"
 
 /**
+ * Flatten X509 object into a buffer
+ * Fire up the crypto library
+ */
+void
+nsdb_connsec_crypto_startup(void)
+{
+	xlog(D_CALL, "%s");
+
+	CRYPTO_malloc_init();
+	ERR_load_crypto_strings();
+	OpenSSL_add_all_algorithms();
+	ENGINE_load_builtin_engines();
+}
+
+/**
+ * Fire up the crypto library
+ */
+void
+nsdb_connsec_crypto_shutdown(void)
+{
+	xlog(D_CALL, "%s");
+
+	OBJ_cleanup();
+	EVP_cleanup();
+	ENGINE_cleanup();
+	CRYPTO_cleanup_all_ex_data();
+	ERR_remove_thread_state(NULL);
+	ERR_free_strings();
+}
+
+/**
+ * Calculate size of buffer to contain DER-encoded certificates from "bio"
+ *
+ * @param certfile NUL-terminated C string containing pathname of file
+ * @param bio open BIO struct of file to analyze
+ * @param len OUT: size of buffer required, in bytes
+ * @return a FedFsStatus code
+ */
+static FedFsStatus
+nsdb_connsec_buffer_size(const char *certfile, BIO *bio, unsigned int *len)
+{
+	unsigned int result;
+	X509 *x509;
+
+	(void)BIO_reset(bio);
+	result = 0;
+	while ((x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) {
+		int size;
+
+		size = i2d_X509(x509, NULL);
+		X509_free(x509);
+		if (size < 0) {
+			xlog(D_GENERAL, "%s: failed to parse %s",
+				__func__, certfile);
+			return FEDFS_ERR_SVRFAULT;
+		}
+
+		xlog(D_GENERAL, "%s: certificate in %s need %u bytes",
+			__func__, certfile, size);
+		result += size + 1;
+	}
+
+	if (result == 0) {
+		xlog(D_CALL, "%s: no certificates found in %s",
+			__func__, certfile);
+		return FEDFS_ERR_INVAL;
+	}
+
+	xlog(D_CALL, "%s: buffer for %s should contain %u bytes",
+		__func__, certfile, result);
+	*len = result;
+	return FEDFS_OK;
+}
+
+/**
+ * Read certificate data into a buffer
+ *
+ * @param certfile NUL-terminated C string containing pathname of file containing PEM-encoded x.509 certificate material
+ * @param bio open BIO struct of file to read
+ * @param buf OUT: pointer to buffer containing DER-encoded x.509 certificate
+ * @return a FedFsStatus code
+ */
+static FedFsStatus
+nsdb_connsec_read_bio_x509_buf(const char *certfile, BIO *bio,
+		unsigned char *buf)
+{
+	FedFsStatus retval;
+	X509 *x509;
+
+	(void)BIO_reset(bio);
+	retval = FEDFS_ERR_IO;
+	while((x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL)) != NULL) {
+		int size;
+
+		size = i2d_X509(x509, &buf);
+		X509_free(x509);
+		if (size < 0) {
+			xlog(D_GENERAL, "%s: failed to parse %s",
+				__func__, certfile);
+			return FEDFS_ERR_SVRFAULT;
+		}
+		xlog(D_CALL, "%s: read a certificate from %s",
+			__func__, certfile);
+		retval = FEDFS_OK;
+	}
+
+	return retval;
+}
+
+/**
+ * Read certificate data from a file
+ *
+ * @param certfile NUL-terminated C string containing pathname of file containing PEM-encoded x.509 certificate material
+ * @param bio open BIO struct of file to read
+ * @param data OUT: pointer to buffer containing DER-encoded x.509 certificate
+ * @param len OUT: length of buffer containing certificate
+ * @return a FedFsStatus code
+ *
+ * Caller must free returned "data" with free(3)
+ */
+static FedFsStatus
+nsdb_connsec_read_bio_x509(const char *certfile, BIO *bio,
+		unsigned char **data, unsigned int *len)
+{
+	FedFsStatus retval;
+	unsigned char *buf;
+	unsigned int size;
+
+	retval = nsdb_connsec_buffer_size(certfile, bio, &size);
+	if (retval != FEDFS_OK)
+		return retval;
+
+	buf = (unsigned char *)calloc(1, size);
+	if (buf == NULL) {
+		xlog(D_GENERAL, "%s: failed to allocate buffer", __func__);
+		return FEDFS_ERR_SVRFAULT;
+	}
+
+	retval = nsdb_connsec_read_bio_x509_buf(certfile, bio, buf);
+	if (retval != FEDFS_OK) {
+		free(buf);
+		return retval;
+	}
+
+	*data = buf;
+	*len = size;
+	return FEDFS_OK;
+}
+
+/**
+ * Read certificate material from a file
+ *
+ * @param certfile NUL-terminated C string containing pathname of file containing PEM-encoded x.509 certificate material
+ * @param data OUT: pointer to buffer containing DER-encoded x.509 certificates
+ * @param len OUT: size of "data", in bytes
+ * @return a FedFsStatus code
+ *
+ * Caller must free returned "data" with free(3)
+ */
+FedFsStatus
+nsdb_connsec_read_pem_file(const char *certfile,
+		char **data, unsigned int *len)
+{
+	FedFsStatus retval;
+	BIO *bio;
+
+	if (certfile == NULL || data == NULL || len == NULL)
+		return FEDFS_ERR_INVAL;
+
+	xlog(D_CALL, "%s: %s", __func__, certfile);
+
+	retval = FEDFS_ERR_ACCESS;
+	bio = BIO_new_file(certfile, "r");
+	if (bio == NULL) {
+		xlog(D_GENERAL, "%s: failed to open %s", __func__, certfile);
+		goto out;
+	}
+
+	retval = nsdb_connsec_read_bio_x509(certfile, bio,
+						(unsigned char **)data, len);
+
+	BIO_free_all(bio);
+
+out:
+	ERR_clear_error();
+	return retval;
+}
+
+/**
+ * Store certificate data in a file
+ *
+ * @param certfile NUL-terminated C string containing name of file to write
+ * @param bio open BIO struct of file to write
+ * @param data pointer to buffer containing DER-encoded x.509 certificate
+ * @param len size of "data" in bytes
+ * @return a FedFsStatus code
+ */
+static FedFsStatus
+nsdb_connsec_write_bio_x509(const char *certfile, BIO *bio,
+		const unsigned char *data, long len)
+{
+	const unsigned char *p, *q;
+	FedFsStatus retval;
+
+	(void)BIO_reset(bio);
+	p = data;
+
+	retval = FEDFS_ERR_INVAL;
+	q = p;
+	do {
+		X509 *x509;
+		int result;
+
+		x509 = d2i_X509(NULL, &data, len);
+		if (x509 == NULL)
+			break;
+		xlog(D_CALL, "%s: parsed %d bytes", __func__, q - p);
+		len -= q - p;
+		p = q;
+
+		result = PEM_write_bio_X509(bio, x509);
+		X509_free(x509);
+		if (result < 0) {
+			xlog(D_GENERAL, "%s: failed to write certificate to %s",
+				__func__, certfile);
+			return FEDFS_ERR_IO;
+		}
+
+		xlog(D_CALL, "%s: wrote a certificate to %s",
+			__func__, certfile);
+		retval = FEDFS_OK;
+	} while (1);
+
+	if (retval != FEDFS_OK)
+		xlog(D_CALL, "%s: found no certificates in buffer", __func__);
+	return retval;
+}
+
+/**
+ * Store certificate data in a file
+ *
+ * @param certfile NUL-terminated C string containing name of file to write
+ * @param data pointer to buffer containing DER-encoded x.509 certificate
+ * @param len size of "data" in bytes
+ * @return a FedFsStatus code
+ */
+FedFsStatus
+nsdb_connsec_write_pem_file(const char *certfile,
+		const char *data, unsigned int len)
+{
+	char *tmpfile = NULL;
+	FedFsStatus retval;
+	BIO *bio;
+
+	if (certfile == NULL || data == NULL)
+		return FEDFS_ERR_INVAL;
+
+	xlog(D_CALL, "%s: %u bytes to %s", __func__, len, certfile);
+
+	tmpfile = malloc(strlen(certfile) + 5);
+	if (tmpfile == NULL)
+		return FEDFS_ERR_SVRFAULT;
+	strcpy(tmpfile, certfile);
+	strcat(tmpfile, ".TMP");
+
+	retval = FEDFS_ERR_INVAL;
+	bio = BIO_new_file(tmpfile, "w");
+	if (bio == NULL) {
+		xlog(D_GENERAL, "%s: failed to open temporary certificate file %s",
+			__func__, certfile);
+		goto out;
+	}
+
+	retval = nsdb_connsec_write_bio_x509(certfile, bio,
+					(const unsigned char *)data,
+					(long)len);
+
+	BIO_free_all(bio);
+
+	if (retval == FEDFS_OK) {
+		if (rename(tmpfile, certfile) == -1) {
+			xlog(D_GENERAL, "%s: rename failed: %m",
+				__func__);
+			(void)unlink(tmpfile);
+			retval = FEDFS_ERR_IO;
+		}
+	}
+
+out:
+	free(tmpfile);
+	ERR_clear_error();
+	return retval;
+}
+
+/**
+ * Create a private file and store certficate data in it
+ *
+ * @param data pointer to buffer containing DER-encoded x.509 certificate
+ * @param len size of "data" in bytes
+ * @param pathname OUT: pointer to NUL-terminated C string containing pathname of new file
+ * @return a FedFsStatus code
+ *
+ * On success, FEDFS_OK is returned, a new cert file is created in our
+ * private certificate directory, the certificate material is copied to it
+ * in PEM format, and "pathname" is filled in.
+ *
+ * Caller must free the pathname with free(3)
+ */
+static FedFsStatus
+nsdb_connsec_create_pem_file(const char *data, const unsigned int len,
+		char **pathname)
+{
+	FedFsStatus retval;
+	char *tmp;
+
+	retval = nsdb_create_private_certfile(&tmp);
+	if (retval != FEDFS_OK)
+		return retval;
+
+	retval = nsdb_connsec_write_pem_file(tmp, data, len);
+	if (retval != FEDFS_OK) {
+		(void)unlink(tmp);
+		free(tmp);
+		return retval;
+	}
+
+	*pathname = tmp;
+	return FEDFS_OK;
+}
+
+/**
+ * Remove the cert file for an NSDB
+ *
+ * @param certfile NUL-terminated UTF-8 string containing pathname of file
+ */
+void
+nsdb_connsec_remove_certfile(const char *certfile)
+{
+	if (certfile == NULL || *certfile == '\0')
+		return;
+
+	xlog(D_CALL, "%s: %s", __func__, certfile);
+	if (unlink(certfile) == -1)
+		xlog(D_GENERAL, "%s: unlink: %m", __func__);
+}
+
+/**
  * Retrieve certificate data for NSDB "host" from NSDB database
  *
  * @param host an initialized nsdb_t object
@@ -37,6 +391,7 @@ 
  * @return a FedFsStatus code
  *
  * On success, FEDFS_OK is returned and the security data is filled in.
+ * For the TLS security type, this is DER-encoded certificate material.
  *
  * Caller must free the returned buffer with free(3).
  */
@@ -53,7 +408,8 @@  nsdb_connsec_get_cert_data(nsdb_t host, char **data, unsigned int *len)
 		retval = FEDFS_ERR_INVAL;
 		break;
 	case FEDFS_SEC_TLS:
-		retval = nsdb_read_certfile(nsdb_certfile(host), data, len);
+		retval = nsdb_connsec_read_pem_file(nsdb_certfile(host),
+								data, len);
 		break;
 	default:
 		retval = FEDFS_ERR_SVRFAULT;
@@ -61,3 +417,130 @@  nsdb_connsec_get_cert_data(nsdb_t host, char **data, unsigned int *len)
 
 	return retval;
 }
+
+/**
+ * Set connection security parameters for an NSDB to "NONE"
+ *
+ * @param hostname NUL-terminated UTF-8 string containing NSDB hostname
+ * @param port integer port number of NSDB
+ * @return a FedFsStatus code
+ *
+ * Affects only new connections to the NSDB.  Existing connections
+ * are not changed or closed by this call.
+ */
+FedFsStatus
+nsdb_connsec_set_none(const char *hostname, const unsigned short port)
+{
+	const char *old_certfile;
+	FedFsStatus retval;
+	nsdb_t host;
+
+	xlog(D_CALL, "%s: %s:%u", __func__, hostname, port);
+
+	retval = nsdb_lookup_nsdb(hostname, port, &host);
+	if (retval != FEDFS_OK)
+		return retval;
+	old_certfile = nsdb_certfile(host);
+
+	retval = nsdb_update_security_nsdbparams(host, FEDFS_SEC_NONE, "");
+	if (retval == FEDFS_OK)
+		nsdb_connsec_remove_certfile(old_certfile);
+
+	nsdb_free_nsdb(host);
+	return retval;
+}
+
+/**
+ * Set connection security parameters for an NSDB to "TLS"
+ *
+ * @param hostname NUL-terminated UTF-8 string containing NSDB hostname
+ * @param port integer port number of NSDB
+ * @param data buffer containing certificate material
+ * @param len length of "data" in bytes
+ * @return a FedFsStatus code
+ *
+ * "data" contains DER-encoded x.509 certificate material.
+ *
+ * Affects only new connections to the NSDB.  Existing connections
+ * are not changed or closed by this call.
+ */
+FedFsStatus
+nsdb_connsec_set_tls_buf(const char *hostname, const unsigned short port,
+		char *data, unsigned int len)
+{
+	char *new_certfile = NULL;
+	const char *old_certfile;
+	FedFsStatus retval;
+	nsdb_t host = NULL;
+
+	retval = nsdb_connsec_create_pem_file(data, len, &new_certfile);
+	if (retval != FEDFS_OK)
+		goto out;
+
+	xlog(D_CALL, "%s: %s:%u, %s", __func__, hostname, port, new_certfile);
+
+	retval = nsdb_lookup_nsdb(hostname, port, &host);
+	if (retval != FEDFS_OK)
+		return retval;
+	old_certfile = nsdb_certfile(host);
+
+	retval = nsdb_update_security_nsdbparams(host, FEDFS_SEC_TLS,
+								new_certfile);
+	if (retval == FEDFS_OK)
+		nsdb_connsec_remove_certfile(old_certfile);
+
+out:
+	nsdb_free_nsdb(host);
+	free(new_certfile);
+	return retval;
+}
+
+/**
+ * Set connection security parameters for an NSDB to "TLS"
+ *
+ * @param hostname NUL-terminated UTF-8 string containing NSDB hostname
+ * @param port integer port number of NSDB
+ * @param certfile NUL-terminated UTF-8 string containing pathname of file
+ * @return a FedFsStatus code
+ *
+ * "certfile" is a file containing a PEM-encoded x.509 certificate.
+ *
+ * Affects only new connections to the NSDB.  Existing connections
+ * are not changed or closed by this call.
+ */
+FedFsStatus
+nsdb_connsec_set_tls_file(const char *hostname, const unsigned short port,
+		const char *certfile)
+{
+	const char *old_certfile;
+	char *new_certfile;
+	FedFsStatus retval;
+	char *data = NULL;
+	unsigned int len;
+	nsdb_t host;
+
+	xlog(D_CALL, "%s: %s:%u, %s", __func__, hostname, port, certfile);
+
+	retval = nsdb_connsec_read_pem_file(certfile, &data, &len);
+	if (retval != FEDFS_OK)
+		return retval;
+
+	retval = nsdb_connsec_create_pem_file(data, len, &new_certfile);
+	if (retval != FEDFS_OK)
+		return retval;
+
+	retval = nsdb_lookup_nsdb(hostname, port, &host);
+	if (retval != FEDFS_OK)
+		return retval;
+	old_certfile = nsdb_certfile(host);
+
+	retval = nsdb_update_security_nsdbparams(host, FEDFS_SEC_TLS,
+							new_certfile);
+	if (retval == FEDFS_OK)
+		nsdb_connsec_remove_certfile(old_certfile);
+
+	nsdb_free_nsdb(host);
+	free(new_certfile);
+	free(data);
+	return retval;
+}
diff --git a/src/libnsdb/nsdb-internal.h b/src/libnsdb/nsdb-internal.h
index 82d707a..eb63624 100644
--- a/src/libnsdb/nsdb-internal.h
+++ b/src/libnsdb/nsdb-internal.h
@@ -129,4 +129,17 @@  void		 nsdb_rollback_transaction(sqlite3 *db);
 _Bool		 nsdb_create_table(sqlite3 *db, const char *table_name,
 				const char *table_def);
 
+/**
+ ** Private security-related APIs (nsdb.c)
+ **/
+FedFsStatus	 nsdb_create_private_certfile(char **pathbuf);
+FedFsStatus	 nsdb_update_security_nsdbparams(struct fedfs_nsdb *host,
+				FedFsConnectionSec type,
+				const char *certfile);
+
+/**
+ ** Private security-related APIs (connsec.c)
+ **/
+void		 nsdb_connsec_remove_certfile(const char *certfile);
+
 #endif	/* !_FEDFS_NSDB_INTERNAL_H_ */
diff --git a/src/libnsdb/nsdb.c b/src/libnsdb/nsdb.c
index 112df22..0c84a37 100644
--- a/src/libnsdb/nsdb.c
+++ b/src/libnsdb/nsdb.c
@@ -174,6 +174,67 @@  nsdb_is_default_parentdir(void)
 }
 
 /**
+ * Create a new file in our private certificate directory
+ *
+ * @param pathbuf OUT: NUL-terminated C string containing pathname of new file
+ * @return a FedFsStatus code
+ *
+ * Caller must free "pathbuf" with free(3).
+ */
+FedFsStatus
+nsdb_create_private_certfile(char **pathbuf)
+{
+	FedFsStatus retval;
+	char *tmp = NULL;
+	int len;
+
+	retval = FEDFS_ERR_SVRFAULT;
+	if (mkdir(fedfs_nsdbcerts_dirname, FEDFS_BASE_DIRMODE) == -1) {
+		if (errno != EEXIST) {
+			xlog(L_ERROR, "Failed to create certfile directory: %m");
+			goto out;
+		}
+	}
+
+	tmp = malloc(PATH_MAX);
+	if (tmp == NULL) {
+		xlog(D_GENERAL, "%s: failed to allocate pathname buffer",
+			__func__);
+		goto out;
+	}
+
+	len = snprintf(tmp, PATH_MAX, "%s/nsdbXXXXXX.pem",
+				fedfs_nsdbcerts_dirname);
+	if (len > PATH_MAX) {
+		xlog(D_GENERAL, "%s: NSDB certificate directory pathname is "
+			"too long", __func__);
+		free(tmp);
+		goto out;
+	}
+
+	if (mkstemps(tmp, 4) == -1) {
+		xlog(D_GENERAL, "%s: failed to create NSDB certificate file %s: %m",
+			__func__, pathbuf);
+		free(tmp);
+		goto out;
+	}
+
+	if (chmod(tmp, FEDFS_CERTFILE_MODE) == -1) {
+		xlog(D_GENERAL, "%s: failed to chmod NSDB certificate file %s: %m",
+			__func__, pathbuf);
+		(void)unlink(tmp);
+		free(tmp);
+		goto out;
+	}
+
+	*pathbuf = tmp;
+	retval = FEDFS_OK;
+
+out:
+	return retval;
+}
+
+/**
  * Create database table for tracking NSDB data
  *
  * @param db an open sqlite3 database descriptor
@@ -744,8 +805,9 @@  nsdb_new_nsdbname(sqlite3 *db, const nsdb_t host)
 	int rc;
 
 	retval = FEDFS_ERR_IO;
-	if (!nsdb_prepare_stmt(db, &stmt, "INSERT INTO nsdbs"
-			" (nsdbName,nsdbPort) VALUES(?,?);"))
+	if (!nsdb_prepare_stmt(db, &stmt, "INSERT INTO nsdbs "
+			"(nsdbName,nsdbPort,securityType,securityFilename) "
+			"VALUES(?,?,0,\"\");"))
 		goto out;
 
 	rc = sqlite3_bind_text(stmt, 1, domainname, -1, SQLITE_STATIC);
@@ -859,6 +921,79 @@  out:
 }
 
 /**
+ * Update security information about an NSDB in our NSDB database
+ *
+ * @param db an open sqlite3 database descriptor
+ * @param host an instantiated nsdb_t object
+ * @param sectype an integer value representing the security type
+ * @param certfile a NUL-terminated UTF-8 C string containing the name of a file containing an x.509 certificate
+ * @return a FedFsStatus code
+ *
+ * Information is copied from the nsdb_t object to the cert store.
+ */
+static FedFsStatus
+nsdb_update_security_nsdbname(sqlite3 *db, const nsdb_t host,
+		unsigned int sectype, const char *certfile)
+{
+	const char *domainname = host->fn_hostname;
+	const int port = host->fn_port;
+	sqlite3_stmt *stmt;
+	FedFsStatus retval;
+	int rc;
+
+	retval = FEDFS_ERR_IO;
+	if (!nsdb_prepare_stmt(db, &stmt, "UPDATE nsdbs "
+			" SET securityType=?,securityFilename=?"
+			"WHERE nsdbName=? and nsdbPort=?;"))
+		goto out;
+
+	rc = sqlite3_bind_int(stmt, 1, sectype);
+	if (rc != SQLITE_OK) {
+		xlog(L_ERROR, "Failed to bind connection security value: %s",
+			sqlite3_errmsg(db));
+		goto out_finalize;
+	}
+
+	rc = sqlite3_bind_text(stmt, 2, certfile, -1, SQLITE_STATIC);
+	if (rc != SQLITE_OK) {
+		xlog(L_ERROR, "Failed to bind security data value: %s",
+			sqlite3_errmsg(db));
+		goto out_finalize;
+	}
+
+	rc = sqlite3_bind_text(stmt, 3, domainname, -1, SQLITE_STATIC);
+	if (rc != SQLITE_OK) {
+		xlog(L_ERROR, "Failed to bind NSDB hostname %s: %s",
+			domainname, sqlite3_errmsg(db));
+		goto out_finalize;
+	}
+
+	rc = sqlite3_bind_int(stmt, 4, port);
+	if (rc != SQLITE_OK) {
+		xlog(L_ERROR, "Failed to bind port number: %s",
+			sqlite3_errmsg(db));
+		goto out_finalize;
+	}
+
+	rc = sqlite3_step(stmt);
+	switch (rc) {
+	case SQLITE_DONE:
+		xlog(D_CALL, "%s: Updated NSDB info record for '%s:%u' "
+			"to nsdbs table", __func__, domainname, port);
+		retval = FEDFS_OK;
+		break;
+	default:
+		xlog(L_ERROR, "Failed to update NSDB info record for '%s:%u': %s",
+			domainname, port, sqlite3_errmsg(db));
+	}
+
+out_finalize:
+	nsdb_finalize_stmt(stmt);
+out:
+	return retval;
+}
+
+/**
  * Update an NSDB's default bind DN
  *
  * @param db an open sqlite3 database descriptor
@@ -1210,6 +1345,56 @@  out:
 }
 
 /**
+ * Create connection parameters row for an NSDB
+ *
+ * @param host an instantiated nsdb_t object
+ * @return a FedFsStatus code
+ */
+static FedFsStatus
+nsdb_create_nsdbparams(nsdb_t host)
+{
+	FedFsStatus retval;
+	sqlite3 *db;
+
+	xlog(D_CALL, "%s: creating row for NSDB '%s'",
+			__func__, host->fn_hostname);
+
+	retval = FEDFS_ERR_IO;
+	db = nsdb_open_db(fedfs_db_filename, SQLITE_OPEN_READWRITE);
+	if (db == NULL)
+		goto out;
+
+	retval = nsdb_new_nsdbname(db, host);
+
+	nsdb_close_db(db);
+out:
+	return retval;
+}
+
+/**
+ * Create connection parameters entry for an NSDB
+ *
+ * @param hostname NUL-terminated UTF-8 string containing NSDB hostname
+ * @param port integer port number of NSDB
+ * @return a FedFsStatus code
+ */
+FedFsStatus
+nsdb_create_nsdb(const char *hostname, const unsigned short port)
+{
+	nsdb_t host;
+	FedFsStatus retval;
+
+	retval = nsdb_new_nsdb(hostname, port, &host);
+	if (retval != FEDFS_OK)
+		return retval;
+
+	retval = nsdb_create_nsdbparams(host);
+
+	nsdb_free_nsdb(host);
+	return retval;
+}
+
+/**
  * Update connection parameters for an NSDB
  *
  * @param host an instantiated nsdb_t object
@@ -1296,6 +1481,45 @@  nsdb_update_nsdb(const char *hostname, const unsigned short port,
 }
 
 /**
+ * Update connection security parameters for an NSDB
+ *
+ * @param host an instantiated nsdb_t object
+ * @param type connection security type for "host"
+ * @param certfile NUL-terminated UTF-8 string containing pathname of file
+ * @return a FedFsStatus code
+ */
+FedFsStatus
+nsdb_update_security_nsdbparams(nsdb_t host, FedFsConnectionSec type,
+		const char *certfile)
+{
+	FedFsStatus retval;
+	sqlite3 *db;
+
+	xlog(D_CALL, "%s: writing parameters for NSDB '%s'",
+			__func__, host->fn_hostname);
+
+	retval = FEDFS_ERR_IO;
+	db = nsdb_open_db(fedfs_db_filename, SQLITE_OPEN_READWRITE);
+	if (db == NULL)
+		goto out;
+
+	retval = nsdb_new_nsdbname(db, host);
+	if (retval != FEDFS_OK)
+		goto out_close;
+
+	retval = nsdb_update_security_nsdbname(db, host, type, certfile);
+	if (retval != FEDFS_OK)
+		goto out_close;
+
+	retval = FEDFS_OK;
+
+out_close:
+	nsdb_close_db(db);
+out:
+	return retval;
+}
+
+/**
  * Update stored default bind DN for an NSDB
  *
  * @param hostname NUL-terminated UTF-8 string containing NSDB hostname
diff --git a/src/plug-ins/Makefile.am b/src/plug-ins/Makefile.am
index 47bf452..9e4ec4d 100644
--- a/src/plug-ins/Makefile.am
+++ b/src/plug-ins/Makefile.am
@@ -27,7 +27,7 @@  lib_LTLIBRARIES		= libnfsjunct.la
 libnfsjunct_la_SOURCES	= nfs-plugin.c
 libnfsjunct_la_LIBADD	= $(LIBLDAP) $(LIBLBER) $(LIBXML2) \
 			  $(LIBSQLITE3) $(LIBIDN) $(LIBUUID) \
-			  $(LIBURIPARSER) \
+			  $(LIBURIPARSER) $(LIBCRYPTO) $(LIBSSL) \
 			  $(top_builddir)/src/libnsdb/libnsdb.la \
 			  $(top_builddir)/src/libxlog/libxlog.la \
 			  $(top_builddir)/src/libjunction/libjunction.la