From 18d307e98ebfe6d376ab039a73dc285190b717a0 Mon Sep 17 00:00:00 2001 From: Richard Levitte Date: Mon, 26 Aug 2019 22:09:27 +0200 Subject: [PATCH] openssl provider: New sub-command, for provider discovery This command is somewhat similar to 'openssl engine', but displays what it can about the given providers. Reviewed-by: Shane Lontis (Merged from https://github.com/openssl/openssl/pull/9697) --- apps/build.info | 2 +- apps/progs.c | 1 + apps/progs.h | 2 + apps/provider.c | 288 ++++++++++++++++++++++++++++++++ doc/man1/provider.pod | 57 +++++++ test/recipes/20-test_provider.t | 47 ++++++ 6 files changed, 396 insertions(+), 1 deletion(-) create mode 100644 apps/provider.c create mode 100644 doc/man1/provider.pod create mode 100644 test/recipes/20-test_provider.t diff --git a/apps/build.info b/apps/build.info index 9732dfb9ca..aa2c6a302d 100644 --- a/apps/build.info +++ b/apps/build.info @@ -27,7 +27,7 @@ $OPENSSLSRC={- pkcs8.c pkey.c pkeyparam.c pkeyutl.c prime.c rand.c req.c rsa.c rsautl.c s_client.c s_server.c s_time.c sess_id.c smime.c speed.c spkac.c srp.c ts.c verify.c version.c x509.c rehash.c storeutl.c - list.c info.c fipsinstall.c); + list.c info.c provider.c fipsinstall.c); join(' ', @opensslsrc); -} # Source for libapps $LIBAPPSSRC=apps.c apps_ui.c opt.c fmt.c s_cb.c s_socket.c app_rand.c \ diff --git a/apps/progs.c b/apps/progs.c index 879e6298bb..721e8cbf33 100644 --- a/apps/progs.c +++ b/apps/progs.c @@ -71,6 +71,7 @@ FUNCTION functions[] = { {FT_general, "pkeyparam", pkeyparam_main, pkeyparam_options}, {FT_general, "pkeyutl", pkeyutl_main, pkeyutl_options}, {FT_general, "prime", prime_main, prime_options}, + {FT_general, "provider", provider_main, provider_options}, {FT_general, "rand", rand_main, rand_options}, {FT_general, "rehash", rehash_main, rehash_options}, {FT_general, "req", req_main, req_options}, diff --git a/apps/progs.h b/apps/progs.h index 675a66ca04..deb9549fb6 100644 --- a/apps/progs.h +++ b/apps/progs.h @@ -46,6 +46,7 @@ extern int pkey_main(int argc, char *argv[]); extern int pkeyparam_main(int argc, char *argv[]); extern int pkeyutl_main(int argc, char *argv[]); extern int prime_main(int argc, char *argv[]); +extern int provider_main(int argc, char *argv[]); extern int rand_main(int argc, char *argv[]); extern int rehash_main(int argc, char *argv[]); extern int req_main(int argc, char *argv[]); @@ -99,6 +100,7 @@ extern const OPTIONS pkey_options[]; extern const OPTIONS pkeyparam_options[]; extern const OPTIONS pkeyutl_options[]; extern const OPTIONS prime_options[]; +extern const OPTIONS provider_options[]; extern const OPTIONS rand_options[]; extern const OPTIONS rehash_options[]; extern const OPTIONS req_options[]; diff --git a/apps/provider.c b/apps/provider.c new file mode 100644 index 0000000000..33f194c332 --- /dev/null +++ b/apps/provider.c @@ -0,0 +1,288 @@ +/* + * Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include + +#include "apps.h" +#include "app_params.h" +#include "progs.h" +#include +#include +#include +#include +#include +#include + +typedef enum OPTION_choice { + OPT_ERR = -1, OPT_EOF = 0, OPT_HELP, + OPT_V = 100, OPT_VV, OPT_VVV +} OPTION_CHOICE; + +const OPTIONS provider_options[] = { + {OPT_HELP_STR, 1, '-', "Usage: %s [options] provider...\n"}, + {OPT_HELP_STR, 1, '-', " provider... Providers to load\n"}, + {"help", OPT_HELP, '-', "Display this summary"}, + {"v", OPT_V, '-', "List the algorithm names of specified provider"}, + {"vv", OPT_VV, '-', "List the algorithm names of specified providers,"}, + {OPT_MORE_STR, 0, '-', "categorised by operation type"}, + {"vvv", OPT_VVV, '-', "List the algorithm names of specified provider"}, + {OPT_MORE_STR, 0, '-', "one at a time, and list all known parameters"}, + {NULL} +}; + +typedef struct info_st INFO; +typedef struct meta_st META; + +struct info_st { + const char *name; + void *method; + const OSSL_PARAM *gettable_params; + const OSSL_PARAM *gettable_ctx_params; + const OSSL_PARAM *settable_ctx_params; +}; + +struct meta_st { + int first; /* For prints */ + int total; + int indent; + int subindent; + int verbose; + const char *label; + OSSL_PROVIDER *prov; + void (*fn)(META *meta, INFO *info); +}; + +static void print_caps(META *meta, INFO *info) +{ + switch (meta->verbose) { + case 1: + BIO_printf(bio_out, meta->first ? "%s" : " %s", info->name); + break; + case 2: + if (meta->first) { + if (meta->total > 0) + BIO_printf(bio_out, "\n"); + BIO_printf(bio_out, "%*s%ss:", meta->indent, " ", meta->label); + } + BIO_printf(bio_out, " %s", info->name); + break; + case 3: + default: + BIO_printf(bio_out, "%*s%s %s\n", meta->indent, " ", meta->label, + info->name); + print_param_types("retrievable algorithm parameters", + info->gettable_params, meta->subindent); + print_param_types("retrievable operation parameters", + info->gettable_ctx_params, meta->subindent); + print_param_types("settable operation parameters", + info->settable_ctx_params, meta->subindent); + break; + } + meta->first = 0; +} + +static void do_method(void *method, const char *name, + const OSSL_PARAM *gettable_params, + const OSSL_PARAM *gettable_ctx_params, + const OSSL_PARAM *settable_ctx_params, + META *meta) +{ + INFO info; + + info.name = name; + info.method = method; + info.gettable_params = gettable_params; + info.gettable_ctx_params = gettable_ctx_params; + info.settable_ctx_params = settable_ctx_params; + meta->fn(meta, &info); + meta->total++; +} + +static void do_cipher(EVP_CIPHER *cipher, void *meta) +{ + do_method(cipher, EVP_CIPHER_name(cipher), + EVP_CIPHER_gettable_params(cipher), + EVP_CIPHER_CTX_gettable_params(cipher), + EVP_CIPHER_CTX_settable_params(cipher), + meta); +} + +static void do_digest(EVP_MD *digest, void *meta) +{ + do_method(digest, EVP_MD_name(digest), + EVP_MD_gettable_params(digest), + EVP_MD_CTX_gettable_params(digest), + EVP_MD_CTX_settable_params(digest), + meta); +} + +static void do_mac(EVP_MAC *mac, void *meta) +{ + do_method(mac, EVP_MAC_name(mac), + EVP_MAC_gettable_params(mac), + EVP_MAC_CTX_gettable_params(mac), + EVP_MAC_CTX_settable_params(mac), + meta); +} + +/* + * TODO(3.0) Enable when KEYMGMT and KEYEXCH have gettables and settables + */ +#if 0 +static void do_keymgmt(EVP_KEYMGMT *keymgmt, void *meta) +{ + do_method(keymgmt, EVP_KEYMGMT_name(keymgmt), + EVP_KEYMGMT_gettable_params(keymgmt), + EVP_KEYMGMT_gettable_ctx_params(keymgmt), + EVP_KEYMGMT_settable_ctx_params(keymgmt), + meta); +} + +static void do_keyexch(EVP_KEYEXCH *keyexch, void *meta) +{ + do_method(keyexch, EVP_KEYEXCH_name(keyexch), + EVP_KEYEXCH_gettable_params(keyexch), + EVP_KEYEXCH_gettable_ctx_params(keyexch), + EVP_KEYEXCH_settable_ctx_params(keyexch), + meta); +} +#endif + +int provider_main(int argc, char **argv) +{ + int ret = 1, i; + int verbose = 0; + STACK_OF(OPENSSL_CSTRING) *providers = sk_OPENSSL_CSTRING_new_null(); + OPTION_CHOICE o; + char *prog; + + prog = opt_init(argc, argv, provider_options); + while ((o = opt_next()) != OPT_EOF) { + switch (o) { + case OPT_EOF: + case OPT_ERR: + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + case OPT_HELP: + opt_help(provider_options); + ret = 0; + goto end; + case OPT_VVV: + case OPT_VV: + case OPT_V: + /* Convert to an integer from one to four. */ + i = (int)(o - OPT_V) + 1; + if (verbose < i) + verbose = i; + break; + } + } + + /* Allow any trailing parameters as provider names. */ + argc = opt_num_rest(); + argv = opt_rest(); + for ( ; *argv; argv++) { + if (**argv == '-') { + BIO_printf(bio_err, "%s: Cannot mix flags and provider names.\n", + prog); + BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); + goto end; + } + sk_OPENSSL_CSTRING_push(providers, *argv); + } + + ret = 0; + for (i = 0; i < sk_OPENSSL_CSTRING_num(providers); i++) { + const char *name = sk_OPENSSL_CSTRING_value(providers, i); + OSSL_PROVIDER *prov = OSSL_PROVIDER_load(NULL, name); + + if (prov != NULL) { + BIO_printf(bio_out, verbose == 0 ? "%s\n" : "[ %s ]\n", name); + + if (verbose > 0) { + META data; + + data.total = 0; + data.first = 1; + data.verbose = verbose; + data.prov = prov; + data.fn = print_caps; + + switch (verbose) { + case 1: + BIO_printf(bio_out, " "); + break; + case 2: + data.indent = 4; + break; + case 3: + default: + data.indent = 4; + data.subindent = 10; + break; + } + + if (verbose > 1) { + data.first = 1; + data.label = "Cipher"; + } + EVP_CIPHER_do_all_ex(NULL, do_cipher, &data); + if (verbose > 1) { + data.first = 1; + data.label = "Digest"; + } + EVP_MD_do_all_ex(NULL, do_digest, &data); + if (verbose > 1) { + data.first = 1; + data.label = "MAC"; + } + EVP_MAC_do_all_ex(NULL, do_mac, &data); + +/* + * TODO(3.0) Enable when KEYMGMT and KEYEXCH have do_all_ex functions + */ +#if 0 + if (verbose > 1) { + data.first = 1; + data.label = "Key manager"; + } + EVP_KEYMGMT_do_all_ex(NULL, do_keymgmt, &data); + if (verbose > 1) { + data.first = 1; + data.label = "Key exchange"; + } + EVP_KEYEXCH_do_all_ex(NULL, do_keyexch, &data); +#endif + + switch (verbose) { + default: + break; + case 2: + case 1: + BIO_printf(bio_out, "\n"); + break; + } + } + OSSL_PROVIDER_unload(prov); + } else { + ERR_print_errors(bio_err); + ret = 1; + /* + * Just because one provider module failed, there's no reason to + * stop, if there are more to try. + */ + } + } + + end: + + ERR_print_errors(bio_err); + sk_OPENSSL_CSTRING_free(providers); + return ret; +} diff --git a/doc/man1/provider.pod b/doc/man1/provider.pod new file mode 100644 index 0000000000..6c71d1cd64 --- /dev/null +++ b/doc/man1/provider.pod @@ -0,0 +1,57 @@ +=pod + +=head1 NAME + +openssl-provider - load and query providers + +=head1 SYNOPSIS + +B +[B<-v>] +[B<-vv>] +[B<-vvv>] +[ I ] + +=head1 DESCRIPTION + +The B command is used to query the capabilities of the specified +I's. + +=head1 OPTIONS + +=over 4 + +=item B<-v> B<-vv> B<-vvv> + +Provides information about each specified provider. +The first flag lists the names of all algorithms each provider +implements; the second lists them by category; the third adds +information on what parameters each of them can handle. + +=back + +=head1 ENVIRONMENT + +=over 4 + +=item B + +The path to the modules directory, where one can expect provider +modules to be located. + +=back + +=head1 SEE ALSO + +L + +=head1 COPYRIGHT + +Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. + +Licensed under the Apache License 2.0 (the "License"). You may not use +this file except in compliance with the License. You can obtain a copy +in the file LICENSE in the source distribution or at +L. + +=cut diff --git a/test/recipes/20-test_provider.t b/test/recipes/20-test_provider.t new file mode 100644 index 0000000000..0cfd81d09b --- /dev/null +++ b/test/recipes/20-test_provider.t @@ -0,0 +1,47 @@ +#! /usr/bin/env perl +# Copyright 2019 The OpenSSL Project Authors. All Rights Reserved. +# +# Licensed under the Apache License 2.0 (the "License"). You may not use +# this file except in compliance with the License. You can obtain a copy +# in the file LICENSE in the source distribution or at +# https://www.openssl.org/source/license.html + + +use strict; +use warnings; + +use OpenSSL::Test; + +setup("test_provider"); + +plan tests => 7; + + SKIP: { + skip "No default provider?", 6 + unless ok(run(app([qw(openssl provider default)])), + "try running 'openssl provider default'"); + + my $prev = 2; # The amount of lines from -v + my @checks = qw( -v -vv -vvv ); + my %op = ( -v => '==', + -vv => '>', + -vvv => '>' ); + my $i = 0; + + foreach (@checks) { + my @cmd = ('openssl', 'provider', $_, 'default'); + my @lines = ( map { (my $x = $_) =~ s|\R$||; $x } + run(app([@cmd]), capture => 1) ); + + my $curr = scalar @lines; + my $cmp = "$curr $op{$_} $prev"; + + ok(eval $cmp, + "'openssl provider $_ default' line count $op{$_} $prev"); + ok($lines[0] eq '[ default ]', + "'openssl provider -v default' first line is '[ default ]'"); + + $prev = $curr; + } +} +