~hww3/caudium

47931f4d3b9dead91ca25da525f6bd24cd9e6563 — William Welliver 11 months ago 478ede2
basic sni support. still probably want a setting to enable sni on the port
3 files changed, 258 insertions(+), 168 deletions(-)

M server/base_server/caudium.pike
M server/base_server/configuration.pike
M server/protocols/ssl3.pike
M server/base_server/caudium.pike => server/base_server/caudium.pike +20 -0
@@ 3493,6 3493,26 @@ int main(int argc, array(string) argv)
  return -1;
}

mapping sni_configs=([]);
object get_configuration_for_sni(string sni) {
  object conf;
  conf = sni_configs[sni];
  if(conf == -1) return 0;
  else if(conf) return conf;
werror("checking configurations\n");
  foreach(configurations;; conf) {
    object x = conf->get_sni_configuration();
    if(!x) continue;
    werror("find %O: %O\n", sni, x->find_cert_domain(sni));
    if(x->find_cert_domain(sni)) {
       sni_configs[sni] = conf;
       return conf;
    }
  }
  sni_configs[sni] = -1;
  return 0;
}

// check to see if we've upgraded yet. if not, run the upgrade.
// if the upgrade_version variable isn't present but upgrade_performed is 
// present, we make the version be 1.4.999. If neither is present, we 

M server/base_server/configuration.pike => server/base_server/configuration.pike +209 -3
@@ 2473,6 2473,183 @@ protected string make_proto_name(string p)
  return "about";
}

protected object sni_configuration;

object get_sni_configuration() {
  if(sni_configuration) return sni_configuration;
  string s = query("sni_tls_config");
  if(!s || sizeof(s) == 0) return 0;
  object ctx = SSL.Context();
  mapping opts = parse_ssl_args(s);
 
  load_ssl_configuration(opts, ctx, query("sni_hostnames"));
  sni_configuration = ctx;
  return ctx;
}

mapping parse_ssl_args(string options)
{
#ifdef SSL3_DEBUG
  roxen_perror(sprintf("SSL3:parse_ssl_args(\"%s\")\n", options));
#endif /* SSL3_DEBUG */
  mapping res = ([]);
  string line;

  foreach(options / "\n", line)
    {
      string key, value;
      if (sscanf(line, "%s%*[ \t]%s", key, value) == 3)
        res[String.trim_whites(key)] = String.trim_all_whites(value);
    }
  return res;
}

