From 0d07ab80ab2fd80c7fc32c0adf7cc0d01028aec9 Mon Sep 17 00:00:00 2001 From: Andreas Baumann Date: Thu, 20 Apr 2017 16:37:16 +0000 Subject: probing for SSL library used by libcurl and started improving the certificate check -C --- plugins/check_curl.c | 186 ++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 147 insertions(+), 39 deletions(-) diff --git a/plugins/check_curl.c b/plugins/check_curl.c index b0026378..d3f3930f 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -91,6 +91,14 @@ typedef struct { char *first_line; /* a copy of the first line */ } curlhelp_statusline; +typedef enum curlhelp_ssl_library { + CURLHELP_SSL_LIBRARY_UNKNOWN, + CURLHELP_SSL_LIBRARY_OPENSSL, + CURLHELP_SSL_LIBRARY_LIBRESSL, + CURLHELP_SSL_LIBRARY_GNUTLS, + CURLHELP_SSL_LIBRARY_NSS +} curlhelp_ssl_library; + enum { REGS = 2, MAX_RE_SIZE = 256 @@ -160,12 +168,14 @@ int ssl_version = CURL_SSLVERSION_DEFAULT; char *client_cert = NULL; char *client_privkey = NULL; char *ca_cert = NULL; +int is_openssl_callback = FALSE; #ifdef HAVE_SSL X509 *cert = NULL; #endif int no_body = FALSE; int maximum_age = -1; int address_family = AF_UNSPEC; +curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN; int process_arguments (int, char**); void handle_curl_option_return_code (CURLcode res, const char* option); @@ -179,6 +189,8 @@ void curlhelp_freewritebuffer (curlhelp_write_curlbuf*); int curlhelp_initreadbuffer (curlhelp_read_curlbuf *, const char *, size_t); int curlhelp_buffer_read_callback (void *, size_t , size_t , void *); void curlhelp_freereadbuffer (curlhelp_read_curlbuf *); +curlhelp_ssl_library curlhelp_get_ssl_library (CURL*); +const char* curlhelp_get_ssl_library_string (curlhelp_ssl_library); int curlhelp_parse_statusline (const char*, curlhelp_statusline *); void curlhelp_free_statusline (curlhelp_statusline *); @@ -368,25 +380,59 @@ check_http (void) handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSL_VERIFYPEER, 0), "CURLOPT_SSL_VERIFYPEER"); handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSL_VERIFYHOST, 0), "CURLOPT_SSL_VERIFYHOST"); } + + /* detect SSL library used by libcurl */ + ssl_library = curlhelp_get_ssl_library (curl); /* try hard to get a stack of certificates to verify against */ if (check_cert) #if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) - /* inform curl to report back certificates (this works for OpenSSL, NSS at least) */ - curl_easy_setopt (curl, CURLOPT_CERTINFO, 1L); -#ifdef LIBCURL_USES_OPENSSL - /* set callback to extract certificate with OpenSSL context function (works with - * OpenSSL only!) - */ - handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), "CURLOPT_SSL_CTX_FUNCTION"); + /* inform curl to report back certificates */ + switch (ssl_library) { + case CURLHELP_SSL_LIBRARY_OPENSSL: + case CURLHELP_SSL_LIBRARY_LIBRESSL: + /* set callback to extract certificate with OpenSSL context function (works with + * OpenSSL-style libraries only!) */ +#ifdef USE_OPENSSL + /* libcurl and monitoring plugins built with OpenSSL, good */ + handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), "CURLOPT_SSL_CTX_FUNCTION"); + is_openssl_callback = TRUE; +#else /* USE_OPENSSL */ #endif /* USE_OPENSSL */ + /* libcurl is built with OpenSSL, monitoring plugins, so falling + * back to manually extracting certificate information */ + handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); + break; + + case CURLHELP_SSL_LIBRARY_NSS: +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) + /* NSS: support for CERTINFO is implemented since 7.34.0 */ + handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); +#else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ + die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library '%s' is too old)\n", curlhelp_get_ssl_library_string (ssl_library)); +#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 34, 0) */ + break; + + case CURLHELP_SSL_LIBRARY_GNUTLS: +#if LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) + /* GnuTLS: support for CERTINFO is implemented since 7.42.0 */ + handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CERTINFO, 1L), "CURLOPT_CERTINFO"); +#else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */ + die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (libcurl linked with SSL library '%s' is too old)\n", curlhelp_get_ssl_library_string (ssl_library)); +#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 42, 0) */ + break; + + case CURLHELP_SSL_LIBRARY_UNKNOWN: + default: + die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (unknown SSL library '%s', must implement first)\n", curlhelp_get_ssl_library_string (ssl_library)); + break; + } #else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */ -#ifdef LIBCURL_USES_OPENSSL - /* Too old curl library, hope we have OpenSSL */ - handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), "CURLOPT_SSL_CTX_FUNCTION"); -#else - die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library)\n"); -#endif /* LIBCURL_USES_OPENSSL */ + /* old libcurl, our only hope is OpenSSL, otherwise we are out of luck */ + if (ssl_library == CURLHELP_SSL_LIBRARY_OPENSSL || ssl_library == CURLHELP_SSL_LIBRARY_LIBRESSL) + handle_curl_option_return_code (curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun), "CURLOPT_SSL_CTX_FUNCTION"); + else + die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates (no CURLOPT_SSL_CTX_FUNCTION, no OpenSSL library or libcurl too old and has no CURLOPT_CERTINFO)\n"); #endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */ #endif /* LIBCURL_FEATURE_SSL */ @@ -480,35 +526,44 @@ check_http (void) #ifdef LIBCURL_FEATURE_SSL if (use_ssl == TRUE) { if (check_cert == TRUE) { - if (verbose >= 2) - printf ("**** REQUEST CERTIFICATES ****\n"); - cert_ptr.to_info = NULL; - res = curl_easy_getinfo (curl, CURLINFO_CERTINFO, &cert_ptr.to_info); - if (!res && cert_ptr.to_info) { - int i; - for (i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) { - struct curl_slist *slist; - for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) { - if (verbose >= 2) - printf ("%d ** %s\n", i, slist->data); + if (is_openssl_callback) { +#ifdef HAVE_SSL + /* check certificate with OpenSSL functions, curl has been built against OpenSSL + * and we actually have OpenSSL in the monitoring tools + */ + result = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit); + return result; +#else /* HAVE_SSL */ + die (STATE_CRITICAL, "HTTP CRITICAL - Cannot retrieve certificates - OpenSSL callback used and not linked against OpenSSL\n"); +#endif /* HAVE_SSL */ + } else { + /* going with the libcurl CURLINFO data */ + if (verbose >= 2) + printf ("**** REQUEST CERTIFICATES ****\n"); + cert_ptr.to_info = NULL; + res = curl_easy_getinfo (curl, CURLINFO_CERTINFO, &cert_ptr.to_info); + if (!res && cert_ptr.to_info) { + int i; + for (i = 0; i < cert_ptr.to_certinfo->num_of_certs; i++) { + struct curl_slist *slist; + for (slist = cert_ptr.to_certinfo->certinfo[i]; slist; slist = slist->next) { + if (verbose >= 2) + printf ("%d ** %s\n", i, slist->data); + } } + } else { + snprintf (msg, DEFAULT_BUFFER_SIZE, _("Cannot retrieve certificates - cURL returned %d - %s"), + res, curl_easy_strerror(res)); + die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); } - } else { - snprintf (msg, DEFAULT_BUFFER_SIZE, _("Cannot retrieve certificates - cURL returned %d - %s"), - res, curl_easy_strerror(res)); - die (STATE_CRITICAL, "HTTP CRITICAL - %s\n", msg); + if (verbose >= 2) + printf ("**** REQUEST CERTIFICATES ****\n"); + // TODO: either convert data to X509 certs we can check with np_net_ssl_check_certificate + // or do something on our own.. + //~ result = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit); + //~ return result; + die (STATE_UNKNOWN, "HTTP UNKNOWN - CERTINFO certificate checks not implemented yet\n"); } - if (verbose >= 2) - printf ("**** REQUEST CERTIFICATES ****\n"); - /* check certificate with OpenSSL functions, curl has been built against OpenSSL - * and we actually have OpenSSL in the monitoring tools - */ -#ifdef HAVE_SSL -#ifdef LIBCURL_USES_OPENSSL - result = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit); -#endif /* LIBCURL_USES_OPENSSL */ -#endif /* HAVE_SSL */ - return result; } } #endif /* LIBCURL_FEATURE_SSL */ @@ -1617,3 +1672,56 @@ check_document_dates (const curlhelp_write_curlbuf *header_buf, char (*msg)[DEFA return date_result; } + +/* TODO: is there a better way in libcurl to check for the SSL library? */ +curlhelp_ssl_library +curlhelp_get_ssl_library (CURL* curl) +{ + curl_version_info_data* version_data; + char *ssl_version; + char *library; + curlhelp_ssl_library ssl_library = CURLHELP_SSL_LIBRARY_UNKNOWN; + + version_data = curl_version_info (CURLVERSION_NOW); + if (version_data == NULL) return CURLHELP_SSL_LIBRARY_UNKNOWN; + + ssl_version = strdup (version_data->ssl_version); + if (ssl_version == NULL ) return CURLHELP_SSL_LIBRARY_UNKNOWN; + + library = strtok (ssl_version, "/"); + if (library == NULL) return CURLHELP_SSL_LIBRARY_UNKNOWN; + + if (strcmp (library, "OpenSSL") == 0) + ssl_library = CURLHELP_SSL_LIBRARY_OPENSSL; + else if (strcmp (library, "LibreSSL") == 0) + ssl_library = CURLHELP_SSL_LIBRARY_LIBRESSL; + else if (strcmp (library, "GnuTLS") == 0) + ssl_library = CURLHELP_SSL_LIBRARY_GNUTLS; + else if (strcmp (library, "NSS") == 0) + ssl_library = CURLHELP_SSL_LIBRARY_NSS; + + if (verbose >= 2) + printf ("* SSL library string is : %s %s (%d)\n", version_data->ssl_version, library, ssl_library); + + free (ssl_version); + + return ssl_library; +} + +const char* +curlhelp_get_ssl_library_string (curlhelp_ssl_library ssl_library) +{ + switch (ssl_library) { + case CURLHELP_SSL_LIBRARY_OPENSSL: + return "OpenSSL"; + case CURLHELP_SSL_LIBRARY_LIBRESSL: + return "LibreSSL"; + case CURLHELP_SSL_LIBRARY_GNUTLS: + return "GnuTLS"; + case CURLHELP_SSL_LIBRARY_NSS: + return "NSS"; + case CURLHELP_SSL_LIBRARY_UNKNOWN: + default: + return "unknown"; + } +} -- cgit v1.2.3-74-g34f1