From f2f5513e6700ef1f20892587c4341ea8739cad70 Mon Sep 17 00:00:00 2001 From: DistroBaker Date: Thu, 28 Jan 2021 14:40:02 +0000 Subject: [PATCH] Merged update from upstream sources This is an automated DistroBaker update from upstream sources. If you do not know what this is about or would like to opt out, contact the OSCI team. Source: https://src.fedoraproject.org/rpms/net-snmp.git#0dba34c092d286d8cfec9e5e6e81cfb21ee45c7e --- net-snmp-5.9-ECC-cert.patch | 98 +++ net-snmp-5.9-intermediate-certs.patch | 1079 +++++++++++++++++++++++++ net-snmp-5.9-ssl-buffer-size.patch | 67 ++ net-snmp.spec | 16 +- 4 files changed, 1259 insertions(+), 1 deletion(-) create mode 100644 net-snmp-5.9-ECC-cert.patch create mode 100644 net-snmp-5.9-intermediate-certs.patch create mode 100644 net-snmp-5.9-ssl-buffer-size.patch diff --git a/net-snmp-5.9-ECC-cert.patch b/net-snmp-5.9-ECC-cert.patch new file mode 100644 index 0000000..5d43d4d --- /dev/null +++ b/net-snmp-5.9-ECC-cert.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 c092a007a..432cb5c27 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/net-snmp-5.9-intermediate-certs.patch b/net-snmp-5.9-intermediate-certs.patch new file mode 100644 index 0000000..3a4ebcd --- /dev/null +++ b/net-snmp-5.9-intermediate-certs.patch @@ -0,0 +1,1079 @@ +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-01-28 12:55:48.969560884 +0100 ++++ b/include/net-snmp/library/cert_util.h 2021-01-28 13:10:25.616592870 +0100 +@@ -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-01-28 12:55:48.969560884 +0100 ++++ b/include/net-snmp/library/dir_utils.h 2021-01-28 13:10:25.616592870 +0100 +@@ -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-01-28 12:55:48.909560222 +0100 ++++ b/snmplib/cert_util.c 2021-01-28 13:14:32.104988765 +0100 +@@ -100,7 +100,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; +@@ -126,6 +126,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); +@@ -345,6 +347,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); +@@ -354,6 +358,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; + } + +@@ -362,6 +368,8 @@ _setup_containers(void) + { + netsnmp_container *additional_keys; + ++ int rc; ++ + _certs = _get_cert_container("netsnmp certificates"); + if (NULL == _certs) + return; +@@ -376,6 +384,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 */ +@@ -389,6 +398,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 */ +@@ -402,6 +412,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"); +@@ -424,9 +435,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; + +@@ -446,8 +457,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); +@@ -884,14 +897,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; +@@ -908,51 +993,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", +@@ -979,7 +1046,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))) +@@ -999,46 +1066,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; + } +@@ -1048,7 +1076,6 @@ netsnmp_okey_get(netsnmp_key *key) + { + BIO *keybio; + EVP_PKEY *okey; +- char file[SNMP_MAXPATH]; + + if (NULL == key) + return NULL; +@@ -1056,19 +1083,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; + } + +@@ -1154,7 +1170,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; +@@ -1163,7 +1179,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; +@@ -1184,7 +1200,7 @@ _find_partner(netsnmp_cert *cert, netsnm + return; + } + +- if(key) { ++ if (key) { + if (key->cert) { + DEBUGMSGT(("cert:partner", "key already has partner\n")); + return; +@@ -1197,7 +1213,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) { +@@ -1217,7 +1234,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; +@@ -1255,76 +1272,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; +@@ -1338,8 +1468,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; +@@ -1381,7 +1513,8 @@ _cert_read_index(const char *dirname, st + netsnmp_directory_container_read_some(NULL, dirname, + _time_filter, &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); +@@ -1425,6 +1558,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)))) || +@@ -1437,9 +1572,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 = _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 { +@@ -1543,7 +1680,8 @@ _add_certdir(const char *dirname) + netsnmp_directory_container_read_some(NULL, dirname, + _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")); +@@ -1631,7 +1769,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))); +@@ -1835,7 +1973,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) +@@ -1881,6 +2020,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) +@@ -2278,6 +2443,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/dir_utils.c b/snmplib/dir_utils.c +--- a/snmplib/dir_utils.c 2021-01-28 12:55:48.911560244 +0100 ++++ b/snmplib/dir_utils.c 2021-01-28 13:10:25.618592889 +0100 +@@ -107,6 +107,9 @@ netsnmp_directory_container_read_some(ne + /** default to unsorted */ + if (! (flags & NETSNMP_DIR_SORTED)) + CONTAINER_SET_OPTIONS(container, CONTAINER_KEY_UNSORTED, rc); ++ /** default to duplicates not allowed */ ++ if (! (flags & NETSNMP_DIR_ALLOW_DUPLICATES)) ++ CONTAINER_SET_OPTIONS(container, CONTAINER_KEY_ALLOW_DUPLICATES, rc); + } + + dir = opendir(dirname); +diff -urNp a/snmplib/transports/snmpTLSBaseDomain.c b/snmplib/transports/snmpTLSBaseDomain.c +--- a/snmplib/transports/snmpTLSBaseDomain.c 2021-01-28 12:55:48.916560299 +0100 ++++ b/snmplib/transports/snmpTLSBaseDomain.c 2021-01-28 13:00:41.437047788 +0100 +@@ -68,7 +68,7 @@ static unsigned long ERR_get_error_all(c + /* 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; +@@ -80,10 +80,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()); +@@ -118,7 +120,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; + } +@@ -434,23 +436,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; +@@ -490,7 +519,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()))) +@@ -556,13 +585,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, +@@ -576,11 +611,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; +@@ -599,7 +634,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"); + } + MAKE_MEM_DEFINED(the_ctx, 256/*sizeof(*the_ctx)*/); + +@@ -608,7 +643,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)); +@@ -616,13 +651,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/net-snmp-5.9-ssl-buffer-size.patch b/net-snmp-5.9-ssl-buffer-size.patch new file mode 100644 index 0000000..5cb1ba6 --- /dev/null +++ b/net-snmp-5.9-ssl-buffer-size.patch @@ -0,0 +1,67 @@ +diff -urNp a/snmplib/snmp_openssl.c b/snmplib/snmp_openssl.c +--- a/snmplib/snmp_openssl.c 2021-01-28 14:10:05.993443671 +0100 ++++ b/snmplib/snmp_openssl.c 2021-01-28 14:17:52.531088559 +0100 +@@ -284,31 +284,29 @@ _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 extension\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 +477,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 +497,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/net-snmp.spec b/net-snmp.spec index a88eca6..b32e7fe 100644 --- a/net-snmp.spec +++ b/net-snmp.spec @@ -10,7 +10,7 @@ Summary: A collection of SNMP protocol tools and libraries Name: net-snmp Version: 5.9 -Release: 4%{?dist} +Release: 6%{?dist} Epoch: 1 License: BSD @@ -53,6 +53,9 @@ Patch23: net-snmp-5.9-available-memory.patch Patch24: net-snmp-5.8-asn-parse-nlength.patch Patch25: net-snmp-5.8-clientaddr-error-message.patch Patch26: net-snmp-5.8-empty-passphrase.patch +Patch27: net-snmp-5.9-ECC-cert.patch +Patch28: net-snmp-5.9-intermediate-certs.patch +Patch29: net-snmp-5.9-ssl-buffer-size.patch # Modern RPM API means at least EL6 Patch101: net-snmp-5.8-modern-rpm-api.patch @@ -230,6 +233,9 @@ cp %{SOURCE10} . %patch24 -p1 -b .asn-parse-nlength %patch25 -p1 -b .clientaddr-error-message %patch26 -p1 -b .empty-passphrase +%patch27 -p1 -b .ECC-cert +%patch28 -p1 -b .intermediate-certs +%patch29 -p1 -b .ssl-buffer-size %patch101 -p1 -b .modern-rpm-api %patch102 -p1 @@ -497,6 +503,14 @@ LD_LIBRARY_PATH=%{buildroot}/%{_libdir} make test %{_libdir}/libnetsnmptrapd*.so.%{soname}* %changelog +* Thu Jan 28 2021 Josef Ridky - 1:5.9-6 +- add support for digests detected from ECC certificates +- add support for intermediate certificates +- fix crash caused by small buffer size + +* Tue Jan 26 2021 Fedora Release Engineering - 1:5.9-5 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_34_Mass_Rebuild + * Mon Jan 18 2021 Josef Ridky - 1:5.9-4 - fix issue with parsing long trap headers (#1912725) - fix error message when the address specified by clientaddr option