From patchwork Wed Jan 16 21:52:27 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chuck Lever X-Patchwork-Id: 213073 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from userp1040.oracle.com (userp1040.oracle.com [156.151.31.81]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "userp1040.oracle.com", Issuer "VeriSign Class 3 International Server CA - G3" (not verified)) by ozlabs.org (Postfix) with ESMTPS id CD67F2C008C for ; Thu, 17 Jan 2013 08:52:37 +1100 (EST) Received: from acsinet21.oracle.com (acsinet21.oracle.com [141.146.126.237]) by userp1040.oracle.com (Sentrion-MTA-4.2.2/Sentrion-MTA-4.2.2) with ESMTP id r0GLqXQt018136 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Wed, 16 Jan 2013 21:52:34 GMT Received: from oss.oracle.com (oss-external.oracle.com [137.254.96.51]) by acsinet21.oracle.com (8.14.4+Sun/8.14.4) with ESMTP id r0GLqWJM011643 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Wed, 16 Jan 2013 21:52:32 GMT Received: from localhost ([127.0.0.1] helo=oss.oracle.com) by oss.oracle.com with esmtp (Exim 4.63) (envelope-from ) id 1TvauO-0006Gd-Bs; Wed, 16 Jan 2013 13:52:32 -0800 Received: from ucsinet21.oracle.com ([156.151.31.93]) by oss.oracle.com with esmtp (Exim 4.63) (envelope-from ) id 1TvauM-0006Fh-8J for fedfs-utils-devel@oss.oracle.com; Wed, 16 Jan 2013 13:52:30 -0800 Received: from userp1020.oracle.com (userp1020.oracle.com [156.151.31.79]) by ucsinet21.oracle.com (8.14.4+Sun/8.14.4) with ESMTP id r0GLqTKK013018 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Wed, 16 Jan 2013 21:52:30 GMT Received: from mail-ie0-f181.google.com (mail-ie0-f181.google.com [209.85.223.181]) by userp1020.oracle.com (Sentrion-MTA-4.2.2/Sentrion-MTA-4.2.2) with ESMTP id r0GLqSZr010554 (version=TLSv1/SSLv3 cipher=RC4-SHA bits=128 verify=OK) for ; Wed, 16 Jan 2013 21:52:29 GMT Received: by mail-ie0-f181.google.com with SMTP id 16so3479113iea.40 for ; Wed, 16 Jan 2013 13:52:28 -0800 (PST) X-Received: by 10.50.179.71 with SMTP id de7mr1141045igc.46.1358373148652; Wed, 16 Jan 2013 13:52:28 -0800 (PST) Received: from seurat.1015granger.net (adsl-99-26-161-222.dsl.sfldmi.sbcglobal.net. [99.26.161.222]) by mx.google.com with ESMTPS id iw5sm5336134igc.5.2013.01.16.13.52.27 (version=TLSv1 cipher=RC4-SHA bits=128/128); Wed, 16 Jan 2013 13:52:28 -0800 (PST) From: Chuck Lever To: fedfs-utils-devel@oss.oracle.com Date: Wed, 16 Jan 2013 16:52:27 -0500 Message-ID: <20130116215226.21683.36703.stgit@seurat.1015granger.net> In-Reply-To: <20130116214757.21683.47697.stgit@seurat.1015granger.net> References: <20130116214757.21683.47697.stgit@seurat.1015granger.net> User-Agent: StGIT/0.14.3 MIME-Version: 1.0 X-Flow-Control-Info: class=Default reputation=ipRepBelow100 ip=209.85.223.181 ct-class=R4 ct-vol1=-96 ct-vol2=8 ct-vol3=8 ct-risk=34 ct-spam1=48 ct-spam2=6 ct-bulk=6 rcpts=1 size=28327 X-MM-CT-Classification: not spam X-MM-CT-RefID: str=0001.0A090202.50F7211D.007A,ss=1,re=-2.300,fgs=0 Subject: [fedfs-utils] [PATCH 05/11] libnsdb: New mechanism for setting NSDB connection security X-BeenThere: fedfs-utils-devel@oss.oracle.com X-Mailman-Version: 2.1.9 Precedence: list Reply-To: fedfs-utils Developers List-Id: fedfs-utils Developers List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: fedfs-utils-devel-bounces@oss.oracle.com Errors-To: fedfs-utils-devel-bounces@oss.oracle.com X-Source-IP: acsinet21.oracle.com [141.146.126.237] 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 --- 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(-) 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 +#include + +#include +#include +#include + #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