void load_ssl_configuration(mapping options, mixed ctx,
  void|array additional_hostnames) {
  string cert,key;
  array certificates = ({});
  if (!options["cert-file"])
  {
    ({ report_error, throw }) ("ssl3: No 'cert-file' argument!\n");
  }

  object privs = Privs ("Reading cert file(s)");
  object msg, part;
  // we can read a chain of certificates, separated by commas
  // with the server cert last in the list.
  foreach((options["cert-file"]/",")-({}), string c)
  {
    c=String.trim_whites(c);
    if(c!="")
    {
      string f = Stdio.read_file(c);
      if (!f)
        ({ report_error, throw }) ("ssl3: Reading cert-file " + c + " failed!\n");

      msg = Tools.PEM.pem_msg()->init(f);

      part = msg->parts["CERTIFICATE"]
        ||msg->parts["X509 CERTIFICATE"];

      if (!part || !(cert = part->decoded_body()))
        ({ report_error, throw }) ("ssl3: No certificate found.\n");
      certificates += ({ cert });

    }
  }

  if(options["client-cert-authorities"])
  {
        array cca = ({});

    foreach((options["client-cert-authorities"]/",")-({}), string c)
    {
      c=String.trim_whites(c);
      if(c!="")
      {
        string f = Stdio.read_file(c);
        if (!f)
          ({ report_error, throw }) ("ssl3: Reading client-cert-authority " + c + " failed!\n");

        msg = Tools.PEM.pem_msg()->init(f);

        part = msg->parts["CERTIFICATE"]
          ||msg->parts["X509 CERTIFICATE"];

        if (!part || !(cert = part->decoded_body()))
          ({ report_error, throw }) ("ssl3: No certificate found.\n");
        cca += ({ cert });
      }
    }

    ctx->set_authorities(cca);
    ctx->verify_certificates = 1;
  }

  if(options["client-cert-issuers"])
  {
        array cca = ({});

    foreach((options["client-cert-issuers"]/",")-({}), string c)
    {
      c=String.trim_whites(c);
      if(c!="")
      {
        string f = Stdio.read_file(c);
        if (!f)
          ({ report_error, throw }) ("ssl3: Reading client-cert-issuer " + c + " failed!\n");

        msg = Tools.PEM.pem_msg()->init(f);

        part = msg->parts["CERTIFICATE"]
          ||msg->parts["X509 CERTIFICATE"];

        if (!part || !(cert = part->decoded_body()))
          ({ report_error, throw }) ("ssl3: No certificate found.\n");
        cca += ({ cert });
      }
    }

    ctx->set_trusted_issuers(({cca}));
    ctx->require_trust = 1;
  }

  string f2 = options["key-file"] && Stdio.read_file(options["key-file"]);

  if (options["key-file"]) {
    if (!f2) {
      ({ report_error, throw }) ("ssl3: Reading key-file failed!\n");
    }
    msg = Tools.PEM.pem_msg()->init(f2);
  }

  destruct (privs);
  part = msg->parts["RSA PRIVATE KEY"];

  if (!part || !(key = part->decoded_body()))
    ({ report_error, throw }) ("ssl3: Private key not found.\n");

  object rsa = Standards.PKCS.RSA.parse_private_key(key);
  if (!rsa)
    ({ report_error, throw }) ("ssl3: Private key not valid.\n");
#if constant(Standards.PKCS.Certificate.check_cert_rsa)
  if (!Standards.PKCS.Certificate.check_cert_rsa (cert, rsa))
    ({ report_error, throw }) ("ssl3: Certificate and private key do not match.\n");
#endif

#ifdef SSL3_DEBUG
  werror(sprintf("RSA key size: %d bits\n", rsa->rsa_size()));
#endif

  if (rsa->rsa_size() > 512)
  {
    /* Too large for export */
    int e = (int)Crypto.Random.random(65537);
    if(!(e%2)) e++;
    ctx->short_rsa = Crypto.RSA()->generate_key(512, e);

    e = (int)Crypto.Random.random(65537);
    if(!(e%2)) e++;
    ctx->long_rsa = Crypto.RSA()->generate_key(rsa->rsa_size(), e);
  }


  if(options["client-cert-request"])
  {
     if(options["client-cert-request"] == "request")
                ctx->auth_level = SSL.Constants.AUTHLEVEL_ask;
     else if(options["client-cert-request"] == "require")
                ctx->auth_level = SSL.Constants.AUTHLEVEL_require;
  }

werror("additional hostnames: %O\n", additional_hostnames);
if(stringp(additional_hostnames)){
  if(!sizeof(additional_hostnames)) additional_hostnames = 0;
}

  // we need the certificates to be in the opposite order (my cert first) for ssl to work.
  ctx->add_cert(rsa, reverse(certificates), additional_hostnames||({}));
}
//!
string MKPORTKEY(array(string) p)
{


@@ 3115,7 3292,7 @@ object enable_module( string modname )
}

//! Called from the configuration interface.
string check_variable(string name, string value)
string check_variable(string name, mixed value)
{
 werror("check_variable(%O, %O)\n", name, value);
  switch(name)


@@ 3136,6 3313,31 @@ string check_variable(string name, string value)
           !(sscanf(value,"%*s://%*s/")==2))
          return "The URL should follow this format: protocol://computer[:port]/";
        return 0;
      case "sni_tls_config":
        if(!value || sizeof(value) == 0) return 0;
        object ctx = SSL.Context();
        mapping options;
        mixed err = catch {
          options = parse_ssl_args(value);
          load_ssl_configuration(options,ctx,query("sni_hostnames"));
        };
        if(err) return Error.mkerror(err)->message();
        werror("%O\n", ctx->get_certificates());
        sni_configuration = 0; 
        return 0;
      case "sni_hostnames":
        if(!query("sni_tls_config")) return 0;
        ctx = SSL.Context();
        options;
        err = catch {
          options = parse_ssl_args(query("sni_tls_config"));
          load_ssl_configuration(options,ctx,value);
        };
        if(err) return Error.mkerror(err)->message();
        werror("%O\n", ctx->get_certificates());
        sni_configuration = 0; 
        return 0;

  }
}



