diff options
Diffstat (limited to 'src/tls_remote.c')
-rw-r--r-- | src/tls_remote.c | 283 |
1 files changed, 147 insertions, 136 deletions
diff --git a/src/tls_remote.c b/src/tls_remote.c index 1318b4e..7986077 100644 --- a/src/tls_remote.c +++ b/src/tls_remote.c @@ -1,14 +1,17 @@ +#include "tls_remote.h" + #include <unistd.h> -#include "ucspissl.h" -#include "fmt.h" -#include "stralloc.h" -#include "str.h" + #include "byte.h" #include "case.h" -#include "dns.h" #include "constmap.h" -#include "tls_remote.h" +#include "fmt.h" +#include "str.h" +#include "stralloc.h" + +#include "dns.h" #include "tls_errors.h" +#include "ucspissl.h" /** @file tls_remote.c -- TLS client functions @brief connection functions: tls_conn, tls_exit; @@ -26,43 +29,40 @@ #define X509_cert_digest X509_digest -int tls_certkey(SSL_CTX *ctx,const char *cert,const char *key,char *ppwd) +int tls_certkey(SSL_CTX *ctx, const char *cert, const char *key, char *ppwd) { if (!cert) return 0; - if (SSL_CTX_use_certificate_chain_file(ctx,cert) != 1) - return -1; + if (SSL_CTX_use_certificate_chain_file(ctx, cert) != 1) return -1; if (!key) key = cert; - if (ppwd) SSL_CTX_set_default_passwd_cb_userdata(ctx,ppwd); + if (ppwd) SSL_CTX_set_default_passwd_cb_userdata(ctx, ppwd); - if (SSL_CTX_use_PrivateKey_file(ctx,key,SSL_FILETYPE_PEM) != 1) - return -2; + if (SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) != 1) return -2; - if (SSL_CTX_check_private_key(ctx) != 1) - return -3; + if (SSL_CTX_check_private_key(ctx) != 1) return -3; return 0; } -int tls_conn(SSL *ssl,int smtpfd) +int tls_conn(SSL *ssl, int smtpfd) { - SSL_set_options(ssl,SSL_OP_NO_SSLv2); - SSL_set_options(ssl,SSL_OP_NO_SSLv3); - return SSL_set_fd(ssl,smtpfd); + SSL_set_options(ssl, SSL_OP_NO_SSLv2); + SSL_set_options(ssl, SSL_OP_NO_SSLv3); + return SSL_set_fd(ssl, smtpfd); } -int tls_checkpeer(SSL *ssl,X509 *cert,const stralloc host,const int flag,const int verify) +int tls_checkpeer(SSL *ssl, X509 *cert, const stralloc host, const int flag, const int verify) { - STACK_OF(GENERAL_NAME) *extensions; + STACK_OF(GENERAL_NAME) * extensions; const GENERAL_NAME *ext; char buf[SSL_NAME_LEN]; char *dnsname = 0; int dname = 0; int num; int len; - int fflag; + int fflag; int i; int rc = 0; @@ -73,58 +73,64 @@ int tls_checkpeer(SSL *ssl,X509 *cert,const stralloc host,const int flag,const i /* X.509 CA DN/SAN name validation against DNS */ if (host.len && fflag > 4) { - extensions = (GENERAL_NAME *)X509_get_ext_d2i(cert,NID_subject_alt_name,0,0); - num = sk_GENERAL_NAME_num(extensions); /* num = 0, if no SAN extensions */ + extensions = (GENERAL_NAME *)X509_get_ext_d2i(cert, NID_subject_alt_name, 0, 0); + num = sk_GENERAL_NAME_num(extensions); /* num = 0, if no SAN extensions */ for (i = 0; i < num; ++i) { - ext = sk_GENERAL_NAME_value(extensions,i); + ext = sk_GENERAL_NAME_value(extensions, i); if (ext->type == GEN_DNS) { - #if (OPENSSL_VERSION_NUMBER < 0x10100000L) // 0xmnnffppsL +#if (OPENSSL_VERSION_NUMBER < 0x10100000L) // 0xmnnffppsL if (ASN1_STRING_type(ext->d.ia5) != V_ASN1_IA5STRING) continue; dnsname = (char *)ASN1_STRING_data(ext->d.ia5); - #else - if (OBJ_sn2nid((const char*)ext->d.ia5) != V_ASN1_IA5STRING) continue; +#else + if (OBJ_sn2nid((const char *)ext->d.ia5) != V_ASN1_IA5STRING) continue; dnsname = (char *)ASN1_STRING_get0_data(ext->d.ia5); - #endif +#endif len = ASN1_STRING_length(ext->d.ia5); dname = 1; } } if (!dname) { - X509_NAME_get_text_by_NID(X509_get_subject_name(cert),NID_commonName,buf,sizeof(buf)); + X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName, buf, sizeof(buf)); buf[SSL_NAME_LEN - 1] = 0; dnsname = buf; len = SSL_NAME_LEN - 1; } switch (fflag) { - case 5: if (dnsname[0] == '*' && dnsname[1] == '.') - if (case_diffrs(dnsname + 1,host.s)) return -3; - if (case_diffrs(dnsname,host.s)) return -3; - rc = 3; break; - case 6: if (case_diffs(dnsname,host.s)) return -3; - rc = 2; break; + case 5: + if (dnsname[0] == '*' && dnsname[1] == '.') + if (case_diffrs(dnsname + 1, host.s)) return -3; + if (case_diffrs(dnsname, host.s)) return -3; + rc = 3; + break; + case 6: + if (case_diffs(dnsname, host.s)) return -3; + rc = 2; + break; } } /* X.509 CA Verification: root CA must be available */ - if (fflag > 3 && verify > -2) { - if (SSL_get_verify_result(ssl) != X509_V_OK) return -2; - else rc = 1; + if (fflag > 3 && verify > -2) { + if (SSL_get_verify_result(ssl) != X509_V_OK) + return -2; + else + rc = 1; } return rc; } -int tls_checkcrl(SSL *ssl) // not implemented yet +int tls_checkcrl(SSL *ssl) // not implemented yet { return 0; } -int dig_ascii(char *digascii,const char *digest,const int len) +int dig_ascii(char *digascii, const char *digest, const int len) { static const char hextab[] = "0123456789abcdef"; int j; @@ -143,25 +149,25 @@ int dig_ascii(char *digascii,const char *digest,const int len) Subjects keys are restricted to 2048 byte in size. Return codes: 1: sucess, 0: failed. */ -int X509_pkey_digest(const X509 *cert,const EVP_MD *type,unsigned char *md,unsigned int *dlen) +int X509_pkey_digest(const X509 *cert, const EVP_MD *type, unsigned char *md, unsigned int *dlen) { unsigned int len = 0; unsigned int size = 2048; unsigned char *buf; unsigned char *buf2; - unsigned char buffer[size]; // avoid malloc + unsigned char buffer[size]; // avoid malloc -/* Following Viktor's suggestion */ + /* Following Viktor's suggestion */ if (!X509_get0_pubkey_bitstr(cert)) return 0; // no Subject public key - len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert),0); + len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), 0); if (len > size) return 0; buf2 = buf = buffer; - i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert),(unsigned char **)&buf2); + i2d_X509_PUBKEY(X509_get_X509_PUBKEY(cert), (unsigned char **)&buf2); if (buf2 - buf != len) return 0; - if (!EVP_Digest(buf,len,md,dlen,type,0)) return 0; // OpenSSL voodoo + if (!EVP_Digest(buf, len, md, dlen, type, 0)) return 0; // OpenSSL voodoo return 1; } @@ -169,7 +175,7 @@ int X509_pkey_digest(const X509 *cert,const EVP_MD *type,unsigned char *md,unsig -2: unsupported type, -1: weird TLSA record 0: No X.509 cert; seen: usage++; */ -int tlsa_check(const STACK_OF(X509) *certs,const stralloc host,const unsigned long p) +int tlsa_check(const STACK_OF(X509) * certs, const stralloc host, const unsigned long p) { const EVP_MD *methodsha256 = EVP_sha256(); const EVP_MD *methodsha512 = EVP_sha512(); @@ -180,25 +186,27 @@ int tlsa_check(const STACK_OF(X509) *certs,const stralloc host,const unsigned lo unsigned int dlen = 0; unsigned int n = 0; int i = 0; - int r; + int r; char port[FMT_ULONG]; uint16 type; uint16 selector; uint16 usage; -// construct TLSA FQDN -- simple procedure; returning Usage + // construct TLSA FQDN -- simple procedure; returning Usage if (host.len < 2) return 0; - if (!stralloc_copyb(&sa,"_",1)) temp_nomem(); - port[fmt_ulong(port,p)] = 0; - if (!stralloc_cats(&sa,port)) temp_nomem(); - if (!stralloc_cats(&sa,"._tcp.")) temp_nomem(); - if (!stralloc_cats(&sa,host.s)) temp_nomem(); - - if (dns_cname(&cn,&sa) > 0) // query name could be a cname - { if (dns_tlsa(&out,&cn) <= 0) return 0; } - else - { if (dns_tlsa(&out,&sa) <= 0) return 0; } + if (!stralloc_copyb(&sa, "_", 1)) temp_nomem(); + port[fmt_ulong(port, p)] = 0; + if (!stralloc_cats(&sa, port)) temp_nomem(); + if (!stralloc_cats(&sa, "._tcp.")) temp_nomem(); + if (!stralloc_cats(&sa, host.s)) temp_nomem(); + + if (dns_cname(&cn, &sa) > 0) // query name could be a cname + { + if (dns_tlsa(&out, &cn) <= 0) return 0; + } else { + if (dns_tlsa(&out, &sa) <= 0) return 0; + } if (out.len < 5) return -1; /* https://www.openssl.org/docs/man3.0/man3/X509_digest.html (1.1.1): @@ -207,23 +215,23 @@ int tlsa_check(const STACK_OF(X509) *certs,const stralloc host,const unsigned lo */ do { - usage = (unsigned char) out.s[i]; // Usage: PKIX-TA [0], PKIX-EE [1], DANE-TA [2], DANE-EE [3] - selector = (unsigned char) out.s[i + 1]; // Selector: 0 = Cert, 1 = SPKI - type = (unsigned char) out.s[i + 2]; // Type: 0/1/2 = [Cert|SPKI]/SHA256/SHA512 + usage = (unsigned char)out.s[i]; // Usage: PKIX-TA [0], PKIX-EE [1], DANE-TA [2], DANE-EE [3] + selector = (unsigned char)out.s[i + 1]; // Selector: 0 = Cert, 1 = SPKI + type = (unsigned char)out.s[i + 2]; // Type: 0/1/2 = [Cert|SPKI]/SHA256/SHA512 unsigned len = sk_X509_num(certs); - for (n = 0; n < len; n++) { - X509 *cert = sk_X509_value(certs,n); + for (n = 0; n < len; n++) { + X509 *cert = sk_X509_value(certs, n); if (type == 1) { - if (selector == 0) r = X509_cert_digest(cert,methodsha256,digest,&dlen); - if (selector == 1) r = X509_pkey_digest(cert,methodsha256,digest,&dlen); + if (selector == 0) r = X509_cert_digest(cert, methodsha256, digest, &dlen); + if (selector == 1) r = X509_pkey_digest(cert, methodsha256, digest, &dlen); } else if (type == 2) { - if (selector == 0) r = X509_cert_digest(cert,methodsha512,digest,&dlen); - if (selector == 1) r = X509_pkey_digest(cert,methodsha512,digest,&dlen); - } else + if (selector == 0) r = X509_cert_digest(cert, methodsha512, digest, &dlen); + if (selector == 1) r = X509_pkey_digest(cert, methodsha512, digest, &dlen); + } else return -2; - if (!byte_diff(digest,dlen,out.s + i + 3)) return ++usage; + if (!byte_diff(digest, dlen, out.s + i + 3)) return ++usage; } i += (dlen + 3); @@ -232,7 +240,7 @@ int tlsa_check(const STACK_OF(X509) *certs,const stralloc host,const unsigned lo return -3; } -int tls_fingerprint(X509 *cert,const char *fingerprint,int dlen) +int tls_fingerprint(X509 *cert, const char *fingerprint, int dlen) { const EVP_MD *methodsha1 = EVP_sha1(); const EVP_MD *methodsha224 = EVP_sha224(); @@ -242,24 +250,27 @@ int tls_fingerprint(X509 *cert,const char *fingerprint,int dlen) unsigned char digascii[257]; unsigned int len; - switch (dlen) { /* fetch digest from cert; len = bitlength/8 */ - case 40: if (!X509_digest(cert,methodsha1,digest,&len)) return -2; - case 56: if (!X509_digest(cert,methodsha224,digest,&len)) return -2; - case 64: if (!X509_digest(cert,methodsha256,digest,&len)) return -2; - case 128: if (!X509_digest(cert,methodsha512,digest,&len)) return -2; - default: return -3; + switch (dlen) { /* fetch digest from cert; len = bitlength/8 */ + case 40: + if (!X509_digest(cert, methodsha1, digest, &len)) return -2; + case 56: + if (!X509_digest(cert, methodsha224, digest, &len)) return -2; + case 64: + if (!X509_digest(cert, methodsha256, digest, &len)) return -2; + case 128: + if (!X509_digest(cert, methodsha512, digest, &len)) return -2; + default: return -3; } - len = dig_ascii(digascii,digest,len); - if (!str_diffn(digascii,fingerprint,len)) return 1; + len = dig_ascii(digascii, digest, len); + if (!str_diffn(digascii, fingerprint, len)) return 1; return 0; } int tls_exit(SSL *ssl) { - if (SSL_shutdown(ssl) == 0) - SSL_shutdown(ssl); + if (SSL_shutdown(ssl) == 0) SSL_shutdown(ssl); return 0; } @@ -283,86 +294,86 @@ int tls_destination(const stralloc hostname) stralloc tlshost = {0}; stralloc tlsdest = {0}; - if (!stralloc_copy(&tlshost,&hostname)) temp_nomem(); + if (!stralloc_copy(&tlshost, &hostname)) temp_nomem(); if (!stralloc_0(&tlshost)) temp_nomem(); -// Host rules + // Host rules + + if (!stralloc_copys(&tlsdest, "!")) temp_nomem(); + if (!stralloc_cats(&tlsdest, tlshost.s)) temp_nomem(); + if ((tlsdestinfo = constmap(&maptlsdestinations, tlsdest.s, tlsdest.len))) return -1; - if (!stralloc_copys(&tlsdest,"!")) temp_nomem(); - if (!stralloc_cats(&tlsdest,tlshost.s)) temp_nomem(); - if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return -1; + if (!stralloc_copys(&tlsdest, "?")) temp_nomem(); + if (!stralloc_cats(&tlsdest, tlshost.s)) temp_nomem(); + if ((tlsdestinfo = constmap(&maptlsdestinations, tlsdest.s, tlsdest.len))) return 9; - if (!stralloc_copys(&tlsdest,"?")) temp_nomem(); - if (!stralloc_cats(&tlsdest,tlshost.s)) temp_nomem(); - if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 9; + if (!stralloc_copys(&tlsdest, "/")) temp_nomem(); + if (!stralloc_cats(&tlsdest, tlshost.s)) temp_nomem(); + if ((tlsdestinfo = constmap(&maptlsdestinations, tlsdest.s, tlsdest.len))) return 8; - if (!stralloc_copys(&tlsdest,"/")) temp_nomem(); - if (!stralloc_cats(&tlsdest,tlshost.s)) temp_nomem(); - if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 8; + if (!stralloc_copys(&tlsdest, "%")) temp_nomem(); // CERT + hash + if (!stralloc_cats(&tlsdest, tlshost.s)) temp_nomem(); + if ((tlsdestinfo = constmap(&maptlsdestinations, tlsdest.s, tlsdest.len))) return 7; - if (!stralloc_copys(&tlsdest,"%")) temp_nomem(); // CERT + hash - if (!stralloc_cats(&tlsdest,tlshost.s)) temp_nomem(); - if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 7; + if (!stralloc_copys(&tlsdest, "=")) temp_nomem(); // CERT + FQDN + if (!stralloc_cats(&tlsdest, tlshost.s)) temp_nomem(); + if ((tlsdestinfo = constmap(&maptlsdestinations, tlsdest.s, tlsdest.len))) return 6; - if (!stralloc_copys(&tlsdest,"=")) temp_nomem(); // CERT + FQDN - if (!stralloc_cats(&tlsdest,tlshost.s)) temp_nomem(); - if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 6; - - if (!stralloc_copys(&tlsdest,"~")) temp_nomem(); // CERT + Wild - if (!stralloc_cats(&tlsdest,tlshost.s)) temp_nomem(); - if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 5; - -// Domain rules + if (!stralloc_copys(&tlsdest, "~")) temp_nomem(); // CERT + Wild + if (!stralloc_cats(&tlsdest, tlshost.s)) temp_nomem(); + if ((tlsdestinfo = constmap(&maptlsdestinations, tlsdest.s, tlsdest.len))) return 5; + + // Domain rules for (i = 0; i < tlshost.len; ++i) // TLS fallthru if ((i == 0) || (tlshost.s[i] == '.')) { - if (!stralloc_copys(&tlsdest,"?")) temp_nomem(); - if (!stralloc_cats(&tlsdest,tlshost.s + i)) temp_nomem(); - if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 9; + if (!stralloc_copys(&tlsdest, "?")) temp_nomem(); + if (!stralloc_cats(&tlsdest, tlshost.s + i)) temp_nomem(); + if ((tlsdestinfo = constmap(&maptlsdestinations, tlsdest.s, tlsdest.len))) return 9; } for (i = 0; i < tlshost.len; ++i) // no TLSA if ((i == 0) || (tlshost.s[i] == '.')) { - if (!stralloc_copys(&tlsdest,"/")) temp_nomem(); - if (!stralloc_cats(&tlsdest,tlshost.s + i)) temp_nomem(); - if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 8; + if (!stralloc_copys(&tlsdest, "/")) temp_nomem(); + if (!stralloc_cats(&tlsdest, tlshost.s + i)) temp_nomem(); + if ((tlsdestinfo = constmap(&maptlsdestinations, tlsdest.s, tlsdest.len))) return 8; } for (i = 0; i < tlshost.len; ++i) // CERT + Wild if ((i == 0) || (tlshost.s[i] == '.')) { - if (!stralloc_copys(&tlsdest,"~")) temp_nomem(); - if (!stralloc_cats(&tlsdest,tlshost.s + i)) temp_nomem(); - if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 5; + if (!stralloc_copys(&tlsdest, "~")) temp_nomem(); + if (!stralloc_cats(&tlsdest, tlshost.s + i)) temp_nomem(); + if ((tlsdestinfo = constmap(&maptlsdestinations, tlsdest.s, tlsdest.len))) return 5; } - for (i = 0; i < tlshost.len; ++i) // CERT - generic + for (i = 0; i < tlshost.len; ++i) // CERT - generic if ((i == 0) || (tlshost.s[i] == '.')) { - if (!stralloc_copys(&tlsdest,"")) temp_nomem(); - if (!stralloc_cats(&tlsdest,tlshost.s + i)) temp_nomem(); - if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 4; + if (!stralloc_copys(&tlsdest, "")) temp_nomem(); + if (!stralloc_cats(&tlsdest, tlshost.s + i)) temp_nomem(); + if ((tlsdestinfo = constmap(&maptlsdestinations, tlsdest.s, tlsdest.len))) return 4; } for (i = 0; i < tlshost.len; ++i) // ADH per host/domain if ((i == 0) || (tlshost.s[i] == '.')) { - if (!stralloc_copys(&tlsdest,"-")) temp_nomem(); - if (!stralloc_cats(&tlsdest,tlshost.s + i)) temp_nomem(); - if ((tlsdestinfo = constmap(&maptlsdestinations,tlsdest.s,tlsdest.len))) return 2; + if (!stralloc_copys(&tlsdest, "-")) temp_nomem(); + if (!stralloc_cats(&tlsdest, tlshost.s + i)) temp_nomem(); + if ((tlsdestinfo = constmap(&maptlsdestinations, tlsdest.s, tlsdest.len))) return 2; } -// General rules (mandatory TLS) + // General rules (mandatory TLS) tlsdestinfo = 0; - if (constmap(&maptlsdestinations,"/*",2)) return 8; // no TLSA - if (constmap(&maptlsdestinations,"=*",2)) return 6; // CERT + FQDN - if (constmap(&maptlsdestinations,"~*",2)) return 5; // CERT + Wild - if (constmap(&maptlsdestinations,"+*",2)) return 4; // CERT - if (constmap(&maptlsdestinations,"-*",2)) return 2; // ADH + if (constmap(&maptlsdestinations, "/*", 2)) return 8; // no TLSA + if (constmap(&maptlsdestinations, "=*", 2)) return 6; // CERT + FQDN + if (constmap(&maptlsdestinations, "~*", 2)) return 5; // CERT + Wild + if (constmap(&maptlsdestinations, "+*", 2)) return 4; // CERT + if (constmap(&maptlsdestinations, "-*", 2)) return 2; // ADH -// Fall thru rules (optional TLS) + // Fall thru rules (optional TLS) - if (constmap(&maptlsdestinations,"?",1)) return 9; // fallback to no TLS - if (constmap(&maptlsdestinations,"*",1)) return 3; // CERT - if (constmap(&maptlsdestinations,"-",1)) return 1; // ADH + if (constmap(&maptlsdestinations, "?", 1)) return 9; // fallback to no TLS + if (constmap(&maptlsdestinations, "*", 1)) return 3; // CERT + if (constmap(&maptlsdestinations, "-", 1)) return 1; // ADH return 0; } @@ -370,18 +381,18 @@ int tls_destination(const stralloc hostname) int tls_domaincerts(const stralloc domainname) { int i; - tlsdomaininfo = 0; // extern + tlsdomaininfo = 0; // extern -/* Our Certs - per domain */ + /* Our Certs - per domain */ if (domainname.len) for (i = 0; i < domainname.len; ++i) if ((i == 0) || (domainname.s[i] == '.')) - if ((tlsdomaininfo = constmap(&mapdomaincerts,domainname.s + i,domainname.len - i))) return 2; + if ((tlsdomaininfo = constmap(&mapdomaincerts, domainname.s + i, domainname.len - i))) return 2; + + /* Standard Cert (if any) */ -/* Standard Cert (if any) */ - - if ((tlsdomaininfo = constmap(&mapdomaincerts,"*",1))) return 1; + if ((tlsdomaininfo = constmap(&mapdomaincerts, "*", 1))) return 1; return 0; } |