From 95ff8f359cfc2189bd4d7e02e15373027d2bda32 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Mon, 29 May 2017 20:05:54 +0300 Subject: Implement openssl process --- libbutl/buildfile | 1 + libbutl/curl.cxx | 2 +- libbutl/curl.ixx | 3 +- libbutl/curl.txx | 2 +- libbutl/fdstream.hxx | 7 ++ libbutl/openssl.cxx | 96 +++++++++++++++++++++++++++ libbutl/openssl.hxx | 165 +++++++++++++++++++++++++++++++++++++++++++++++ libbutl/openssl.ixx | 31 +++++++++ libbutl/openssl.txx | 56 ++++++++++++++++ libbutl/sendmail.ixx | 1 + tests/buildfile | 3 +- tests/openssl/buildfile | 7 ++ tests/openssl/driver.cxx | 35 ++++++++++ tests/openssl/testscript | 5 ++ 14 files changed, 409 insertions(+), 5 deletions(-) create mode 100644 libbutl/openssl.cxx create mode 100644 libbutl/openssl.hxx create mode 100644 libbutl/openssl.ixx create mode 100644 libbutl/openssl.txx create mode 100644 tests/openssl/buildfile create mode 100644 tests/openssl/driver.cxx create mode 100644 tests/openssl/testscript diff --git a/libbutl/buildfile b/libbutl/buildfile index 473c1d5..d5f492b 100644 --- a/libbutl/buildfile +++ b/libbutl/buildfile @@ -15,6 +15,7 @@ lib{butl}: \ {hxx cxx}{ manifest-parser } \ {hxx cxx}{ manifest-serializer } \ {hxx }{ multi-index } \ + {hxx ixx txx cxx}{ openssl } \ {hxx }{ optional } \ {hxx cxx}{ pager } \ {hxx ixx txx cxx}{ path } \ diff --git a/libbutl/curl.cxx b/libbutl/curl.cxx index daa1fd3..a04d52e 100644 --- a/libbutl/curl.cxx +++ b/libbutl/curl.cxx @@ -4,7 +4,7 @@ #include -#include // move(), forward() +#include // move() #include // invalid_argument #include // casecmp() diff --git a/libbutl/curl.ixx b/libbutl/curl.ixx index fcc1bab..204dfa1 100644 --- a/libbutl/curl.ixx +++ b/libbutl/curl.ixx @@ -2,7 +2,8 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include // move(), forward() +#include // size_t +#include // forward() namespace butl { diff --git a/libbutl/curl.txx b/libbutl/curl.txx index 5fd81dc..f6f0e96 100644 --- a/libbutl/curl.txx +++ b/libbutl/curl.txx @@ -2,7 +2,7 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file -#include // move(), forward() +#include // forward() #include // invalid_argument namespace butl diff --git a/libbutl/fdstream.hxx b/libbutl/fdstream.hxx index dd33263..a8f203d 100644 --- a/libbutl/fdstream.hxx +++ b/libbutl/fdstream.hxx @@ -387,6 +387,13 @@ namespace butl void open (auto_fd&& fd) {buf_.open (std::move (fd)); clear ();} + void + open (auto_fd&& fd, fdstream_mode m) + { + open (std::move (fd)); + skip_ = (m & fdstream_mode::skip) == fdstream_mode::skip; + } + void close (); auto_fd release (); // Note: no skipping. bool is_open () const {return buf_.is_open ();} diff --git a/libbutl/openssl.cxx b/libbutl/openssl.cxx new file mode 100644 index 0000000..aa4e720 --- /dev/null +++ b/libbutl/openssl.cxx @@ -0,0 +1,96 @@ +// file : libbutl/openssl.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include + +#include // move() + +using namespace std; + +namespace butl +{ + int openssl:: + map_in (nullfd_t, io_data& d) + { + d.pipe.in = fdnull (); // /dev/null + return d.pipe.in.get (); + } + + int openssl:: + map_in (const path& f, io_data& d) + { + if (f.string () == "-") + { + // Note: no need for any options, openssl reads from stdin by default. + // + d.pipe = fdopen_pipe (fdopen_mode::binary); + out.open (move (d.pipe.out)); + } + else + { + d.options.push_back ("-in"); + d.options.push_back (f.string ().c_str ()); + d.pipe.in = fdnull (); // /dev/null + } + + return d.pipe.in.get (); + } + + int openssl:: + map_in (fdstream_mode m, io_data& d) + { + assert (m == fdstream_mode::text || m == fdstream_mode::binary); + + // Note: no need for any options, openssl reads from stdin by default. + // + d.pipe = fdopen_pipe (m == fdstream_mode::binary + ? fdopen_mode::binary + : fdopen_mode::none); + + out.open (move (d.pipe.out)); + return d.pipe.in.get (); + } + + int openssl:: + map_out (nullfd_t, io_data& d) + { + d.pipe.out = fdnull (); + return d.pipe.out.get (); // /dev/null + } + + int openssl:: + map_out (const path& f, io_data& d) + { + if (f.string () == "-") + { + // Note: no need for any options, openssl writes to stdout by default. + // + d.pipe = fdopen_pipe (fdopen_mode::binary); + in.open (move (d.pipe.in), fdstream_mode::skip); + } + else + { + d.options.push_back ("-out"); + d.options.push_back (f.string ().c_str ()); + d.pipe.out = fdnull (); // /dev/null + } + + return d.pipe.out.get (); + } + + int openssl:: + map_out (fdstream_mode m, io_data& d) + { + assert (m == fdstream_mode::text || m == fdstream_mode::binary); + + // Note: no need for any options, openssl writes to stdout by default. + // + d.pipe = fdopen_pipe (m == fdstream_mode::binary + ? fdopen_mode::binary + : fdopen_mode::none); + + in.open (move (d.pipe.in), fdstream_mode::skip); + return d.pipe.out.get (); + } +} diff --git a/libbutl/openssl.hxx b/libbutl/openssl.hxx new file mode 100644 index 0000000..2f54f93 --- /dev/null +++ b/libbutl/openssl.hxx @@ -0,0 +1,165 @@ +// file : libbutl/openssl.hxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef LIBBUTL_OPENSSL_HXX +#define LIBBUTL_OPENSSL_HXX + +#include +#include + +#include + +#include +#include +#include + +namespace butl +{ + // Perform a crypto operation using the openssl(1) program. Throw + // process_error and io_error (both derive from system_error) in case of + // errors. + // + // The I (in) and O (out) can be of the following types/values: + // + // nullfd Signal that no input/output is expected. + // + // path Read input/write output from/to a file. If the special "-" + // value is used, then instead input is connected to the + // openssl::out ofdstream member and output -- to the + // openssl::in ifdstream member. Note that the argument type + // should be path, not string (i.e., pass path("-")). Also + // note that the streams are opened in the binary mode. To + // change that, use fdstream_mode::text instead (see below). + // + // fdstream_mode Only text and binary values are meaningful. Same as + // path("-"), but also specifies the translation mode. + // + // other Forwarded as is to process_start(). Normally either int or + // auto_fd. + // + // For example: + // + // openssl os (path ("key.pub.pem"), // Read key from file, + // path ("-"), // Write result to openssl::in. + // 2, + // "openssl", "pkey", + // "-pubin", "-outform", "DER"); + // + // Typical usage: + // + // try + // { + // openssl os (nullfd, // No input expected. + // path ("-"), // Output to openssl::in. + // 2, // Diagnostics to stderr. + // path ("openssl"), // Program path. + // "rand", // Command. + // 64); // Command options. + // + // vector r (os.in.read_binary ()); + // os.in.close (); + // + // if (!os.wait ()) + // ... // openssl returned non-zero status. + // } + // catch (const system_error& e) + // { + // cerr << "openssl error: " << e << endl; + // } + // + // Notes: + // + // 1. If opened, in stream is in the skip mode (see fdstream_mode). + // + // 2. If opened, in/out must be explicitly closed before calling wait(). + // + // 3. Normally the order of options is not important (unless they override + // each other). However, openssl 1.0.1 seems to have bugs in that + // department (that were apparently fixed in 1.0.2). To work around these + // bugs pass user-supplied options first. + // + class LIBBUTL_EXPORT openssl: public process + { + public: + ifdstream in; + ofdstream out; + + template + openssl (I&& in, + O&& out, + E&& err, + const P& program, + const std::string& command, + A&&... options); + + // Version with the command line callback (see process_run() for details). + // + template + openssl (const C&, + I&& in, + O&& out, + E&& err, + const P& program, + const std::string& command, + A&&... options); + + private: + template + struct is_other + { + using type = typename std::remove_reference< + typename std::remove_cv::type>::type; + + static const bool value = !(std::is_same::value || + std::is_same::value || + std::is_same::value); + }; + + struct io_data + { + fdpipe pipe; + small_vector options; + }; + + int + map_in (nullfd_t, io_data&); + + int + map_in (const path&, io_data&); + + int + map_in (fdstream_mode, io_data&); + + template + typename std::enable_if::value, I>::type + map_in (I&&, io_data&); + + int + map_out (nullfd_t, io_data&); + + int + map_out (const path&, io_data&); + + int + map_out (fdstream_mode, io_data&); + + template + typename std::enable_if::value, O>::type + map_out (O&&, io_data&); + }; +} + +#include +#include + +#endif // LIBBUTL_OPENSSL_HXX diff --git a/libbutl/openssl.ixx b/libbutl/openssl.ixx new file mode 100644 index 0000000..2af5029 --- /dev/null +++ b/libbutl/openssl.ixx @@ -0,0 +1,31 @@ +// file : libbutl/openssl.ixx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include // size_t +#include // move(), forward() + +namespace butl +{ + template + inline openssl:: + openssl (I&& in, + O&& out, + E&& err, + const P& program, + const std::string& command, + A&&... options) + : openssl ([] (const char* [], std::size_t) {}, + std::forward (in), + std::forward (out), + std::forward (err), + program, + command, + std::forward (options)...) + { + } +} diff --git a/libbutl/openssl.txx b/libbutl/openssl.txx new file mode 100644 index 0000000..aaaa239 --- /dev/null +++ b/libbutl/openssl.txx @@ -0,0 +1,56 @@ +// file : libbutl/openssl.txx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include // move(), forward() + +namespace butl +{ + template + typename std::enable_if::value, I>::type openssl:: + map_in (I&& in, io_data&) + { + return std::forward (in); + } + + template + typename std::enable_if::value, O>::type openssl:: + map_out (O&& out, io_data&) + { + return std::forward (out); + } + + template + openssl:: + openssl (const C& cmdc, + I&& in, + O&& out, + E&& err, + const P& program, + const std::string& command, + A&&... options) + { + io_data in_data; + io_data out_data; + + process& p (*this); + p = process_start ( + cmdc, + map_in (std::forward (in), in_data), + map_out (std::forward (out), out_data), + std::forward (err), + dir_path (), + program, + command, + in_data.options, + out_data.options, + std::forward (options)...); + + // Note: leaving this scope closes any open ends of the pipes in io_data. + } +} diff --git a/libbutl/sendmail.ixx b/libbutl/sendmail.ixx index b88ee20..9746a8b 100644 --- a/libbutl/sendmail.ixx +++ b/libbutl/sendmail.ixx @@ -2,6 +2,7 @@ // copyright : Copyright (c) 2014-2017 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file +#include // size_t #include // move(), forward() namespace butl diff --git a/tests/buildfile b/tests/buildfile index 6761a42..a721517 100644 --- a/tests/buildfile +++ b/tests/buildfile @@ -2,5 +2,4 @@ # copyright : Copyright (c) 2014-2017 Code Synthesis Ltd # license : MIT; see accompanying LICENSE file -./: {*/ -curl/ -sendmail/} - +./: {*/ -curl/ -openssl/ -sendmail/} diff --git a/tests/openssl/buildfile b/tests/openssl/buildfile new file mode 100644 index 0000000..3a3ab49 --- /dev/null +++ b/tests/openssl/buildfile @@ -0,0 +1,7 @@ +# file : tests/openssl/buildfile +# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +exe{driver}: cxx{driver} ../../libbutl/lib{butl} test{testscript} + +include ../../libbutl/ diff --git a/tests/openssl/driver.cxx b/tests/openssl/driver.cxx new file mode 100644 index 0000000..769c77e --- /dev/null +++ b/tests/openssl/driver.cxx @@ -0,0 +1,35 @@ +// file : tests/openssl/driver.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include +#include +#include +#include + +#include +#include // operator<<(ostream, exception) +#include + +using namespace std; +using namespace butl; + +// Usage: argv[0] +// +int +main (int, const char* argv[]) +try +{ + openssl os (nullfd, path ("-"), 2, path ("openssl"), "rand", 128); + + vector r + ((istreambuf_iterator (os.in)), istreambuf_iterator ()); + + os.in.close (); + return os.wait () && r.size () == 128 ? 0 : 1; +} +catch (const system_error& e) +{ + cerr << argv[0] << ": " << e << endl; + return 1; +} diff --git a/tests/openssl/testscript b/tests/openssl/testscript new file mode 100644 index 0000000..f0eef62 --- /dev/null +++ b/tests/openssl/testscript @@ -0,0 +1,5 @@ +# file : tests/openssl/testscript +# copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +# license : MIT; see accompanying LICENSE file + +$* -- cgit v1.1