diff options
author | Timo Teräs <timo.teras@iki.fi> | 2017-10-05 16:39:47 +0300 |
---|---|---|
committer | Timo Teräs <timo.teras@iki.fi> | 2017-10-05 16:59:14 +0300 |
commit | 52fd85a8dcfee9c93522d80693673bc95cc1caaf (patch) | |
tree | 33022a0fb973c548ea7e1fdf2b1e9f1ff268f041 /libfetch/common.c | |
parent | 531fea4c9082d6542c776dcb6d4ca0a3949ecdd0 (diff) | |
download | apk-tools-52fd85a8dcfee9c93522d80693673bc95cc1caaf.tar.gz apk-tools-52fd85a8dcfee9c93522d80693673bc95cc1caaf.tar.bz2 apk-tools-52fd85a8dcfee9c93522d80693673bc95cc1caaf.tar.xz apk-tools-52fd85a8dcfee9c93522d80693673bc95cc1caaf.zip |
libfetch: improve ssl connections
loosely based on the freebsd implementation, implement https
connection settings to override CA, and use client certificate.
new files supported in /etc/apk/:
ca.pem - if exists, it contains CAs acceptable for https
(otherwise system wide CAs are used)
crl.pem - if ca.pem is used, this is the (optional) CRL for it
cert.pem - used as client authentication certificate (+ key)
cert.key - used as client key (can be also inside cert.pem)
Diffstat (limited to 'libfetch/common.c')
-rw-r--r-- | libfetch/common.c | 114 |
1 files changed, 108 insertions, 6 deletions
diff --git a/libfetch/common.c b/libfetch/common.c index 729c885..278c606 100644 --- a/libfetch/common.c +++ b/libfetch/common.c @@ -410,6 +410,87 @@ fetch_cache_put(conn_t *conn, int (*closecb)(conn_t *)) } /* + * Configure peer verification based on environment: + * 1. If compile time #define CA_CERT_FILE is set, and it exists, use it. + * 2. Use system default CA store settings. + */ +static int fetch_ssl_setup_peer_verification(SSL_CTX *ctx, int verbose) +{ + const char *ca_file = NULL; + +#ifdef CA_CERT_FILE + if (access(CA_CERT_FILE, R_OK) == 0) { + ca_file = CA_CERT_FILE; +#ifdef CA_CRL_FILE + if (access(CA_CRL_FILE, R_OK) == 0) { + X509_STORE *crl_store = SSL_CTX_get_cert_store(ctx); + X509_LOOKUP *crl_lookup = X509_STORE_add_lookup(crl_store, X509_LOOKUP_file()); + if (!crl_lookup || !X509_load_crl_file(crl_lookup, CA_CRL_FILE, X509_FILETYPE_PEM)) { + fprintf(stderr, "Could not load CRL file %s\n", CA_CRL_FILE); + return 0; + } + X509_STORE_set_flags(crl_store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); + } +#endif + } +#endif + if (ca_file) + SSL_CTX_load_verify_locations(ctx, ca_file, NULL); + else + SSL_CTX_set_default_verify_paths(ctx); + + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, 0); + return 1; +} + +/* + * Configure client certificate based on environment: + * 1. Use SSL_CLIENT_{CERT,KEY}_FILE environment variables if set + * 2. Use compile time set CLIENT_{CERT,KEY}_FILE #define's if set + * 3. No client certificate used + * + * If the key file is not specified, it is assumed that the certificate + * file is a .pem file containing both the cert and the key. + */ +static int fetch_ssl_setup_client_certificate(SSL_CTX *ctx, int verbose) +{ + const char *cert_file = NULL, *key_file = NULL; + + cert_file = getenv("SSL_CLIENT_CERT_FILE"); + if (cert_file) key_file = getenv("SSL_CLIENT_KEY_FILE"); + +#ifdef CLIENT_CERT_FILE + if (!cert_file && access(CLIENT_CERT_FILE, R_OK) == 0) { + cert_file = CLIENT_CERT_FILE; +#ifdef CLIENT_KEY_FILE + if (access(CLIENT_KEY_FILE, R_OK) == 0) + key_file = CLIENT_KEY_FILE; +#endif + } +#endif + if (!cert_file) return 1; + if (!key_file) key_file = cert_file; + + if (verbose) { + fetch_info("Using client cert file: %s", cert_file); + fetch_info("Using client key file: %s", key_file); + } + + if (SSL_CTX_use_certificate_chain_file(ctx, cert_file) != 1) { + fprintf(stderr, "Could not load client certificate %s\n", + cert_file); + return 0; + } + + if (SSL_CTX_use_PrivateKey_file(ctx, key_file, SSL_FILETYPE_PEM) != 1) { + fprintf(stderr, "Could not load client key %s\n", key_file); + return 0; + } + + return 1; +} + +/* * Enable SSL on a connection. */ int @@ -423,10 +504,15 @@ fetch_ssl(conn_t *conn, const struct url *URL, int verbose) SSL_load_error_strings(); - conn->ssl_meth = SSLv23_client_method(); + conn->ssl_meth = TLS_client_method(); conn->ssl_ctx = SSL_CTX_new(conn->ssl_meth); SSL_CTX_set_mode(conn->ssl_ctx, SSL_MODE_AUTO_RETRY); + if (!fetch_ssl_setup_peer_verification(conn->ssl_ctx, verbose)) + return (-1); + if (!fetch_ssl_setup_client_certificate(conn->ssl_ctx, verbose)) + return (-1); + conn->ssl = SSL_new(conn->ssl_ctx); if (conn->ssl == NULL){ fprintf(stderr, "SSL context creation failed\n"); @@ -446,20 +532,36 @@ fetch_ssl(conn_t *conn, const struct url *URL, int verbose) return (-1); } + conn->ssl_cert = SSL_get_peer_certificate(conn->ssl); + if (!conn->ssl_cert) { + fprintf(stderr, "No server SSL certificate\n"); + return -1; + } + + if (getenv("SSL_NO_VERIFY_HOSTNAME") == NULL) { + if (verbose) + fetch_info("Verify hostname"); + if (X509_check_host(conn->ssl_cert, URL->host, 0, + X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS, + NULL) != 1) { + fprintf(stderr, "SSL certificate subject doesn't match host %s\n", + URL->host); + return -1; + } + } + if (verbose) { X509_NAME *name; char *str; - fprintf(stderr, "SSL connection established using %s\n", - SSL_get_cipher(conn->ssl)); - conn->ssl_cert = SSL_get_peer_certificate(conn->ssl); + fetch_info("SSL connection established using %s\n", SSL_get_cipher(conn->ssl)); name = X509_get_subject_name(conn->ssl_cert); str = X509_NAME_oneline(name, 0, 0); - printf("Certificate subject: %s\n", str); + fetch_info("Certificate subject: %s", str); free(str); name = X509_get_issuer_name(conn->ssl_cert); str = X509_NAME_oneline(name, 0, 0); - printf("Certificate issuer: %s\n", str); + fetch_info("Certificate issuer: %s", str); free(str); } |