/src/net-snmp/snmplib/cert_util.c
| Line | Count | Source (jump to first uncovered line) | 
| 1 |  | /* | 
| 2 |  |  * Portions of this file are subject to the following copyright(s).  See | 
| 3 |  |  * the Net-SNMP's COPYING file for more details and other copyrights | 
| 4 |  |  * that may apply: | 
| 5 |  |  * | 
| 6 |  |  * Portions of this file are copyrighted by: | 
| 7 |  |  * Copyright (c) 2016 VMware, Inc. All rights reserved. | 
| 8 |  |  * Use is subject to license terms specified in the COPYING file | 
| 9 |  |  * distributed with the Net-SNMP package. | 
| 10 |  |  */ | 
| 11 |  |  | 
| 12 |  | #include <net-snmp/net-snmp-config.h> | 
| 13 |  | #include <net-snmp/net-snmp-features.h> | 
| 14 |  |  | 
| 15 |  | #if defined(NETSNMP_USE_OPENSSL) && defined(HAVE_LIBSSL) && NETSNMP_TRANSPORT_TLSBASE_DOMAIN | 
| 16 |  | netsnmp_feature_child_of(cert_util_all, libnetsnmp); | 
| 17 |  | netsnmp_feature_child_of(cert_util, cert_util_all); | 
| 18 |  | #ifdef NETSNMP_FEATURE_REQUIRE_CERT_UTIL | 
| 19 |  | netsnmp_feature_require(container_directory); | 
| 20 |  | netsnmp_feature_require(container_fifo); | 
| 21 |  | netsnmp_feature_require(container_dup); | 
| 22 |  | netsnmp_feature_require(container_free_all); | 
| 23 |  | netsnmp_feature_require(subcontainer_find); | 
| 24 |  |  | 
| 25 |  | netsnmp_feature_child_of(cert_map_remove, netsnmp_unused); | 
| 26 |  | netsnmp_feature_child_of(cert_map_find, netsnmp_unused); | 
| 27 |  | netsnmp_feature_child_of(tlstmparams_external, cert_util_all); | 
| 28 |  | netsnmp_feature_child_of(tlstmparams_container, tlstmparams_external); | 
| 29 |  | netsnmp_feature_child_of(tlstmparams_remove, tlstmparams_external); | 
| 30 |  | netsnmp_feature_child_of(tlstmparams_find, tlstmparams_external); | 
| 31 |  | netsnmp_feature_child_of(tlstmAddr_remove, netsnmp_unused); | 
| 32 |  | netsnmp_feature_child_of(tlstmaddr_external, cert_util_all); | 
| 33 |  | netsnmp_feature_child_of(tlstmaddr_container, tlstmaddr_external); | 
| 34 |  | netsnmp_feature_child_of(tlstmAddr_get_serverId, tlstmaddr_external); | 
| 35 |  |  | 
| 36 |  | netsnmp_feature_child_of(cert_fingerprints, cert_util_all); | 
| 37 |  | netsnmp_feature_child_of(tls_fingerprint_build, cert_util_all); | 
| 38 |  |  | 
| 39 |  | #endif /* NETSNMP_FEATURE_REQUIRE_CERT_UTIL */ | 
| 40 |  |  | 
| 41 |  | #ifndef NETSNMP_FEATURE_REMOVE_CERT_UTIL | 
| 42 |  |  | 
| 43 |  | #include <ctype.h> | 
| 44 |  |  | 
| 45 |  | #include <stddef.h> | 
| 46 |  |  | 
| 47 |  | #ifdef HAVE_STDLIB_H | 
| 48 |  | #include <stdlib.h> | 
| 49 |  | #endif | 
| 50 |  |  | 
| 51 |  | #ifdef HAVE_STRING_H | 
| 52 |  | #include <string.h> | 
| 53 |  | #else | 
| 54 |  | #include <strings.h> | 
| 55 |  | #endif | 
| 56 |  |  | 
| 57 |  | #ifdef HAVE_SYS_STAT_H | 
| 58 |  | #   include <sys/stat.h> | 
| 59 |  | #endif | 
| 60 |  | #ifdef HAVE_DIRENT_H | 
| 61 |  | #include <dirent.h> | 
| 62 |  | #endif | 
| 63 |  |  | 
| 64 |  | #include <net-snmp/types.h> | 
| 65 |  | #include <net-snmp/output_api.h> | 
| 66 |  | #include <net-snmp/config_api.h> | 
| 67 |  |  | 
| 68 |  | #include <net-snmp/library/snmp.h> | 
| 69 |  | #include <net-snmp/library/snmp_assert.h> | 
| 70 |  | #include <net-snmp/library/snmp_transport.h> | 
| 71 |  | #include <net-snmp/library/system.h> | 
| 72 |  | #include <net-snmp/library/tools.h> | 
| 73 |  | #include <net-snmp/library/container.h> | 
| 74 |  | #include <net-snmp/library/data_list.h> | 
| 75 |  | #include <net-snmp/library/file_utils.h> | 
| 76 |  | #include <net-snmp/library/dir_utils.h> | 
| 77 |  | #include <net-snmp/library/read_config.h> | 
| 78 |  |  | 
| 79 |  | #include <openssl/ssl.h> | 
| 80 |  | #include <openssl/err.h> | 
| 81 |  | #include <openssl/x509v3.h> | 
| 82 |  | #include <net-snmp/library/cert_util.h> | 
| 83 |  | #include <net-snmp/library/snmp_openssl.h> | 
| 84 |  |  | 
| 85 |  | #ifndef NAME_MAX | 
| 86 |  | #define NAME_MAX 255 | 
| 87 |  | #endif | 
| 88 |  |  | 
| 89 |  | /* | 
| 90 |  |  * bump this value whenever cert index format changes, so indexes | 
| 91 |  |  * will be regenerated with new format. | 
| 92 |  |  */ | 
| 93 | 0 | #define CERT_INDEX_FORMAT  2 | 
| 94 |  |  | 
| 95 |  | static netsnmp_container *_certs = NULL; | 
| 96 |  | static netsnmp_container *_keys = NULL; | 
| 97 |  | static netsnmp_container *_maps = NULL; | 
| 98 |  | static netsnmp_container *_tlstmParams = NULL; | 
| 99 |  | static netsnmp_container *_tlstmAddr = NULL; | 
| 100 |  | static struct snmp_enum_list *_certindexes = NULL; | 
| 101 |  |  | 
| 102 |  | static netsnmp_container *_trusted_certs = NULL; | 
| 103 |  |  | 
| 104 |  | static void _setup_containers(void); | 
| 105 |  |  | 
| 106 |  | static void _cert_indexes_load(void); | 
| 107 |  | static void _cert_free(netsnmp_cert *cert, void *context); | 
| 108 |  | static void _key_free(netsnmp_key *key, void *context); | 
| 109 |  | static int  _cert_compare(netsnmp_cert *lhs, netsnmp_cert *rhs); | 
| 110 |  | static int  _cert_sn_compare(netsnmp_cert *lhs, netsnmp_cert *rhs); | 
| 111 |  | static int  _cert_sn_ncompare(netsnmp_cert *lhs, netsnmp_cert *rhs); | 
| 112 |  | static int  _cert_cn_compare(netsnmp_cert *lhs, netsnmp_cert *rhs); | 
| 113 |  | static int  _cert_fn_compare(netsnmp_cert_common *lhs, | 
| 114 |  |                              netsnmp_cert_common *rhs); | 
| 115 |  | static int  _cert_fn_ncompare(netsnmp_cert_common *lhs, | 
| 116 |  |                               netsnmp_cert_common *rhs); | 
| 117 |  | static void _find_partner(netsnmp_cert *cert, netsnmp_key *key); | 
| 118 |  | static netsnmp_cert *_find_issuer(netsnmp_cert *cert); | 
| 119 |  | static netsnmp_void_array *_cert_reduce_subset_first(netsnmp_void_array *matching); | 
| 120 |  | static netsnmp_void_array *_cert_reduce_subset_what(netsnmp_void_array *matching, int what); | 
| 121 |  | static netsnmp_void_array *_cert_find_subset_fn(const char *filename, | 
| 122 |  |                                                 const char *directory); | 
| 123 |  | static netsnmp_void_array *_cert_find_subset_sn(const char *subject); | 
| 124 |  | static netsnmp_void_array *_key_find_subset(const char *filename); | 
| 125 |  | static netsnmp_cert *_cert_find_fp(const char *fingerprint); | 
| 126 |  | static char *_find_tlstmParams_fingerprint(const char *param); | 
| 127 |  | static char *_find_tlstmAddr_fingerprint(const char *name); | 
| 128 |  | static const char *_mode_str(u_char mode); | 
| 129 |  | static const char *_where_str(u_int what); | 
| 130 |  | void netsnmp_cert_dump_all(void); | 
| 131 |  |  | 
| 132 |  | int netsnmp_cert_load_x509(netsnmp_cert *cert); | 
| 133 |  |  | 
| 134 |  | void netsnmp_cert_free(netsnmp_cert *cert); | 
| 135 |  | void netsnmp_key_free(netsnmp_key *key); | 
| 136 |  |  | 
| 137 |  | static int _certindex_add( const char *dirname, int i ); | 
| 138 |  |  | 
| 139 |  | static int _time_filter(const void *text, void *ctx); | 
| 140 |  |  | 
| 141 |  | static void _init_tlstmCertToTSN(void); | 
| 142 | 0 | #define TRUSTCERT_CONFIG_TOKEN "trustCert" | 
| 143 |  | static void _parse_trustcert(const char *token, char *line); | 
| 144 |  |  | 
| 145 |  | static void _init_tlstmParams(void); | 
| 146 |  | static void _init_tlstmAddr(void); | 
| 147 |  |  | 
| 148 |  | /** mode descriptions should match up with header */ | 
| 149 |  | static const char _modes[][256] = | 
| 150 |  |         { | 
| 151 |  |             "none", | 
| 152 |  |             "identity", | 
| 153 |  |             "remote_peer", | 
| 154 |  |             "identity+remote_peer", | 
| 155 |  |             "reserved1", | 
| 156 |  |             "reserved1+identity", | 
| 157 |  |             "reserved1+remote_peer", | 
| 158 |  |             "reserved1+identity+remote_peer", | 
| 159 |  |             "CA", | 
| 160 |  |             "CA+identity", | 
| 161 |  |             "CA+remote_peer", | 
| 162 |  |             "CA+identity+remote_peer", | 
| 163 |  |             "CA+reserved1", | 
| 164 |  |             "CA+reserved1+identity", | 
| 165 |  |             "CA+reserved1+remote_peer", | 
| 166 |  |             "CA+reserved1+identity+remote_peer", | 
| 167 |  |         }; | 
| 168 |  |  | 
| 169 |  | /* ##################################################################### | 
| 170 |  |  * | 
| 171 |  |  * init and shutdown functions | 
| 172 |  |  * | 
| 173 |  |  */ | 
| 174 |  |  | 
| 175 |  | void | 
| 176 |  | _netsnmp_release_trustcerts(void) | 
| 177 | 0 | { | 
| 178 | 0 |     if (NULL != _trusted_certs) { | 
| 179 | 0 |         CONTAINER_FREE_ALL(_trusted_certs, NULL); | 
| 180 | 0 |         CONTAINER_FREE(_trusted_certs); | 
| 181 | 0 |         _trusted_certs = NULL; | 
| 182 | 0 |     } | 
| 183 | 0 | } | 
| 184 |  |  | 
| 185 |  | void | 
| 186 |  | _setup_trusted_certs(void) | 
| 187 | 0 | { | 
| 188 | 0 |     _trusted_certs = netsnmp_container_find("trusted_certs:fifo"); | 
| 189 | 0 |     if (NULL == _trusted_certs) { | 
| 190 | 0 |         snmp_log(LOG_ERR, "could not create container for trusted certs\n"); | 
| 191 | 0 |         netsnmp_certs_shutdown(); | 
| 192 | 0 |         return; | 
| 193 | 0 |     } | 
| 194 | 0 |     _trusted_certs->container_name = strdup("trusted certificates"); | 
| 195 | 0 |     _trusted_certs->compare = (netsnmp_container_compare*) strcmp; | 
| 196 | 0 | } | 
| 197 |  |  | 
| 198 |  | /* | 
| 199 |  |  * secname mapping for servers. | 
| 200 |  |  */ | 
| 201 |  | void | 
| 202 |  | netsnmp_certs_agent_init(void) | 
| 203 | 0 | { | 
| 204 | 0 |     _init_tlstmCertToTSN(); | 
| 205 | 0 |     _init_tlstmParams(); | 
| 206 | 0 |     _init_tlstmAddr(); | 
| 207 | 0 | } | 
| 208 |  |  | 
| 209 |  | void | 
| 210 |  | netsnmp_certs_init(void) | 
| 211 | 0 | { | 
| 212 | 0 |     const char *trustCert_help = TRUSTCERT_CONFIG_TOKEN | 
| 213 | 0 |         " FINGERPRINT|FILENAME"; | 
| 214 |  | 
 | 
| 215 | 0 |     register_config_handler("snmp", TRUSTCERT_CONFIG_TOKEN, | 
| 216 | 0 |                             _parse_trustcert, _netsnmp_release_trustcerts, | 
| 217 | 0 |                             trustCert_help); | 
| 218 | 0 |     _setup_containers(); | 
| 219 |  |  | 
| 220 |  |     /** add certificate type mapping */ | 
| 221 | 0 |     se_add_pair_to_slist("cert_types", strdup("pem"), NS_CERT_TYPE_PEM); | 
| 222 | 0 |     se_add_pair_to_slist("cert_types", strdup("crt"), NS_CERT_TYPE_DER); | 
| 223 | 0 |     se_add_pair_to_slist("cert_types", strdup("cer"), NS_CERT_TYPE_DER); | 
| 224 | 0 |     se_add_pair_to_slist("cert_types", strdup("cert"), NS_CERT_TYPE_DER); | 
| 225 | 0 |     se_add_pair_to_slist("cert_types", strdup("der"), NS_CERT_TYPE_DER); | 
| 226 | 0 |     se_add_pair_to_slist("cert_types", strdup("key"), NS_CERT_TYPE_KEY); | 
| 227 | 0 |     se_add_pair_to_slist("cert_types", strdup("private"), NS_CERT_TYPE_KEY); | 
| 228 |  |  | 
| 229 |  |     /** hash algs */ | 
| 230 | 0 |     se_add_pair_to_slist("cert_hash_alg", strdup("sha1"), NS_HASH_SHA1); | 
| 231 | 0 |     se_add_pair_to_slist("cert_hash_alg", strdup("md5"), NS_HASH_MD5); | 
| 232 | 0 |     se_add_pair_to_slist("cert_hash_alg", strdup("sha224"), NS_HASH_SHA224); | 
| 233 | 0 |     se_add_pair_to_slist("cert_hash_alg", strdup("sha256"), NS_HASH_SHA256); | 
| 234 | 0 |     se_add_pair_to_slist("cert_hash_alg", strdup("sha384"), NS_HASH_SHA384); | 
| 235 | 0 |     se_add_pair_to_slist("cert_hash_alg", strdup("sha512"), NS_HASH_SHA512); | 
| 236 |  |  | 
| 237 |  |     /** map types */ | 
| 238 | 0 |     se_add_pair_to_slist("cert_map_type", strdup("cn"), | 
| 239 | 0 |                          TSNM_tlstmCertCommonName); | 
| 240 | 0 |     se_add_pair_to_slist("cert_map_type", strdup("ip"), | 
| 241 | 0 |                          TSNM_tlstmCertSANIpAddress); | 
| 242 | 0 |     se_add_pair_to_slist("cert_map_type", strdup("rfc822"), | 
| 243 | 0 |                          TSNM_tlstmCertSANRFC822Name); | 
| 244 | 0 |     se_add_pair_to_slist("cert_map_type", strdup("dns"), | 
| 245 | 0 |                          TSNM_tlstmCertSANDNSName); | 
| 246 | 0 |     se_add_pair_to_slist("cert_map_type", strdup("any"), TSNM_tlstmCertSANAny); | 
| 247 | 0 |     se_add_pair_to_slist("cert_map_type", strdup("sn"), | 
| 248 | 0 |                          TSNM_tlstmCertSpecified); | 
| 249 |  | 
 | 
| 250 | 0 | } | 
| 251 |  |  | 
| 252 |  | void | 
| 253 |  | netsnmp_certs_shutdown(void) | 
| 254 | 0 | { | 
| 255 | 0 |     DEBUGMSGT(("cert:util:shutdown","shutdown\n")); | 
| 256 | 0 |     netsnmp_container ***c, **containers[] = { | 
| 257 | 0 |         &_tlstmParams, &_tlstmAddr, &_maps, &_certs, &_keys, NULL | 
| 258 | 0 |     }; | 
| 259 |  | 
 | 
| 260 | 0 |     for (c = containers; *c; c++) { | 
| 261 | 0 |         if (!**c) | 
| 262 | 0 |             continue; | 
| 263 | 0 |         CONTAINER_FREE_ALL(**c, NULL); | 
| 264 | 0 |         CONTAINER_FREE(**c); | 
| 265 | 0 |         **c = NULL; | 
| 266 | 0 |     } | 
| 267 | 0 |     _netsnmp_release_trustcerts(); | 
| 268 | 0 | } | 
| 269 |  |  | 
| 270 |  | void | 
| 271 |  | netsnmp_certs_load(void) | 
| 272 | 0 | { | 
| 273 | 0 |     netsnmp_iterator  *itr; | 
| 274 | 0 |     netsnmp_key        *key; | 
| 275 | 0 |     netsnmp_cert       *cert; | 
| 276 |  | 
 | 
| 277 | 0 |     DEBUGMSGT(("cert:util:init","init\n")); | 
| 278 |  | 
 | 
| 279 | 0 |     if (NULL == _certs) { | 
| 280 | 0 |         snmp_log(LOG_ERR, "cant load certs without container\n"); | 
| 281 | 0 |         return; | 
| 282 | 0 |     } | 
| 283 |  |  | 
| 284 | 0 |     if (CONTAINER_SIZE(_certs) != 0) { | 
| 285 | 0 |         DEBUGMSGT(("cert:util:init", "ignoring duplicate init\n")); | 
| 286 | 0 |         return; | 
| 287 | 0 |     } | 
| 288 |  |  | 
| 289 | 0 |     netsnmp_init_openssl(); | 
| 290 |  |  | 
| 291 |  |     /** scan config dirs for certs */ | 
| 292 | 0 |     _cert_indexes_load(); | 
| 293 |  |  | 
| 294 |  |     /** match up keys w/certs */ | 
| 295 | 0 |     itr = CONTAINER_ITERATOR(_keys); | 
| 296 | 0 |     if (NULL == itr) { | 
| 297 | 0 |         snmp_log(LOG_ERR, "could not get iterator for keys\n"); | 
| 298 | 0 |         netsnmp_certs_shutdown(); | 
| 299 | 0 |         return; | 
| 300 | 0 |     } | 
| 301 | 0 |     key = ITERATOR_FIRST(itr); | 
| 302 | 0 |     for( ; key; key = ITERATOR_NEXT(itr)) | 
| 303 | 0 |         _find_partner(NULL, key); | 
| 304 | 0 |     ITERATOR_RELEASE(itr); | 
| 305 |  | 
 | 
| 306 | 0 |     DEBUGIF("cert:dump") { | 
| 307 | 0 |         itr = CONTAINER_ITERATOR(_certs); | 
| 308 | 0 |         if (NULL == itr) { | 
| 309 | 0 |             snmp_log(LOG_ERR, "could not get iterator for certs\n"); | 
| 310 | 0 |             netsnmp_certs_shutdown(); | 
| 311 | 0 |             return; | 
| 312 | 0 |         } | 
| 313 | 0 |         cert = ITERATOR_FIRST(itr); | 
| 314 | 0 |         for( ; cert; cert = ITERATOR_NEXT(itr)) { | 
| 315 | 0 |             netsnmp_cert_load_x509(cert); | 
| 316 | 0 |         } | 
| 317 | 0 |         ITERATOR_RELEASE(itr); | 
| 318 | 0 |         DEBUGMSGT(("cert:dump", | 
| 319 | 0 |                    "-------------------- Certificates -----------------\n")); | 
| 320 | 0 |         netsnmp_cert_dump_all(); | 
| 321 | 0 |         DEBUGMSGT(("cert:dump", | 
| 322 | 0 |                    "------------------------ End ----------------------\n")); | 
| 323 | 0 |     } | 
| 324 | 0 | } | 
| 325 |  |  | 
| 326 |  | /* ##################################################################### | 
| 327 |  |  * | 
| 328 |  |  * cert container functions | 
| 329 |  |  */ | 
| 330 |  |  | 
| 331 |  | static netsnmp_container * | 
| 332 |  | _get_cert_container(const char *use) | 
| 333 | 0 | { | 
| 334 | 0 |     netsnmp_container *c; | 
| 335 |  | 
 | 
| 336 | 0 |     int rc; | 
| 337 |  | 
 | 
| 338 | 0 |     c = netsnmp_container_find("certs:binary_array"); | 
| 339 | 0 |     if (NULL == c) { | 
| 340 | 0 |         snmp_log(LOG_ERR, "could not create container for %s\n", use); | 
| 341 | 0 |         return NULL; | 
| 342 | 0 |     } | 
| 343 | 0 |     c->container_name = strdup(use); | 
| 344 | 0 |     c->free_item = (netsnmp_container_obj_func*)_cert_free; | 
| 345 | 0 |     c->compare = (netsnmp_container_compare*)_cert_compare; | 
| 346 |  | 
 | 
| 347 | 0 |     CONTAINER_SET_OPTIONS(c, CONTAINER_KEY_ALLOW_DUPLICATES, rc); | 
| 348 |  | 
 | 
| 349 | 0 |     return c; | 
| 350 | 0 | } | 
| 351 |  |  | 
| 352 |  | static void | 
| 353 |  | _setup_containers(void) | 
| 354 | 0 | { | 
| 355 | 0 |     netsnmp_container *additional_keys; | 
| 356 |  | 
 | 
| 357 | 0 |     int rc; | 
| 358 |  | 
 | 
| 359 | 0 |     _certs = _get_cert_container("netsnmp certificates"); | 
| 360 | 0 |     if (NULL == _certs) | 
| 361 | 0 |         return; | 
| 362 |  |  | 
| 363 |  |     /** additional keys: common name */ | 
| 364 | 0 |     additional_keys = netsnmp_container_find("certs_cn:binary_array"); | 
| 365 | 0 |     if (NULL == additional_keys) { | 
| 366 | 0 |         snmp_log(LOG_ERR, "could not create CN container for certificates\n"); | 
| 367 | 0 |         netsnmp_certs_shutdown(); | 
| 368 | 0 |         return; | 
| 369 | 0 |     } | 
| 370 | 0 |     additional_keys->container_name = strdup("certs_cn"); | 
| 371 | 0 |     additional_keys->free_item = NULL; | 
| 372 | 0 |     additional_keys->compare = (netsnmp_container_compare*)_cert_cn_compare; | 
| 373 | 0 |     CONTAINER_SET_OPTIONS(additional_keys, CONTAINER_KEY_ALLOW_DUPLICATES, rc); | 
| 374 | 0 |     netsnmp_container_add_index(_certs, additional_keys); | 
| 375 |  |  | 
| 376 |  |     /** additional keys: subject name */ | 
| 377 | 0 |     additional_keys = netsnmp_container_find("certs_sn:binary_array"); | 
| 378 | 0 |     if (NULL == additional_keys) { | 
| 379 | 0 |         snmp_log(LOG_ERR, "could not create SN container for certificates\n"); | 
| 380 | 0 |         netsnmp_certs_shutdown(); | 
| 381 | 0 |         return; | 
| 382 | 0 |     } | 
| 383 | 0 |     additional_keys->container_name = strdup("certs_sn"); | 
| 384 | 0 |     additional_keys->free_item = NULL; | 
| 385 | 0 |     additional_keys->compare = (netsnmp_container_compare*)_cert_sn_compare; | 
| 386 | 0 |     additional_keys->ncompare = (netsnmp_container_compare*)_cert_sn_ncompare; | 
| 387 | 0 |     CONTAINER_SET_OPTIONS(additional_keys, CONTAINER_KEY_ALLOW_DUPLICATES, rc); | 
| 388 | 0 |     netsnmp_container_add_index(_certs, additional_keys); | 
| 389 |  |  | 
| 390 |  |     /** additional keys: file name */ | 
| 391 | 0 |     additional_keys = netsnmp_container_find("certs_fn:binary_array"); | 
| 392 | 0 |     if (NULL == additional_keys) { | 
| 393 | 0 |         snmp_log(LOG_ERR, "could not create FN container for certificates\n"); | 
| 394 | 0 |         netsnmp_certs_shutdown(); | 
| 395 | 0 |         return; | 
| 396 | 0 |     } | 
| 397 | 0 |     additional_keys->container_name = strdup("certs_fn"); | 
| 398 | 0 |     additional_keys->free_item = NULL; | 
| 399 | 0 |     additional_keys->compare = (netsnmp_container_compare*)_cert_fn_compare; | 
| 400 | 0 |     additional_keys->ncompare = (netsnmp_container_compare*)_cert_fn_ncompare; | 
| 401 | 0 |     CONTAINER_SET_OPTIONS(additional_keys, CONTAINER_KEY_ALLOW_DUPLICATES, rc); | 
| 402 | 0 |     netsnmp_container_add_index(_certs, additional_keys); | 
| 403 |  | 
 | 
| 404 | 0 |     _keys = netsnmp_container_find("cert_keys:binary_array"); | 
| 405 | 0 |     if (NULL == _keys) { | 
| 406 | 0 |         snmp_log(LOG_ERR, "could not create container for certificate keys\n"); | 
| 407 | 0 |         netsnmp_certs_shutdown(); | 
| 408 | 0 |         return; | 
| 409 | 0 |     } | 
| 410 | 0 |     _keys->container_name = strdup("netsnmp certificate keys"); | 
| 411 | 0 |     _keys->free_item = (netsnmp_container_obj_func*)_key_free; | 
| 412 | 0 |     _keys->compare = (netsnmp_container_compare*)_cert_fn_compare; | 
| 413 |  | 
 | 
| 414 | 0 |     _setup_trusted_certs(); | 
| 415 | 0 | } | 
| 416 |  |  | 
| 417 |  | netsnmp_container * | 
| 418 |  | netsnmp_cert_map_container(void) | 
| 419 | 0 | { | 
| 420 | 0 |     return _maps; | 
| 421 | 0 | } | 
| 422 |  |  | 
| 423 |  | static netsnmp_cert * | 
| 424 |  | _new_cert(const char *dirname, const char *filename, int certType, int offset, | 
| 425 |  |           int allowed_uses, int hashType, const char *fingerprint, | 
| 426 |  |           const char *common_name,  const char *subject) | 
| 427 | 0 | { | 
| 428 | 0 |     netsnmp_cert    *cert; | 
| 429 |  | 
 | 
| 430 | 0 |     if ((NULL == dirname) || (NULL == filename)) { | 
| 431 | 0 |         snmp_log(LOG_ERR, "bad parameters to _new_cert\n"); | 
| 432 | 0 |         return NULL; | 
| 433 | 0 |     } | 
| 434 |  |  | 
| 435 | 0 |     cert = SNMP_MALLOC_TYPEDEF(netsnmp_cert); | 
| 436 | 0 |     if (NULL == cert) { | 
| 437 | 0 |         snmp_log(LOG_ERR,"could not allocate memory for certificate at %s/%s\n", | 
| 438 | 0 |                  dirname, filename); | 
| 439 | 0 |         return NULL; | 
| 440 | 0 |     } | 
| 441 |  |  | 
| 442 | 0 |     DEBUGMSGT(("9:cert:struct:new","new cert 0x%p for %s\n", cert, filename)); | 
| 443 |  | 
 | 
| 444 | 0 |     cert->info.dir = strdup(dirname); | 
| 445 | 0 |     cert->info.filename = strdup(filename); | 
| 446 |  |     /* only the first certificate is allowed to be a remote peer */ | 
| 447 | 0 |     cert->info.allowed_uses = allowed_uses; | 
| 448 | 0 |     cert->info.type = certType; | 
| 449 | 0 |     cert->offset = offset; | 
| 450 | 0 |     if (fingerprint) { | 
| 451 | 0 |         cert->hash_type = hashType; | 
| 452 | 0 |         cert->fingerprint = strdup(fingerprint); | 
| 453 | 0 |     } | 
| 454 | 0 |     if (common_name) | 
| 455 | 0 |         cert->common_name = strdup(common_name); | 
| 456 | 0 |     if (subject) | 
| 457 | 0 |         cert->subject = strdup(subject); | 
| 458 |  | 
 | 
| 459 | 0 |     return cert; | 
| 460 | 0 | } | 
| 461 |  |  | 
| 462 |  | static netsnmp_key * | 
| 463 |  | _new_key(const char *dirname, const char *filename) | 
| 464 | 0 | { | 
| 465 | 0 |     netsnmp_key    *key; | 
| 466 | 0 |     struct stat     fstat; | 
| 467 | 0 |     char            fn[SNMP_MAXPATH]; | 
| 468 |  | 
 | 
| 469 | 0 |     if ((NULL == dirname) || (NULL == filename)) { | 
| 470 | 0 |         snmp_log(LOG_ERR, "bad parameters to _new_key\n"); | 
| 471 | 0 |         return NULL; | 
| 472 | 0 |     } | 
| 473 |  |  | 
| 474 |  |     /** check file permissions */ | 
| 475 | 0 |     snprintf(fn, sizeof(fn), "%s/%s", dirname, filename); | 
| 476 | 0 |     if (stat(fn, &fstat) != 0) { | 
| 477 | 0 |         snmp_log(LOG_ERR, "could  not stat %s\n", fn); | 
| 478 | 0 |         return NULL; | 
| 479 | 0 |     } | 
| 480 |  |  | 
| 481 | 0 | #if !defined(_MSC_VER) && !defined(__MINGW32__) | 
| 482 | 0 |     if ((fstat.st_mode & S_IROTH) || (fstat.st_mode & S_IWOTH)) { | 
| 483 | 0 |         snmp_log(LOG_ERR, | 
| 484 | 0 |                  "refusing to read world readable or writable key %s\n", fn); | 
| 485 | 0 |         return NULL; | 
| 486 | 0 |     } | 
| 487 | 0 | #endif | 
| 488 |  |  | 
| 489 | 0 |     key = SNMP_MALLOC_TYPEDEF(netsnmp_key); | 
| 490 | 0 |     if (NULL == key) { | 
| 491 | 0 |         snmp_log(LOG_ERR, "could not allocate memory for key at %s/%s\n", | 
| 492 | 0 |                  dirname, filename); | 
| 493 | 0 |         return NULL; | 
| 494 | 0 |     } | 
| 495 |  |  | 
| 496 | 0 |     DEBUGMSGT(("cert:key:struct:new","new key %p for %s\n", key, filename)); | 
| 497 |  | 
 | 
| 498 | 0 |     key->info.type = NS_CERT_TYPE_KEY; | 
| 499 | 0 |     key->info.dir = strdup(dirname); | 
| 500 | 0 |     key->info.filename = strdup(filename); | 
| 501 | 0 |     key->info.allowed_uses = NS_CERT_IDENTITY; | 
| 502 |  | 
 | 
| 503 | 0 |     return key; | 
| 504 | 0 | } | 
| 505 |  |  | 
| 506 |  | void | 
| 507 |  | netsnmp_cert_free(netsnmp_cert *cert) | 
| 508 | 0 | { | 
| 509 | 0 |     if (NULL == cert) | 
| 510 | 0 |         return; | 
| 511 |  |  | 
| 512 | 0 |     DEBUGMSGT(("9:cert:struct:free","freeing cert %p, %s (fp %s; CN %s)\n", | 
| 513 | 0 |                cert, cert->info.filename ? cert->info.filename : "UNK", | 
| 514 | 0 |                cert->fingerprint ? cert->fingerprint : "UNK", | 
| 515 | 0 |                cert->common_name ? cert->common_name : "UNK")); | 
| 516 |  | 
 | 
| 517 | 0 |     SNMP_FREE(cert->info.dir); | 
| 518 | 0 |     SNMP_FREE(cert->info.filename); | 
| 519 | 0 |     SNMP_FREE(cert->subject); | 
| 520 | 0 |     SNMP_FREE(cert->issuer); | 
| 521 | 0 |     SNMP_FREE(cert->fingerprint); | 
| 522 | 0 |     SNMP_FREE(cert->common_name); | 
| 523 | 0 |     if (cert->ocert) | 
| 524 | 0 |         X509_free(cert->ocert); | 
| 525 | 0 |     if (cert->key && cert->key->cert == cert) | 
| 526 | 0 |         cert->key->cert = NULL; | 
| 527 |  | 
 | 
| 528 | 0 |     free(cert); /* SNMP_FREE not needed on parameters */ | 
| 529 | 0 | } | 
| 530 |  |  | 
| 531 |  | void | 
| 532 |  | netsnmp_key_free(netsnmp_key *key) | 
| 533 | 0 | { | 
| 534 | 0 |     if (NULL == key) | 
| 535 | 0 |         return; | 
| 536 |  |  | 
| 537 | 0 |     DEBUGMSGT(("cert:key:struct:free","freeing key %p, %s\n", | 
| 538 | 0 |                key, key->info.filename ? key->info.filename : "UNK")); | 
| 539 |  | 
 | 
| 540 | 0 |     SNMP_FREE(key->info.dir); | 
| 541 | 0 |     SNMP_FREE(key->info.filename); | 
| 542 | 0 |     EVP_PKEY_free(key->okey); | 
| 543 | 0 |     if (key->cert && key->cert->key == key) | 
| 544 | 0 |         key->cert->key = NULL; | 
| 545 |  | 
 | 
| 546 | 0 |     free(key); /* SNMP_FREE not needed on parameters */ | 
| 547 | 0 | } | 
| 548 |  |  | 
| 549 |  | static void | 
| 550 |  | _cert_free(netsnmp_cert *cert, void *context) | 
| 551 | 0 | { | 
| 552 | 0 |     netsnmp_cert_free(cert); | 
| 553 | 0 | } | 
| 554 |  |  | 
| 555 |  | static void | 
| 556 |  | _key_free(netsnmp_key *key, void *context) | 
| 557 | 0 | { | 
| 558 | 0 |     netsnmp_key_free(key); | 
| 559 | 0 | } | 
| 560 |  |  | 
| 561 |  | static int | 
| 562 |  | _cert_compare(netsnmp_cert *lhs, netsnmp_cert *rhs) | 
| 563 | 0 | { | 
| 564 | 0 |     netsnmp_assert((lhs != NULL) && (rhs != NULL)); | 
| 565 | 0 |     netsnmp_assert((lhs->fingerprint != NULL) && | 
| 566 | 0 |                    (rhs->fingerprint != NULL)); | 
| 567 |  |  | 
| 568 |  |     /** ignore hash type? */ | 
| 569 | 0 |     return strcmp(lhs->fingerprint, rhs->fingerprint); | 
| 570 | 0 | } | 
| 571 |  |  | 
| 572 |  | static int | 
| 573 |  | _cert_path_compare(netsnmp_cert_common *lhs, netsnmp_cert_common *rhs) | 
| 574 | 0 | { | 
| 575 | 0 |     int rc; | 
| 576 |  | 
 | 
| 577 | 0 |     netsnmp_assert((lhs != NULL) && (rhs != NULL)); | 
| 578 |  |      | 
| 579 |  |     /** dir name first */ | 
| 580 | 0 |     rc = strcmp(lhs->dir, rhs->dir); | 
| 581 | 0 |     if (rc) | 
| 582 | 0 |         return rc; | 
| 583 |  |  | 
| 584 |  |     /** filename */ | 
| 585 | 0 |     return strcmp(lhs->filename, rhs->filename); | 
| 586 | 0 | } | 
| 587 |  |  | 
| 588 |  | static int | 
| 589 |  | _cert_cn_compare(netsnmp_cert *lhs, netsnmp_cert *rhs) | 
| 590 | 0 | { | 
| 591 | 0 |     int rc; | 
| 592 | 0 |     const char *lhcn, *rhcn; | 
| 593 |  | 
 | 
| 594 | 0 |     netsnmp_assert((lhs != NULL) && (rhs != NULL)); | 
| 595 |  | 
 | 
| 596 | 0 |     if (NULL == lhs->common_name) | 
| 597 | 0 |         lhcn = ""; | 
| 598 | 0 |     else | 
| 599 | 0 |         lhcn = lhs->common_name; | 
| 600 | 0 |     if (NULL == rhs->common_name) | 
| 601 | 0 |         rhcn = ""; | 
| 602 | 0 |     else | 
| 603 | 0 |         rhcn = rhs->common_name; | 
| 604 |  | 
 | 
| 605 | 0 |     rc = strcmp(lhcn, rhcn); | 
| 606 | 0 |     if (rc) | 
| 607 | 0 |         return rc; | 
| 608 |  |  | 
| 609 |  |     /** in case of equal common names, sub-sort by path */ | 
| 610 | 0 |     return _cert_path_compare((netsnmp_cert_common*)lhs, | 
| 611 | 0 |                               (netsnmp_cert_common*)rhs); | 
| 612 | 0 | } | 
| 613 |  |  | 
| 614 |  | static int | 
| 615 |  | _cert_sn_compare(netsnmp_cert *lhs, netsnmp_cert *rhs) | 
| 616 | 0 | { | 
| 617 | 0 |     int rc; | 
| 618 | 0 |     const char *lhsn, *rhsn; | 
| 619 |  | 
 | 
| 620 | 0 |     netsnmp_assert((lhs != NULL) && (rhs != NULL)); | 
| 621 |  | 
 | 
| 622 | 0 |     if (NULL == lhs->subject) | 
| 623 | 0 |         lhsn = ""; | 
| 624 | 0 |     else | 
| 625 | 0 |         lhsn = lhs->subject; | 
| 626 | 0 |     if (NULL == rhs->subject) | 
| 627 | 0 |         rhsn = ""; | 
| 628 | 0 |     else | 
| 629 | 0 |         rhsn = rhs->subject; | 
| 630 |  | 
 | 
| 631 | 0 |     rc = strcmp(lhsn, rhsn); | 
| 632 | 0 |     if (rc) | 
| 633 | 0 |         return rc; | 
| 634 |  |  | 
| 635 |  |     /** in case of equal common names, sub-sort by path */ | 
| 636 | 0 |     return _cert_path_compare((netsnmp_cert_common*)lhs, | 
| 637 | 0 |                               (netsnmp_cert_common*)rhs); | 
| 638 | 0 | } | 
| 639 |  |  | 
| 640 |  | static int | 
| 641 |  | _cert_fn_compare(netsnmp_cert_common *lhs, netsnmp_cert_common *rhs) | 
| 642 | 0 | { | 
| 643 | 0 |     int rc; | 
| 644 |  | 
 | 
| 645 | 0 |     netsnmp_assert((lhs != NULL) && (rhs != NULL)); | 
| 646 |  | 
 | 
| 647 | 0 |     rc = strcmp(lhs->filename, rhs->filename); | 
| 648 | 0 |     if (rc) | 
| 649 | 0 |         return rc; | 
| 650 |  |  | 
| 651 |  |     /** in case of equal common names, sub-sort by dir */ | 
| 652 | 0 |     return strcmp(lhs->dir, rhs->dir); | 
| 653 | 0 | } | 
| 654 |  |  | 
| 655 |  | static int | 
| 656 |  | _cert_fn_ncompare(netsnmp_cert_common *lhs, netsnmp_cert_common *rhs) | 
| 657 | 0 | { | 
| 658 | 0 |     netsnmp_assert((lhs != NULL) && (rhs != NULL)); | 
| 659 | 0 |     netsnmp_assert((lhs->filename != NULL) && (rhs->filename != NULL)); | 
| 660 |  | 
 | 
| 661 | 0 |     return strncmp(lhs->filename, rhs->filename, strlen(rhs->filename)); | 
| 662 | 0 | } | 
| 663 |  |  | 
| 664 |  | static int | 
| 665 |  | _cert_sn_ncompare(netsnmp_cert *lhs, netsnmp_cert *rhs) | 
| 666 | 0 | { | 
| 667 | 0 |     netsnmp_assert((lhs != NULL) && (rhs != NULL)); | 
| 668 | 0 |     netsnmp_assert((lhs->subject != NULL) && (rhs->subject != NULL)); | 
| 669 |  | 
 | 
| 670 | 0 |     return strncmp(lhs->subject, rhs->subject, strlen(rhs->subject)); | 
| 671 | 0 | } | 
| 672 |  |  | 
| 673 |  | static int | 
| 674 |  | _cert_ext_type(const char *ext) | 
| 675 | 0 | { | 
| 676 | 0 |     int rc = se_find_value_in_slist("cert_types", ext); | 
| 677 | 0 |     if (SE_DNE == rc) | 
| 678 | 0 |         return NS_CERT_TYPE_UNKNOWN; | 
| 679 | 0 |     return rc; | 
| 680 | 0 | } | 
| 681 |  |  | 
| 682 |  | static int | 
| 683 |  | _type_from_filename(const char *filename) | 
| 684 | 0 | { | 
| 685 | 0 |     char     *pos; | 
| 686 | 0 |     int       type; | 
| 687 |  | 
 | 
| 688 | 0 |     if (NULL == filename) | 
| 689 | 0 |         return NS_CERT_TYPE_UNKNOWN; | 
| 690 |  |  | 
| 691 | 0 |     pos = strrchr(filename, '.'); | 
| 692 | 0 |     if (NULL == pos) | 
| 693 | 0 |         return NS_CERT_TYPE_UNKNOWN; | 
| 694 |  |  | 
| 695 | 0 |     type = _cert_ext_type(++pos); | 
| 696 | 0 |     return type; | 
| 697 | 0 | } | 
| 698 |  |  | 
| 699 |  | /* | 
| 700 |  |  * filter functions; return 1 to include file, 0 to exclude | 
| 701 |  |  */ | 
| 702 |  | static int _cert_cert_filter(const void *text, void *ctx) | 
| 703 | 0 | { | 
| 704 | 0 |     const char *filename = text; | 
| 705 | 0 |     int  len = strlen(filename); | 
| 706 | 0 |     const char *pos; | 
| 707 |  | 
 | 
| 708 | 0 |     if (len < 5) /* shortest name: x.YYY */ | 
| 709 | 0 |         return 0; | 
| 710 |  |  | 
| 711 | 0 |     pos = strrchr(filename, '.'); | 
| 712 | 0 |     if (NULL == pos) | 
| 713 | 0 |         return 0; | 
| 714 |  |  | 
| 715 | 0 |     if (_cert_ext_type(++pos) != NS_CERT_TYPE_UNKNOWN) | 
| 716 | 0 |         return 1; | 
| 717 |  |  | 
| 718 | 0 |     return 0; | 
| 719 | 0 | } | 
| 720 |  |  | 
| 721 |  | /* ##################################################################### | 
| 722 |  |  * | 
| 723 |  |  * cert index functions | 
| 724 |  |  * | 
| 725 |  |  * This code mimics what the mib index code does. The persistent | 
| 726 |  |  * directory will have a subdirectory named 'cert_indexes'. Inside | 
| 727 |  |  * this directory will be some number of files with ascii numeric | 
| 728 |  |  * names (0, 1, 2, etc). Each of these files will start with a line | 
| 729 |  |  * with the text "DIR ", followed by a directory name. The rest of the | 
| 730 |  |  * file will be certificate fields and the certificate file name, one | 
| 731 |  |  * certificate per line. The numeric file name is the integer 'directory | 
| 732 |  |  * index'. | 
| 733 |  |  */ | 
| 734 |  |  | 
| 735 |  | /** | 
| 736 |  |  * _certindex_add | 
| 737 |  |  * | 
| 738 |  |  * add a directory name to the indexes | 
| 739 |  |  */ | 
| 740 |  | static int | 
| 741 |  | _certindex_add( const char *dirname, int i ) | 
| 742 | 0 | { | 
| 743 | 0 |     int rc; | 
| 744 | 0 |     char *dirname_copy = strdup(dirname); | 
| 745 |  | 
 | 
| 746 | 0 |     if ( i == -1 ) { | 
| 747 | 0 |         int max = se_find_free_value_in_list(_certindexes); | 
| 748 | 0 |         if (SE_DNE == max) | 
| 749 | 0 |             i = 0; | 
| 750 | 0 |         else | 
| 751 | 0 |             i = max; | 
| 752 | 0 |     } | 
| 753 |  | 
 | 
| 754 | 0 |     DEBUGMSGT(("cert:index:add","dir %s at index %d\n", dirname, i )); | 
| 755 | 0 |     rc = se_add_pair_to_list(&_certindexes, dirname_copy, i); | 
| 756 | 0 |     if (SE_OK != rc) { | 
| 757 | 0 |         snmp_log(LOG_ERR, "adding certindex dirname failed; " | 
| 758 | 0 |                  "%d (%s) not added\n", i, dirname); | 
| 759 | 0 |         return -1; | 
| 760 | 0 |     } | 
| 761 |  |  | 
| 762 | 0 |     return i; | 
| 763 | 0 | } | 
| 764 |  |  | 
| 765 |  | /** | 
| 766 |  |  * _certindex_load | 
| 767 |  |  * | 
| 768 |  |  * read in the existing indexes | 
| 769 |  |  */ | 
| 770 |  | static void | 
| 771 |  | _certindexes_load( void ) | 
| 772 | 0 | { | 
| 773 | 0 |     DIR *dir; | 
| 774 | 0 |     struct dirent *file; | 
| 775 | 0 |     FILE *fp; | 
| 776 | 0 |     char filename[SNMP_MAXPATH], line[300]; | 
| 777 | 0 |     int  i; | 
| 778 | 0 |     char *cp, *pos; | 
| 779 |  |  | 
| 780 |  |     /* | 
| 781 |  |      * Open the CERT index directory, or create it (empty) | 
| 782 |  |      */ | 
| 783 | 0 |     snprintf( filename, sizeof(filename), "%s/cert_indexes", | 
| 784 | 0 |               get_persistent_directory()); | 
| 785 | 0 |     filename[sizeof(filename)-1] = 0; | 
| 786 | 0 |     dir = opendir( filename ); | 
| 787 | 0 |     if ( dir == NULL ) { | 
| 788 | 0 |         DEBUGMSGT(("cert:index:load", | 
| 789 | 0 |                    "creating new cert_indexes directory\n")); | 
| 790 | 0 |         mkdirhier( filename, NETSNMP_AGENT_DIRECTORY_MODE, 0); | 
| 791 | 0 |         return; | 
| 792 | 0 |     } | 
| 793 |  |  | 
| 794 |  |     /* | 
| 795 |  |      * Create a list of which directory each file refers to | 
| 796 |  |      */ | 
| 797 | 0 |     while ((file = readdir( dir ))) { | 
| 798 | 0 |         if ( !isdigit(0xFF & file->d_name[0])) | 
| 799 | 0 |             continue; | 
| 800 | 0 |         i = atoi( file->d_name ); | 
| 801 |  | 
 | 
| 802 | 0 |         snprintf( filename, sizeof(filename), "%s/cert_indexes/%d", | 
| 803 | 0 |               get_persistent_directory(), i ); | 
| 804 | 0 |         filename[sizeof(filename)-1] = 0; | 
| 805 | 0 |         fp = fopen( filename, "r" ); | 
| 806 | 0 |         if ( !fp ) { | 
| 807 | 0 |             DEBUGMSGT(("cert:index:load", "error opening index (%d)\n", i)); | 
| 808 | 0 |             continue; | 
| 809 | 0 |         } | 
| 810 | 0 |         cp = fgets( line, sizeof(line), fp ); | 
| 811 | 0 |         if ( cp ) { | 
| 812 | 0 |             line[strlen(line)-1] = 0; | 
| 813 | 0 |             pos = strrchr(line, ' '); | 
| 814 | 0 |             if (pos) | 
| 815 | 0 |                 *pos = '\0'; | 
| 816 | 0 |             DEBUGMSGT(("9:cert:index:load","adding (%d) %s\n", i, line)); | 
| 817 | 0 |             (void)_certindex_add( line+4, i );  /* Skip 'DIR ' */ | 
| 818 | 0 |         } else { | 
| 819 | 0 |             DEBUGMSGT(("cert:index:load", "Empty index (%d)\n", i)); | 
| 820 | 0 |         } | 
| 821 | 0 |         fclose( fp ); | 
| 822 | 0 |     } | 
| 823 | 0 |     closedir( dir ); | 
| 824 | 0 | } | 
| 825 |  |  | 
| 826 |  | /** | 
| 827 |  |  * _certindex_lookup | 
| 828 |  |  * | 
| 829 |  |  * find index for a directory | 
| 830 |  |  */ | 
| 831 |  | static char * | 
| 832 |  | _certindex_lookup( const char *dirname ) | 
| 833 | 0 | { | 
| 834 | 0 |     int i; | 
| 835 | 0 |     char filename[SNMP_MAXPATH]; | 
| 836 |  |  | 
| 837 |  | 
 | 
| 838 | 0 |     i = se_find_value_in_list(_certindexes, dirname); | 
| 839 | 0 |     if (SE_DNE == i) { | 
| 840 | 0 |         DEBUGMSGT(("9:cert:index:lookup","%s : (none)\n", dirname)); | 
| 841 | 0 |         return NULL; | 
| 842 | 0 |     } | 
| 843 |  |  | 
| 844 | 0 |     snprintf(filename, sizeof(filename), "%s/cert_indexes/%d", | 
| 845 | 0 |              get_persistent_directory(), i); | 
| 846 | 0 |     filename[sizeof(filename)-1] = 0; | 
| 847 | 0 |     DEBUGMSGT(("cert:index:lookup", "%s (%d) %s\n", dirname, i, filename )); | 
| 848 | 0 |     return strdup(filename); | 
| 849 | 0 | } | 
| 850 |  |  | 
| 851 |  | static FILE * | 
| 852 |  | _certindex_new( const char *dirname ) | 
| 853 | 0 | { | 
| 854 | 0 |     FILE *fp; | 
| 855 | 0 |     char  filename[SNMP_MAXPATH], *cp; | 
| 856 | 0 |     int   i; | 
| 857 |  | 
 | 
| 858 | 0 |     cp = _certindex_lookup( dirname ); | 
| 859 | 0 |     if (!cp) { | 
| 860 | 0 |         i  = _certindex_add( dirname, -1 ); | 
| 861 | 0 |         if (-1 == i) | 
| 862 | 0 |             return NULL; /* msg already logged */ | 
| 863 | 0 |         snprintf( filename, sizeof(filename), "%s/cert_indexes/%d", | 
| 864 | 0 |                   get_persistent_directory(), i ); | 
| 865 | 0 |         filename[sizeof(filename)-1] = 0; | 
| 866 | 0 |         cp = filename; | 
| 867 | 0 |     } | 
| 868 | 0 |     DEBUGMSGT(("9:cert:index:new", "%s (%s)\n", dirname, cp )); | 
| 869 | 0 |     fp = fopen( cp, "w" ); | 
| 870 | 0 |     if (fp) | 
| 871 | 0 |         fprintf( fp, "DIR %s %d\n", dirname, CERT_INDEX_FORMAT ); | 
| 872 | 0 |     else | 
| 873 | 0 |         DEBUGMSGTL(("cert:index", "error opening new index file %s\n", dirname)); | 
| 874 |  | 
 | 
| 875 | 0 |     if (cp != filename) | 
| 876 | 0 |         free(cp); | 
| 877 |  | 
 | 
| 878 | 0 |     return fp; | 
| 879 | 0 | } | 
| 880 |  |  | 
| 881 |  | /* ##################################################################### | 
| 882 |  |  * | 
| 883 |  |  * certificate utility functions | 
| 884 |  |  * | 
| 885 |  |  */ | 
| 886 |  | static BIO * | 
| 887 |  | netsnmp_open_bio(const char *dir, const char *filename) | 
| 888 | 0 | { | 
| 889 | 0 |     BIO            *certbio; | 
| 890 | 0 |     char            file[SNMP_MAXPATH]; | 
| 891 |  | 
 | 
| 892 | 0 |     DEBUGMSGT(("9:cert:read", "Checking file %s\n", filename)); | 
| 893 |  | 
 | 
| 894 | 0 |     certbio = BIO_new(BIO_s_file()); | 
| 895 | 0 |     if (NULL == certbio) { | 
| 896 | 0 |         snmp_log(LOG_ERR, "error creating BIO\n"); | 
| 897 | 0 |         return NULL; | 
| 898 | 0 |     } | 
| 899 |  |  | 
| 900 | 0 |     snprintf(file, sizeof(file),"%s/%s", dir, filename); | 
| 901 | 0 |     if (BIO_read_filename(certbio, file) <=0) { | 
| 902 | 0 |         snmp_log(LOG_ERR, "error reading certificate/key %s into BIO\n", file); | 
| 903 | 0 |         BIO_vfree(certbio); | 
| 904 | 0 |         return NULL; | 
| 905 | 0 |     } | 
| 906 |  |  | 
| 907 | 0 |     return certbio; | 
| 908 | 0 | } | 
| 909 |  |  | 
| 910 |  | static void | 
| 911 |  | netsnmp_ocert_parse(netsnmp_cert *cert, X509 *ocert) | 
| 912 | 0 | { | 
| 913 | 0 |     int             is_ca; | 
| 914 |  | 
 | 
| 915 | 0 |     cert->ocert = ocert; | 
| 916 |  |  | 
| 917 |  |     /* | 
| 918 |  |      * X509_check_ca return codes: | 
| 919 |  |      * 0 not a CA | 
| 920 |  |      * 1 is a CA | 
| 921 |  |      * 2 basicConstraints absent so "maybe" a CA | 
| 922 |  |      * 3 basicConstraints absent but self signed V1. | 
| 923 |  |      * 4 basicConstraints absent but keyUsage present and keyCertSign asserted. | 
| 924 |  |      * 5 outdated Netscape Certificate Type CA extension. | 
| 925 |  |      */ | 
| 926 | 0 |     is_ca = X509_check_ca(ocert); | 
| 927 | 0 |     if (1 == is_ca) | 
| 928 | 0 |         cert->info.allowed_uses |= NS_CERT_CA; | 
| 929 |  | 
 | 
| 930 | 0 |     if (NULL == cert->subject) { | 
| 931 | 0 |         cert->subject = X509_NAME_oneline(X509_get_subject_name(ocert), NULL, | 
| 932 | 0 |                                           0); | 
| 933 | 0 |         DEBUGMSGT(("9:cert:add:subject", "subject name: %s\n", cert->subject)); | 
| 934 | 0 |     } | 
| 935 |  | 
 | 
| 936 | 0 |     if (NULL == cert->issuer) { | 
| 937 | 0 |         cert->issuer = X509_NAME_oneline(X509_get_issuer_name(ocert), NULL, 0); | 
| 938 | 0 |         if (strcmp(cert->subject, cert->issuer) == 0) { | 
| 939 | 0 |             free(cert->issuer); | 
| 940 | 0 |             cert->issuer = strdup("self-signed"); | 
| 941 | 0 |         } | 
| 942 | 0 |         DEBUGMSGT(("9:cert:add:issuer", "CA issuer: %s\n", cert->issuer)); | 
| 943 | 0 |     } | 
| 944 |  | 
 | 
| 945 | 0 |     if (NULL == cert->fingerprint) { | 
| 946 | 0 |         cert->hash_type = netsnmp_openssl_cert_get_hash_type(ocert); | 
| 947 | 0 |         cert->fingerprint = | 
| 948 | 0 |             netsnmp_openssl_cert_get_fingerprint(ocert, cert->hash_type); | 
| 949 | 0 |     } | 
| 950 |  | 
 | 
| 951 | 0 |     if (NULL == cert->common_name) { | 
| 952 | 0 |         cert->common_name =netsnmp_openssl_cert_get_commonName(ocert, NULL, | 
| 953 | 0 |                                                                NULL); | 
| 954 | 0 |         DEBUGMSGT(("9:cert:add:name","%s\n", cert->common_name)); | 
| 955 | 0 |     } | 
| 956 |  | 
 | 
| 957 | 0 | } | 
| 958 |  |  | 
| 959 |  | static X509 * | 
| 960 |  | netsnmp_ocert_get(netsnmp_cert *cert) | 
| 961 | 0 | { | 
| 962 | 0 |     BIO            *certbio; | 
| 963 | 0 |     X509           *ocert = NULL; | 
| 964 | 0 |     X509           *ncert = NULL; | 
| 965 | 0 |     EVP_PKEY       *okey = NULL; | 
| 966 |  | 
 | 
| 967 | 0 |     if (NULL == cert) | 
| 968 | 0 |         return NULL; | 
| 969 |  |  | 
| 970 | 0 |     if (cert->ocert) | 
| 971 | 0 |         return cert->ocert; | 
| 972 |  |  | 
| 973 | 0 |     if (NS_CERT_TYPE_UNKNOWN == cert->info.type) { | 
| 974 | 0 |         cert->info.type = _type_from_filename(cert->info.filename); | 
| 975 | 0 |         if (NS_CERT_TYPE_UNKNOWN == cert->info.type) { | 
| 976 | 0 |             snmp_log(LOG_ERR, "unknown certificate type %d for %s\n", | 
| 977 | 0 |                      cert->info.type, cert->info.filename); | 
| 978 | 0 |             return NULL; | 
| 979 | 0 |         } | 
| 980 | 0 |     } | 
| 981 |  |  | 
| 982 | 0 |     certbio = netsnmp_open_bio(cert->info.dir, cert->info.filename); | 
| 983 | 0 |     if (!certbio) { | 
| 984 | 0 |         return NULL; | 
| 985 | 0 |     } | 
| 986 |  |  | 
| 987 | 0 |     switch (cert->info.type) { | 
| 988 |  |  | 
| 989 | 0 |         case NS_CERT_TYPE_DER: | 
| 990 | 0 |             (void)BIO_seek(certbio, cert->offset); | 
| 991 | 0 |             ocert = d2i_X509_bio(certbio,NULL); /* DER/ASN1 */ | 
| 992 | 0 |             if (NULL != ocert) | 
| 993 | 0 |                 break; | 
| 994 |  |             /* Check for PEM if DER didn't work */ | 
| 995 | 0 |             NETSNMP_FALLTHROUGH; | 
| 996 |  |  | 
| 997 | 0 |         case NS_CERT_TYPE_PEM: | 
| 998 | 0 |             (void)BIO_seek(certbio, cert->offset); | 
| 999 | 0 |             ocert = ncert = PEM_read_bio_X509_AUX(certbio, NULL, NULL, NULL); | 
| 1000 | 0 |             if (NULL == ocert) | 
| 1001 | 0 |                 break; | 
| 1002 | 0 |             if (NS_CERT_TYPE_DER == cert->info.type) { | 
| 1003 | 0 |                 DEBUGMSGT(("9:cert:read", "Changing type from DER to PEM\n")); | 
| 1004 | 0 |                 cert->info.type = NS_CERT_TYPE_PEM; | 
| 1005 | 0 |             } | 
| 1006 |  |             /** check for private key too, but only if we're the first certificate */ | 
| 1007 | 0 |             if (0 == cert->offset && NULL == cert->key) { | 
| 1008 | 0 |                 okey = PEM_read_bio_PrivateKey(certbio, NULL, NULL, NULL); | 
| 1009 | 0 |                 if (NULL != okey) { | 
| 1010 | 0 |                     netsnmp_key  *key; | 
| 1011 | 0 |                     DEBUGMSGT(("cert:read:key", "found key with cert in %s\n", | 
| 1012 | 0 |                                cert->info.filename)); | 
| 1013 | 0 |                     key = _new_key(cert->info.dir, cert->info.filename); | 
| 1014 | 0 |                     if (NULL != key) { | 
| 1015 | 0 |                         key->okey = okey; | 
| 1016 | 0 |                         if (-1 == CONTAINER_INSERT(_keys, key)) { | 
| 1017 | 0 |                             DEBUGMSGT(("cert:read:key:add", | 
| 1018 | 0 |                                        "error inserting key into container\n")); | 
| 1019 | 0 |                             netsnmp_key_free(key); | 
| 1020 | 0 |                             key = NULL; | 
| 1021 | 0 |                         } | 
| 1022 | 0 |                         else { | 
| 1023 | 0 |                             DEBUGMSGT(("cert:read:partner", "%s match found!\n", | 
| 1024 | 0 |                                        cert->info.filename)); | 
| 1025 | 0 |                             key->cert = cert; | 
| 1026 | 0 |                             cert->key = key; | 
| 1027 | 0 |                             cert->info.allowed_uses |= NS_CERT_IDENTITY; | 
| 1028 | 0 |                         } | 
| 1029 | 0 |                     } | 
| 1030 | 0 |                 } /* null return from read */ | 
| 1031 | 0 |             } /* null key */ | 
| 1032 | 0 |             break; | 
| 1033 |  | #ifdef CERT_PKCS12_SUPPORT_MAYBE_LATER | 
| 1034 |  |         case NS_CERT_TYPE_PKCS12: | 
| 1035 |  |             (void)BIO_seek(certbio, cert->offset); | 
| 1036 |  |             PKCS12 *p12 = d2i_PKCS12_bio(certbio, NULL); | 
| 1037 |  |             if ( (NULL != p12) && (PKCS12_verify_mac(p12, "", 0) || | 
| 1038 |  |                                    PKCS12_verify_mac(p12, NULL, 0))) | 
| 1039 |  |                 PKCS12_parse(p12, "", NULL, &cert, NULL); | 
| 1040 |  |             break; | 
| 1041 |  | #endif | 
| 1042 | 0 |         default: | 
| 1043 | 0 |             snmp_log(LOG_ERR, "unknown certificate type %d for %s\n", | 
| 1044 | 0 |                      cert->info.type, cert->info.filename); | 
| 1045 | 0 |     } | 
| 1046 |  |  | 
| 1047 | 0 |     BIO_vfree(certbio); | 
| 1048 |  | 
 | 
| 1049 | 0 |     if (NULL == ocert) { | 
| 1050 | 0 |         snmp_log(LOG_ERR, "error parsing certificate file %s\n", | 
| 1051 | 0 |                  cert->info.filename); | 
| 1052 | 0 |         return NULL; | 
| 1053 | 0 |     } | 
| 1054 |  |  | 
| 1055 | 0 |     netsnmp_ocert_parse(cert, ocert); | 
| 1056 |  | 
 | 
| 1057 | 0 |     return ocert; | 
| 1058 | 0 | } | 
| 1059 |  |  | 
| 1060 |  | EVP_PKEY * | 
| 1061 |  | netsnmp_okey_get(netsnmp_key  *key) | 
| 1062 | 0 | { | 
| 1063 | 0 |     BIO            *keybio; | 
| 1064 | 0 |     EVP_PKEY       *okey; | 
| 1065 |  | 
 | 
| 1066 | 0 |     if (NULL == key) | 
| 1067 | 0 |         return NULL; | 
| 1068 |  |  | 
| 1069 | 0 |     if (key->okey) | 
| 1070 | 0 |         return key->okey; | 
| 1071 |  |  | 
| 1072 | 0 |     keybio = netsnmp_open_bio(key->info.dir, key->info.filename); | 
| 1073 | 0 |     if (!keybio) { | 
| 1074 | 0 |         return NULL; | 
| 1075 | 0 |     } | 
| 1076 |  |  | 
| 1077 | 0 |     okey = PEM_read_bio_PrivateKey(keybio, NULL, NULL, NULL); | 
| 1078 | 0 |     if (NULL == okey) | 
| 1079 | 0 |         snmp_log(LOG_ERR, "error parsing certificate file %s\n", | 
| 1080 | 0 |                  key->info.filename); | 
| 1081 | 0 |     else | 
| 1082 | 0 |         key->okey = okey; | 
| 1083 |  | 
 | 
| 1084 | 0 |     BIO_vfree(keybio); | 
| 1085 |  | 
 | 
| 1086 | 0 |     return okey; | 
| 1087 | 0 | } | 
| 1088 |  |  | 
| 1089 |  | static netsnmp_cert * | 
| 1090 |  | _find_issuer(netsnmp_cert *cert) | 
| 1091 | 0 | { | 
| 1092 | 0 |     netsnmp_void_array *matching; | 
| 1093 | 0 |     netsnmp_cert       *candidate, *issuer = NULL; | 
| 1094 | 0 |     int                 i; | 
| 1095 |  | 
 | 
| 1096 | 0 |     if ((NULL == cert) || (NULL == cert->issuer)) | 
| 1097 | 0 |         return NULL; | 
| 1098 |  |  | 
| 1099 |  |     /** find matching subject names */ | 
| 1100 |  |  | 
| 1101 | 0 |     matching = _cert_find_subset_sn(cert->issuer); | 
| 1102 | 0 |     if (NULL == matching) | 
| 1103 | 0 |         return NULL; | 
| 1104 |  |  | 
| 1105 |  |     /** check each to see if it's the issuer */ | 
| 1106 | 0 |     for ( i=0; (NULL == issuer) && (i < matching->size); ++i) { | 
| 1107 |  |         /** make sure we have ocert */ | 
| 1108 | 0 |         candidate = (netsnmp_cert*)matching->array[i]; | 
| 1109 | 0 |         if ((NULL == candidate->ocert) && | 
| 1110 | 0 |             (netsnmp_ocert_get(candidate) == NULL)) | 
| 1111 | 0 |             continue; | 
| 1112 |  |  | 
| 1113 |  |         /** compare **/ | 
| 1114 | 0 |         if (netsnmp_openssl_cert_issued_by(candidate->ocert, cert->ocert)) | 
| 1115 | 0 |             issuer = candidate; | 
| 1116 | 0 |     } /** candidate loop */ | 
| 1117 |  | 
 | 
| 1118 | 0 |     free(matching->array); | 
| 1119 | 0 |     free(matching); | 
| 1120 |  | 
 | 
| 1121 | 0 |     return issuer; | 
| 1122 | 0 | } | 
| 1123 |  |  | 
| 1124 | 0 | #define CERT_LOAD_OK       0 | 
| 1125 | 0 | #define CERT_LOAD_ERR     -1 | 
| 1126 | 0 | #define CERT_LOAD_PARTIAL -2 | 
| 1127 |  | int | 
| 1128 |  | netsnmp_cert_load_x509(netsnmp_cert *cert) | 
| 1129 | 0 | { | 
| 1130 | 0 |     int rc = CERT_LOAD_OK; | 
| 1131 |  |  | 
| 1132 |  |     /** load ocert */ | 
| 1133 | 0 |     if ((NULL == cert->ocert) && (netsnmp_ocert_get(cert) == NULL)) { | 
| 1134 | 0 |         DEBUGMSGT(("cert:load:err", "couldn't load cert for %s\n", | 
| 1135 | 0 |                    cert->info.filename)); | 
| 1136 | 0 |         rc = CERT_LOAD_ERR; | 
| 1137 | 0 |     } | 
| 1138 |  |  | 
| 1139 |  |     /** load key */ | 
| 1140 | 0 |     if ((NULL != cert->key) && (NULL == cert->key->okey) && | 
| 1141 | 0 |         (netsnmp_okey_get(cert->key) == NULL)) { | 
| 1142 | 0 |         DEBUGMSGT(("cert:load:err", "couldn't load key for cert %s\n", | 
| 1143 | 0 |                    cert->info.filename)); | 
| 1144 | 0 |         rc = CERT_LOAD_ERR; | 
| 1145 | 0 |     } | 
| 1146 |  |  | 
| 1147 |  |     /** make sure we have cert chain */ | 
| 1148 | 0 |     for (; cert && cert->issuer; cert = cert->issuer_cert) { | 
| 1149 |  |         /** skip self signed */ | 
| 1150 | 0 |         if (strcmp(cert->issuer, "self-signed") == 0) { | 
| 1151 | 0 |             netsnmp_assert(cert->issuer_cert == NULL); | 
| 1152 | 0 |             break; | 
| 1153 | 0 |         } | 
| 1154 |  |         /** get issuer cert */ | 
| 1155 | 0 |         if (NULL == cert->issuer_cert) { | 
| 1156 | 0 |             cert->issuer_cert =  _find_issuer(cert); | 
| 1157 | 0 |             if (NULL == cert->issuer_cert) { | 
| 1158 | 0 |                 DEBUGMSGT(("cert:load:warn", | 
| 1159 | 0 |                            "couldn't load full CA chain for cert %s\n", | 
| 1160 | 0 |                            cert->info.filename)); | 
| 1161 | 0 |                 rc = CERT_LOAD_PARTIAL; | 
| 1162 | 0 |                 break; | 
| 1163 | 0 |             } | 
| 1164 | 0 |         } | 
| 1165 |  |         /** get issuer ocert */ | 
| 1166 | 0 |         if ((NULL == cert->issuer_cert->ocert) && | 
| 1167 | 0 |             (netsnmp_ocert_get(cert->issuer_cert) == NULL)) { | 
| 1168 | 0 |             DEBUGMSGT(("cert:load:warn", "couldn't load full cert chain for %s\n", | 
| 1169 | 0 |                        cert->info.filename)); | 
| 1170 | 0 |             rc = CERT_LOAD_PARTIAL; | 
| 1171 | 0 |             break; | 
| 1172 | 0 |         } | 
| 1173 | 0 |     } /* cert CA for loop */ | 
| 1174 |  | 
 | 
| 1175 | 0 |     return rc; | 
| 1176 | 0 | } | 
| 1177 |  |  | 
| 1178 |  | static void | 
| 1179 |  | _find_partner(netsnmp_cert *cert, netsnmp_key *key) | 
| 1180 | 0 | { | 
| 1181 | 0 |     netsnmp_void_array *matching = NULL; | 
| 1182 | 0 |     char                filename[NAME_MAX], *pos; | 
| 1183 |  | 
 | 
| 1184 | 0 |     if ((cert && key) || (!cert && ! key)) { | 
| 1185 | 0 |         DEBUGMSGT(("cert:partner", "bad parameters searching for partner\n")); | 
| 1186 | 0 |         return; | 
| 1187 | 0 |     } | 
| 1188 |  |  | 
| 1189 | 0 |     if (key) { | 
| 1190 | 0 |         if (key->cert) { | 
| 1191 | 0 |             DEBUGMSGT(("cert:partner", "key already has partner\n")); | 
| 1192 | 0 |             return; | 
| 1193 | 0 |         } | 
| 1194 | 0 |         DEBUGMSGT(("9:cert:partner", "%s looking for partner near %s\n", | 
| 1195 | 0 |                    key->info.filename, key->info.dir)); | 
| 1196 | 0 |         snprintf(filename, sizeof(filename), "%s", key->info.filename); | 
| 1197 | 0 |         pos = strrchr(filename, '.'); | 
| 1198 | 0 |         if (NULL == pos) | 
| 1199 | 0 |             return; | 
| 1200 | 0 |         *pos = 0; | 
| 1201 |  | 
 | 
| 1202 | 0 |         matching = _cert_reduce_subset_first(_cert_find_subset_fn( filename, | 
| 1203 | 0 |                                              key->info.dir )); | 
| 1204 | 0 |         if (!matching) | 
| 1205 | 0 |             return; | 
| 1206 | 0 |         if (1 == matching->size) { | 
| 1207 | 0 |             cert = (netsnmp_cert*)matching->array[0]; | 
| 1208 | 0 |             if (NULL == cert->key) { | 
| 1209 | 0 |                 DEBUGMSGT(("cert:partner", "%s match found!\n", | 
| 1210 | 0 |                            cert->info.filename)); | 
| 1211 | 0 |                 key->cert = cert; | 
| 1212 | 0 |                 cert->key = key; | 
| 1213 | 0 |                 cert->info.allowed_uses |= NS_CERT_IDENTITY; | 
| 1214 | 0 |             } | 
| 1215 | 0 |             else if (cert->key != key) | 
| 1216 | 0 |                 snmp_log(LOG_ERR, "%s matching cert already has partner\n", | 
| 1217 | 0 |                          cert->info.filename); | 
| 1218 | 0 |         } | 
| 1219 | 0 |         else | 
| 1220 | 0 |             DEBUGMSGT(("cert:partner", "%s matches multiple certs\n", | 
| 1221 | 0 |                           key->info.filename)); | 
| 1222 | 0 |     } | 
| 1223 | 0 |     else if (cert) { | 
| 1224 | 0 |         if (cert->key) { | 
| 1225 | 0 |             DEBUGMSGT(("cert:partner", "cert already has partner\n")); | 
| 1226 | 0 |             return; | 
| 1227 | 0 |         } | 
| 1228 | 0 |         DEBUGMSGT(("9:cert:partner", "%s looking for partner\n", | 
| 1229 | 0 |                    cert->info.filename)); | 
| 1230 | 0 |         snprintf(filename, sizeof(filename), "%s", cert->info.filename); | 
| 1231 | 0 |         pos = strrchr(filename, '.'); | 
| 1232 | 0 |         if (NULL == pos) | 
| 1233 | 0 |             return; | 
| 1234 | 0 |         *pos = 0; | 
| 1235 |  | 
 | 
| 1236 | 0 |         matching = _key_find_subset(filename); | 
| 1237 | 0 |         if (!matching) | 
| 1238 | 0 |             return; | 
| 1239 | 0 |         if (1 == matching->size) { | 
| 1240 | 0 |             key = (netsnmp_key*)matching->array[0]; | 
| 1241 | 0 |             if (NULL == key->cert) { | 
| 1242 | 0 |                 DEBUGMSGT(("cert:partner", "%s found!\n", cert->info.filename)); | 
| 1243 | 0 |                 key->cert = cert; | 
| 1244 | 0 |                 cert->key = key; | 
| 1245 | 0 |             } | 
| 1246 | 0 |             else if (key->cert != cert) | 
| 1247 | 0 |                 snmp_log(LOG_ERR, "%s matching key already has partner\n", | 
| 1248 | 0 |                          cert->info.filename); | 
| 1249 | 0 |         } | 
| 1250 | 0 |         else | 
| 1251 | 0 |             DEBUGMSGT(("cert:partner", "%s matches multiple keys\n", | 
| 1252 | 0 |                        cert->info.filename)); | 
| 1253 | 0 |     } | 
| 1254 |  |      | 
| 1255 | 0 |     if (matching) { | 
| 1256 | 0 |         free(matching->array); | 
| 1257 | 0 |         free(matching); | 
| 1258 | 0 |     } | 
| 1259 | 0 | } | 
| 1260 |  |  | 
| 1261 |  | static netsnmp_key * | 
| 1262 |  | _add_key(EVP_PKEY *okey, const char* dirname, const char* filename, FILE *index) | 
| 1263 | 0 | { | 
| 1264 | 0 |     netsnmp_key  *key; | 
| 1265 |  | 
 | 
| 1266 | 0 |     key = _new_key(dirname, filename); | 
| 1267 | 0 |     if (NULL == key) { | 
| 1268 | 0 |         return NULL; | 
| 1269 | 0 |     } | 
| 1270 |  |  | 
| 1271 | 0 |     key->okey = okey; | 
| 1272 |  | 
 | 
| 1273 | 0 |     if (-1 == CONTAINER_INSERT(_keys, key)) { | 
| 1274 | 0 |         DEBUGMSGT(("cert:key:file:add:err", | 
| 1275 | 0 |                    "error inserting key into container\n")); | 
| 1276 | 0 |         netsnmp_key_free(key); | 
| 1277 | 0 |         key = NULL; | 
| 1278 | 0 |     } | 
| 1279 | 0 |     if (index) { | 
| 1280 | 0 |         fprintf(index, "k:%s\n", filename); | 
| 1281 | 0 |     } | 
| 1282 |  | 
 | 
| 1283 | 0 |     return key; | 
| 1284 | 0 | } | 
| 1285 |  |  | 
| 1286 |  | static netsnmp_cert * | 
| 1287 |  | _add_cert(X509 *ocert, const char* dirname, const char* filename, int type, int offset, | 
| 1288 |  |           int allowed_uses, FILE *index) | 
| 1289 | 0 | { | 
| 1290 | 0 |     netsnmp_cert *cert; | 
| 1291 |  | 
 | 
| 1292 | 0 |     cert = _new_cert(dirname, filename, type, offset, | 
| 1293 | 0 |                      allowed_uses, -1, NULL, NULL, NULL); | 
| 1294 | 0 |     if (NULL == cert) | 
| 1295 | 0 |         return NULL; | 
| 1296 |  |  | 
| 1297 | 0 |     netsnmp_ocert_parse(cert, ocert); | 
| 1298 |  | 
 | 
| 1299 | 0 |     if (-1 == CONTAINER_INSERT(_certs, cert)) { | 
| 1300 | 0 |         DEBUGMSGT(("cert:file:add:err", | 
| 1301 | 0 |                    "error inserting cert into container\n")); | 
| 1302 | 0 |         netsnmp_cert_free(cert); | 
| 1303 | 0 |         return NULL; | 
| 1304 | 0 |     } | 
| 1305 |  |  | 
| 1306 | 0 |     if (index) { | 
| 1307 |  |         /** filename = NAME_MAX = 255 */ | 
| 1308 |  |         /** fingerprint max = 64*3=192 for sha512 */ | 
| 1309 |  |         /** common name / CN  = 64 */ | 
| 1310 | 0 |         if (cert) | 
| 1311 | 0 |             fprintf(index, "c:%s %d %d %d %d %s '%s' '%s'\n", filename, | 
| 1312 | 0 |                     cert->info.type, cert->offset, cert->info.allowed_uses, | 
| 1313 | 0 |                     cert->hash_type, cert->fingerprint, | 
| 1314 | 0 |                     cert->common_name, cert->subject); | 
| 1315 | 0 |     } | 
| 1316 |  | 
 | 
| 1317 | 0 |     return cert; | 
| 1318 | 0 | } | 
| 1319 |  |  | 
| 1320 |  | static int | 
| 1321 |  | _add_certfile(const char* dirname, const char* filename, FILE *index) | 
| 1322 | 0 | { | 
| 1323 | 0 |     BIO          *certbio; | 
| 1324 | 0 |     X509         *ocert = NULL; | 
| 1325 | 0 |     X509         *ncert; | 
| 1326 | 0 |     EVP_PKEY     *okey = NULL; | 
| 1327 | 0 |     netsnmp_cert *cert = NULL; | 
| 1328 | 0 |     netsnmp_key  *key = NULL; | 
| 1329 | 0 |     char          certfile[SNMP_MAXPATH]; | 
| 1330 | 0 |     int           type; | 
| 1331 | 0 |     int           offset = 0; | 
| 1332 |  | 
 | 
| 1333 | 0 |     if (((const void*)NULL == dirname) || (NULL == filename)) | 
| 1334 | 0 |         return -1; | 
| 1335 |  |  | 
| 1336 | 0 |     type = _type_from_filename(filename); | 
| 1337 | 0 |     if (type == NS_CERT_TYPE_UNKNOWN) { | 
| 1338 | 0 |         snmp_log(LOG_ERR, "certificate file '%s' type not recognised, ignoring\n", filename); | 
| 1339 | 0 |         return -1; | 
| 1340 | 0 |     } | 
| 1341 |  |  | 
| 1342 | 0 |     certbio = netsnmp_open_bio(dirname, filename); | 
| 1343 | 0 |     if (!certbio) { | 
| 1344 | 0 |         return -1; | 
| 1345 | 0 |     } | 
| 1346 |  |  | 
| 1347 | 0 |     switch (type) { | 
| 1348 |  |  | 
| 1349 | 0 |        case NS_CERT_TYPE_KEY:  | 
| 1350 |  | 
 | 
| 1351 | 0 |            okey = PEM_read_bio_PrivateKey(certbio, NULL, NULL, NULL); | 
| 1352 | 0 |            if (NULL == okey) | 
| 1353 | 0 |                snmp_log(LOG_ERR, "error parsing key file %s\n", filename); | 
| 1354 | 0 |            else { | 
| 1355 | 0 |                key = _add_key(okey, dirname, filename, index); | 
| 1356 | 0 |                if (NULL == key) { | 
| 1357 | 0 |                    EVP_PKEY_free(okey); | 
| 1358 | 0 |                       okey = NULL; | 
| 1359 | 0 |                } | 
| 1360 | 0 |            } | 
| 1361 | 0 |            break; | 
| 1362 |  |  | 
| 1363 | 0 |         case NS_CERT_TYPE_DER: | 
| 1364 |  | 
 | 
| 1365 | 0 |             ocert = d2i_X509_bio(certbio, NULL); /* DER/ASN1 */ | 
| 1366 | 0 |             if (NULL != ocert) { | 
| 1367 | 0 |                 if (!_add_cert(ocert, dirname, filename, type, 0, | 
| 1368 | 0 |                                NS_CERT_REMOTE_PEER, index)) { | 
| 1369 | 0 |                     X509_free(ocert); | 
| 1370 | 0 |                     ocert = NULL; | 
| 1371 | 0 |                 } | 
| 1372 | 0 |                 break; | 
| 1373 | 0 |             } | 
| 1374 | 0 |             (void)BIO_reset(certbio); | 
| 1375 |  |             /* Check for PEM if DER didn't work */ | 
| 1376 | 0 |             NETSNMP_FALLTHROUGH; | 
| 1377 |  | 
 | 
| 1378 | 0 |         case NS_CERT_TYPE_PEM: | 
| 1379 |  | 
 | 
| 1380 | 0 |             if (NS_CERT_TYPE_DER == type) { | 
| 1381 | 0 |                 DEBUGMSGT(("9:cert:read", "Changing type from DER to PEM\n")); | 
| 1382 | 0 |                 type = NS_CERT_TYPE_PEM; | 
| 1383 | 0 |             } | 
| 1384 |  |  | 
| 1385 |  |             /* read the private key first so we can record this in the index */ | 
| 1386 | 0 |             okey = PEM_read_bio_PrivateKey(certbio, NULL, NULL, NULL); | 
| 1387 |  | 
 | 
| 1388 | 0 |             (void)BIO_reset(certbio); | 
| 1389 |  |  | 
| 1390 |  |             /* certs are read after the key */ | 
| 1391 | 0 |       ocert = ncert = PEM_read_bio_X509_AUX(certbio, NULL, NULL, NULL); | 
| 1392 | 0 |             if (NULL != ocert) { | 
| 1393 | 0 |                 cert = _add_cert(ncert, dirname, filename, type, 0, | 
| 1394 | 0 |                                  okey ? NS_CERT_IDENTITY | NS_CERT_REMOTE_PEER : | 
| 1395 | 0 |                                  NS_CERT_REMOTE_PEER, index); | 
| 1396 | 0 |                 if (NULL == cert) { | 
| 1397 | 0 |                     X509_free(ocert); | 
| 1398 | 0 |                     ocert = ncert = NULL; | 
| 1399 | 0 |                 } | 
| 1400 | 0 |             } | 
| 1401 | 0 |             while (NULL != ncert) { | 
| 1402 | 0 |                 offset = BIO_tell(certbio); | 
| 1403 | 0 |                 ncert = PEM_read_bio_X509_AUX(certbio, NULL, NULL, NULL); | 
| 1404 | 0 |                 if (ncert) { | 
| 1405 | 0 |                     if (NULL == _add_cert(ncert, dirname, filename, type, offset, 0, index)) { | 
| 1406 | 0 |                         X509_free(ncert); | 
| 1407 | 0 |                         ncert = NULL; | 
| 1408 | 0 |                     } | 
| 1409 | 0 |                 } | 
| 1410 | 0 |             } | 
| 1411 |  | 
 | 
| 1412 | 0 |             if (okey && cert) { | 
| 1413 | 0 |                 DEBUGMSGT(("cert:read:key", "found key with cert in %s\n", | 
| 1414 | 0 |                            cert->info.filename)); | 
| 1415 | 0 |                 key = _add_key(okey, dirname, filename, NULL); | 
| 1416 | 0 |                 if (NULL != key) { | 
| 1417 | 0 |                     DEBUGMSGT(("cert:read:partner", "%s match found!\n", | 
| 1418 | 0 |                                cert->info.filename)); | 
| 1419 | 0 |                     key->cert = cert; | 
| 1420 | 0 |                     cert->key = key; | 
| 1421 | 0 |                 } | 
| 1422 | 0 |                 else { | 
| 1423 | 0 |                     EVP_PKEY_free(okey); | 
| 1424 | 0 |                     okey = NULL; | 
| 1425 | 0 |                 } | 
| 1426 | 0 |             } else if (okey) { | 
| 1427 | 0 |                 EVP_PKEY_free(okey); | 
| 1428 | 0 |                 okey = NULL; | 
| 1429 | 0 |             } | 
| 1430 |  | 
 | 
| 1431 | 0 |             break; | 
| 1432 |  |  | 
| 1433 |  | #ifdef CERT_PKCS12_SUPPORT_MAYBE_LATER | 
| 1434 |  |         case NS_CERT_TYPE_PKCS12: | 
| 1435 |  | #endif | 
| 1436 |  |  | 
| 1437 | 0 |         default: | 
| 1438 | 0 |             break; | 
| 1439 | 0 |     } | 
| 1440 |  |  | 
| 1441 | 0 |     BIO_vfree(certbio); | 
| 1442 |  | 
 | 
| 1443 | 0 |     if ((NULL == ocert) && (NULL == okey)) { | 
| 1444 | 0 |         snmp_log(LOG_ERR, "certificate file '%s' contained neither certificate nor key, ignoring\n", certfile); | 
| 1445 | 0 |         return -1; | 
| 1446 | 0 |     } | 
| 1447 |  |  | 
| 1448 | 0 |     return 0; | 
| 1449 | 0 | } | 
| 1450 |  |  | 
| 1451 |  | static int | 
| 1452 |  | _cert_read_index(const char *dirname, struct stat *dirstat) | 
| 1453 | 0 | { | 
| 1454 | 0 |     FILE           *index; | 
| 1455 | 0 |     char           *idxname, *pos; | 
| 1456 | 0 |     struct stat     idx_stat; | 
| 1457 | 0 |     char            tmpstr[SNMP_MAXPATH + 5], filename[NAME_MAX]; | 
| 1458 | 0 |     char            fingerprint[EVP_MAX_MD_SIZE*3], common_name[64+1], type_str[15]; | 
| 1459 | 0 |     char            subject[SNMP_MAXBUF_SMALL], hash_str[15], offset_str[15]; | 
| 1460 | 0 |     char            allowed_uses_str[15]; | 
| 1461 | 0 |     ssize_t         offset; | 
| 1462 | 0 |     int             count = 0, type, allowed_uses, hash, version; | 
| 1463 | 0 |     netsnmp_cert    *cert; | 
| 1464 | 0 |     netsnmp_key     *key; | 
| 1465 | 0 |     netsnmp_container *newer, *found; | 
| 1466 |  | 
 | 
| 1467 | 0 |     netsnmp_assert(NULL != dirname); | 
| 1468 |  | 
 | 
| 1469 | 0 |     idxname = _certindex_lookup( dirname ); | 
| 1470 | 0 |     if (NULL == idxname) { | 
| 1471 | 0 |         DEBUGMSGT(("cert:index:parse", "no index for cert directory\n")); | 
| 1472 | 0 |         return -1; | 
| 1473 | 0 |     } | 
| 1474 |  |  | 
| 1475 |  |     /* | 
| 1476 |  |      * see if directory has been modified more recently than the index | 
| 1477 |  |      */ | 
| 1478 | 0 |     if (stat(idxname, &idx_stat) != 0) { | 
| 1479 | 0 |         DEBUGMSGT(("cert:index:parse", "error getting index file stats\n")); | 
| 1480 | 0 |         SNMP_FREE(idxname); | 
| 1481 | 0 |         return -1; | 
| 1482 | 0 |     } | 
| 1483 |  |  | 
| 1484 |  | #if (defined(WIN32) || defined(cygwin)) | 
| 1485 |  |     /* For Win32 platforms, the directory does not maintain a last modification | 
| 1486 |  |      * date that we can compare with the modification date of the .index file. | 
| 1487 |  |      */ | 
| 1488 |  | #else | 
| 1489 | 0 |     if (dirstat->st_mtime >= idx_stat.st_mtime) { | 
| 1490 | 0 |         DEBUGMSGT(("cert:index:parse", "Index outdated; dir modified\n")); | 
| 1491 | 0 |         SNMP_FREE(idxname); | 
| 1492 | 0 |         return -1; | 
| 1493 | 0 |     } | 
| 1494 | 0 | #endif | 
| 1495 |  |  | 
| 1496 |  |     /* | 
| 1497 |  |      * dir mtime doesn't change when files are touched, so we need to check | 
| 1498 |  |      * each file against the index in case a file has been modified. | 
| 1499 |  |      */ | 
| 1500 | 0 |     newer = | 
| 1501 | 0 |         netsnmp_directory_container_read_some(NULL, dirname, | 
| 1502 | 0 |                                               _time_filter, &idx_stat, | 
| 1503 | 0 |                                               NETSNMP_DIR_NSFILE | | 
| 1504 | 0 |                                               NETSNMP_DIR_NSFILE_STATS | | 
| 1505 | 0 |                                               NETSNMP_DIR_ALLOW_DUPLICATES); | 
| 1506 | 0 |     if (newer) { | 
| 1507 | 0 |         DEBUGMSGT(("cert:index:parse", "Index outdated; files modified\n")); | 
| 1508 | 0 |         CONTAINER_FREE_ALL(newer, NULL); | 
| 1509 | 0 |         CONTAINER_FREE(newer); | 
| 1510 | 0 |         SNMP_FREE(idxname); | 
| 1511 | 0 |         return -1; | 
| 1512 | 0 |     } | 
| 1513 |  |  | 
| 1514 | 0 |     DEBUGMSGT(("cert:index:parse", "The index for %s looks good\n", dirname)); | 
| 1515 |  | 
 | 
| 1516 | 0 |     index = fopen(idxname, "r"); | 
| 1517 | 0 |     if (NULL == index) { | 
| 1518 | 0 |         snmp_log(LOG_ERR, "cert:index:parse can't open index for %s\n", | 
| 1519 | 0 |             dirname); | 
| 1520 | 0 |         SNMP_FREE(idxname); | 
| 1521 | 0 |         return -1; | 
| 1522 | 0 |     } | 
| 1523 |  |  | 
| 1524 | 0 |     found = _get_cert_container(idxname); | 
| 1525 |  |  | 
| 1526 |  |     /* | 
| 1527 |  |      * check index format version | 
| 1528 |  |      */ | 
| 1529 | 0 |     NETSNMP_IGNORE_RESULT(fgets(tmpstr, sizeof(tmpstr), index)); | 
| 1530 | 0 |     pos = strrchr(tmpstr, ' '); | 
| 1531 | 0 |     if (pos) { | 
| 1532 | 0 |         ++pos; | 
| 1533 | 0 |         version = atoi(pos); | 
| 1534 | 0 |     } | 
| 1535 | 0 |     if ((NULL == pos) || (version != CERT_INDEX_FORMAT)) { | 
| 1536 | 0 |         DEBUGMSGT(("cert:index:add", "missing or wrong index format!\n")); | 
| 1537 | 0 |         fclose(index); | 
| 1538 | 0 |         SNMP_FREE(idxname); | 
| 1539 | 0 |         count = -1; | 
| 1540 | 0 |         goto free_cert_container; | 
| 1541 | 0 |     } | 
| 1542 | 0 |     while (1) { | 
| 1543 | 0 |         if (NULL == fgets(tmpstr, sizeof(tmpstr), index)) | 
| 1544 | 0 |             break; | 
| 1545 |  |  | 
| 1546 | 0 |         if ('c' == tmpstr[0]) { | 
| 1547 | 0 |             pos = &tmpstr[2]; | 
| 1548 | 0 |             if ((NULL == (pos=copy_nword(pos, filename, sizeof(filename)))) || | 
| 1549 | 0 |                 (NULL == (pos=copy_nword(pos, type_str, sizeof(type_str)))) || | 
| 1550 | 0 |                 (NULL == (pos=copy_nword(pos, offset_str, sizeof(offset_str)))) || | 
| 1551 | 0 |                 (NULL == (pos=copy_nword(pos, allowed_uses_str, sizeof(allowed_uses_str)))) || | 
| 1552 | 0 |                 (NULL == (pos=copy_nword(pos, hash_str, sizeof(hash_str)))) || | 
| 1553 | 0 |                 (NULL == (pos=copy_nword(pos, fingerprint, | 
| 1554 | 0 |                                          sizeof(fingerprint)))) || | 
| 1555 | 0 |                 (NULL == (pos=copy_nword(pos, common_name, | 
| 1556 | 0 |                                            sizeof(common_name)))) || | 
| 1557 | 0 |                 (NULL != copy_nword(pos, subject, sizeof(subject)))) { | 
| 1558 | 0 |                 snmp_log(LOG_ERR, "_cert_read_index: error parsing line: %s\n", | 
| 1559 | 0 |                          tmpstr); | 
| 1560 | 0 |                 count = -1; | 
| 1561 | 0 |                 break; | 
| 1562 | 0 |             } | 
| 1563 | 0 |             type = atoi(type_str); | 
| 1564 | 0 |             offset = atoi(offset_str); | 
| 1565 | 0 |             allowed_uses = atoi(allowed_uses_str); | 
| 1566 | 0 |             hash = atoi(hash_str); | 
| 1567 | 0 |             cert = _new_cert(dirname, filename, type, offset, allowed_uses, hash, | 
| 1568 | 0 |                              fingerprint, common_name, subject); | 
| 1569 | 0 |             if (cert && 0 == CONTAINER_INSERT(found, cert)) | 
| 1570 | 0 |                 ++count; | 
| 1571 | 0 |             else { | 
| 1572 | 0 |                 DEBUGMSGT(("cert:index:add", | 
| 1573 | 0 |                            "error inserting cert into container\n")); | 
| 1574 | 0 |                 netsnmp_cert_free(cert); | 
| 1575 | 0 |                 cert = NULL; | 
| 1576 | 0 |             } | 
| 1577 | 0 |         } | 
| 1578 | 0 |         else if ('k' == tmpstr[0]) { | 
| 1579 | 0 |             if (NULL != copy_nword(&tmpstr[2], filename, sizeof(filename))) { | 
| 1580 | 0 |                 snmp_log(LOG_ERR, "_cert_read_index: error parsing line %s\n", | 
| 1581 | 0 |                     tmpstr); | 
| 1582 | 0 |                 continue; | 
| 1583 | 0 |             } | 
| 1584 | 0 |             key = _new_key(dirname, filename); | 
| 1585 | 0 |             if (key && 0 == CONTAINER_INSERT(_keys, key)) | 
| 1586 | 0 |                 ++count; | 
| 1587 | 0 |             else { | 
| 1588 | 0 |                 DEBUGMSGT(("cert:index:add:key", | 
| 1589 | 0 |                            "error inserting key into container\n")); | 
| 1590 | 0 |                 netsnmp_key_free(key); | 
| 1591 | 0 |             } | 
| 1592 | 0 |         } | 
| 1593 | 0 |         else { | 
| 1594 | 0 |             snmp_log(LOG_ERR, "unknown line in cert index for %s\n", dirname); | 
| 1595 | 0 |             continue; | 
| 1596 | 0 |         } | 
| 1597 | 0 |     } /* while */ | 
| 1598 | 0 |     fclose(index); | 
| 1599 | 0 |     SNMP_FREE(idxname); | 
| 1600 |  | 
 | 
| 1601 | 0 |     if (count > 0) { | 
| 1602 | 0 |         netsnmp_iterator  *itr = CONTAINER_ITERATOR(found); | 
| 1603 | 0 |         if (NULL == itr) { | 
| 1604 | 0 |             snmp_log(LOG_ERR, "could not get iterator for found certs\n"); | 
| 1605 | 0 |             count = -1; | 
| 1606 | 0 |         } | 
| 1607 | 0 |         else { | 
| 1608 | 0 |             cert = ITERATOR_FIRST(itr); | 
| 1609 | 0 |             for( ; cert; cert = ITERATOR_NEXT(itr)) | 
| 1610 | 0 |                 CONTAINER_INSERT(_certs, cert); | 
| 1611 | 0 |             ITERATOR_RELEASE(itr); | 
| 1612 | 0 |             DEBUGMSGT(("cert:index:parse","added %d certs from index\n", | 
| 1613 | 0 |                        count)); | 
| 1614 | 0 |         } | 
| 1615 | 0 |     } | 
| 1616 |  | 
 | 
| 1617 | 0 | free_cert_container: | 
| 1618 | 0 |     if (count < 0) | 
| 1619 | 0 |         CONTAINER_FREE_ALL(found, NULL); | 
| 1620 | 0 |     CONTAINER_FREE(found); | 
| 1621 |  | 
 | 
| 1622 | 0 |     return count; | 
| 1623 | 0 | } | 
| 1624 |  |  | 
| 1625 |  | static int | 
| 1626 |  | _add_certdir(const char *dirname) | 
| 1627 | 0 | { | 
| 1628 | 0 |     FILE           *index; | 
| 1629 | 0 |     char           *file; | 
| 1630 | 0 |     int             count = 0; | 
| 1631 | 0 |     netsnmp_container *cert_container; | 
| 1632 | 0 |     netsnmp_iterator  *it; | 
| 1633 | 0 |     struct stat     statbuf; | 
| 1634 |  | 
 | 
| 1635 | 0 |     netsnmp_assert(NULL != dirname); | 
| 1636 |  | 
 | 
| 1637 | 0 |     DEBUGMSGT(("9:cert:dir:add", " config dir: %s\n", dirname )); | 
| 1638 |  | 
 | 
| 1639 | 0 |     if (stat(dirname, &statbuf) != 0) { | 
| 1640 | 0 |         DEBUGMSGT(("9:cert:dir:add", " dir not present: %s\n", | 
| 1641 | 0 |                    dirname )); | 
| 1642 | 0 |         return -1; | 
| 1643 | 0 |     } | 
| 1644 | 0 | #ifdef S_ISDIR | 
| 1645 | 0 |     if (!S_ISDIR(statbuf.st_mode)) { | 
| 1646 | 0 |         DEBUGMSGT(("9:cert:dir:add", " not a dir: %s\n", dirname )); | 
| 1647 | 0 |         return -1; | 
| 1648 | 0 |     } | 
| 1649 | 0 | #endif | 
| 1650 |  |  | 
| 1651 | 0 |     DEBUGMSGT(("cert:index:dir", "Scanning directory %s\n", dirname)); | 
| 1652 |  |  | 
| 1653 |  |     /* | 
| 1654 |  |      * look for existing index | 
| 1655 |  |      */ | 
| 1656 | 0 |     count = _cert_read_index(dirname, &statbuf); | 
| 1657 | 0 |     if (count >= 0) | 
| 1658 | 0 |         return count; | 
| 1659 |  |  | 
| 1660 | 0 |     index = _certindex_new( dirname ); | 
| 1661 | 0 |     if (NULL == index) { | 
| 1662 | 0 |         DEBUGMSGT(("9:cert:index:dir", | 
| 1663 | 0 |                     "error opening index for cert directory\n")); | 
| 1664 | 0 |         DEBUGMSGTL(("cert:index", "could not open certificate index file\n")); | 
| 1665 | 0 |     } | 
| 1666 |  |  | 
| 1667 |  |     /* | 
| 1668 |  |      * index was missing, out of date or bad. rescan directory. | 
| 1669 |  |      */ | 
| 1670 | 0 |     cert_container = | 
| 1671 | 0 |         netsnmp_directory_container_read_some(NULL, dirname, | 
| 1672 | 0 |                                               _cert_cert_filter, NULL, | 
| 1673 | 0 |                                               NETSNMP_DIR_RELATIVE_PATH | | 
| 1674 | 0 |                                               NETSNMP_DIR_EMPTY_OK | | 
| 1675 | 0 |                                               NETSNMP_DIR_ALLOW_DUPLICATES); | 
| 1676 | 0 |     if (NULL == cert_container) { | 
| 1677 | 0 |         DEBUGMSGT(("cert:index:dir", | 
| 1678 | 0 |                     "error creating container for cert files\n")); | 
| 1679 | 0 |         goto err_index; | 
| 1680 | 0 |     } | 
| 1681 |  |  | 
| 1682 |  |     /* | 
| 1683 |  |      * iterate through the found files and add them to index | 
| 1684 |  |      */ | 
| 1685 | 0 |     it = CONTAINER_ITERATOR(cert_container); | 
| 1686 | 0 |     if (NULL == it) { | 
| 1687 | 0 |         DEBUGMSGT(("cert:index:dir", | 
| 1688 | 0 |                     "error creating iterator for cert files\n")); | 
| 1689 | 0 |         goto err_container; | 
| 1690 | 0 |     } | 
| 1691 |  |  | 
| 1692 | 0 |     for (file = ITERATOR_FIRST(it); file; file = ITERATOR_NEXT(it)) { | 
| 1693 | 0 |         DEBUGMSGT(("cert:index:dir", "adding %s to index\n", file)); | 
| 1694 | 0 |         if ( 0 == _add_certfile( dirname, file, index )) | 
| 1695 | 0 |             count++; | 
| 1696 | 0 |         else | 
| 1697 | 0 |             DEBUGMSGT(("cert:index:dir", "error adding %s to index\n", | 
| 1698 | 0 |                         file)); | 
| 1699 | 0 |     } | 
| 1700 |  |  | 
| 1701 |  |     /* | 
| 1702 |  |      * clean up and return | 
| 1703 |  |      */ | 
| 1704 | 0 |     ITERATOR_RELEASE(it); | 
| 1705 |  | 
 | 
| 1706 | 0 |   err_container: | 
| 1707 | 0 |     netsnmp_directory_container_free(cert_container); | 
| 1708 |  | 
 | 
| 1709 | 0 |   err_index: | 
| 1710 | 0 |     if (index) | 
| 1711 | 0 |         fclose(index); | 
| 1712 |  | 
 | 
| 1713 | 0 |     return count; | 
| 1714 | 0 | } | 
| 1715 |  |  | 
| 1716 |  | static void | 
| 1717 |  | _cert_indexes_load(void) | 
| 1718 | 0 | { | 
| 1719 | 0 |     const char     *confpath; | 
| 1720 | 0 |     char           *confpath_copy, *dir, *st = NULL; | 
| 1721 | 0 |     char            certdir[SNMP_MAXPATH]; | 
| 1722 | 0 |     const char     *subdirs[] = { NULL, "ca-certs", "certs", "private", NULL }; | 
| 1723 | 0 |     int             i = 0; | 
| 1724 |  |  | 
| 1725 |  |     /* | 
| 1726 |  |      * load indexes from persistent dir | 
| 1727 |  |      */ | 
| 1728 | 0 |     _certindexes_load(); | 
| 1729 |  |  | 
| 1730 |  |     /* | 
| 1731 |  |      * duplicate path building from read_config_files_of_type() in | 
| 1732 |  |      * read_config.c. That is, use SNMPCONFPATH environment variable if | 
| 1733 |  |      * it is defined, otherwise use configuration directory. | 
| 1734 |  |      */ | 
| 1735 | 0 |     confpath = netsnmp_getenv("SNMPCONFPATH"); | 
| 1736 | 0 |     if (NULL == confpath) | 
| 1737 | 0 |         confpath = get_configuration_directory(); | 
| 1738 |  | 
 | 
| 1739 | 0 |     subdirs[0] = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, | 
| 1740 | 0 |                                        NETSNMP_DS_LIB_CERT_EXTRA_SUBDIR); | 
| 1741 | 0 |     confpath_copy = strdup(confpath); | 
| 1742 | 0 |     if (!confpath_copy) | 
| 1743 | 0 |         return; | 
| 1744 | 0 |     for ( dir = strtok_r(confpath_copy, ENV_SEPARATOR, &st); | 
| 1745 | 0 |           dir; dir = strtok_r(NULL, ENV_SEPARATOR, &st)) { | 
| 1746 |  | 
 | 
| 1747 | 0 |         i = (NULL == subdirs[0]) ? 1 : 0; | 
| 1748 | 0 |         for ( ; subdirs[i] ; ++i ) { | 
| 1749 |  |             /** check tls subdir */ | 
| 1750 | 0 |             snprintf(certdir, sizeof(certdir), "%s/tls/%s", dir, subdirs[i]); | 
| 1751 | 0 |             _add_certdir(certdir); | 
| 1752 | 0 |         } /* for subdirs */ | 
| 1753 | 0 |     } /* for conf path dirs */ | 
| 1754 | 0 |     SNMP_FREE(confpath_copy); | 
| 1755 | 0 | } | 
| 1756 |  |  | 
| 1757 |  | static void | 
| 1758 |  | _cert_print(netsnmp_cert *c, void *context) | 
| 1759 | 0 | { | 
| 1760 | 0 |     if (NULL == c) | 
| 1761 | 0 |         return; | 
| 1762 |  |  | 
| 1763 | 0 |     DEBUGMSGT(("cert:dump", "cert %s in %s at offset %d\n", c->info.filename, c->info.dir, c->offset)); | 
| 1764 | 0 |     DEBUGMSGT(("cert:dump", "   type %d flags 0x%x (%s)\n", | 
| 1765 | 0 |              c->info.type, c->info.allowed_uses, | 
| 1766 | 0 |               _mode_str(c->info.allowed_uses))); | 
| 1767 | 0 |     DEBUGIF("9:cert:dump") { | 
| 1768 | 0 |         if (NS_CERT_TYPE_KEY != c->info.type) { | 
| 1769 | 0 |             if(c->subject) { | 
| 1770 | 0 |                 if (c->info.allowed_uses & NS_CERT_CA) | 
| 1771 | 0 |                     DEBUGMSGT(("9:cert:dump", "   CA: %s\n", c->subject)); | 
| 1772 | 0 |                 else | 
| 1773 | 0 |                     DEBUGMSGT(("9:cert:dump", "   subject: %s\n", c->subject)); | 
| 1774 | 0 |             } | 
| 1775 | 0 |             if(c->issuer) | 
| 1776 | 0 |                 DEBUGMSGT(("9:cert:dump", "   issuer: %s\n", c->issuer)); | 
| 1777 | 0 |             if(c->fingerprint) | 
| 1778 | 0 |                 DEBUGMSGT(("9:cert:dump", "   fingerprint: %s(%d):%s\n", | 
| 1779 | 0 |                            se_find_label_in_slist("cert_hash_alg", c->hash_type), | 
| 1780 | 0 |                            c->hash_type, c->fingerprint)); | 
| 1781 | 0 |         } | 
| 1782 |  |         /* netsnmp_feature_require(cert_utils_dump_names) */ | 
| 1783 |  |         /* netsnmp_openssl_cert_dump_names(c->ocert); */ | 
| 1784 | 0 |         netsnmp_openssl_cert_dump_extensions(c->ocert); | 
| 1785 | 0 |     } | 
| 1786 |  |      | 
| 1787 | 0 | } | 
| 1788 |  |  | 
| 1789 |  | static void | 
| 1790 |  | _key_print(netsnmp_key *k, void *context) | 
| 1791 | 0 | { | 
| 1792 | 0 |     if (NULL == k) | 
| 1793 | 0 |         return; | 
| 1794 |  |  | 
| 1795 | 0 |     DEBUGMSGT(("cert:dump", "key %s in %s\n", k->info.filename, k->info.dir)); | 
| 1796 | 0 |     DEBUGMSGT(("cert:dump", "   type %d flags 0x%x (%s)\n", k->info.type, | 
| 1797 | 0 |               k->info.allowed_uses, _mode_str(k->info.allowed_uses))); | 
| 1798 | 0 | } | 
| 1799 |  |  | 
| 1800 |  | void | 
| 1801 |  | netsnmp_cert_dump_all(void) | 
| 1802 | 0 | { | 
| 1803 | 0 |     CONTAINER_FOR_EACH(_certs, (netsnmp_container_obj_func*)_cert_print, NULL); | 
| 1804 | 0 |     CONTAINER_FOR_EACH(_keys, (netsnmp_container_obj_func*)_key_print, NULL); | 
| 1805 | 0 | } | 
| 1806 |  |  | 
| 1807 |  | #ifdef CERT_MAIN | 
| 1808 |  | /* | 
| 1809 |  |  * export BLD=~/net-snmp/build/ SRC=~/net-snmp/src  | 
| 1810 |  |  * cc -DCERT_MAIN `$BLD/net-snmp-config --cflags` `$BLD/net-snmp-config --build-includes $BLD/`  $SRC/snmplib/cert_util.c   -o cert_util `$BLD/net-snmp-config --build-lib-dirs $BLD` `$BLD/net-snmp-config --libs` -lcrypto -lssl | 
| 1811 |  |  * | 
| 1812 |  |  */ | 
| 1813 |  | int | 
| 1814 |  | main(int argc, char** argv) | 
| 1815 |  | { | 
| 1816 |  |     int          ch; | 
| 1817 |  |     extern char *optarg; | 
| 1818 |  |  | 
| 1819 |  |     while ((ch = getopt(argc, argv, "D:fHLMx:")) != EOF) | 
| 1820 |  |         switch(ch) { | 
| 1821 |  |             case 'D': | 
| 1822 |  |                 debug_register_tokens(optarg); | 
| 1823 |  |                 snmp_set_do_debugging(1); | 
| 1824 |  |                 break; | 
| 1825 |  |             default: | 
| 1826 |  |                 fprintf(stderr,"unknown option %c\n", ch); | 
| 1827 |  |         } | 
| 1828 |  |  | 
| 1829 |  |     init_snmp("dtlsapp"); | 
| 1830 |  |  | 
| 1831 |  |     netsnmp_cert_dump_all(); | 
| 1832 |  |  | 
| 1833 |  |     return 0; | 
| 1834 |  | } | 
| 1835 |  |  | 
| 1836 |  | #endif /* CERT_MAIN */ | 
| 1837 |  |  | 
| 1838 |  | static netsnmp_cert *_cert_find_fp(const char *fingerprint); | 
| 1839 |  |  | 
| 1840 |  | void | 
| 1841 |  | netsnmp_fp_lowercase_and_strip_colon(char *fp) | 
| 1842 | 0 | { | 
| 1843 | 0 |     char *pos, *dest=NULL; | 
| 1844 |  |      | 
| 1845 | 0 |     if(!fp) | 
| 1846 | 0 |         return; | 
| 1847 |  |  | 
| 1848 |  |     /** skip to first : */ | 
| 1849 | 0 |     for (pos = fp; *pos; ++pos ) { | 
| 1850 | 0 |         if (':' == *pos) { | 
| 1851 | 0 |             dest = pos; | 
| 1852 | 0 |             break; | 
| 1853 | 0 |         } | 
| 1854 | 0 |         else | 
| 1855 | 0 |             *pos = isalpha(0xFF & *pos) ? tolower(0xFF & *pos) : *pos; | 
| 1856 | 0 |     } | 
| 1857 | 0 |     if (!*pos) | 
| 1858 | 0 |         return; | 
| 1859 |  |  | 
| 1860 |  |     /** copy, skipping any ':' */ | 
| 1861 | 0 |     for (++pos; *pos; ++pos) { | 
| 1862 | 0 |         if (':' == *pos) | 
| 1863 | 0 |             continue; | 
| 1864 | 0 |         *dest++ = tolower(0xFF & *pos); | 
| 1865 | 0 |     } | 
| 1866 | 0 |     *dest = *pos; /* nul termination */ | 
| 1867 | 0 | } | 
| 1868 |  |  | 
| 1869 |  | netsnmp_cert * | 
| 1870 |  | netsnmp_cert_find(int what, int where, void *hint) | 
| 1871 | 0 | { | 
| 1872 | 0 |     netsnmp_cert *result = NULL; | 
| 1873 | 0 |     char         *fp, *hint_str; | 
| 1874 |  | 
 | 
| 1875 | 0 |     DEBUGMSGT(("cert:find:params", "looking for %s(%d) in %s(0x%x), hint %p\n", | 
| 1876 | 0 |                _mode_str(what), what, _where_str(where), where, hint)); | 
| 1877 |  | 
 | 
| 1878 | 0 |     if (NS_CERTKEY_DEFAULT == where) { | 
| 1879 |  |              | 
| 1880 | 0 |         switch (what) { | 
| 1881 | 0 |             case NS_CERT_IDENTITY: /* want my ID */ | 
| 1882 | 0 |                 fp = | 
| 1883 | 0 |                     netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, | 
| 1884 | 0 |                                           NETSNMP_DS_LIB_TLS_LOCAL_CERT); | 
| 1885 |  |                 /** temp backwards compability; remove in 5.7 */ | 
| 1886 | 0 |                 if (!fp) { | 
| 1887 | 0 |                     int           tmp; | 
| 1888 | 0 |                     tmp = (ptrdiff_t)hint; | 
| 1889 | 0 |                     DEBUGMSGT(("cert:find:params", " hint = %s\n", | 
| 1890 | 0 |                                tmp ? "server" : "client")); | 
| 1891 | 0 |                     fp = | 
| 1892 | 0 |                         netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, tmp ? | 
| 1893 | 0 |                                               NETSNMP_DS_LIB_X509_SERVER_PUB : | 
| 1894 | 0 |                                               NETSNMP_DS_LIB_X509_CLIENT_PUB ); | 
| 1895 | 0 |                 } | 
| 1896 | 0 |                 if (!fp) { | 
| 1897 |  |                     /* As a special case, use the application type to | 
| 1898 |  |                        determine a file name to pull the default identity | 
| 1899 |  |                        from. */ | 
| 1900 | 0 |                     return netsnmp_cert_find(what, NS_CERTKEY_FILE, netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPTYPE)); | 
| 1901 | 0 |                 } | 
| 1902 | 0 |                 break; | 
| 1903 | 0 |             case NS_CERT_REMOTE_PEER: | 
| 1904 | 0 |                 fp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, | 
| 1905 | 0 |                                            NETSNMP_DS_LIB_TLS_PEER_CERT); | 
| 1906 |  |                 /** temp backwards compability; remove in 5.7 */ | 
| 1907 | 0 |                 if (!fp) | 
| 1908 | 0 |                     fp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, | 
| 1909 | 0 |                                                NETSNMP_DS_LIB_X509_SERVER_PUB); | 
| 1910 | 0 |                 break; | 
| 1911 | 0 |             default: | 
| 1912 | 0 |                 DEBUGMSGT(("cert:find:err", "unhandled type %d for %s(%d)\n", | 
| 1913 | 0 |                            what, _where_str(where), where)); | 
| 1914 | 0 |                 return NULL; | 
| 1915 | 0 |         } | 
| 1916 | 0 |         if (fp) | 
| 1917 | 0 |             return netsnmp_cert_find(what, NS_CERTKEY_MULTIPLE, fp); | 
| 1918 | 0 |         return NULL; | 
| 1919 | 0 |     } /* where = ds store */ | 
| 1920 | 0 |     else if (NS_CERTKEY_MULTIPLE == where) { | 
| 1921 |  |         /* tries multiple sources of certificates based on ascii lookup keys */ | 
| 1922 |  |  | 
| 1923 |  |         /* Try a fingerprint match first, which should always be done first */ | 
| 1924 |  |         /* (to avoid people naming filenames with conflicting FPs) */ | 
| 1925 | 0 |         result = netsnmp_cert_find(what, NS_CERTKEY_FINGERPRINT, hint); | 
| 1926 | 0 |         if (!result) { | 
| 1927 |  |             /* Then try a file name lookup */ | 
| 1928 | 0 |             result = netsnmp_cert_find(what, NS_CERTKEY_FILE, hint); | 
| 1929 | 0 |         } | 
| 1930 | 0 |     } | 
| 1931 | 0 |     else if (NS_CERTKEY_FINGERPRINT == where) { | 
| 1932 | 0 |         DEBUGMSGT(("cert:find:params", " hint = %s\n", (char *)hint)); | 
| 1933 | 0 |         result = _cert_find_fp((char *)hint); | 
| 1934 | 0 |     } | 
| 1935 | 0 |     else if (NS_CERTKEY_TARGET_PARAM == where) { | 
| 1936 | 0 |         if (what != NS_CERT_IDENTITY) { | 
| 1937 | 0 |             snmp_log(LOG_ERR, "only identity is valid for target params\n"); | 
| 1938 | 0 |             return NULL; | 
| 1939 | 0 |         } | 
| 1940 |  |         /** hint == target mib data */ | 
| 1941 | 0 |         hint_str = (char *)hint; | 
| 1942 | 0 |         fp = _find_tlstmParams_fingerprint(hint_str); | 
| 1943 | 0 |         if (NULL != fp) | 
| 1944 | 0 |             result = _cert_find_fp(fp); | 
| 1945 |  | 
 | 
| 1946 | 0 |     } | 
| 1947 | 0 |     else if (NS_CERTKEY_TARGET_ADDR == where) { | 
| 1948 |  |          | 
| 1949 |  |         /** hint == target mib data */ | 
| 1950 | 0 |         if (what != NS_CERT_REMOTE_PEER) { | 
| 1951 | 0 |             snmp_log(LOG_ERR, "only peer is valid for target addr\n"); | 
| 1952 | 0 |             return NULL; | 
| 1953 | 0 |         } | 
| 1954 |  |         /** hint == target mib data */ | 
| 1955 | 0 |         hint_str = (char *)hint; | 
| 1956 | 0 |         fp = _find_tlstmAddr_fingerprint(hint_str); | 
| 1957 | 0 |         if (NULL != fp) | 
| 1958 | 0 |             result = _cert_find_fp(fp); | 
| 1959 |  | 
 | 
| 1960 | 0 |     } | 
| 1961 | 0 |     else if (NS_CERTKEY_FILE == where) { | 
| 1962 |  |         /** hint == filename */ | 
| 1963 | 0 |         char               *filename = (char*)hint; | 
| 1964 | 0 |         netsnmp_void_array *matching; | 
| 1965 |  | 
 | 
| 1966 | 0 |         DEBUGMSGT(("cert:find:params", " hint = %s\n", (char *)hint)); | 
| 1967 | 0 |         matching = _cert_reduce_subset_what(_cert_find_subset_fn( | 
| 1968 | 0 |                                             filename, NULL ), what); | 
| 1969 | 0 |         if (!matching) | 
| 1970 | 0 |             return NULL; | 
| 1971 | 0 |         if (1 == matching->size) | 
| 1972 | 0 |             result = (netsnmp_cert*)matching->array[0]; | 
| 1973 | 0 |         else { | 
| 1974 | 0 |             DEBUGMSGT(("cert:find:err", "%s matches multiple certs\n", | 
| 1975 | 0 |                        filename)); | 
| 1976 | 0 |             result = NULL; | 
| 1977 | 0 |         } | 
| 1978 | 0 |         free(matching->array); | 
| 1979 | 0 |         free(matching); | 
| 1980 | 0 |     } /* where = NS_CERTKEY_FILE */ | 
| 1981 | 0 |     else { /* unknown location */ | 
| 1982 |  |          | 
| 1983 | 0 |         DEBUGMSGT(("cert:find:err", "unhandled location %d for %d\n", where, | 
| 1984 | 0 |                    what)); | 
| 1985 | 0 |         return NULL; | 
| 1986 | 0 |     } | 
| 1987 |  |      | 
| 1988 | 0 |     if (NULL == result) | 
| 1989 | 0 |         return NULL; | 
| 1990 |  |  | 
| 1991 |  |     /** make sure result found can be used for specified type */ | 
| 1992 | 0 |     if (!(result->info.allowed_uses & what)) { | 
| 1993 | 0 |         DEBUGMSGT(("cert:find:err", | 
| 1994 | 0 |                    "cert %s / %s not allowed for %s(%d) (uses=%s (%d))\n", | 
| 1995 | 0 |                    result->info.filename, result->fingerprint, _mode_str(what), | 
| 1996 | 0 |                    what , _mode_str(result->info.allowed_uses), | 
| 1997 | 0 |                    result->info.allowed_uses)); | 
| 1998 | 0 |         return NULL; | 
| 1999 | 0 |     } | 
| 2000 |  |      | 
| 2001 |  |     /** make sure we have the cert data */ | 
| 2002 | 0 |     if (netsnmp_cert_load_x509(result) == CERT_LOAD_ERR) | 
| 2003 | 0 |         return NULL; | 
| 2004 |  |  | 
| 2005 | 0 |     DEBUGMSGT(("cert:find:found", | 
| 2006 | 0 |                "using cert %s / %s for %s(%d) (uses=%s (%d))\n", | 
| 2007 | 0 |                result->info.filename, result->fingerprint, _mode_str(what), | 
| 2008 | 0 |                what , _mode_str(result->info.allowed_uses), | 
| 2009 | 0 |                result->info.allowed_uses)); | 
| 2010 |  |              | 
| 2011 | 0 |     return result; | 
| 2012 | 0 | } | 
| 2013 |  |  | 
| 2014 |  | netsnmp_void_array * | 
| 2015 |  | netsnmp_certs_find(int what, int where, void *hint) | 
| 2016 | 0 | { | 
| 2017 |  | 
 | 
| 2018 | 0 |     DEBUGMSGT(("certs:find:params", "looking for %s(%d) in %s(0x%x), hint %p\n", | 
| 2019 | 0 |                _mode_str(what), what, _where_str(where), where, hint)); | 
| 2020 |  | 
 | 
| 2021 | 0 |     if (NS_CERTKEY_FILE == where) { | 
| 2022 |  |         /** hint == filename */ | 
| 2023 | 0 |         char               *filename = (char*)hint; | 
| 2024 | 0 |         netsnmp_void_array *matching; | 
| 2025 |  | 
 | 
| 2026 | 0 |         DEBUGMSGT(("cert:find:params", " hint = %s\n", (char *)hint)); | 
| 2027 | 0 |         matching = _cert_reduce_subset_what(_cert_find_subset_fn( | 
| 2028 | 0 |                                             filename, NULL ), what); | 
| 2029 |  | 
 | 
| 2030 | 0 |         return matching; | 
| 2031 | 0 |     } /* where = NS_CERTKEY_FILE */ | 
| 2032 | 0 |     else { /* unknown location */ | 
| 2033 |  | 
 | 
| 2034 | 0 |         DEBUGMSGT(("certs:find:err", "unhandled location %d for %d\n", where, | 
| 2035 | 0 |                    what)); | 
| 2036 | 0 |         return NULL; | 
| 2037 | 0 |     } | 
| 2038 | 0 | } | 
| 2039 |  |  | 
| 2040 |  | #ifndef NETSNMP_FEATURE_REMOVE_CERT_FINGERPRINTS | 
| 2041 |  | int | 
| 2042 |  | netsnmp_cert_check_vb_fingerprint(const netsnmp_variable_list *var) | 
| 2043 | 0 | { | 
| 2044 | 0 |     if (!var) | 
| 2045 | 0 |         return SNMP_ERR_GENERR; | 
| 2046 |  |  | 
| 2047 | 0 |     if (0 == var->val_len) /* empty allowed in some cases */ | 
| 2048 | 0 |         return SNMP_ERR_NOERROR; | 
| 2049 |  |  | 
| 2050 | 0 |     if (! (0x01 & var->val_len)) { /* odd len */ | 
| 2051 | 0 |         DEBUGMSGT(("cert:varbind:fingerprint", | 
| 2052 | 0 |                    "expecting odd length for fingerprint\n")); | 
| 2053 | 0 |         return SNMP_ERR_WRONGLENGTH; | 
| 2054 | 0 |     } | 
| 2055 |  |  | 
| 2056 | 0 |     if (var->val.string[0] > NS_HASH_MAX) { | 
| 2057 | 0 |         DEBUGMSGT(("cert:varbind:fingerprint", "hashtype %d > max %d\n", | 
| 2058 | 0 |                    var->val.string[0], NS_HASH_MAX)); | 
| 2059 | 0 |         return SNMP_ERR_WRONGVALUE; | 
| 2060 | 0 |     } | 
| 2061 |  |  | 
| 2062 | 0 |     return SNMP_ERR_NOERROR; | 
| 2063 | 0 | } | 
| 2064 |  |  | 
| 2065 |  | /** | 
| 2066 |  |  * break a SnmpTLSFingerprint into an integer hash type + hex string | 
| 2067 |  |  * | 
| 2068 |  |  * @return SNMPERR_SUCCESS : on success | 
| 2069 |  |  * @return SNMPERR_GENERR  : on failure | 
| 2070 |  |  */ | 
| 2071 |  | int | 
| 2072 |  | netsnmp_tls_fingerprint_parse(const u_char *binary_fp, int fp_len, | 
| 2073 |  |                               char **fp_str_ptr, u_int *fp_str_len, int realloc, | 
| 2074 |  |                               u_char *hash_type_ptr) | 
| 2075 | 0 | { | 
| 2076 | 0 |     int     needed; | 
| 2077 | 0 |     size_t  fp_str_size; | 
| 2078 |  | 
 | 
| 2079 | 0 |     netsnmp_require_ptr_LRV( hash_type_ptr, SNMPERR_GENERR ); | 
| 2080 | 0 |     netsnmp_require_ptr_LRV( fp_str_ptr, SNMPERR_GENERR ); | 
| 2081 | 0 |     netsnmp_require_ptr_LRV( fp_str_len, SNMPERR_GENERR ); | 
| 2082 |  |  | 
| 2083 |  |     /* | 
| 2084 |  |      * output string is binary fp length (minus 1 for initial hash type  | 
| 2085 |  |      * char) * 2 for bin to hex conversion, + 1 for null termination. | 
| 2086 |  |      */ | 
| 2087 | 0 |     needed = ((fp_len - 1) * 2) + 1; | 
| 2088 | 0 |     if (*fp_str_len < needed) { | 
| 2089 | 0 |         DEBUGMSGT(("tls:fp:parse", "need %d bytes for output\n", needed )); | 
| 2090 | 0 |         return SNMPERR_GENERR; | 
| 2091 | 0 |     } | 
| 2092 |  |  | 
| 2093 |  |     /* | 
| 2094 |  |      * make sure hash type is in valid range | 
| 2095 |  |      */ | 
| 2096 | 0 |     if ((0 == binary_fp[0]) || (binary_fp[0] > NS_HASH_MAX)) { | 
| 2097 | 0 |         DEBUGMSGT(("tls:fp:parse", "invalid hash type %d\n", | 
| 2098 | 0 |                    binary_fp[0])); | 
| 2099 | 0 |         return SNMPERR_GENERR; | 
| 2100 | 0 |     } | 
| 2101 |  |  | 
| 2102 |  |     /* | 
| 2103 |  |      * netsnmp_binary_to_hex allocate space for string, if needed | 
| 2104 |  |      */ | 
| 2105 | 0 |     fp_str_size = *fp_str_len; | 
| 2106 | 0 |     *hash_type_ptr = binary_fp[0]; | 
| 2107 | 0 |     netsnmp_binary_to_hex((u_char**)fp_str_ptr, &fp_str_size, | 
| 2108 | 0 |                           realloc, &binary_fp[1], fp_len - 1); | 
| 2109 | 0 |     *fp_str_len = fp_str_size; | 
| 2110 | 0 |     if (0 == *fp_str_len) | 
| 2111 | 0 |         return SNMPERR_GENERR; | 
| 2112 |  |  | 
| 2113 | 0 |     return SNMPERR_SUCCESS; | 
| 2114 | 0 | } | 
| 2115 |  | #endif /* NETSNMP_FEATURE_REMOVE_CERT_FINGERPRINTS */ | 
| 2116 |  |  | 
| 2117 |  | #ifndef NETSNMP_FEATURE_REMOVE_TLS_FINGERPRINT_BUILD | 
| 2118 |  | /** | 
| 2119 |  |  * combine a hash type and hex fingerprint into a SnmpTLSFingerprint | 
| 2120 |  |  * | 
| 2121 |  |  * On entry, tls_fp_len should point to the size of the tls_fp buffer. | 
| 2122 |  |  * On a successful exit, tls_fp_len will contain the length of the | 
| 2123 |  |  * fingerprint buffer. | 
| 2124 |  |  */ | 
| 2125 |  | int | 
| 2126 |  | netsnmp_tls_fingerprint_build(int hash_type, const char *hex_fp, | 
| 2127 |  |                                    u_char **tls_fp, size_t *tls_fp_len, | 
| 2128 |  |                                    int realloc) | 
| 2129 | 0 | { | 
| 2130 | 0 |     int     hex_fp_len, rc; | 
| 2131 | 0 |     size_t  tls_fp_size = *tls_fp_len; | 
| 2132 | 0 |     size_t  offset; | 
| 2133 |  | 
 | 
| 2134 | 0 |     netsnmp_require_ptr_LRV( hex_fp, SNMPERR_GENERR ); | 
| 2135 | 0 |     netsnmp_require_ptr_LRV( tls_fp, SNMPERR_GENERR ); | 
| 2136 | 0 |     netsnmp_require_ptr_LRV( tls_fp_len, SNMPERR_GENERR ); | 
| 2137 |  |  | 
| 2138 | 0 |     hex_fp_len = strlen(hex_fp); | 
| 2139 | 0 |     if (0 == hex_fp_len) { | 
| 2140 | 0 |         *tls_fp_len = 0; | 
| 2141 | 0 |         return SNMPERR_SUCCESS; | 
| 2142 | 0 |     } | 
| 2143 |  |  | 
| 2144 | 0 |     if ((hash_type <= NS_HASH_NONE) || (hash_type > NS_HASH_MAX)) { | 
| 2145 | 0 |         DEBUGMSGT(("tls:fp:build", "invalid hash type %d\n", hash_type )); | 
| 2146 | 0 |         return SNMPERR_GENERR; | 
| 2147 | 0 |     } | 
| 2148 |  |  | 
| 2149 |  |     /* | 
| 2150 |  |      * convert to binary | 
| 2151 |  |      */ | 
| 2152 | 0 |     offset = 1; | 
| 2153 | 0 |     rc = netsnmp_hex_to_binary(tls_fp, &tls_fp_size, &offset, realloc, hex_fp, | 
| 2154 | 0 |                                ":"); | 
| 2155 | 0 |     *tls_fp_len = tls_fp_size; | 
| 2156 | 0 |     if (rc != 1) | 
| 2157 | 0 |         return SNMPERR_GENERR; | 
| 2158 | 0 |     *tls_fp_len = offset; | 
| 2159 | 0 |     (*tls_fp)[0] = hash_type; | 
| 2160 |  |                                 | 
| 2161 | 0 |     return SNMPERR_SUCCESS; | 
| 2162 | 0 | } | 
| 2163 |  | #endif /* NETSNMP_FEATURE_REMOVE_TLS_FINGERPRINT_BUILD */ | 
| 2164 |  |  | 
| 2165 |  | /** | 
| 2166 |  |  * Trusts a given certificate for use in TLS translations. | 
| 2167 |  |  * | 
| 2168 |  |  * @param ctx The SSL context to trust the certificate in | 
| 2169 |  |  * @param thiscert The netsnmp_cert certificate to trust | 
| 2170 |  |  * | 
| 2171 |  |  * @return SNMPERR_SUCCESS : on success | 
| 2172 |  |  * @return SNMPERR_GENERR  : on failure | 
| 2173 |  |  */ | 
| 2174 |  | int | 
| 2175 |  | netsnmp_cert_trust(SSL_CTX *ctx, netsnmp_cert *thiscert) | 
| 2176 | 0 | { | 
| 2177 | 0 |     X509_STORE     *certstore; | 
| 2178 | 0 |     X509           *cert; | 
| 2179 | 0 |     char           *fingerprint; | 
| 2180 |  |  | 
| 2181 |  |     /* ensure all needed pieces are present */ | 
| 2182 | 0 |     netsnmp_assert_or_msgreturn(NULL != thiscert, "NULL certificate passed in", | 
| 2183 | 0 |                                 SNMPERR_GENERR); | 
| 2184 | 0 |     netsnmp_assert_or_msgreturn(NULL != thiscert->info.dir, | 
| 2185 | 0 |                                 "NULL certificate directory name passed in", | 
| 2186 | 0 |                                 SNMPERR_GENERR); | 
| 2187 | 0 |     netsnmp_assert_or_msgreturn(NULL != thiscert->info.filename, | 
| 2188 | 0 |                                 "NULL certificate filename name passed in", | 
| 2189 | 0 |                                 SNMPERR_GENERR); | 
| 2190 |  |  | 
| 2191 |  |     /* get the trusted certificate store and the certificate to load into it */ | 
| 2192 | 0 |     certstore = SSL_CTX_get_cert_store(ctx); | 
| 2193 | 0 |     netsnmp_assert_or_msgreturn(NULL != certstore, | 
| 2194 | 0 |                                 "failed to get certificate trust store", | 
| 2195 | 0 |                                 SNMPERR_GENERR); | 
| 2196 | 0 |     cert = netsnmp_ocert_get(thiscert); | 
| 2197 | 0 |     netsnmp_assert_or_msgreturn(NULL != cert, | 
| 2198 | 0 |                                 "failed to get certificate from netsnmp_cert", | 
| 2199 | 0 |                                 SNMPERR_GENERR); | 
| 2200 |  |  | 
| 2201 |  |     /* Put the certificate into the store */ | 
| 2202 | 0 |     fingerprint = netsnmp_openssl_cert_get_fingerprint(cert, -1); | 
| 2203 | 0 |     DEBUGMSGTL(("cert:trust", | 
| 2204 | 0 |                 "putting trusted cert %p = %s in certstore %p\n", cert, | 
| 2205 | 0 |                 fingerprint, certstore)); | 
| 2206 | 0 |     SNMP_FREE(fingerprint); | 
| 2207 | 0 |     X509_STORE_add_cert(certstore, cert); | 
| 2208 |  | 
 | 
| 2209 | 0 |     return SNMPERR_SUCCESS; | 
| 2210 | 0 | } | 
| 2211 |  |  | 
| 2212 |  | /** | 
| 2213 |  |  * Trusts a given certificate's root CA for use in TLS translations. | 
| 2214 |  |  * If no issuer is found the existing certificate will be trusted instead. | 
| 2215 |  |  * | 
| 2216 |  |  * @param ctx The SSL context to trust the certificate in | 
| 2217 |  |  * @param thiscert The netsnmp_cert certificate  | 
| 2218 |  |  * | 
| 2219 |  |  * @return SNMPERR_SUCCESS : on success | 
| 2220 |  |  * @return SNMPERR_GENERR  : on failure | 
| 2221 |  |  */ | 
| 2222 |  | int | 
| 2223 |  | netsnmp_cert_trust_ca(SSL_CTX *ctx, netsnmp_cert *thiscert) | 
| 2224 | 0 | { | 
| 2225 | 0 |     netsnmp_assert_or_msgreturn(NULL != thiscert, "NULL certificate passed in", | 
| 2226 | 0 |                                 SNMPERR_GENERR); | 
| 2227 |  |  | 
| 2228 |  |     /* find the root CA certificate in the chain */ | 
| 2229 | 0 |     DEBUGMSGTL(("cert:trust_ca", "checking roots for %p \n", thiscert)); | 
| 2230 | 0 |     while (thiscert->issuer_cert) { | 
| 2231 | 0 |         thiscert = thiscert->issuer_cert; | 
| 2232 | 0 |         DEBUGMSGTL(("cert:trust_ca", "  up one to %p\n", thiscert)); | 
| 2233 | 0 |     } | 
| 2234 |  |  | 
| 2235 |  |     /* Add the found top level certificate to the store */ | 
| 2236 | 0 |     return netsnmp_cert_trust(ctx, thiscert); | 
| 2237 | 0 | } | 
| 2238 |  |  | 
| 2239 |  | netsnmp_container * | 
| 2240 |  | netsnmp_cert_get_trustlist(void) | 
| 2241 | 0 | { | 
| 2242 | 0 |     if (!_trusted_certs) | 
| 2243 | 0 |         _setup_trusted_certs(); | 
| 2244 | 0 |     return _trusted_certs; | 
| 2245 | 0 | } | 
| 2246 |  |  | 
| 2247 |  | static void | 
| 2248 |  | _parse_trustcert(const char *token, char *line) | 
| 2249 | 0 | { | 
| 2250 | 0 |     if (!_trusted_certs) | 
| 2251 | 0 |         _setup_trusted_certs(); | 
| 2252 |  | 
 | 
| 2253 | 0 |     if (!_trusted_certs) | 
| 2254 | 0 |         return; | 
| 2255 |  |  | 
| 2256 | 0 |     CONTAINER_INSERT(_trusted_certs, strdup(line)); | 
| 2257 | 0 | } | 
| 2258 |  |  | 
| 2259 |  | /* *************************************************************************** | 
| 2260 |  |  * | 
| 2261 |  |  * mode text functions | 
| 2262 |  |  * | 
| 2263 |  |  */ | 
| 2264 |  | static const char *_mode_str(u_char mode) | 
| 2265 | 0 | { | 
| 2266 | 0 |     return _modes[mode]; | 
| 2267 | 0 | } | 
| 2268 |  |  | 
| 2269 |  | static const char *_where_str(u_int what) | 
| 2270 | 0 | { | 
| 2271 | 0 |     switch (what) { | 
| 2272 | 0 |         case NS_CERTKEY_DEFAULT: return "DEFAULT"; | 
| 2273 | 0 |         case NS_CERTKEY_FILE: return "FILE"; | 
| 2274 | 0 |         case NS_CERTKEY_FINGERPRINT: return "FINGERPRINT"; | 
| 2275 | 0 |         case NS_CERTKEY_MULTIPLE: return "MULTIPLE"; | 
| 2276 | 0 |         case NS_CERTKEY_CA: return "CA"; | 
| 2277 | 0 |         case NS_CERTKEY_SAN_RFC822: return "SAN_RFC822"; | 
| 2278 | 0 |         case NS_CERTKEY_SAN_DNS: return "SAN_DNS"; | 
| 2279 | 0 |         case NS_CERTKEY_SAN_IPADDR: return "SAN_IPADDR"; | 
| 2280 | 0 |         case NS_CERTKEY_COMMON_NAME: return "COMMON_NAME"; | 
| 2281 | 0 |         case NS_CERTKEY_TARGET_PARAM: return "TARGET_PARAM"; | 
| 2282 | 0 |         case NS_CERTKEY_TARGET_ADDR: return "TARGET_ADDR"; | 
| 2283 | 0 |     } | 
| 2284 |  |  | 
| 2285 | 0 |     return "UNKNOWN"; | 
| 2286 | 0 | } | 
| 2287 |  |  | 
| 2288 |  | /* *************************************************************************** | 
| 2289 |  |  * | 
| 2290 |  |  * find functions | 
| 2291 |  |  * | 
| 2292 |  |  */ | 
| 2293 |  | static netsnmp_cert * | 
| 2294 |  | _cert_find_fp(const char *fingerprint) | 
| 2295 | 0 | { | 
| 2296 | 0 |     netsnmp_cert cert, *result = NULL; | 
| 2297 | 0 |     char         fp[EVP_MAX_MD_SIZE*3]; | 
| 2298 |  | 
 | 
| 2299 | 0 |     if (NULL == fingerprint) | 
| 2300 | 0 |         return NULL; | 
| 2301 |  |  | 
| 2302 | 0 |     strlcpy(fp, fingerprint, sizeof(fp)); | 
| 2303 | 0 |     netsnmp_fp_lowercase_and_strip_colon(fp); | 
| 2304 |  |  | 
| 2305 |  |     /** clear search key */ | 
| 2306 | 0 |     memset(&cert, 0x00, sizeof(cert)); | 
| 2307 |  | 
 | 
| 2308 | 0 |     cert.fingerprint = fp; | 
| 2309 |  | 
 | 
| 2310 | 0 |     result = CONTAINER_FIND(_certs,&cert); | 
| 2311 | 0 |     return result; | 
| 2312 | 0 | } | 
| 2313 |  |  | 
| 2314 |  | /* | 
| 2315 |  |  * reduce subset by eliminating any filenames that are longer than | 
| 2316 |  |  * the specified file name. e.g. 'snmp' would match 'snmp.key' and | 
| 2317 |  |  * 'snmpd.key'. We only want 'snmp.X', where X is a valid extension. | 
| 2318 |  |  */ | 
| 2319 |  | static void | 
| 2320 |  | _reduce_subset(netsnmp_void_array *matching, const char *filename) | 
| 2321 | 0 | { | 
| 2322 | 0 |     netsnmp_cert_common *cc; | 
| 2323 | 0 |     int i = 0, j, newsize, pos; | 
| 2324 |  | 
 | 
| 2325 | 0 |     if ((NULL == matching) || (NULL == filename)) | 
| 2326 | 0 |         return; | 
| 2327 |  |  | 
| 2328 | 0 |     pos = strlen(filename); | 
| 2329 | 0 |     newsize = matching->size; | 
| 2330 |  | 
 | 
| 2331 | 0 |     for( ; i < matching->size; ) { | 
| 2332 |  |         /* | 
| 2333 |  |          * if we've shifted matches down we'll hit a NULL entry before | 
| 2334 |  |          * we hit the end of the array. | 
| 2335 |  |          */ | 
| 2336 | 0 |         if (NULL == matching->array[i]) | 
| 2337 | 0 |             break; | 
| 2338 |  |         /* | 
| 2339 |  |          * skip over valid matches. Note that we do not want to use | 
| 2340 |  |          * _type_from_filename. | 
| 2341 |  |          */ | 
| 2342 | 0 |         cc = (netsnmp_cert_common*)matching->array[i]; | 
| 2343 | 0 |         if (('.' == cc->filename[pos]) && | 
| 2344 | 0 |             (NS_CERT_TYPE_UNKNOWN != _cert_ext_type(&cc->filename[pos+1]))) { | 
| 2345 | 0 |             ++i; | 
| 2346 | 0 |             continue; | 
| 2347 | 0 |         } | 
| 2348 |  |         /* | 
| 2349 |  |          * shrink array by shifting everything down a spot. Might not be | 
| 2350 |  |          * the most efficient soloution, but this is just happening at | 
| 2351 |  |          * startup and hopefully most certs won't have common prefixes. | 
| 2352 |  |          */ | 
| 2353 | 0 |         --newsize; | 
| 2354 | 0 |         for ( j=i; j < newsize; ++j ) | 
| 2355 | 0 |             matching->array[j] = matching->array[j+1]; | 
| 2356 | 0 |         matching->array[j] = NULL; | 
| 2357 |  |         /** no ++i; just shifted down, need to look at same position again */ | 
| 2358 | 0 |     } | 
| 2359 |  |     /* | 
| 2360 |  |      * if we shifted, set the new size | 
| 2361 |  |      */ | 
| 2362 | 0 |     if (newsize != matching->size) { | 
| 2363 | 0 |         DEBUGMSGT(("9:cert:subset:reduce", "shrank from %" NETSNMP_PRIz "d to %d\n", | 
| 2364 | 0 |                    matching->size, newsize)); | 
| 2365 | 0 |         matching->size = newsize; | 
| 2366 | 0 |     } | 
| 2367 | 0 | } | 
| 2368 |  |  | 
| 2369 |  | /* | 
| 2370 |  |  * reduce subset by eliminating any filenames that are not under the | 
| 2371 |  |  * specified directory path. | 
| 2372 |  |  */ | 
| 2373 |  | static void | 
| 2374 |  | _reduce_subset_dir(netsnmp_void_array *matching, const char *directory) | 
| 2375 | 0 | { | 
| 2376 | 0 |     netsnmp_cert_common *cc; | 
| 2377 | 0 |     int                  i = 0, j, newsize, dir_len; | 
| 2378 | 0 |     char                 dir[SNMP_MAXPATH], *pos; | 
| 2379 |  | 
 | 
| 2380 | 0 |     if ((NULL == matching) || (NULL == directory)) | 
| 2381 | 0 |         return; | 
| 2382 |  |  | 
| 2383 | 0 |     newsize = matching->size; | 
| 2384 |  |  | 
| 2385 |  |     /* | 
| 2386 |  |      * dir struct should be something like | 
| 2387 |  |      *          /usr/share/snmp/tls/certs | 
| 2388 |  |      *          /usr/share/snmp/tls/private | 
| 2389 |  |      * | 
| 2390 |  |      * so we want to backup up on directory for compares.. | 
| 2391 |  |      */ | 
| 2392 | 0 |     strlcpy(dir, directory, sizeof(dir)); | 
| 2393 | 0 |     pos = strrchr(dir, '/'); | 
| 2394 | 0 |     if (NULL == pos) { | 
| 2395 | 0 |         DEBUGMSGTL(("cert:subset:dir", "no '/' in directory %s\n", directory)); | 
| 2396 | 0 |         return; | 
| 2397 | 0 |     } | 
| 2398 | 0 |     *pos = '\0'; | 
| 2399 | 0 |     dir_len = strlen(dir); | 
| 2400 |  | 
 | 
| 2401 | 0 |     for( ; i < matching->size; ) { | 
| 2402 |  |         /* | 
| 2403 |  |          * if we've shifted matches down we'll hit a NULL entry before | 
| 2404 |  |          * we hit the end of the array. | 
| 2405 |  |          */ | 
| 2406 | 0 |         if (NULL == matching->array[i]) | 
| 2407 | 0 |             break; | 
| 2408 |  |         /* | 
| 2409 |  |          * skip over valid matches.  | 
| 2410 |  |          */ | 
| 2411 | 0 |         cc = (netsnmp_cert_common*)matching->array[i]; | 
| 2412 | 0 |         if (strncmp(dir, cc->dir, dir_len) == 0) { | 
| 2413 | 0 |             ++i; | 
| 2414 | 0 |             continue; | 
| 2415 | 0 |         } | 
| 2416 |  |         /* | 
| 2417 |  |          * shrink array by shifting everything down a spot. Might not be | 
| 2418 |  |          * the most efficient soloution, but this is just happening at | 
| 2419 |  |          * startup and hopefully most certs won't have common prefixes. | 
| 2420 |  |          */ | 
| 2421 | 0 |         --newsize; | 
| 2422 | 0 |         for ( j=i; j < newsize; ++j ) | 
| 2423 | 0 |             matching->array[j] = matching->array[j+1]; | 
| 2424 | 0 |         matching->array[j] = NULL; | 
| 2425 |  |         /** no ++i; just shifted down, need to look at same position again */ | 
| 2426 | 0 |     } | 
| 2427 |  |     /* | 
| 2428 |  |      * if we shifted, set the new size | 
| 2429 |  |      */ | 
| 2430 | 0 |     if (newsize != matching->size) { | 
| 2431 | 0 |         DEBUGMSGT(("9:cert:subset:dir", "shrank from %" NETSNMP_PRIz "d to %d\n", | 
| 2432 | 0 |                    matching->size, newsize)); | 
| 2433 | 0 |         matching->size = newsize; | 
| 2434 | 0 |     } | 
| 2435 | 0 | } | 
| 2436 |  |  | 
| 2437 |  | /* | 
| 2438 |  |  * reduce subset by eliminating any certificates that are not the | 
| 2439 |  |  * first certficate in a file. This allows us to ignore certificate | 
| 2440 |  |  * chains when testing for specific certificates, and to match keys | 
| 2441 |  |  * to the first certificate only. | 
| 2442 |  |  */ | 
| 2443 |  | static netsnmp_void_array * | 
| 2444 |  | _cert_reduce_subset_first(netsnmp_void_array *matching) | 
| 2445 | 0 | { | 
| 2446 | 0 |     netsnmp_cert *cc; | 
| 2447 | 0 |     int i = 0, j, newsize; | 
| 2448 |  | 
 | 
| 2449 | 0 |     if ((NULL == matching)) | 
| 2450 | 0 |         return matching; | 
| 2451 |  |  | 
| 2452 | 0 |     newsize = matching->size; | 
| 2453 |  | 
 | 
| 2454 | 0 |     for( ; i < matching->size; ) { | 
| 2455 |  |         /* | 
| 2456 |  |          * if we've shifted matches down we'll hit a NULL entry before | 
| 2457 |  |          * we hit the end of the array. | 
| 2458 |  |          */ | 
| 2459 | 0 |         if (NULL == matching->array[i]) | 
| 2460 | 0 |             break; | 
| 2461 |  |         /* | 
| 2462 |  |          * skip over valid matches. The first entry has an offset of zero. | 
| 2463 |  |          */ | 
| 2464 | 0 |         cc = (netsnmp_cert*)matching->array[i]; | 
| 2465 | 0 |         if (0 == cc->offset) { | 
| 2466 | 0 |             ++i; | 
| 2467 | 0 |             continue; | 
| 2468 | 0 |         } | 
| 2469 |  |         /* | 
| 2470 |  |          * shrink array by shifting everything down a spot. Might not be | 
| 2471 |  |          * the most efficient soloution, but this is just happening at | 
| 2472 |  |          * startup and hopefully most certs won't have common prefixes. | 
| 2473 |  |          */ | 
| 2474 | 0 |         --newsize; | 
| 2475 | 0 |         for ( j=i; j < newsize; ++j ) | 
| 2476 | 0 |             matching->array[j] = matching->array[j+1]; | 
| 2477 | 0 |         matching->array[j] = NULL; | 
| 2478 |  |         /** no ++i; just shifted down, need to look at same position again */ | 
| 2479 | 0 |     } | 
| 2480 |  |     /* | 
| 2481 |  |      * if we shifted, set the new size | 
| 2482 |  |      */ | 
| 2483 | 0 |     if (newsize != matching->size) { | 
| 2484 | 0 |         DEBUGMSGT(("9:cert:subset:first", "shrank from %" NETSNMP_PRIz "d to %d\n", | 
| 2485 | 0 |                    matching->size, newsize)); | 
| 2486 | 0 |         matching->size = newsize; | 
| 2487 | 0 |     } | 
| 2488 |  | 
 | 
| 2489 | 0 |     if (0 == matching->size) { | 
| 2490 | 0 |         free(matching->array); | 
| 2491 | 0 |         SNMP_FREE(matching); | 
| 2492 | 0 |     } | 
| 2493 |  | 
 | 
| 2494 | 0 |     return matching; | 
| 2495 | 0 | } | 
| 2496 |  |  | 
| 2497 |  | /* | 
| 2498 |  |  * reduce subset by eliminating any certificates that do not match | 
| 2499 |  |  * purpose specified. | 
| 2500 |  |  */ | 
| 2501 |  | static netsnmp_void_array * | 
| 2502 |  | _cert_reduce_subset_what(netsnmp_void_array *matching, int what) | 
| 2503 | 0 | { | 
| 2504 | 0 |     netsnmp_cert_common *cc; | 
| 2505 | 0 |     int i = 0, j, newsize; | 
| 2506 |  | 
 | 
| 2507 | 0 |     if ((NULL == matching)) | 
| 2508 | 0 |         return matching; | 
| 2509 |  |  | 
| 2510 | 0 |     newsize = matching->size; | 
| 2511 |  | 
 | 
| 2512 | 0 |     for( ; i < matching->size; ) { | 
| 2513 |  |         /* | 
| 2514 |  |          * if we've shifted matches down we'll hit a NULL entry before | 
| 2515 |  |          * we hit the end of the array. | 
| 2516 |  |          */ | 
| 2517 | 0 |         if (NULL == matching->array[i]) | 
| 2518 | 0 |             break; | 
| 2519 |  |         /* | 
| 2520 |  |          * skip over valid matches. The first entry has an offset of zero. | 
| 2521 |  |          */ | 
| 2522 | 0 |         cc = (netsnmp_cert_common *)matching->array[i]; | 
| 2523 | 0 |         if ((cc->allowed_uses & what)) { | 
| 2524 | 0 |             ++i; | 
| 2525 | 0 |             continue; | 
| 2526 | 0 |         } | 
| 2527 |  |         /* | 
| 2528 |  |          * shrink array by shifting everything down a spot. Might not be | 
| 2529 |  |          * the most efficient soloution, but this is just happening at | 
| 2530 |  |          * startup and hopefully most certs won't have common prefixes. | 
| 2531 |  |          */ | 
| 2532 | 0 |         --newsize; | 
| 2533 | 0 |         for ( j=i; j < newsize; ++j ) | 
| 2534 | 0 |             matching->array[j] = matching->array[j+1]; | 
| 2535 | 0 |         matching->array[j] = NULL; | 
| 2536 |  |         /** no ++i; just shifted down, need to look at same position again */ | 
| 2537 | 0 |     } | 
| 2538 |  |     /* | 
| 2539 |  |      * if we shifted, set the new size | 
| 2540 |  |      */ | 
| 2541 | 0 |     if (newsize != matching->size) { | 
| 2542 | 0 |         DEBUGMSGT(("9:cert:subset:what", "shrank from %" NETSNMP_PRIz "d to %d\n", | 
| 2543 | 0 |                    matching->size, newsize)); | 
| 2544 | 0 |         matching->size = newsize; | 
| 2545 | 0 |     } | 
| 2546 |  | 
 | 
| 2547 | 0 |     if (0 == matching->size) { | 
| 2548 | 0 |         free(matching->array); | 
| 2549 | 0 |         SNMP_FREE(matching); | 
| 2550 | 0 |     } | 
| 2551 |  | 
 | 
| 2552 | 0 |     return matching; | 
| 2553 | 0 | } | 
| 2554 |  |  | 
| 2555 |  | static netsnmp_void_array * | 
| 2556 |  | _cert_find_subset_common(const char *filename, netsnmp_container *container) | 
| 2557 | 0 | { | 
| 2558 | 0 |     netsnmp_cert_common   search; | 
| 2559 | 0 |     netsnmp_void_array   *matching; | 
| 2560 |  | 
 | 
| 2561 | 0 |     netsnmp_assert(filename && container); | 
| 2562 |  | 
 | 
| 2563 | 0 |     memset(&search, 0x00, sizeof(search));    /* clear search key */ | 
| 2564 |  | 
 | 
| 2565 | 0 |     search.filename = NETSNMP_REMOVE_CONST(char*,filename); | 
| 2566 |  | 
 | 
| 2567 | 0 |     matching = CONTAINER_GET_SUBSET(container, &search); | 
| 2568 | 0 |     DEBUGMSGT(("9:cert:subset:found", "%" NETSNMP_PRIz "d matches\n", matching ? | 
| 2569 | 0 |                matching->size : 0)); | 
| 2570 | 0 |     if (matching && matching->size > 1) { | 
| 2571 | 0 |         _reduce_subset(matching, filename); | 
| 2572 | 0 |         if (0 == matching->size) { | 
| 2573 | 0 |             free(matching->array); | 
| 2574 | 0 |             SNMP_FREE(matching); | 
| 2575 | 0 |         } | 
| 2576 | 0 |     } | 
| 2577 | 0 |     return matching; | 
| 2578 | 0 | } | 
| 2579 |  |  | 
| 2580 |  | static netsnmp_void_array * | 
| 2581 |  | _cert_find_subset_fn(const char *filename, const char *directory) | 
| 2582 | 0 | { | 
| 2583 | 0 |     netsnmp_container    *fn_container; | 
| 2584 | 0 |     netsnmp_void_array   *matching; | 
| 2585 |  |  | 
| 2586 |  |     /** find subcontainer with filename as key */ | 
| 2587 | 0 |     fn_container = SUBCONTAINER_FIND(_certs, "certs_fn"); | 
| 2588 | 0 |     netsnmp_assert(fn_container); | 
| 2589 |  | 
 | 
| 2590 | 0 |     matching = _cert_find_subset_common(filename, fn_container); | 
| 2591 | 0 |     if (matching && (matching->size > 1) && directory) { | 
| 2592 | 0 |         _reduce_subset_dir(matching, directory); | 
| 2593 | 0 |         if (0 == matching->size) { | 
| 2594 | 0 |             free(matching->array); | 
| 2595 | 0 |             SNMP_FREE(matching); | 
| 2596 | 0 |         } | 
| 2597 | 0 |     } | 
| 2598 | 0 |     return matching; | 
| 2599 | 0 | } | 
| 2600 |  |  | 
| 2601 |  | static netsnmp_void_array * | 
| 2602 |  | _cert_find_subset_sn(const char *subject) | 
| 2603 | 0 | { | 
| 2604 | 0 |     netsnmp_cert          search; | 
| 2605 | 0 |     netsnmp_void_array   *matching; | 
| 2606 | 0 |     netsnmp_container    *sn_container; | 
| 2607 |  |  | 
| 2608 |  |     /** find subcontainer with subject as key */ | 
| 2609 | 0 |     sn_container = SUBCONTAINER_FIND(_certs, "certs_sn"); | 
| 2610 | 0 |     netsnmp_assert(sn_container); | 
| 2611 |  | 
 | 
| 2612 | 0 |     memset(&search, 0x00, sizeof(search));    /* clear search key */ | 
| 2613 |  | 
 | 
| 2614 | 0 |     search.subject = NETSNMP_REMOVE_CONST(char*,subject); | 
| 2615 |  | 
 | 
| 2616 | 0 |     matching = CONTAINER_GET_SUBSET(sn_container, &search); | 
| 2617 | 0 |     DEBUGMSGT(("9:cert:subset:found", "%" NETSNMP_PRIz "d matches\n", matching ? | 
| 2618 | 0 |                matching->size : 0)); | 
| 2619 | 0 |     return matching; | 
| 2620 | 0 | } | 
| 2621 |  |  | 
| 2622 |  | static netsnmp_void_array * | 
| 2623 |  | _key_find_subset(const char *filename) | 
| 2624 | 0 | { | 
| 2625 | 0 |     return _cert_find_subset_common(filename, _keys); | 
| 2626 | 0 | } | 
| 2627 |  |  | 
| 2628 |  | /** find all entries matching given fingerprint */ | 
| 2629 |  | static netsnmp_void_array * | 
| 2630 |  | _find_subset_fp(netsnmp_container *certs, const char *fp) | 
| 2631 | 0 | { | 
| 2632 | 0 |     netsnmp_cert_map    entry; | 
| 2633 | 0 |     netsnmp_container  *fp_container; | 
| 2634 | 0 |     netsnmp_void_array *va; | 
| 2635 |  | 
 | 
| 2636 | 0 |     if ((NULL == certs) || (NULL == fp)) | 
| 2637 | 0 |         return NULL; | 
| 2638 |  |  | 
| 2639 | 0 |     fp_container = SUBCONTAINER_FIND(certs, "cert2sn_fp"); | 
| 2640 | 0 |     netsnmp_assert_or_msgreturn(fp_container, "cert2sn_fp container missing", | 
| 2641 | 0 |                                 NULL); | 
| 2642 |  |  | 
| 2643 | 0 |     memset(&entry, 0x0, sizeof(entry)); | 
| 2644 |  | 
 | 
| 2645 | 0 |     entry.fingerprint = NETSNMP_REMOVE_CONST(char*,fp); | 
| 2646 |  | 
 | 
| 2647 | 0 |     va = CONTAINER_GET_SUBSET(fp_container, &entry); | 
| 2648 | 0 |     return va; | 
| 2649 | 0 | } | 
| 2650 |  |  | 
| 2651 |  | #if 0  /* not used yet */ | 
| 2652 |  | static netsnmp_key * | 
| 2653 |  | _key_find_fn(const char *filename) | 
| 2654 |  | { | 
| 2655 |  |     netsnmp_key key, *result = NULL; | 
| 2656 |  |  | 
| 2657 |  |     netsnmp_assert(NULL != filename); | 
| 2658 |  |  | 
| 2659 |  |     memset(&key, 0x00, sizeof(key));    /* clear search key */ | 
| 2660 |  |     key.info.filename = NETSNMP_REMOVE_CONST(char*,filename); | 
| 2661 |  |     result = CONTAINER_FIND(_keys,&key); | 
| 2662 |  |     return result; | 
| 2663 |  | } | 
| 2664 |  | #endif | 
| 2665 |  |  | 
| 2666 |  | static int | 
| 2667 |  | _time_filter(const void *text, void *ctx) | 
| 2668 | 0 | { | 
| 2669 | 0 |     const netsnmp_file *f = text; | 
| 2670 | 0 |     struct stat *idx = ctx; | 
| 2671 |  |  | 
| 2672 |  |     /** include if mtime or ctime newer than index mtime */ | 
| 2673 | 0 |     if (f && idx && f->stats && | 
| 2674 | 0 |         ((f->stats->st_mtime >= idx->st_mtime) || | 
| 2675 | 0 |          (f->stats->st_ctime >= idx->st_mtime))) | 
| 2676 | 0 |         return NETSNMP_DIR_INCLUDE; | 
| 2677 |  |  | 
| 2678 | 0 |     return NETSNMP_DIR_EXCLUDE; | 
| 2679 | 0 | } | 
| 2680 |  |  | 
| 2681 |  | /* *************************************************************************** | 
| 2682 |  |  * *************************************************************************** | 
| 2683 |  |  * | 
| 2684 |  |  * | 
| 2685 |  |  * cert map functions | 
| 2686 |  |  * | 
| 2687 |  |  * | 
| 2688 |  |  * *************************************************************************** | 
| 2689 |  |  * ***************************************************************************/ | 
| 2690 | 0 | #define MAP_CONFIG_TOKEN "certSecName" | 
| 2691 |  | static void _parse_map(const char *token, char *line); | 
| 2692 |  | static void _map_free(netsnmp_cert_map* entry, void *ctx); | 
| 2693 |  | static void _purge_config_entries(void); | 
| 2694 |  |  | 
| 2695 |  | static void | 
| 2696 |  | _init_tlstmCertToTSN(void) | 
| 2697 | 0 | { | 
| 2698 | 0 |     const char *certSecName_help = MAP_CONFIG_TOKEN " PRIORITY FINGERPRINT " | 
| 2699 | 0 |         "[--shaNN|md5] <--sn SECNAME | --rfc822 | --dns | --ip | --cn | --any>"; | 
| 2700 |  |  | 
| 2701 |  |     /* | 
| 2702 |  |      * container for cert to fingerprint mapping, with fingerprint key | 
| 2703 |  |      */ | 
| 2704 | 0 |     _maps = netsnmp_cert_map_container_create(1); | 
| 2705 |  | 
 | 
| 2706 | 0 |     register_config_handler(NULL, MAP_CONFIG_TOKEN, _parse_map, _purge_config_entries, | 
| 2707 | 0 |                             certSecName_help); | 
| 2708 | 0 | } | 
| 2709 |  |  | 
| 2710 |  | netsnmp_cert_map * | 
| 2711 |  | netsnmp_cert_map_alloc(char *fingerprint, X509 *ocert) | 
| 2712 | 0 | { | 
| 2713 | 0 |     netsnmp_cert_map *cert_map = SNMP_MALLOC_TYPEDEF(netsnmp_cert_map); | 
| 2714 | 0 |     if (NULL == cert_map) { | 
| 2715 | 0 |         snmp_log(LOG_ERR, "could not allocate netsnmp_cert_map\n"); | 
| 2716 | 0 |         return NULL; | 
| 2717 | 0 |     } | 
| 2718 |  |      | 
| 2719 | 0 |     if (fingerprint) { | 
| 2720 |  |         /** MIB limits to 255 bytes; 2x since we've got ascii */ | 
| 2721 | 0 |         if (strlen(fingerprint) > (SNMPADMINLENGTH * 2)) { | 
| 2722 | 0 |             snmp_log(LOG_ERR, "fingerprint %s exceeds max length %d\n", | 
| 2723 | 0 |                      fingerprint, (SNMPADMINLENGTH * 2)); | 
| 2724 | 0 |             free(cert_map); | 
| 2725 | 0 |             return NULL; | 
| 2726 | 0 |         } | 
| 2727 | 0 |         cert_map->fingerprint = strdup(fingerprint); | 
| 2728 | 0 |     } | 
| 2729 | 0 |     if (ocert) { | 
| 2730 | 0 |         cert_map->hashType = netsnmp_openssl_cert_get_hash_type(ocert); | 
| 2731 | 0 |         cert_map->ocert = ocert; | 
| 2732 | 0 |     } | 
| 2733 |  | 
 | 
| 2734 | 0 |     return cert_map; | 
| 2735 | 0 | } | 
| 2736 |  |  | 
| 2737 |  | void | 
| 2738 |  | netsnmp_cert_map_free(netsnmp_cert_map *cert_map) | 
| 2739 | 0 | { | 
| 2740 | 0 |     if (NULL == cert_map) | 
| 2741 | 0 |         return; | 
| 2742 |  |  | 
| 2743 | 0 |     SNMP_FREE(cert_map->fingerprint); | 
| 2744 | 0 |     SNMP_FREE(cert_map->data); | 
| 2745 |  |     /** x509 cert isn't ours */ | 
| 2746 | 0 |     free(cert_map); /* SNMP_FREE wasted on param */ | 
| 2747 | 0 | } | 
| 2748 |  |  | 
| 2749 |  | int | 
| 2750 |  | netsnmp_cert_map_add(netsnmp_cert_map *map) | 
| 2751 | 0 | { | 
| 2752 | 0 |     int                rc; | 
| 2753 |  | 
 | 
| 2754 | 0 |     if (NULL == map) | 
| 2755 | 0 |         return -1; | 
| 2756 |  |  | 
| 2757 | 0 |     DEBUGMSGTL(("cert:map:add", "pri %d, fp %s\n", | 
| 2758 | 0 |                 map->priority, map->fingerprint)); | 
| 2759 |  | 
 | 
| 2760 | 0 |     if ((rc = CONTAINER_INSERT(_maps, map)) != 0) | 
| 2761 | 0 |         snmp_log(LOG_ERR, "could not insert new certificate map"); | 
| 2762 |  | 
 | 
| 2763 | 0 |     return rc; | 
| 2764 | 0 | } | 
| 2765 |  |  | 
| 2766 |  | #ifndef NETSNMP_FEATURE_REMOVE_CERT_MAP_REMOVE | 
| 2767 |  | int | 
| 2768 |  | netsnmp_cert_map_remove(netsnmp_cert_map *map) | 
| 2769 | 0 | { | 
| 2770 | 0 |     int                rc; | 
| 2771 |  | 
 | 
| 2772 | 0 |     if (NULL == map) | 
| 2773 | 0 |         return -1; | 
| 2774 |  |  | 
| 2775 | 0 |     DEBUGMSGTL(("cert:map:remove", "pri %d, fp %s\n", | 
| 2776 | 0 |                 map->priority, map->fingerprint)); | 
| 2777 |  | 
 | 
| 2778 | 0 |     if ((rc = CONTAINER_REMOVE(_maps, map)) != 0) | 
| 2779 | 0 |         snmp_log(LOG_ERR, "could not remove certificate map"); | 
| 2780 |  | 
 | 
| 2781 | 0 |     return rc; | 
| 2782 | 0 | } | 
| 2783 |  | #endif /* NETSNMP_FEATURE_REMOVE_CERT_MAP_REMOVE */ | 
| 2784 |  |  | 
| 2785 |  | #ifndef NETSNMP_FEATURE_REMOVE_CERT_MAP_FIND | 
| 2786 |  | netsnmp_cert_map * | 
| 2787 |  | netsnmp_cert_map_find(netsnmp_cert_map *map) | 
| 2788 | 0 | { | 
| 2789 | 0 |     if (NULL == map) | 
| 2790 | 0 |         return NULL; | 
| 2791 |  |  | 
| 2792 | 0 |     return CONTAINER_FIND(_maps, map); | 
| 2793 | 0 | } | 
| 2794 |  | #endif /* NETSNMP_FEATURE_REMOVE_CERT_MAP_FIND */ | 
| 2795 |  |  | 
| 2796 |  | static void | 
| 2797 |  | _map_free(netsnmp_cert_map *map, void *context) | 
| 2798 | 0 | { | 
| 2799 | 0 |     netsnmp_cert_map_free(map); | 
| 2800 | 0 | } | 
| 2801 |  |  | 
| 2802 |  | static int | 
| 2803 |  | _map_compare(netsnmp_cert_map *lhs, netsnmp_cert_map *rhs) | 
| 2804 | 0 | { | 
| 2805 | 0 |     netsnmp_assert((lhs != NULL) && (rhs != NULL)); | 
| 2806 |  | 
 | 
| 2807 | 0 |     if (lhs->priority < rhs->priority) | 
| 2808 | 0 |         return -1; | 
| 2809 | 0 |     else if (lhs->priority > rhs->priority) | 
| 2810 | 0 |         return 1; | 
| 2811 |  |  | 
| 2812 | 0 |     return strcmp(lhs->fingerprint, rhs->fingerprint); | 
| 2813 | 0 | } | 
| 2814 |  |  | 
| 2815 |  | static int | 
| 2816 |  | _map_fp_compare(netsnmp_cert_map *lhs, netsnmp_cert_map *rhs) | 
| 2817 | 0 | { | 
| 2818 | 0 |     int rc; | 
| 2819 | 0 |     netsnmp_assert((lhs != NULL) && (rhs != NULL)); | 
| 2820 |  | 
 | 
| 2821 | 0 |     if ((rc = strcmp(lhs->fingerprint, rhs->fingerprint)) != 0) | 
| 2822 | 0 |         return rc; | 
| 2823 |  |  | 
| 2824 | 0 |     if (lhs->priority < rhs->priority) | 
| 2825 | 0 |         return -1; | 
| 2826 | 0 |     else if (lhs->priority > rhs->priority) | 
| 2827 | 0 |         return 1; | 
| 2828 |  |  | 
| 2829 | 0 |     return 0; | 
| 2830 | 0 | } | 
| 2831 |  |  | 
| 2832 |  | static int | 
| 2833 |  | _map_fp_ncompare(netsnmp_cert_map *lhs, netsnmp_cert_map *rhs) | 
| 2834 | 0 | { | 
| 2835 | 0 |     netsnmp_assert((lhs != NULL) && (rhs != NULL)); | 
| 2836 |  | 
 | 
| 2837 | 0 |     return strncmp(lhs->fingerprint, rhs->fingerprint, | 
| 2838 | 0 |                    strlen(rhs->fingerprint)); | 
| 2839 | 0 | } | 
| 2840 |  |  | 
| 2841 |  | netsnmp_container * | 
| 2842 |  | netsnmp_cert_map_container_create(int with_fp) | 
| 2843 | 0 | { | 
| 2844 | 0 |     netsnmp_container *chain_map, *fp; | 
| 2845 |  | 
 | 
| 2846 | 0 |     chain_map = netsnmp_container_find("cert_map:stack:binary_array"); | 
| 2847 | 0 |     if (NULL == chain_map) { | 
| 2848 | 0 |         snmp_log(LOG_ERR, "could not allocate container for cert_map\n"); | 
| 2849 | 0 |         return NULL; | 
| 2850 | 0 |     } | 
| 2851 |  |  | 
| 2852 | 0 |     chain_map->container_name = strdup("cert_map"); | 
| 2853 | 0 |     chain_map->free_item = (netsnmp_container_obj_func*)_map_free; | 
| 2854 | 0 |     chain_map->compare = (netsnmp_container_compare*)_map_compare; | 
| 2855 |  | 
 | 
| 2856 | 0 |     if (!with_fp) | 
| 2857 | 0 |         return chain_map; | 
| 2858 |  |  | 
| 2859 |  |     /* | 
| 2860 |  |      * add a secondary index to the table container | 
| 2861 |  |      */ | 
| 2862 | 0 |     fp = netsnmp_container_find("cert2sn_fp:binary_array"); | 
| 2863 | 0 |     if (NULL == fp) { | 
| 2864 | 0 |         snmp_log(LOG_ERR, | 
| 2865 | 0 |                  "error creating sub-container for tlstmCertToTSNTable\n"); | 
| 2866 | 0 |         CONTAINER_FREE(chain_map); | 
| 2867 | 0 |         return NULL; | 
| 2868 | 0 |     } | 
| 2869 | 0 |     fp->container_name = strdup("cert2sn_fp"); | 
| 2870 | 0 |     fp->compare = (netsnmp_container_compare*)_map_fp_compare; | 
| 2871 | 0 |     fp->ncompare = (netsnmp_container_compare*)_map_fp_ncompare; | 
| 2872 | 0 |     netsnmp_container_add_index(chain_map, fp); | 
| 2873 |  | 
 | 
| 2874 | 0 |     return chain_map; | 
| 2875 | 0 | } | 
| 2876 |  |  | 
| 2877 |  | int | 
| 2878 |  | netsnmp_cert_parse_hash_type(const char *str) | 
| 2879 | 0 | { | 
| 2880 | 0 |     int rc = se_find_value_in_slist("cert_hash_alg", str); | 
| 2881 | 0 |     if (SE_DNE == rc) | 
| 2882 | 0 |         return NS_HASH_NONE; | 
| 2883 | 0 |     return rc; | 
| 2884 | 0 | } | 
| 2885 |  |  | 
| 2886 |  | void | 
| 2887 |  | netsnmp_cert_map_container_free(netsnmp_container *c) | 
| 2888 | 0 | { | 
| 2889 | 0 |     if (NULL == c) | 
| 2890 | 0 |         return; | 
| 2891 |  |  | 
| 2892 | 0 |     CONTAINER_FREE_ALL(c, NULL); | 
| 2893 | 0 |     CONTAINER_FREE(c); | 
| 2894 | 0 | } | 
| 2895 |  |  | 
| 2896 |  | /** clear out config rows | 
| 2897 |  |  * called during reconfig processing (e.g. SIGHUP) | 
| 2898 |  | */ | 
| 2899 |  | static void | 
| 2900 |  | _purge_config_entries(void) | 
| 2901 | 0 | { | 
| 2902 |  |     /** | 
| 2903 |  |      ** dup container | 
| 2904 |  |      ** iterate looking for NSCM_FROM_CONFIG flag | 
| 2905 |  |      ** delete from original | 
| 2906 |  |      ** delete dup | 
| 2907 |  |      **/ | 
| 2908 | 0 |     netsnmp_iterator   *itr; | 
| 2909 | 0 |     netsnmp_cert_map   *cert_map; | 
| 2910 | 0 |     netsnmp_container  *cert_maps = netsnmp_cert_map_container(); | 
| 2911 | 0 |     netsnmp_container  *tmp_maps = NULL; | 
| 2912 |  | 
 | 
| 2913 | 0 |     if ((NULL == cert_maps) || (CONTAINER_SIZE(cert_maps) == 0)) | 
| 2914 | 0 |         return; | 
| 2915 |  |  | 
| 2916 | 0 |     DEBUGMSGT(("cert:map:reconfig", "removing locally configured rows\n")); | 
| 2917 |  |      | 
| 2918 |  |     /* | 
| 2919 |  |      * duplicate cert_maps and then iterate over the copy. That way we can | 
| 2920 |  |      * add/remove to cert_maps without distrubing the iterator. | 
| 2921 |  | xx | 
| 2922 |  |      */ | 
| 2923 | 0 |     tmp_maps = CONTAINER_DUP(cert_maps, NULL, 0); | 
| 2924 | 0 |     if (NULL == tmp_maps) { | 
| 2925 | 0 |         snmp_log(LOG_ERR, "could not duplicate maps for reconfig\n"); | 
| 2926 | 0 |         return; | 
| 2927 | 0 |     } | 
| 2928 |  |  | 
| 2929 | 0 |     itr = CONTAINER_ITERATOR(tmp_maps); | 
| 2930 | 0 |     if (NULL == itr) { | 
| 2931 | 0 |         snmp_log(LOG_ERR, "could not get iterator for reconfig\n"); | 
| 2932 | 0 |         CONTAINER_FREE(tmp_maps); | 
| 2933 | 0 |         return; | 
| 2934 | 0 |     } | 
| 2935 | 0 |     cert_map = ITERATOR_FIRST(itr); | 
| 2936 | 0 |     for( ; cert_map; cert_map = ITERATOR_NEXT(itr)) { | 
| 2937 |  | 
 | 
| 2938 | 0 |         if (!(cert_map->flags & NSCM_FROM_CONFIG)) | 
| 2939 | 0 |             continue; | 
| 2940 |  |  | 
| 2941 | 0 |         if (CONTAINER_REMOVE(cert_maps, cert_map) == 0) | 
| 2942 | 0 |             netsnmp_cert_map_free(cert_map); | 
| 2943 | 0 |     } | 
| 2944 | 0 |     ITERATOR_RELEASE(itr); | 
| 2945 | 0 |     CONTAINER_FREE(tmp_maps); | 
| 2946 |  | 
 | 
| 2947 | 0 |     return; | 
| 2948 | 0 | } | 
| 2949 |  |  | 
| 2950 |  | /* | 
| 2951 |  |   certSecName PRIORITY [--shaNN|md5] FINGERPRINT <--sn SECNAME | --rfc822 | --dns | --ip | --cn | --any> | 
| 2952 |  |  | 
| 2953 |  |   certSecName  100  ff:..11 --sn Wes | 
| 2954 |  |   certSecName  200  ee:..:22 --sn JohnDoe | 
| 2955 |  |   certSecName  300  ee:..:22 --rfc822 | 
| 2956 |  | */ | 
| 2957 |  | netsnmp_cert_map * | 
| 2958 |  | netsnmp_certToTSN_parse_common(char **line) | 
| 2959 | 0 | { | 
| 2960 | 0 |     netsnmp_cert_map *map; | 
| 2961 | 0 |     char             *tmp, buf[SNMP_MAXBUF_SMALL]; | 
| 2962 | 0 |     size_t            len; | 
| 2963 | 0 |     netsnmp_cert     *tmpcert; | 
| 2964 |  | 
 | 
| 2965 | 0 |     if ((NULL == line) || (NULL == *line)) | 
| 2966 | 0 |         return NULL; | 
| 2967 |  |  | 
| 2968 |  |     /** need somewhere to save rows */ | 
| 2969 | 0 |     if (NULL == _maps) { | 
| 2970 | 0 |         NETSNMP_LOGONCE((LOG_ERR, "no container for certificate mappings\n")); | 
| 2971 | 0 |         return NULL; | 
| 2972 | 0 |     } | 
| 2973 |  |  | 
| 2974 | 0 |     DEBUGMSGT(("cert:util:config", "parsing %s\n", *line)); | 
| 2975 |  |  | 
| 2976 |  |     /* read the priority */ | 
| 2977 | 0 |     len = sizeof(buf); | 
| 2978 | 0 |     tmp = buf; | 
| 2979 | 0 |     *line = read_config_read_octet_string(*line, (u_char **)&tmp, &len); | 
| 2980 | 0 |     tmp[len] = 0; | 
| 2981 | 0 |     if (!isdigit(0xFF & tmp[0])) { | 
| 2982 | 0 |         netsnmp_config_error("could not parse priority"); | 
| 2983 | 0 |         return NULL; | 
| 2984 | 0 |     } | 
| 2985 | 0 |     map = netsnmp_cert_map_alloc(NULL, NULL); | 
| 2986 | 0 |     if (NULL == map) { | 
| 2987 | 0 |         netsnmp_config_error("could not allocate cert map struct"); | 
| 2988 | 0 |         return NULL; | 
| 2989 | 0 |     } | 
| 2990 | 0 |     map->flags |= NSCM_FROM_CONFIG; | 
| 2991 | 0 |     map->priority = atoi(buf); | 
| 2992 |  |  | 
| 2993 |  |     /* read the flag or the fingerprint */ | 
| 2994 | 0 |     len = sizeof(buf); | 
| 2995 | 0 |     tmp = buf; | 
| 2996 | 0 |     *line = read_config_read_octet_string(*line, (u_char **)&tmp, &len); | 
| 2997 | 0 |     tmp[len] = 0; | 
| 2998 | 0 |     if ((buf[0] == '-') && (buf[1] == '-')) { | 
| 2999 | 0 |         map->hashType = netsnmp_cert_parse_hash_type(&buf[2]); | 
| 3000 | 0 |         if (NS_HASH_NONE == map->hashType) { | 
| 3001 | 0 |             netsnmp_config_error("invalid hash type"); | 
| 3002 | 0 |             goto end; | 
| 3003 | 0 |         } | 
| 3004 |  |  | 
| 3005 |  |         /** set up for fingerprint */ | 
| 3006 | 0 |         len = sizeof(buf); | 
| 3007 | 0 |         tmp = buf; | 
| 3008 | 0 |         *line = read_config_read_octet_string(*line, (u_char **)&tmp, &len); | 
| 3009 | 0 |         tmp[len] = 0; | 
| 3010 | 0 |     } | 
| 3011 | 0 |     else | 
| 3012 | 0 |         map->hashType = NS_HASH_SHA1; | 
| 3013 |  |  | 
| 3014 |  |     /* look up the fingerprint */ | 
| 3015 | 0 |     tmpcert = netsnmp_cert_find(NS_CERT_REMOTE_PEER, NS_CERTKEY_MULTIPLE, buf); | 
| 3016 | 0 |     if (NULL == tmpcert) { | 
| 3017 |  |         /* assume it's a raw fingerprint we don't have */ | 
| 3018 | 0 |         netsnmp_fp_lowercase_and_strip_colon(buf); | 
| 3019 | 0 |         map->fingerprint = strdup(buf); | 
| 3020 | 0 |     } else { | 
| 3021 | 0 |         map->fingerprint = | 
| 3022 | 0 |             netsnmp_openssl_cert_get_fingerprint(tmpcert->ocert, -1); | 
| 3023 | 0 |     } | 
| 3024 |  |      | 
| 3025 | 0 |     if (NULL == *line) { | 
| 3026 | 0 |         netsnmp_config_error("must specify map type"); | 
| 3027 | 0 |         goto end; | 
| 3028 | 0 |     } | 
| 3029 |  |  | 
| 3030 |  |     /* read the mapping type */ | 
| 3031 | 0 |     len = sizeof(buf); | 
| 3032 | 0 |     tmp = buf; | 
| 3033 | 0 |     *line = read_config_read_octet_string(*line, (u_char **)&tmp, &len); | 
| 3034 | 0 |     tmp[len] = 0; | 
| 3035 | 0 |     if ((buf[0] != '-') || (buf[1] != '-')) { | 
| 3036 | 0 |         netsnmp_config_error("unexpected fromat: %s\n", *line); | 
| 3037 | 0 |         goto end; | 
| 3038 | 0 |     } | 
| 3039 | 0 |     if (strcmp(&buf[2], "sn") == 0) { | 
| 3040 | 0 |         if (NULL == *line) { | 
| 3041 | 0 |             netsnmp_config_error("must specify secName for --sn"); | 
| 3042 | 0 |             goto end; | 
| 3043 | 0 |         } | 
| 3044 | 0 |         len = sizeof(buf); | 
| 3045 | 0 |         tmp = buf; | 
| 3046 | 0 |         *line = read_config_read_octet_string(*line, (u_char **)&tmp, &len); | 
| 3047 | 0 |         map->data = strdup(buf); | 
| 3048 | 0 |         if (map->data) | 
| 3049 | 0 |             map->mapType = TSNM_tlstmCertSpecified; | 
| 3050 | 0 |     } | 
| 3051 | 0 |     else if (strcmp(&buf[2], "cn") == 0) | 
| 3052 | 0 |         map->mapType = TSNM_tlstmCertCommonName; | 
| 3053 | 0 |     else if (strcmp(&buf[2], "ip") == 0) | 
| 3054 | 0 |         map->mapType = TSNM_tlstmCertSANIpAddress; | 
| 3055 | 0 |     else if (strcmp(&buf[2], "rfc822") == 0) | 
| 3056 | 0 |         map->mapType = TSNM_tlstmCertSANRFC822Name; | 
| 3057 | 0 |     else if (strcmp(&buf[2], "dns") == 0) | 
| 3058 | 0 |         map->mapType = TSNM_tlstmCertSANDNSName; | 
| 3059 | 0 |     else if (strcmp(&buf[2], "any") == 0) | 
| 3060 | 0 |         map->mapType = TSNM_tlstmCertSANAny; | 
| 3061 | 0 |     else | 
| 3062 | 0 |         netsnmp_config_error("unknown argument %s\n", buf); | 
| 3063 |  |      | 
| 3064 | 0 |   end: | 
| 3065 | 0 |     if (0 == map->mapType) { | 
| 3066 | 0 |         netsnmp_cert_map_free(map); | 
| 3067 | 0 |         map = NULL; | 
| 3068 | 0 |     } | 
| 3069 |  | 
 | 
| 3070 | 0 |     return map; | 
| 3071 | 0 | } | 
| 3072 |  |  | 
| 3073 |  | static void | 
| 3074 |  | _parse_map(const char *token, char *line) | 
| 3075 | 0 | { | 
| 3076 | 0 |     netsnmp_cert_map *map = netsnmp_certToTSN_parse_common(&line); | 
| 3077 | 0 |     if (NULL == map) | 
| 3078 | 0 |         return; | 
| 3079 |  |  | 
| 3080 | 0 |     if (netsnmp_cert_map_add(map) != 0) { | 
| 3081 | 0 |         netsnmp_cert_map_free(map); | 
| 3082 | 0 |         netsnmp_config_error(MAP_CONFIG_TOKEN | 
| 3083 | 0 |                              ": duplicate priority for certificate map"); | 
| 3084 | 0 |     } | 
| 3085 | 0 | } | 
| 3086 |  |  | 
| 3087 |  | static int | 
| 3088 |  | _fill_cert_map(netsnmp_cert_map *cert_map, netsnmp_cert_map *entry) | 
| 3089 | 0 | { | 
| 3090 | 0 |     DEBUGMSGT(("cert:map:secname", "map: pri %d type %d data %s\n", | 
| 3091 | 0 |                entry->priority, entry->mapType, entry->data)); | 
| 3092 | 0 |     cert_map->priority = entry->priority; | 
| 3093 | 0 |     cert_map->mapType = entry->mapType; | 
| 3094 | 0 |     cert_map->hashType = entry->hashType; | 
| 3095 | 0 |     if (entry->data) { | 
| 3096 | 0 |         cert_map->data = strdup(entry->data); | 
| 3097 | 0 |         if (NULL == cert_map->data ) { | 
| 3098 | 0 |             snmp_log(LOG_ERR, "secname map data dup failed\n"); | 
| 3099 | 0 |             return -1; | 
| 3100 | 0 |         } | 
| 3101 | 0 |     } | 
| 3102 |  |  | 
| 3103 | 0 |     return 0; | 
| 3104 | 0 | } | 
| 3105 |  |  | 
| 3106 |  | /* | 
| 3107 |  |  * get secname map(s) for fingerprints | 
| 3108 |  |  */ | 
| 3109 |  | int | 
| 3110 |  | netsnmp_cert_get_secname_maps(netsnmp_container *cert_maps) | 
| 3111 | 0 | { | 
| 3112 | 0 |     netsnmp_iterator   *itr; | 
| 3113 | 0 |     netsnmp_cert_map   *cert_map, *new_cert_map, *entry; | 
| 3114 | 0 |     netsnmp_container  *new_maps = NULL; | 
| 3115 | 0 |     netsnmp_void_array *results; | 
| 3116 | 0 |     int                 j; | 
| 3117 |  | 
 | 
| 3118 | 0 |     if ((NULL == cert_maps) || (CONTAINER_SIZE(cert_maps) == 0)) | 
| 3119 | 0 |         return -1; | 
| 3120 |  |  | 
| 3121 | 0 |     DEBUGMSGT(("cert:map:secname", "looking for matches for %" NETSNMP_PRIz "d fingerprints\n", | 
| 3122 | 0 |                CONTAINER_SIZE(cert_maps))); | 
| 3123 |  |      | 
| 3124 |  |     /* | 
| 3125 |  |      * duplicate cert_maps and then iterate over the copy. That way we can | 
| 3126 |  |      * add/remove to cert_maps without distrubing the iterator. | 
| 3127 |  |      */ | 
| 3128 | 0 |     new_maps = CONTAINER_DUP(cert_maps, NULL, 0); | 
| 3129 | 0 |     if (NULL == new_maps) { | 
| 3130 | 0 |         snmp_log(LOG_ERR, "could not duplicate maps for secname mapping\n"); | 
| 3131 | 0 |         return -1; | 
| 3132 | 0 |     } | 
| 3133 |  |  | 
| 3134 | 0 |     itr = CONTAINER_ITERATOR(new_maps); | 
| 3135 | 0 |     if (NULL == itr) { | 
| 3136 | 0 |         snmp_log(LOG_ERR, "could not get iterator for secname mappings\n"); | 
| 3137 | 0 |         CONTAINER_FREE(new_maps); | 
| 3138 | 0 |         return -1; | 
| 3139 | 0 |     } | 
| 3140 | 0 |     cert_map = ITERATOR_FIRST(itr); | 
| 3141 | 0 |     for( ; cert_map; cert_map = ITERATOR_NEXT(itr)) { | 
| 3142 |  | 
 | 
| 3143 | 0 |         results = _find_subset_fp( netsnmp_cert_map_container(), | 
| 3144 | 0 |                                    cert_map->fingerprint ); | 
| 3145 | 0 |         if (NULL == results) { | 
| 3146 | 0 |             DEBUGMSGT(("cert:map:secname", "no match for %s\n", | 
| 3147 | 0 |                        cert_map->fingerprint)); | 
| 3148 | 0 |             if (CONTAINER_REMOVE(cert_maps, cert_map) != 0) | 
| 3149 | 0 |                 goto fail; | 
| 3150 | 0 |             continue; | 
| 3151 | 0 |         } | 
| 3152 | 0 |         DEBUGMSGT(("cert:map:secname", "%" NETSNMP_PRIz "d matches for %s\n", | 
| 3153 | 0 |                    results->size, cert_map->fingerprint)); | 
| 3154 |  |         /* | 
| 3155 |  |          * first entry is a freebie | 
| 3156 |  |          */ | 
| 3157 | 0 |         entry = (netsnmp_cert_map*)results->array[0]; | 
| 3158 | 0 |         if (_fill_cert_map(cert_map, entry) != 0) | 
| 3159 | 0 |             goto fail; | 
| 3160 |  |  | 
| 3161 |  |         /* | 
| 3162 |  |          * additional entries must be allocated/inserted | 
| 3163 |  |          */ | 
| 3164 | 0 |         if (results->size > 1) { | 
| 3165 | 0 |             for(j=1; j < results->size; ++j) { | 
| 3166 | 0 |                 entry = (netsnmp_cert_map*)results->array[j]; | 
| 3167 | 0 |                 new_cert_map = netsnmp_cert_map_alloc(entry->fingerprint, | 
| 3168 | 0 |                                                       entry->ocert); | 
| 3169 | 0 |                 if (NULL == new_cert_map) { | 
| 3170 | 0 |                     snmp_log(LOG_ERR, | 
| 3171 | 0 |                              "could not allocate new cert map entry\n"); | 
| 3172 | 0 |                     goto fail; | 
| 3173 | 0 |                 } | 
| 3174 | 0 |                 if (_fill_cert_map(new_cert_map, entry) != 0) { | 
| 3175 | 0 |                     netsnmp_cert_map_free(new_cert_map); | 
| 3176 | 0 |                     goto fail; | 
| 3177 | 0 |                 } | 
| 3178 | 0 |                 new_cert_map->ocert = cert_map->ocert; | 
| 3179 | 0 |                 if (CONTAINER_INSERT(cert_maps,new_cert_map) != 0) { | 
| 3180 | 0 |                     netsnmp_cert_map_free(new_cert_map); | 
| 3181 | 0 |                     goto fail; | 
| 3182 | 0 |                 } | 
| 3183 | 0 |             } /* for results */ | 
| 3184 | 0 |         } /* results size > 1 */ | 
| 3185 |  |  | 
| 3186 | 0 |         free(results->array); | 
| 3187 | 0 |         SNMP_FREE(results); | 
| 3188 | 0 |     } | 
| 3189 | 0 |     ITERATOR_RELEASE(itr); | 
| 3190 | 0 |     CONTAINER_FREE(new_maps); | 
| 3191 |  | 
 | 
| 3192 | 0 |     DEBUGMSGT(("cert:map:secname", | 
| 3193 | 0 |                "found %" NETSNMP_PRIz "d matches for fingerprints\n", | 
| 3194 | 0 |                CONTAINER_SIZE(cert_maps))); | 
| 3195 | 0 |     return 0; | 
| 3196 |  |  | 
| 3197 | 0 |   fail: | 
| 3198 | 0 |     if (results) { | 
| 3199 | 0 |         free(results->array); | 
| 3200 | 0 |         free(results); | 
| 3201 | 0 |     } | 
| 3202 | 0 |     ITERATOR_RELEASE(itr); | 
| 3203 | 0 |     CONTAINER_FREE(new_maps); | 
| 3204 | 0 |     return -1; | 
| 3205 | 0 | } | 
| 3206 |  |  | 
| 3207 |  | /* *************************************************************************** | 
| 3208 |  |  * *************************************************************************** | 
| 3209 |  |  * | 
| 3210 |  |  * | 
| 3211 |  |  * snmpTlstmParmsTable data | 
| 3212 |  |  * | 
| 3213 |  |  * | 
| 3214 |  |  * *************************************************************************** | 
| 3215 |  |  * ***************************************************************************/ | 
| 3216 | 0 | #define PARAMS_CONFIG_TOKEN "snmpTlstmParams" | 
| 3217 |  | static void _parse_params(const char *token, char *line); | 
| 3218 |  |  | 
| 3219 |  | static void | 
| 3220 |  | _init_tlstmParams(void) | 
| 3221 | 0 | { | 
| 3222 | 0 |     const char *params_help =  | 
| 3223 | 0 |         PARAMS_CONFIG_TOKEN " targetParamsName hashType:fingerPrint"; | 
| 3224 |  |      | 
| 3225 |  |     /* | 
| 3226 |  |      * container for snmpTlstmParamsTable data | 
| 3227 |  |      */ | 
| 3228 | 0 |     _tlstmParams = netsnmp_container_find("tlstmParams:string"); | 
| 3229 | 0 |     if (NULL == _tlstmParams) | 
| 3230 | 0 |         snmp_log(LOG_ERR, | 
| 3231 | 0 |                  "error creating sub-container for tlstmParamsTable\n"); | 
| 3232 | 0 |     else | 
| 3233 | 0 |         _tlstmParams->container_name = strdup("tlstmParams"); | 
| 3234 |  | 
 | 
| 3235 | 0 |     register_config_handler(NULL, PARAMS_CONFIG_TOKEN, _parse_params, NULL, | 
| 3236 | 0 |                                 params_help); | 
| 3237 | 0 | } | 
| 3238 |  |  | 
| 3239 |  | #ifndef NETSNMP_FEATURE_REMOVE_TLSTMPARAMS_CONTAINER | 
| 3240 |  | netsnmp_container * | 
| 3241 |  | netsnmp_tlstmParams_container(void) | 
| 3242 | 0 | { | 
| 3243 | 0 |     return _tlstmParams; | 
| 3244 | 0 | } | 
| 3245 |  | #endif /* NETSNMP_FEATURE_REMOVE_TLSTMPARAMS_CONTAINER */ | 
| 3246 |  |  | 
| 3247 |  | snmpTlstmParams * | 
| 3248 |  | netsnmp_tlstmParams_create(const char *name, int hashType, const char *fp, | 
| 3249 |  |                            int fp_len) | 
| 3250 | 0 | { | 
| 3251 | 0 |     snmpTlstmParams *stp = SNMP_MALLOC_TYPEDEF(snmpTlstmParams); | 
| 3252 | 0 |     if (NULL == stp) | 
| 3253 | 0 |         return NULL; | 
| 3254 |  |  | 
| 3255 | 0 |     if (name) | 
| 3256 | 0 |         stp->name = strdup(name); | 
| 3257 | 0 |     stp->hashType = hashType; | 
| 3258 | 0 |     if (fp) | 
| 3259 | 0 |         stp->fingerprint = strdup(fp); | 
| 3260 | 0 |     DEBUGMSGT(("9:tlstmParams:create", "%p: %s\n", stp, | 
| 3261 | 0 |                stp->name ? stp->name : "null")); | 
| 3262 |  | 
 | 
| 3263 | 0 |     return stp; | 
| 3264 | 0 | } | 
| 3265 |  |  | 
| 3266 |  | void | 
| 3267 |  | netsnmp_tlstmParams_free(snmpTlstmParams *stp) | 
| 3268 | 0 | { | 
| 3269 | 0 |     if (NULL == stp) | 
| 3270 | 0 |         return; | 
| 3271 |  |  | 
| 3272 | 0 |     DEBUGMSGT(("9:tlstmParams:release", "%p %s\n", stp, | 
| 3273 | 0 |                stp->name ? stp->name : "null")); | 
| 3274 | 0 |     SNMP_FREE(stp->name); | 
| 3275 | 0 |     SNMP_FREE(stp->fingerprint); | 
| 3276 | 0 |     free(stp); /* SNMP_FREE pointless on parameter */ | 
| 3277 | 0 | } | 
| 3278 |  |  | 
| 3279 |  | snmpTlstmParams * | 
| 3280 |  | netsnmp_tlstmParams_restore_common(char **line) | 
| 3281 | 0 | { | 
| 3282 | 0 |     snmpTlstmParams  *stp; | 
| 3283 | 0 |     char             *tmp, buf[SNMP_MAXBUF_SMALL]; | 
| 3284 | 0 |     size_t            len; | 
| 3285 |  | 
 | 
| 3286 | 0 |     if ((NULL == line) || (NULL == *line)) | 
| 3287 | 0 |         return NULL; | 
| 3288 |  |  | 
| 3289 |  |     /** need somewhere to save rows */ | 
| 3290 | 0 |     netsnmp_assert(_tlstmParams); | 
| 3291 |  | 
 | 
| 3292 | 0 |     stp = netsnmp_tlstmParams_create(NULL, 0, NULL, 0); | 
| 3293 | 0 |     if (NULL == stp) | 
| 3294 | 0 |         return NULL; | 
| 3295 |  |  | 
| 3296 |  |     /** name */ | 
| 3297 | 0 |     len = sizeof(buf); | 
| 3298 | 0 |     tmp = buf; | 
| 3299 | 0 |     *line = read_config_read_octet_string(*line, (u_char **)&tmp, &len); | 
| 3300 | 0 |     tmp[len] = 0; | 
| 3301 |  |     /** xxx-rks: validate snmpadminstring? */ | 
| 3302 | 0 |     if (len) | 
| 3303 | 0 |         stp->name = strdup(buf); | 
| 3304 |  |  | 
| 3305 |  |     /** fingerprint hash type*/ | 
| 3306 | 0 |     len = sizeof(buf); | 
| 3307 | 0 |     tmp = buf; | 
| 3308 | 0 |     *line = read_config_read_octet_string(*line, (u_char **)&tmp, &len); | 
| 3309 | 0 |     tmp[len] = 0; | 
| 3310 | 0 |     if ((buf[0] == '-') && (buf[1] == '-')) { | 
| 3311 | 0 |         stp->hashType = netsnmp_cert_parse_hash_type(&buf[2]); | 
| 3312 |  |  | 
| 3313 |  |         /** set up for fingerprint */ | 
| 3314 | 0 |         len = sizeof(buf); | 
| 3315 | 0 |         tmp = buf; | 
| 3316 | 0 |         *line = read_config_read_octet_string(*line, (u_char **)&tmp, &len); | 
| 3317 | 0 |         tmp[len] = 0; | 
| 3318 | 0 |     } | 
| 3319 | 0 |     else | 
| 3320 | 0 |         stp->hashType =NS_HASH_SHA1; | 
| 3321 |  |      | 
| 3322 | 0 |     netsnmp_fp_lowercase_and_strip_colon(buf); | 
| 3323 | 0 |     stp->fingerprint = strdup(buf); | 
| 3324 | 0 |     stp->fingerprint_len = strlen(buf); | 
| 3325 |  | 
 | 
| 3326 | 0 |     DEBUGMSGTL(("tlstmParams:restore:common", "name '%s'\n", stp->name)); | 
| 3327 |  | 
 | 
| 3328 | 0 |     return stp; | 
| 3329 | 0 | } | 
| 3330 |  |  | 
| 3331 |  | int | 
| 3332 |  | netsnmp_tlstmParams_add(snmpTlstmParams *stp) | 
| 3333 | 0 | { | 
| 3334 | 0 |     if (NULL == stp) | 
| 3335 | 0 |         return -1; | 
| 3336 |  |  | 
| 3337 | 0 |     DEBUGMSGTL(("tlstmParams:add", "adding entry %p %s\n", stp, stp->name)); | 
| 3338 |  | 
 | 
| 3339 | 0 |     if (CONTAINER_INSERT(_tlstmParams, stp) != 0) { | 
| 3340 | 0 |         snmp_log(LOG_ERR, "error inserting tlstmParams %s", stp->name); | 
| 3341 | 0 |         netsnmp_tlstmParams_free(stp); | 
| 3342 | 0 |         return -1; | 
| 3343 | 0 |     } | 
| 3344 |  |  | 
| 3345 | 0 |     return 0; | 
| 3346 | 0 | } | 
| 3347 |  |  | 
| 3348 |  | #ifndef NETSNMP_FEATURE_REMOVE_TLSTMPARAMS_REMOVE | 
| 3349 |  | int | 
| 3350 |  | netsnmp_tlstmParams_remove(snmpTlstmParams *stp) | 
| 3351 | 0 | { | 
| 3352 | 0 |     if (NULL == stp) | 
| 3353 | 0 |         return -1; | 
| 3354 |  |  | 
| 3355 | 0 |     DEBUGMSGTL(("tlstmParams:remove", "removing entry %p %s\n", stp, | 
| 3356 | 0 |                 stp->name)); | 
| 3357 |  | 
 | 
| 3358 | 0 |     if (CONTAINER_REMOVE(_tlstmParams, stp) != 0) { | 
| 3359 | 0 |         snmp_log(LOG_ERR, "error removing tlstmParams %s", stp->name); | 
| 3360 | 0 |         return -1; | 
| 3361 | 0 |     } | 
| 3362 |  |  | 
| 3363 | 0 |     return 0; | 
| 3364 | 0 | } | 
| 3365 |  | #endif /* NETSNMP_FEATURE_REMOVE_TLSTMPARAMS_REMOVE */ | 
| 3366 |  |  | 
| 3367 |  | #ifndef NETSNMP_FEATURE_REMOVE_TLSTMPARAMS_FIND | 
| 3368 |  | snmpTlstmParams * | 
| 3369 |  | netsnmp_tlstmParams_find(snmpTlstmParams *stp) | 
| 3370 | 0 | { | 
| 3371 | 0 |     snmpTlstmParams *found; | 
| 3372 |  | 
 | 
| 3373 | 0 |     if (NULL == stp) | 
| 3374 | 0 |         return NULL; | 
| 3375 |  |  | 
| 3376 | 0 |     found = CONTAINER_FIND(_tlstmParams, stp); | 
| 3377 | 0 |     return found; | 
| 3378 | 0 | } | 
| 3379 |  | #endif /* NETSNMP_FEATURE_REMOVE_TLSTMPARAMS_FIND */ | 
| 3380 |  |  | 
| 3381 |  | static void | 
| 3382 |  | _parse_params(const char *token, char *line) | 
| 3383 | 0 | { | 
| 3384 | 0 |     snmpTlstmParams *stp = netsnmp_tlstmParams_restore_common(&line); | 
| 3385 |  | 
 | 
| 3386 | 0 |     if (!stp) | 
| 3387 | 0 |         return; | 
| 3388 |  |  | 
| 3389 | 0 |     stp->flags = TLSTM_PARAMS_FROM_CONFIG | TLSTM_PARAMS_NONVOLATILE; | 
| 3390 |  | 
 | 
| 3391 | 0 |     netsnmp_tlstmParams_add(stp); | 
| 3392 | 0 | } | 
| 3393 |  |  | 
| 3394 |  | static char * | 
| 3395 |  | _find_tlstmParams_fingerprint(const char *name) | 
| 3396 | 0 | { | 
| 3397 | 0 |     snmpTlstmParams lookup_key, *result; | 
| 3398 |  | 
 | 
| 3399 | 0 |     if (NULL == name) | 
| 3400 | 0 |         return NULL; | 
| 3401 |  |  | 
| 3402 | 0 |     lookup_key.name = NETSNMP_REMOVE_CONST(char*, name); | 
| 3403 |  | 
 | 
| 3404 | 0 |     result = CONTAINER_FIND(_tlstmParams, &lookup_key); | 
| 3405 | 0 |     if ((NULL == result) || (NULL == result->fingerprint)) | 
| 3406 | 0 |         return NULL; | 
| 3407 |  |  | 
| 3408 | 0 |     return result->fingerprint; | 
| 3409 | 0 | } | 
| 3410 |  | /* | 
| 3411 |  |  * END snmpTlstmParmsTable data | 
| 3412 |  |  * ***************************************************************************/ | 
| 3413 |  |  | 
| 3414 |  | /* *************************************************************************** | 
| 3415 |  |  * *************************************************************************** | 
| 3416 |  |  * | 
| 3417 |  |  * | 
| 3418 |  |  * snmpTlstmAddrTable data | 
| 3419 |  |  * | 
| 3420 |  |  * | 
| 3421 |  |  * *************************************************************************** | 
| 3422 |  |  * ***************************************************************************/ | 
| 3423 | 0 | #define ADDR_CONFIG_TOKEN "snmpTlstmAddr" | 
| 3424 |  | static void _parse_addr(const char *token, char *line); | 
| 3425 |  |  | 
| 3426 |  | static void | 
| 3427 |  | _init_tlstmAddr(void) | 
| 3428 | 0 | { | 
| 3429 | 0 |     const char *addr_help =  | 
| 3430 | 0 |         ADDR_CONFIG_TOKEN " targetAddrName hashType:fingerprint serverIdentity"; | 
| 3431 |  |      | 
| 3432 |  |     /* | 
| 3433 |  |      * container for snmpTlstmAddrTable data | 
| 3434 |  |      */ | 
| 3435 | 0 |     _tlstmAddr = netsnmp_container_find("tlstmAddr:string"); | 
| 3436 | 0 |     if (NULL == _tlstmAddr) | 
| 3437 | 0 |         snmp_log(LOG_ERR, | 
| 3438 | 0 |                  "error creating sub-container for tlstmAddrTable\n"); | 
| 3439 | 0 |     else | 
| 3440 | 0 |         _tlstmAddr->container_name = strdup("tlstmAddr"); | 
| 3441 |  | 
 | 
| 3442 | 0 |     register_config_handler(NULL, ADDR_CONFIG_TOKEN, _parse_addr, NULL, | 
| 3443 | 0 |                             addr_help); | 
| 3444 | 0 | } | 
| 3445 |  |  | 
| 3446 |  | #ifndef NETSNMP_FEATURE_REMOVE_TLSTMADDR_CONTAINER | 
| 3447 |  | netsnmp_container * | 
| 3448 |  | netsnmp_tlstmAddr_container(void) | 
| 3449 | 0 | { | 
| 3450 | 0 |     return _tlstmAddr; | 
| 3451 | 0 | } | 
| 3452 |  | #endif /* NETSNMP_FEATURE_REMOVE_TLSTMADDR_CONTAINER */ | 
| 3453 |  |  | 
| 3454 |  | /* | 
| 3455 |  |  * create a new row in the table  | 
| 3456 |  |  */ | 
| 3457 |  | snmpTlstmAddr * | 
| 3458 |  | netsnmp_tlstmAddr_create(char *targetAddrName) | 
| 3459 | 0 | { | 
| 3460 | 0 |     snmpTlstmAddr *entry; | 
| 3461 |  | 
 | 
| 3462 | 0 |     if (NULL == targetAddrName) | 
| 3463 | 0 |         return NULL; | 
| 3464 |  |  | 
| 3465 | 0 |     entry = SNMP_MALLOC_TYPEDEF(snmpTlstmAddr); | 
| 3466 | 0 |     if (!entry) | 
| 3467 | 0 |         return NULL; | 
| 3468 |  |  | 
| 3469 | 0 |     DEBUGMSGT(("tlstmAddr:entry:create", "entry %p %s\n", entry, | 
| 3470 | 0 |                targetAddrName ? targetAddrName : "NULL")); | 
| 3471 |  | 
 | 
| 3472 | 0 |     entry->name = strdup(targetAddrName); | 
| 3473 |  | 
 | 
| 3474 | 0 |     return entry; | 
| 3475 | 0 | } | 
| 3476 |  |  | 
| 3477 |  | void | 
| 3478 |  | netsnmp_tlstmAddr_free(snmpTlstmAddr *entry) | 
| 3479 | 0 | { | 
| 3480 | 0 |     if (!entry) | 
| 3481 | 0 |         return; | 
| 3482 |  |  | 
| 3483 | 0 |     SNMP_FREE(entry->name); | 
| 3484 | 0 |     SNMP_FREE(entry->fingerprint); | 
| 3485 | 0 |     SNMP_FREE(entry->identity); | 
| 3486 | 0 |     free(entry); | 
| 3487 | 0 | } | 
| 3488 |  |  | 
| 3489 |  | int | 
| 3490 |  | netsnmp_tlstmAddr_restore_common(char **line, char *name, size_t *name_len, | 
| 3491 |  |                                  char *id, size_t *id_len, char *fp, | 
| 3492 |  |                                  size_t *fp_len, u_char *ht) | 
| 3493 | 0 | { | 
| 3494 | 0 |     size_t fp_len_save = *fp_len - 1; | 
| 3495 |  |  | 
| 3496 |  |     /* | 
| 3497 |  |      * Calling this function with name == NULL, fp == NULL or id == NULL would | 
| 3498 |  |      * trigger a memory leak. | 
| 3499 |  |      */ | 
| 3500 | 0 |     if (!name || !fp || !id || *name_len == 0 || *id_len == 0 || *fp_len == 0) | 
| 3501 | 0 |         return -1; | 
| 3502 |  |  | 
| 3503 | 0 |     (*name_len)--; | 
| 3504 | 0 |     (*fp_len)--; | 
| 3505 | 0 |     (*id_len)--; | 
| 3506 |  | 
 | 
| 3507 | 0 |     *line = read_config_read_octet_string(*line, (u_char **)&name, name_len); | 
| 3508 | 0 |     if (NULL == *line) { | 
| 3509 | 0 |         config_perror("incomplete line"); | 
| 3510 | 0 |         return -1; | 
| 3511 | 0 |     } | 
| 3512 | 0 |     name[*name_len] = 0; | 
| 3513 |  | 
 | 
| 3514 | 0 |     *line = read_config_read_octet_string(*line, (u_char **)&fp, fp_len); | 
| 3515 | 0 |     if (NULL == *line) { | 
| 3516 | 0 |         config_perror("incomplete line"); | 
| 3517 | 0 |         return -1; | 
| 3518 | 0 |     } | 
| 3519 | 0 |     fp[*fp_len] = 0; | 
| 3520 | 0 |     if ((fp[0] == '-') && (fp[1] == '-')) { | 
| 3521 | 0 |         *ht = netsnmp_cert_parse_hash_type(&fp[2]); | 
| 3522 |  |          | 
| 3523 |  |         /** set up for fingerprint */ | 
| 3524 | 0 |         *fp_len = fp_len_save; | 
| 3525 | 0 |         *line = read_config_read_octet_string(*line, (u_char **)&fp, fp_len); | 
| 3526 | 0 |         fp[*fp_len] = 0; | 
| 3527 | 0 |     } | 
| 3528 | 0 |     else | 
| 3529 | 0 |         *ht = NS_HASH_SHA1; | 
| 3530 | 0 |     netsnmp_fp_lowercase_and_strip_colon(fp); | 
| 3531 | 0 |     *fp_len = strlen(fp); | 
| 3532 |  |      | 
| 3533 | 0 |     *line = read_config_read_octet_string(*line, (u_char **)&id, id_len); | 
| 3534 | 0 |     id[*id_len] = 0; | 
| 3535 |  |      | 
| 3536 | 0 |     if (*ht <= NS_HASH_NONE || *ht > NS_HASH_MAX) { | 
| 3537 | 0 |         config_perror("invalid algorithm for fingerprint"); | 
| 3538 | 0 |         return -1; | 
| 3539 | 0 |     } | 
| 3540 |  |  | 
| 3541 | 0 |     if ((0 == *fp_len) && ((0 == *id_len || (*id_len == 1 && id[0] == '*')))) { | 
| 3542 |  |         /* | 
| 3543 |  |          * empty fingerprint not allowed with '*' identity | 
| 3544 |  |          */ | 
| 3545 | 0 |         config_perror("must specify fingerprint for '*' identity"); | 
| 3546 | 0 |         return -1; | 
| 3547 | 0 |     } | 
| 3548 |  |  | 
| 3549 | 0 |     return 0; | 
| 3550 | 0 | } | 
| 3551 |  |  | 
| 3552 |  | int | 
| 3553 |  | netsnmp_tlstmAddr_add(snmpTlstmAddr *entry) | 
| 3554 | 0 | { | 
| 3555 | 0 |     if (!entry) | 
| 3556 | 0 |         return -1; | 
| 3557 |  |  | 
| 3558 | 0 |     DEBUGMSGTL(("tlstmAddr:add", "adding entry %p %s %s\n", | 
| 3559 | 0 |                 entry, entry->name, entry->fingerprint)); | 
| 3560 | 0 |     if (CONTAINER_INSERT(_tlstmAddr, entry) != 0) { | 
| 3561 | 0 |         snmp_log(LOG_ERR, "could not insert addr %s", entry->name); | 
| 3562 | 0 |         netsnmp_tlstmAddr_free(entry); | 
| 3563 | 0 |         return -1; | 
| 3564 | 0 |     } | 
| 3565 |  |  | 
| 3566 | 0 |     return 0; | 
| 3567 | 0 | } | 
| 3568 |  |  | 
| 3569 |  | #ifndef NETSNMP_FEATURE_REMOVE_TLSTMADDR_REMOVE | 
| 3570 |  | int | 
| 3571 |  | netsnmp_tlstmAddr_remove(snmpTlstmAddr *entry) | 
| 3572 | 0 | { | 
| 3573 | 0 |     if (!entry) | 
| 3574 | 0 |         return -1; | 
| 3575 |  |  | 
| 3576 | 0 |     if (CONTAINER_REMOVE(_tlstmAddr, entry) != 0) { | 
| 3577 | 0 |         snmp_log(LOG_ERR, "could not remove addr %s", entry->name); | 
| 3578 | 0 |         return -1; | 
| 3579 | 0 |     } | 
| 3580 |  |  | 
| 3581 | 0 |     return 0; | 
| 3582 | 0 | } | 
| 3583 |  | #endif /* NETSNMP_FEATURE_REMOVE_TLSTMADDR_REMOVE */ | 
| 3584 |  |  | 
| 3585 |  | static void | 
| 3586 |  | _parse_addr(const char *token, char *line) | 
| 3587 | 0 | { | 
| 3588 | 0 |     snmpTlstmAddr *entry; | 
| 3589 | 0 |     char           name[SNMPADMINLENGTH  + 1], id[SNMPADMINLENGTH  + 1], | 
| 3590 | 0 |                    fingerprint[SNMPTLSFINGERPRINT_MAX_LEN + 1]; | 
| 3591 | 0 |     size_t         name_len = sizeof(name), id_len = sizeof(id), | 
| 3592 | 0 |                    fp_len = sizeof(fingerprint); | 
| 3593 | 0 |     u_char         hashType; | 
| 3594 | 0 |     int            rc; | 
| 3595 |  | 
 | 
| 3596 | 0 |     rc = netsnmp_tlstmAddr_restore_common(&line, name, &name_len, id, &id_len, | 
| 3597 | 0 |                                           fingerprint, &fp_len, &hashType); | 
| 3598 | 0 |     if (rc < 0) | 
| 3599 | 0 |         return; | 
| 3600 |  |  | 
| 3601 | 0 |     if (NULL != line) | 
| 3602 | 0 |         config_pwarn("ignore extra tokens on line"); | 
| 3603 |  | 
 | 
| 3604 | 0 |     entry = netsnmp_tlstmAddr_create(name); | 
| 3605 | 0 |     if (NULL == entry) | 
| 3606 | 0 |         return; | 
| 3607 |  |  | 
| 3608 | 0 |     entry->flags |= TLSTM_ADDR_FROM_CONFIG; | 
| 3609 | 0 |     entry->hashType = hashType; | 
| 3610 | 0 |     if (fp_len) | 
| 3611 | 0 |         entry->fingerprint = strdup(fingerprint); | 
| 3612 | 0 |     if (id_len) | 
| 3613 | 0 |         entry->identity = strdup(id); | 
| 3614 |  | 
 | 
| 3615 | 0 |     netsnmp_tlstmAddr_add(entry); | 
| 3616 | 0 | } | 
| 3617 |  |  | 
| 3618 |  | static char * | 
| 3619 |  | _find_tlstmAddr_fingerprint(const char *name) | 
| 3620 | 0 | { | 
| 3621 | 0 |     snmpTlstmAddr    lookup_key, *result; | 
| 3622 |  | 
 | 
| 3623 | 0 |     if (NULL == name) | 
| 3624 | 0 |         return NULL; | 
| 3625 |  |  | 
| 3626 | 0 |     lookup_key.name = NETSNMP_REMOVE_CONST(char*, name); | 
| 3627 |  | 
 | 
| 3628 | 0 |     result = CONTAINER_FIND(_tlstmAddr, &lookup_key); | 
| 3629 | 0 |     if (NULL == result) | 
| 3630 | 0 |         return NULL; | 
| 3631 |  |  | 
| 3632 | 0 |     return result->fingerprint; | 
| 3633 | 0 | } | 
| 3634 |  |  | 
| 3635 |  | #ifndef NETSNMP_FEATURE_REMOVE_TLSTMADDR_GET_SERVERID | 
| 3636 |  | char * | 
| 3637 |  | netsnmp_tlstmAddr_get_serverId(const char *name) | 
| 3638 | 0 | { | 
| 3639 | 0 |     snmpTlstmAddr    lookup_key, *result; | 
| 3640 |  | 
 | 
| 3641 | 0 |     if (NULL == name) | 
| 3642 | 0 |         return NULL; | 
| 3643 |  |  | 
| 3644 | 0 |     lookup_key.name = NETSNMP_REMOVE_CONST(char*, name); | 
| 3645 |  | 
 | 
| 3646 | 0 |     result = CONTAINER_FIND(_tlstmAddr, &lookup_key); | 
| 3647 | 0 |     if (NULL == result) | 
| 3648 | 0 |         return NULL; | 
| 3649 |  |  | 
| 3650 | 0 |     return result->identity; | 
| 3651 | 0 | } | 
| 3652 |  | #endif /* NETSNMP_FEATURE_REMOVE_TLSTMADDR_GET_SERVERID */ | 
| 3653 |  | /* | 
| 3654 |  |  * END snmpTlstmAddrTable data | 
| 3655 |  |  * ***************************************************************************/ | 
| 3656 |  |  | 
| 3657 |  | #else | 
| 3658 |  | netsnmp_feature_unused(cert_util); | 
| 3659 |  | #endif /* NETSNMP_FEATURE_REMOVE_CERT_UTIL */ | 
| 3660 |  | netsnmp_feature_unused(cert_util); | 
| 3661 |  | #endif /* defined(NETSNMP_USE_OPENSSL) && defined(HAVE_LIBSSL) && NETSNMP_TRANSPORT_TLSBASE_DOMAIN */ |