You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
856 lines
30 KiB
856 lines
30 KiB
diff --git a/include/net-snmp/library/cert_util.h b/include/net-snmp/library/cert_util.h
|
|
index 80e2a19..143adbb 100644
|
|
--- a/include/net-snmp/library/cert_util.h
|
|
+++ b/include/net-snmp/library/cert_util.h
|
|
@@ -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 --git a/include/net-snmp/library/dir_utils.h b/include/net-snmp/library/dir_utils.h
|
|
index 471bb0b..ac7f69a 100644
|
|
--- a/include/net-snmp/library/dir_utils.h
|
|
+++ b/include/net-snmp/library/dir_utils.h
|
|
@@ -53,7 +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
|
|
|
|
|
|
#ifdef __cplusplus
|
|
diff --git a/snmplib/cert_util.c b/snmplib/cert_util.c
|
|
index 210ad8b..b1f8144 100644
|
|
--- a/snmplib/cert_util.c
|
|
+++ b/snmplib/cert_util.c
|
|
@@ -100,7 +100,7 @@ netsnmp_feature_child_of(tls_fingerprint_build, cert_util_all);
|
|
* 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_cert_common *lhs,
|
|
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,7 +435,7 @@ netsnmp_cert_map_container(void)
|
|
}
|
|
|
|
static netsnmp_cert *
|
|
-_new_cert(const char *dirname, const char *filename, int certType,
|
|
+_new_cert(const char *dirname, const char *filename, int certType, int offset,
|
|
int hashType, const char *fingerprint, const char *common_name,
|
|
const char *subject)
|
|
{
|
|
@@ -446,8 +457,10 @@ _new_cert(const char *dirname, const char *filename, int certType,
|
|
|
|
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 = offset ? 0 : NS_CERT_REMOTE_PEER;
|
|
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 *cert)
|
|
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 *cert)
|
|
/** 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, netsnmp_key *key)
|
|
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, netsnmp_key *key)
|
|
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, netsnmp_key *key)
|
|
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,182 @@ _find_partner(netsnmp_cert *cert, netsnmp_key *key)
|
|
}
|
|
}
|
|
|
|
+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, FILE *index)
|
|
+{
|
|
+ netsnmp_cert *cert;
|
|
+
|
|
+ cert = _new_cert(dirname, filename, type, offset, -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 %s '%s' '%s'\n", filename,
|
|
+ cert->info.type, cert->offset, 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, 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;
|
|
+ }
|
|
+ ocert = ncert = PEM_read_bio_X509_AUX(certbio, NULL, NULL, NULL);
|
|
+ if (NULL != ocert) {
|
|
+ cert = _add_cert(ncert, dirname, filename, type, offset, 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, index)) {
|
|
+ X509_free(ncert);
|
|
+ ncert = NULL;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ BIO_seek(certbio, offset);
|
|
+
|
|
+ /** check for private key too */
|
|
+ okey = PEM_read_bio_PrivateKey(certbio, NULL, NULL, 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;
|
|
+ cert->info.allowed_uses |= NS_CERT_IDENTITY;
|
|
+ }
|
|
+ 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,7 +1461,8 @@ _cert_read_index(const char *dirname, struct stat *dirstat)
|
|
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];
|
|
+ char subject[SNMP_MAXBUF_SMALL], hash_str[15], offset_str[15];
|
|
+ ssize_t offset;
|
|
int count = 0, type, hash, version;
|
|
netsnmp_cert *cert;
|
|
netsnmp_key *key;
|
|
@@ -1381,7 +1505,8 @@ _cert_read_index(const char *dirname, struct stat *dirstat)
|
|
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);
|
|
@@ -1426,6 +1551,7 @@ _cert_read_index(const char *dirname, struct stat *dirstat)
|
|
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, hash_str, sizeof(hash_str)))) ||
|
|
(NULL == (pos=copy_nword(pos, fingerprint,
|
|
sizeof(fingerprint)))) ||
|
|
@@ -1438,8 +1564,9 @@ _cert_read_index(const char *dirname, struct stat *dirstat)
|
|
break;
|
|
}
|
|
type = atoi(type_str);
|
|
+ offset = atoi(offset_str);
|
|
hash = atoi(hash_str);
|
|
- cert = _new_cert(dirname, filename, type, hash, fingerprint,
|
|
+ cert = _new_cert(dirname, filename, type, offset, hash, fingerprint,
|
|
common_name, subject);
|
|
if (cert && 0 == CONTAINER_INSERT(found, cert))
|
|
++count;
|
|
@@ -1546,7 +1673,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"));
|
|
@@ -1634,7 +1762,7 @@ _cert_print(netsnmp_cert *c, void *context)
|
|
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)));
|
|
@@ -1838,7 +1966,8 @@ netsnmp_cert_find(int what, int where, void *hint)
|
|
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)
|
|
@@ -2281,6 +2410,124 @@ _reduce_subset_dir(netsnmp_void_array *matching, const char *directory)
|
|
}
|
|
}
|
|
|
|
+/*
|
|
+ * 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 --git a/snmplib/dir_utils.c b/snmplib/dir_utils.c
|
|
index c2dd989..e7145e4 100644
|
|
--- a/snmplib/dir_utils.c
|
|
+++ b/snmplib/dir_utils.c
|
|
@@ -107,6 +107,9 @@ netsnmp_directory_container_read_some(netsnmp_container *user_container,
|
|
/** 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);
|