aboutsummaryrefslogtreecommitdiff
path: root/openssl/client
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2018-10-15 21:08:04 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2018-10-17 15:02:42 +0300
commitde91921561092689369b56c54950474e0a86e66f (patch)
treea9949058021d911db1106b1a2e4d9e0e9281de16 /openssl/client
parentfb65c93daaf369157bd712f2c4c20161c4840b94 (diff)
Add implementation
Diffstat (limited to 'openssl/client')
-rw-r--r--openssl/client/client.cxx191
-rw-r--r--openssl/client/options.cli109
2 files changed, 300 insertions, 0 deletions
diff --git a/openssl/client/client.cxx b/openssl/client/client.cxx
new file mode 100644
index 0000000..42c4f92
--- /dev/null
+++ b/openssl/client/client.cxx
@@ -0,0 +1,191 @@
+// file : openssl/client/client.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <iostream> // cin, cout
+
+#include <libbutl/pager.mxx>
+
+#include <openssl/protocol.hxx>
+#include <openssl/diagnostics.hxx>
+
+#include <openssl/client/options.hxx>
+
+namespace openssl
+{
+ namespace client
+ {
+ using namespace std;
+ using namespace butl;
+
+ static int
+ main (int argc, char* argv[])
+ try
+ {
+ // Parse the command/options.
+ //
+ string cmd;
+ options ops;
+
+ int i (1);
+ if (argc > i && *argv[i] != '-')
+ cmd = argv[i++];
+
+ cli::argv_scanner scan (i, argc, argv);
+
+ ops.parse (scan);
+
+ // Version.
+ //
+ if (ops.version ())
+ {
+ cout << "openssl-client " << OPENSSL_AGENT_VERSION_ID << endl
+ << "libbutl " << LIBBUTL_VERSION_ID << endl
+ << "Copyright (c) 2014-2018 Code Synthesis Ltd" << endl
+ << "This is free software released under the MIT license."
+ << endl;
+
+ return 0;
+ }
+
+ // Help.
+ //
+ if (ops.help ())
+ {
+ pager p ("openssl-client help", false);
+ print_openssl_client_usage (p.stream ());
+
+ // If the pager failed, assume it has issued some diagnostics.
+ //
+ return p.wait () ? 0 : 1;
+ }
+
+ if (cmd != "rsautl")
+ fail << "openssl-client command expected" <<
+ info << "run '" << argv[0] << " --help' for more information";
+
+ if (!ops.sign ())
+ fail << "-sign option is required";
+
+ if (!ops.keyform_specified ())
+ fail << "-keyform option is required";
+
+ if (ops.keyform () != "engine")
+ fail << "invalid value '" << ops.keyform ()
+ << "' for option -keyform";
+
+ if (!ops.engine_specified ())
+ fail << "-engine option is required";
+
+ if (ops.engine () != "pkcs11")
+ fail << "invalid value '" << ops.engine () << "' for option -engine";
+
+ if (!ops.inkey_specified ())
+ fail << "-inkey option is required";
+
+ // Obtain the agent socket path.
+ //
+ path sock_path;
+
+ try
+ {
+ optional<string> p (getenv ("OPENSSL_AGENT_PKCS11_SOCK"));
+
+ if (!p)
+ fail << "OPENSSL_AGENT_PKCS11_SOCK environment variable is not set";
+
+ sock_path = path (move (*p));
+ }
+ catch (const invalid_path& e)
+ {
+ fail << "invalid OPENSSL_AGENT_PKCS11_SOCK environment variable "
+ << "value '" << e.path << "'";
+ }
+
+ // Read the data to sign from stdin.
+ //
+ vector<char> data;
+
+ try
+ {
+ stdin_fdmode (fdstream_mode::binary);
+
+ cin.exceptions (ostream::badbit | ostream::failbit);
+
+ data = vector<char> (istreambuf_iterator<char> (cin),
+ istreambuf_iterator<char> ());
+ }
+ catch (const io_error&)
+ {
+ fail << "unable to read data from stdin";
+ }
+
+ // Sign the data.
+ //
+ vector<char> signature;
+
+ try
+ {
+ auto_fd sock (connect (sock_path));
+
+ ifdstream is (fddup (sock.get ()));
+ ofdstream os (move (sock));
+
+ string s (ops.simulate_specified ()
+ ? to_string (ops.simulate ())
+ : "");
+
+ os << request ("sign",
+ strings ({ops.inkey (), move (s)}),
+ move (data));
+ os.close ();
+
+ response r;
+ is >> r;
+
+ if (r.status != 0)
+ {
+ text << r.error;
+ return r.status;
+ }
+
+ signature = move (r.output);
+ }
+ catch (const io_error&)
+ {
+ fail << "unable to communicate with openssl-agent-pkcs11";
+ }
+
+ // Write it out.
+ //
+ try
+ {
+ stdout_fdmode (fdstream_mode::binary);
+
+ cout.exceptions (ostream::badbit | ostream::failbit);
+ cout.write (signature.data (), signature.size ());
+ }
+ catch (const io_error&)
+ {
+ fail << "error: unable to write signature to stdout";
+ }
+
+ return 0;
+ }
+ catch (const failed&)
+ {
+ return 1; // Diagnostics has already been issued.
+ }
+ catch (const cli::exception& e)
+ {
+ error << e;
+ return 1;
+ }
+ }
+}
+
+int
+main (int argc, char* argv[])
+{
+ return openssl::client::main (argc, argv);
+}
diff --git a/openssl/client/options.cli b/openssl/client/options.cli
new file mode 100644
index 0000000..4a2872a
--- /dev/null
+++ b/openssl/client/options.cli
@@ -0,0 +1,109 @@
+// file : openssl/client/options.cli
+// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+include <openssl/options.cli>;
+
+"\section=1"
+"\name=openssl-client"
+"\summary=OpenSSL client"
+
+namespace openssl
+{
+ namespace client
+ {
+ {
+ "<options>",
+
+ "
+ \h|SYNOPSIS|
+
+ \c{\b{openssl-client --help}\n
+ \b{openssl-client --version}\n
+ \b{openssl-client} rsautl [<options>]}
+
+ \h|DESCRIPTION|
+
+ The \cb{rsautl} command is a drop-in replacement for the
+ \cb{openssl-rsautl(1)} cryptographic operations. Instead of performing
+ the operations itself, it forwards the request to an OpenSSL key agent
+ that keeps the private key unlocked for the session.
+
+ Currently, data signing with a private key stored in a \cb{PKCS#11}
+ token is the only supported arrangement. This limits the
+ \cb{openssl-rsautl(1)} options and values to the following usage:
+
+ \
+ $ openssl-client rsautl -sign -keyform engine -engine pkcs11 -inkey pkcs11:...
+ \
+
+ This command reads data from \cb{stdin}, asks
+ \cb{openssl-agent-pkcs11(1)} to sign it using the specified unlocked
+ private key, and prints the resulting signature to \cb{stdout}.
+
+ The command can be simulated without actually performing any
+ cryptographic operations. If the \cb{--simulate} option is specified
+ with the \cb{success} outcome, then the command prints a dummy signature
+ produced by the agent and exits with zero status. The \cb{failure}
+ outcome causes it to print the diagnostics to \cb{stderr} and exit with
+ non-zero status. This mode is mostly useful for OpenSSL key agents
+ testing.
+ "
+ }
+
+ class options
+ {
+ "\h|OPTIONS|"
+
+ bool --help {"Print usage information and exit."}
+ bool --version {"Print version and exit."}
+
+ bool -sign
+ {
+ "Sign data read from \cb{stdin}."
+ }
+
+ string -keyform
+ {
+ "<form>",
+ "Private key format. The only supported format is \cb{engine}."
+ }
+
+ string -engine
+ {
+ "<engine>",
+ "Engine to use for the cryptographic operation. The only supported
+ engine is \cb{pkcs11}."
+ }
+
+ string -inkey
+ {
+ "<location>",
+ "Private key location. Its format (file path, URL, etc) depends on the
+ engine used. For the \cb{pkcs11} engine it should be a \cb{PKCS#11}
+ URL."
+ }
+
+ simulate_outcome --simulate
+ {
+ "<outcome>",
+ "Ask the agent to simulate the cryptographic operation instead of
+ performing it for real."
+ }
+ };
+
+ "
+ \h|ENVIRONMENT|
+
+ If \cb{-engine} is \cb{pkcs11}, then the \cb{OPENSSL_AGENT_PKCS11_SOCK}
+ environment variable should be set to the Unix-domain socket of the
+ \cb{openssl-agent-pkcs11(1)} daemon.
+ "
+
+ "
+ \h|EXIT STATUS|
+
+ Non-zero exit status is returned in case of an error.
+ "
+ }
+}