diff mbox

[10/11] ADMIN: Transform certificate material properly

Message ID 20130116215315.21683.49069.stgit@seurat.1015granger.net
State Accepted
Headers show

Commit Message

Chuck Lever Jan. 16, 2013, 9:53 p.m. UTC
The latest ADMIN protocol document requires the use of DER encoding
when transferring certificates on the wire.  Our LDAP library uses
certificates stored in PEM-format files when performing a START_TLS
operation.

Transformation between these two formats is required:

  o  rpc.fedfsd should validate that incoming certificates are DER
     encoded, and should transform them to PEM format when storing
     them for use by our LDAP client (FEDFS_SET_NSDB_PARAMS)

  o  rpc.fedfsd should transform stored PEM format certificates to
     DER before returning them to clients (FEDFS_GET_NSDB_PARAMS)

  o  The fedfs-set-nsdb-params tool should validate user-provided
     certificate material, and should transform it to DER encoding
     before sending it to fedfsd

  o  The fedfs-get-nsdb-params tool should transform certificate
     material retrieved from fedfsd into a PEM format file instead
     of displaying the raw certificate data.  DER-encoded data is
     gibberish to a human

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

 doc/man/fedfs-get-nsdb-params.8    |    8 ++++++
 doc/man/fedfs-set-nsdb-params.8    |    7 ++++-
 src/fedfsc/fedfs-get-nsdb-params.c |   33 +++++++++++++++++-------
 src/fedfsc/fedfs-set-nsdb-params.c |   50 ++++++++++++------------------------
 src/fedfsd/main.c                  |    4 +++
 src/fedfsd/svc.c                   |   42 +++++++++++++++++++++---------
 6 files changed, 87 insertions(+), 57 deletions(-)
diff mbox

Patch

diff --git a/doc/man/fedfs-get-nsdb-params.8 b/doc/man/fedfs-get-nsdb-params.8
index 2aac0b2..fb14df3 100644
--- a/doc/man/fedfs-get-nsdb-params.8
+++ b/doc/man/fedfs-get-nsdb-params.8
@@ -29,6 +29,8 @@  fedfs-get-nsdb-params \- send a FEDFS_GET_NSDB_PARAMS ADMIN protocol request
 .SH SYNOPSIS
 .B fedfs-get-nsdb-params
 .RB [ \-?d ]
