net-snmp/net-snmp-5.9-intermediate-certs.patch
DistroBaker f2f5513e67 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
2021-01-28 14:40:02 +00:00

1080 lines
40 KiB
Diff

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? */