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 +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?= +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 +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 - 1:5.8-22 +- update engineTime when sending traps (#1973252) + +* Wed Jun 09 2021 Josef Ridky - 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 - 1:5.8-20 - fix issue with parsing of long traps (#1912242) - modify fix for #1877375