+.RB [ \-f
+.IR certfile ]
 .RB [ \-n
 .IR nettype ]
 .RB [ \-h
@@ -80,6 +82,12 @@  Displays
 .BR fedfs-get-nsdb-params (8)
 version information and a usage message on
 .IR stderr .
+.IP "\fB-f, \-\-certfile=\fIpathname\fP"
+Specifies the pathname of a local file to write received certificate
+material into, when the specified NSDB's connection security type is
+.BR TLS .
+If no file is specified, the certificate is ignored.
+The certificate is written to the specified file in PEM format.
 .IP "\fB\-h, \-\-hostname=\fIhostname\fP"
 Specifies the hostname of a remote FedFS ADMIN service.
 If this option is not specified, the default value is
diff --git a/doc/man/fedfs-set-nsdb-params.8 b/doc/man/fedfs-set-nsdb-params.8
index 4321693..78bfdf1 100644
--- a/doc/man/fedfs-set-nsdb-params.8
+++ b/doc/man/fedfs-set-nsdb-params.8
@@ -86,8 +86,11 @@  Displays
 version information and a usage message on
 .IR stderr .
 .IP "\fB-f, \-\-certfile=\fIpathname\fP"
-Specifies the pathname of a local file containing the X.509 certificate
-to use for establishing a TLS connection with the specified NSDB.
+Specifies the pathname of a local file containing an x.509 certificate
+the remote system can use to authenticate the specified NSDB node.
+The specified file may be deleted after the command succeeds.
+Details on the contents of this file can be found in
+.BR nsdb-parameters (7).
 .IP "\fB\-h, \-\-hostname=\fIhostname\fP"
 Specifies the hostname of a remote FedFS ADMIN service.
 If this option is not specified, the default value is
diff --git a/src/fedfsc/fedfs-get-nsdb-params.c b/src/fedfsc/fedfs-get-nsdb-params.c
index 786804d..b8c7995 100644
--- a/src/fedfsc/fedfs-get-nsdb-params.c
+++ b/src/fedfsc/fedfs-get-nsdb-params.c
@@ -52,12 +52,13 @@  static struct timeval fedfs_get_nsdb_params_timeout = { 25, 0 };
 /**
  * Short form command line options
  */
-static const char fedfs_get_nsdb_params_opts[] = "?dh:l:n:r:";
+static const char fedfs_get_nsdb_params_opts[] = "?df:h:l:n:r:";
 
 /**
  * Long form command line options
  */
 static const struct option fedfs_get_nsdb_params_longopts[] = {
+	{ "certfile", 1, NULL, 'f', },
 	{ "debug", 0, NULL, 'd', },
 	{ "help", 0, NULL, '?', },
 	{ "hostname", 1, NULL, 'h', },
@@ -72,10 +73,12 @@  fedfs_get_nsdb_params_usage(const char *progname)
 {
 	fprintf(stderr, "\n%s version " VERSION "\n", progname);
 	fprintf(stderr, "Usage: %s [-d] [-n nettype] [-h hostname] "
-			"[-l nsdbname] [-r nsdbport]\n\n", progname);
+			"[-f certfile] [-l nsdbname] [-r nsdbport]\n\n",
+			progname);
 
 	fprintf(stderr, "\t-?, --help           Print this help\n");
 	fprintf(stderr, "\t-d, --debug          Enable debug messages\n");
+	fprintf(stderr, "\t-f, --certfile       Where to write certificate\n");
 	fprintf(stderr, "\t-n, --nettype        RPC transport (default: 'netpath')\n");
 	fprintf(stderr, "\t-h, --hostname       ADMIN server hostname (default: 'localhost')\n");
 	fprintf(stderr, "\t-l, --nsdbname       NSDB hostname\n");
@@ -87,7 +90,8 @@  fedfs_get_nsdb_params_usage(const char *progname)
 }
 
 static void
-fedfs_get_nsdb_params_print_result(FedFsGetNsdbParamsRes result)
+fedfs_get_nsdb_params_print_result(FedFsGetNsdbParamsRes result,
+		const char *certfile)
 {
 	FedFsNsdbParams *params = &result.FedFsGetNsdbParamsRes_u.params;
 
@@ -101,8 +105,10 @@  fedfs_get_nsdb_params_print_result(FedFsGetNsdbParamsRes result)
 		break;
 	case FEDFS_SEC_TLS:
 		printf("ConnectionSec: FEDFS_SEC_TLS\n");
-		printf("X.509 cert:\n%s\n",
-			params->FedFsNsdbParams_u.secData.secData_val);
+		if (certfile != NULL)
+			(void)nsdb_connsec_write_pem_file(certfile,
+				params->FedFsNsdbParams_u.secData.secData_val,
+				params->FedFsNsdbParams_u.secData.secData_len);
 		break;
 	default:
 		printf("Unrecognized FedFsConnectionSec value: %u\n",
@@ -112,7 +118,8 @@  fedfs_get_nsdb_params_print_result(FedFsGetNsdbParamsRes result)
 
 static FedFsStatus
 fedfs_get_nsdb_params_call(const char *hostname, const char *nettype,
-		char *nsdbname, const unsigned short nsdbport)
+		char *nsdbname, const unsigned short nsdbport,
+		const char *certfile)
 {
 	FedFsGetNsdbParamsRes result;
 	enum clnt_stat status;
@@ -141,7 +148,7 @@  fedfs_get_nsdb_params_call(const char *hostname, const char *nettype,
 		clnt_perror(client, "FEDFS_GET_NSDB_PARAMS call failed");
 		result.status = FEDFS_ERR_SVRFAULT;
 	} else {
-		fedfs_get_nsdb_params_print_result(result);
+		fedfs_get_nsdb_params_print_result(result, certfile);
 		clnt_freeres(client,
 			(xdrproc_t)xdr_FedFsGetNsdbParamsRes,
 			(caddr_t)&result);
@@ -155,7 +162,7 @@  out:
 int
 main(int argc, char **argv)
 {
-	char *progname, *hostname, *nettype, *nsdbname;
+	char *progname, *hostname, *nettype, *nsdbname, *certfile;
 	unsigned short nsdbport;
 	unsigned int seconds;
 	FedFsStatus status;
@@ -179,11 +186,15 @@  main(int argc, char **argv)
 
 	hostname = "localhost";
 	nettype = "netpath";
+	certfile = NULL;
 	while ((arg = getopt_long(argc, argv, fedfs_get_nsdb_params_opts, fedfs_get_nsdb_params_longopts, NULL)) != -1) {
 		switch (arg) {
 		case 'd':
 			xlog_config(D_ALL, 1);
 			break;
+		case 'f':
+			certfile = optarg;
+			break;
 		case 'h':
 			hostname = optarg;
 			break;
@@ -220,9 +231,11 @@  main(int argc, char **argv)
 		fedfs_get_nsdb_params_usage(progname);
 	}
 
+	nsdb_connsec_crypto_startup();
+
 	for (seconds = FEDFS_DELAY_MIN_SECS;; seconds = fedfs_delay(seconds)) {
 		status = fedfs_get_nsdb_params_call(hostname, nettype,
-							nsdbname, nsdbport);
+						nsdbname, nsdbport, certfile);
 		if (status != FEDFS_ERR_DELAY)
 			break;
 
@@ -230,5 +243,7 @@  main(int argc, char **argv)
 		if (sleep(seconds) != 0)
 			break;
 	}
+
+	nsdb_connsec_crypto_shutdown();
 	return (int)status;
 }
diff --git a/src/fedfsc/fedfs-set-nsdb-params.c b/src/fedfsc/fedfs-set-nsdb-params.c
index df53620..ca88ef1 100644
--- a/src/fedfsc/fedfs-set-nsdb-params.c
+++ b/src/fedfsc/fedfs-set-nsdb-params.c
@@ -77,7 +77,7 @@  fedfs_set_nsdb_params_usage(const char *progname)
 			progname);
 
 	fprintf(stderr, "\t-d, --debug          Enable debug messages\n");
-	fprintf(stderr, "\t-f, --certfile	Name of file containing X.509 cert\n");
+	fprintf(stderr, "\t-f, --certfile       Where to read certificate\n");
 	fprintf(stderr, "\t-?, --help           Print this help\n");
 	fprintf(stderr, "\t-n, --nettype        RPC transport (default: 'netpath')\n");
 	fprintf(stderr, "\t-h, --hostname       ADMIN server hostname (default: 'localhost')\n");
@@ -92,50 +92,30 @@  fedfs_set_nsdb_params_usage(const char *progname)
 static _Bool
 fedfs_set_nsdb_params_get_params(const char *certfile, FedFsNsdbParams *params)
 {
-	struct stat stb;
-	size_t size;
-	ssize_t len;
+	FedFsStatus retval;
+	unsigned int len;
 	char *buf;
-	int fd;
 
 	if (certfile == NULL) {
 		params->secType = FEDFS_SEC_NONE;
 		return true;
 	}
 
-	if (lstat(certfile, &stb) == -1) {
-		fprintf(stderr, "Failed to stat %s: %s\n",
-				certfile, strerror(errno));
+	retval = nsdb_connsec_read_pem_file(certfile, &buf, &len);
+	switch (retval) {
+	case FEDFS_OK:
+		break;
+	case FEDFS_ERR_INVAL:
+		fprintf(stderr, "Invalid certificate material\n");
 		return false;
-	}
-	size = stb.st_size;
-
-	fd = open(certfile, O_RDONLY);
-	if (fd == -1) {
-		fprintf(stderr, "Failed to open %s: %s\n",
-				certfile, strerror(errno));
-		return false;
-	}
-
-	buf = malloc(size);
-	if (buf == NULL) {
-		fprintf(stderr, "Failed to allocate buffer to read %s\n",
-				certfile);
-		(void)close(fd);
-		return false;
-	}
-
-	len = read(fd, buf, size);
-	if (len < 0 || (size_t)len > size) {
-		fprintf(stderr, "Failed to read %s: %s\n",
-				certfile, strerror(errno));
-		free(buf);
-		(void)close(fd);
+	default:
+		fprintf(stderr, "Failed to validate %s: %s\n",
+			certfile, nsdb_display_fedfsstatus(retval));
 		return false;
 	}
 
 	params->secType = FEDFS_SEC_TLS;
-	params->FedFsNsdbParams_u.secData.secData_len = size;
+	params->FedFsNsdbParams_u.secData.secData_len = len;
 	params->FedFsNsdbParams_u.secData.secData_val = buf;
 	return true;
 }
@@ -256,6 +236,8 @@  main(int argc, char **argv)
 		fedfs_set_nsdb_params_usage(progname);
 	}
 
+	nsdb_connsec_crypto_startup();
+
 	for (seconds = FEDFS_DELAY_MIN_SECS;; seconds = fedfs_delay(seconds)) {
 		status = fedfs_set_nsdb_params_call(hostname, nettype,
 						nsdbname, nsdbport, certfile);
@@ -266,5 +248,7 @@  main(int argc, char **argv)
 		if (sleep(seconds) != 0)
 			break;
 	}
+
+	nsdb_connsec_crypto_shutdown();
 	return (int)status;
 }
diff --git a/src/fedfsd/main.c b/src/fedfsd/main.c
index 59cb12a..7e481f6 100644
--- a/src/fedfsd/main.c
+++ b/src/fedfsd/main.c
@@ -232,10 +232,14 @@  int main(int argc, char **argv)
 	xlog(L_NOTICE, "Version " VERSION " (built %s at %s) starting",
 			__DATE__, __TIME__);
 
+	nsdb_connsec_crypto_startup();
+
 	/* Normally doesn't return */
 	fedfsd_svc_create("fedfs", FEDFS_PROG, FEDFS_V1,
 			fedfsd_dispatch_1, listen_port);
 
+	nsdb_connsec_crypto_shutdown();
+
 	xlog(L_WARNING, "Exiting unexpectedly");
 	exit(EXIT_FAILURE);
 }
diff --git a/src/fedfsd/svc.c b/src/fedfsd/svc.c
index e3c9bc4..861e92f 100644
--- a/src/fedfsd/svc.c
+++ b/src/fedfsd/svc.c
@@ -984,11 +984,11 @@  fedfsd_test_nsdb(const char *hostname, unsigned short port)
 static void
 fedfsd_svc_set_nsdb_params_1(SVCXPRT *xprt)
 {
-	struct fedfs_secdata secdata;
 	FedFsSetNsdbParamsArgs args;
 	char *hostname = NULL;
 	unsigned short port;
 	FedFsStatus result;
+	nsdb_t host;
 
 	memset(&args, 0, sizeof(args));
 	if (!svc_getargs(xprt, (xdrproc_t)xdr_FedFsSetNsdbParamsArgs, (caddr_t)&args)) {
@@ -1001,28 +1001,44 @@  fedfsd_svc_set_nsdb_params_1(SVCXPRT *xprt)
 	if (result != FEDFS_OK)
 		goto out;
 
-	result = fedfsd_test_nsdb(hostname, port);
-	if (result != FEDFS_OK)
+	result = nsdb_lookup_nsdb(hostname, port, &host);
+	switch (result) {
+	case FEDFS_OK:
+		nsdb_free_nsdb(host);
+		break;
+	case FEDFS_ERR_NSDB_PARAMS:
+		result = fedfsd_test_nsdb(hostname, port);
+		if (result != FEDFS_OK)
+			goto out;
+		result = nsdb_create_nsdb(hostname, port);
+		if (result != FEDFS_OK) {
+			xlog(L_ERROR, "Failed to create entry for %s:%u in "
+				"local NSDB connection parameter database: %s",
+				hostname, port,
+				nsdb_display_fedfsstatus(result));
+			goto out;
+		}
+		break;
+	default:
+		xlog(L_ERROR, "Failed to access local NSDB "
+			"connection parameter dataase: %s",
+			nsdb_display_fedfsstatus(result));
 		goto out;
+	}
 
 	switch (args.params.secType) {
 	case FEDFS_SEC_NONE:
-		secdata.len = 0;
-		secdata.data = "";
+		result = nsdb_connsec_set_none(hostname, port);
 		break;
 	case FEDFS_SEC_TLS:
-		secdata.len =
-			args.params.FedFsNsdbParams_u.secData.secData_len;
-		secdata.data =
-			args.params.FedFsNsdbParams_u.secData.secData_val;
+		result = nsdb_connsec_set_tls_buf(hostname, port,
+			args.params.FedFsNsdbParams_u.secData.secData_val,
+			args.params.FedFsNsdbParams_u.secData.secData_len);
 		break;
 	default:
-		result = FEDFS_ERR_BADXDR;
+		result = FEDFS_ERR_INVAL;
 		goto out;
 	}
-	secdata.type = args.params.secType;
-
-	result = nsdb_update_nsdb(hostname, port, &secdata);
 
 out:
 	xlog(D_CALL, "%s: Replying with %s",