X-Git-Url: http://git.openwrt.org/?a=blobdiff_plain;f=ustream-openssl.c;h=a45e2f49714fb67fa787e481d90ef3e945178533;hb=ba34903f4163d0650118bee76f012de961951409;hp=c826e4e9bace45ccf5899528fe56f5e85952e515;hpb=f19b2ec17f0c3d663a5f349de07b12af40016902;p=project%2Fustream-ssl.git diff --git a/ustream-openssl.c b/ustream-openssl.c index c826e4e..a45e2f4 100644 --- a/ustream-openssl.c +++ b/ustream-openssl.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include +#include #include "ustream-ssl.h" #include "ustream-internal.h" @@ -48,17 +50,27 @@ __ustream_ssl_context_new(bool server) if (!c) return NULL; - if (server) - SSL_CTX_set_verify(c, SSL_VERIFY_NONE, NULL); + SSL_CTX_set_verify(c, SSL_VERIFY_NONE, NULL); return (void *) c; } +__hidden int __ustream_ssl_add_ca_crt_file(struct ustream_ssl_ctx *ctx, const char *file) +{ + int ret; + + ret = SSL_CTX_load_verify_locations((void *) ctx, file, NULL); + if (ret < 1) + return -1; + + return 0; +} + __hidden int __ustream_ssl_set_crt_file(struct ustream_ssl_ctx *ctx, const char *file) { int ret; - ret = SSL_CTX_use_certificate_file((void *) ctx, file, SSL_FILETYPE_PEM); + ret = SSL_CTX_use_certificate_chain_file((void *) ctx, file); if (ret < 1) ret = SSL_CTX_use_certificate_file((void *) ctx, file, SSL_FILETYPE_ASN1); @@ -93,6 +105,133 @@ static void ustream_ssl_error(struct ustream_ssl *us, int ret) uloop_timeout_set(&us->error_timer, 0); } +static bool host_pattern_match(const unsigned char *pattern, const char *cn) +{ + char c; + + for (; (c = tolower(*pattern++)) != 0; cn++) { + if (c != '*') { + if (c != *cn) + return false; + continue; + } + + do { + c = tolower(*pattern++); + } while (c == '*'); + + while (*cn) { + if (c == tolower(*cn) && + host_pattern_match(pattern, cn)) + return true; + if (*cn == '.') + return false; + cn++; + } + + return !c; + } + return !*cn; +} + +static bool host_pattern_match_asn1(ASN1_STRING *asn1, const char *cn) +{ + unsigned char *pattern; + bool ret = false; + + if (ASN1_STRING_to_UTF8(&pattern, asn1) < 0) + return false; + + if (!pattern) + return false; + + if (strlen((char *) pattern) == ASN1_STRING_length(asn1)) + ret = host_pattern_match(pattern, cn); + + OPENSSL_free(pattern); + + return ret; +} + +static bool ustream_ssl_verify_cn_alt(struct ustream_ssl *us, X509 *cert) +{ + GENERAL_NAMES *alt_names; + int i, n_alt; + + alt_names = X509_get_ext_d2i (cert, NID_subject_alt_name, NULL, NULL); + if (!alt_names) + return false; + + n_alt = sk_GENERAL_NAME_num(alt_names); + for (i = 0; i < n_alt; i++) { + const GENERAL_NAME *name = sk_GENERAL_NAME_value(alt_names, i); + + if (!name) + continue; + + if (name->type != GEN_DNS) + continue; + + if (host_pattern_match_asn1(name->d.dNSName, us->peer_cn)) + return true; + } + + return false; +} + +static bool ustream_ssl_verify_cn(struct ustream_ssl *us, X509 *cert) +{ + ASN1_STRING *astr; + X509_NAME *xname; + int i, last; + + if (!us->peer_cn) + return false; + + if (ustream_ssl_verify_cn_alt(us, cert)) + return true; + + xname = X509_get_subject_name(cert); + + last = -1; + while (1) { + i = X509_NAME_get_index_by_NID(xname, NID_commonName, last); + if (i < 0) + break; + + last = i; + } + + if (last < 0) + return false; + + astr = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(xname, last)); + + return host_pattern_match_asn1(astr, us->peer_cn); +} + + +static void ustream_ssl_verify_cert(struct ustream_ssl *us) +{ + void *ssl = us->ssl; + X509 *cert; + int res; + + cert = SSL_get_peer_certificate(ssl); + if (!cert) + return; + + res = SSL_get_verify_result(ssl); + if (res != X509_V_OK) { + if (us->notify_verify_error) + us->notify_verify_error(us, res, X509_verify_cert_error_string(res)); + return; + } + + us->valid_cert = true; + us->valid_cn = ustream_ssl_verify_cn(us, cert); +} + __hidden enum ssl_conn_status __ustream_ssl_connect(struct ustream_ssl *us) { void *ssl = us->ssl; @@ -103,8 +242,10 @@ __hidden enum ssl_conn_status __ustream_ssl_connect(struct ustream_ssl *us) else r = SSL_connect(ssl); - if (r == 1) + if (r == 1) { + ustream_ssl_verify_cert(us); return U_SSL_OK; + } r = SSL_get_error(ssl, r); if (r == SSL_ERROR_WANT_READ || r == SSL_ERROR_WANT_WRITE)