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";
-
}