diff --git a/SOURCES/net-snmp-5.8-broken-errmsg.patch b/SOURCES/net-snmp-5.8-broken-errmsg.patch
new file mode 100644
index 0000000..24b9039
--- /dev/null
+++ b/SOURCES/net-snmp-5.8-broken-errmsg.patch
@@ -0,0 +1,90 @@
+diff -urNp a/agent/mibgroup/host/hrh_filesys.c b/agent/mibgroup/host/hrh_filesys.c
+--- a/agent/mibgroup/host/hrh_filesys.c	2021-06-09 10:30:07.744455758 +0200
++++ b/agent/mibgroup/host/hrh_filesys.c	2021-06-09 10:32:50.657160232 +0200
+@@ -219,6 +219,7 @@ var_hrhfilesys(struct variable *vp,
+ {
+     int             fsys_idx;
+     static char    *string;
++    static char     empty_str[1];
+ 
+     fsys_idx =
+         header_hrhfilesys(vp, name, length, exact, var_len, write_method);
+@@ -235,7 +236,7 @@ var_hrhfilesys(struct variable *vp,
+         *var_len = 0;
+         if (asprintf(&string, "%s", HRFS_entry->path) >= 0)
+             *var_len = strlen(string);
+-        return (u_char *) string;
++        return (u_char *)(string ? string : empty_str);
+     case HRFSYS_RMOUNT:
+         free(string);
+         if (HRFS_entry->flags & NETSNMP_FS_FLAG_REMOTE) {
+@@ -245,7 +246,7 @@ var_hrhfilesys(struct variable *vp,
+             string = strdup("");
+         }
+         *var_len = string ? strlen(string) : 0;
+-        return (u_char *) string;
++        return (u_char *)(string ? string : empty_str);
+ 
+     case HRFSYS_TYPE:
+         fsys_type_id[fsys_type_len - 1] = 
+diff -urNp a/agent/mibgroup/ucd-snmp/disk.c b/agent/mibgroup/ucd-snmp/disk.c
+--- a/agent/mibgroup/ucd-snmp/disk.c	2021-06-09 10:30:07.728455689 +0200
++++ b/agent/mibgroup/ucd-snmp/disk.c	2021-06-09 10:34:32.722597366 +0200
+@@ -842,6 +842,7 @@ var_extensible_disk(struct variable *vp,
+     struct dsk_entry entry;
+     static long     long_ret;
+     static char    *errmsg;
++    static char     empty_str[1];
+ 
+     int i;
+     for (i = 0; i < numdisks; i++){
+@@ -950,7 +951,7 @@ tryAgain:
+                 *var_len = strlen(errmsg);
+             }
+         }
+-        return (u_char *) (errmsg);
++        return (u_char *)(errmsg ? errmsg : empty_str);
+     }
+     return NULL;
+ }
+diff -urNp a/agent/mibgroup/ucd-snmp/disk_hw.c b/agent/mibgroup/ucd-snmp/disk_hw.c
+--- a/agent/mibgroup/ucd-snmp/disk_hw.c	2021-06-09 10:30:07.727455684 +0200
++++ b/agent/mibgroup/ucd-snmp/disk_hw.c	2021-06-09 10:35:53.420943010 +0200
+@@ -314,6 +314,7 @@ var_extensible_disk(struct variable *vp,
+     unsigned long long val;
+     static long     long_ret;
+     static char    *errmsg;
++    static char     empty_str[1];
+     netsnmp_cache  *cache;
+ 
+     /* Update the fsys H/W module */
+@@ -432,7 +433,7 @@ tryAgain:
+              >= 0)) {
+             *var_len = strlen(errmsg);
+         }
+-        return (u_char *) errmsg;
++        return (u_char *)(errmsg ? errmsg : empty_str);
+     }
+     return NULL;
+ }
+diff -urNp a/agent/mibgroup/ucd-snmp/proc.c b/agent/mibgroup/ucd-snmp/proc.c
+--- a/agent/mibgroup/ucd-snmp/proc.c	2021-06-09 10:30:07.725455676 +0200
++++ b/agent/mibgroup/ucd-snmp/proc.c	2021-06-09 10:37:31.143361548 +0200
+@@ -267,7 +267,7 @@ var_extensible_proc(struct variable *vp,
+     struct myproc  *proc;
+     static long     long_ret;
+     static char    *errmsg;
+-
++    static char     empty_str[1];
+ 
+     if (header_simple_table
+         (vp, name, length, exact, var_len, write_method, numprocs))
+@@ -330,7 +330,7 @@ var_extensible_proc(struct variable *vp,
+                 }
+             }
+             *var_len = errmsg ? strlen(errmsg) : 0;
+-            return ((u_char *) errmsg);
++            return (u_char *)(errmsg ? errmsg : empty_str);
+         case ERRORFIX:
+             *write_method = fixProcError;
+             long_return = fixproc.result;
diff --git a/SOURCES/net-snmp-5.8-digest-from-ECC.patch b/SOURCES/net-snmp-5.8-digest-from-ECC.patch
new file mode 100644
index 0000000..dab54de
--- /dev/null
+++ b/SOURCES/net-snmp-5.8-digest-from-ECC.patch
@@ -0,0 +1,98 @@
+From a1968db524e087a36a19a351b89bf6f1633819aa Mon Sep 17 00:00:00 2001
+From: minfrin <minfrin@users.noreply.github.com>
+Date: Tue, 5 Jan 2021 23:17:14 +0000
+Subject: [PATCH] Add support for digests detected from ECC certificates
+
+Previously, the digest could be detected on RSA certificates only. This
+patch adds detection for ECC certificates.
+
+[ bvanassche: changed _htmap2 into a two-dimensional array and renamed _htmap2
+  back to _htmap ]
+---
+ snmplib/snmp_openssl.c | 60 +++++++++++++++++++++++++++++++++++-------
+ 1 file changed, 50 insertions(+), 10 deletions(-)
+
+diff --git a/snmplib/snmp_openssl.c b/snmplib/snmp_openssl.c
+index c092a007af..432cb5c27c 100644
+--- a/snmplib/snmp_openssl.c
++++ b/snmplib/snmp_openssl.c
+@@ -521,18 +521,54 @@ netsnmp_openssl_cert_dump_extensions(X509 *ocert)
+     }
+ }
+ 
+-static int _htmap[NS_HASH_MAX + 1] = {
+-    0, NID_md5WithRSAEncryption, NID_sha1WithRSAEncryption,
+-    NID_sha224WithRSAEncryption, NID_sha256WithRSAEncryption,
+-    NID_sha384WithRSAEncryption, NID_sha512WithRSAEncryption };
++static const struct {
++    uint16_t nid;
++    uint16_t ht;
++} _htmap[] = {
++    { 0, NS_HASH_NONE },
++#ifdef NID_md5WithRSAEncryption
++    { NID_md5WithRSAEncryption, NS_HASH_MD5 },
++#endif
++#ifdef NID_sha1WithRSAEncryption
++    { NID_sha1WithRSAEncryption, NS_HASH_SHA1 },
++#endif
++#ifdef NID_ecdsa_with_SHA1
++    { NID_ecdsa_with_SHA1, NS_HASH_SHA1 },
++#endif
++#ifdef NID_sha224WithRSAEncryption
++    { NID_sha224WithRSAEncryption, NS_HASH_SHA224 },
++#endif
++#ifdef NID_ecdsa_with_SHA224
++    { NID_ecdsa_with_SHA224, NS_HASH_SHA224 },
++#endif
++#ifdef NID_sha256WithRSAEncryption
++    { NID_sha256WithRSAEncryption, NS_HASH_SHA256 },
++#endif
++#ifdef NID_ecdsa_with_SHA256
++    { NID_ecdsa_with_SHA256, NS_HASH_SHA256 },
++#endif
++#ifdef NID_sha384WithRSAEncryption
++    { NID_sha384WithRSAEncryption, NS_HASH_SHA384 },
++#endif
++#ifdef NID_ecdsa_with_SHA384
++    { NID_ecdsa_with_SHA384, NS_HASH_SHA384 },
++#endif
++#ifdef NID_sha512WithRSAEncryption
++    { NID_sha512WithRSAEncryption, NS_HASH_SHA512 },
++#endif
++#ifdef NID_ecdsa_with_SHA512
++    { NID_ecdsa_with_SHA512, NS_HASH_SHA512 },
++#endif
++};
+ 
+ int
+ _nid2ht(int nid)
+ {
+     int i;
+-    for (i=1; i<= NS_HASH_MAX; ++i) {
+-        if (nid == _htmap[i])
+-            return i;
++
++    for (i = 0; i < sizeof(_htmap) / sizeof(_htmap[0]); i++) {
++        if (_htmap[i].nid == nid)
++            return _htmap[i].ht;
+     }
+     return 0;
+ }
+@@ -541,9 +577,13 @@ _nid2ht(int nid)
+ int
+ _ht2nid(int ht)
+ {
+-    if ((ht < 0) || (ht > NS_HASH_MAX))
+-        return 0;
+-    return _htmap[ht];
++    int i;
++
++    for (i = 0; i < sizeof(_htmap) / sizeof(_htmap[0]); i++) {
++        if (_htmap[i].ht == ht)
++            return _htmap[i].nid;
++    }
++    return 0;
+ }
+ #endif /* NETSNMP_FEATURE_REMOVE_OPENSSL_HT2NID */
+ 
+
diff --git a/SOURCES/net-snmp-5.8-double-IP-parsing.patch b/SOURCES/net-snmp-5.8-double-IP-parsing.patch
new file mode 100644
index 0000000..333e301
--- /dev/null
+++ b/SOURCES/net-snmp-5.8-double-IP-parsing.patch
@@ -0,0 +1,48 @@
+From 1bb941d6fcd7ac2db5a54b95ee0ed07ec9861e70 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Josef=20=C5=98=C3=ADdk=C3=BD?= <jridky@redhat.com>
+Date: Fri, 12 Mar 2021 10:15:30 +0100
+Subject: [PATCH] Prevent parsing IP address twice (#199)
+
+This fixes issue, that is caused by parsing IP address twice.
+First as IPv4 and as IPv6 at second, even thow the address was
+properly parsed as a valid IPv4 address.
+---
+ snmplib/transports/snmpUDPDomain.c     |  2 +-
+ snmplib/transports/snmpUDPIPv6Domain.c | 10 +++++++++-
+ 2 files changed, 10 insertions(+), 2 deletions(-)
+
+diff --git a/snmplib/transports/snmpUDPDomain.c b/snmplib/transports/snmpUDPDomain.c
+index b96497f3a3..b594a389b9 100644
+--- a/snmplib/transports/snmpUDPDomain.c
++++ b/snmplib/transports/snmpUDPDomain.c
+@@ -387,7 +387,7 @@ netsnmp_udp_parse_security(const char *token, char *param)
+             /* Nope, wasn't a dotted quad.  Must be a hostname. */
+             int ret = netsnmp_gethostbyname_v4(sourcep, &network.s_addr);
+             if (ret < 0) {
+-                config_perror("cannot resolve source hostname");
++                config_perror("cannot resolve IPv4 source hostname");
+                 return;
+             }
+         }
+diff --git a/snmplib/transports/snmpUDPIPv6Domain.c b/snmplib/transports/snmpUDPIPv6Domain.c
+index 238c8a9d63..7db19c5c02 100644
+--- a/snmplib/transports/snmpUDPIPv6Domain.c
++++ b/snmplib/transports/snmpUDPIPv6Domain.c
+@@ -736,7 +736,15 @@ netsnmp_udp6_parse_security(const char *token, char *param)
+                 memset(&pton_addr.sin6_addr.s6_addr, '\0',
+                        sizeof(struct in6_addr));
+             } else if (inet_pton(AF_INET6, sourcep, &pton_addr.sin6_addr) != 1) {
+-                /* Nope, wasn't a numeric address. Must be a hostname. */
++                /* Nope, wasn't a numeric IPv6 address. Must be IPv4 or a hostname. */
++
++                /* Try interpreting as dotted quad - IPv4 */
++                struct in_addr network;
++                if (inet_pton(AF_INET, sourcep, &network) > 0){
++                    /* Yes, it's IPv4 - so it's already parsed and we can return. */
++                    DEBUGMSGTL(("com2sec6", "IPv4 detected for IPv6 parser. Skipping.\n"));
++                    return;
++                }
+ #if HAVE_GETADDRINFO
+                 int             gai_error;
+ 
+
diff --git a/SOURCES/net-snmp-5.8-engine-id.patch b/SOURCES/net-snmp-5.8-engine-id.patch
new file mode 100644
index 0000000..16c46a9
--- /dev/null
+++ b/SOURCES/net-snmp-5.8-engine-id.patch
@@ -0,0 +1,25 @@
+From 79f014464ba761e2430cc767b021993ab9379822 Mon Sep 17 00:00:00 2001
+From: Wes Hardaker <opensource@hardakers.net>
+Date: Tue, 8 Jan 2019 08:52:29 -0800
+Subject: [PATCH] NEWS: snmptrap: BUG: 2899: Patch from Drew Roedersheimer to
+ set library engineboots/time values before sending
+
+---
+ apps/snmptrap.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/apps/snmptrap.c b/apps/snmptrap.c
+index d16d2fa671..12808d07e4 100644
+--- a/apps/snmptrap.c
++++ b/apps/snmptrap.c
+@@ -237,6 +237,9 @@ main(int argc, char *argv[])
+             session.engineBoots = 1;
+         if (session.engineTime == 0)    /* not really correct, */
+             session.engineTime = get_uptime();  /* but it'll work. Sort of. */
++
++        set_enginetime(session.securityEngineID, session.securityEngineIDLen,
++                       session.engineBoots, session.engineTime, TRUE);
+     }
+ 
+     ss = snmp_add(&session,
+
diff --git a/SOURCES/net-snmp-5.8-fix-cert-crash.patch b/SOURCES/net-snmp-5.8-fix-cert-crash.patch
new file mode 100644
index 0000000..281bbae
--- /dev/null
+++ b/SOURCES/net-snmp-5.8-fix-cert-crash.patch
@@ -0,0 +1,67 @@
+diff -urNp a/snmplib/snmp_openssl.c b/snmplib/snmp_openssl.c
+--- a/snmplib/snmp_openssl.c	2021-06-09 12:38:23.196037329 +0200
++++ b/snmplib/snmp_openssl.c	2021-06-09 12:44:11.782503048 +0200
+@@ -284,31 +284,30 @@ _cert_get_extension(X509_EXTENSION  *oex
+     }
+     if (X509V3_EXT_print(bio, oext, 0, 0) != 1) {
+         snmp_log(LOG_ERR, "could not print extension!\n");
+-        BIO_vfree(bio);
+-        return NULL;
++        goto out;
+     }
+ 
+     space = BIO_get_mem_data(bio, &data);
+     if (buf && *buf) {
+-        if (*len < space) 
+-            buf_ptr = NULL;
+-        else
+-            buf_ptr = *buf;
++        if (*len < space +1) {
++            snmp_log(LOG_ERR, "not enough buffer space to print extension\n"); 
++            goto out;
++        }
++        buf_ptr = *buf;    
++    } else {
++        buf_ptr = calloc(1, space + 1);
+     }
+-    else
+-        buf_ptr = calloc(1,space + 1);
+     
+     if (!buf_ptr) {
+-        snmp_log(LOG_ERR,
+-                 "not enough space or error in allocation for extenstion\n");
+-        BIO_vfree(bio);
+-        return NULL;
++        snmp_log(LOG_ERR, "error in allocation for extenstion\n");
++        goto out;
+     }
+     memcpy(buf_ptr, data, space);
+     buf_ptr[space] = 0;
+     if (len)
+         *len = space;
+ 
++out:
+     BIO_vfree(bio);
+ 
+     return buf_ptr;
+@@ -479,7 +478,7 @@ netsnmp_openssl_cert_dump_extensions(X50
+ {
+     X509_EXTENSION  *extension;
+     const char      *extension_name;
+-    char             buf[SNMP_MAXBUF_SMALL], *buf_ptr = buf, *str, *lf;
++    char             buf[SNMP_MAXBUF], *buf_ptr = buf, *str, *lf;
+     int              i, num_extensions, buf_len, nid;
+ 
+     if (NULL == ocert)
+@@ -499,6 +498,11 @@ netsnmp_openssl_cert_dump_extensions(X50
+         extension_name = OBJ_nid2sn(nid);
+         buf_len = sizeof(buf);
+         str = _cert_get_extension_str_at(ocert, i, &buf_ptr, &buf_len, 0);
++        if (!str) {
++            DEBUGMSGT(("9:cert:dump", "    %2d: %s\n", i,
++                        extension_name));
++            continue;
++        }
+         lf = strchr(str, '\n'); /* look for multiline strings */
+         if (NULL != lf)
+             *lf = '\0'; /* only log first line of multiline here */
diff --git a/SOURCES/net-snmp-5.8-intermediate-certs.patch b/SOURCES/net-snmp-5.8-intermediate-certs.patch
new file mode 100644
index 0000000..aff1ea2
--- /dev/null
+++ b/SOURCES/net-snmp-5.8-intermediate-certs.patch
@@ -0,0 +1,1066 @@
+diff -urNp a/include/net-snmp/library/cert_util.h b/include/net-snmp/library/cert_util.h
+--- a/include/net-snmp/library/cert_util.h	2021-06-09 10:55:22.767954797 +0200
++++ b/include/net-snmp/library/cert_util.h	2021-06-09 10:56:36.725272293 +0200
+@@ -55,7 +55,8 @@ extern "C" {
+         char           *common_name;
+ 
+         u_char          hash_type;
+-        u_char          _pad[3]; /* for future use */
++        u_char          _pad[1]; /* for future use */
++        u_short         offset;
+     } netsnmp_cert;
+ 
+ /** types */
+@@ -100,6 +101,7 @@ extern "C" {
+ 
+     NETSNMP_IMPORT
+     netsnmp_cert *netsnmp_cert_find(int what, int where, void *hint);
++    netsnmp_void_array *netsnmp_certs_find(int what, int where, void *hint);
+ 
+     int netsnmp_cert_check_vb_fingerprint(const netsnmp_variable_list *var);
+ 
+diff -urNp a/include/net-snmp/library/dir_utils.h b/include/net-snmp/library/dir_utils.h
+--- a/include/net-snmp/library/dir_utils.h	2021-06-09 10:55:22.767954797 +0200
++++ b/include/net-snmp/library/dir_utils.h	2021-06-09 10:56:36.726272298 +0200
+@@ -53,6 +53,8 @@ extern "C" {
+ #define NETSNMP_DIR_NSFILE                            0x0010
+ /** load stats in netsnmp_file */
+ #define NETSNMP_DIR_NSFILE_STATS                      0x0020
++/** allow files to be indexed more than once */
++#define NETSNMP_DIR_ALLOW_DUPLICATES                  0x0040
+ 
+     
+         
+diff -urNp a/snmplib/cert_util.c b/snmplib/cert_util.c
+--- a/snmplib/cert_util.c	2021-06-09 10:55:22.785954874 +0200
++++ b/snmplib/cert_util.c	2021-06-09 11:02:43.890848394 +0200
+@@ -104,7 +104,7 @@ netsnmp_feature_child_of(tls_fingerprint
+  * bump this value whenever cert index format changes, so indexes
+  * will be regenerated with new format.
+  */
+-#define CERT_INDEX_FORMAT  1
++#define CERT_INDEX_FORMAT  2
+ 
+ static netsnmp_container *_certs = NULL;
+ static netsnmp_container *_keys = NULL;
+@@ -130,6 +130,8 @@ static int  _cert_fn_ncompare(netsnmp_ce
+                               netsnmp_cert_common *rhs);
+ static void _find_partner(netsnmp_cert *cert, netsnmp_key *key);
+ static netsnmp_cert *_find_issuer(netsnmp_cert *cert);
++static netsnmp_void_array *_cert_reduce_subset_first(netsnmp_void_array *matching);
++static netsnmp_void_array *_cert_reduce_subset_what(netsnmp_void_array *matching, int what);
+ static netsnmp_void_array *_cert_find_subset_fn(const char *filename,
+                                                 const char *directory);
+ static netsnmp_void_array *_cert_find_subset_sn(const char *subject);
+@@ -349,6 +351,8 @@ _get_cert_container(const char *use)
+ {
+     netsnmp_container *c;
+ 
++    int rc;
++
+     c = netsnmp_container_find("certs:binary_array");
+     if (NULL == c) {
+         snmp_log(LOG_ERR, "could not create container for %s\n", use);
+@@ -358,6 +362,8 @@ _get_cert_container(const char *use)
+     c->free_item = (netsnmp_container_obj_func*)_cert_free;
+     c->compare = (netsnmp_container_compare*)_cert_compare;
+ 
++    CONTAINER_SET_OPTIONS(c, CONTAINER_KEY_ALLOW_DUPLICATES, rc);
++
+     return c;
+ }
+ 
+@@ -366,6 +372,8 @@ _setup_containers(void)
+ {
+     netsnmp_container *additional_keys;
+ 
++    int rc;
++
+     _certs = _get_cert_container("netsnmp certificates");
+     if (NULL == _certs)
+         return;
+@@ -380,6 +388,7 @@ _setup_containers(void)
+     additional_keys->container_name = strdup("certs_cn");
+     additional_keys->free_item = NULL;
+     additional_keys->compare = (netsnmp_container_compare*)_cert_cn_compare;
++    CONTAINER_SET_OPTIONS(additional_keys, CONTAINER_KEY_ALLOW_DUPLICATES, rc);
+     netsnmp_container_add_index(_certs, additional_keys);
+ 
+     /** additional keys: subject name */
+@@ -393,6 +402,7 @@ _setup_containers(void)
+     additional_keys->free_item = NULL;
+     additional_keys->compare = (netsnmp_container_compare*)_cert_sn_compare;
+     additional_keys->ncompare = (netsnmp_container_compare*)_cert_sn_ncompare;
++    CONTAINER_SET_OPTIONS(additional_keys, CONTAINER_KEY_ALLOW_DUPLICATES, rc);
+     netsnmp_container_add_index(_certs, additional_keys);
+ 
+     /** additional keys: file name */
+@@ -406,6 +416,7 @@ _setup_containers(void)
+     additional_keys->free_item = NULL;
+     additional_keys->compare = (netsnmp_container_compare*)_cert_fn_compare;
+     additional_keys->ncompare = (netsnmp_container_compare*)_cert_fn_ncompare;
++    CONTAINER_SET_OPTIONS(additional_keys, CONTAINER_KEY_ALLOW_DUPLICATES, rc);
+     netsnmp_container_add_index(_certs, additional_keys);
+ 
+     _keys = netsnmp_container_find("cert_keys:binary_array");
+@@ -428,9 +439,9 @@ netsnmp_cert_map_container(void)
+ }
+ 
+ static netsnmp_cert *
+-_new_cert(const char *dirname, const char *filename, int certType,
+-          int hashType, const char *fingerprint, const char *common_name,
+-          const char *subject)
++_new_cert(const char *dirname, const char *filename, int certType, int offset,
++          int allowed_uses, int hashType, const char *fingerprint,
++          const char *common_name,  const char *subject)
+ {
+     netsnmp_cert    *cert;
+ 
+@@ -450,8 +461,10 @@ _new_cert(const char *dirname, const cha
+ 
+     cert->info.dir = strdup(dirname);
+     cert->info.filename = strdup(filename);
+-    cert->info.allowed_uses = NS_CERT_REMOTE_PEER;
++    /* only the first certificate is allowed to be a remote peer */
++    cert->info.allowed_uses = allowed_uses;
+     cert->info.type = certType;
++    cert->offset = offset;
+     if (fingerprint) {
+         cert->hash_type = hashType;
+         cert->fingerprint = strdup(fingerprint);
+@@ -888,14 +901,86 @@ _certindex_new( const char *dirname )
+  * certificate utility functions
+  *
+  */
++static BIO *
++netsnmp_open_bio(const char *dir, const char *filename)
++{
++    BIO            *certbio;
++    char            file[SNMP_MAXPATH];
++
++    DEBUGMSGT(("9:cert:read", "Checking file %s\n", filename));
++
++    certbio = BIO_new(BIO_s_file());
++    if (NULL == certbio) {
++        snmp_log(LOG_ERR, "error creating BIO\n");
++        return NULL;
++    }
++
++    snprintf(file, sizeof(file),"%s/%s", dir, filename);
++    if (BIO_read_filename(certbio, file) <=0) {
++        snmp_log(LOG_ERR, "error reading certificate/key %s into BIO\n", file);
++        BIO_vfree(certbio);
++        return NULL;
++    }
++
++    return certbio;
++}
++
++static void
++netsnmp_ocert_parse(netsnmp_cert *cert, X509 *ocert)
++{
++    int             is_ca;
++
++    cert->ocert = ocert;
++
++    /*
++     * X509_check_ca return codes:
++     * 0 not a CA
++     * 1 is a CA
++     * 2 basicConstraints absent so "maybe" a CA
++     * 3 basicConstraints absent but self signed V1.
++     * 4 basicConstraints absent but keyUsage present and keyCertSign asserted.
++     * 5 outdated Netscape Certificate Type CA extension.
++     */
++    is_ca = X509_check_ca(ocert);
++    if (1 == is_ca)
++        cert->info.allowed_uses |= NS_CERT_CA;
++
++    if (NULL == cert->subject) {
++        cert->subject = X509_NAME_oneline(X509_get_subject_name(ocert), NULL,
++                                          0);
++        DEBUGMSGT(("9:cert:add:subject", "subject name: %s\n", cert->subject));
++    }
++
++    if (NULL == cert->issuer) {
++        cert->issuer = X509_NAME_oneline(X509_get_issuer_name(ocert), NULL, 0);
++        if (strcmp(cert->subject, cert->issuer) == 0) {
++            free(cert->issuer);
++            cert->issuer = strdup("self-signed");
++        }
++        DEBUGMSGT(("9:cert:add:issuer", "CA issuer: %s\n", cert->issuer));
++    }
++
++    if (NULL == cert->fingerprint) {
++        cert->hash_type = netsnmp_openssl_cert_get_hash_type(ocert);
++        cert->fingerprint =
++            netsnmp_openssl_cert_get_fingerprint(ocert, cert->hash_type);
++    }
++
++    if (NULL == cert->common_name) {
++        cert->common_name =netsnmp_openssl_cert_get_commonName(ocert, NULL,
++                                                               NULL);
++        DEBUGMSGT(("9:cert:add:name","%s\n", cert->common_name));
++    }
++
++}
++
+ static X509 *
+ netsnmp_ocert_get(netsnmp_cert *cert)
+ {
+     BIO            *certbio;
+     X509           *ocert = NULL;
++    X509           *ncert = NULL;
+     EVP_PKEY       *okey = NULL;
+-    char            file[SNMP_MAXPATH];
+-    int             is_ca;
+ 
+     if (NULL == cert)
+         return NULL;
+@@ -912,51 +997,33 @@ netsnmp_ocert_get(netsnmp_cert *cert)
+         }
+     }
+ 
+-    DEBUGMSGT(("9:cert:read", "Checking file %s\n", cert->info.filename));
+-
+-    certbio = BIO_new(BIO_s_file());
+-    if (NULL == certbio) {
+-        snmp_log(LOG_ERR, "error creating BIO\n");
+-        return NULL;
+-    }
+-
+-    snprintf(file, sizeof(file),"%s/%s", cert->info.dir, cert->info.filename);
+-    if (BIO_read_filename(certbio, file) <=0) {
+-        snmp_log(LOG_ERR, "error reading certificate %s into BIO\n", file);
+-        BIO_vfree(certbio);
++    certbio = netsnmp_open_bio(cert->info.dir, cert->info.filename);
++    if (!certbio) {
+         return NULL;
+     }
+ 
+-    if (NS_CERT_TYPE_UNKNOWN == cert->info.type) {
+-        char *pos = strrchr(cert->info.filename, '.');
+-        if (NULL == pos)
+-            return NULL;
+-        cert->info.type = _cert_ext_type(++pos);
+-        netsnmp_assert(cert->info.type != NS_CERT_TYPE_UNKNOWN);
+-    }
+-
+     switch (cert->info.type) {
+ 
+         case NS_CERT_TYPE_DER:
++            (void)BIO_seek(certbio, cert->offset);
+             ocert = d2i_X509_bio(certbio,NULL); /* DER/ASN1 */
+             if (NULL != ocert)
+                 break;
+-            (void)BIO_reset(certbio);
+             /* Check for PEM if DER didn't work */
+             /* FALLTHROUGH */
+ 
+         case NS_CERT_TYPE_PEM:
+-            ocert = PEM_read_bio_X509_AUX(certbio, NULL, NULL, NULL);
++            (void)BIO_seek(certbio, cert->offset);
++            ocert = ncert = PEM_read_bio_X509_AUX(certbio, NULL, NULL, NULL);
+             if (NULL == ocert)
+                 break;
+             if (NS_CERT_TYPE_DER == cert->info.type) {
+                 DEBUGMSGT(("9:cert:read", "Changing type from DER to PEM\n"));
+                 cert->info.type = NS_CERT_TYPE_PEM;
+             }
+-            /** check for private key too */
+-            if (NULL == cert->key) {
+-                (void)BIO_reset(certbio);
+-                okey =  PEM_read_bio_PrivateKey(certbio, NULL, NULL, NULL);
++            /** check for private key too, but only if we're the first certificate */
++            if (0 == cert->offset && NULL == cert->key) {
++                okey = PEM_read_bio_PrivateKey(certbio, NULL, NULL, NULL);
+                 if (NULL != okey) {
+                     netsnmp_key  *key;
+                     DEBUGMSGT(("cert:read:key", "found key with cert in %s\n",
+@@ -983,7 +1050,7 @@ netsnmp_ocert_get(netsnmp_cert *cert)
+             break;
+ #ifdef CERT_PKCS12_SUPPORT_MAYBE_LATER
+         case NS_CERT_TYPE_PKCS12:
+-            (void)BIO_reset(certbio);
++            (void)BIO_seek(certbio, cert->offset);
+             PKCS12 *p12 = d2i_PKCS12_bio(certbio, NULL);
+             if ( (NULL != p12) && (PKCS12_verify_mac(p12, "", 0) ||
+                                    PKCS12_verify_mac(p12, NULL, 0)))
+@@ -1003,46 +1070,7 @@ netsnmp_ocert_get(netsnmp_cert *cert)
+         return NULL;
+     }
+ 
+-    cert->ocert = ocert;
+-    /*
+-     * X509_check_ca return codes:
+-     * 0 not a CA
+-     * 1 is a CA
+-     * 2 basicConstraints absent so "maybe" a CA
+-     * 3 basicConstraints absent but self signed V1.
+-     * 4 basicConstraints absent but keyUsage present and keyCertSign asserted.
+-     * 5 outdated Netscape Certificate Type CA extension.
+-     */
+-    is_ca = X509_check_ca(ocert);
+-    if (1 == is_ca)
+-        cert->info.allowed_uses |= NS_CERT_CA;
+-
+-    if (NULL == cert->subject) {
+-        cert->subject = X509_NAME_oneline(X509_get_subject_name(ocert), NULL,
+-                                          0);
+-        DEBUGMSGT(("9:cert:add:subject", "subject name: %s\n", cert->subject));
+-    }
+-
+-    if (NULL == cert->issuer) {
+-        cert->issuer = X509_NAME_oneline(X509_get_issuer_name(ocert), NULL, 0);
+-        if (strcmp(cert->subject, cert->issuer) == 0) {
+-            free(cert->issuer);
+-            cert->issuer = strdup("self-signed");
+-        }
+-        DEBUGMSGT(("9:cert:add:issuer", "CA issuer: %s\n", cert->issuer));
+-    }
+-    
+-    if (NULL == cert->fingerprint) {
+-        cert->hash_type = netsnmp_openssl_cert_get_hash_type(ocert);
+-        cert->fingerprint =
+-            netsnmp_openssl_cert_get_fingerprint(ocert, cert->hash_type);
+-    }
+-    
+-    if (NULL == cert->common_name) {
+-        cert->common_name =netsnmp_openssl_cert_get_commonName(ocert, NULL,
+-                                                               NULL);
+-        DEBUGMSGT(("9:cert:add:name","%s\n", cert->common_name));
+-    }
++    netsnmp_ocert_parse(cert, ocert);
+ 
+     return ocert;
+ }
+@@ -1052,7 +1080,6 @@ netsnmp_okey_get(netsnmp_key  *key)
+ {
+     BIO            *keybio;
+     EVP_PKEY       *okey;
+-    char            file[SNMP_MAXPATH];
+ 
+     if (NULL == key)
+         return NULL;
+@@ -1060,19 +1087,8 @@ netsnmp_okey_get(netsnmp_key  *key)
+     if (key->okey)
+         return key->okey;
+ 
+-    snprintf(file, sizeof(file),"%s/%s", key->info.dir, key->info.filename);
+-    DEBUGMSGT(("cert:key:read", "Checking file %s\n", key->info.filename));
+-
+-    keybio = BIO_new(BIO_s_file());
+-    if (NULL == keybio) {
+-        snmp_log(LOG_ERR, "error creating BIO\n");
+-        return NULL;
+-    }
+-
+-    if (BIO_read_filename(keybio, file) <=0) {
+-        snmp_log(LOG_ERR, "error reading certificate %s into BIO\n",
+-                 key->info.filename);
+-        BIO_vfree(keybio);
++    keybio = netsnmp_open_bio(key->info.dir, key->info.filename);
++    if (!keybio) {
+         return NULL;
+     }
+ 
+@@ -1158,7 +1174,7 @@ netsnmp_cert_load_x509(netsnmp_cert *cer
+             cert->issuer_cert =  _find_issuer(cert);
+             if (NULL == cert->issuer_cert) {
+                 DEBUGMSGT(("cert:load:warn",
+-                           "couldn't load CA chain for cert %s\n",
++                           "couldn't load full CA chain for cert %s\n",
+                            cert->info.filename));
+                 rc = CERT_LOAD_PARTIAL;
+                 break;
+@@ -1167,7 +1183,7 @@ netsnmp_cert_load_x509(netsnmp_cert *cer
+         /** get issuer ocert */
+         if ((NULL == cert->issuer_cert->ocert) &&
+             (netsnmp_ocert_get(cert->issuer_cert) == NULL)) {
+-            DEBUGMSGT(("cert:load:warn", "couldn't load cert chain for %s\n",
++            DEBUGMSGT(("cert:load:warn", "couldn't load full cert chain for %s\n",
+                        cert->info.filename));
+             rc = CERT_LOAD_PARTIAL;
+             break;
+@@ -1188,7 +1204,7 @@ _find_partner(netsnmp_cert *cert, netsnm
+         return;
+     }
+ 
+-    if(key) {
++    if (key) {
+         if (key->cert) {
+             DEBUGMSGT(("cert:partner", "key already has partner\n"));
+             return;
+@@ -1201,7 +1217,8 @@ _find_partner(netsnmp_cert *cert, netsnm
+             return;
+         *pos = 0;
+ 
+-        matching = _cert_find_subset_fn( filename, key->info.dir );
++        matching = _cert_reduce_subset_first(_cert_find_subset_fn( filename,
++                                             key->info.dir ));
+         if (!matching)
+             return;
+         if (1 == matching->size) {
+@@ -1221,7 +1238,7 @@ _find_partner(netsnmp_cert *cert, netsnm
+             DEBUGMSGT(("cert:partner", "%s matches multiple certs\n",
+                           key->info.filename));
+     }
+-    else if(cert) {
++    else if (cert) {
+         if (cert->key) {
+             DEBUGMSGT(("cert:partner", "cert already has partner\n"));
+             return;
+@@ -1259,76 +1276,189 @@ _find_partner(netsnmp_cert *cert, netsnm
+     }
+ }
+ 
++static netsnmp_key *
++_add_key(EVP_PKEY *okey, const char* dirname, const char* filename, FILE *index)
++{
++    netsnmp_key  *key;
++
++    key = _new_key(dirname, filename);
++    if (NULL == key) {
++        return NULL;
++    }
++
++    key->okey = okey;
++
++    if (-1 == CONTAINER_INSERT(_keys, key)) {
++        DEBUGMSGT(("cert:key:file:add:err",
++                   "error inserting key into container\n"));
++        netsnmp_key_free(key);
++        key = NULL;
++    }
++    if (index) {
++        fprintf(index, "k:%s\n", filename);
++    }
++
++    return key;
++}
++
++static netsnmp_cert *
++_add_cert(X509 *ocert, const char* dirname, const char* filename, int type, int offset,
++          int allowed_uses, FILE *index)
++{
++    netsnmp_cert *cert;
++
++    cert = _new_cert(dirname, filename, type, offset,
++                     allowed_uses, -1, NULL, NULL, NULL);
++    if (NULL == cert)
++        return NULL;
++
++    netsnmp_ocert_parse(cert, ocert);
++
++    if (-1 == CONTAINER_INSERT(_certs, cert)) {
++        DEBUGMSGT(("cert:file:add:err",
++                   "error inserting cert into container\n"));
++        netsnmp_cert_free(cert);
++        return NULL;
++    }
++
++    if (index) {
++        /** filename = NAME_MAX = 255 */
++        /** fingerprint max = 64*3=192 for sha512 */
++        /** common name / CN  = 64 */
++        if (cert)
++            fprintf(index, "c:%s %d %d %d %d %s '%s' '%s'\n", filename,
++                    cert->info.type, cert->offset, cert->info.allowed_uses,
++                    cert->hash_type, cert->fingerprint,
++                    cert->common_name, cert->subject);
++    }
++
++    return cert;
++}
++
+ static int
+ _add_certfile(const char* dirname, const char* filename, FILE *index)
+ {
+-    X509         *ocert;
+-    EVP_PKEY     *okey;
++    BIO          *certbio;
++    X509         *ocert = NULL;
++    X509         *ncert;
++    EVP_PKEY     *okey = NULL;
+     netsnmp_cert *cert = NULL;
+     netsnmp_key  *key = NULL;
+     char          certfile[SNMP_MAXPATH];
+     int           type;
++    int           offset = 0;
+ 
+     if (((const void*)NULL == dirname) || (NULL == filename))
+         return -1;
+ 
+     type = _type_from_filename(filename);
+-    netsnmp_assert(type != NS_CERT_TYPE_UNKNOWN);
++    if (type == NS_CERT_TYPE_UNKNOWN) {
++        snmp_log(LOG_ERR, "certificate file '%s' type not recognised, ignoring\n", filename);
++        return -1;
++    }
+ 
+-    snprintf(certfile, sizeof(certfile),"%s/%s", dirname, filename);
++    certbio = netsnmp_open_bio(dirname, filename);
++    if (!certbio) {
++        return -1;
++    }
+ 
+-    DEBUGMSGT(("9:cert:file:add", "Checking file: %s (type %d)\n", filename,
+-               type));
++    switch (type) {
+ 
+-    if (NS_CERT_TYPE_KEY == type) {
+-        key = _new_key(dirname, filename);
+-        if (NULL == key)
+-            return -1;
+-        okey = netsnmp_okey_get(key);
+-        if (NULL == okey) {
+-            netsnmp_key_free(key);
+-            return -1;
+-        }
+-        key->okey = okey;
+-        if (-1 == CONTAINER_INSERT(_keys, key)) {
+-            DEBUGMSGT(("cert:key:file:add:err",
+-                       "error inserting key into container\n"));
+-            netsnmp_key_free(key);
+-            key = NULL;
+-        }
+-    }
+-    else {
+-        cert = _new_cert(dirname, filename, type, -1, NULL, NULL, NULL);
+-        if (NULL == cert)
+-            return -1;
+-        ocert = netsnmp_ocert_get(cert);
+-        if (NULL == ocert) {
+-            netsnmp_cert_free(cert);
+-            return -1;
+-        }
+-        cert->ocert = ocert;
+-        if (-1 == CONTAINER_INSERT(_certs, cert)) {
+-            DEBUGMSGT(("cert:file:add:err",
+-                       "error inserting cert into container\n"));
+-            netsnmp_cert_free(cert);
+-            cert = NULL;
+-        }
+-    }
+-    if ((NULL == cert) && (NULL == key)) {
+-        DEBUGMSGT(("cert:file:add:failure", "for %s\n", certfile));
+-        return -1;
++       case NS_CERT_TYPE_KEY: 
++
++           okey = PEM_read_bio_PrivateKey(certbio, NULL, NULL, NULL);
++           if (NULL == okey)
++               snmp_log(LOG_ERR, "error parsing key file %s\n",
++                     key->info.filename);
++           else {
++               key = _add_key(okey, dirname, filename, index);
++               if (NULL == key) {
++                   EVP_PKEY_free(okey);
++                      okey = NULL;
++               }
++           }
++           break;
++
++        case NS_CERT_TYPE_DER:
++
++            ocert = d2i_X509_bio(certbio, NULL); /* DER/ASN1 */
++            if (NULL != ocert) {
++                if (!_add_cert(ocert, dirname, filename, type, 0,
++                               NS_CERT_REMOTE_PEER, index)) {
++                    X509_free(ocert);
++                    ocert = NULL;
++                }
++                break;
++            }
++            (void)BIO_reset(certbio);
++            /* Check for PEM if DER didn't work */
++            /* FALLTHROUGH */
++
++        case NS_CERT_TYPE_PEM:
++
++            if (NS_CERT_TYPE_DER == type) {
++                DEBUGMSGT(("9:cert:read", "Changing type from DER to PEM\n"));
++                type = NS_CERT_TYPE_PEM;
++            }
++
++            /* read the private key first so we can record this in the index */
++            okey = PEM_read_bio_PrivateKey(certbio, NULL, NULL, NULL);
++
++            (void)BIO_reset(certbio);
++
++            /* certs are read after the key */
++	    ocert = ncert = PEM_read_bio_X509_AUX(certbio, NULL, NULL, NULL);
++            if (NULL != ocert) {
++                cert = _add_cert(ncert, dirname, filename, type, 0,
++                                 okey ? NS_CERT_IDENTITY | NS_CERT_REMOTE_PEER :
++                                 NS_CERT_REMOTE_PEER, index);
++                if (NULL == cert) {
++                    X509_free(ocert);
++                    ocert = ncert = NULL;
++                }
++            }
++            while (NULL != ncert) {
++                offset = BIO_tell(certbio);
++                ncert = PEM_read_bio_X509_AUX(certbio, NULL, NULL, NULL);
++                if (ncert) {
++                    if (NULL == _add_cert(ncert, dirname, filename, type, offset, 0, index)) {
++                        X509_free(ncert);
++                        ncert = NULL;
++                    }
++                }
++            }
++
++            if (NULL != okey) {
++                DEBUGMSGT(("cert:read:key", "found key with cert in %s\n",
++                           cert->info.filename));
++                key = _add_key(okey, dirname, filename, NULL);
++                if (NULL != key) {
++                    DEBUGMSGT(("cert:read:partner", "%s match found!\n",
++                               cert->info.filename));
++                    key->cert = cert;
++                    cert->key = key;
++                }
++                else {
++                    EVP_PKEY_free(okey);
++                    okey = NULL;
++                }
++            }
++
++            break;
++
++#ifdef CERT_PKCS12_SUPPORT_MAYBE_LATER
++        case NS_CERT_TYPE_PKCS12:
++#endif
++
++        default:
++            break;
+     }
+ 
+-    if (index) {
+-        /** filename = NAME_MAX = 255 */
+-        /** fingerprint max = 64*3=192 for sha512 */
+-        /** common name / CN  = 64 */
+-        if (cert)
+-            fprintf(index, "c:%s %d %d %s '%s' '%s'\n", filename,
+-                    cert->info.type, cert->hash_type, cert->fingerprint,
+-                    cert->common_name, cert->subject);
+-        else if (key)
+-            fprintf(index, "k:%s\n", filename);
++    BIO_vfree(certbio);
++
++    if ((NULL == ocert) && (NULL == okey)) {
++        snmp_log(LOG_ERR, "certificate file '%s' contained neither certificate nor key, ignoring\n", certfile);
++        return -1;
+     }
+ 
+     return 0;
+@@ -1342,8 +1472,10 @@ _cert_read_index(const char *dirname, st
+     struct stat     idx_stat;
+     char            tmpstr[SNMP_MAXPATH + 5], filename[NAME_MAX];
+     char            fingerprint[EVP_MAX_MD_SIZE*3], common_name[64+1], type_str[15];
+-    char            subject[SNMP_MAXBUF_SMALL], hash_str[15];
+-    int             count = 0, type, hash, version;
++    char            subject[SNMP_MAXBUF_SMALL], hash_str[15], offset_str[15];
++    char            allowed_uses_str[15];
++    ssize_t         offset;
++    int             count = 0, type, allowed_uses, hash, version;
+     netsnmp_cert    *cert;
+     netsnmp_key     *key;
+     netsnmp_container *newer, *found;
+@@ -1386,7 +1518,8 @@ _cert_read_index(const char *dirname, st
+                                               (netsnmp_directory_filter*)
+                                               _time_filter,(void*)&idx_stat,
+                                               NETSNMP_DIR_NSFILE |
+-                                              NETSNMP_DIR_NSFILE_STATS);
++                                              NETSNMP_DIR_NSFILE_STATS |
++                                              NETSNMP_DIR_ALLOW_DUPLICATES);
+     if (newer) {
+         DEBUGMSGT(("cert:index:parse", "Index outdated; files modified\n"));
+         CONTAINER_FREE_ALL(newer, NULL);
+@@ -1430,6 +1563,8 @@ _cert_read_index(const char *dirname, st
+             pos = &tmpstr[2];
+             if ((NULL == (pos=copy_nword(pos, filename, sizeof(filename)))) ||
+                 (NULL == (pos=copy_nword(pos, type_str, sizeof(type_str)))) ||
++                (NULL == (pos=copy_nword(pos, offset_str, sizeof(offset_str)))) ||
++                (NULL == (pos=copy_nword(pos, allowed_uses_str, sizeof(allowed_uses_str)))) ||
+                 (NULL == (pos=copy_nword(pos, hash_str, sizeof(hash_str)))) ||
+                 (NULL == (pos=copy_nword(pos, fingerprint,
+                                          sizeof(fingerprint)))) ||
+@@ -1442,9 +1577,11 @@ _cert_read_index(const char *dirname, st
+                 break;
+             }
+             type = atoi(type_str);
++            offset = atoi(offset_str);
++            allowed_uses = atoi(allowed_uses_str);
+             hash = atoi(hash_str);
+-            cert = (void*)_new_cert(dirname, filename, type, hash, fingerprint,
+-                                    common_name, subject);
++            cert = _new_cert(dirname, filename, type, offset, allowed_uses, hash,
++                             fingerprint, common_name, subject);
+             if (cert && 0 == CONTAINER_INSERT(found, cert))
+                 ++count;
+             else {
+@@ -1549,7 +1686,8 @@ _add_certdir(const char *dirname)
+                                               (netsnmp_directory_filter*)
+                                               &_cert_cert_filter, NULL,
+                                               NETSNMP_DIR_RELATIVE_PATH |
+-                                              NETSNMP_DIR_EMPTY_OK );
++                                              NETSNMP_DIR_EMPTY_OK |
++                                              NETSNMP_DIR_ALLOW_DUPLICATES);
+     if (NULL == cert_container) {
+         DEBUGMSGT(("cert:index:dir",
+                     "error creating container for cert files\n"));
+@@ -1637,7 +1775,7 @@ _cert_print(netsnmp_cert *c, void *conte
+     if (NULL == c)
+         return;
+ 
+-    DEBUGMSGT(("cert:dump", "cert %s in %s\n", c->info.filename, c->info.dir));
++    DEBUGMSGT(("cert:dump", "cert %s in %s at offset %d\n", c->info.filename, c->info.dir, c->offset));
+     DEBUGMSGT(("cert:dump", "   type %d flags 0x%x (%s)\n",
+              c->info.type, c->info.allowed_uses,
+               _mode_str(c->info.allowed_uses)));
+@@ -1841,7 +1979,8 @@ netsnmp_cert_find(int what, int where, v
+         netsnmp_void_array *matching;
+ 
+         DEBUGMSGT(("cert:find:params", " hint = %s\n", (char *)hint));
+-        matching = _cert_find_subset_fn( filename, NULL );
++        matching = _cert_reduce_subset_what(_cert_find_subset_fn(
++                                            filename, NULL ), what);
+         if (!matching)
+             return NULL;
+         if (1 == matching->size)
+@@ -1887,6 +2026,32 @@ netsnmp_cert_find(int what, int where, v
+     return result;
+ }
+ 
++netsnmp_void_array *
++netsnmp_certs_find(int what, int where, void *hint)
++{
++
++    DEBUGMSGT(("certs:find:params", "looking for %s(%d) in %s(0x%x), hint %p\n",
++               _mode_str(what), what, _where_str(where), where, hint));
++
++    if (NS_CERTKEY_FILE == where) {
++        /** hint == filename */
++        char               *filename = (char*)hint;
++        netsnmp_void_array *matching;
++
++        DEBUGMSGT(("cert:find:params", " hint = %s\n", (char *)hint));
++        matching = _cert_reduce_subset_what(_cert_find_subset_fn(
++                                            filename, NULL ), what);
++
++        return matching;
++    } /* where = NS_CERTKEY_FILE */
++    else { /* unknown location */
++
++        DEBUGMSGT(("certs:find:err", "unhandled location %d for %d\n", where,
++                   what));
++        return NULL;
++    }
++}
++
+ #ifndef NETSNMP_FEATURE_REMOVE_CERT_FINGERPRINTS
+ int
+ netsnmp_cert_check_vb_fingerprint(const netsnmp_variable_list *var)
+@@ -2284,6 +2449,124 @@ _reduce_subset_dir(netsnmp_void_array *m
+     }
+ }
+ 
++/*
++ * reduce subset by eliminating any certificates that are not the
++ * first certficate in a file. This allows us to ignore certificate
++ * chains when testing for specific certificates, and to match keys
++ * to the first certificate only.
++ */
++static netsnmp_void_array *
++_cert_reduce_subset_first(netsnmp_void_array *matching)
++{
++    netsnmp_cert *cc;
++    int i = 0, j, newsize;
++
++    if ((NULL == matching))
++        return matching;
++
++    newsize = matching->size;
++
++    for( ; i < matching->size; ) {
++        /*
++         * if we've shifted matches down we'll hit a NULL entry before
++         * we hit the end of the array.
++         */
++        if (NULL == matching->array[i])
++            break;
++        /*
++         * skip over valid matches. The first entry has an offset of zero.
++         */
++        cc = (netsnmp_cert*)matching->array[i];
++        if (0 == cc->offset) {
++            ++i;
++            continue;
++        }
++        /*
++         * shrink array by shifting everything down a spot. Might not be
++         * the most efficient soloution, but this is just happening at
++         * startup and hopefully most certs won't have common prefixes.
++         */
++        --newsize;
++        for ( j=i; j < newsize; ++j )
++            matching->array[j] = matching->array[j+1];
++        matching->array[j] = NULL;
++        /** no ++i; just shifted down, need to look at same position again */
++    }
++    /*
++     * if we shifted, set the new size
++     */
++    if (newsize != matching->size) {
++        DEBUGMSGT(("9:cert:subset:first", "shrank from %" NETSNMP_PRIz "d to %d\n",
++                   matching->size, newsize));
++        matching->size = newsize;
++    }
++
++    if (0 == matching->size) {
++        free(matching->array);
++        SNMP_FREE(matching);
++    }
++
++    return matching;
++}
++
++/*
++ * reduce subset by eliminating any certificates that do not match
++ * purpose specified.
++ */
++static netsnmp_void_array *
++_cert_reduce_subset_what(netsnmp_void_array *matching, int what)
++{
++    netsnmp_cert_common *cc;
++    int i = 0, j, newsize;
++
++    if ((NULL == matching))
++        return matching;
++
++    newsize = matching->size;
++
++    for( ; i < matching->size; ) {
++        /*
++         * if we've shifted matches down we'll hit a NULL entry before
++         * we hit the end of the array.
++         */
++        if (NULL == matching->array[i])
++            break;
++        /*
++         * skip over valid matches. The first entry has an offset of zero.
++         */
++        cc = (netsnmp_cert_common *)matching->array[i];
++        if ((cc->allowed_uses & what)) {
++            ++i;
++            continue;
++        }
++        /*
++         * shrink array by shifting everything down a spot. Might not be
++         * the most efficient soloution, but this is just happening at
++         * startup and hopefully most certs won't have common prefixes.
++         */
++        --newsize;
++        for ( j=i; j < newsize; ++j )
++            matching->array[j] = matching->array[j+1];
++        matching->array[j] = NULL;
++        /** no ++i; just shifted down, need to look at same position again */
++    }
++    /*
++     * if we shifted, set the new size
++     */
++    if (newsize != matching->size) {
++        DEBUGMSGT(("9:cert:subset:what", "shrank from %" NETSNMP_PRIz "d to %d\n",
++                   matching->size, newsize));
++        matching->size = newsize;
++    }
++
++    if (0 == matching->size) {
++        free(matching->array);
++        SNMP_FREE(matching);
++    }
++
++    return matching;
++}
++
+ static netsnmp_void_array *
+ _cert_find_subset_common(const char *filename, netsnmp_container *container)
+ {
+diff -urNp a/snmplib/transports/snmpTLSBaseDomain.c b/snmplib/transports/snmpTLSBaseDomain.c
+--- a/snmplib/transports/snmpTLSBaseDomain.c	2021-06-09 10:55:22.791954900 +0200
++++ b/snmplib/transports/snmpTLSBaseDomain.c	2021-06-09 10:56:36.727272302 +0200
+@@ -59,7 +59,7 @@ int openssl_local_index;
+ /* this is called during negotiation */
+ int verify_callback(int ok, X509_STORE_CTX *ctx) {
+     int err, depth;
+-    char buf[1024], *fingerprint;
++    char subject[SNMP_MAXBUF_MEDIUM], issuer[SNMP_MAXBUF_MEDIUM], *fingerprint;
+     X509 *thecert;
+     netsnmp_cert *cert;
+     _netsnmp_verify_info *verify_info;
+@@ -71,10 +71,12 @@ int verify_callback(int ok, X509_STORE_C
+     
+     /* things to do: */
+ 
+-    X509_NAME_oneline(X509_get_subject_name(thecert), buf, sizeof(buf));
++    X509_NAME_oneline(X509_get_subject_name(thecert), subject, sizeof(subject));
++    X509_NAME_oneline(X509_get_issuer_name(thecert), issuer, sizeof(issuer));
+     fingerprint = netsnmp_openssl_cert_get_fingerprint(thecert, -1);
+-    DEBUGMSGTL(("tls_x509:verify", "Cert: %s\n", buf));
+-    DEBUGMSGTL(("tls_x509:verify", "  fp: %s\n", fingerprint ?
++    DEBUGMSGTL(("tls_x509:verify", " subject: %s\n", subject));
++    DEBUGMSGTL(("tls_x509:verify", "  issuer: %s\n", issuer));
++    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());
+@@ -109,7 +111,7 @@ int verify_callback(int ok, X509_STORE_C
+         } 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_log(LOG_ERR, "tls verification failure: ok=%d ctx=%p depth=%d fp=%s subject='%s' issuer='%s' err=%i:%s\n", ok, ctx, depth, fingerprint, subject, issuer, err, X509_verify_cert_error_string(err));
+             SNMP_FREE(fingerprint);
+             return 0;
+         }
+@@ -425,23 +427,50 @@ netsnmp_tlsbase_extract_security_name(SS
+ int
+ _trust_this_cert(SSL_CTX *the_ctx, char *certspec) {
+     netsnmp_cert *trustcert;
++    netsnmp_cert *candidate;
++    netsnmp_void_array *matching = NULL;
++
++    int                 i;
+ 
+     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,
++                                  NS_CERTKEY_FINGERPRINT,
+                                   certspec);
++
++    /* loop through all CA certs in the given files */
++    if (!trustcert) {
++        matching = netsnmp_certs_find(NS_CERT_CA,
++                                      NS_CERTKEY_FILE,
++                                      certspec);
++        for (i = 0; (matching) && (i < matching->size); ++i) {
++            candidate = (netsnmp_cert*)matching->array[i];
++            if (netsnmp_cert_trust(the_ctx, candidate) != SNMPERR_SUCCESS) {
++                free(matching->array);
++                free(matching);
++                LOGANDDIE("failed to load trust certificate");
++            }
++        } /** matching loop */
++
++        if (matching) {
++            free(matching->array);
++            free(matching);
++            return 1;
++	}
++    }
++
++    /* fall back to trusting the remote peer certificate */
+     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)
++    if (netsnmp_cert_trust(the_ctx, trustcert) != SNMPERR_SUCCESS)
+         LOGANDDIE("failed to load trust certificate");
+ 
+     return 1;
+@@ -481,7 +510,7 @@ _sslctx_common_setup(SSL_CTX *the_ctx, _
+                                     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));
++        DEBUGMSGTL(("sslctx_common", "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())))
+@@ -546,13 +575,19 @@ sslctx_client_setup(const SSL_METHOD *me
+                 id_cert->key->info.filename));
+ 
+     if (SSL_CTX_use_certificate(the_ctx, id_cert->ocert) <= 0)
+-        LOGANDDIE("failed to set the certificate to use");
++        LOGANDDIE("failed to set the client certificate to use");
+ 
+     if (SSL_CTX_use_PrivateKey(the_ctx, id_cert->key->okey) <= 0)
+-        LOGANDDIE("failed to set the private key to use");
++        LOGANDDIE("failed to set the client private key to use");
+ 
+     if (!SSL_CTX_check_private_key(the_ctx))
+-        LOGANDDIE("public and private keys incompatible");
++        LOGANDDIE("client public and private keys incompatible");
++
++    while (id_cert->issuer_cert) {
++        id_cert = id_cert->issuer_cert;
++        if (!SSL_CTX_add_extra_chain_cert(the_ctx, id_cert->ocert))
++            LOGANDDIE("failed to add intermediate client certificate");
++    }
+ 
+     if (tlsbase->their_identity)
+         peer_cert = netsnmp_cert_find(NS_CERT_REMOTE_PEER,
+@@ -566,11 +601,11 @@ sslctx_client_setup(const SSL_METHOD *me
+                     peer_cert ? peer_cert->info.filename : "none"));
+ 
+         /* Trust the expected certificate */
+-        if (netsnmp_cert_trust_ca(the_ctx, peer_cert) != SNMPERR_SUCCESS)
++        if (netsnmp_cert_trust(the_ctx, peer_cert) != SNMPERR_SUCCESS)
+             LOGANDDIE ("failed to set verify paths");
+     }
+ 
+-    /* trust a certificate (possibly a CA) aspecifically passed in */
++    /* trust a certificate (possibly a CA) specifically passed in */
+     if (tlsbase->trust_cert) {
+         if (!_trust_this_cert(the_ctx, tlsbase->trust_cert))
+             return 0;
+@@ -589,7 +624,7 @@ sslctx_server_setup(const SSL_METHOD *me
+     /* 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");
++        LOGANDDIE("can't create a new server context");
+     }
+ 
+     id_cert = netsnmp_cert_find(NS_CERT_IDENTITY, NS_CERTKEY_DEFAULT, NULL);
+@@ -597,7 +632,7 @@ sslctx_server_setup(const SSL_METHOD *me
+         LOGANDDIE ("error finding server identity keys");
+ 
+     if (!id_cert->key || !id_cert->key->okey)
+-        LOGANDDIE("failed to load private key");
++        LOGANDDIE("failed to load server private key");
+ 
+     DEBUGMSGTL(("sslctx_server", "using public key: %s\n",
+                 id_cert->info.filename));
+@@ -605,13 +640,19 @@ sslctx_server_setup(const SSL_METHOD *me
+                 id_cert->key->info.filename));
+ 
+     if (SSL_CTX_use_certificate(the_ctx, id_cert->ocert) <= 0)
+-        LOGANDDIE("failed to set the certificate to use");
++        LOGANDDIE("failed to set the server certificate to use");
+ 
+     if (SSL_CTX_use_PrivateKey(the_ctx, id_cert->key->okey) <= 0)
+-        LOGANDDIE("failed to set the private key to use");
++        LOGANDDIE("failed to set the server private key to use");
+ 
+     if (!SSL_CTX_check_private_key(the_ctx))
+-        LOGANDDIE("public and private keys incompatible");
++        LOGANDDIE("server public and private keys incompatible");
++
++    while (id_cert->issuer_cert) {
++        id_cert = id_cert->issuer_cert;
++        if (!SSL_CTX_add_extra_chain_cert(the_ctx, id_cert->ocert))
++            LOGANDDIE("failed to add intermediate server certificate");
++    }
+ 
+     SSL_CTX_set_read_ahead(the_ctx, 1); /* XXX: DTLS only? */
+ 
diff --git a/SPECS/net-snmp.spec b/SPECS/net-snmp.spec
index 7fa1420..6d8cbda 100644
--- a/SPECS/net-snmp.spec
+++ b/SPECS/net-snmp.spec
@@ -10,7 +10,7 @@
 Summary:    A collection of SNMP protocol tools and libraries
 Name:       net-snmp
 Version:    5.8
-Release:    20%{?dist}
+Release:    22%{?dist}
 Epoch:      1
 
 License:    BSD
@@ -62,6 +62,12 @@ Patch33:    net-snmp-5.8-clientaddr-error-message.patch
 Patch34:    net-snmp-5.8-ipv6-disabled.patch
 Patch35:    net-snmp-5.8-empty-passphrase.patch
 Patch36:    net-snmp-5.8-asn-parse-nlength.patch
+Patch37:    net-snmp-5.8-double-IP-parsing.patch
+Patch38:    net-snmp-5.8-digest-from-ECC.patch
+Patch39:    net-snmp-5.8-broken-errmsg.patch
+Patch40:    net-snmp-5.8-intermediate-certs.patch
+Patch41:    net-snmp-5.8-fix-cert-crash.patch
+Patch42:    net-snmp-5.8-engine-id.patch
 
 # Modern RPM API means at least EL6
 Patch101:   net-snmp-5.8-modern-rpm-api.patch
@@ -223,6 +229,12 @@ rm -r python
 %patch34 -p1 -b .ipv6-disabled
 %patch35 -p1 -b .empty-passphrase
 %patch36 -p1 -b .asn-parse-nlength
+%patch37 -p1 -b .double-IP-parsing
+%patch38 -p1 -b .digest-from-ECC
+%patch39 -p1 -b .broken-errmsg
+%patch40 -p1 -b .intermediate-certs
+%patch41 -p1 -b .fix-cert-crash
+%patch42 -p1 -b .engine-id
 
 %patch101 -p1 -b .modern-rpm-api
 
@@ -477,6 +489,16 @@ LD_LIBRARY_PATH=%{buildroot}/%{_libdir} make test
 %{_libdir}/libnetsnmptrapd*.so.%{soname}*
 
 %changelog
+* Mon Jun 28 2021 Josef Ridky <jridky@redhat.com> - 1:5.8-22
+- update engineTime when sending traps (#1973252)
+
+* Wed Jun 09 2021 Josef Ridky <jridky@redhat.com> - 1:5.8-21
+- prevent parsing IP address twice (#1768908)
+- add support for digests detected from ECC certs (#1919714)
+- fix broken ErrorMsg at ucd-snmp (#1933150)
+- add support for intermediate certs (#1914656)
+- fix crash of certs with longer extension (#1908718)
+
 * Tue Jan 05 2021 Josef Ridky <jridky@redhat.com> - 1:5.8-20
 - fix issue with parsing of long traps (#1912242)
 - modify fix for #1877375