#pike __REAL_VERSION__ |
#pragma strict_types |
#require constant(Crypto.Hash) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef SSL3_DEBUG |
#define SSL3_DEBUG_MSG(X ...) werror(X) |
#else /*! SSL3_DEBUG */ |
#define SSL3_DEBUG_MSG(X ...) |
#endif /* SSL3_DEBUG */ |
|
import "."; |
import Constants; |
|
protected void create() |
{ |
SSL3_DEBUG_MSG("SSL.Context->create\n"); |
|
|
preferred_suites = get_suites(128, 1); |
} |
|
|
|
|
|
|
|
ProtocolVersion min_version = PROTOCOL_TLS_1_0; |
|
|
|
|
|
|
|
ProtocolVersion max_version = PROTOCOL_TLS_MAX; |
|
|
|
array(string(8bit)) advertised_protocols; |
|
|
|
|
|
mapping(Standards.ASN1.Types.Identifier:Crypto.Hash) verifier_algorithms |
= filter(Standards.X509.get_algorithms(), |
lambda(object o) { |
return !(< |
#if constant(Crypto.MD2) |
Crypto.MD2, |
#endif |
Crypto.MD5, |
Crypto.SHA1 |
>)[o]; |
}); |
|
|
|
int packet_max_size = PACKET_MAX_SIZE; |
|
|
|
|
|
array(int) preferred_compressors = ({ COMPRESSION_null }); |
|
|
|
|
int(0..1) heartbleed_probe = 0; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Alert alert_factory(object con, |
int(1..2) level, int(8bit) description, |
ProtocolVersion version, string|void message) |
{ |
return Alert(level, description, version, message); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Crypto.RSA.State long_rsa; |
Crypto.RSA.State short_rsa; |
|
|
int long_rsa_counter; |
int short_rsa_counter; |
|
|
|
|
|
function(int(0..):string(8bit)) random = Crypto.Random.random_string; |
|
|
int encrypt_then_mac = 1; |
|
|
|
array(int) preferred_suites; |
|
|
array(int) ecc_curves = reverse(sort(indices(ECC_CURVES))); |
|
|
|
array(Crypto.DH.Parameters) dh_groups = ({ |
Crypto.DH.FFDHE2048, |
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
array(array(int)) signature_algorithms = ({ |
#if constant(Crypto.SHA512) |
#if constant(Crypto.ECC.Curve) |
({ HASH_sha512, SIGNATURE_ecdsa }), |
#endif |
({ HASH_sha512, SIGNATURE_rsa }), |
#endif |
#if constant(Crypto.SHA384) |
#if constant(Crypto.ECC.Curve) |
({ HASH_sha384, SIGNATURE_ecdsa }), |
#endif |
({ HASH_sha384, SIGNATURE_rsa }), |
#endif |
#if constant(Crypto.ECC.Curve) |
({ HASH_sha256, SIGNATURE_ecdsa }), |
#endif |
({ HASH_sha256, SIGNATURE_dsa }), |
({ HASH_sha256, SIGNATURE_rsa }), |
#if constant(Crypto.SHA224) |
#if constant(Crypto.ECC.Curve) |
({ HASH_sha224, SIGNATURE_ecdsa }), |
#endif |
({ HASH_sha224, SIGNATURE_dsa }), |
#endif |
#if constant(Crypto.ECC.Curve) |
({ HASH_sha, SIGNATURE_ecdsa }), |
#endif |
({ HASH_sha, SIGNATURE_dsa }), |
({ HASH_sha, SIGNATURE_rsa }), |
}); |
|
|
|
|
|
array(array(int)) get_signature_algorithms(array(array(int))|void signature_algorithms) |
{ |
if (!signature_algorithms) { |
signature_algorithms = this_program::signature_algorithms; |
} |
|
#if constant(Crypto.ECC.Curve) && constant(Crypto.SHA512) && \ |
constant(Crypto.SHA384) && constant(Crypto.SHA224) |
return signature_algorithms; |
#else |
return [array(array(int))] |
filter(signature_algorithms, |
lambda(array(int) pair) { |
[int hash, int sign] = pair; |
#if !constant(Crypto.ECC.Curve) |
if (sign == SIGNATURE_ecdsa) return 0; |
#endif |
if ((< |
#if !constant(Crypto.SHA512) |
HASH_sha512, |
#endif |
#if !constant(Crypto.SHA384) |
HASH_sha384, |
#endif |
#if !constant(Crypto.SHA224) |
HASH_sha224, |
#endif |
>)[hash]) return 0; |
return 1; |
}); |
#endif |
} |
|
|
|
|
protected int cipher_suite_sort_key(int suite) |
{ |
array(int) info = [array(int)] (CIPHER_SUITES[suite] || ({ 0, 0, 0 })); |
|
int keylength = CIPHER_effective_keylengths[info[1]]; |
|
|
int hash = info[2]; |
|
|
if (sizeof(info) > 3) { |
hash |= info[3]<<5; |
if (info[3] == MODE_cbc) { |
|
keylength >>= 1; |
} |
} else { |
|
|
|
keylength >>= 1; |
} |
|
|
int cipher = info[1]; |
|
|
int ke_prio = ([ |
KE_null: 0, |
KE_dh_anon: 1, |
KE_ecdh_anon: 2, |
KE_fortezza: 3, |
KE_dms: 4, |
KE_dh_rsa: 5, |
KE_dh_dss: 6, |
KE_rsa: 7, |
KE_rsa_fips: 8, |
KE_ecdh_rsa: 9, |
KE_ecdh_ecdsa: 10, |
KE_dhe_rsa: 11, |
KE_dhe_dss: 12, |
KE_ecdhe_rsa: 13, |
KE_ecdhe_ecdsa: 14, |
])[info[0]]; |
|
int auth_prio = keylength && ([ |
KE_null: 0, |
KE_dh_anon: 0, |
KE_ecdh_anon: 0, |
KE_fortezza: 1, |
KE_dms: 2, |
KE_rsa: 8, |
KE_rsa_fips: 8, |
KE_dhe_rsa: 8, |
KE_ecdhe_rsa: 8, |
KE_dh_dss: 8, |
KE_dh_rsa: 8, |
KE_dhe_dss: 8, |
KE_ecdh_rsa: 8, |
KE_ecdh_ecdsa: 8, |
KE_ecdhe_ecdsa: 8, |
])[info[0]]; |
|
|
|
|
|
|
|
|
|
|
|
return cipher | hash << 8 | ke_prio << 16 | keylength << 24 | auth_prio << 36; |
} |
|
|
|
|
|
|
|
|
|
|
array(int) sort_suites(array(int) suites) |
{ |
sort(map(suites, cipher_suite_sort_key), suites); |
return reverse(suites); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
array(int) get_suites(int(-1..)|void min_keylength, |
int(0..2)|void ke_flags, |
multiset(int)|void blacklisted_ciphers, |
multiset(KeyExchangeType)|void blacklisted_kes, |
multiset(HashAlgorithm)|void blacklisted_hashes, |
multiset(CipherModes)|void blacklisted_ciphermodes) |
{ |
if (!min_keylength) min_keylength = 128; |
|
|
multiset(int) kes = (< |
KE_dhe_rsa, KE_dhe_dss, |
KE_ecdhe_rsa, KE_ecdhe_ecdsa, |
>); |
|
if (ke_flags) { |
|
kes |= (< |
KE_rsa, KE_rsa_fips, |
KE_dh_rsa, KE_dh_dss, |
#if constant(Crypto.ECC.Curve) |
KE_ecdh_rsa, |
KE_ecdh_ecdsa, |
#endif |
>); |
if (ke_flags == 2) { |
|
kes |= (< KE_null, KE_dh_anon, |
#if constant(Crypto.ECC.Curve) |
KE_ecdh_anon, |
#endif |
>); |
} |
} |
|
#if constant(Crypto.ECC.Curve) |
if (!sizeof(ecc_curves)) { |
|
kes -= (< |
KE_ecdhe_rsa, KE_ecdhe_ecdsa, |
KE_ecdh_rsa, KE_ecdh_ecdsa, |
KE_ecdh_anon, |
>); |
} |
#endif |
|
if (blacklisted_kes) { |
kes -= blacklisted_kes; |
} |
|
|
array(int) res = |
filter(indices(CIPHER_SUITES), |
lambda(int suite) { |
return kes[CIPHER_SUITES[suite][0]]; |
}); |
|
|
if (min_keylength > 0) { |
res = filter(res, |
lambda(int suite, int min_keylength) { |
return min_keylength <= |
CIPHER_effective_keylengths[CIPHER_SUITES[suite][1]]; |
}, min_keylength); |
} |
|
if (blacklisted_ciphers) { |
res = filter(res, |
lambda(int suite, multiset(int) blacklisted_hashes) { |
return !blacklisted_hashes[CIPHER_SUITES[suite][1]]; |
}, blacklisted_ciphers); |
} |
|
#if !constant(Crypto.SHA384) |
|
if (!blacklisted_hashes) |
blacklisted_hashes = (< HASH_sha384 >); |
else |
blacklisted_hashes[HASH_sha384] = 1; |
#endif |
if (blacklisted_hashes) { |
res = filter(res, |
lambda(int suite, multiset(int) blacklisted_hashes) { |
return !blacklisted_hashes[CIPHER_SUITES[suite][2]]; |
}, blacklisted_hashes); |
} |
|
if (blacklisted_ciphermodes) { |
res = filter(res, |
lambda(int suite, multiset(int) blacklisted_ciphermodes) { |
array(int) info = [array(int)]CIPHER_SUITES[suite]; |
int mode = (sizeof(info) > 3)?info[3]:MODE_cbc; |
return !blacklisted_ciphermodes[mode]; |
}, blacklisted_ciphermodes); |
} |
|
switch(max_version) { |
case PROTOCOL_TLS_1_1: |
case PROTOCOL_TLS_1_0: |
case PROTOCOL_SSL_3_0: |
res = filter(res, |
lambda(int suite) { |
array(int) info = [array(int)]CIPHER_SUITES[suite]; |
|
|
|
|
return (sizeof(info) < 4) && (info[2] <= HASH_sha); |
}); |
break; |
} |
|
return sort_suites(res); |
} |
|
|
|
void filter_weak_suites(int min_keylength) |
{ |
if (!preferred_suites || !min_keylength) return; |
preferred_suites = |
filter(preferred_suites, |
lambda(int suite) { |
array(int) def = [array(int)]CIPHER_SUITES[suite]; |
return def && |
(CIPHER_effective_keylengths[def[1]] >= min_keylength); |
}); |
} |
|
#if constant(Crypto.ECC.Curve) && constant(Crypto.AES.GCM) && constant(Crypto.SHA384) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void configure_suite_b(int(128..)|void min_keylength, |
int(0..)|void strictness_level) |
{ |
if (min_keylength < 128) min_keylength = 128; |
|
if (min_keylength > 128) { |
preferred_suites = ({ |
TLS_ecdhe_ecdsa_with_aes_256_gcm_sha384, |
}); |
} else { |
preferred_suites = ({ |
TLS_ecdhe_ecdsa_with_aes_128_gcm_sha256, |
TLS_ecdhe_ecdsa_with_aes_256_gcm_sha384, |
}); |
} |
|
max_version = PROTOCOL_TLS_MAX; |
min_version = PROTOCOL_TLS_1_2; |
|
if (strictness_level < 2) { |
|
|
|
min_version = PROTOCOL_TLS_1_0; |
|
|
if (min_keylength > 128) { |
|
preferred_suites += ({ |
TLS_ecdhe_ecdsa_with_aes_256_cbc_sha, |
}); |
} else { |
|
preferred_suites += ({ |
TLS_ecdhe_ecdsa_with_aes_128_cbc_sha, |
TLS_ecdhe_ecdsa_with_aes_256_cbc_sha, |
}); |
} |
|
if (strictness_level < 1) { |
|
|
preferred_suites += get_suites(min_keylength) - preferred_suites; |
} |
} |
} |
|
#endif /* Crypto.ECC.Curve && Crypto.AES.GCM && Crypto.SHA384 */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int auth_level; |
|
|
|
|
|
|
|
|
|
|
|
|
|
void set_authorities(array(string) a) |
{ |
authorities = a; |
update_authorities(); |
} |
|
|
|
|
|
|
int require_trust=0; |
|
|
array(string) get_authorities() |
{ |
return authorities; |
} |
|
protected array(string) authorities = ({}); |
array(string(8bit)) authorities_cache = ({}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void set_trusted_issuers(array(array(string)) i) |
{ |
trusted_issuers = i; |
update_trusted_issuers(); |
} |
|
|
array(array(string)) get_trusted_issuers() |
{ |
return trusted_issuers; |
} |
|
protected array(array(string)) trusted_issuers = ({}); |
mapping(string:array(Standards.X509.Verifier)) trusted_issuers_cache = ([]); |
|
|
|
int verify_certificates = 0; |
|
|
|
array(int) preferred_auth_methods = |
({ AUTH_rsa_sign }); |
|
|
|
protected mapping(string(8bit):array(CertificatePair)) cert_chains_issuer = ([]); |
|
|
|
protected mapping(string(8bit):array(CertificatePair)) cert_chains_domain = ([]); |
|
|
|
array(CertificatePair) find_cert_issuer(array(string) ders) |
{ |
|
|
foreach(ders, string der) |
if(cert_chains_issuer[der]) |
return cert_chains_issuer[der]; |
|
|
|
return UNDEFINED; |
} |
|
|
|
array(CertificatePair) find_cert_domain(string(8bit) domain) |
{ |
if( domain ) |
{ |
if( cert_chains_domain[domain] ) |
return cert_chains_domain[domain]; |
|
|
foreach(cert_chains_domain; string g; array(CertificatePair) chains) |
if( glob(g, domain) && (g != "*") ) |
return chains; |
} |
|
return cert_chains_domain["*"]; |
} |
|
|
array(CertificatePair) get_certificates() |
{ |
mapping(CertificatePair:int) c = ([]); |
foreach(cert_chains_domain;; array(CertificatePair) chains) |
foreach(chains, CertificatePair p) |
c[p]++; |
return indices(c); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void add_cert(Crypto.Sign.State key, array(string(8bit)) certs, |
array(string(8bit))|void extra_name_globs) |
{ |
CertificatePair cp = CertificatePair(key, certs, extra_name_globs); |
add_cert(cp); |
} |
variant void add_cert(string(8bit) key, array(string(8bit)) certs, |
array(string(8bit))|void extra_name_globs) |
{ |
Crypto.Sign.State _key = Standards.PKCS.RSA.parse_private_key(key) || |
Standards.PKCS.DSA.parse_private_key(key) || |
#if constant(Crypto.ECC.Curve) |
Standards.PKCS.ECDSA.parse_private_key(key) || |
#endif |
0; |
add_cert(_key, certs, extra_name_globs); |
} |
variant void add_cert(CertificatePair cp) |
{ |
void add(string what, mapping(string:array(CertificatePair)) to) |
{ |
if( !to[what] ) |
to[what] = ({cp}); |
else |
to[what] = sort( to[what]+({cp}) ); |
}; |
|
|
|
|
|
|
foreach( cp->globs, string id ) |
add(id, cert_chains_domain); |
|
add(cp->issuers[0], cert_chains_issuer); |
} |
|
|
private void update_authorities() |
{ |
authorities_cache = ({}); |
foreach(authorities, string a) |
authorities_cache += ({ Standards.X509.decode_certificate(a)-> |
subject->get_der() }); |
} |
|
|
private void update_trusted_issuers() |
{ |
trusted_issuers_cache=([]); |
foreach(trusted_issuers, array(string) i) |
{ |
|
mapping result = Standards.X509.verify_certificate_chain(i, ([]), 0); |
|
if(!result->verified) |
error("Broken trusted issuer chain!\n"); |
|
|
|
|
|
Standards.X509.TBSCertificate cert = |
([array(object(Standards.X509.TBSCertificate))]result->certificates)[-1]; |
|
if( !cert->ext_basicConstraints_cA || |
!(cert->ext_keyUsage & Standards.X509.KU_keyCertSign) ) |
error("Trusted issuer not allowed to sign other certificates.\n"); |
|
trusted_issuers_cache[cert->subject->get_der()] += ({ cert->public_key }); |
} |
} |
|
|
|
|
|
|
|
int use_cache = 1; |
|
|
|
|
int session_lifetime = 600; |
|
|
int max_sessions = 300; |
|
|
ADT.Queue active_sessions = ADT.Queue(); |
|
mapping(string:Session) session_cache = ([]); |
|
|
void forget_old_sessions() |
{ |
int t = time() - session_lifetime; |
array pair; |
while ( (pair = [array]active_sessions->peek()) |
&& (pair[0] < t)) { |
SSL3_DEBUG_MSG("SSL.Context->forget_old_sessions: " |
"garbing session %O due to session_lifetime limit\n", |
pair[1]); |
m_delete (session_cache, [string]([array]active_sessions->get())[1]); |
} |
} |
|
|
|
|
Session lookup_session(string id) |
{ |
if (use_cache) |
return session_cache[id]; |
else |
return 0; |
} |
|
|
|
|
|
|
|
|
|
|
|
|
Session decode_ticket(string(8bit) ticket) |
{ |
return lookup_session(ticket); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
array(string(8bit)|int) encode_ticket(Session session) |
{ |
if (!use_cache) return 0; |
string(8bit) ticket = session->ticket; |
if (!sizeof(ticket||"")) { |
do { |
ticket = random(32); |
} while(session_cache[ticket]); |
|
|
session->ticket = ticket; |
session->ticket_expiry_time = time(1) + 3600; |
} |
string(8bit) orig_id = session->identity; |
session->identity = ticket; |
record_session(session); |
session->identity = orig_id; |
|
return ({ ticket, 3600 }); |
} |
|
|
Session new_session() |
{ |
string(8bit) id = ""; |
if(use_cache) |
do { |
id = random(32); |
} while( session_cache[id] ); |
|
return Session(id); |
} |
|
|
void record_session(Session s) |
{ |
if (use_cache && sizeof(s->identity||"")) |
{ |
while (sizeof (active_sessions) >= max_sessions) { |
array pair = [array] active_sessions->get(); |
SSL3_DEBUG_MSG("SSL.Context->record_session: " |
"garbing session %O due to max_sessions limit\n", pair[1]); |
m_delete (session_cache, [string]pair[1]); |
} |
forget_old_sessions(); |
SSL3_DEBUG_MSG("SSL.Context->record_session: caching session %O\n", |
s->identity); |
active_sessions->put( ({ time(1), s->identity }) ); |
session_cache[s->identity] = s; |
} |
} |
|
|
void purge_session(Session s) |
{ |
SSL3_DEBUG_MSG("SSL.Context->purge_session: %O\n", s->identity || ""); |
if (s->identity) |
m_delete (session_cache, s->identity); |
|
|
|
|
|
|
s->identity = 0; |
if (s->version > PROTOCOL_TLS_1_2) { |
|
|
|
s->master_secret = 0; |
} |
|
} |
|
|
|
|
|
|
protected Crypto.RSA.State compat_rsa; |
protected array(string(8bit)) compat_certificates; |
|
|
|
|
|
|
|
|
|
__deprecated__ Crypto.RSA.State `rsa() |
{ |
return compat_rsa; |
} |
|
|
|
|
|
|
|
|
|
__deprecated__ void `rsa=(Crypto.RSA.State k) |
{ |
compat_rsa = k; |
if (k && compat_certificates) { |
catch { |
add_cert(k, compat_certificates); |
}; |
} |
} |
|
|
|
|
|
|
|
|
|
|
__deprecated__ array(string(8bit)) `certificates() |
{ |
return compat_certificates; |
} |
|
|
|
|
|
|
|
|
|
|
__deprecated__ void `certificates=(array(string(8bit)) certs) |
{ |
compat_certificates = certs; |
|
if (compat_rsa && certs) { |
catch { |
add_cert(compat_rsa, certs); |
}; |
} |
} |
|
|
|
|
|
|
|
|
|
__deprecated__ Crypto.RSA.State `client_rsa() |
{ |
return compat_rsa; |
} |
|
|
|
|
|
|
|
|
|
__deprecated__ void `client_rsa=(Crypto.RSA.State k) |
{ |
compat_rsa = k; |
if (k && compat_certificates) { |
catch { |
add_cert(k, compat_certificates); |
}; |
} |
} |
|
|
|
|
|
|
|
|
|
|
__deprecated__ array(array(string(8bit))) `client_certificates() |
{ |
return compat_certificates && ({ compat_certificates }); |
} |
|
|
|
|
|
|
|
|
|
|
__deprecated__ void `client_certificates=(array(array(string(8bit))) certs) |
{ |
compat_certificates = certs && (sizeof(certs)?certs[0]:({})); |
|
if (compat_rsa && certs) { |
foreach(certs, array(string(8bit)) chain) { |
catch { |
add_cert(compat_rsa, chain); |
}; |
} |
} |
} |
|
|
|
__deprecated__ Crypto.DSA.State `dsa() |
{ |
return UNDEFINED; |
} |
|
|
|
__deprecated__ void `dsa=(Crypto.DSA.State k) |
{ |
error("The old DSA API is not supported anymore.\n"); |
} |
|
|
|
|
|
|
|
|
|
|
__deprecated__ void rsa_mode(int(0..)|void min_keylength) |
{ |
SSL3_DEBUG_MSG("SSL.Context: rsa_mode()\n"); |
preferred_suites = get_suites(min_keylength, 1); |
} |
|
|
|
|
|
|
|
|
|
|
__deprecated__ void dhe_dss_mode(int(0..)|void min_keylength) |
{ |
SSL3_DEBUG_MSG("SSL.Context: dhe_dss_mode()\n"); |
preferred_suites = get_suites(min_keylength, 1); |
} |
|
|