From patchwork Wed Jan 16 21:53:15 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: [10/11] ADMIN: Transform certificate material properly Date: Wed, 16 Jan 2013 11:53:15 -0000 From: Chuck Lever X-Patchwork-Id: 213078 Message-Id: <20130116215315.21683.49069.stgit@seurat.1015granger.net> To: fedfs-utils-devel@oss.oracle.com 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 --- 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 --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",