Blame SOURCES/net-snmp-5.8-intermediate-certs.patch

6af263
diff -urNp a/include/net-snmp/library/cert_util.h b/include/net-snmp/library/cert_util.h
6af263
--- a/include/net-snmp/library/cert_util.h	2021-06-09 10:55:22.767954797 +0200
6af263
+++ b/include/net-snmp/library/cert_util.h	2021-06-09 10:56:36.725272293 +0200
6af263
@@ -55,7 +55,8 @@ extern "C" {
6af263
         char           *common_name;
6af263
 
6af263
         u_char          hash_type;
6af263
-        u_char          _pad[3]; /* for future use */
6af263
+        u_char          _pad[1]; /* for future use */
6af263
+        u_short         offset;
6af263
     } netsnmp_cert;
6af263
 
6af263
 /** types */
6af263
@@ -100,6 +101,7 @@ extern "C" {
6af263
 
6af263
     NETSNMP_IMPORT
6af263
     netsnmp_cert *netsnmp_cert_find(int what, int where, void *hint);
6af263
+    netsnmp_void_array *netsnmp_certs_find(int what, int where, void *hint);
6af263
 
6af263
     int netsnmp_cert_check_vb_fingerprint(const netsnmp_variable_list *var);
6af263
 
6af263
diff -urNp a/include/net-snmp/library/dir_utils.h b/include/net-snmp/library/dir_utils.h
6af263
--- a/include/net-snmp/library/dir_utils.h	2021-06-09 10:55:22.767954797 +0200
6af263
+++ b/include/net-snmp/library/dir_utils.h	2021-06-09 10:56:36.726272298 +0200
6af263
@@ -53,6 +53,8 @@ extern "C" {
6af263
 #define NETSNMP_DIR_NSFILE                            0x0010
6af263
 /** load stats in netsnmp_file */
6af263
 #define NETSNMP_DIR_NSFILE_STATS                      0x0020
6af263
+/** allow files to be indexed more than once */
6af263
+#define NETSNMP_DIR_ALLOW_DUPLICATES                  0x0040
6af263
 
6af263
     
6af263
         
6af263
diff -urNp a/snmplib/cert_util.c b/snmplib/cert_util.c
6af263
--- a/snmplib/cert_util.c	2021-06-09 10:55:22.785954874 +0200
6af263
+++ b/snmplib/cert_util.c	2021-06-09 11:02:43.890848394 +0200
6af263
@@ -104,7 +104,7 @@ netsnmp_feature_child_of(tls_fingerprint
6af263
  * bump this value whenever cert index format changes, so indexes
6af263
  * will be regenerated with new format.
6af263
  */
6af263
-#define CERT_INDEX_FORMAT  1
6af263
+#define CERT_INDEX_FORMAT  2
6af263
 
6af263
 static netsnmp_container *_certs = NULL;
6af263
 static netsnmp_container *_keys = NULL;
6af263
@@ -130,6 +130,8 @@ static int  _cert_fn_ncompare(netsnmp_ce
6af263
                               netsnmp_cert_common *rhs);
6af263
 static void _find_partner(netsnmp_cert *cert, netsnmp_key *key);
6af263
 static netsnmp_cert *_find_issuer(netsnmp_cert *cert);
6af263
+static netsnmp_void_array *_cert_reduce_subset_first(netsnmp_void_array *matching);
6af263
+static netsnmp_void_array *_cert_reduce_subset_what(netsnmp_void_array *matching, int what);
6af263
 static netsnmp_void_array *_cert_find_subset_fn(const char *filename,
6af263
                                                 const char *directory);
6af263
 static netsnmp_void_array *_cert_find_subset_sn(const char *subject);
6af263
@@ -349,6 +351,8 @@ _get_cert_container(const char *use)
6af263
 {
6af263
     netsnmp_container *c;
6af263
 
6af263
+    int rc;
6af263
+
6af263
     c = netsnmp_container_find("certs:binary_array");
6af263
     if (NULL == c) {
6af263
         snmp_log(LOG_ERR, "could not create container for %s\n", use);
6af263
@@ -358,6 +362,8 @@ _get_cert_container(const char *use)
6af263
     c->free_item = (netsnmp_container_obj_func*)_cert_free;
6af263
     c->compare = (netsnmp_container_compare*)_cert_compare;
6af263
 
6af263
+    CONTAINER_SET_OPTIONS(c, CONTAINER_KEY_ALLOW_DUPLICATES, rc);
6af263
+
6af263
     return c;
6af263
 }
6af263
 
6af263
@@ -366,6 +372,8 @@ _setup_containers(void)
6af263
 {
6af263
     netsnmp_container *additional_keys;
6af263
 
6af263
+    int rc;
6af263
+
6af263
     _certs = _get_cert_container("netsnmp certificates");
6af263
     if (NULL == _certs)
6af263
         return;
6af263
@@ -380,6 +388,7 @@ _setup_containers(void)
6af263
     additional_keys->container_name = strdup("certs_cn");
6af263
     additional_keys->free_item = NULL;
6af263
     additional_keys->compare = (netsnmp_container_compare*)_cert_cn_compare;
6af263
+    CONTAINER_SET_OPTIONS(additional_keys, CONTAINER_KEY_ALLOW_DUPLICATES, rc);
6af263
     netsnmp_container_add_index(_certs, additional_keys);
6af263
 
6af263
     /** additional keys: subject name */
6af263
@@ -393,6 +402,7 @@ _setup_containers(void)
6af263
     additional_keys->free_item = NULL;
6af263
     additional_keys->compare = (netsnmp_container_compare*)_cert_sn_compare;
6af263
     additional_keys->ncompare = (netsnmp_container_compare*)_cert_sn_ncompare;
6af263
+    CONTAINER_SET_OPTIONS(additional_keys, CONTAINER_KEY_ALLOW_DUPLICATES, rc);
6af263
     netsnmp_container_add_index(_certs, additional_keys);
6af263
 
6af263
     /** additional keys: file name */
6af263
@@ -406,6 +416,7 @@ _setup_containers(void)
6af263
     additional_keys->free_item = NULL;
6af263
     additional_keys->compare = (netsnmp_container_compare*)_cert_fn_compare;
6af263
     additional_keys->ncompare = (netsnmp_container_compare*)_cert_fn_ncompare;
6af263
+    CONTAINER_SET_OPTIONS(additional_keys, CONTAINER_KEY_ALLOW_DUPLICATES, rc);
6af263
     netsnmp_container_add_index(_certs, additional_keys);
6af263
 
6af263
     _keys = netsnmp_container_find("cert_keys:binary_array");
6af263
@@ -428,9 +439,9 @@ netsnmp_cert_map_container(void)
6af263
 }
6af263
 
6af263
 static netsnmp_cert *
6af263
-_new_cert(const char *dirname, const char *filename, int certType,
6af263
-          int hashType, const char *fingerprint, const char *common_name,
6af263
-          const char *subject)
6af263
+_new_cert(const char *dirname, const char *filename, int certType, int offset,
6af263
+          int allowed_uses, int hashType, const char *fingerprint,
6af263
+          const char *common_name,  const char *subject)
6af263
 {
6af263
     netsnmp_cert    *cert;
6af263
 
6af263
@@ -450,8 +461,10 @@ _new_cert(const char *dirname, const cha
6af263
 
6af263
     cert->info.dir = strdup(dirname);
6af263
     cert->info.filename = strdup(filename);
6af263
-    cert->info.allowed_uses = NS_CERT_REMOTE_PEER;
6af263
+    /* only the first certificate is allowed to be a remote peer */
6af263
+    cert->info.allowed_uses = allowed_uses;
6af263
     cert->info.type = certType;
6af263
+    cert->offset = offset;
6af263
     if (fingerprint) {
6af263
         cert->hash_type = hashType;
6af263
         cert->fingerprint = strdup(fingerprint);
6af263
@@ -888,14 +901,86 @@ _certindex_new( const char *dirname )
6af263
  * certificate utility functions
6af263
  *
6af263
  */
6af263
+static BIO *
6af263
+netsnmp_open_bio(const char *dir, const char *filename)
6af263
+{
6af263
+    BIO            *certbio;
6af263
+    char            file[SNMP_MAXPATH];
6af263
+
6af263
+    DEBUGMSGT(("9:cert:read", "Checking file %s\n", filename));
6af263
+
6af263
+    certbio = BIO_new(BIO_s_file());
6af263
+    if (NULL == certbio) {
6af263
+        snmp_log(LOG_ERR, "error creating BIO\n");
6af263
+        return NULL;
6af263
+    }
6af263
+
6af263
+    snprintf(file, sizeof(file),"%s/%s", dir, filename);
6af263
+    if (BIO_read_filename(certbio, file) <=0) {
6af263
+        snmp_log(LOG_ERR, "error reading certificate/key %s into BIO\n", file);
6af263
+        BIO_vfree(certbio);
6af263
+        return NULL;
6af263
+    }
6af263
+
6af263
+    return certbio;
6af263
+}
6af263
+
6af263
+static void
6af263
+netsnmp_ocert_parse(netsnmp_cert *cert, X509 *ocert)
6af263
+{
6af263
+    int             is_ca;
6af263
+
6af263
+    cert->ocert = ocert;
6af263
+
6af263
+    /*
6af263
+     * X509_check_ca return codes:
6af263
+     * 0 not a CA
6af263
+     * 1 is a CA
6af263
+     * 2 basicConstraints absent so "maybe" a CA
6af263
+     * 3 basicConstraints absent but self signed V1.
6af263
+     * 4 basicConstraints absent but keyUsage present and keyCertSign asserted.
6af263
+     * 5 outdated Netscape Certificate Type CA extension.
6af263
+     */
6af263
+    is_ca = X509_check_ca(ocert);
6af263
+    if (1 == is_ca)
6af263
+        cert->info.allowed_uses |= NS_CERT_CA;
6af263
+
6af263
+    if (NULL == cert->subject) {
6af263
+        cert->subject = X509_NAME_oneline(X509_get_subject_name(ocert), NULL,
6af263
+                                          0);
6af263
+        DEBUGMSGT(("9:cert:add:subject", "subject name: %s\n", cert->subject));
6af263
+    }
6af263
+
6af263
+    if (NULL == cert->issuer) {
6af263
+        cert->issuer = X509_NAME_oneline(X509_get_issuer_name(ocert), NULL, 0);
6af263
+        if (strcmp(cert->subject, cert->issuer) == 0) {
6af263
+            free(cert->issuer);
6af263
+            cert->issuer = strdup("self-signed");
6af263
+        }
6af263
+        DEBUGMSGT(("9:cert:add:issuer", "CA issuer: %s\n", cert->issuer));
6af263
+    }
6af263
+
6af263
+    if (NULL == cert->fingerprint) {
6af263
+        cert->hash_type = netsnmp_openssl_cert_get_hash_type(ocert);
6af263
+        cert->fingerprint =
6af263
+            netsnmp_openssl_cert_get_fingerprint(ocert, cert->hash_type);
6af263
+    }
6af263
+
6af263
+    if (NULL == cert->common_name) {
6af263
+        cert->common_name =netsnmp_openssl_cert_get_commonName(ocert, NULL,
6af263
+                                                               NULL);
6af263
+        DEBUGMSGT(("9:cert:add:name","%s\n", cert->common_name));
6af263
+    }
6af263
+
6af263
+}
6af263
+
6af263
 static X509 *
6af263
 netsnmp_ocert_get(netsnmp_cert *cert)
6af263
 {
6af263
     BIO            *certbio;
6af263
     X509           *ocert = NULL;
6af263
+    X509           *ncert = NULL;
6af263
     EVP_PKEY       *okey = NULL;
6af263
-    char            file[SNMP_MAXPATH];
6af263
-    int             is_ca;
6af263
 
6af263
     if (NULL == cert)
6af263
         return NULL;
6af263
@@ -912,51 +997,33 @@ netsnmp_ocert_get(netsnmp_cert *cert)
6af263
         }
6af263
     }
6af263
 
6af263
-    DEBUGMSGT(("9:cert:read", "Checking file %s\n", cert->info.filename));
6af263
-
6af263
-    certbio = BIO_new(BIO_s_file());
6af263
-    if (NULL == certbio) {
6af263
-        snmp_log(LOG_ERR, "error creating BIO\n");
6af263
-        return NULL;
6af263
-    }
6af263
-
6af263
-    snprintf(file, sizeof(file),"%s/%s", cert->info.dir, cert->info.filename);
6af263
-    if (BIO_read_filename(certbio, file) <=0) {
6af263
-        snmp_log(LOG_ERR, "error reading certificate %s into BIO\n", file);
6af263
-        BIO_vfree(certbio);
6af263
+    certbio = netsnmp_open_bio(cert->info.dir, cert->info.filename);
6af263
+    if (!certbio) {
6af263
         return NULL;
6af263
     }
6af263
 
6af263
-    if (NS_CERT_TYPE_UNKNOWN == cert->info.type) {
6af263
-        char *pos = strrchr(cert->info.filename, '.');
6af263
-        if (NULL == pos)
6af263
-            return NULL;
6af263
-        cert->info.type = _cert_ext_type(++pos);
6af263
-        netsnmp_assert(cert->info.type != NS_CERT_TYPE_UNKNOWN);
6af263
-    }
6af263
-
6af263
     switch (cert->info.type) {
6af263
 
6af263
         case NS_CERT_TYPE_DER:
6af263
+            (void)BIO_seek(certbio, cert->offset);
6af263
             ocert = d2i_X509_bio(certbio,NULL); /* DER/ASN1 */
6af263
             if (NULL != ocert)
6af263
                 break;
6af263
-            (void)BIO_reset(certbio);
6af263
             /* Check for PEM if DER didn't work */
6af263
             /* FALLTHROUGH */
6af263
 
6af263
         case NS_CERT_TYPE_PEM:
6af263
-            ocert = PEM_read_bio_X509_AUX(certbio, NULL, NULL, NULL);
6af263
+            (void)BIO_seek(certbio, cert->offset);
6af263
+            ocert = ncert = PEM_read_bio_X509_AUX(certbio, NULL, NULL, NULL);
6af263
             if (NULL == ocert)
6af263
                 break;
6af263
             if (NS_CERT_TYPE_DER == cert->info.type) {
6af263
                 DEBUGMSGT(("9:cert:read", "Changing type from DER to PEM\n"));
6af263
                 cert->info.type = NS_CERT_TYPE_PEM;
6af263
             }
6af263
-            /** check for private key too */
6af263
-            if (NULL == cert->key) {
6af263
-                (void)BIO_reset(certbio);
6af263
-                okey =  PEM_read_bio_PrivateKey(certbio, NULL, NULL, NULL);
6af263
+            /** check for private key too, but only if we're the first certificate */
6af263
+            if (0 == cert->offset && NULL == cert->key) {
6af263
+                okey = PEM_read_bio_PrivateKey(certbio, NULL, NULL, NULL);
6af263
                 if (NULL != okey) {
6af263
                     netsnmp_key  *key;
6af263
                     DEBUGMSGT(("cert:read:key", "found key with cert in %s\n",
6af263
@@ -983,7 +1050,7 @@ netsnmp_ocert_get(netsnmp_cert *cert)
6af263
             break;
6af263
 #ifdef CERT_PKCS12_SUPPORT_MAYBE_LATER
6af263
         case NS_CERT_TYPE_PKCS12:
6af263
-            (void)BIO_reset(certbio);
6af263
+            (void)BIO_seek(certbio, cert->offset);
6af263
             PKCS12 *p12 = d2i_PKCS12_bio(certbio, NULL);
6af263
             if ( (NULL != p12) && (PKCS12_verify_mac(p12, "", 0) ||
6af263
                                    PKCS12_verify_mac(p12, NULL, 0)))
6af263
@@ -1003,46 +1070,7 @@ netsnmp_ocert_get(netsnmp_cert *cert)
6af263
         return NULL;
6af263
     }
6af263
 
6af263
-    cert->ocert = ocert;
6af263
-    /*
6af263
-     * X509_check_ca return codes:
6af263
-     * 0 not a CA
6af263
-     * 1 is a CA
6af263
-     * 2 basicConstraints absent so "maybe" a CA
6af263
-     * 3 basicConstraints absent but self signed V1.
6af263
-     * 4 basicConstraints absent but keyUsage present and keyCertSign asserted.
6af263
-     * 5 outdated Netscape Certificate Type CA extension.
6af263
-     */
6af263
-    is_ca = X509_check_ca(ocert);
6af263
-    if (1 == is_ca)
6af263
-        cert->info.allowed_uses |= NS_CERT_CA;
6af263
-
6af263
-    if (NULL == cert->subject) {
6af263
-        cert->subject = X509_NAME_oneline(X509_get_subject_name(ocert), NULL,
6af263
-                                          0);
6af263
-        DEBUGMSGT(("9:cert:add:subject", "subject name: %s\n", cert->subject));
6af263
-    }
6af263
-
6af263
-    if (NULL == cert->issuer) {
6af263
-        cert->issuer = X509_NAME_oneline(X509_get_issuer_name(ocert), NULL, 0);
6af263
-        if (strcmp(cert->subject, cert->issuer) == 0) {
6af263
-            free(cert->issuer);
6af263
-            cert->issuer = strdup("self-signed");
6af263
-        }
6af263
-        DEBUGMSGT(("9:cert:add:issuer", "CA issuer: %s\n", cert->issuer));
6af263
-    }
6af263
-    
6af263
-    if (NULL == cert->fingerprint) {
6af263
-        cert->hash_type = netsnmp_openssl_cert_get_hash_type(ocert);
6af263
-        cert->fingerprint =
6af263
-            netsnmp_openssl_cert_get_fingerprint(ocert, cert->hash_type);
6af263
-    }
6af263
-    
6af263
-    if (NULL == cert->common_name) {
6af263
-        cert->common_name =netsnmp_openssl_cert_get_commonName(ocert, NULL,
6af263
-                                                               NULL);
6af263
-        DEBUGMSGT(("9:cert:add:name","%s\n", cert->common_name));
6af263
-    }
6af263
+    netsnmp_ocert_parse(cert, ocert);
6af263
 
6af263
     return ocert;
6af263
 }
6af263
@@ -1052,7 +1080,6 @@ netsnmp_okey_get(netsnmp_key  *key)
6af263
 {
6af263
     BIO            *keybio;
6af263
     EVP_PKEY       *okey;
6af263
-    char            file[SNMP_MAXPATH];
6af263
 
6af263
     if (NULL == key)
6af263
         return NULL;
6af263
@@ -1060,19 +1087,8 @@ netsnmp_okey_get(netsnmp_key  *key)
6af263
     if (key->okey)
6af263
         return key->okey;
6af263
 
6af263
-    snprintf(file, sizeof(file),"%s/%s", key->info.dir, key->info.filename);
6af263
-    DEBUGMSGT(("cert:key:read", "Checking file %s\n", key->info.filename));
6af263
-
6af263
-    keybio = BIO_new(BIO_s_file());
6af263
-    if (NULL == keybio) {
6af263
-        snmp_log(LOG_ERR, "error creating BIO\n");
6af263
-        return NULL;
6af263
-    }
6af263
-
6af263
-    if (BIO_read_filename(keybio, file) <=0) {
6af263
-        snmp_log(LOG_ERR, "error reading certificate %s into BIO\n",
6af263
-                 key->info.filename);
6af263
-        BIO_vfree(keybio);
6af263
+    keybio = netsnmp_open_bio(key->info.dir, key->info.filename);
6af263
+    if (!keybio) {
6af263
         return NULL;
6af263
     }
6af263
 
6af263
@@ -1158,7 +1174,7 @@ netsnmp_cert_load_x509(netsnmp_cert *cer
6af263
             cert->issuer_cert =  _find_issuer(cert);
6af263
             if (NULL == cert->issuer_cert) {
6af263
                 DEBUGMSGT(("cert:load:warn",
6af263
-                           "couldn't load CA chain for cert %s\n",
6af263
+                           "couldn't load full CA chain for cert %s\n",
6af263
                            cert->info.filename));
6af263
                 rc = CERT_LOAD_PARTIAL;
6af263
                 break;
6af263
@@ -1167,7 +1183,7 @@ netsnmp_cert_load_x509(netsnmp_cert *cer
6af263
         /** get issuer ocert */
6af263
         if ((NULL == cert->issuer_cert->ocert) &&
6af263
             (netsnmp_ocert_get(cert->issuer_cert) == NULL)) {
6af263
-            DEBUGMSGT(("cert:load:warn", "couldn't load cert chain for %s\n",
6af263
+            DEBUGMSGT(("cert:load:warn", "couldn't load full cert chain for %s\n",
6af263
                        cert->info.filename));
6af263
             rc = CERT_LOAD_PARTIAL;
6af263
             break;
6af263
@@ -1188,7 +1204,7 @@ _find_partner(netsnmp_cert *cert, netsnm
6af263
         return;
6af263
     }
6af263
 
6af263
-    if(key) {
6af263
+    if (key) {
6af263
         if (key->cert) {
6af263
             DEBUGMSGT(("cert:partner", "key already has partner\n"));
6af263
             return;
6af263
@@ -1201,7 +1217,8 @@ _find_partner(netsnmp_cert *cert, netsnm
6af263
             return;
6af263
         *pos = 0;
6af263
 
6af263
-        matching = _cert_find_subset_fn( filename, key->info.dir );
6af263
+        matching = _cert_reduce_subset_first(_cert_find_subset_fn( filename,
6af263
+                                             key->info.dir ));
6af263
         if (!matching)
6af263
             return;
6af263
         if (1 == matching->size) {
6af263
@@ -1221,7 +1238,7 @@ _find_partner(netsnmp_cert *cert, netsnm
6af263
             DEBUGMSGT(("cert:partner", "%s matches multiple certs\n",
6af263
                           key->info.filename));
6af263
     }
6af263
-    else if(cert) {
6af263
+    else if (cert) {
6af263
         if (cert->key) {
6af263
             DEBUGMSGT(("cert:partner", "cert already has partner\n"));
6af263
             return;
6af263
@@ -1259,76 +1276,189 @@ _find_partner(netsnmp_cert *cert, netsnm
6af263
     }
6af263
 }
6af263
 
6af263
+static netsnmp_key *
6af263
+_add_key(EVP_PKEY *okey, const char* dirname, const char* filename, FILE *index)
6af263
+{
6af263
+    netsnmp_key  *key;
6af263
+
6af263
+    key = _new_key(dirname, filename);
6af263
+    if (NULL == key) {
6af263
+        return NULL;
6af263
+    }
6af263
+
6af263
+    key->okey = okey;
6af263
+
6af263
+    if (-1 == CONTAINER_INSERT(_keys, key)) {
6af263
+        DEBUGMSGT(("cert:key:file:add:err",
6af263
+                   "error inserting key into container\n"));
6af263
+        netsnmp_key_free(key);
6af263
+        key = NULL;
6af263
+    }
6af263
+    if (index) {
6af263
+        fprintf(index, "k:%s\n", filename);
6af263
+    }
6af263
+
6af263
+    return key;
6af263
+}
6af263
+
6af263
+static netsnmp_cert *
6af263
+_add_cert(X509 *ocert, const char* dirname, const char* filename, int type, int offset,
6af263
+          int allowed_uses, FILE *index)
6af263
+{
6af263
+    netsnmp_cert *cert;
6af263
+
6af263
+    cert = _new_cert(dirname, filename, type, offset,
6af263
+                     allowed_uses, -1, NULL, NULL, NULL);
6af263
+    if (NULL == cert)
6af263
+        return NULL;
6af263
+
6af263
+    netsnmp_ocert_parse(cert, ocert);
6af263
+
6af263
+    if (-1 == CONTAINER_INSERT(_certs, cert)) {
6af263
+        DEBUGMSGT(("cert:file:add:err",
6af263
+                   "error inserting cert into container\n"));
6af263
+        netsnmp_cert_free(cert);
6af263
+        return NULL;
6af263
+    }
6af263
+
6af263
+    if (index) {
6af263
+        /** filename = NAME_MAX = 255 */
6af263
+        /** fingerprint max = 64*3=192 for sha512 */
6af263
+        /** common name / CN  = 64 */
6af263
+        if (cert)
6af263
+            fprintf(index, "c:%s %d %d %d %d %s '%s' '%s'\n", filename,
6af263
+                    cert->info.type, cert->offset, cert->info.allowed_uses,
6af263
+                    cert->hash_type, cert->fingerprint,
6af263
+                    cert->common_name, cert->subject);
6af263
+    }
6af263
+
6af263
+    return cert;
6af263
+}
6af263
+
6af263
 static int
6af263
 _add_certfile(const char* dirname, const char* filename, FILE *index)
6af263
 {
6af263
-    X509         *ocert;
6af263
-    EVP_PKEY     *okey;
6af263
+    BIO          *certbio;
6af263
+    X509         *ocert = NULL;
6af263
+    X509         *ncert;
6af263
+    EVP_PKEY     *okey = NULL;
6af263
     netsnmp_cert *cert = NULL;
6af263
     netsnmp_key  *key = NULL;
6af263
     char          certfile[SNMP_MAXPATH];
6af263
     int           type;
6af263
+    int           offset = 0;
6af263
 
6af263
     if (((const void*)NULL == dirname) || (NULL == filename))
6af263
         return -1;
6af263
 
6af263
     type = _type_from_filename(filename);
6af263
-    netsnmp_assert(type != NS_CERT_TYPE_UNKNOWN);
6af263
+    if (type == NS_CERT_TYPE_UNKNOWN) {
6af263
+        snmp_log(LOG_ERR, "certificate file '%s' type not recognised, ignoring\n", filename);
6af263
+        return -1;
6af263
+    }
6af263
 
6af263
-    snprintf(certfile, sizeof(certfile),"%s/%s", dirname, filename);
6af263
+    certbio = netsnmp_open_bio(dirname, filename);
6af263
+    if (!certbio) {
6af263
+        return -1;
6af263
+    }
6af263
 
6af263
-    DEBUGMSGT(("9:cert:file:add", "Checking file: %s (type %d)\n", filename,
6af263
-               type));
6af263
+    switch (type) {
6af263
 
6af263
-    if (NS_CERT_TYPE_KEY == type) {
6af263
-        key = _new_key(dirname, filename);
6af263
-        if (NULL == key)
6af263
-            return -1;
6af263
-        okey = netsnmp_okey_get(key);
6af263
-        if (NULL == okey) {
6af263
-            netsnmp_key_free(key);
6af263
-            return -1;
6af263
-        }
6af263
-        key->okey = okey;
6af263
-        if (-1 == CONTAINER_INSERT(_keys, key)) {
6af263
-            DEBUGMSGT(("cert:key:file:add:err",
6af263
-                       "error inserting key into container\n"));
6af263
-            netsnmp_key_free(key);
6af263
-            key = NULL;
6af263
-        }
6af263
-    }
6af263
-    else {
6af263
-        cert = _new_cert(dirname, filename, type, -1, NULL, NULL, NULL);
6af263
-        if (NULL == cert)
6af263
-            return -1;
6af263
-        ocert = netsnmp_ocert_get(cert);
6af263
-        if (NULL == ocert) {
6af263
-            netsnmp_cert_free(cert);
6af263
-            return -1;
6af263
-        }
6af263
-        cert->ocert = ocert;
6af263
-        if (-1 == CONTAINER_INSERT(_certs, cert)) {
6af263
-            DEBUGMSGT(("cert:file:add:err",
6af263
-                       "error inserting cert into container\n"));
6af263
-            netsnmp_cert_free(cert);
6af263
-            cert = NULL;
6af263
-        }
6af263
-    }
6af263
-    if ((NULL == cert) && (NULL == key)) {
6af263
-        DEBUGMSGT(("cert:file:add:failure", "for %s\n", certfile));
6af263
-        return -1;
6af263
+       case NS_CERT_TYPE_KEY: 
6af263
+
6af263
+           okey = PEM_read_bio_PrivateKey(certbio, NULL, NULL, NULL);
6af263
+           if (NULL == okey)
6af263
+               snmp_log(LOG_ERR, "error parsing key file %s\n",
6af263
+                     key->info.filename);
6af263
+           else {
6af263
+               key = _add_key(okey, dirname, filename, index);
6af263
+               if (NULL == key) {
6af263
+                   EVP_PKEY_free(okey);
6af263
+                      okey = NULL;
6af263
+               }
6af263
+           }
6af263
+           break;
6af263
+
6af263
+        case NS_CERT_TYPE_DER:
6af263
+
6af263
+            ocert = d2i_X509_bio(certbio, NULL); /* DER/ASN1 */
6af263
+            if (NULL != ocert) {
6af263
+                if (!_add_cert(ocert, dirname, filename, type, 0,
6af263
+                               NS_CERT_REMOTE_PEER, index)) {
6af263
+                    X509_free(ocert);
6af263
+                    ocert = NULL;
6af263
+                }
6af263
+                break;
6af263
+            }
6af263
+            (void)BIO_reset(certbio);
6af263
+            /* Check for PEM if DER didn't work */
6af263
+            /* FALLTHROUGH */
6af263
+
6af263
+        case NS_CERT_TYPE_PEM:
6af263
+
6af263
+            if (NS_CERT_TYPE_DER == type) {
6af263
+                DEBUGMSGT(("9:cert:read", "Changing type from DER to PEM\n"));
6af263
+                type = NS_CERT_TYPE_PEM;
6af263
+            }
6af263
+
6af263
+            /* read the private key first so we can record this in the index */
6af263
+            okey = PEM_read_bio_PrivateKey(certbio, NULL, NULL, NULL);
6af263
+
6af263
+            (void)BIO_reset(certbio);
6af263
+
6af263
+            /* certs are read after the key */
6af263
+	    ocert = ncert = PEM_read_bio_X509_AUX(certbio, NULL, NULL, NULL);
6af263
+            if (NULL != ocert) {
6af263
+                cert = _add_cert(ncert, dirname, filename, type, 0,
6af263
+                                 okey ? NS_CERT_IDENTITY | NS_CERT_REMOTE_PEER :
6af263
+                                 NS_CERT_REMOTE_PEER, index);
6af263
+                if (NULL == cert) {
6af263
+                    X509_free(ocert);
6af263
+                    ocert = ncert = NULL;
6af263
+                }
6af263
+            }
6af263
+            while (NULL != ncert) {
6af263
+                offset = BIO_tell(certbio);
6af263
+                ncert = PEM_read_bio_X509_AUX(certbio, NULL, NULL, NULL);
6af263
+                if (ncert) {
6af263
+                    if (NULL == _add_cert(ncert, dirname, filename, type, offset, 0, index)) {
6af263
+                        X509_free(ncert);
6af263
+                        ncert = NULL;
6af263
+                    }
6af263
+                }
6af263
+            }
6af263
+
6af263
+            if (NULL != okey) {
6af263
+                DEBUGMSGT(("cert:read:key", "found key with cert in %s\n",
6af263
+                           cert->info.filename));
6af263
+                key = _add_key(okey, dirname, filename, NULL);
6af263
+                if (NULL != key) {
6af263
+                    DEBUGMSGT(("cert:read:partner", "%s match found!\n",
6af263
+                               cert->info.filename));
6af263
+                    key->cert = cert;
6af263
+                    cert->key = key;
6af263
+                }
6af263
+                else {
6af263
+                    EVP_PKEY_free(okey);
6af263
+                    okey = NULL;
6af263
+                }
6af263
+            }
6af263
+
6af263
+            break;
6af263
+
6af263
+#ifdef CERT_PKCS12_SUPPORT_MAYBE_LATER
6af263
+        case NS_CERT_TYPE_PKCS12:
6af263
+#endif
6af263
+
6af263
+        default:
6af263
+            break;
6af263
     }
6af263
 
6af263
-    if (index) {
6af263
-        /** filename = NAME_MAX = 255 */
6af263
-        /** fingerprint max = 64*3=192 for sha512 */
6af263
-        /** common name / CN  = 64 */
6af263
-        if (cert)
6af263
-            fprintf(index, "c:%s %d %d %s '%s' '%s'\n", filename,
6af263
-                    cert->info.type, cert->hash_type, cert->fingerprint,
6af263
-                    cert->common_name, cert->subject);
6af263
-        else if (key)
6af263
-            fprintf(index, "k:%s\n", filename);
6af263
+    BIO_vfree(certbio);
6af263
+
6af263
+    if ((NULL == ocert) && (NULL == okey)) {
6af263
+        snmp_log(LOG_ERR, "certificate file '%s' contained neither certificate nor key, ignoring\n", certfile);
6af263
+        return -1;
6af263
     }
6af263
 
6af263
     return 0;
6af263
@@ -1342,8 +1472,10 @@ _cert_read_index(const char *dirname, st
6af263
     struct stat     idx_stat;
6af263
     char            tmpstr[SNMP_MAXPATH + 5], filename[NAME_MAX];
6af263
     char            fingerprint[EVP_MAX_MD_SIZE*3], common_name[64+1], type_str[15];
6af263
-    char            subject[SNMP_MAXBUF_SMALL], hash_str[15];
6af263
-    int             count = 0, type, hash, version;
6af263
+    char            subject[SNMP_MAXBUF_SMALL], hash_str[15], offset_str[15];
6af263
+    char            allowed_uses_str[15];
6af263
+    ssize_t         offset;
6af263
+    int             count = 0, type, allowed_uses, hash, version;
6af263
     netsnmp_cert    *cert;
6af263
     netsnmp_key     *key;
6af263
     netsnmp_container *newer, *found;
6af263
@@ -1386,7 +1518,8 @@ _cert_read_index(const char *dirname, st
6af263
                                               (netsnmp_directory_filter*)
6af263
                                               _time_filter,(void*)&idx_stat,
6af263
                                               NETSNMP_DIR_NSFILE |
6af263
-                                              NETSNMP_DIR_NSFILE_STATS);
6af263
+                                              NETSNMP_DIR_NSFILE_STATS |
6af263
+                                              NETSNMP_DIR_ALLOW_DUPLICATES);
6af263
     if (newer) {
6af263
         DEBUGMSGT(("cert:index:parse", "Index outdated; files modified\n"));
6af263
         CONTAINER_FREE_ALL(newer, NULL);
6af263
@@ -1430,6 +1563,8 @@ _cert_read_index(const char *dirname, st
6af263
             pos = &tmpstr[2];
6af263
             if ((NULL == (pos=copy_nword(pos, filename, sizeof(filename)))) ||
6af263
                 (NULL == (pos=copy_nword(pos, type_str, sizeof(type_str)))) ||
6af263
+                (NULL == (pos=copy_nword(pos, offset_str, sizeof(offset_str)))) ||
6af263
+                (NULL == (pos=copy_nword(pos, allowed_uses_str, sizeof(allowed_uses_str)))) ||
6af263
                 (NULL == (pos=copy_nword(pos, hash_str, sizeof(hash_str)))) ||
6af263
                 (NULL == (pos=copy_nword(pos, fingerprint,
6af263
                                          sizeof(fingerprint)))) ||
6af263
@@ -1442,9 +1577,11 @@ _cert_read_index(const char *dirname, st
6af263
                 break;
6af263
             }
6af263
             type = atoi(type_str);
6af263
+            offset = atoi(offset_str);
6af263
+            allowed_uses = atoi(allowed_uses_str);
6af263
             hash = atoi(hash_str);
6af263
-            cert = (void*)_new_cert(dirname, filename, type, hash, fingerprint,
6af263
-                                    common_name, subject);
6af263
+            cert = _new_cert(dirname, filename, type, offset, allowed_uses, hash,
6af263
+                             fingerprint, common_name, subject);
6af263
             if (cert && 0 == CONTAINER_INSERT(found, cert))
6af263
                 ++count;
6af263
             else {
6af263
@@ -1549,7 +1686,8 @@ _add_certdir(const char *dirname)
6af263
                                               (netsnmp_directory_filter*)
6af263
                                               &_cert_cert_filter, NULL,
6af263
                                               NETSNMP_DIR_RELATIVE_PATH |
6af263
-                                              NETSNMP_DIR_EMPTY_OK );
6af263
+                                              NETSNMP_DIR_EMPTY_OK |
6af263
+                                              NETSNMP_DIR_ALLOW_DUPLICATES);
6af263
     if (NULL == cert_container) {
6af263
         DEBUGMSGT(("cert:index:dir",
6af263
                     "error creating container for cert files\n"));
6af263
@@ -1637,7 +1775,7 @@ _cert_print(netsnmp_cert *c, void *conte
6af263
     if (NULL == c)
6af263
         return;
6af263
 
6af263
-    DEBUGMSGT(("cert:dump", "cert %s in %s\n", c->info.filename, c->info.dir));
6af263
+    DEBUGMSGT(("cert:dump", "cert %s in %s at offset %d\n", c->info.filename, c->info.dir, c->offset));
6af263
     DEBUGMSGT(("cert:dump", "   type %d flags 0x%x (%s)\n",
6af263
              c->info.type, c->info.allowed_uses,
6af263
               _mode_str(c->info.allowed_uses)));
6af263
@@ -1841,7 +1979,8 @@ netsnmp_cert_find(int what, int where, v
6af263
         netsnmp_void_array *matching;
6af263
 
6af263
         DEBUGMSGT(("cert:find:params", " hint = %s\n", (char *)hint));
6af263
-        matching = _cert_find_subset_fn( filename, NULL );
6af263
+        matching = _cert_reduce_subset_what(_cert_find_subset_fn(
6af263
+                                            filename, NULL ), what);
6af263
         if (!matching)
6af263
             return NULL;
6af263
         if (1 == matching->size)
6af263
@@ -1887,6 +2026,32 @@ netsnmp_cert_find(int what, int where, v
6af263
     return result;
6af263
 }
6af263
 
6af263
+netsnmp_void_array *
6af263
+netsnmp_certs_find(int what, int where, void *hint)
6af263
+{
6af263
+
6af263
+    DEBUGMSGT(("certs:find:params", "looking for %s(%d) in %s(0x%x), hint %p\n",
6af263
+               _mode_str(what), what, _where_str(where), where, hint));
6af263
+
6af263
+    if (NS_CERTKEY_FILE == where) {
6af263
+        /** hint == filename */
6af263
+        char               *filename = (char*)hint;
6af263
+        netsnmp_void_array *matching;
6af263
+
6af263
+        DEBUGMSGT(("cert:find:params", " hint = %s\n", (char *)hint));
6af263
+        matching = _cert_reduce_subset_what(_cert_find_subset_fn(
6af263
+                                            filename, NULL ), what);
6af263
+
6af263
+        return matching;
6af263
+    } /* where = NS_CERTKEY_FILE */
6af263
+    else { /* unknown location */
6af263
+
6af263
+        DEBUGMSGT(("certs:find:err", "unhandled location %d for %d\n", where,
6af263
+                   what));
6af263
+        return NULL;
6af263
+    }
6af263
+}
6af263
+
6af263
 #ifndef NETSNMP_FEATURE_REMOVE_CERT_FINGERPRINTS
6af263
 int
6af263
 netsnmp_cert_check_vb_fingerprint(const netsnmp_variable_list *var)
6af263
@@ -2284,6 +2449,124 @@ _reduce_subset_dir(netsnmp_void_array *m
6af263
     }
6af263
 }
6af263
 
6af263
+/*
6af263
+ * reduce subset by eliminating any certificates that are not the
6af263
+ * first certficate in a file. This allows us to ignore certificate
6af263
+ * chains when testing for specific certificates, and to match keys
6af263
+ * to the first certificate only.
6af263
+ */
6af263
+static netsnmp_void_array *
6af263
+_cert_reduce_subset_first(netsnmp_void_array *matching)
6af263
+{
6af263
+    netsnmp_cert *cc;
6af263
+    int i = 0, j, newsize;
6af263
+
6af263
+    if ((NULL == matching))
6af263
+        return matching;
6af263
+
6af263
+    newsize = matching->size;
6af263
+
6af263
+    for( ; i < matching->size; ) {
6af263
+        /*
6af263
+         * if we've shifted matches down we'll hit a NULL entry before
6af263
+         * we hit the end of the array.
6af263
+         */
6af263
+        if (NULL == matching->array[i])
6af263
+            break;
6af263
+        /*
6af263
+         * skip over valid matches. The first entry has an offset of zero.
6af263
+         */
6af263
+        cc = (netsnmp_cert*)matching->array[i];
6af263
+        if (0 == cc->offset) {
6af263
+            ++i;
6af263
+            continue;
6af263
+        }
6af263
+        /*
6af263
+         * shrink array by shifting everything down a spot. Might not be
6af263
+         * the most efficient soloution, but this is just happening at
6af263
+         * startup and hopefully most certs won't have common prefixes.
6af263
+         */
6af263
+        --newsize;
6af263
+        for ( j=i; j < newsize; ++j )
6af263
+            matching->array[j] = matching->array[j+1];
6af263
+        matching->array[j] = NULL;
6af263
+        /** no ++i; just shifted down, need to look at same position again */
6af263
+    }
6af263
+    /*
6af263
+     * if we shifted, set the new size
6af263
+     */
6af263
+    if (newsize != matching->size) {
6af263
+        DEBUGMSGT(("9:cert:subset:first", "shrank from %" NETSNMP_PRIz "d to %d\n",
6af263
+                   matching->size, newsize));
6af263
+        matching->size = newsize;
6af263
+    }
6af263
+
6af263
+    if (0 == matching->size) {
6af263
+        free(matching->array);
6af263
+        SNMP_FREE(matching);
6af263
+    }
6af263
+
6af263
+    return matching;
6af263
+}
6af263
+
6af263
+/*
6af263
+ * reduce subset by eliminating any certificates that do not match
6af263
+ * purpose specified.
6af263
+ */
6af263
+static netsnmp_void_array *
6af263
+_cert_reduce_subset_what(netsnmp_void_array *matching, int what)
6af263
+{
6af263
+    netsnmp_cert_common *cc;
6af263
+    int i = 0, j, newsize;
6af263
+
6af263
+    if ((NULL == matching))
6af263
+        return matching;
6af263
+
6af263
+    newsize = matching->size;
6af263
+
6af263
+    for( ; i < matching->size; ) {
6af263
+        /*
6af263
+         * if we've shifted matches down we'll hit a NULL entry before
6af263
+         * we hit the end of the array.
6af263
+         */
6af263
+        if (NULL == matching->array[i])
6af263
+            break;
6af263
+        /*
6af263
+         * skip over valid matches. The first entry has an offset of zero.
6af263
+         */
6af263
+        cc = (netsnmp_cert_common *)matching->array[i];
6af263
+        if ((cc->allowed_uses & what)) {
6af263
+            ++i;
6af263
+            continue;
6af263
+        }
6af263
+        /*
6af263
+         * shrink array by shifting everything down a spot. Might not be
6af263
+         * the most efficient soloution, but this is just happening at
6af263
+         * startup and hopefully most certs won't have common prefixes.
6af263
+         */
6af263
+        --newsize;
6af263
+        for ( j=i; j < newsize; ++j )
6af263
+            matching->array[j] = matching->array[j+1];
6af263
+        matching->array[j] = NULL;
6af263
+        /** no ++i; just shifted down, need to look at same position again */
6af263
+    }
6af263
+    /*
6af263
+     * if we shifted, set the new size
6af263
+     */
6af263
+    if (newsize != matching->size) {
6af263
+        DEBUGMSGT(("9:cert:subset:what", "shrank from %" NETSNMP_PRIz "d to %d\n",
6af263
+                   matching->size, newsize));
6af263
+        matching->size = newsize;
6af263
+    }
6af263
+
6af263
+    if (0 == matching->size) {
6af263
+        free(matching->array);
6af263
+        SNMP_FREE(matching);
6af263
+    }
6af263
+
6af263
+    return matching;
6af263
+}
6af263
+
6af263
 static netsnmp_void_array *
6af263
 _cert_find_subset_common(const char *filename, netsnmp_container *container)
6af263
 {
6af263
diff -urNp a/snmplib/transports/snmpTLSBaseDomain.c b/snmplib/transports/snmpTLSBaseDomain.c
6af263
--- a/snmplib/transports/snmpTLSBaseDomain.c	2021-06-09 10:55:22.791954900 +0200
6af263
+++ b/snmplib/transports/snmpTLSBaseDomain.c	2021-06-09 10:56:36.727272302 +0200
6af263
@@ -59,7 +59,7 @@ int openssl_local_index;
6af263
 /* this is called during negotiation */
6af263
 int verify_callback(int ok, X509_STORE_CTX *ctx) {
6af263
     int err, depth;
6af263
-    char buf[1024], *fingerprint;
6af263
+    char subject[SNMP_MAXBUF_MEDIUM], issuer[SNMP_MAXBUF_MEDIUM], *fingerprint;
6af263
     X509 *thecert;
6af263
     netsnmp_cert *cert;
6af263
     _netsnmp_verify_info *verify_info;
6af263
@@ -71,10 +71,12 @@ int verify_callback(int ok, X509_STORE_C
6af263
     
6af263
     /* things to do: */
6af263
 
6af263
-    X509_NAME_oneline(X509_get_subject_name(thecert), buf, sizeof(buf));
6af263
+    X509_NAME_oneline(X509_get_subject_name(thecert), subject, sizeof(subject));
6af263
+    X509_NAME_oneline(X509_get_issuer_name(thecert), issuer, sizeof(issuer));
6af263
     fingerprint = netsnmp_openssl_cert_get_fingerprint(thecert, -1);
6af263
-    DEBUGMSGTL(("tls_x509:verify", "Cert: %s\n", buf));
6af263
-    DEBUGMSGTL(("tls_x509:verify", "  fp: %s\n", fingerprint ?
6af263
+    DEBUGMSGTL(("tls_x509:verify", " subject: %s\n", subject));
6af263
+    DEBUGMSGTL(("tls_x509:verify", "  issuer: %s\n", issuer));
6af263
+    DEBUGMSGTL(("tls_x509:verify", "      fp: %s\n", fingerprint ?
6af263
                 fingerprint : "unknown"));
6af263
 
6af263
     ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
6af263
@@ -109,7 +111,7 @@ int verify_callback(int ok, X509_STORE_C
6af263
         } else {
6af263
             DEBUGMSGTL(("tls_x509:verify", "  no matching fp found\n"));
6af263
             /* log where we are and why called */
6af263
-            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));
6af263
+            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));
6af263
             SNMP_FREE(fingerprint);
6af263
             return 0;
6af263
         }
6af263
@@ -425,23 +427,50 @@ netsnmp_tlsbase_extract_security_name(SS
6af263
 int
6af263
 _trust_this_cert(SSL_CTX *the_ctx, char *certspec) {
6af263
     netsnmp_cert *trustcert;
6af263
+    netsnmp_cert *candidate;
6af263
+    netsnmp_void_array *matching = NULL;
6af263
+
6af263
+    int                 i;
6af263
 
6af263
     DEBUGMSGTL(("sslctx_client", "Trying to load a trusted certificate: %s\n",
6af263
                 certspec));
6af263
 
6af263
     /* load this identifier into the trust chain */
6af263
     trustcert = netsnmp_cert_find(NS_CERT_CA,
6af263
-                                  NS_CERTKEY_MULTIPLE,
6af263
+                                  NS_CERTKEY_FINGERPRINT,
6af263
                                   certspec);
6af263
+
6af263
+    /* loop through all CA certs in the given files */
6af263
+    if (!trustcert) {
6af263
+        matching = netsnmp_certs_find(NS_CERT_CA,
6af263
+                                      NS_CERTKEY_FILE,
6af263
+                                      certspec);
6af263
+        for (i = 0; (matching) && (i < matching->size); ++i) {
6af263
+            candidate = (netsnmp_cert*)matching->array[i];
6af263
+            if (netsnmp_cert_trust(the_ctx, candidate) != SNMPERR_SUCCESS) {
6af263
+                free(matching->array);
6af263
+                free(matching);
6af263
+                LOGANDDIE("failed to load trust certificate");
6af263
+            }
6af263
+        } /** matching loop */
6af263
+
6af263
+        if (matching) {
6af263
+            free(matching->array);
6af263
+            free(matching);
6af263
+            return 1;
6af263
+	}
6af263
+    }
6af263
+
6af263
+    /* fall back to trusting the remote peer certificate */
6af263
     if (!trustcert)
6af263
         trustcert = netsnmp_cert_find(NS_CERT_REMOTE_PEER,
6af263
                                       NS_CERTKEY_MULTIPLE,
6af263
                                       certspec);
6af263
     if (!trustcert)
6af263
         LOGANDDIE("failed to find requested certificate to trust");
6af263
-        
6af263
+
6af263
     /* Add the certificate to the context */
6af263
-    if (netsnmp_cert_trust_ca(the_ctx, trustcert) != SNMPERR_SUCCESS)
6af263
+    if (netsnmp_cert_trust(the_ctx, trustcert) != SNMPERR_SUCCESS)
6af263
         LOGANDDIE("failed to load trust certificate");
6af263
 
6af263
     return 1;
6af263
@@ -481,7 +510,7 @@ _sslctx_common_setup(SSL_CTX *the_ctx, _
6af263
                                     NETSNMP_DS_LIB_X509_CRL_FILE);
6af263
     if (NULL != crlFile) {
6af263
         cert_store = SSL_CTX_get_cert_store(the_ctx);
6af263
-        DEBUGMSGTL(("sslctx_client", "loading CRL: %s\n", crlFile));
6af263
+        DEBUGMSGTL(("sslctx_common", "loading CRL: %s\n", crlFile));
6af263
         if (!cert_store)
6af263
             LOGANDDIE("failed to find certificate store");
6af263
         if (!(lookup = X509_STORE_add_lookup(cert_store, X509_LOOKUP_file())))
6af263
@@ -546,13 +575,19 @@ sslctx_client_setup(const SSL_METHOD *me
6af263
                 id_cert->key->info.filename));
6af263
 
6af263
     if (SSL_CTX_use_certificate(the_ctx, id_cert->ocert) <= 0)
6af263
-        LOGANDDIE("failed to set the certificate to use");
6af263
+        LOGANDDIE("failed to set the client certificate to use");
6af263
 
6af263
     if (SSL_CTX_use_PrivateKey(the_ctx, id_cert->key->okey) <= 0)
6af263
-        LOGANDDIE("failed to set the private key to use");
6af263
+        LOGANDDIE("failed to set the client private key to use");
6af263
 
6af263
     if (!SSL_CTX_check_private_key(the_ctx))
6af263
-        LOGANDDIE("public and private keys incompatible");
6af263
+        LOGANDDIE("client public and private keys incompatible");
6af263
+
6af263
+    while (id_cert->issuer_cert) {
6af263
+        id_cert = id_cert->issuer_cert;
6af263
+        if (!SSL_CTX_add_extra_chain_cert(the_ctx, id_cert->ocert))
6af263
+            LOGANDDIE("failed to add intermediate client certificate");
6af263
+    }
6af263
 
6af263
     if (tlsbase->their_identity)
6af263
         peer_cert = netsnmp_cert_find(NS_CERT_REMOTE_PEER,
6af263
@@ -566,11 +601,11 @@ sslctx_client_setup(const SSL_METHOD *me
6af263
                     peer_cert ? peer_cert->info.filename : "none"));
6af263
 
6af263
         /* Trust the expected certificate */
6af263
-        if (netsnmp_cert_trust_ca(the_ctx, peer_cert) != SNMPERR_SUCCESS)
6af263
+        if (netsnmp_cert_trust(the_ctx, peer_cert) != SNMPERR_SUCCESS)
6af263
             LOGANDDIE ("failed to set verify paths");
6af263
     }
6af263
 
6af263
-    /* trust a certificate (possibly a CA) aspecifically passed in */
6af263
+    /* trust a certificate (possibly a CA) specifically passed in */
6af263
     if (tlsbase->trust_cert) {
6af263
         if (!_trust_this_cert(the_ctx, tlsbase->trust_cert))
6af263
             return 0;
6af263
@@ -589,7 +624,7 @@ sslctx_server_setup(const SSL_METHOD *me
6af263
     /* setting up for ssl */
6af263
     SSL_CTX *the_ctx = SSL_CTX_new(NETSNMP_REMOVE_CONST(SSL_METHOD *, method));
6af263
     if (!the_ctx) {
6af263
-        LOGANDDIE("can't create a new context");
6af263
+        LOGANDDIE("can't create a new server context");
6af263
     }
6af263
 
6af263
     id_cert = netsnmp_cert_find(NS_CERT_IDENTITY, NS_CERTKEY_DEFAULT, NULL);
6af263
@@ -597,7 +632,7 @@ sslctx_server_setup(const SSL_METHOD *me
6af263
         LOGANDDIE ("error finding server identity keys");
6af263
 
6af263
     if (!id_cert->key || !id_cert->key->okey)
6af263
-        LOGANDDIE("failed to load private key");
6af263
+        LOGANDDIE("failed to load server private key");
6af263
 
6af263
     DEBUGMSGTL(("sslctx_server", "using public key: %s\n",
6af263
                 id_cert->info.filename));
6af263
@@ -605,13 +640,19 @@ sslctx_server_setup(const SSL_METHOD *me
6af263
                 id_cert->key->info.filename));
6af263
 
6af263
     if (SSL_CTX_use_certificate(the_ctx, id_cert->ocert) <= 0)
6af263
-        LOGANDDIE("failed to set the certificate to use");
6af263
+        LOGANDDIE("failed to set the server certificate to use");
6af263
 
6af263
     if (SSL_CTX_use_PrivateKey(the_ctx, id_cert->key->okey) <= 0)
6af263
-        LOGANDDIE("failed to set the private key to use");
6af263
+        LOGANDDIE("failed to set the server private key to use");
6af263
 
6af263
     if (!SSL_CTX_check_private_key(the_ctx))
6af263
-        LOGANDDIE("public and private keys incompatible");
6af263
+        LOGANDDIE("server public and private keys incompatible");
6af263
+
6af263
+    while (id_cert->issuer_cert) {
6af263
+        id_cert = id_cert->issuer_cert;
6af263
+        if (!SSL_CTX_add_extra_chain_cert(the_ctx, id_cert->ocert))
6af263
+            LOGANDDIE("failed to add intermediate server certificate");
6af263
+    }
6af263
 
6af263
     SSL_CTX_set_read_ahead(the_ctx, 1); /* XXX: DTLS only? */
6af263