@@ 3860,9 4062,13 @@ void create(string config)
         TYPE_TEXT_FIELD|VAR_MORE,
         "This text will be visible in the configuration interface, it "
         " can be quite useful to use as a memory helper.");
  defvar("sni_tls_config", "", "SNI TLS Configuration",
  defvar("sni_tls_config", "", "SNI: TLS Configuration",
         TYPE_SSL_CONFIG,
         "Configuration for TLS-SNI based virtual hosting."); 
         "TLS Configuration SNI based virtual hosting."); 
  defvar("sni_hostnames", "", "SNI: Additional host names",
         TYPE_STRING_LIST,
         "Additional host names to accept. Can be used to specify "
         "host names when a wildcard certificate is employed.");
  defvar("name", "", "Virtual server name",
         TYPE_STRING|VAR_MORE,
         "This is the name that will be used in the configuration "

M server/protocols/ssl3.pike => server/protocols/ssl3.pike +29 -165
@@ 64,44 64,38 @@ int req_time = HRTIME();
#define MARK_FD(X) REQUEST_WERR(X)
#endif

mapping parse_args(string options)
{
#ifdef SSL3_DEBUG
  roxen_perror(sprintf("SSL3:parse_args(\"%s\")\n", options));
#endif /* SSL3_DEBUG */
  mapping res = ([]);
  string line;
  
  foreach(options / "\n", line)
    {
      string key, value;
      if (sscanf(line, "%s%*[ \t]%s", key, value) == 3)
	res[String.trim_whites(key)] = String.trim_all_whites(value);
    }
  return res;
}

class roxen_ssl_context {
  import SSL.Constants;

  inherit SSL.Context;
  int port; /* port number */

  protected object caudium_server;
  
  protected void create(object _caudium) {
     caudium_server = _caudium;
     ::create(); 
  }
  
  array(CertificatePair) find_cert_domain(string(8bit) domain) {
//    werror("find_cert_domain(%O)\n", domain);
    array res =  ::find_cert_domain(domain);
//    werror("find_cert_domain(%O) -> %O\n", domain, res);
    array res;
    object conf = caudium->get_configuration_for_sni(domain);
    if(conf) {
      res = conf->get_sni_configuration()->find_cert_domain(domain);; 
    } 
    else { 
      res =  ::find_cert_domain(domain);
    }
    return res;
  }
}

private object new_context(object c)
{
#ifdef SSL3_DEBUG
//#ifdef SSL3_DEBUG
  roxen_perror(sprintf("SSL3:new_context(X)\n"));
#endif /* SSL3_DEBUG */
//#endif /* SSL3_DEBUG */
  mapping contexts = caudium->query_var("ssl3_contexts");
  object ctx = roxen_ssl_context();
  object ctx = roxen_ssl_context(caudium);
  
  if (!contexts)
  {


@@ 130,7 124,6 @@ array|void real_port(array port, object cfg)
  werror(sprintf("port = %O\n", port));
#endif

  string cert, key;
  object ctx = new_context(cfg);

  ctx->certificates=({});


@@ 139,147 132,13 @@ array|void real_port(array port, object cfg)
  // enable use of the session cache
  ctx->use_cache = 1;

  mapping options = parse_args(port[3]);
  mapping options = cfg->parse_ssl_args(port[3]);

#ifdef SSL3_DEBUG
  werror(sprintf("options = %O\n", options));
#endif
  cfg->load_ssl_configuration(options, ctx);

  if (!options["cert-file"])
  {
    ({ report_error, throw }) ("ssl3: No 'cert-file' argument!\n");
  }

  object privs = Privs ("Reading cert file(s)");
  object msg, part;
  // we can read a chain of certificates, separated by commas
  // with the server cert last in the list.
  foreach((options["cert-file"]/",")-({}), string c)
  {
    c=String.trim_whites(c);
    if(c!="")
    {
      string f = read_file(c);
      if (!f)
        ({ report_error, throw }) ("ssl3: Reading cert-file " + c + " failed!\n");
  
      msg = Tools.PEM.pem_msg()->init(f);

      part = msg->parts["CERTIFICATE"]
        ||msg->parts["X509 CERTIFICATE"];
  
      if (!part || !(cert = part->decoded_body()))
        ({ report_error, throw }) ("ssl3: No certificate found.\n");
      ctx->certificates += ({ cert });
    }
  }

  if(options["client-cert-authorities"]) 
  {
	array cca = ({});

    foreach((options["client-cert-authorities"]/",")-({}), string c)
    {
      c=String.trim_whites(c);
      if(c!="")
      {
        string f = read_file(c);
        if (!f)
          ({ report_error, throw }) ("ssl3: Reading client-cert-authority " + c + " failed!\n");
  
        msg = Tools.PEM.pem_msg()->init(f);
 
        part = msg->parts["CERTIFICATE"]
          ||msg->parts["X509 CERTIFICATE"];
  
        if (!part || !(cert = part->decoded_body()))
          ({ report_error, throw }) ("ssl3: No certificate found.\n");
        cca += ({ cert });
      }
    }

    ctx->set_authorities(cca);
    ctx->verify_certificates = 1;
  }

  if(options["client-cert-issuers"]) 
  {
	array cca = ({});

    foreach((options["client-cert-issuers"]/",")-({}), string c)
    {
      c=String.trim_whites(c);
      if(c!="")
      {
        string f = read_file(c);
        if (!f)
          ({ report_error, throw }) ("ssl3: Reading client-cert-issuer " + c + " failed!\n");
  
        msg = Tools.PEM.pem_msg()->init(f);
 
        part = msg->parts["CERTIFICATE"]
          ||msg->parts["X509 CERTIFICATE"];
  
        if (!part || !(cert = part->decoded_body()))
          ({ report_error, throw }) ("ssl3: No certificate found.\n");
        cca += ({ cert });
      }
    }

    ctx->set_trusted_issuers(({cca}));
    ctx->require_trust = 1;
  }

  string f2 = options["key-file"] && read_file(options["key-file"]);
  destruct (privs);

  if (options["key-file"]) {
    if (!f2)
      ({ report_error, throw }) ("ssl3: Reading key-file failed!\n");
    msg = Tools.PEM.pem_msg()->init(f2);
  }

  part = msg->parts["RSA PRIVATE KEY"];
  
  if (!part || !(key = part->decoded_body()))
    ({ report_error, throw }) ("ssl3: Private key not found.\n");

  object rsa = Standards.PKCS.RSA.parse_private_key(key);
  if (!rsa)
    ({ report_error, throw }) ("ssl3: Private key not valid.\n");

#if constant(Standards.PKCS.Certificate.check_cert_rsa)
  if (!Standards.PKCS.Certificate.check_cert_rsa (cert, rsa))
    ({ report_error, throw }) ("ssl3: Certificate and private key do not match.\n");
#endif

#ifdef SSL3_DEBUG
  werror(sprintf("RSA key size: %d bits\n", rsa->rsa_size()));
#endif

  if (rsa->rsa_size() > 512)
  {
    /* Too large for export */
    int e = (int)Crypto.Random.random(65537);
    if(!(e%2)) e++;
    ctx->short_rsa = Crypto.RSA()->generate_key(512, e);

    e = (int)Crypto.Random.random(65537);
    if(!(e%2)) e++;
    ctx->long_rsa = Crypto.RSA()->generate_key(rsa->rsa_size(), e);
  }

  if(options["client-cert-request"])
  {
     if(options["client-cert-request"] == "request")
		ctx->auth_level = SSL.Constants.AUTHLEVEL_ask;
     else if(options["client-cert-request"] == "require")
		ctx->auth_level = SSL.Constants.AUTHLEVEL_require;
  }

  // we need the certificates to be in the opposite order (my cert first) for ssl to work.
  ctx->certificates=reverse(ctx->certificates);
  ctx->rsa = rsa;
}

#define CHUNK 16384


@@ 505,6 364,14 @@ void do_log()

protected int parse_got()
{
  string server_name = my_fd->get_server_name();
  if(server_name) {
    object c = caudium->get_configuration_for_sni(server_name);
    if(c) { werror("got sni config: %O\n", c); 
      conf = c;
    }
  }

  int r = ::parse_got();
  if(r == -1)
  {


@@ 758,14 625,11 @@ void create(void|object f, void|object c)
    if(my_fd->set_alert_callback)

    my_fd->accept();
        my_fd->set_alert_callback(http_fallback);
    my_fd->set_alert_callback(http_fallback);

  } else {
    // Main object. 
  }
  


  server_protocol="HTTPS";

}