From 55e8d89b0a24dc8a1a3d6ea031a2189b1cf3f497 Mon Sep 17 00:00:00 2001 From: Andreas Baumann Date: Wed, 19 Apr 2017 14:36:01 +0200 Subject: some work on certificate checking for non-OpenSSL libraries diff --git a/plugins/check_curl.c b/plugins/check_curl.c index ff79f15..5ed47e2 100644 --- a/plugins/check_curl.c +++ b/plugins/check_curl.c @@ -149,6 +149,10 @@ int onredirect = STATE_OK; int use_ssl = FALSE; int use_sni = TRUE; int check_cert = FALSE; +union { + struct curl_slist* to_info; + struct curl_certinfo* to_certinfo; +} cert_ptr; int ssl_version = CURL_SSLVERSION_DEFAULT; char *client_cert = NULL; char *client_privkey = NULL; @@ -212,6 +216,10 @@ main (int argc, char **argv) int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) { + /* TODO: we get all certificates of the chain, so which ones + * should we test? + * TODO: is the last certificate always the server certificate? + */ cert = X509_STORE_CTX_get_current_cert(x509_ctx); return 1; } @@ -330,6 +338,8 @@ check_http (void) /* set HTTP headers */ handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_HTTPHEADER, header_list ), "CURLOPT_HTTPHEADER"); +#ifdef LIBCURL_FEATURE_SSL + /* set SSL version, warn about unsecure or unsupported versions */ if (use_ssl) { handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_SSLVERSION, ssl_version), "CURLOPT_SSLVERSION"); @@ -344,7 +354,7 @@ check_http (void) /* per default if we have a CA verify both the peer and the * hostname in the certificate, can be switched off later */ handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_CAINFO, ca_cert), "CURLOPT_CAINFO"); - handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_SSL_VERIFYPEER, 2), "CURLOPT_SSL_VERIFYPEER"); + handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_SSL_VERIFYPEER, 1), "CURLOPT_SSL_VERIFYPEER"); handle_curl_option_return_code (curl_easy_setopt( curl, CURLOPT_SSL_VERIFYHOST, 2), "CURLOPT_SSL_VERIFYHOST"); } else { /* backward-compatible behaviour, be tolerant in checks @@ -354,11 +364,28 @@ 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"); } - - /* set callback to extract certificate */ - if(check_cert) { + + /* 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 USE_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"); - } +#endif /* USE_OPENSSL */ +#else /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */ +#ifdef USE_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 /* USE_OPENSSL */ +#endif /* LIBCURL_VERSION_NUM >= MAKE_LIBCURL_VERSION(7, 19, 1) */ + +#endif /* HAVE_SSL */ /* set default or user-given user agent identification */ handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_USERAGENT, user_agent), "CURLOPT_USERAGENT"); @@ -402,8 +429,10 @@ check_http (void) handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_WHATEVER)"); else if (address_family == AF_INET) handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V4)"); +#ifdef USE_IPV6 else if (address_family == AF_INET6) handle_curl_option_return_code (curl_easy_setopt (curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6), "CURLOPT_IPRESOLVE(CURL_IPRESOLVE_V6)"); +#endif /* either send http POST data (any data, not only POST)*/ if (!strcmp(http_method, "POST") ||!strcmp(http_method, "PUT")) { @@ -444,14 +473,41 @@ check_http (void) } /* certificate checks */ -#ifdef HAVE_SSL +#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); + } + } + } 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"); + /* check certificate with OpenSSL functions, curl has been built against OpenSSL + * and we actually have OpenSSL in the monitoring tools + */ +#ifdef HAVE_SSL +#ifdef USE_OPENSSL result = np_net_ssl_check_certificate(cert, days_till_exp_warn, days_till_exp_crit); - return(result); +#endif /* USE_OPENSSL */ +#endif /* HAVE_SSL */ + return result; } } -#endif /* HAVE_SSL */ +#endif /* LIBCURL_FEATURE_SSL */ /* we got the data and we executed the request in a given time, so we can append * performance data to the answer always @@ -481,7 +537,6 @@ check_http (void) (int)body_buf.buflen); } - /* return a CRITICAL status if we couldn't read any data */ if (strlen(header_buf.buf) == 0 && strlen(body_buf.buf) == 0) die (STATE_CRITICAL, _("HTTP CRITICAL - No header received from host\n")); @@ -605,6 +660,16 @@ check_http (void) /* make sure the page is of an appropriate size * TODO: as far I can tell check_http gets the full size of header and * if -N is not given header+body. Does this make sense? + * + * TODO: check_http.c had a get_length function, the question is really + * here what to use? the raw data size of the header_buf, the value of + * Content-Length, both and warn if they differ? Should the length be + * header+body or only body? + * + * One possible policy: + * - use header_buf.buflen (warning, if it mismatches to the Content-Length value + * - if -N (nobody) is given, use Content-Length only and hope the server set + * the value correcly */ page_len = header_buf.buflen + body_buf.buflen; if ((max_page_len > 0) && (page_len > max_page_len)) { @@ -637,8 +702,8 @@ check_http (void) curlhelp_free_statusline(&status_line); curl_easy_cleanup (curl); curl_global_cleanup (); - curlhelp_freewritebuffer(&body_buf); - curlhelp_freewritebuffer(&header_buf); + curlhelp_freewritebuffer (&body_buf); + curlhelp_freewritebuffer (&header_buf); if (!strcmp (http_method, "PUT")) { curlhelp_freereadbuffer (&put_buf); } @@ -1493,6 +1558,7 @@ get_header_value (const struct phr_header* headers, const size_t nof_headers, co return NULL; } +/* TODO: use CURL_EXTERN time_t curl_getdate(const char *p, const time_t *unused); here */ static time_t parse_time_string (const char *string) { -- cgit v0.10-9-g596f