summaryrefslogtreecommitdiff
path: root/snmplib/transports/snmpTLSBaseDomain.c
diff options
context:
space:
mode:
Diffstat (limited to 'snmplib/transports/snmpTLSBaseDomain.c')
-rw-r--r--snmplib/transports/snmpTLSBaseDomain.c1183
1 files changed, 1183 insertions, 0 deletions
diff --git a/snmplib/transports/snmpTLSBaseDomain.c b/snmplib/transports/snmpTLSBaseDomain.c
new file mode 100644
index 0000000..c54d6ad
--- /dev/null
+++ b/snmplib/transports/snmpTLSBaseDomain.c
@@ -0,0 +1,1183 @@
+#include <net-snmp/net-snmp-config.h>
+
+#include <net-snmp/net-snmp-features.h>
+
+netsnmp_feature_require(cert_util)
+
+#include <net-snmp/library/snmpTLSBaseDomain.h>
+
+#if HAVE_DMALLOC_H
+#include <dmalloc.h>
+#endif
+#if HAVE_STRING_H
+#include <string.h>
+#else
+#include <strings.h>
+#endif
+#if HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#if HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#if HAVE_NETDB_H
+#include <netdb.h>
+#endif
+#include <errno.h>
+#include <ctype.h>
+
+/* OpenSSL Includes */
+#include <openssl/bio.h>
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/x509.h>
+#include <openssl/x509_vfy.h>
+#include <openssl/x509v3.h>
+
+#include <net-snmp/types.h>
+#include <net-snmp/config_api.h>
+#include <net-snmp/library/cert_util.h>
+#include <net-snmp/library/snmp_openssl.h>
+#include <net-snmp/library/default_store.h>
+#include <net-snmp/library/callback.h>
+#include <net-snmp/library/snmp_logging.h>
+#include <net-snmp/library/snmp_api.h>
+#include <net-snmp/library/tools.h>
+#include <net-snmp/library/snmp_debug.h>
+#include <net-snmp/library/snmp_assert.h>
+#include <net-snmp/library/snmp_transport.h>
+#include <net-snmp/library/snmp_secmod.h>
+#include <net-snmp/library/read_config.h>
+#include <net-snmp/library/system.h>
+
+#define LOGANDDIE(msg) do { snmp_log(LOG_ERR, "%s\n", msg); return 0; } while(0)
+
+int openssl_local_index;
+
+/* this is called during negotiationn */
+int verify_callback(int ok, X509_STORE_CTX *ctx) {
+ int err, depth;
+ char buf[1024], *fingerprint;
+ X509 *thecert;
+ netsnmp_cert *cert;
+ _netsnmp_verify_info *verify_info;
+ SSL *ssl;
+
+ thecert = X509_STORE_CTX_get_current_cert(ctx);
+ err = X509_STORE_CTX_get_error(ctx);
+ depth = X509_STORE_CTX_get_error_depth(ctx);
+
+ /* things to do: */
+
+ X509_NAME_oneline(X509_get_subject_name(thecert), buf, sizeof(buf));
+ fingerprint = netsnmp_openssl_cert_get_fingerprint(thecert, -1);
+ DEBUGMSGTL(("tls_x509:verify", "Cert: %s\n", buf));
+ DEBUGMSGTL(("tls_x509:verify", " fp: %s\n", fingerprint ?
+ fingerprint : "unknown"));
+
+ ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+ verify_info = SSL_get_ex_data(ssl, openssl_local_index);
+
+ if (verify_info && ok && depth > 0) {
+ /* remember that a parent certificate has been marked as trusted */
+ verify_info->flags |= VRFY_PARENT_WAS_OK;
+ }
+
+ /* this ensures for self-signed certificates we have a valid
+ locally known fingerprint and then accept it */
+ if (!ok &&
+ (X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT == err ||
+ X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY == err ||
+ X509_V_ERR_CERT_UNTRUSTED == err ||
+ X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE == err ||
+ X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN == err)) {
+
+ cert = netsnmp_cert_find(NS_CERT_REMOTE_PEER, NS_CERTKEY_FINGERPRINT,
+ (void*)fingerprint);
+ if (cert)
+ DEBUGMSGTL(("tls_x509:verify", " Found locally: %s/%s\n",
+ cert->info.dir, cert->info.filename));
+
+
+ if (cert) {
+ DEBUGMSGTL(("tls_x509:verify", "verify_callback called with: ok=%d ctx=%p depth=%d err=%i:%s\n", ok, ctx, depth, err, X509_verify_cert_error_string(err)));
+ DEBUGMSGTL(("tls_x509:verify", " accepting matching fp of self-signed certificate found in: %s\n",
+ cert->info.filename));
+ SNMP_FREE(fingerprint);
+ return 1;
+ } else {
+ DEBUGMSGTL(("tls_x509:verify", " no matching fp found\n"));
+ /* log where we are and why called */
+ snmp_log(LOG_ERR, "tls verification failure: ok=%d ctx=%p depth=%d err=%i:%s\n", ok, ctx, depth, err, X509_verify_cert_error_string(err));
+ SNMP_FREE(fingerprint);
+ return 0;
+ }
+
+ if (0 == depth && verify_info &&
+ (verify_info->flags & VRFY_PARENT_WAS_OK)) {
+ DEBUGMSGTL(("tls_x509:verify", "verify_callback called with: ok=%d ctx=%p depth=%d err=%i:%s\n", ok, ctx, depth, err, X509_verify_cert_error_string(err)));
+ DEBUGMSGTL(("tls_x509:verify", " a parent was ok, so returning ok for this child certificate\n"));
+ SNMP_FREE(fingerprint);
+ return 1; /* we'll check the hostname later at this level */
+ }
+ }
+
+ if (0 == ok)
+ snmp_log(LOG_ERR, "tls verification failure: ok=%d ctx=%p depth=%d err=%i:%s\n", ok, ctx, depth, err, X509_verify_cert_error_string(err));
+ else
+ DEBUGMSGTL(("tls_x509:verify", "verify_callback called with: ok=%d ctx=%p depth=%d err=%i:%s\n", ok, ctx, depth, err, X509_verify_cert_error_string(err)));
+ DEBUGMSGTL(("tls_x509:verify", " returning the passed in value of %d\n",
+ ok));
+ SNMP_FREE(fingerprint);
+ return(ok);
+}
+
+#define VERIFIED_FINGERPRINT 0
+#define NO_FINGERPRINT_AVAILABLE 1
+#define FAILED_FINGERPRINT_VERIFY 2
+
+static int
+_netsnmp_tlsbase_verify_remote_fingerprint(X509 *remote_cert,
+ _netsnmpTLSBaseData *tlsdata,
+ int try_default) {
+
+ char *fingerprint;
+
+ fingerprint =
+ netsnmp_openssl_cert_get_fingerprint(remote_cert, -1);
+
+ if (!fingerprint) {
+ /* no peer cert */
+ snmp_log(LOG_ERR, "failed to get fingerprint of remote certificate\n");
+ return FAILED_FINGERPRINT_VERIFY;
+ }
+
+ if (!tlsdata->their_fingerprint && tlsdata->their_identity) {
+ /* we have an identity; try and find it's fingerprint */
+ netsnmp_cert *peer_cert;
+ peer_cert =
+ netsnmp_cert_find(NS_CERT_REMOTE_PEER, NS_CERTKEY_MULTIPLE,
+ tlsdata->their_identity);
+
+ if (peer_cert)
+ tlsdata->their_fingerprint =
+ netsnmp_openssl_cert_get_fingerprint(peer_cert->ocert, -1);
+ }
+
+ if (!tlsdata->their_fingerprint && try_default) {
+ /* try for the default instead */
+ netsnmp_cert *peer_cert;
+ peer_cert =
+ netsnmp_cert_find(NS_CERT_REMOTE_PEER, NS_CERTKEY_DEFAULT,
+ NULL);
+
+ if (peer_cert)
+ tlsdata->their_fingerprint =
+ netsnmp_openssl_cert_get_fingerprint(peer_cert->ocert, -1);
+ }
+
+ if (tlsdata->their_fingerprint) {
+ netsnmp_fp_lowercase_and_strip_colon(tlsdata->their_fingerprint);
+ if (0 != strcmp(tlsdata->their_fingerprint, fingerprint)) {
+ snmp_log(LOG_ERR, "The fingerprint from the remote side's certificate didn't match the expected\n");
+ snmp_log(LOG_ERR, " got %s, expected %s\n",
+ fingerprint, tlsdata->their_fingerprint);
+ free(fingerprint);
+ return FAILED_FINGERPRINT_VERIFY;
+ }
+ } else {
+ DEBUGMSGTL(("tls_x509:verify", "No fingerprint for the remote entity available to verify\n"));
+ free(fingerprint);
+ return NO_FINGERPRINT_AVAILABLE;
+ }
+
+ free(fingerprint);
+ return VERIFIED_FINGERPRINT;
+}
+
+/* this is called after the connection on the client side by us to check
+ other aspects about the connection */
+int
+netsnmp_tlsbase_verify_server_cert(SSL *ssl, _netsnmpTLSBaseData *tlsdata) {
+ /* XXX */
+ X509 *remote_cert;
+ char *check_name;
+ int ret;
+
+ netsnmp_assert_or_return(ssl != NULL, SNMPERR_GENERR);
+ netsnmp_assert_or_return(tlsdata != NULL, SNMPERR_GENERR);
+
+ if (NULL == (remote_cert = SSL_get_peer_certificate(ssl))) {
+ /* no peer cert */
+ DEBUGMSGTL(("tls_x509:verify",
+ "remote connection provided no certificate (yet)\n"));
+ return SNMPERR_TLS_NO_CERTIFICATE;
+ }
+
+ /* make sure that the fingerprint matches */
+ ret = _netsnmp_tlsbase_verify_remote_fingerprint(remote_cert, tlsdata, 1);
+ switch(ret) {
+ case VERIFIED_FINGERPRINT:
+ return SNMPERR_SUCCESS;
+
+ case FAILED_FINGERPRINT_VERIFY:
+ return SNMPERR_GENERR;
+
+ case NO_FINGERPRINT_AVAILABLE:
+ if (tlsdata->their_hostname && tlsdata->their_hostname[0] != '\0') {
+ GENERAL_NAMES *onames;
+ const GENERAL_NAME *oname = NULL;
+ int i, j;
+ int count;
+ char buf[SPRINT_MAX_LEN];
+ int is_wildcarded = 0;
+ char *compare_to;
+
+ /* see if the requested hostname has a wildcard prefix */
+ if (strncmp(tlsdata->their_hostname, "*.", 2) == 0) {
+ is_wildcarded = 1;
+ compare_to = tlsdata->their_hostname + 2;
+ } else {
+ compare_to = tlsdata->their_hostname;
+ }
+
+ /* if the hostname we were expecting to talk to matches
+ the cert, then we can accept this connection. */
+
+ /* check against the DNS subjectAltName */
+ onames = (GENERAL_NAMES *)X509_get_ext_d2i(remote_cert,
+ NID_subject_alt_name,
+ NULL, NULL );
+ if (NULL != onames) {
+ count = sk_GENERAL_NAME_num(onames);
+
+ for (i=0 ; i <count; ++i) {
+ oname = sk_GENERAL_NAME_value(onames, i);
+ if (GEN_DNS == oname->type) {
+
+ /* get the value */
+ ASN1_STRING_to_UTF8((unsigned char**)&check_name,
+ oname->d.ia5);
+
+ /* convert to lowercase for comparisons */
+ for (j = 0 ;
+ *check_name && j < sizeof(buf)-1;
+ ++check_name, ++j ) {
+ if (isascii(*check_name))
+ buf[j] = tolower(0xFF & *check_name);
+ }
+ if (j < sizeof(buf))
+ buf[j] = '\0';
+ check_name = buf;
+
+ if (is_wildcarded) {
+ /* we *only* allow passing till the first '.' */
+ /* ie *.example.com can't match a.b.example.com */
+ check_name = strchr(check_name, '.') + 1;
+ }
+
+ DEBUGMSGTL(("tls_x509:verify", "checking subjectAltname of dns:%s\n", check_name));
+ if (strcmp(compare_to, check_name) == 0) {
+
+ DEBUGMSGTL(("tls_x509:verify", "Successful match on a subjectAltname of dns:%s\n", check_name));
+ return SNMPERR_SUCCESS;
+ }
+ }
+ }
+ }
+
+ /* check the common name for a match */
+ check_name =
+ netsnmp_openssl_cert_get_commonName(remote_cert, NULL, NULL);
+
+ if (is_wildcarded) {
+ /* we *only* allow passing till the first '.' */
+ /* ie *.example.com can't match a.b.example.com */
+ check_name = strchr(check_name, '.') + 1;
+ }
+
+ if (strcmp(compare_to, check_name) == 0) {
+ DEBUGMSGTL(("tls_x509:verify", "Successful match on a common name of %s\n", check_name));
+ return SNMPERR_SUCCESS;
+ }
+
+ snmp_log(LOG_ERR, "No matching names in the certificate to match the expected %s\n", tlsdata->their_hostname);
+ return SNMPERR_GENERR;
+
+ }
+ /* XXX: check for hostname match instead */
+ snmp_log(LOG_ERR, "Can not verify a remote server identity without configuration\n");
+ return SNMPERR_GENERR;
+ }
+ DEBUGMSGTL(("tls_x509:verify", "shouldn't get here\n"));
+ return SNMPERR_GENERR;
+}
+
+/* this is called after the connection on the server side by us to check
+ the validity of the client's certificate */
+int
+netsnmp_tlsbase_verify_client_cert(SSL *ssl, _netsnmpTLSBaseData *tlsdata) {
+ /* XXX */
+ X509 *remote_cert;
+ int ret;
+
+ /* RFC5953: section 5.3.2, paragraph 1:
+ A (D)TLS server should accept new session connections from any client
+ that it is able to verify the client's credentials for. This is done
+ by authenticating the client's presented certificate through a
+ certificate path validation process (e.g. [RFC5280]) or through
+ certificate fingerprint verification using fingerprints configured in
+ the snmpTlstmCertToTSNTable. Afterward the server will determine the
+ identity of the remote entity using the following procedures.
+ */
+ /* Implementation notes:
+ + path validation is taken care of during the openssl verify
+ routines, our part of which is hanlded in verify_callback
+ above.
+ + fingerprint verification happens below.
+ */
+ if (NULL == (remote_cert = SSL_get_peer_certificate(ssl))) {
+ /* no peer cert */
+ DEBUGMSGTL(("tls_x509:verify",
+ "remote connection provided no certificate (yet)\n"));
+ return SNMPERR_TLS_NO_CERTIFICATE;
+ }
+
+ /* we don't force a known remote fingerprint for a client since we
+ will accept any certificate we know about (and later derive the
+ securityName from it and apply access control) */
+ ret = _netsnmp_tlsbase_verify_remote_fingerprint(remote_cert, tlsdata, 0);
+ switch(ret) {
+ case FAILED_FINGERPRINT_VERIFY:
+ DEBUGMSGTL(("tls_x509:verify", "failed to verify a client fingerprint\n"));
+ return SNMPERR_GENERR;
+
+ case NO_FINGERPRINT_AVAILABLE:
+ DEBUGMSGTL(("tls_x509:verify", "no known fingerprint available (not a failure case)\n"));
+ return SNMPERR_SUCCESS;
+
+ case VERIFIED_FINGERPRINT:
+ DEBUGMSGTL(("tls_x509:verify", "Verified client fingerprint\n"));
+ tlsdata->flags |= NETSNMP_TLSBASE_CERT_FP_VERIFIED;
+ return SNMPERR_SUCCESS;
+ }
+
+ DEBUGMSGTL(("tls_x509:verify", "shouldn't get here\n"));
+ return SNMPERR_GENERR;
+}
+
+/* this is called after the connection on the server side by us to
+ check other aspects about the connection and obtain the
+ securityName from the remote certificate. */
+int
+netsnmp_tlsbase_extract_security_name(SSL *ssl, _netsnmpTLSBaseData *tlsdata) {
+ netsnmp_container *chain_maps;
+ netsnmp_cert_map *cert_map, *peer_cert;
+ netsnmp_iterator *itr;
+ int rc;
+
+ netsnmp_assert_or_return(ssl != NULL, SNMPERR_GENERR);
+ netsnmp_assert_or_return(tlsdata != NULL, SNMPERR_GENERR);
+
+ if (NULL == (chain_maps = netsnmp_openssl_get_cert_chain(ssl)))
+ return SNMPERR_GENERR;
+ /*
+ * map fingerprints to mapping entries
+ */
+ rc = netsnmp_cert_get_secname_maps(chain_maps);
+ if ((-1 == rc) || (CONTAINER_SIZE(chain_maps) == 0)) {
+ netsnmp_cert_map_container_free(chain_maps);
+ return SNMPERR_GENERR;
+ }
+
+ /*
+ * change container to sorted (by clearing unsorted option), then
+ * iterate over it until we find a map that returns a secname.
+ */
+ CONTAINER_SET_OPTIONS(chain_maps, 0, rc);
+ itr = CONTAINER_ITERATOR(chain_maps);
+ if (NULL == itr) {
+ snmp_log(LOG_ERR, "could not get iterator for secname fingerprints\n");
+ netsnmp_cert_map_container_free(chain_maps);
+ return SNMPERR_GENERR;
+ }
+ peer_cert = cert_map = ITERATOR_FIRST(itr);
+ for( ; !tlsdata->securityName && cert_map; cert_map = ITERATOR_NEXT(itr))
+ tlsdata->securityName =
+ netsnmp_openssl_extract_secname(cert_map, peer_cert);
+ ITERATOR_RELEASE(itr);
+
+ netsnmp_cert_map_container_free(chain_maps);
+
+ return (tlsdata->securityName ? SNMPERR_SUCCESS : SNMPERR_GENERR);
+}
+
+int
+_trust_this_cert(SSL_CTX *the_ctx, char *certspec) {
+ netsnmp_cert *trustcert;
+
+ DEBUGMSGTL(("sslctx_client", "Trying to load a trusted certificate: %s\n",
+ certspec));
+
+ /* load this identifier into the trust chain */
+ trustcert = netsnmp_cert_find(NS_CERT_CA,
+ NS_CERTKEY_MULTIPLE,
+ certspec);
+ if (!trustcert)
+ trustcert = netsnmp_cert_find(NS_CERT_REMOTE_PEER,
+ NS_CERTKEY_MULTIPLE,
+ certspec);
+ if (!trustcert)
+ LOGANDDIE("failed to find requested certificate to trust");
+
+ /* Add the certificate to the context */
+ if (netsnmp_cert_trust_ca(the_ctx, trustcert) != SNMPERR_SUCCESS)
+ LOGANDDIE("failed to load trust certificate");
+
+ return 1;
+}
+
+void
+_load_trusted_certs(SSL_CTX *the_ctx) {
+ netsnmp_container *trusted_certs = NULL;
+ netsnmp_iterator *trusted_cert_iterator = NULL;
+ char *fingerprint;
+
+ trusted_certs = netsnmp_cert_get_trustlist();
+ trusted_cert_iterator = CONTAINER_ITERATOR(trusted_certs);
+ if (trusted_cert_iterator) {
+ for (fingerprint = (char *) ITERATOR_FIRST(trusted_cert_iterator);
+ fingerprint; fingerprint = ITERATOR_NEXT(trusted_cert_iterator)) {
+ if (!_trust_this_cert(the_ctx, fingerprint))
+ snmp_log(LOG_ERR, "failed to load trust cert: %s\n",
+ fingerprint);
+ }
+ ITERATOR_RELEASE(trusted_cert_iterator);
+ }
+}
+
+SSL_CTX *
+_sslctx_common_setup(SSL_CTX *the_ctx, _netsnmpTLSBaseData *tlsbase) {
+ char *crlFile;
+ char *cipherList;
+ X509_LOOKUP *lookup;
+ X509_STORE *cert_store = NULL;
+
+ _load_trusted_certs(the_ctx);
+
+ /* add in the CRLs if available */
+
+ crlFile = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_X509_CRL_FILE);
+ if (NULL != crlFile) {
+ cert_store = SSL_CTX_get_cert_store(the_ctx);
+ DEBUGMSGTL(("sslctx_client", "loading CRL: %s\n", crlFile));
+ if (!cert_store)
+ LOGANDDIE("failed to find certificate store");
+ if (!(lookup = X509_STORE_add_lookup(cert_store, X509_LOOKUP_file())))
+ LOGANDDIE("failed to create a lookup function for the CRL file");
+ if (X509_load_crl_file(lookup, crlFile, X509_FILETYPE_PEM) != 1)
+ LOGANDDIE("failed to load the CRL file");
+ /* tell openssl to check CRLs */
+ X509_STORE_set_flags(cert_store,
+ X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
+ }
+
+ cipherList = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_TLS_ALGORITMS);
+ if (NULL != cipherList) {
+ if (SSL_CTX_set_cipher_list(the_ctx, cipherList) != 1)
+ LOGANDDIE("failed to set the cipher list to the requested value");
+ else
+ snmp_log(LOG_INFO,"set SSL cipher list to '%s'\n", cipherList);
+ }
+ return the_ctx;
+}
+
+SSL_CTX *
+sslctx_client_setup(const SSL_METHOD *method, _netsnmpTLSBaseData *tlsbase) {
+ netsnmp_cert *id_cert, *peer_cert;
+ SSL_CTX *the_ctx;
+
+ /***********************************************************************
+ * Set up the client context
+ */
+ the_ctx = SSL_CTX_new(NETSNMP_REMOVE_CONST(SSL_METHOD *, method));
+ if (!the_ctx) {
+ snmp_log(LOG_ERR, "ack: %p\n", the_ctx);
+ LOGANDDIE("can't create a new context");
+ }
+ SSL_CTX_set_read_ahead (the_ctx, 1); /* Required for DTLS */
+
+ SSL_CTX_set_verify(the_ctx,
+ SSL_VERIFY_PEER|
+ SSL_VERIFY_FAIL_IF_NO_PEER_CERT|
+ SSL_VERIFY_CLIENT_ONCE,
+ &verify_callback);
+
+ if (tlsbase->our_identity) {
+ DEBUGMSGTL(("sslctx_client", "looking for local id: %s\n", tlsbase->our_identity));
+ id_cert = netsnmp_cert_find(NS_CERT_IDENTITY, NS_CERTKEY_MULTIPLE,
+ tlsbase->our_identity);
+ } else {
+ DEBUGMSGTL(("sslctx_client", "looking for default local id: %s\n", tlsbase->our_identity));
+ id_cert = netsnmp_cert_find(NS_CERT_IDENTITY, NS_CERTKEY_DEFAULT, NULL);
+ }
+
+ if (!id_cert)
+ LOGANDDIE ("error finding client identity keys");
+
+ if (!id_cert->key || !id_cert->key->okey)
+ LOGANDDIE("failed to load private key");
+
+ DEBUGMSGTL(("sslctx_client", "using public key: %s\n",
+ id_cert->info.filename));
+ DEBUGMSGTL(("sslctx_client", "using private key: %s\n",
+ id_cert->key->info.filename));
+
+ if (SSL_CTX_use_certificate(the_ctx, id_cert->ocert) <= 0)
+ LOGANDDIE("failed to set the certificate to use");
+
+ if (SSL_CTX_use_PrivateKey(the_ctx, id_cert->key->okey) <= 0)
+ LOGANDDIE("failed to set the private key to use");
+
+ if (!SSL_CTX_check_private_key(the_ctx))
+ LOGANDDIE("public and private keys incompatible");
+
+ if (tlsbase->their_identity)
+ peer_cert = netsnmp_cert_find(NS_CERT_REMOTE_PEER,
+ NS_CERTKEY_MULTIPLE,
+ tlsbase->their_identity);
+ else
+ peer_cert = netsnmp_cert_find(NS_CERT_REMOTE_PEER, NS_CERTKEY_DEFAULT,
+ NULL);
+ if (peer_cert) {
+ DEBUGMSGTL(("sslctx_client", "server's expected public key: %s\n",
+ peer_cert ? peer_cert->info.filename : "none"));
+
+ /* Trust the expected certificate */
+ if (netsnmp_cert_trust_ca(the_ctx, peer_cert) != SNMPERR_SUCCESS)
+ LOGANDDIE ("failed to set verify paths");
+ }
+
+ /* trust a certificate (possibly a CA) aspecifically passed in */
+ if (tlsbase->trust_cert) {
+ if (!_trust_this_cert(the_ctx, tlsbase->trust_cert))
+ return 0;
+ }
+
+ return _sslctx_common_setup(the_ctx, tlsbase);
+}
+
+SSL_CTX *
+sslctx_server_setup(const SSL_METHOD *method) {
+ netsnmp_cert *id_cert;
+
+ /***********************************************************************
+ * Set up the server context
+ */
+ /* setting up for ssl */
+ SSL_CTX *the_ctx = SSL_CTX_new(NETSNMP_REMOVE_CONST(SSL_METHOD *, method));
+ if (!the_ctx) {
+ LOGANDDIE("can't create a new context");
+ }
+
+ id_cert = netsnmp_cert_find(NS_CERT_IDENTITY, NS_CERTKEY_DEFAULT, NULL);
+ if (!id_cert)
+ LOGANDDIE ("error finding server identity keys");
+
+ if (!id_cert->key || !id_cert->key->okey)
+ LOGANDDIE("failed to load private key");
+
+ DEBUGMSGTL(("sslctx_server", "using public key: %s\n",
+ id_cert->info.filename));
+ DEBUGMSGTL(("sslctx_server", "using private key: %s\n",
+ id_cert->key->info.filename));
+
+ if (SSL_CTX_use_certificate(the_ctx, id_cert->ocert) <= 0)
+ LOGANDDIE("failed to set the certificate to use");
+
+ if (SSL_CTX_use_PrivateKey(the_ctx, id_cert->key->okey) <= 0)
+ LOGANDDIE("failed to set the private key to use");
+
+ if (!SSL_CTX_check_private_key(the_ctx))
+ LOGANDDIE("public and private keys incompatible");
+
+ SSL_CTX_set_read_ahead(the_ctx, 1); /* XXX: DTLS only? */
+
+ SSL_CTX_set_verify(the_ctx,
+ SSL_VERIFY_PEER|
+ SSL_VERIFY_FAIL_IF_NO_PEER_CERT|
+ SSL_VERIFY_CLIENT_ONCE,
+ &verify_callback);
+
+ return _sslctx_common_setup(the_ctx, NULL);
+}
+
+int
+netsnmp_tlsbase_config(struct netsnmp_transport_s *t, const char *token, const char *value) {
+ _netsnmpTLSBaseData *tlsdata;
+
+ netsnmp_assert_or_return(t != NULL, -1);
+ netsnmp_assert_or_return(t->data != NULL, -1);
+
+ tlsdata = t->data;
+
+ if ((strcmp(token, "localCert") == 0) ||
+ (strcmp(token, "our_identity") == 0)) {
+ SNMP_FREE(tlsdata->our_identity);
+ tlsdata->our_identity = strdup(value);
+ DEBUGMSGT(("tls:config","our identity %s\n", value));
+ }
+
+ if ((strcmp(token, "peerCert") == 0) ||
+ (strcmp(token, "their_identity") == 0)) {
+ SNMP_FREE(tlsdata->their_identity);
+ tlsdata->their_identity = strdup(value);
+ DEBUGMSGT(("tls:config","their identity %s\n", value));
+ }
+
+ if ((strcmp(token, "peerHostname") == 0) ||
+ (strcmp(token, "their_hostname") == 0)) {
+ SNMP_FREE(tlsdata->their_hostname);
+ tlsdata->their_hostname = strdup(value);
+ }
+
+ if ((strcmp(token, "trust_cert") == 0) ||
+ (strcmp(token, "trustCert") == 0)) {
+ SNMP_FREE(tlsdata->trust_cert);
+ tlsdata->trust_cert = strdup(value);
+ }
+
+ return SNMPERR_SUCCESS;
+}
+
+int
+netsnmp_tlsbase_session_init(struct netsnmp_transport_s *transport,
+ struct snmp_session *sess) {
+ /* the default security model here should be TSM; most other
+ things won't work with TLS because we'll throw out the packet
+ if it doesn't have a proper tmStateRef (and onyl TSM generates
+ this at the moment */
+ if (!(transport->flags & NETSNMP_TRANSPORT_FLAG_LISTEN)) {
+ if (sess->securityModel == SNMP_DEFAULT_SECMODEL) {
+ sess->securityModel = SNMP_SEC_MODEL_TSM;
+ } else if (sess->securityModel != SNMP_SEC_MODEL_TSM) {
+ sess->securityModel = SNMP_SEC_MODEL_TSM;
+ snmp_log(LOG_ERR, "A security model other than TSM is being used with (D)TLS; using TSM anyways\n");
+ }
+
+ if (NULL == sess->securityName) {
+ /* client side doesn't need a real securityName */
+ /* XXX: call-home issues require them to set one for VACM; but
+ we don't do callhome yet */
+ sess->securityName = strdup("__BOGUS__");
+ sess->securityNameLen = strlen(sess->securityName);
+ }
+
+ if (sess->version != SNMP_VERSION_3) {
+ sess->version = SNMP_VERSION_3;
+ snmp_log(LOG_ERR, "A SNMP version other than 3 was requested with (D)TLS; using 3 anyways\n");
+ }
+
+ if (sess->securityLevel <= 0) {
+ sess->securityLevel = SNMP_SEC_LEVEL_AUTHPRIV;
+ }
+ }
+ return SNMPERR_SUCCESS;
+}
+
+static int have_done_bootstrap = 0;
+
+static int
+tls_bootstrap(int majorid, int minorid, void *serverarg, void *clientarg) {
+ char indexname[] = "_netsnmp_verify_info";
+
+ /* don't do this more than once */
+ if (have_done_bootstrap)
+ return 0;
+ have_done_bootstrap = 1;
+
+ netsnmp_certs_load();
+
+ openssl_local_index =
+ SSL_get_ex_new_index(0, indexname, NULL, NULL, NULL);
+
+ return 0;
+}
+
+int
+tls_get_verify_info_index() {
+ return openssl_local_index;
+}
+
+static void _parse_client_cert(const char *tok, char *line)
+{
+ config_pwarn("clientCert is deprecated. Clients should use localCert, servers should use peerCert");
+ if (*line == '"') {
+ char buf[SNMP_MAXBUF];
+ copy_nword(line, buf, sizeof(buf));
+ netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_X509_CLIENT_PUB, buf);
+ } else
+ netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_X509_CLIENT_PUB, line);
+}
+
+static void _parse_server_cert(const char *tok, char *line)
+{
+ config_pwarn("serverCert is deprecated. Clients should use peerCert, servers should use localCert.");
+ if (*line == '"') {
+ char buf[SNMP_MAXBUF];
+ copy_nword(line, buf, sizeof(buf));
+ netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_X509_CLIENT_PUB, buf);
+ } else
+ netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_X509_SERVER_PUB, line);
+}
+
+void
+netsnmp_tlsbase_ctor(void) {
+
+ /* bootstrap ssl since we'll need it */
+ netsnmp_init_openssl();
+
+ /* the private client cert to authenticate with */
+ netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "extraX509SubDir",
+ NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_CERT_EXTRA_SUBDIR);
+
+ /* Do we have a CRL list? */
+ netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "x509CRLFile",
+ NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_X509_CRL_FILE);
+
+ /* What TLS algorithms should be use */
+ netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "tlsAlgorithms",
+ NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_TLS_ALGORITMS);
+
+ /*
+ * for the client
+ */
+
+ /* the public client cert to authenticate with */
+ register_config_handler("snmp", "clientCert", _parse_client_cert, NULL,
+ NULL);
+
+ /*
+ * for the server
+ */
+
+ /* The X509 server key to use */
+ register_config_handler("snmp", "serverCert", _parse_server_cert, NULL,
+ NULL);
+ /*
+ * remove cert config ambiguity: localCert, peerCert
+ */
+ netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "localCert",
+ NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_TLS_LOCAL_CERT);
+ netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "peerCert",
+ NETSNMP_DS_LIBRARY_ID,
+ NETSNMP_DS_LIB_TLS_PEER_CERT);
+
+ /*
+ * register our boot-strapping needs
+ */
+ snmp_register_callback(SNMP_CALLBACK_LIBRARY,
+ SNMP_CALLBACK_POST_PREMIB_READ_CONFIG,
+ tls_bootstrap, NULL);
+
+}
+
+_netsnmpTLSBaseData *
+netsnmp_tlsbase_allocate_tlsdata(netsnmp_transport *t, int isserver) {
+
+ _netsnmpTLSBaseData *tlsdata;
+
+ if (NULL == t)
+ return NULL;
+
+ /* allocate our TLS specific data */
+ tlsdata = SNMP_MALLOC_TYPEDEF(_netsnmpTLSBaseData);
+ if (NULL == tlsdata) {
+ SNMP_FREE(t);
+ return NULL;
+ }
+
+ if (!isserver)
+ tlsdata->flags |= NETSNMP_TLSBASE_IS_CLIENT;
+
+ return tlsdata;
+}
+
+void netsnmp_tlsbase_free_tlsdata(_netsnmpTLSBaseData *tlsbase) {
+ if (!tlsbase)
+ return;
+
+ DEBUGMSGTL(("tlsbase","Freeing TLS Base data for a session\n"));
+
+ if (tlsbase->ssl)
+ SSL_free(tlsbase->ssl);
+
+ if (tlsbase->ssl_context)
+ SSL_CTX_free(tlsbase->ssl_context);
+ /* don't free the accept_bio since it's the parent bio */
+ /*
+ if (tlsbase->accept_bio)
+ BIO_free(tlsbase->accept_bio);
+ */
+ /* and this is freed by the SSL shutdown */
+ /*
+ if (tlsbase->accepted_bio)
+ BIO_free(tlsbase->accept_bio);
+ */
+
+ /* free the config data */
+ SNMP_FREE(tlsbase->securityName);
+ SNMP_FREE(tlsbase->addr_string);
+ SNMP_FREE(tlsbase->our_identity);
+ SNMP_FREE(tlsbase->their_identity);
+ SNMP_FREE(tlsbase->their_fingerprint);
+ SNMP_FREE(tlsbase->their_hostname);
+ SNMP_FREE(tlsbase->trust_cert);
+
+ /* free the base itself */
+ SNMP_FREE(tlsbase);
+}
+
+int netsnmp_tlsbase_wrapup_recv(netsnmp_tmStateReference *tmStateRef,
+ _netsnmpTLSBaseData *tlsdata,
+ void **opaque, int *olength) {
+ int no_auth, no_priv;
+
+ if (NULL == tlsdata)
+ return SNMPERR_GENERR;
+
+ /* RFC5953 Section 5.1.2 step 2: tmSecurityLevel */
+ /*
+ * Don't accept null authentication. Null encryption ok.
+ *
+ * XXX: this should actually check for a configured list of encryption
+ * algorithms to map to NOPRIV, but for the moment we'll
+ * accept any encryption alogrithms that openssl is using.
+ */
+ netsnmp_openssl_null_checks(tlsdata->ssl, &no_auth, &no_priv);
+ if (no_auth == 1) { /* null/unknown authentication */
+ /* xxx-rks: snmp_increment_statistic(STAT_???); */
+ snmp_log(LOG_ERR, "tls connection with NULL authentication\n");
+ SNMP_FREE(tmStateRef);
+ return SNMPERR_GENERR;
+ }
+ else if (no_priv == 1) /* null/unknown encryption */
+ tmStateRef->transportSecurityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV;
+ else
+ tmStateRef->transportSecurityLevel = SNMP_SEC_LEVEL_AUTHPRIV;
+ DEBUGMSGTL(("tls:secLevel", "SecLevel %d\n",
+ tmStateRef->transportSecurityLevel));
+
+ /* use x509 cert to do lookup to secname if DNE in cachep yet */
+
+ /* RFC5953: section 5.3.2, paragraph 2:
+ The (D)TLS server identifies the authenticated identity from the
+ (D)TLS client's principal certificate using configuration information
+ from the snmpTlstmCertToTSNTable mapping table. The (D)TLS server
+ MUST request and expect a certificate from the client and MUST NOT
+ accept SNMP messages over the (D)TLS connection until the client has
+ sent a certificate and it has been authenticated. The resulting
+ derived tmSecurityName is recorded in the tmStateReference cache as
+ tmSecurityName. The details of the lookup process are fully
+ described in the DESCRIPTION clause of the snmpTlstmCertToTSNTable
+ MIB object. If any verification fails in any way (for example
+ because of failures in cryptographic verification or because of the
+ lack of an appropriate row in the snmpTlstmCertToTSNTable) then the
+ session establishment MUST fail, and the
+ snmpTlstmSessionInvalidClientCertificates object is incremented. If
+ the session can not be opened for any reason at all, including
+ cryptographic verification failures, then the
+ snmpTlstmSessionOpenErrors counter is incremented and processing
+ stops.
+ */
+
+ if (!tlsdata->securityName) {
+ netsnmp_tlsbase_extract_security_name(tlsdata->ssl, tlsdata);
+ if (NULL != tlsdata->securityName) {
+ DEBUGMSGTL(("tls", "set SecName to: %s\n", tlsdata->securityName));
+ } else {
+ snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONINVALIDCLIENTCERTIFICATES);
+ snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS);
+ SNMP_FREE(tmStateRef);
+ return SNMPERR_GENERR;
+ }
+ }
+
+ /* RFC5953 Section 5.1.2 step 2: tmSecurityName */
+ /* XXX: detect and throw out overflow secname sizes rather
+ than truncating. */
+ strlcpy(tmStateRef->securityName, tlsdata->securityName,
+ sizeof(tmStateRef->securityName));
+ tmStateRef->securityNameLen = strlen(tmStateRef->securityName);
+
+ /* RFC5953 Section 5.1.2 step 2: tmSessionID */
+ /* use our TLSData pointer as the session ID */
+ memcpy(tmStateRef->sessionID, &tlsdata, sizeof(netsnmp_tmStateReference *));
+
+ /* save the tmStateRef in our special pointer */
+ *opaque = tmStateRef;
+ *olength = sizeof(netsnmp_tmStateReference);
+
+ return SNMPERR_SUCCESS;
+}
+
+netsnmp_feature_child_of(_x509_get_error, netsnmp_unused)
+#ifndef NETSNMP_FEATURE_REMOVE__X509_GET_ERROR
+const char * _x509_get_error(int x509failvalue, const char *location) {
+ static const char *reason = NULL;
+
+ /* XXX: use this instead: X509_verify_cert_error_string(err) */
+
+ switch (x509failvalue) {
+ case X509_V_OK:
+ reason = "X509_V_OK";
+ break;
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+ reason = "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT";
+ break;
+ case X509_V_ERR_UNABLE_TO_GET_CRL:
+ reason = "X509_V_ERR_UNABLE_TO_GET_CRL";
+ break;
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+ reason = "X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE";
+ break;
+ case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+ reason = "X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE";
+ break;
+ case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+ reason = "X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY";
+ break;
+ case X509_V_ERR_CERT_SIGNATURE_FAILURE:
+ reason = "X509_V_ERR_CERT_SIGNATURE_FAILURE";
+ break;
+ case X509_V_ERR_CRL_SIGNATURE_FAILURE:
+ reason = "X509_V_ERR_CRL_SIGNATURE_FAILURE";
+ break;
+ case X509_V_ERR_CERT_NOT_YET_VALID:
+ reason = "X509_V_ERR_CERT_NOT_YET_VALID";
+ break;
+ case X509_V_ERR_CERT_HAS_EXPIRED:
+ reason = "X509_V_ERR_CERT_HAS_EXPIRED";
+ break;
+ case X509_V_ERR_CRL_NOT_YET_VALID:
+ reason = "X509_V_ERR_CRL_NOT_YET_VALID";
+ break;
+ case X509_V_ERR_CRL_HAS_EXPIRED:
+ reason = "X509_V_ERR_CRL_HAS_EXPIRED";
+ break;
+ case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+ reason = "X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD";
+ break;
+ case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+ reason = "X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD";
+ break;
+ case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+ reason = "X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD";
+ break;
+ case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+ reason = "X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD";
+ break;
+ case X509_V_ERR_OUT_OF_MEM:
+ reason = "X509_V_ERR_OUT_OF_MEM";
+ break;
+ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+ reason = "X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT";
+ break;
+ case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+ reason = "X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN";
+ break;
+ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+ reason = "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY";
+ break;
+ case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+ reason = "X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE";
+ break;
+ case X509_V_ERR_CERT_CHAIN_TOO_LONG:
+ reason = "X509_V_ERR_CERT_CHAIN_TOO_LONG";
+ break;
+ case X509_V_ERR_CERT_REVOKED:
+ reason = "X509_V_ERR_CERT_REVOKED";
+ break;
+ case X509_V_ERR_INVALID_CA:
+ reason = "X509_V_ERR_INVALID_CA";
+ break;
+ case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+ reason = "X509_V_ERR_PATH_LENGTH_EXCEEDED";
+ break;
+ case X509_V_ERR_INVALID_PURPOSE:
+ reason = "X509_V_ERR_INVALID_PURPOSE";
+ break;
+ case X509_V_ERR_CERT_UNTRUSTED:
+ reason = "X509_V_ERR_CERT_UNTRUSTED";
+ break;
+ case X509_V_ERR_CERT_REJECTED:
+ reason = "X509_V_ERR_CERT_REJECTED";
+ break;
+ case X509_V_ERR_SUBJECT_ISSUER_MISMATCH:
+ reason = "X509_V_ERR_SUBJECT_ISSUER_MISMATCH";
+ break;
+ case X509_V_ERR_AKID_SKID_MISMATCH:
+ reason = "X509_V_ERR_AKID_SKID_MISMATCH";
+ break;
+ case X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH:
+ reason = "X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH";
+ break;
+ case X509_V_ERR_KEYUSAGE_NO_CERTSIGN:
+ reason = "X509_V_ERR_KEYUSAGE_NO_CERTSIGN";
+ break;
+ case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
+ reason = "X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER";
+ break;
+ case X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION:
+ reason = "X509_V_ERR_UNHANDLED_CRITICAL_EXTENSION";
+ break;
+ case X509_V_ERR_KEYUSAGE_NO_CRL_SIGN:
+ reason = "X509_V_ERR_KEYUSAGE_NO_CRL_SIGN";
+ break;
+ case X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION:
+ reason = "X509_V_ERR_UNHANDLED_CRITICAL_CRL_EXTENSION";
+ break;
+ case X509_V_ERR_INVALID_NON_CA:
+ reason = "X509_V_ERR_INVALID_NON_CA";
+ break;
+ case X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED:
+ reason = "X509_V_ERR_PROXY_PATH_LENGTH_EXCEEDED";
+ break;
+ case X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE:
+ reason = "X509_V_ERR_KEYUSAGE_NO_DIGITAL_SIGNATURE";
+ break;
+ case X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED:
+ reason = "X509_V_ERR_PROXY_CERTIFICATES_NOT_ALLOWED";
+ break;
+#ifdef X509_V_ERR_INVALID_EXTENSION /* not avail on darwin */
+ case X509_V_ERR_INVALID_EXTENSION:
+ reason = "X509_V_ERR_INVALID_EXTENSION";
+ break;
+#endif
+#ifdef X509_V_ERR_INVALID_POLICY_EXTENSION /* not avail on darwin */
+ case X509_V_ERR_INVALID_POLICY_EXTENSION:
+ reason = "X509_V_ERR_INVALID_POLICY_EXTENSION";
+ break;
+#endif
+#ifdef X509_V_ERR_NO_EXPLICIT_POLICY /* not avail on darwin */
+ case X509_V_ERR_NO_EXPLICIT_POLICY:
+ reason = "X509_V_ERR_NO_EXPLICIT_POLICY";
+ break;
+#endif
+#ifdef X509_V_ERR_UNNESTED_RESOURCE /* not avail on darwin */
+ case X509_V_ERR_UNNESTED_RESOURCE:
+ reason = "X509_V_ERR_UNNESTED_RESOURCE";
+ break;
+#endif
+ case X509_V_ERR_APPLICATION_VERIFICATION:
+ reason = "X509_V_ERR_APPLICATION_VERIFICATION";
+ break;
+ default:
+ reason = "unknown failure code";
+ }
+
+ return reason;
+}
+#endif /* NETSNMP_FEATURE_REMOVE__X509_GET_ERROR */
+
+void _openssl_log_error(int rc, SSL *con, const char *location) {
+ const char *reason, *file, *data;
+ unsigned long numerical_reason;
+ int flags, line;
+
+ snmp_log(LOG_ERR, "---- OpenSSL Related Errors: ----\n");
+
+ /* SSL specific errors */
+ if (con) {
+
+ int sslnum = SSL_get_error(con, rc);
+
+ switch(sslnum) {
+ case SSL_ERROR_NONE:
+ reason = "SSL_ERROR_NONE";
+ break;
+
+ case SSL_ERROR_SSL:
+ reason = "SSL_ERROR_SSL";
+ break;
+
+ case SSL_ERROR_WANT_READ:
+ reason = "SSL_ERROR_WANT_READ";
+ break;
+
+ case SSL_ERROR_WANT_WRITE:
+ reason = "SSL_ERROR_WANT_WRITE";
+ break;
+
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ reason = "SSL_ERROR_WANT_X509_LOOKUP";
+ break;
+
+ case SSL_ERROR_SYSCALL:
+ reason = "SSL_ERROR_SYSCALL";
+ snmp_log(LOG_ERR, "TLS error: %s: rc=%d, sslerror = %d (%s): system_error=%d (%s)\n",
+ location, rc, sslnum, reason, errno, strerror(errno));
+ snmp_log(LOG_ERR, "TLS Error: %s\n",
+ ERR_reason_error_string(ERR_get_error()));
+ return;
+
+ case SSL_ERROR_ZERO_RETURN:
+ reason = "SSL_ERROR_ZERO_RETURN";
+ break;
+
+ case SSL_ERROR_WANT_CONNECT:
+ reason = "SSL_ERROR_WANT_CONNECT";
+ break;
+
+ case SSL_ERROR_WANT_ACCEPT:
+ reason = "SSL_ERROR_WANT_ACCEPT";
+ break;
+
+ default:
+ reason = "unknown";
+ }
+
+ snmp_log(LOG_ERR, " TLS error: %s: rc=%d, sslerror = %d (%s)\n",
+ location, rc, sslnum, reason);
+
+ snmp_log(LOG_ERR, " TLS Error: %s\n",
+ ERR_reason_error_string(ERR_get_error()));
+
+ }
+
+ /* other errors */
+ while ((numerical_reason =
+ ERR_get_error_line_data(&file, &line, &data, &flags)) != 0) {
+ snmp_log(LOG_ERR, " error: #%lu (file %s, line %d)\n",
+ numerical_reason, file, line);
+
+ /* if we have a text translation: */
+ if (data && (flags & ERR_TXT_STRING)) {
+ snmp_log(LOG_ERR, " Textual Error: %s\n", data);
+ /*
+ * per openssl man page: If it has been allocated by
+ * OPENSSL_malloc(), *flags&ERR_TXT_MALLOCED is true.
+ *
+ * arggh... stupid openssl prototype for ERR_get_error_line_data
+ * wants a const char **, but returns something that we might
+ * need to free??
+ */
+ if (flags & ERR_TXT_MALLOCED)
+ OPENSSL_free(NETSNMP_REMOVE_CONST(void *, data)); }
+ }
+
+ snmp_log(LOG_ERR, "---- End of OpenSSL Errors ----\n");
+}