aboutsummaryrefslogtreecommitdiff
path: root/net/nginx-util/src/nginx-ssl-util.cpp
diff options
context:
space:
mode:
authorPeter Stadler <peter.stadler@student.uibk.ac.at>2020-02-04 13:10:00 +0100
committerPeter Stadler <peter.stadler@student.uibk.ac.at>2020-02-09 13:02:54 +0100
commite247efa254599f0a2cf9178b2ef16b5ac6cc8fb8 (patch)
tree6df72a3c49077843ae491d2619df390c8fd9ea59 /net/nginx-util/src/nginx-ssl-util.cpp
parentfed1b3b11bf9361dbbd158a1018c701d8180e49b (diff)
nginx-util: add tests, clean up and fix issues
Add tests for nginx-ssl-util and nginx-ssl-util-nopcre using (fake)chroot. Clean the code up making nginx-ssl-util a header file. Both changes are for better (future) code quality only. There are minor functional improvements: * fix compiler error of gcc7 by using std=c++17 * fix error if there is no lan/loopback interface * notice instead of error message if there is no default server * add ipv6-prefix-assignment.*.local-address.address for LAN * add CONFLICTS in Makefile for choosing the right version * add cast to release of unique_ptr to avoid warning * add version message to help message Signed-off-by: Peter Stadler <peter.stadler@student.uibk.ac.at>
Diffstat (limited to 'net/nginx-util/src/nginx-ssl-util.cpp')
-rw-r--r--net/nginx-util/src/nginx-ssl-util.cpp660
1 files changed, 0 insertions, 660 deletions
diff --git a/net/nginx-util/src/nginx-ssl-util.cpp b/net/nginx-util/src/nginx-ssl-util.cpp
deleted file mode 100644
index 7b268cbd4..000000000
--- a/net/nginx-util/src/nginx-ssl-util.cpp
+++ /dev/null
@@ -1,660 +0,0 @@
-#include <thread>
-
-#ifdef NO_PCRE
-#include <regex>
-namespace rgx = std;
-#else
-#include "regex-pcre.hpp"
-#endif
-
-#include "nginx-util.hpp"
-#include "px5g-openssl.hpp"
-
-
-#ifndef NO_UBUS
-static constexpr auto UBUS_TIMEOUT = 1000;
-#endif
-
-// once a year:
-static constexpr auto CRON_INTERVAL = std::string_view{"3 3 12 12 *"};
-
-static constexpr auto LAN_SSL_LISTEN =
- std::string_view{"/var/lib/nginx/lan_ssl.listen"};
-
-static constexpr auto LAN_SSL_LISTEN_DEFAULT =
- std::string_view{"/var/lib/nginx/lan_ssl.listen.default"};
-
-static constexpr auto ADD_SSL_FCT = std::string_view{"add_ssl"};
-
-static constexpr auto SSL_SESSION_CACHE_ARG =
- [](const std::string_view & /*name*/) -> std::string
- { return "shared:SSL:32k"; };
-
-static constexpr auto SSL_SESSION_TIMEOUT_ARG = std::string_view{"64m"};
-
-
-using _Line =
- std::array< std::string (*)(const std::string &, const std::string &), 2 >;
-
-class Line {
-
-private:
-
- _Line _line;
-
-public:
-
- explicit Line(const _Line & line) noexcept : _line{line} {}
-
- template<const _Line & ...xn>
- static auto build() noexcept -> Line
- {
- return Line{_Line{
- [](const std::string & p, const std::string & b) -> std::string
- { return (... + xn[0](p, b)); },
- [](const std::string & p, const std::string & b) -> std::string
- { return (... + xn[1](p, b)); }
- }};
- }
-
-
- [[nodiscard]] auto STR(const std::string & param, const std::string & begin)
- const -> std::string
- { return _line[0](param, begin); }
-
-
- [[nodiscard]] auto RGX() const -> rgx::regex
- { return rgx::regex{_line[1]("", "")}; }
-
-};
-
-
-auto get_if_missed(const std::string & conf, const Line & LINE,
- const std::string & val,
- const std::string & indent="\n ", bool compare=true)
- -> std::string;
-
-
-auto delete_if(const std::string & conf, const rgx::regex & rgx,
- const std::string & val="", bool compare=false)
- -> std::string;
-
-
-void add_ssl_directives_to(const std::string & name, bool isdefault);
-
-
-void create_ssl_certificate(const std::string & crtpath,
- const std::string & keypath,
- int days=792);
-
-
-void use_cron_to_recreate_certificate(const std::string & name);
-
-
-void add_ssl_if_needed(const std::string & name);
-
-
-void del_ssl_directives_from(const std::string & name, bool isdefault);
-
-
-void del_ssl(const std::string & name);
-
-
-constexpr auto _begin = _Line{
- [](const std::string & /*param*/, const std::string & begin) -> std::string
- { return begin; },
-
- [](const std::string & /*param*/, const std::string & /*begin*/)
- -> std::string
- { return R"([{;](?:\s*#[^\n]*(?=\n))*(\s*))"; }
-};
-
-
-constexpr auto _space = _Line{
- [](const std::string & /*param*/, const std::string & /*begin*/)
- -> std::string
- { return std::string{" "}; },
-
- [](const std::string & /*param*/, const std::string & /*begin*/)
- -> std::string
- { return R"(\s+)"; }
-};
-
-
-constexpr auto _newline = _Line{
- [](const std::string & /*param*/, const std::string & /*begin*/)
- -> std::string
- { return std::string{"\n"}; },
-
- [](const std::string & /*param*/, const std::string & /*begin*/)
- -> std::string
- { return std::string{"\n"}; }
-};
-
-
-constexpr auto _end = _Line{
- [](const std::string & /*param*/, const std::string & /*begin*/)
- -> std::string
- { return std::string{";"}; },
-
- [](const std::string & /*param*/, const std::string & /*begin*/)
- -> std::string
- { return std::string{R"(\s*;(?:[\t ]*#[^\n]*)?)"}; }
-};
-
-
-template<char clim='\0'>
-constexpr auto _capture = _Line{
- [](const std::string & param, const std::string & /*begin*/) -> std::string
- { return '\'' + param + '\''; },
-
- [](const std::string & /*param*/, const std::string & /*begin*/)
- -> std::string
- {
- const auto lim = clim=='\0' ? std::string{"\\s"} : std::string{clim};
- return std::string{R"(((?:(?:"[^"]*")|(?:[^'")"} +
- lim + "][^" + lim + "]*)|(?:'[^']*'))+)";
- }
-};
-
-
-template<const std::string_view & strptr, char clim='\0'>
-constexpr auto _escape = _Line{
- [](const std::string & /*param*/, const std::string & /*begin*/)
- -> std::string
- {
- return clim=='\0' ?
- std::string{strptr.data()} :
- clim + std::string{strptr.data()} + clim;
- },
-
- [](const std::string & /*param*/, const std::string & /*begin*/)
- -> std::string
- {
- std::string ret{};
- for (char c : strptr) {
- switch(c) {
- case '^': ret += '\\'; [[fallthrough]];
- case '_': [[fallthrough]];
- case '-': ret += c;
- break;
- default:
- if ((isalpha(c)!=0) || (isdigit(c)!=0)) { ret += c; }
- else { ret += std::string{"["}+c+"]"; }
- }
- }
- return "(?:"+ret+"|'"+ret+"'"+"|\""+ret+"\""+")";
- }
-};
-
-
-constexpr std::string_view _server_name = "server_name";
-
-constexpr std::string_view _include = "include";
-
-constexpr std::string_view _ssl_certificate = "ssl_certificate";
-
-constexpr std::string_view _ssl_certificate_key = "ssl_certificate_key";
-
-constexpr std::string_view _ssl_session_cache = "ssl_session_cache";
-
-constexpr std::string_view _ssl_session_timeout = "ssl_session_timeout";
-
-
-// For a compile time regex lib, this must be fixed, use one of these options:
-// * Hand craft or macro concat them (loosing more or less flexibility).
-// * Use Macro concatenation of __VA_ARGS__ with the help of:
-// https://p99.gforge.inria.fr/p99-html/group__preprocessor__for.html
-// * Use constexpr---not available for strings or char * for now---look at lib.
-
-static const auto CRON_CMD = Line::build
- < _space, _escape<NGINX_UTIL>, _space, _escape<ADD_SSL_FCT,'\''>, _space,
- _capture<>, _newline >();
-
-static const auto NGX_SERVER_NAME =
- Line::build<_begin, _escape<_server_name>, _space, _capture<';'>, _end>();
-
-static const auto NGX_INCLUDE_LAN_LISTEN = Line::build
- <_begin, _escape<_include>, _space, _escape<LAN_LISTEN,'\''>, _end>();
-
-static const auto NGX_INCLUDE_LAN_LISTEN_DEFAULT = Line::build
- < _begin, _escape<_include>, _space,
- _escape<LAN_LISTEN_DEFAULT, '\''>, _end >();
-
-static const auto NGX_INCLUDE_LAN_SSL_LISTEN = Line::build
- <_begin, _escape<_include>, _space, _escape<LAN_SSL_LISTEN, '\''>, _end>();
-
-static const auto NGX_INCLUDE_LAN_SSL_LISTEN_DEFAULT = Line::build
- < _begin, _escape<_include>, _space,
- _escape<LAN_SSL_LISTEN_DEFAULT, '\''>, _end >();
-
-static const auto NGX_SSL_CRT = Line::build
- <_begin, _escape<_ssl_certificate>, _space, _capture<';'>, _end>();
-
-static const auto NGX_SSL_KEY = Line::build
- <_begin, _escape<_ssl_certificate_key>, _space, _capture<';'>, _end>();
-
-static const auto NGX_SSL_SESSION_CACHE = Line::build
- <_begin, _escape<_ssl_session_cache>, _space, _capture<';'>, _end>();
-
-static const auto NGX_SSL_SESSION_TIMEOUT = Line::build
- <_begin, _escape<_ssl_session_timeout>, _space, _capture<';'>, _end>();
-
-
-auto get_if_missed(const std::string & conf, const Line & LINE,
- const std::string & val,
- const std::string & indent, bool compare)
- -> std::string
-{
- if (!compare || val.empty()) {
- return rgx::regex_search(conf, LINE.RGX()) ? "" : LINE.STR(val, indent);
- }
-
- rgx::smatch match; // assuming last capture has the value!
-
- for (auto pos = conf.begin();
- rgx::regex_search(pos, conf.end(), match, LINE.RGX());
- pos += match.position(0) + match.length(0))
- {
- const std::string value = match.str(match.size() - 1);
-
- if (value==val || value=="'"+val+"'" || value=='"'+val+'"') {
- return "";
- }
- }
-
- return LINE.STR(val, indent);
-}
-
-
-auto delete_if(const std::string & conf, const rgx::regex & rgx,
- const std::string & val, const bool compare)
- -> std::string
-{
- std::string ret{};
- auto pos = conf.begin();
-
- for (rgx::smatch match;
- rgx::regex_search(pos, conf.end(), match, rgx);
- pos += match.position(0) + match.length(0))
- {
- const std::string value = match.str(match.size() - 1);
- auto len = match.position(1);
- if (compare && value!=val && value!="'"+val+"'" && value!='"'+val+'"') {
- len = match.position(0) + match.length(0);
- }
- ret.append(pos, pos + len);
- }
-
- ret.append(pos, conf.end());
- return ret;
-}
-
-
-void add_ssl_directives_to(const std::string & name, const bool isdefault)
-{
- const std::string prefix = std::string{CONF_DIR} + name;
-
- std::string conf = read_file(prefix+".conf");
-
- const std::string & const_conf = conf; // iteration needs const string.
- rgx::smatch match; // captures str(1)=indentation spaces, str(2)=server name
- for (auto pos = const_conf.begin();
- rgx::regex_search(pos, const_conf.end(), match, NGX_SERVER_NAME.RGX());
- pos += match.position(0) + match.length(0))
- {
- if (match.str(2).find(name) == std::string::npos) { continue; }
-
- const std::string indent = match.str(1);
-
- std::string adds = isdefault ?
- get_if_missed(conf, NGX_INCLUDE_LAN_SSL_LISTEN_DEFAULT,"",indent) :
- get_if_missed(conf, NGX_INCLUDE_LAN_SSL_LISTEN, "", indent);
-
- adds += get_if_missed(conf, NGX_SSL_CRT, prefix+".crt", indent);
-
- adds += get_if_missed(conf, NGX_SSL_KEY, prefix+".key", indent);
-
- adds += get_if_missed(conf, NGX_SSL_SESSION_CACHE,
- SSL_SESSION_CACHE_ARG(name), indent, false);
-
- adds += get_if_missed(conf, NGX_SSL_SESSION_TIMEOUT,
- std::string{SSL_SESSION_TIMEOUT_ARG}, indent, false);
-
- if (adds.length() > 0) {
- pos += match.position(0) + match.length(0);
-
- conf = std::string(const_conf.begin(), pos) + adds +
- std::string(pos, const_conf.end());
-
- conf = isdefault ?
- delete_if(conf, NGX_INCLUDE_LAN_LISTEN_DEFAULT.RGX()) :
- delete_if(conf, NGX_INCLUDE_LAN_LISTEN.RGX());
-
- write_file(prefix+".conf", conf);
-
- std::cerr<<"Added SSL directives to "<<prefix<<".conf: ";
- std::cerr<<adds<<std::endl;
- }
-
- return;
- }
-
- auto errmsg = std::string{"add_ssl_directives_to error: "};
- errmsg += "cannot add SSL directives to " + name + ".conf, missing: ";
- errmsg += NGX_SERVER_NAME.STR(name, "\n ") + "\n";
- throw std::runtime_error(errmsg.c_str());
-}
-
-
-template<typename T>
-inline auto num2hex(T bytes) -> std::array<char, 2*sizeof(bytes)+1>
-{
- constexpr auto n = 2*sizeof(bytes);
- std::array<char, n+1> str{};
-
- for (size_t i=0; i<n; ++i) {
- static const std::array<char, 17> hex{"0123456789ABCDEF"};
- static constexpr auto get = 0x0fU;
- str.at(i) = hex.at(bytes & get);
-
- static constexpr auto move = 4U;
- bytes >>= move;
- }
-
- str[n] = '\0';
- return str;
-}
-
-
-template<typename T>
-inline auto get_nonce(const T salt=0) -> T
-{
- T nonce = 0;
-
- std::ifstream urandom{"/dev/urandom"};
-
- static constexpr auto move = 6U;
-
- constexpr size_t steps = (sizeof(nonce)*8 - 1)/move + 1;
-
- for (size_t i=0; i<steps; ++i) {
- if (!urandom.good()) { throw std::runtime_error("get_nonce error"); }
- nonce = (nonce << move) + static_cast<unsigned>(urandom.get());
- }
-
- nonce ^= salt;
-
- return nonce;
-}
-
-
-void create_ssl_certificate(const std::string & crtpath,
- const std::string & keypath,
- const int days)
-{
- size_t nonce = 0;
-
- try { nonce = get_nonce(nonce); }
-
- catch (...) { // the address of a variable should be random enough:
- auto addr = &crtpath;
- auto addrptr = static_cast<const size_t *>(
- static_cast<const void *>(&addr) );
- nonce += *addrptr;
- }
-
- auto noncestr = num2hex(nonce);
-
- const auto tmpcrtpath = crtpath + ".new-" + noncestr.data();
- const auto tmpkeypath = keypath + ".new-" + noncestr.data();
-
- try {
- auto pkey = gen_eckey(NID_secp384r1);
-
- write_key(pkey, tmpkeypath);
-
- std::string subject {"/C=ZZ/ST=Somewhere/L=None/CN=OpenWrt/O=OpenWrt"};
- subject += noncestr.data();
-
- selfsigned(pkey, days, subject, tmpcrtpath);
-
- static constexpr auto to_seconds = 24*60*60;
- static constexpr auto leeway = 42;
- if (!checkend(tmpcrtpath, days*to_seconds - leeway)) {
- throw std::runtime_error("bug: created certificate is not valid!!");
- }
-
- } catch (...) {
- std::cerr<<"create_ssl_certificate error: ";
- std::cerr<<"cannot create selfsigned certificate, ";
- std::cerr<<"removing temporary files ..."<<std::endl;
-
- if (remove(tmpcrtpath.c_str())!=0) {
- auto errmsg = "\t cannot remove "+tmpcrtpath;
- perror(errmsg.c_str());
- }
-
- if (remove(tmpkeypath.c_str())!=0) {
- auto errmsg = "\t cannot remove "+tmpkeypath;
- perror(errmsg.c_str());
- }
-
- throw;
- }
-
- if ( rename(tmpcrtpath.c_str(), crtpath.c_str())!=0 ||
- rename(tmpkeypath.c_str(), keypath.c_str())!=0 )
- {
- auto errmsg = std::string{"create_ssl_certificate warning: "};
- errmsg += "cannot move "+tmpcrtpath+" to "+crtpath;
- errmsg += " or "+tmpkeypath+" to "+keypath+", continuing ... ";
- perror(errmsg.c_str());
- }
-
-}
-
-
-void use_cron_to_recreate_certificate(const std::string & name)
-{
- static const char * filename = "/etc/crontabs/root";
-
- std::string conf{};
- try { conf = read_file(filename); }
- catch (const std::ifstream::failure &) { /* is ok if not found, create. */ }
-
- const std::string add = get_if_missed(conf, CRON_CMD, name);
-
- if (add.length() > 0) {
-#ifndef NO_UBUS
- auto service = ubus::call("service","list",UBUS_TIMEOUT).filter("cron");
-
- if (!service) {
- std::string errmsg{"use_cron_to_recreate_certificate error: "};
- errmsg += "Cron unavailable to re-create the ssl certificate for ";
- errmsg += name + "\n";
- throw std::runtime_error(errmsg.c_str());
- } // else active with or without instances:
-#endif
-
- write_file(filename, std::string{CRON_INTERVAL}+add, std::ios::app);
-
-#ifndef NO_UBUS
- call("/etc/init.d/cron", "reload");
-#endif
-
- std::cerr<<"Rebuild the ssl certificate for '";
- std::cerr<<name<<"' annually with cron."<<std::endl;
- }
-}
-
-
-void add_ssl_if_needed(const std::string & name)
-{
- try { add_ssl_directives_to(name, name==LAN_NAME); }
- catch (...) {
- std::cerr<<"add_ssl_if_needed error: ";
- std::cerr<<"cannot add SSL directives to "<<name<<".conf"<<std::endl;
- throw;
- }
-
- const auto crtpath = std::string{CONF_DIR} + name + ".crt";
- const auto keypath = std::string{CONF_DIR} + name + ".key";
- constexpr auto remaining_seconds = (365 + 32)*24*60*60;
- constexpr auto validity_days = 3*(365 + 31);
-
- bool is_valid = true;
-
- if (access(keypath.c_str(), R_OK) != 0 ||
- access(crtpath.c_str(), R_OK) != 0)
- { is_valid = false; }
-
- else {
- try {
- if (!checkend(crtpath, remaining_seconds)) {
- is_valid = false;
- }
- }
- catch (...) { // something went wrong, maybe it is in DER format:
- try {
- if (!checkend(crtpath, remaining_seconds, false)) {
- is_valid = false;
- }
- }
- catch (...) { // it has neither DER nor PEM format, rebuild.
- is_valid = false;
- }
- }
- }
-
- if (!is_valid) { create_ssl_certificate(crtpath, keypath, validity_days); }
-
- try { use_cron_to_recreate_certificate(name); }
- catch (...) {
- std::cerr<<"add_ssl_if_needed warning: ";
- std::cerr<<"cannot use cron to rebuild certificate for "<<name<<"\n";
- }
-}
-
-
-void del_ssl_directives_from(const std::string & name, const bool isdefault)
-{
- const std::string prefix = std::string{CONF_DIR} + name;
-
- std::string conf = read_file(prefix+".conf");
-
- const std::string & const_conf = conf; // iteration needs const string.
- rgx::smatch match; // captures str(1)=indentation spaces, str(2)=server name
- for (auto pos = const_conf.begin();
- rgx::regex_search(pos, const_conf.end(), match, NGX_SERVER_NAME.RGX());
- pos += match.position(0) + match.length(0))
- {
- if (match.str(2).find(name) == std::string::npos) { continue; }
-
- const std::string indent = match.str(1);
-
- std::string adds = isdefault ?
- get_if_missed(conf, NGX_INCLUDE_LAN_LISTEN_DEFAULT,"",indent) :
- get_if_missed(conf, NGX_INCLUDE_LAN_LISTEN, "", indent);
-
- if (adds.length() > 0) {
- pos += match.position(1);
-
- conf = std::string(const_conf.begin(), pos) + adds
- + std::string(pos, const_conf.end());
-
- conf = isdefault ?
- delete_if(conf, NGX_INCLUDE_LAN_SSL_LISTEN_DEFAULT.RGX())
- : delete_if(conf, NGX_INCLUDE_LAN_SSL_LISTEN.RGX());
-
- const auto crtpath = prefix+".crt";
- conf = delete_if(conf, NGX_SSL_CRT.RGX(), crtpath, true);
-
- const auto keypath = prefix+".key";
- conf = delete_if(conf, NGX_SSL_KEY.RGX(), keypath, true);
-
- conf = delete_if(conf, NGX_SSL_SESSION_CACHE.RGX());
-
- conf = delete_if(conf, NGX_SSL_SESSION_TIMEOUT.RGX());
-
- write_file(prefix+".conf", conf);
-
- std::cerr<<"Deleted SSL directives from "<<prefix<<".conf\n";
- }
-
- return;
- }
-
- auto errmsg = std::string{"del_ssl_directives_from error: "};
- errmsg += "cannot delete SSL directives from " + name + ".conf, missing: ";
- errmsg += NGX_SERVER_NAME.STR(name, "\n ") + "\n";
- throw std::runtime_error(errmsg.c_str());
-}
-
-
-void del_ssl(const std::string & name)
-{
- static const char * filename = "/etc/crontabs/root";
-
- try {
- const auto const_conf = read_file(filename);
-
- bool changed = false;
- auto conf = std::string{};
-
- size_t prev = 0;
- size_t curr = 0;
- while ((curr=const_conf.find('\n', prev)) != std::string::npos) {
-
- auto line = const_conf.substr(prev, curr-prev+1);
-
- if (line==delete_if(line,CRON_CMD.RGX(),std::string{name},true)) {
- conf += line;
- } else { changed = true; }
-
- prev = curr + 1;
- }
-
- if (changed) {
- write_file(filename, conf);
-
- std::cerr<<"Do not rebuild the ssl certificate for '";
- std::cerr<<name<<"' annually with cron anymore."<<std::endl;
-
-#ifndef NO_UBUS
- if (ubus::call("service", "list", UBUS_TIMEOUT).filter("cron"))
- { call("/etc/init.d/cron", "reload"); }
-#endif
- }
-
- } catch (...) {
- std::cerr<<"del_ssl warning: ";
- std::cerr<<"cannot delete cron job for "<<name<<" in "<<filename<<"\n";
- }
-
- try { del_ssl_directives_from(name, name==LAN_NAME); }
- catch (...) {
- std::cerr<<"del_ssl error: ";
- std::cerr<<"cannot delete SSL directives from "<<name<<".conf\n";
- throw;
- }
-
- const auto crtpath = std::string{CONF_DIR} + name + ".crt";
-
- if (remove(crtpath.c_str())!=0) {
- auto errmsg = "del_ssl warning: cannot remove "+crtpath;
- perror(errmsg.c_str());
- }
-
- const auto keypath = std::string{CONF_DIR} + name + ".key";
-
- if (remove(keypath.c_str())!=0) {
- auto errmsg = "del_ssl warning: cannot remove "+keypath;
- perror(errmsg.c_str());
- }
-}
-
-
-// reuse main(...) and common functions:
-#define NGINX_OPENSSL
-#include "nginx-util.cpp"