[monitoring-plugins] some work on certificate checking for non-OpenSSL ...

Andreas Baumann git at monitoring-plugins.org
Wed Apr 19 14:40:12 CEST 2017


 Module: monitoring-plugins
 Branch: feature_check_curl
 Commit: 4355136fdffe94d03f2790b921a59a089ef2bddf
 Author: Andreas Baumann <mail at andreasbaumann.cc>
   Date: Wed Apr 19 14:36:01 2017 +0200
    URL: https://www.monitoring-plugins.org/repositories/monitoring-plugins/commit/?id=4355136

some work on certificate checking for non-OpenSSL libraries

---

 plugins/check_curl.c | 88 +++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 77 insertions(+), 11 deletions(-)

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)
 {



More information about the Commits mailing list