[monitoring-plugins] probing for SSL library used by libcurl and ...

Andreas Baumann git at monitoring-plugins.org
Thu Apr 20 18:40:13 CEST 2017


 Module: monitoring-plugins
 Branch: feature_check_curl
 Commit: 0d07ab80ab2fd80c7fc32c0adf7cc0d01028aec9
 Author: Andreas Baumann <mail at andreasbaumann.cc>
   Date: Thu Apr 20 16:37:16 2017 +0000
    URL: https://www.monitoring-plugins.org/repositories/monitoring-plugins/commit/?id=0d07ab8

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 b002637..d3f3930 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";
+  }
+}



More information about the Commits mailing list