244 lines
8.9 KiB
C++
244 lines
8.9 KiB
C++
#include "output_https.h"
|
|
#include <mist/procs.h>
|
|
|
|
namespace Mist{
|
|
mbedtls_entropy_context OutHTTPS::entropy;
|
|
mbedtls_ctr_drbg_context OutHTTPS::ctr_drbg;
|
|
mbedtls_ssl_config OutHTTPS::sslConf;
|
|
mbedtls_x509_crt OutHTTPS::srvcert;
|
|
mbedtls_pk_context OutHTTPS::pkey;
|
|
|
|
void OutHTTPS::init(Util::Config *cfg){
|
|
Output::init(cfg);
|
|
capa["name"] = "HTTPS";
|
|
capa["friendly"] = "HTTPS (HTTP+TLS)";
|
|
capa["desc"] = "HTTPS connection handler, provides all enabled HTTP-based outputs";
|
|
capa["provides"] = "HTTP";
|
|
capa["protocol"] = "https://";
|
|
capa["required"]["cert"]["name"] = "Certificate";
|
|
capa["required"]["cert"]["help"] = "(Root) certificate(s) file(s) to append to chain";
|
|
capa["required"]["cert"]["option"] = "--cert";
|
|
capa["required"]["cert"]["short"] = "C";
|
|
capa["required"]["cert"]["default"] = "";
|
|
capa["required"]["cert"]["type"] = "str";
|
|
capa["required"]["key"]["name"] = "Key";
|
|
capa["required"]["key"]["help"] = "Private key for SSL";
|
|
capa["required"]["key"]["option"] = "--key";
|
|
capa["required"]["key"]["short"] = "K";
|
|
capa["required"]["key"]["default"] = "";
|
|
capa["required"]["key"]["type"] = "str";
|
|
|
|
capa["optional"]["wrappers"]["name"] = "Active players";
|
|
capa["optional"]["wrappers"]["help"] = "Which players are attempted and in what order.";
|
|
capa["optional"]["wrappers"]["default"] = "";
|
|
capa["optional"]["wrappers"]["type"] = "ord_multi_sel";
|
|
capa["optional"]["wrappers"]["allowed"].append("html5");
|
|
capa["optional"]["wrappers"]["allowed"].append("videojs");
|
|
capa["optional"]["wrappers"]["allowed"].append("dashjs");
|
|
capa["optional"]["wrappers"]["allowed"].append("flash_strobe");
|
|
capa["optional"]["wrappers"]["allowed"].append("silverlight");
|
|
capa["optional"]["wrappers"]["allowed"].append("img");
|
|
capa["optional"]["wrappers"]["option"] = "--wrappers";
|
|
capa["optional"]["wrappers"]["short"] = "w";
|
|
cfg->addConnectorOptions(4433, capa);
|
|
cfg->addOption("pubaddr", JSON::fromString("{\"arg\":\"string\", \"default\":\"\", \"short\":\"A\",\"long\":\"public-address\",\"help\":\"Full public address this output is available as.\"}"));
|
|
capa["optional"]["pubaddr"]["name"] = "Public address";
|
|
capa["optional"]["pubaddr"]["help"] = "Full public address this output is available as, if being proxied";
|
|
capa["optional"]["pubaddr"]["default"] = "";
|
|
capa["optional"]["pubaddr"]["type"] = "str";
|
|
capa["optional"]["pubaddr"]["option"] = "--public-address";
|
|
config = cfg;
|
|
}
|
|
|
|
OutHTTPS::OutHTTPS(Socket::Connection &C) : Output(C){
|
|
int ret;
|
|
mbedtls_net_init(&client_fd);
|
|
client_fd.fd = C.getSocket();
|
|
mbedtls_ssl_init(&ssl);
|
|
if ((ret = mbedtls_ctr_drbg_reseed(&ctr_drbg, (const unsigned char *)"child", 5)) != 0){
|
|
FAIL_MSG("Could not reseed");
|
|
C.close();
|
|
return;
|
|
}
|
|
|
|
// Set up the SSL connection
|
|
if ((ret = mbedtls_ssl_setup(&ssl, &sslConf)) != 0){
|
|
FAIL_MSG("Could not set up SSL connection");
|
|
C.close();
|
|
return;
|
|
}
|
|
|
|
// Inform mbedtls how we'd like to use the connection (uses default bio handlers)
|
|
// We tell it to use non-blocking IO here
|
|
mbedtls_net_set_nonblock(&client_fd);
|
|
mbedtls_ssl_set_bio(&ssl, &client_fd, mbedtls_net_send, mbedtls_net_recv, NULL);
|
|
// do the SSL handshake
|
|
while ((ret = mbedtls_ssl_handshake(&ssl)) != 0){
|
|
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE){
|
|
char error_buf[200];
|
|
mbedtls_strerror(ret, error_buf, 200);
|
|
MEDIUM_MSG("Could not handshake, SSL error: %s (%d)", error_buf, ret);
|
|
C.close();
|
|
return;
|
|
}else{
|
|
Util::sleep(100);
|
|
}
|
|
}
|
|
HIGH_MSG("Started SSL connection handler");
|
|
}
|
|
|
|
int OutHTTPS::run(){
|
|
unsigned char buf[1024 * 4]; // 4k internal buffer
|
|
int ret;
|
|
|
|
// Start a MistOutHTTP process, connected to this SSL connection
|
|
int fderr = 2;
|
|
int fd[2];
|
|
if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fd) != 0){
|
|
FAIL_MSG("Could not open anonymous socket for SSL<->HTTP connection!");
|
|
return 1;
|
|
}
|
|
std::deque<std::string> args;
|
|
args.push_back(Util::getMyPath() + "MistOutHTTP");
|
|
args.push_back("--ip");
|
|
args.push_back(myConn.getHost());
|
|
if (config->getString("pubaddr").size()){
|
|
args.push_back("--public-address");
|
|
args.push_back(config->getString("pubaddr"));
|
|
}
|
|
args.push_back("");
|
|
Util::Procs::socketList.insert(fd[0]);
|
|
pid_t http_proc = Util::Procs::StartPiped(args, &(fd[1]), &(fd[1]), &fderr);
|
|
close(fd[1]);
|
|
if (http_proc < 2){
|
|
FAIL_MSG("Could not spawn MistOutHTTP process for SSL connection!");
|
|
return 1;
|
|
}
|
|
Socket::Connection http(fd[0]);
|
|
http.setBlocking(false);
|
|
Socket::Buffer &http_buf = http.Received();
|
|
http_buf.splitter.clear();
|
|
|
|
// pass data back and forth between the SSL connection and HTTP process while connected
|
|
while (config->is_active && http){
|
|
bool activity = false;
|
|
// attempt to read SSL data, pass to HTTP
|
|
ret = mbedtls_ssl_read(&ssl, buf, sizeof(buf));
|
|
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE){
|
|
if (ret <= 0){
|
|
HIGH_MSG("SSL disconnect!");
|
|
break;
|
|
}
|
|
// we received ret bytes of data to pass on. Do so.
|
|
activity = true;
|
|
http.SendNow((const char *)buf, ret);
|
|
}
|
|
|
|
// attempt to read HTTP data, pass to SSL
|
|
if (http.spool() || http_buf.size()){
|
|
// We have data - pass it on
|
|
activity = true;
|
|
while (http_buf.size() && http){
|
|
int todo = http_buf.get().size();
|
|
int done = 0;
|
|
while (done < todo){
|
|
ret = mbedtls_ssl_write(&ssl, (const unsigned char*)http_buf.get().data() + done, todo - done);
|
|
if (ret == MBEDTLS_ERR_NET_CONN_RESET || ret == MBEDTLS_ERR_SSL_CLIENT_RECONNECT){
|
|
HIGH_MSG("SSL disconnect!");
|
|
http.close();
|
|
break;
|
|
}
|
|
if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE){
|
|
done += ret;
|
|
}else{
|
|
Util::sleep(50);
|
|
}
|
|
}
|
|
http_buf.get().clear();
|
|
}
|
|
}
|
|
if (!activity){
|
|
Util::sleep(50);
|
|
}
|
|
}
|
|
// close the HTTP process (close stdio, kill its PID)
|
|
http.close();
|
|
Util::Procs::Stop(http_proc);
|
|
uint16_t waiting = 0;
|
|
while (++waiting < 100){
|
|
if (!Util::Procs::isRunning(http_proc)){break;}
|
|
Util::sleep(100);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
OutHTTPS::~OutHTTPS(){
|
|
HIGH_MSG("Ending SSL connection handler");
|
|
// close when we're done
|
|
mbedtls_ssl_close_notify(&ssl);
|
|
mbedtls_ssl_free(&ssl);
|
|
mbedtls_net_free(&client_fd);
|
|
myConn.close();
|
|
}
|
|
|
|
/// Listens for HTTPS requests, accepting them and connecting them to a HTTP socket
|
|
void OutHTTPS::listener(Util::Config &conf, int (*callback)(Socket::Connection &S)){
|
|
if (config->getOption("cert", true).size() < 2 || config->getOption("key", true).size() < 2){
|
|
FAIL_MSG("The cert/key required options were not passed!");
|
|
return;
|
|
}
|
|
|
|
//Declare and set up all required mbedtls structures
|
|
int ret;
|
|
mbedtls_ssl_config_init(&sslConf);
|
|
mbedtls_entropy_init(&entropy);
|
|
mbedtls_pk_init(&pkey);
|
|
mbedtls_x509_crt_init(&srvcert);
|
|
mbedtls_ctr_drbg_init(&ctr_drbg);
|
|
|
|
// seed the rng
|
|
if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *)"MistServer", 10)) != 0){
|
|
FAIL_MSG("Could not seed the random number generator!");
|
|
}
|
|
|
|
//Read certificate chain(s) from cmdline option(s)
|
|
JSON::Value certs = config->getOption("cert", true);
|
|
jsonForEach(certs, it){
|
|
if (it->asStringRef().size()){//Ignore empty entries (default is empty)
|
|
ret = mbedtls_x509_crt_parse_file(&srvcert, it->asStringRef().c_str());
|
|
if (ret != 0){
|
|
WARN_MSG("Could not load any certificates from file: %s", it->asStringRef().c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
//Read key from cmdline option
|
|
ret = mbedtls_pk_parse_keyfile(&pkey, config->getString("key").c_str(), 0);
|
|
if (ret != 0){
|
|
FAIL_MSG("Could not load any keys from file: %s", config->getString("key").c_str());
|
|
return;
|
|
}
|
|
|
|
if ((ret = mbedtls_ssl_config_defaults(&sslConf, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0){
|
|
FAIL_MSG("SSL config defaults failed");
|
|
return;
|
|
}
|
|
mbedtls_ssl_conf_rng(&sslConf, mbedtls_ctr_drbg_random, &ctr_drbg);
|
|
mbedtls_ssl_conf_ca_chain(&sslConf, srvcert.next, NULL);
|
|
if ((ret = mbedtls_ssl_conf_own_cert(&sslConf, &srvcert, &pkey)) != 0){
|
|
FAIL_MSG("SSL config own certificate failed");
|
|
return;
|
|
}
|
|
|
|
Output::listener(conf, callback);
|
|
|
|
//Free all the mbedtls structures
|
|
mbedtls_x509_crt_free(&srvcert);
|
|
mbedtls_pk_free(&pkey);
|
|
mbedtls_ssl_config_free(&sslConf);
|
|
mbedtls_ctr_drbg_free(&ctr_drbg);
|
|
mbedtls_entropy_free(&entropy);
|
|
}
|
|
}
|
|
|