Roxen.git/
server/
base_server/
roxen.pike
Branch:
Tag:
Non-build tags
All tags
No tags
2017-11-03
2017-11-03 11:12:14 by Henrik Grubbström (Grubba) <grubba@grubba.org>
1407d09f34e787f52f0353629a2f76a34f6909cb (
218
lines) (+
76
/-
142
)
[
Show
|
Annotate
]
Branch:
b0ffc9894bc11eb90a58e8f4fee8ebd8798915f4
StartTLSProtocol: Use the CertDB.
2590:
int old_cert_failure = cert_failure; cert_failure = 0;
-
array(string)
certificates
= (
{});
-
array(object) decoded
_
certs = ({});
-
array(object) decoded_
keys
= ({}
);
+
Variable.Variable
Keys
=
getvar
(
"ssl
_keys
"
);
-
void handle_pem_file
(
string pem_file, Variable.Variable conf_var
)
-
{
-
string raw_cert;
-
SSL3_WERR
(
sprintf ("Reading PEM file %O\n", pem_file
)
)
;
-
if
(
catch{ raw_cert = lopen
(
pem_file, "r")->read
()
; }
)
-
{
-
CERT_WARNING
(conf_var,
-
LOC_M(66, "Reading PEM file %O failed: %s\n"),
-
pem_file, strerror (errno()));
-
return;
-
}
+
array
(
int
)
keypairs
=
Keys->query
();
+
if (
!sizeof
(
keypairs
)) {
+
//
No
new-style
certificates
configured.
-
Standards.PEM.Messages
msgs
= Standards.PEM.Messages(raw_cert);
-
-
foreach(msgs->fragments, string|Standards.PEM.Message msg) {
-
if
(stringp(msg))
{
-
if (String.trim_all_whites(msg) != "") {
-
CERT_WARNING(conf_var,
-
LOC_M(67, "Invalid PEM in %O.\n"),
-
pem_file);
-
}
-
continue;
-
}
-
string body = msg
-
>body;
-
if (msg->headers["dek-info"]) {
-
mixed err = catch {
-
body = Standards.PEM.decrypt_body(msg->headers["dek-info"],
-
body, query("ssl_password"));
-
};
-
if (err) {
-
CERT_WARNING(conf_var,
-
LOC_M(68, "Invalid decryption password for %O.\n"),
-
pem_file);
-
}
-
}
-
switch(msg->pre) {
-
case "CERTIFICATE":
-
case "X509 CERTIFICATE":
-
Standards.X509.TBSCertificate tbs =
-
Standards.X509.decode_certificate(body);
-
if (!tbs) {
-
CERT_WARNING (conf_var,
-
LOC_M(13, "Certificate not valid (DER).\n"));
-
return;
-
}
-
certificates
+= ({ body })
;
-
decoded_certs += ({ Standards.X509.decode_certificate(body) });
-
break;
-
case
"PRIVATE
KEY":
-
case
"RSA
PRIVATE
KEY":
-
case "DSA PRIVATE KEY":
-
case "ECDSA PRIVATE KEY":
-
Crypto
.
Sign key = Standards.X509.parse_private_key(body);
-
if (!key) {
-
CERT_ERROR (conf_var,
-
LOC_M(69,"Private key not valid")+" (DER).\n");
-
return;
-
}
-
decoded_keys += ({ key });
-
break;
-
}
-
}
-
};
-
+
//
Check
if
there
are
old
-
style
certificates;
in
case
of
which
+
//
this
is
probably
an
upgrade
.
Variable.Variable Certificates = getvar("ssl_cert_file"); Variable.Variable KeyFile = getvar("ssl_key_file");
-
object
privs
=
Privs
(
"Reading
cert
file
");
+
keypairs
=
+
CertDB.register_pem_files
(
Certificates->query()
+
({ KeyFile->query() }),
+
query(
"
ssl_password"
)
)
;
-
foreach
(
map
(
Certificates->query(
)
, String.trim_whites
)
,
string
cert_file)
{
-
if
(cert_file
==
"")
continue;
-
handle
_
pem
_
file
(
cert_file, Certificates
);
+
if
(
!sizeof
(
keypairs
))
{
+
// No Old-style certificate configuration found.
+
//
Fall
back to using all known certs.
+
keypairs = Keys->get
_
choice
_
list
();
}
-
string
key_file
=
String.trim_whites
(
KeyFile->query
())
;
-
if (key_file != "")
{
-
handle_pem_file(key_file,
KeyFile);
-
} else {
-
KeyFile
=
Certificates
;
-
}
+
if
(
sizeof
(
keypairs
)) {
+
//
Certificates
found.
+
Keys->set(keypairs)
;
-
privs
=
0
;
+
// Clear the old-style variables.
+
//Certificates->set(({}))
;
+
//KeyFile->set("");
-
if
(
!sizeof(decoded_certs
)
)
{
-
CERT_ERROR(Certificates,
LOC_M(63,"No
certificates
found.\n"));
+
save
()
;
+
} else
{
+
//
FIXME: Use anonymous suites?
report_error ("TLS port %s: %s", get_url(), LOC_M(63,"No certificates found.\n")); cert_err_unbind(); cert_failure = 1; return; }
-
-
if (!sizeof(decoded_keys)) {
-
CERT_ERROR (KeyFile, LOC_M (17,"No private key found.\n"));
-
report_error ("TLS port %s: %s", get_url(),
-
LOC_M (17,"No private key found.\n"));
-
cert_err_unbind();
-
cert_failure = 1;
-
return;
+
} // FIXME: Only do this if there are certs loaded? // We must reset the set of certificates.
-
+
// NB: Race condition here where the new SSLContext is
+
// live before it has been configured completely.
ctx = SSLContext(); set_version(); filter_preferred_suites();
-
mapping
(
string:array(
int
))
cert
_
lookup = ([]
)
;
-
foreach(decoded_certs;
int
no; Standards
.
X509
.
TBSCertificate tbs
)
{
-
cert_lookup[tbs->subject->
get_
der
()
]
+=
({
no
});
-
}
+
foreach
(
keypairs,
int
keypair
_
id)
{
+
array(Crypto
.
Sign
.
State|array(string
)
)
keypair =
+
CertDB.
get_
keypair
(
keypair_id
)
;
+
if
(!keypair)
continue;
-
foreach(decoded_keys,
Crypto.Sign
key) {
-
// NB: We need to support multiple certificates with the same key
.
-
int found;
-
Standards.X509.TBSCertificate tbs;
-
foreach(decoded
_
certs; int no; tbs) {
-
if (!tbs->public_
key
->pkc->public_key_equal(key))
-
continue;
-
-
array(
int
)
cert_nos
=
({ no })
;
-
-
// Build the certificate chain.
-
Standards.X509.TBSCertificate issuer;
-
do {
-
string issuer_der = tbs
->
issuer->get
_
der();
-
array(int) issuer_nos =
cert
_lookup[issuer_der];
-
if
(
!issuer
_
nos)
break;
-
-
issuer = decoded_
certs
[issuer_nos[0]];
-
-
// FIXME: Verify that the issuer has signed the cert.
-
-
if
(
issuer != tbs)
{
-
cert_nos += ({ issuer_nos[0]
});
-
} else {
-
// Self-signed.
-
issuer = UNDEFINED;
-
break;
+
[
Crypto.Sign.
State
private
_key
,
array(
string
)
certs]
=
keypair
;
+
ctx
->
add
_cert(
private
_
key,
certs
,
({
name,
"*"
})
)
;
}
-
} while ((tbs = issuer));
+
-
report_notice("Adding %s certificate (%d certs) for %s\n",
-
key->name(), sizeof(cert_nos), get_url());
-
// FIXME: Ought to only add "*" for the certificate chains
-
// belonging to the default server.
-
ctx->add_cert(key, rows(certificates, cert_nos), ({ name, "*" }));
-
found = 1;
-
}
-
if (!found) {
-
CERT_ERROR (KeyFile,
-
LOC_M(70, "Private key without matching certificate.\n"));
-
continue;
-
}
-
}
-
+
#if 0 // FIXME: How do this in current Pike 8.0? if (!sizeof(ctx->cert_pairs)) {
2772:
} }
+
class CertificateKeyChoiceVariable
+
{
+
inherit Variable.IntChoice;
+
+
mapping(int:string) get_translation_table()
+
{
+
array(mapping(string:int|string)) keypairs = CertDB.list_keypairs();
+
return mkmapping(keypairs->id, keypairs->name);
+
}
+
+
array(int) get_choice_list()
+
{
+
return CertDB.list_keypairs()->id;
+
}
+
+
array(string|mixed) verify_set(array(int) new_value)
+
{
+
if (!sizeof(new_value)) {
+
// The list of certificates should never be empty.
+
return ({ "Selection reset to all selected.", get_choice_list() });
+
}
+
return ::verify_set(new_value);
+
}
+
+
protected void create( void|int _flags, void|LocaleString std_name,
+
void|LocaleString std_doc )
+
{
+
::create(({}), UNDEFINED, _flags, std_name, std_doc);
+
}
+
}
+
+
#if 1
+
// Old-style SSL Certificate variables.
+
// FIXME: Keep these around for at least a few major versions (10 years?).
class CertificateListVariable { inherit Variable.FileList;
2795:
getcwd()); } }
+
#endif
void create(int pn, string i, void|int ignore_eaddrinuse) {
2820:
// changed callback is called. Currently you can get warnings // that the files don't match if you update both variables // at the same time.
+
getvar ("ssl_keys")->set_changed_callback(certificates_changed);
getvar ("ssl_cert_file")->set_changed_callback (certificates_changed); getvar ("ssl_key_file")->set_changed_callback (certificates_changed);