net-snmp 5.7
|
00001 #include <net-snmp/net-snmp-config.h> 00002 #include <net-snmp/net-snmp-features.h> 00003 00004 #if defined(NETSNMP_USE_OPENSSL) && defined(HAVE_LIBSSL) 00005 netsnmp_feature_child_of(cert_util_all, libnetsnmp) 00006 netsnmp_feature_child_of(cert_util, cert_util_all) 00007 #ifdef NETSNMP_FEATURE_REQUIRE_CERT_UTIL 00008 netsnmp_feature_require(container_directory) 00009 netsnmp_feature_require(container_fifo) 00010 netsnmp_feature_require(container_dup) 00011 netsnmp_feature_require(container_free_all) 00012 netsnmp_feature_require(subcontainer_find) 00013 00014 netsnmp_feature_child_of(cert_map_remove, netsnmp_unused) 00015 netsnmp_feature_child_of(cert_map_find, netsnmp_unused) 00016 netsnmp_feature_child_of(tlstmparams_external, cert_util_all) 00017 netsnmp_feature_child_of(tlstmparams_container, tlstmparams_external) 00018 netsnmp_feature_child_of(tlstmparams_remove, tlstmparams_external) 00019 netsnmp_feature_child_of(tlstmparams_find, tlstmparams_external) 00020 netsnmp_feature_child_of(tlstmAddr_remove, netsnmp_unused) 00021 netsnmp_feature_child_of(tlstmaddr_external, cert_util_all) 00022 netsnmp_feature_child_of(tlstmaddr_container, tlstmaddr_external) 00023 netsnmp_feature_child_of(tlstmAddr_get_serverId, tlstmaddr_external) 00024 00025 netsnmp_feature_child_of(cert_fingerprints, cert_util_all) 00026 netsnmp_feature_child_of(tls_fingerprint_build, cert_util_all) 00027 00028 #endif /* NETSNMP_FEATURE_REQUIRE_CERT_UTIL */ 00029 00030 #ifndef NETSNMP_FEATURE_REMOVE_CERT_UTIL 00031 00032 #include <ctype.h> 00033 00034 #if HAVE_STDLIB_H 00035 #include <stdlib.h> 00036 #endif 00037 00038 #if HAVE_STRING_H 00039 #include <string.h> 00040 #else 00041 #include <strings.h> 00042 #endif 00043 00044 #if HAVE_SYS_STAT_H 00045 # include <sys/stat.h> 00046 #endif 00047 #if HAVE_DIRENT_H 00048 # include <dirent.h> 00049 # define NAMLEN(dirent) strlen((dirent)->d_name) 00050 #else 00051 # define dirent direct 00052 # define NAMLEN(dirent) (dirent)->d_namlen 00053 # if HAVE_SYS_NDIR_H 00054 # include <sys/ndir.h> 00055 # endif 00056 # if HAVE_SYS_DIR_H 00057 # include <sys/dir.h> 00058 # endif 00059 # if HAVE_NDIR_H 00060 # include <ndir.h> 00061 # endif 00062 #endif 00063 00064 #if HAVE_DMALLOC_H 00065 #include <dmalloc.h> 00066 #endif 00067 00068 #include <net-snmp/types.h> 00069 #include <net-snmp/output_api.h> 00070 #include <net-snmp/config_api.h> 00071 00072 #include <net-snmp/library/snmp_assert.h> 00073 #include <net-snmp/library/snmp_transport.h> 00074 #include <net-snmp/library/system.h> 00075 #include <net-snmp/library/tools.h> 00076 #include <net-snmp/library/container.h> 00077 #include <net-snmp/library/data_list.h> 00078 #include <net-snmp/library/file_utils.h> 00079 #include <net-snmp/library/dir_utils.h> 00080 #include <net-snmp/library/read_config.h> 00081 00082 #include <openssl/ssl.h> 00083 #include <openssl/err.h> 00084 #include <openssl/x509v3.h> 00085 #include <net-snmp/library/cert_util.h> 00086 #include <net-snmp/library/snmp_openssl.h> 00087 00088 #ifndef NAME_MAX 00089 #define NAME_MAX 255 00090 #endif 00091 00092 /* 00093 * bump this value whenever cert index format changes, so indexes 00094 * will be regenerated with new format. 00095 */ 00096 #define CERT_INDEX_FORMAT 1 00097 00098 static netsnmp_container *_certs = NULL; 00099 static netsnmp_container *_keys = NULL; 00100 static netsnmp_container *_maps = NULL; 00101 static netsnmp_container *_tlstmParams = NULL; 00102 static netsnmp_container *_tlstmAddr = NULL; 00103 static struct snmp_enum_list *_certindexes = NULL; 00104 00105 static netsnmp_container *_trusted_certs = NULL; 00106 00107 static void _setup_containers(void); 00108 00109 static void _cert_indexes_load(void); 00110 static void _cert_free(netsnmp_cert *cert, void *context); 00111 static void _key_free(netsnmp_key *key, void *context); 00112 static int _cert_compare(netsnmp_cert *lhs, netsnmp_cert *rhs); 00113 static int _cert_sn_compare(netsnmp_cert *lhs, netsnmp_cert *rhs); 00114 static int _cert_sn_ncompare(netsnmp_cert *lhs, netsnmp_cert *rhs); 00115 static int _cert_cn_compare(netsnmp_cert *lhs, netsnmp_cert *rhs); 00116 static int _cert_fn_compare(netsnmp_cert_common *lhs, 00117 netsnmp_cert_common *rhs); 00118 static int _cert_fn_ncompare(netsnmp_cert_common *lhs, 00119 netsnmp_cert_common *rhs); 00120 static void _find_partner(netsnmp_cert *cert, netsnmp_key *key); 00121 static netsnmp_cert *_find_issuer(netsnmp_cert *cert); 00122 static netsnmp_void_array *_cert_find_subset_fn(const char *filename, 00123 const char *directory); 00124 static netsnmp_void_array *_cert_find_subset_sn(const char *subject); 00125 static netsnmp_void_array *_key_find_subset(const char *filename); 00126 static netsnmp_cert *_cert_find_fp(const char *fingerprint); 00127 static char *_find_tlstmParams_fingerprint(const char *param); 00128 static char *_find_tlstmAddr_fingerprint(const char *name); 00129 static const char *_mode_str(u_char mode); 00130 static const char *_where_str(u_int what); 00131 void netsnmp_cert_dump_all(void); 00132 00133 int netsnmp_cert_load_x509(netsnmp_cert *cert); 00134 00135 void netsnmp_cert_free(netsnmp_cert *cert); 00136 void netsnmp_key_free(netsnmp_key *key); 00137 00138 static int _certindex_add( const char *dirname, int i ); 00139 00140 static int _time_filter(netsnmp_file *f, struct stat *idx); 00141 00142 static void _init_tlstmCertToTSN(void); 00143 #define TRUSTCERT_CONFIG_TOKEN "trustCert" 00144 static void _parse_trustcert(const char *token, char *line); 00145 00146 static void _init_tlstmParams(void); 00147 static void _init_tlstmAddr(void); 00148 00150 static const char _modes[][256] = 00151 { 00152 "none", 00153 "identity", 00154 "remote_peer", 00155 "identity+remote_peer", 00156 "reserved1", 00157 "reserved1+identity", 00158 "reserved1+remote_peer", 00159 "reserved1+identity+remote_peer", 00160 "CA", 00161 "CA+identity", 00162 "CA+remote_peer", 00163 "CA+identity+remote_peer", 00164 "CA+reserved1", 00165 "CA+reserved1+identity", 00166 "CA+reserved1+remote_peer", 00167 "CA+reserved1+identity+remote_peer", 00168 }; 00169 00170 /* ##################################################################### 00171 * 00172 * init and shutdown functions 00173 * 00174 */ 00175 00176 void 00177 _netsnmp_release_trustcerts(void) 00178 { 00179 if (NULL != _trusted_certs) { 00180 CONTAINER_FREE_ALL(_trusted_certs, NULL); 00181 CONTAINER_FREE(_trusted_certs); 00182 _trusted_certs = NULL; 00183 } 00184 } 00185 00186 void 00187 _setup_trusted_certs(void) 00188 { 00189 _trusted_certs = netsnmp_container_find("trusted_certs:fifo"); 00190 if (NULL == _trusted_certs) { 00191 snmp_log(LOG_ERR, "could not create container for trusted certs\n"); 00192 netsnmp_certs_shutdown(); 00193 return; 00194 } 00195 _trusted_certs->container_name = strdup("trusted certificates"); 00196 _trusted_certs->free_item = (netsnmp_container_obj_func*) free; 00197 _trusted_certs->compare = (netsnmp_container_compare*) strcmp; 00198 } 00199 00200 /* 00201 * secname mapping for servers. 00202 */ 00203 void 00204 netsnmp_certs_agent_init(void) 00205 { 00206 _init_tlstmCertToTSN(); 00207 _init_tlstmParams(); 00208 _init_tlstmAddr(); 00209 } 00210 00211 void 00212 netsnmp_certs_init(void) 00213 { 00214 const char *trustCert_help = TRUSTCERT_CONFIG_TOKEN 00215 " FINGERPRINT|FILENAME"; 00216 00217 register_config_handler("snmp", TRUSTCERT_CONFIG_TOKEN, 00218 _parse_trustcert, _netsnmp_release_trustcerts, 00219 trustCert_help); 00220 _setup_containers(); 00221 00223 se_add_pair_to_slist("cert_types", strdup("pem"), NS_CERT_TYPE_PEM); 00224 se_add_pair_to_slist("cert_types", strdup("crt"), NS_CERT_TYPE_DER); 00225 se_add_pair_to_slist("cert_types", strdup("cer"), NS_CERT_TYPE_DER); 00226 se_add_pair_to_slist("cert_types", strdup("cert"), NS_CERT_TYPE_DER); 00227 se_add_pair_to_slist("cert_types", strdup("der"), NS_CERT_TYPE_DER); 00228 se_add_pair_to_slist("cert_types", strdup("key"), NS_CERT_TYPE_KEY); 00229 se_add_pair_to_slist("cert_types", strdup("private"), NS_CERT_TYPE_KEY); 00230 00232 se_add_pair_to_slist("cert_hash_alg", strdup("sha1"), NS_HASH_SHA1); 00233 se_add_pair_to_slist("cert_hash_alg", strdup("md5"), NS_HASH_MD5); 00234 se_add_pair_to_slist("cert_hash_alg", strdup("sha224"), NS_HASH_SHA224); 00235 se_add_pair_to_slist("cert_hash_alg", strdup("sha256"), NS_HASH_SHA256); 00236 se_add_pair_to_slist("cert_hash_alg", strdup("sha384"), NS_HASH_SHA384); 00237 se_add_pair_to_slist("cert_hash_alg", strdup("sha512"), NS_HASH_SHA512); 00238 00240 se_add_pair_to_slist("cert_map_type", strdup("cn"), 00241 TSNM_tlstmCertCommonName); 00242 se_add_pair_to_slist("cert_map_type", strdup("ip"), 00243 TSNM_tlstmCertSANIpAddress); 00244 se_add_pair_to_slist("cert_map_type", strdup("rfc822"), 00245 TSNM_tlstmCertSANRFC822Name); 00246 se_add_pair_to_slist("cert_map_type", strdup("dns"), 00247 TSNM_tlstmCertSANDNSName); 00248 se_add_pair_to_slist("cert_map_type", strdup("any"), TSNM_tlstmCertSANAny); 00249 se_add_pair_to_slist("cert_map_type", strdup("sn"), 00250 TSNM_tlstmCertSpecified); 00251 00252 } 00253 00254 void 00255 netsnmp_certs_shutdown(void) 00256 { 00257 DEBUGMSGT(("cert:util:shutdown","shutdown\n")); 00258 if (_maps) { 00259 CONTAINER_FREE_ALL(_maps, NULL); 00260 CONTAINER_FREE(_maps); 00261 _maps = NULL; 00262 } 00263 if (NULL != _certs) { 00264 CONTAINER_FREE_ALL(_certs, NULL); 00265 CONTAINER_FREE(_certs); 00266 _certs = NULL; 00267 } 00268 if (NULL != _keys) { 00269 CONTAINER_FREE_ALL(_keys, NULL); 00270 CONTAINER_FREE(_keys); 00271 _keys = NULL; 00272 } 00273 _netsnmp_release_trustcerts(); 00274 } 00275 00276 void 00277 netsnmp_certs_load(void) 00278 { 00279 netsnmp_iterator *itr; 00280 netsnmp_key *key; 00281 netsnmp_cert *cert; 00282 00283 DEBUGMSGT(("cert:util:init","init\n")); 00284 00285 if (NULL == _certs) { 00286 snmp_log(LOG_ERR, "cant load certs without container\n"); 00287 return; 00288 } 00289 00290 if (CONTAINER_SIZE(_certs) != 0) { 00291 DEBUGMSGT(("cert:util:init", "ignoring duplicate init\n")); 00292 return; 00293 } 00294 00295 netsnmp_init_openssl(); 00296 00298 _cert_indexes_load(); 00299 00301 itr = CONTAINER_ITERATOR(_keys); 00302 if (NULL == itr) { 00303 snmp_log(LOG_ERR, "could not get iterator for keys\n"); 00304 netsnmp_certs_shutdown(); 00305 return; 00306 } 00307 key = ITERATOR_FIRST(itr); 00308 for( ; key; key = ITERATOR_NEXT(itr)) 00309 _find_partner(NULL, key); 00310 ITERATOR_RELEASE(itr); 00311 00312 DEBUGIF("cert:dump") { 00313 itr = CONTAINER_ITERATOR(_certs); 00314 if (NULL == itr) { 00315 snmp_log(LOG_ERR, "could not get iterator for certs\n"); 00316 netsnmp_certs_shutdown(); 00317 return; 00318 } 00319 cert = ITERATOR_FIRST(itr); 00320 for( ; cert; cert = ITERATOR_NEXT(itr)) { 00321 netsnmp_cert_load_x509(cert); 00322 } 00323 ITERATOR_RELEASE(itr); 00324 DEBUGMSGT(("cert:dump", 00325 "-------------------- Certificates -----------------\n")); 00326 netsnmp_cert_dump_all(); 00327 DEBUGMSGT(("cert:dump", 00328 "------------------------ End ----------------------\n")); 00329 } 00330 } 00331 00332 /* ##################################################################### 00333 * 00334 * cert container functions 00335 */ 00336 00337 static netsnmp_container * 00338 _get_cert_container(const char *use) 00339 { 00340 netsnmp_container *c; 00341 00342 c = netsnmp_container_find("certs:binary_array"); 00343 if (NULL == c) { 00344 snmp_log(LOG_ERR, "could not create container for %s\n", use); 00345 return NULL; 00346 } 00347 c->container_name = strdup(use); 00348 c->free_item = (netsnmp_container_obj_func*)_cert_free; 00349 c->compare = (netsnmp_container_compare*)_cert_compare; 00350 00351 return c; 00352 } 00353 00354 static void 00355 _setup_containers(void) 00356 { 00357 netsnmp_container *additional_keys; 00358 00359 _certs = _get_cert_container("netsnmp certificates"); 00360 if (NULL == _certs) 00361 return; 00362 00364 additional_keys = netsnmp_container_find("certs_cn:binary_array"); 00365 if (NULL == additional_keys) { 00366 snmp_log(LOG_ERR, "could not create CN container for certificates\n"); 00367 netsnmp_certs_shutdown(); 00368 return; 00369 } 00370 additional_keys->container_name = strdup("certs_cn"); 00371 additional_keys->free_item = NULL; 00372 additional_keys->compare = (netsnmp_container_compare*)_cert_cn_compare; 00373 netsnmp_container_add_index(_certs, additional_keys); 00374 00376 additional_keys = netsnmp_container_find("certs_sn:binary_array"); 00377 if (NULL == additional_keys) { 00378 snmp_log(LOG_ERR, "could not create SN container for certificates\n"); 00379 netsnmp_certs_shutdown(); 00380 return; 00381 } 00382 additional_keys->container_name = strdup("certs_sn"); 00383 additional_keys->free_item = NULL; 00384 additional_keys->compare = (netsnmp_container_compare*)_cert_sn_compare; 00385 additional_keys->ncompare = (netsnmp_container_compare*)_cert_sn_ncompare; 00386 netsnmp_container_add_index(_certs, additional_keys); 00387 00389 additional_keys = netsnmp_container_find("certs_fn:binary_array"); 00390 if (NULL == additional_keys) { 00391 snmp_log(LOG_ERR, "could not create FN container for certificates\n"); 00392 netsnmp_certs_shutdown(); 00393 return; 00394 } 00395 additional_keys->container_name = strdup("certs_fn"); 00396 additional_keys->free_item = NULL; 00397 additional_keys->compare = (netsnmp_container_compare*)_cert_fn_compare; 00398 additional_keys->ncompare = (netsnmp_container_compare*)_cert_fn_ncompare; 00399 netsnmp_container_add_index(_certs, additional_keys); 00400 00401 _keys = netsnmp_container_find("cert_keys:binary_array"); 00402 if (NULL == _keys) { 00403 snmp_log(LOG_ERR, "could not create container for certificate keys\n"); 00404 netsnmp_certs_shutdown(); 00405 return; 00406 } 00407 _keys->container_name = strdup("netsnmp certificate keys"); 00408 _keys->free_item = (netsnmp_container_obj_func*)_key_free; 00409 _keys->compare = (netsnmp_container_compare*)_cert_fn_compare; 00410 00411 _setup_trusted_certs(); 00412 } 00413 00414 netsnmp_container * 00415 netsnmp_cert_map_container(void) 00416 { 00417 return _maps; 00418 } 00419 00420 static netsnmp_cert * 00421 _new_cert(const char *dirname, const char *filename, int certType, 00422 int hashType, const char *fingerprint, const char *common_name, 00423 const char *subject) 00424 { 00425 netsnmp_cert *cert; 00426 00427 if ((NULL == dirname) || (NULL == filename)) { 00428 snmp_log(LOG_ERR, "bad parameters to _new_cert\n"); 00429 return NULL; 00430 } 00431 00432 cert = SNMP_MALLOC_TYPEDEF(netsnmp_cert); 00433 if (NULL == cert) { 00434 snmp_log(LOG_ERR,"could not allocate memory for certificate at %s/%s\n", 00435 dirname, filename); 00436 return NULL; 00437 } 00438 00439 DEBUGMSGT(("9:cert:struct:new","new cert 0x%#lx for %s\n", (u_long)cert, 00440 filename)); 00441 00442 cert->info.dir = strdup(dirname); 00443 cert->info.filename = strdup(filename); 00444 cert->info.allowed_uses = NS_CERT_REMOTE_PEER; 00445 cert->info.type = certType; 00446 if (fingerprint) { 00447 cert->hash_type = hashType; 00448 cert->fingerprint = strdup(fingerprint); 00449 } 00450 if (common_name) 00451 cert->common_name = strdup(common_name); 00452 if (subject) 00453 cert->subject = strdup(subject); 00454 00455 return cert; 00456 } 00457 00458 static netsnmp_key * 00459 _new_key(const char *dirname, const char *filename) 00460 { 00461 netsnmp_key *key; 00462 struct stat fstat; 00463 char fn[SNMP_MAXPATH]; 00464 00465 if ((NULL == dirname) || (NULL == filename)) { 00466 snmp_log(LOG_ERR, "bad parameters to _new_key\n"); 00467 return NULL; 00468 } 00469 00471 snprintf(fn, sizeof(fn), "%s/%s", dirname, filename); 00472 if (stat(fn, &fstat) != 0) { 00473 snmp_log(LOG_ERR, "could not stat %s\n", fn); 00474 return NULL; 00475 } 00476 00477 if ((fstat.st_mode & S_IROTH) || (fstat.st_mode & S_IROTH)) { 00478 snmp_log(LOG_ERR, 00479 "refusing to read world readable or writable key %s\n", fn); 00480 return NULL; 00481 } 00482 00483 key = SNMP_MALLOC_TYPEDEF(netsnmp_key); 00484 if (NULL == key) { 00485 snmp_log(LOG_ERR, "could not allocate memory for key at %s/%s\n", 00486 dirname, filename); 00487 return NULL; 00488 } 00489 00490 DEBUGMSGT(("cert:key:struct:new","new key 0x%#lx for %s\n", (u_long)key, 00491 filename)); 00492 00493 key->info.type = NS_CERT_TYPE_KEY; 00494 key->info.dir = strdup(dirname); 00495 key->info.filename = strdup(filename); 00496 key->info.allowed_uses = NS_CERT_IDENTITY; 00497 00498 return key; 00499 } 00500 00501 void 00502 netsnmp_cert_free(netsnmp_cert *cert) 00503 { 00504 if (NULL == cert) 00505 return; 00506 00507 DEBUGMSGT(("9:cert:struct:free","freeing cert 0x%#lx, %s (fp %s; CN %s)\n", 00508 (u_long)cert, cert->info.filename ? cert->info.filename : "UNK", 00509 cert->fingerprint ? cert->fingerprint : "UNK", 00510 cert->common_name ? cert->common_name : "UNK")); 00511 00512 SNMP_FREE(cert->info.dir); 00513 SNMP_FREE(cert->info.filename); 00514 SNMP_FREE(cert->subject); 00515 SNMP_FREE(cert->issuer); 00516 SNMP_FREE(cert->fingerprint); 00517 SNMP_FREE(cert->common_name); 00518 if (cert->ocert) 00519 X509_free(cert->ocert); 00520 if (cert->key && cert->key->cert == cert) 00521 cert->key->cert = NULL; 00522 00523 free(cert); /* SNMP_FREE not needed on parameters */ 00524 } 00525 00526 void 00527 netsnmp_key_free(netsnmp_key *key) 00528 { 00529 if (NULL == key) 00530 return; 00531 00532 DEBUGMSGT(("cert:key:struct:free","freeing key 0x%#lx, %s\n", 00533 (u_long)key, key->info.filename ? key->info.filename : "UNK")); 00534 00535 SNMP_FREE(key->info.dir); 00536 SNMP_FREE(key->info.filename); 00537 EVP_PKEY_free(key->okey); 00538 if (key->cert && key->cert->key == key) 00539 key->cert->key = NULL; 00540 00541 free(key); /* SNMP_FREE not needed on parameters */ 00542 } 00543 00544 static void 00545 _cert_free(netsnmp_cert *cert, void *context) 00546 { 00547 netsnmp_cert_free(cert); 00548 } 00549 00550 static void 00551 _key_free(netsnmp_key *key, void *context) 00552 { 00553 netsnmp_key_free(key); 00554 } 00555 00556 static int 00557 _cert_compare(netsnmp_cert *lhs, netsnmp_cert *rhs) 00558 { 00559 netsnmp_assert((lhs != NULL) && (rhs != NULL)); 00560 netsnmp_assert((lhs->fingerprint != NULL) && 00561 (rhs->fingerprint != NULL)); 00562 00564 return strcmp(lhs->fingerprint, rhs->fingerprint); 00565 } 00566 00567 static int 00568 _cert_path_compare(netsnmp_cert_common *lhs, netsnmp_cert_common *rhs) 00569 { 00570 int rc; 00571 00572 netsnmp_assert((lhs != NULL) && (rhs != NULL)); 00573 00575 rc = strcmp(lhs->dir, rhs->dir); 00576 if (rc) 00577 return rc; 00578 00580 return strcmp(lhs->filename, rhs->filename); 00581 } 00582 00583 static int 00584 _cert_cn_compare(netsnmp_cert *lhs, netsnmp_cert *rhs) 00585 { 00586 int rc; 00587 const char *lhcn, *rhcn; 00588 00589 netsnmp_assert((lhs != NULL) && (rhs != NULL)); 00590 00591 if (NULL == lhs->common_name) 00592 lhcn = ""; 00593 else 00594 lhcn = lhs->common_name; 00595 if (NULL == rhs->common_name) 00596 rhcn = ""; 00597 else 00598 rhcn = rhs->common_name; 00599 00600 rc = strcmp(lhcn, rhcn); 00601 if (rc) 00602 return rc; 00603 00605 return _cert_path_compare((netsnmp_cert_common*)lhs, 00606 (netsnmp_cert_common*)rhs); 00607 } 00608 00609 static int 00610 _cert_sn_compare(netsnmp_cert *lhs, netsnmp_cert *rhs) 00611 { 00612 int rc; 00613 const char *lhsn, *rhsn; 00614 00615 netsnmp_assert((lhs != NULL) && (rhs != NULL)); 00616 00617 if (NULL == lhs->subject) 00618 lhsn = ""; 00619 else 00620 lhsn = lhs->subject; 00621 if (NULL == rhs->subject) 00622 rhsn = ""; 00623 else 00624 rhsn = rhs->subject; 00625 00626 rc = strcmp(lhsn, rhsn); 00627 if (rc) 00628 return rc; 00629 00631 return _cert_path_compare((netsnmp_cert_common*)lhs, 00632 (netsnmp_cert_common*)rhs); 00633 } 00634 00635 static int 00636 _cert_fn_compare(netsnmp_cert_common *lhs, netsnmp_cert_common *rhs) 00637 { 00638 int rc; 00639 00640 netsnmp_assert((lhs != NULL) && (rhs != NULL)); 00641 00642 rc = strcmp(lhs->filename, rhs->filename); 00643 if (rc) 00644 return rc; 00645 00647 return strcmp(lhs->dir, rhs->dir); 00648 } 00649 00650 static int 00651 _cert_fn_ncompare(netsnmp_cert_common *lhs, netsnmp_cert_common *rhs) 00652 { 00653 netsnmp_assert((lhs != NULL) && (rhs != NULL)); 00654 netsnmp_assert((lhs->filename != NULL) && (rhs->filename != NULL)); 00655 00656 return strncmp(lhs->filename, rhs->filename, strlen(rhs->filename)); 00657 } 00658 00659 static int 00660 _cert_sn_ncompare(netsnmp_cert *lhs, netsnmp_cert *rhs) 00661 { 00662 netsnmp_assert((lhs != NULL) && (rhs != NULL)); 00663 netsnmp_assert((lhs->subject != NULL) && (rhs->subject != NULL)); 00664 00665 return strncmp(lhs->subject, rhs->subject, strlen(rhs->subject)); 00666 } 00667 00668 static int 00669 _cert_ext_type(const char *ext) 00670 { 00671 int rc = se_find_value_in_slist("cert_types", ext); 00672 if (SE_DNE == rc) 00673 return NS_CERT_TYPE_UNKNOWN; 00674 return rc; 00675 } 00676 00677 static int 00678 _type_from_filename(const char *filename) 00679 { 00680 char *pos; 00681 int type; 00682 00683 if (NULL == filename) 00684 return NS_CERT_TYPE_UNKNOWN; 00685 00686 pos = strrchr(filename, '.'); 00687 if (NULL == pos) 00688 return NS_CERT_TYPE_UNKNOWN; 00689 00690 type = _cert_ext_type(++pos); 00691 return type; 00692 } 00693 00694 /* 00695 * filter functions; return 1 to include file, 0 to exclude 00696 */ 00697 static int 00698 _cert_cert_filter(const char *filename) 00699 { 00700 int len = strlen(filename); 00701 const char *pos; 00702 00703 if (len < 5) /* shortest name: x.YYY */ 00704 return 0; 00705 00706 pos = strrchr(filename, '.'); 00707 if (NULL == pos) 00708 return 0; 00709 00710 if (_cert_ext_type(++pos) != NS_CERT_TYPE_UNKNOWN) 00711 return 1; 00712 00713 return 0; 00714 } 00715 00716 /* ##################################################################### 00717 * 00718 * cert index functions 00719 * 00720 * This code mimics what the mib index code does. The persistent 00721 * directory will have a subdirectory named 'cert_indexes'. Inside 00722 * this directory will be some number of files with ascii numeric 00723 * names (0, 1, 2, etc). Each of these files will start with a line 00724 * with the text "DIR ", followed by a directory name. The rest of the 00725 * file will be certificate fields and the certificate file name, one 00726 * certificate per line. The numeric file name is the integer 'directory 00727 * index'. 00728 */ 00729 00735 static int 00736 _certindex_add( const char *dirname, int i ) 00737 { 00738 int rc; 00739 char *dirname_copy = strdup(dirname); 00740 00741 if ( i == -1 ) { 00742 int max = se_find_free_value_in_list(_certindexes); 00743 if (SE_DNE == max) 00744 i = 0; 00745 else 00746 i = max; 00747 } 00748 00749 DEBUGMSGT(("cert:index:add","dir %s at index %d\n", dirname, i )); 00750 rc = se_add_pair_to_list(&_certindexes, dirname_copy, i); 00751 if (SE_OK != rc) { 00752 snmp_log(LOG_ERR, "adding certindex dirname failed; " 00753 "%d (%s) not added\n", i, dirname); 00754 free(dirname_copy); 00755 return -1; 00756 } 00757 00758 return i; 00759 } 00760 00766 static void 00767 _certindexes_load( void ) 00768 { 00769 DIR *dir; 00770 struct dirent *file; 00771 FILE *fp; 00772 char filename[SNMP_MAXPATH], line[300]; 00773 int i; 00774 char *cp, *pos; 00775 00776 /* 00777 * Open the CERT index directory, or create it (empty) 00778 */ 00779 snprintf( filename, sizeof(filename), "%s/cert_indexes", 00780 get_persistent_directory()); 00781 filename[sizeof(filename)-1] = 0; 00782 dir = opendir( filename ); 00783 if ( dir == NULL ) { 00784 DEBUGMSGT(("cert:index:load", 00785 "creating new cert_indexes directory\n")); 00786 mkdirhier( filename, NETSNMP_AGENT_DIRECTORY_MODE, 0); 00787 return; 00788 } 00789 00790 /* 00791 * Create a list of which directory each file refers to 00792 */ 00793 while ((file = readdir( dir ))) { 00794 if ( !isdigit(file->d_name[0])) 00795 continue; 00796 i = atoi( file->d_name ); 00797 00798 snprintf( filename, sizeof(filename), "%s/cert_indexes/%d", 00799 get_persistent_directory(), i ); 00800 filename[sizeof(filename)-1] = 0; 00801 fp = fopen( filename, "r" ); 00802 if ( !fp ) { 00803 DEBUGMSGT(("cert:index:load", "error opening index (%d)\n", i)); 00804 fclose(fp); 00805 continue; 00806 } 00807 cp = fgets( line, sizeof(line), fp ); 00808 if ( cp ) { 00809 line[strlen(line)-1] = 0; 00810 pos = strrchr(line, ' '); 00811 if (pos) 00812 *pos = '\0'; 00813 DEBUGMSGT(("9:cert:index:load","adding (%d) %s\n", i, line)); 00814 (void)_certindex_add( line+4, i ); /* Skip 'DIR ' */ 00815 } else { 00816 DEBUGMSGT(("cert:index:load", "Empty index (%d)\n", i)); 00817 } 00818 fclose( fp ); 00819 } 00820 closedir( dir ); 00821 } 00822 00828 static char * 00829 _certindex_lookup( const char *dirname ) 00830 { 00831 int i; 00832 char filename[SNMP_MAXPATH]; 00833 00834 00835 i = se_find_value_in_list(_certindexes, dirname); 00836 if (SE_DNE == i) { 00837 DEBUGMSGT(("9:cert:index:lookup","%s : (none)\n", dirname)); 00838 return NULL; 00839 } 00840 00841 snprintf(filename, sizeof(filename), "%s/cert_indexes/%d", 00842 get_persistent_directory(), i); 00843 filename[sizeof(filename)-1] = 0; 00844 DEBUGMSGT(("cert:index:lookup", "%s (%d) %s\n", dirname, i, filename )); 00845 return strdup(filename); 00846 } 00847 00848 static FILE * 00849 _certindex_new( const char *dirname ) 00850 { 00851 FILE *fp; 00852 char filename[SNMP_MAXPATH], *cp; 00853 int i; 00854 00855 cp = _certindex_lookup( dirname ); 00856 if (!cp) { 00857 i = _certindex_add( dirname, -1 ); 00858 if (-1 == i) 00859 return NULL; /* msg already logged */ 00860 snprintf( filename, sizeof(filename), "%s/cert_indexes/%d", 00861 get_persistent_directory(), i ); 00862 filename[sizeof(filename)-1] = 0; 00863 cp = filename; 00864 } 00865 DEBUGMSGT(("9:cert:index:new", "%s (%s)\n", dirname, cp )); 00866 fp = fopen( cp, "w" ); 00867 if (fp) 00868 fprintf( fp, "DIR %s %d\n", dirname, CERT_INDEX_FORMAT ); 00869 else 00870 DEBUGMSGTL(("cert:index", "error opening new index file %s\n", dirname)); 00871 00872 if (cp != filename) 00873 free(cp); 00874 00875 return fp; 00876 } 00877 00878 /* ##################################################################### 00879 * 00880 * certificate utility functions 00881 * 00882 */ 00883 static X509 * 00884 netsnmp_ocert_get(netsnmp_cert *cert) 00885 { 00886 BIO *certbio; 00887 X509 *ocert = NULL; 00888 EVP_PKEY *okey = NULL; 00889 char file[SNMP_MAXPATH]; 00890 int is_ca; 00891 00892 if (NULL == cert) 00893 return NULL; 00894 00895 if (cert->ocert) 00896 return cert->ocert; 00897 00898 if (NS_CERT_TYPE_UNKNOWN == cert->info.type) { 00899 cert->info.type = _type_from_filename(cert->info.filename); 00900 if (NS_CERT_TYPE_UNKNOWN == cert->info.type) { 00901 snmp_log(LOG_ERR, "unknown certificate type %d for %s\n", 00902 cert->info.type, cert->info.filename); 00903 return NULL; 00904 } 00905 } 00906 00907 DEBUGMSGT(("9:cert:read", "Checking file %s\n", cert->info.filename)); 00908 00909 certbio = BIO_new(BIO_s_file()); 00910 if (NULL == certbio) { 00911 snmp_log(LOG_ERR, "error creating BIO\n"); 00912 return NULL; 00913 } 00914 00915 snprintf(file, sizeof(file),"%s/%s", cert->info.dir, cert->info.filename); 00916 if (BIO_read_filename(certbio, file) <=0) { 00917 snmp_log(LOG_ERR, "error reading certificate %s into BIO\n", file); 00918 BIO_vfree(certbio); 00919 return NULL; 00920 } 00921 00922 if (NS_CERT_TYPE_UNKNOWN == cert->info.type) { 00923 char *pos = strrchr(cert->info.filename, '.'); 00924 if (NULL == pos) 00925 return NULL; 00926 cert->info.type = _cert_ext_type(++pos); 00927 netsnmp_assert(cert->info.type != NS_CERT_TYPE_UNKNOWN); 00928 } 00929 00930 switch (cert->info.type) { 00931 00932 case NS_CERT_TYPE_DER: 00933 ocert = d2i_X509_bio(certbio,NULL); /* DER/ASN1 */ 00934 if (NULL != ocert) 00935 break; 00936 (void)BIO_reset(certbio); 00939 case NS_CERT_TYPE_PEM: 00940 ocert = PEM_read_bio_X509_AUX(certbio, NULL, NULL, NULL); 00941 if (NULL == ocert) 00942 break; 00943 if (NS_CERT_TYPE_DER == cert->info.type) { 00944 DEBUGMSGT(("9:cert:read", "Changing type from DER to PEM\n")); 00945 cert->info.type = NS_CERT_TYPE_PEM; 00946 } 00948 if (NULL == cert->key) { 00949 (void)BIO_reset(certbio); 00950 okey = PEM_read_bio_PrivateKey(certbio, NULL, NULL, NULL); 00951 if (NULL != okey) { 00952 netsnmp_key *key; 00953 DEBUGMSGT(("cert:read:key", "found key with cert in %s\n", 00954 cert->info.filename)); 00955 key = _new_key(cert->info.dir, cert->info.filename); 00956 if (NULL != key) { 00957 key->okey = okey; 00958 if (-1 == CONTAINER_INSERT(_keys, key)) { 00959 DEBUGMSGT(("cert:read:key:add", 00960 "error inserting key into container\n")); 00961 netsnmp_key_free(key); 00962 key = NULL; 00963 } 00964 else { 00965 DEBUGMSGT(("cert:read:partner", "%s match found!\n", 00966 cert->info.filename)); 00967 key->cert = cert; 00968 cert->key = key; 00969 cert->info.allowed_uses |= NS_CERT_IDENTITY; 00970 } 00971 } 00972 } /* null return from read */ 00973 } /* null key */ 00974 break; 00975 #ifdef CERT_PKCS12_SUPPORT_MAYBE_LATER 00976 case NS_CERT_TYPE_PKCS12: 00977 (void)BIO_reset(certbio); 00978 PKCS12 *p12 = d2i_PKCS12_bio(certbio, NULL); 00979 if ( (NULL != p12) && (PKCS12_verify_mac(p12, "", 0) || 00980 PKCS12_verify_mac(p12, NULL, 0))) 00981 PKCS12_parse(p12, "", NULL, &cert, NULL); 00982 break; 00983 #endif 00984 default: 00985 snmp_log(LOG_ERR, "unknown certificate type %d for %s\n", 00986 cert->info.type, cert->info.filename); 00987 } 00988 00989 BIO_vfree(certbio); 00990 00991 if (NULL == ocert) { 00992 snmp_log(LOG_ERR, "error parsing certificate file %s\n", 00993 cert->info.filename); 00994 return NULL; 00995 } 00996 00997 cert->ocert = ocert; 00998 /* 00999 * X509_check_ca return codes: 01000 * 0 not a CA 01001 * 1 is a CA 01002 * 2 basicConstraints absent so "maybe" a CA 01003 * 3 basicConstraints absent but self signed V1. 01004 * 4 basicConstraints absent but keyUsage present and keyCertSign asserted. 01005 * 5 outdated Netscape Certificate Type CA extension. 01006 */ 01007 is_ca = X509_check_ca(ocert); 01008 if (1 == is_ca) 01009 cert->info.allowed_uses |= NS_CERT_CA; 01010 01011 if (NULL == cert->subject) { 01012 cert->subject = X509_NAME_oneline(X509_get_subject_name(ocert), NULL, 01013 0); 01014 DEBUGMSGT(("9:cert:add:subject", "subject name: %s\n", cert->subject)); 01015 } 01016 01017 if (NULL == cert->issuer) { 01018 cert->issuer = X509_NAME_oneline(X509_get_issuer_name(ocert), NULL, 0); 01019 if (strcmp(cert->subject, cert->issuer) == 0) { 01020 free(cert->issuer); 01021 cert->issuer = strdup("self-signed"); 01022 } 01023 DEBUGMSGT(("9:cert:add:issuer", "CA issuer: %s\n", cert->issuer)); 01024 } 01025 01026 if (NULL == cert->fingerprint) { 01027 cert->hash_type = netsnmp_openssl_cert_get_hash_type(ocert); 01028 cert->fingerprint = 01029 netsnmp_openssl_cert_get_fingerprint(ocert, cert->hash_type); 01030 } 01031 01032 if (NULL == cert->common_name) { 01033 cert->common_name =netsnmp_openssl_cert_get_commonName(ocert, NULL, 01034 NULL); 01035 DEBUGMSGT(("9:cert:add:name","%s\n", cert->common_name)); 01036 } 01037 01038 return ocert; 01039 } 01040 01041 EVP_PKEY * 01042 netsnmp_okey_get(netsnmp_key *key) 01043 { 01044 BIO *keybio; 01045 EVP_PKEY *okey; 01046 char file[SNMP_MAXPATH]; 01047 01048 if (NULL == key) 01049 return NULL; 01050 01051 if (key->okey) 01052 return key->okey; 01053 01054 snprintf(file, sizeof(file),"%s/%s", key->info.dir, key->info.filename); 01055 DEBUGMSGT(("cert:key:read", "Checking file %s\n", key->info.filename)); 01056 01057 keybio = BIO_new(BIO_s_file()); 01058 if (NULL == keybio) { 01059 snmp_log(LOG_ERR, "error creating BIO\n"); 01060 return NULL; 01061 } 01062 01063 if (BIO_read_filename(keybio, file) <=0) { 01064 snmp_log(LOG_ERR, "error reading certificate %s into BIO\n", 01065 key->info.filename); 01066 BIO_vfree(keybio); 01067 return NULL; 01068 } 01069 01070 okey = PEM_read_bio_PrivateKey(keybio, NULL, NULL, NULL); 01071 if (NULL == okey) 01072 snmp_log(LOG_ERR, "error parsing certificate file %s\n", 01073 key->info.filename); 01074 else 01075 key->okey = okey; 01076 01077 BIO_vfree(keybio); 01078 01079 return okey; 01080 } 01081 01082 static netsnmp_cert * 01083 _find_issuer(netsnmp_cert *cert) 01084 { 01085 netsnmp_void_array *matching; 01086 netsnmp_cert *candidate, *issuer = NULL; 01087 int i; 01088 01089 if ((NULL == cert) || (NULL == cert->issuer)) 01090 return NULL; 01091 01094 matching = _cert_find_subset_sn(cert->issuer); 01095 if (NULL == matching) 01096 return NULL; 01097 01099 for ( i=0; (NULL == issuer) && (i < matching->size); ++i) { 01101 candidate = (netsnmp_cert*)matching->array[i]; 01102 if ((NULL == candidate->ocert) && 01103 (netsnmp_ocert_get(candidate) == NULL)) 01104 continue; 01105 01107 if (netsnmp_openssl_cert_issued_by(candidate->ocert, cert->ocert)) 01108 issuer = candidate; 01109 } 01111 free(matching->array); 01112 free(matching); 01113 01114 return issuer; 01115 } 01116 01117 #define CERT_LOAD_OK 0 01118 #define CERT_LOAD_ERR -1 01119 #define CERT_LOAD_PARTIAL -2 01120 int 01121 netsnmp_cert_load_x509(netsnmp_cert *cert) 01122 { 01123 int rc = CERT_LOAD_OK; 01124 01126 if ((NULL == cert->ocert) && (netsnmp_ocert_get(cert) == NULL)) { 01127 DEBUGMSGT(("cert:load:err", "couldn't load cert for %s\n", 01128 cert->info.filename)); 01129 rc = CERT_LOAD_ERR; 01130 } 01131 01133 if ((NULL != cert->key) && (NULL == cert->key->okey) && 01134 (netsnmp_okey_get(cert->key) == NULL)) { 01135 DEBUGMSGT(("cert:load:err", "couldn't load key for cert %s\n", 01136 cert->info.filename)); 01137 rc = CERT_LOAD_ERR; 01138 } 01139 01141 for (; cert && cert->issuer; cert = cert->issuer_cert) { 01143 if (strcmp(cert->issuer, "self-signed") == 0) { 01144 netsnmp_assert(cert->issuer_cert == NULL); 01145 break; 01146 } 01148 if (NULL == cert->issuer_cert) { 01149 cert->issuer_cert = _find_issuer(cert); 01150 if (NULL == cert->issuer_cert) { 01151 DEBUGMSGT(("cert:load:warn", 01152 "couldn't load CA chain for cert %s\n", 01153 cert->info.filename)); 01154 rc = CERT_LOAD_PARTIAL; 01155 break; 01156 } 01157 } 01159 if ((NULL == cert->issuer_cert->ocert) && 01160 (netsnmp_ocert_get(cert->issuer_cert) == NULL)) { 01161 DEBUGMSGT(("cert:load:warn", "couldn't load cert chain for %s\n", 01162 cert->info.filename)); 01163 rc = CERT_LOAD_PARTIAL; 01164 break; 01165 } 01166 } /* cert CA for loop */ 01167 01168 return rc; 01169 } 01170 01171 static void 01172 _find_partner(netsnmp_cert *cert, netsnmp_key *key) 01173 { 01174 netsnmp_void_array *matching = NULL; 01175 char filename[NAME_MAX], *pos; 01176 01177 if ((cert && key) || (!cert && ! key)) { 01178 DEBUGMSGT(("cert:partner", "bad parameters searching for partner\n")); 01179 return; 01180 } 01181 01182 snprintf(filename, sizeof(filename), "%s", key->info.filename); 01183 pos = strrchr(filename, '.'); 01184 if (NULL == pos) 01185 return; 01186 *pos = 0; 01187 01188 if(key) { 01189 if (key->cert) { 01190 DEBUGMSGT(("cert:partner", "key already has partner\n")); 01191 return; 01192 } 01193 DEBUGMSGT(("9:cert:partner", "%s looking for partner near %s\n", 01194 key->info.filename, key->info.dir)); 01195 01196 matching = _cert_find_subset_fn( filename, key->info.dir ); 01197 if (!matching) 01198 return; 01199 if (1 == matching->size) { 01200 cert = (netsnmp_cert*)matching->array[0]; 01201 if (NULL == cert->key) { 01202 DEBUGMSGT(("cert:partner", "%s match found!\n", 01203 cert->info.filename)); 01204 key->cert = cert; 01205 cert->key = key; 01206 cert->info.allowed_uses |= NS_CERT_IDENTITY; 01207 } 01208 else if (cert->key != key) 01209 snmp_log(LOG_ERR, "%s matching cert already has partner\n", 01210 cert->info.filename); 01211 } 01212 else 01213 DEBUGMSGT(("cert:partner", "%s matches multiple certs\n", 01214 key->info.filename)); 01215 } 01216 else if(cert) { 01217 if (cert->key) { 01218 DEBUGMSGT(("cert:partner", "cert already has partner\n")); 01219 return; 01220 } 01221 DEBUGMSGT(("9:cert:partner", "%s looking for partner\n", 01222 cert->info.filename)); 01223 01224 matching = _key_find_subset(filename); 01225 if (!matching) 01226 return; 01227 if (1 == matching->size) { 01228 key = (netsnmp_key*)matching->array[0]; 01229 if (NULL == key->cert) { 01230 DEBUGMSGT(("cert:partner", "%s found!\n", cert->info.filename)); 01231 key->cert = cert; 01232 cert->key = key; 01233 } 01234 else if (key->cert != cert) 01235 snmp_log(LOG_ERR, "%s matching key already has partner\n", 01236 cert->info.filename); 01237 } 01238 else 01239 DEBUGMSGT(("cert:partner", "%s matches multiple keys\n", 01240 cert->info.filename)); 01241 } 01242 01243 if (matching) { 01244 free(matching->array); 01245 free(matching); 01246 } 01247 } 01248 01249 static int 01250 _add_certfile(const char* dirname, const char* filename, FILE *index) 01251 { 01252 X509 *ocert; 01253 EVP_PKEY *okey; 01254 netsnmp_cert *cert = NULL; 01255 netsnmp_key *key = NULL; 01256 char certfile[SNMP_MAXPATH]; 01257 int type; 01258 01259 if (((const void*)NULL == dirname) || (NULL == filename)) 01260 return -1; 01261 01262 type = _type_from_filename(filename); 01263 netsnmp_assert(type != NS_CERT_TYPE_UNKNOWN); 01264 01265 snprintf(certfile, sizeof(certfile),"%s/%s", dirname, filename); 01266 01267 DEBUGMSGT(("9:cert:file:add", "Checking file: %s (type %d)\n", filename, 01268 type)); 01269 01270 if (NS_CERT_TYPE_KEY == type) { 01271 key = _new_key(dirname, filename); 01272 if (NULL == key) 01273 return -1; 01274 okey = netsnmp_okey_get(key); 01275 if (NULL == okey) { 01276 netsnmp_key_free(key); 01277 return -1; 01278 } 01279 key->okey = okey; 01280 if (-1 == CONTAINER_INSERT(_keys, key)) { 01281 DEBUGMSGT(("cert:key:file:add:err", 01282 "error inserting key into container\n")); 01283 netsnmp_key_free(key); 01284 key = NULL; 01285 } 01286 } 01287 else { 01288 cert = _new_cert(dirname, filename, type, -1, NULL, NULL, NULL); 01289 if (NULL == cert) 01290 return -1; 01291 ocert = netsnmp_ocert_get(cert); 01292 if (NULL == ocert) { 01293 netsnmp_cert_free(cert); 01294 return -1; 01295 } 01296 cert->ocert = ocert; 01297 if (-1 == CONTAINER_INSERT(_certs, cert)) { 01298 DEBUGMSGT(("cert:file:add:err", 01299 "error inserting cert into container\n")); 01300 netsnmp_cert_free(cert); 01301 cert = NULL; 01302 } 01303 } 01304 if ((NULL == cert) && (NULL == key)) { 01305 DEBUGMSGT(("cert:file:add:failure", "for %s\n", certfile)); 01306 return -1; 01307 } 01308 01309 if (index) { 01313 if (cert) 01314 fprintf(index, "c:%s %d %d %s '%s' '%s'\n", filename, 01315 cert->info.type, cert->hash_type, cert->fingerprint, 01316 cert->common_name, cert->subject); 01317 else if (key) 01318 fprintf(index, "k:%s\n", filename); 01319 } 01320 01321 return 0; 01322 } 01323 01324 static int 01325 _cert_read_index(const char *dirname, struct stat *dirstat) 01326 { 01327 FILE *index; 01328 char *idxname, *pos; 01329 struct stat idx_stat; 01330 char tmpstr[SNMP_MAXPATH + 5], filename[NAME_MAX]; 01331 char fingerprint[60+1], common_name[64+1], type_str[15]; 01332 char subject[SNMP_MAXBUF_SMALL], hash_str[15]; 01333 int count = 0, type, hash, version; 01334 netsnmp_cert *cert; 01335 netsnmp_key *key; 01336 netsnmp_container *newer, *found; 01337 01338 netsnmp_assert(NULL != dirname); 01339 01340 idxname = _certindex_lookup( dirname ); 01341 if (NULL == idxname) { 01342 DEBUGMSGT(("cert:index:parse", "no index for cert directory\n")); 01343 return -1; 01344 } 01345 01346 /* 01347 * see if directory has been modified more recently than the index 01348 */ 01349 if (stat(idxname, &idx_stat) != 0) { 01350 DEBUGMSGT(("cert:index:parse", "error getting index file stats\n")); 01351 SNMP_FREE(idxname); 01352 return -1; 01353 } 01354 01355 #if (defined(WIN32) || defined(cygwin)) 01356 /* For Win32 platforms, the directory does not maintain a last modification 01357 * date that we can compare with the modification date of the .index file. 01358 */ 01359 #else 01360 if (dirstat->st_mtime >= idx_stat.st_mtime) { 01361 DEBUGMSGT(("cert:index:parse", "Index outdated; dir modified\n")); 01362 SNMP_FREE(idxname); 01363 return -1; 01364 } 01365 #endif 01366 01367 /* 01368 * dir mtime doesn't change when files are touched, so we need to check 01369 * each file against the index in case a file has been modified. 01370 */ 01371 newer = 01372 netsnmp_directory_container_read_some(NULL, dirname, 01373 (netsnmp_directory_filter*) 01374 _time_filter,(void*)&idx_stat, 01375 NETSNMP_DIR_NSFILE | 01376 NETSNMP_DIR_NSFILE_STATS); 01377 if (newer) { 01378 DEBUGMSGT(("cert:index:parse", "Index outdated; files modified\n")); 01379 CONTAINER_FREE_ALL(newer, NULL); 01380 CONTAINER_FREE(newer); 01381 SNMP_FREE(idxname); 01382 return -1; 01383 } 01384 01385 DEBUGMSGT(("cert:index:parse", "The index for %s looks good\n", dirname)); 01386 01387 index = fopen(idxname, "r"); 01388 if (NULL == index) { 01389 snmp_log(LOG_ERR, "cert:index:parse can't open index for %s\n", 01390 dirname); 01391 SNMP_FREE(idxname); 01392 return -1; 01393 } 01394 01395 found = _get_cert_container(idxname); 01396 01397 /* 01398 * check index format version 01399 */ 01400 fgets(tmpstr, sizeof(tmpstr), index); 01401 pos = strrchr(tmpstr, ' '); 01402 if (pos) { 01403 ++pos; 01404 version = atoi(pos); 01405 } 01406 if ((NULL == pos) || (version != CERT_INDEX_FORMAT)) { 01407 DEBUGMSGT(("cert:index:add", "missing or wrong index format!\n")); 01408 fclose(index); 01409 SNMP_FREE(idxname); 01410 return -1; 01411 } 01412 while (1) { 01413 if (NULL == fgets(tmpstr, sizeof(tmpstr), index)) 01414 break; 01415 01416 if ('c' == tmpstr[0]) { 01417 pos = &tmpstr[2]; 01418 if ((NULL == (pos=copy_nword(pos, filename, sizeof(filename)))) || 01419 (NULL == (pos=copy_nword(pos, type_str, sizeof(type_str)))) || 01420 (NULL == (pos=copy_nword(pos, hash_str, sizeof(hash_str)))) || 01421 (NULL == (pos=copy_nword(pos, fingerprint, 01422 sizeof(fingerprint)))) || 01423 (NULL == (pos=copy_nword(pos, common_name, 01424 sizeof(common_name)))) || 01425 (NULL != copy_nword(pos, subject, sizeof(subject)))) { 01426 snmp_log(LOG_ERR, "_cert_read_index: error parsing line: %s\n", 01427 tmpstr); 01428 count = -1; 01429 break; 01430 } 01431 type = atoi(type_str); 01432 hash = atoi(hash_str); 01433 cert = (void*)_new_cert(dirname, filename, type, hash, fingerprint, 01434 common_name, subject); 01435 if (cert && 0 == CONTAINER_INSERT(found, cert)) 01436 ++count; 01437 else { 01438 DEBUGMSGT(("cert:index:add", 01439 "error inserting cert into container\n")); 01440 netsnmp_cert_free(cert); 01441 cert = NULL; 01442 } 01443 } 01444 else if ('k' == tmpstr[0]) { 01445 if (NULL != copy_nword(&tmpstr[2], filename, sizeof(filename))) { 01446 snmp_log(LOG_ERR, "_cert_read_index: error parsing line %s\n", 01447 tmpstr); 01448 continue; 01449 } 01450 key = _new_key(dirname, filename); 01451 if (key && 0 == CONTAINER_INSERT(_keys, key)) 01452 ++count; 01453 else { 01454 DEBUGMSGT(("cert:index:add:key", 01455 "error inserting key into container\n")); 01456 netsnmp_key_free(key); 01457 } 01458 } 01459 else { 01460 snmp_log(LOG_ERR, "unknown line in cert index for %s\n", dirname); 01461 continue; 01462 } 01463 } /* while */ 01464 fclose(index); 01465 SNMP_FREE(idxname); 01466 01467 if (count > 0) { 01468 netsnmp_iterator *itr = CONTAINER_ITERATOR(found); 01469 if (NULL == itr) { 01470 snmp_log(LOG_ERR, "could not get iterator for found certs\n"); 01471 count = -1; 01472 } 01473 else { 01474 cert = ITERATOR_FIRST(itr); 01475 for( ; cert; cert = ITERATOR_NEXT(itr)) 01476 CONTAINER_INSERT(_certs, cert); 01477 ITERATOR_RELEASE(itr); 01478 DEBUGMSGT(("cert:index:parse","added %d certs from index\n", 01479 count)); 01480 } 01481 } 01482 if (count < 0) 01483 CONTAINER_FREE_ALL(found, NULL); 01484 CONTAINER_FREE(found); 01485 01486 return count; 01487 } 01488 01489 static int 01490 _add_certdir(const char *dirname) 01491 { 01492 FILE *index; 01493 char *file; 01494 int count = 0; 01495 netsnmp_container *cert_container; 01496 netsnmp_iterator *it; 01497 struct stat statbuf; 01498 01499 netsnmp_assert(NULL != dirname); 01500 01501 DEBUGMSGT(("9:cert:dir:add", " config dir: %s\n", dirname )); 01502 01503 if (stat(dirname, &statbuf) != 0) { 01504 DEBUGMSGT(("9:cert:dir:add", " dir not present: %s\n", 01505 dirname )); 01506 return -1; 01507 } 01508 #ifdef S_ISDIR 01509 if (!S_ISDIR(statbuf.st_mode)) { 01510 DEBUGMSGT(("9:cert:dir:add", " not a dir: %s\n", dirname )); 01511 return -1; 01512 } 01513 #endif 01514 01515 DEBUGMSGT(("cert:index:dir", "Scanning directory %s\n", dirname)); 01516 01517 /* 01518 * look for existing index 01519 */ 01520 count = _cert_read_index(dirname, &statbuf); 01521 if (count >= 0) 01522 return count; 01523 01524 index = _certindex_new( dirname ); 01525 if (NULL == index) { 01526 DEBUGMSGT(("9:cert:index:dir", 01527 "error opening index for cert directory\n")); 01528 DEBUGMSGTL(("cert:index", "could not open certificate index file\n")); 01529 } 01530 01531 /* 01532 * index was missing, out of date or bad. rescan directory. 01533 */ 01534 cert_container = 01535 netsnmp_directory_container_read_some(NULL, dirname, 01536 (netsnmp_directory_filter*) 01537 &_cert_cert_filter, NULL, 01538 NETSNMP_DIR_RELATIVE_PATH | 01539 NETSNMP_DIR_EMPTY_OK ); 01540 if (NULL == cert_container) { 01541 DEBUGMSGT(("cert:index:dir", 01542 "error creating container for cert files\n")); 01543 goto err_index; 01544 } 01545 01546 /* 01547 * iterate through the found files and add them to index 01548 */ 01549 it = CONTAINER_ITERATOR(cert_container); 01550 if (NULL == it) { 01551 DEBUGMSGT(("cert:index:dir", 01552 "error creating iterator for cert files\n")); 01553 goto err_container; 01554 } 01555 01556 for (file = ITERATOR_FIRST(it); file; file = ITERATOR_NEXT(it)) { 01557 DEBUGMSGT(("cert:index:dir", "adding %s to index\n", file)); 01558 if ( 0 == _add_certfile( dirname, file, index )) 01559 count++; 01560 else 01561 DEBUGMSGT(("cert:index:dir", "error adding %s to index\n", 01562 file)); 01563 } 01564 01565 /* 01566 * clean up and return 01567 */ 01568 ITERATOR_RELEASE(it); 01569 01570 err_container: 01571 netsnmp_directory_container_free(cert_container); 01572 01573 err_index: 01574 if (index) 01575 fclose(index); 01576 01577 return count; 01578 } 01579 01580 static void 01581 _cert_indexes_load(void) 01582 { 01583 const char *confpath; 01584 char *confpath_copy, *dir, *st = NULL; 01585 char certdir[SNMP_MAXPATH]; 01586 const char *subdirs[] = { NULL, "ca-certs", "certs", "private", NULL }; 01587 int i = 0; 01588 01589 /* 01590 * load indexes from persistent dir 01591 */ 01592 _certindexes_load(); 01593 01594 /* 01595 * duplicate path building from read_config_files_of_type() in 01596 * read_config.c. That is, use SNMPCONFPATH environment variable if 01597 * it is defined, otherwise use configuration directory. 01598 */ 01599 confpath = netsnmp_getenv("SNMPCONFPATH"); 01600 if (NULL == confpath) 01601 confpath = get_configuration_directory(); 01602 01603 subdirs[0] = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 01604 NETSNMP_DS_LIB_CERT_EXTRA_SUBDIR); 01605 confpath_copy = strdup(confpath); 01606 for ( dir = strtok_r(confpath_copy, ENV_SEPARATOR, &st); 01607 dir; dir = strtok_r(NULL, ENV_SEPARATOR, &st)) { 01608 01609 i = (NULL == subdirs[0]) ? 1 : 0; 01610 for ( ; subdirs[i] ; ++i ) { 01612 snprintf(certdir, sizeof(certdir), "%s/tls/%s", dir, subdirs[i]); 01613 _add_certdir(certdir); 01614 } /* for subdirs */ 01615 } /* for conf path dirs */ 01616 SNMP_FREE(confpath_copy); 01617 } 01618 01619 static void 01620 _cert_print(netsnmp_cert *c, void *context) 01621 { 01622 if (NULL == c) 01623 return; 01624 01625 DEBUGMSGT(("cert:dump", "cert %s in %s\n", c->info.filename, c->info.dir)); 01626 DEBUGMSGT(("cert:dump", " type %d flags 0x%x (%s)\n", 01627 c->info.type, c->info.allowed_uses, 01628 _mode_str(c->info.allowed_uses))); 01629 DEBUGIF("9:cert:dump") { 01630 if (NS_CERT_TYPE_KEY != c->info.type) { 01631 if(c->subject) { 01632 if (c->info.allowed_uses & NS_CERT_CA) 01633 DEBUGMSGT(("9:cert:dump", " CA: %s\n", c->subject)); 01634 else 01635 DEBUGMSGT(("9:cert:dump", " subject: %s\n", c->subject)); 01636 } 01637 if(c->issuer) 01638 DEBUGMSGT(("9:cert:dump", " issuer: %s\n", c->issuer)); 01639 if(c->fingerprint) 01640 DEBUGMSGT(("9:cert:dump", " fingerprint: %s(%d):%s\n", 01641 se_find_label_in_slist("cert_hash_alg", c->hash_type), 01642 c->hash_type, c->fingerprint)); 01643 } 01644 /* netsnmp_feature_require(cert_utils_dump_names) */ 01645 /* netsnmp_openssl_cert_dump_names(c->ocert); */ 01646 netsnmp_openssl_cert_dump_extensions(c->ocert); 01647 } 01648 01649 } 01650 01651 static void 01652 _key_print(netsnmp_key *k, void *context) 01653 { 01654 if (NULL == k) 01655 return; 01656 01657 DEBUGMSGT(("cert:dump", "key %s in %s\n", k->info.filename, k->info.dir)); 01658 DEBUGMSGT(("cert:dump", " type %d flags 0x%x (%s)\n", k->info.type, 01659 k->info.allowed_uses, _mode_str(k->info.allowed_uses))); 01660 } 01661 01662 void 01663 netsnmp_cert_dump_all(void) 01664 { 01665 CONTAINER_FOR_EACH(_certs, (netsnmp_container_obj_func*)_cert_print, NULL); 01666 CONTAINER_FOR_EACH(_keys, (netsnmp_container_obj_func*)_key_print, NULL); 01667 } 01668 01669 #ifdef CERT_MAIN 01670 /* 01671 * export BLD=~/net-snmp/build/ SRC=~/net-snmp/src 01672 * 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 01673 * 01674 */ 01675 int 01676 main(int argc, char** argv) 01677 { 01678 int ch; 01679 extern char *optarg; 01680 01681 while ((ch = getopt(argc, argv, "D:fHLMx:")) != EOF) 01682 switch(ch) { 01683 case 'D': 01684 debug_register_tokens(optarg); 01685 snmp_set_do_debugging(1); 01686 break; 01687 default: 01688 fprintf(stderr,"unknown option %c\n", ch); 01689 } 01690 01691 init_snmp("dtlsapp"); 01692 01693 netsnmp_cert_dump_all(); 01694 01695 return 0; 01696 } 01697 01698 #endif /* CERT_MAIN */ 01699 01700 static netsnmp_cert *_cert_find_fp(const char *fingerprint); 01701 01702 void 01703 netsnmp_fp_lowercase_and_strip_colon(char *fp) 01704 { 01705 char *pos, *dest=NULL; 01706 01707 if(!fp) 01708 return; 01709 01711 for (pos = fp; *pos; ++pos ) { 01712 if (':' == *pos) { 01713 dest = pos; 01714 break; 01715 } 01716 else 01717 *pos = isalpha(*pos) ? tolower(*pos) : *pos; 01718 } 01719 if (!*pos) 01720 return; 01721 01723 for (++pos; *pos; ++pos) { 01724 if (':' == *pos) 01725 continue; 01726 *dest++ = isalpha(*pos) ? tolower(*pos) : *pos; 01727 } 01728 *dest = *pos; /* nul termination */ 01729 } 01730 01731 netsnmp_cert * 01732 netsnmp_cert_find(int what, int where, void *hint) 01733 { 01734 netsnmp_cert *result = NULL; 01735 char *fp, *hint_str; 01736 01737 DEBUGMSGT(("cert:find:params", "looking for %s(%d) in %s(0x%x), hint %lu\n", 01738 _mode_str(what), what, _where_str(where), where, (u_long)hint)); 01739 01740 if (NS_CERTKEY_DEFAULT == where) { 01741 01742 switch (what) { 01743 case NS_CERT_IDENTITY: /* want my ID */ 01744 fp = 01745 netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 01746 NETSNMP_DS_LIB_TLS_LOCAL_CERT); 01748 if (!fp) { 01749 int tmp; 01750 tmp = (ptrdiff_t)hint; 01751 DEBUGMSGT(("cert:find:params", " hint = %s\n", 01752 tmp ? "server" : "client")); 01753 fp = 01754 netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, tmp ? 01755 NETSNMP_DS_LIB_X509_SERVER_PUB : 01756 NETSNMP_DS_LIB_X509_CLIENT_PUB ); 01757 } 01758 if (!fp) { 01759 /* As a special case, use the application type to 01760 determine a file name to pull the default identity 01761 from. */ 01762 return netsnmp_cert_find(what, NS_CERTKEY_FILE, netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPTYPE)); 01763 } 01764 break; 01765 case NS_CERT_REMOTE_PEER: 01766 fp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 01767 NETSNMP_DS_LIB_TLS_PEER_CERT); 01769 if (!fp) 01770 fp = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, 01771 NETSNMP_DS_LIB_X509_SERVER_PUB); 01772 break; 01773 default: 01774 DEBUGMSGT(("cert:find:err", "unhandled type %d for %s(%d)\n", 01775 what, _where_str(where), where)); 01776 return NULL; 01777 } 01778 if (fp) 01779 return netsnmp_cert_find(what, NS_CERTKEY_MULTIPLE, fp); 01780 return NULL; 01781 } /* where = ds store */ 01782 else if (NS_CERTKEY_MULTIPLE == where) { 01783 /* tries multiple sources of certificates based on ascii lookup keys */ 01784 01785 /* Try a fingerprint match first, which should always be done first */ 01786 /* (to avoid people naming filenames with conflicting FPs) */ 01787 result = netsnmp_cert_find(what, NS_CERTKEY_FINGERPRINT, hint); 01788 if (!result) { 01789 /* Then try a file name lookup */ 01790 result = netsnmp_cert_find(what, NS_CERTKEY_FILE, hint); 01791 } 01792 } 01793 else if (NS_CERTKEY_FINGERPRINT == where) { 01794 DEBUGMSGT(("cert:find:params", " hint = %s\n", (char *)hint)); 01795 result = _cert_find_fp((char *)hint); 01796 } 01797 else if (NS_CERTKEY_TARGET_PARAM == where) { 01798 if (what != NS_CERT_IDENTITY) { 01799 snmp_log(LOG_ERR, "only identity is valid for target params\n"); 01800 return NULL; 01801 } 01803 hint_str = (char *)hint; 01804 fp = _find_tlstmParams_fingerprint(hint_str); 01805 if (NULL != fp) 01806 result = _cert_find_fp(fp); 01807 01808 } 01809 else if (NS_CERTKEY_TARGET_ADDR == where) { 01810 01812 if (what != NS_CERT_REMOTE_PEER) { 01813 snmp_log(LOG_ERR, "only peer is valid for target addr\n"); 01814 return NULL; 01815 } 01817 hint_str = (char *)hint; 01818 fp = _find_tlstmAddr_fingerprint(hint_str); 01819 if (NULL != fp) 01820 result = _cert_find_fp(fp); 01821 01822 } 01823 else if (NS_CERTKEY_FILE == where) { 01825 char *filename = (char*)hint; 01826 netsnmp_void_array *matching; 01827 01828 DEBUGMSGT(("cert:find:params", " hint = %s\n", (char *)hint)); 01829 matching = _cert_find_subset_fn( filename, NULL ); 01830 if (!matching) 01831 return NULL; 01832 if (1 == matching->size) 01833 result = (netsnmp_cert*)matching->array[0]; 01834 else { 01835 DEBUGMSGT(("cert:find:err", "%s matches multiple certs\n", 01836 filename)); 01837 result = NULL; 01838 } 01839 free(matching->array); 01840 free(matching); 01841 } /* where = NS_CERTKEY_FILE */ 01842 else { /* unknown location */ 01843 01844 DEBUGMSGT(("cert:find:err", "unhandled location %d for %d\n", where, 01845 what)); 01846 return NULL; 01847 } 01848 01849 if (NULL == result) 01850 return NULL; 01851 01853 if (!(result->info.allowed_uses & what)) { 01854 DEBUGMSGT(("cert:find:err", 01855 "cert %s / %s not allowed for %s(%d) (uses=%s (%d))\n", 01856 result->info.filename, result->fingerprint, _mode_str(what), 01857 what , _mode_str(result->info.allowed_uses), 01858 result->info.allowed_uses)); 01859 return NULL; 01860 } 01861 01863 if (netsnmp_cert_load_x509(result) == CERT_LOAD_ERR) 01864 return NULL; 01865 01866 DEBUGMSGT(("cert:find:found", 01867 "using cert %s / %s for %s(%d) (uses=%s (%d))\n", 01868 result->info.filename, result->fingerprint, _mode_str(what), 01869 what , _mode_str(result->info.allowed_uses), 01870 result->info.allowed_uses)); 01871 01872 return result; 01873 } 01874 01875 #ifndef NETSNMP_FEATURE_REMOVE_CERT_FINGERPRINTS 01876 int 01877 netsnmp_cert_check_vb_fingerprint(const netsnmp_variable_list *var) 01878 { 01879 if (!var) 01880 return SNMP_ERR_GENERR; 01881 01882 if (0 == var->val_len) /* empty allowed in some cases */ 01883 return SNMP_ERR_NOERROR; 01884 01885 if (! (0x01 & var->val_len)) { /* odd len */ 01886 DEBUGMSGT(("cert:varbind:fingerprint", 01887 "expecting odd length for fingerprint\n")); 01888 return SNMP_ERR_WRONGLENGTH; 01889 } 01890 01891 if (var->val.string[0] > NS_HASH_MAX) { 01892 DEBUGMSGT(("cert:varbind:fingerprint", "hashtype %d > max %d\n", 01893 var->val.string[0], NS_HASH_MAX)); 01894 return SNMP_ERR_WRONGVALUE; 01895 } 01896 01897 return SNMP_ERR_NOERROR; 01898 } 01899 01906 int 01907 netsnmp_tls_fingerprint_parse(const u_char *binary_fp, int fp_len, 01908 char **fp_str_ptr, u_int *fp_str_len, int realloc, 01909 u_char *hash_type_ptr) 01910 { 01911 int needed; 01912 size_t fp_str_size; 01913 01914 netsnmp_require_ptr_LRV( hash_type_ptr, SNMPERR_GENERR ); 01915 netsnmp_require_ptr_LRV( fp_str_ptr, SNMPERR_GENERR ); 01916 netsnmp_require_ptr_LRV( fp_str_len, SNMPERR_GENERR ); 01917 01918 /* 01919 * output string is binary fp length (minus 1 for initial hash type 01920 * char) * 2 for bin to hex conversion, + 1 for null termination. 01921 */ 01922 needed = ((fp_len - 1) * 2) + 1; 01923 if (*fp_str_len < needed) { 01924 DEBUGMSGT(("tls:fp:parse", "need %d bytes for output\n", needed )); 01925 return SNMPERR_GENERR; 01926 } 01927 01928 /* 01929 * make sure hash type is in valid range 01930 */ 01931 if ((0 == binary_fp[0]) || (binary_fp[0] > NS_HASH_MAX)) { 01932 DEBUGMSGT(("tls:fp:parse", "invalid hash type %d\n", 01933 binary_fp[0])); 01934 return SNMPERR_GENERR; 01935 } 01936 01937 /* 01938 * netsnmp_binary_to_hex allocate space for string, if needed 01939 */ 01940 fp_str_size = *fp_str_len; 01941 *hash_type_ptr = binary_fp[0]; 01942 netsnmp_binary_to_hex((u_char**)fp_str_ptr, &fp_str_size, 01943 realloc, &binary_fp[1], fp_len - 1); 01944 *fp_str_len = fp_str_size; 01945 if (0 == *fp_str_len) 01946 return SNMPERR_GENERR; 01947 01948 return SNMPERR_SUCCESS; 01949 } 01950 #endif /* NETSNMP_FEATURE_REMOVE_CERT_FINGERPRINTS */ 01951 01952 #ifndef NETSNMP_FEATURE_REMOVE_TLS_FINGERPRINT_BUILD 01953 01960 int 01961 netsnmp_tls_fingerprint_build(int hash_type, const char *hex_fp, 01962 u_char **tls_fp, size_t *tls_fp_len, 01963 int realloc) 01964 { 01965 int hex_fp_len, rc; 01966 size_t tls_fp_size; 01967 size_t offset; 01968 01969 netsnmp_require_ptr_LRV( hex_fp, SNMPERR_GENERR ); 01970 netsnmp_require_ptr_LRV( tls_fp, SNMPERR_GENERR ); 01971 netsnmp_require_ptr_LRV( tls_fp_len, SNMPERR_GENERR ); 01972 01973 hex_fp_len = strlen(hex_fp); 01974 if (0 == hex_fp_len) { 01975 *tls_fp_len = 0; 01976 return SNMPERR_SUCCESS; 01977 } 01978 01979 if ((hash_type <= NS_HASH_NONE) || (hash_type > NS_HASH_MAX)) { 01980 DEBUGMSGT(("tls:fp:build", "invalid hash type %d\n", hash_type )); 01981 return SNMPERR_GENERR; 01982 } 01983 01984 /* 01985 * convert to binary 01986 */ 01987 offset = 1; 01988 rc = netsnmp_hex_to_binary(tls_fp, &tls_fp_size, &offset, realloc, hex_fp, 01989 ":"); 01990 *tls_fp_len = tls_fp_size; 01991 if (rc != 1) 01992 return SNMPERR_GENERR; 01993 *tls_fp_len = offset; 01994 (*tls_fp)[0] = hash_type; 01995 01996 return SNMPERR_SUCCESS; 01997 } 01998 #endif /* NETSNMP_FEATURE_REMOVE_TLS_FINGERPRINT_BUILD */ 01999 02009 int 02010 netsnmp_cert_trust(SSL_CTX *ctx, netsnmp_cert *thiscert) 02011 { 02012 X509_STORE *certstore; 02013 X509 *cert; 02014 02015 /* ensure all needed pieces are present */ 02016 netsnmp_assert_or_msgreturn(NULL != thiscert, "NULL certificate passed in", 02017 SNMPERR_GENERR); 02018 netsnmp_assert_or_msgreturn(NULL != thiscert->info.dir, 02019 "NULL certificate directory name passed in", 02020 SNMPERR_GENERR); 02021 netsnmp_assert_or_msgreturn(NULL != thiscert->info.filename, 02022 "NULL certificate filename name passed in", 02023 SNMPERR_GENERR); 02024 02025 /* get the trusted certificate store and the certificate to load into it */ 02026 certstore = SSL_CTX_get_cert_store(ctx); 02027 netsnmp_assert_or_msgreturn(NULL != certstore, 02028 "failed to get certificate trust store", 02029 SNMPERR_GENERR); 02030 cert = netsnmp_ocert_get(thiscert); 02031 netsnmp_assert_or_msgreturn(NULL != cert, 02032 "failed to get certificate from netsnmp_cert", 02033 SNMPERR_GENERR); 02034 02035 /* Put the certificate into the store */ 02036 DEBUGMSGTL(("cert:trust", 02037 "putting trusted cert %p = %s in certstore %p\n", cert, 02038 netsnmp_openssl_cert_get_fingerprint(cert, -1), 02039 certstore)); 02040 X509_STORE_add_cert(certstore, cert); 02041 02042 return SNMPERR_SUCCESS; 02043 } 02044 02055 int 02056 netsnmp_cert_trust_ca(SSL_CTX *ctx, netsnmp_cert *thiscert) 02057 { 02058 netsnmp_assert_or_msgreturn(NULL != thiscert, "NULL certificate passed in", 02059 SNMPERR_GENERR); 02060 02061 /* find the root CA certificate in the chain */ 02062 DEBUGMSGTL(("cert:trust_ca", "checking roots for %p \n", thiscert)); 02063 while (thiscert->issuer_cert) { 02064 thiscert = thiscert->issuer_cert; 02065 DEBUGMSGTL(("cert:trust_ca", " up one to %p\n", thiscert)); 02066 } 02067 02068 /* Add the found top lever certificate to the store */ 02069 return netsnmp_cert_trust(ctx, thiscert); 02070 } 02071 02072 netsnmp_container * 02073 netsnmp_cert_get_trustlist(void) 02074 { 02075 if (!_trusted_certs) 02076 _setup_trusted_certs(); 02077 return _trusted_certs; 02078 } 02079 02080 static void 02081 _parse_trustcert(const char *token, char *line) 02082 { 02083 if (!_trusted_certs) 02084 _setup_trusted_certs(); 02085 02086 if (!_trusted_certs) 02087 return; 02088 02089 CONTAINER_INSERT(_trusted_certs, strdup(line)); 02090 } 02091 02092 /* *************************************************************************** 02093 * 02094 * mode text functions 02095 * 02096 */ 02097 static const char *_mode_str(u_char mode) 02098 { 02099 return _modes[mode]; 02100 } 02101 02102 static const char *_where_str(u_int what) 02103 { 02104 switch (what) { 02105 case NS_CERTKEY_DEFAULT: return "DEFAULT"; 02106 case NS_CERTKEY_FILE: return "FILE"; 02107 case NS_CERTKEY_FINGERPRINT: return "FINGERPRINT"; 02108 case NS_CERTKEY_MULTIPLE: return "MULTIPLE"; 02109 case NS_CERTKEY_CA: return "CA"; 02110 case NS_CERTKEY_SAN_RFC822: return "SAN_RFC822"; 02111 case NS_CERTKEY_SAN_DNS: return "SAN_DNS"; 02112 case NS_CERTKEY_SAN_IPADDR: return "SAN_IPADDR"; 02113 case NS_CERTKEY_COMMON_NAME: return "COMMON_NAME"; 02114 case NS_CERTKEY_TARGET_PARAM: return "TARGET_PARAM"; 02115 case NS_CERTKEY_TARGET_ADDR: return "TARGET_ADDR"; 02116 } 02117 02118 return "UNKNOWN"; 02119 } 02120 02121 /* *************************************************************************** 02122 * 02123 * find functions 02124 * 02125 */ 02126 static netsnmp_cert * 02127 _cert_find_fp(const char *fingerprint) 02128 { 02129 netsnmp_cert cert, *result = NULL; 02130 char fp[EVP_MAX_MD_SIZE]; 02131 02132 if (NULL == fingerprint) 02133 return NULL; 02134 02135 strncpy(fp, fingerprint, sizeof(fp)); 02136 netsnmp_fp_lowercase_and_strip_colon(fp); 02137 02139 memset(&cert, 0x00, sizeof(cert)); 02140 02141 cert.fingerprint = fp; 02142 02143 result = CONTAINER_FIND(_certs,&cert); 02144 return result; 02145 } 02146 02147 /* 02148 * reduce subset by eliminating any filenames that are longer than 02149 * the specified file name. e.g. 'snmp' would match 'snmp.key' and 02150 * 'snmpd.key'. We only want 'snmp.X', where X is a valid extension. 02151 */ 02152 static void 02153 _reduce_subset(netsnmp_void_array *matching, const char *filename) 02154 { 02155 netsnmp_cert_common *cc; 02156 int i = 0, j, newsize, pos; 02157 02158 if ((NULL == matching) || (NULL == filename)) 02159 return; 02160 02161 pos = strlen(filename); 02162 newsize = matching->size; 02163 02164 for( ; i < matching->size; ) { 02165 /* 02166 * if we've shifted matches down we'll hit a NULL entry before 02167 * we hit the end of the array. 02168 */ 02169 if (NULL == matching->array[i]) 02170 break; 02171 /* 02172 * skip over valid matches. Note that we do not want to use 02173 * _type_from_filename. 02174 */ 02175 cc = (netsnmp_cert_common*)matching->array[i]; 02176 if (('.' == cc->filename[pos]) && 02177 (NS_CERT_TYPE_UNKNOWN != _cert_ext_type(&cc->filename[pos+1]))) { 02178 ++i; 02179 continue; 02180 } 02181 /* 02182 * shrink array by shifting everything down a spot. Might not be 02183 * the most efficient soloution, but this is just happening at 02184 * startup and hopefully most certs won't have common prefixes. 02185 */ 02186 --newsize; 02187 for ( j=i; j < newsize; ++j ) 02188 matching->array[j] = matching->array[j+1]; 02189 matching->array[j] = NULL; 02191 } 02192 /* 02193 * if we shifted, set the new size 02194 */ 02195 if (newsize != matching->size) { 02196 DEBUGMSGT(("9:cert:subset:reduce", "shrank from %" NETSNMP_PRIz "d to %d\n", 02197 matching->size, newsize)); 02198 matching->size = newsize; 02199 } 02200 } 02201 02202 /* 02203 * reduce subset by eliminating any filenames that are not under the 02204 * specified directory path. 02205 */ 02206 static void 02207 _reduce_subset_dir(netsnmp_void_array *matching, const char *directory) 02208 { 02209 netsnmp_cert_common *cc; 02210 int i = 0, j, newsize, dir_len; 02211 char dir[SNMP_MAXPATH], *pos; 02212 02213 if ((NULL == matching) || (NULL == directory)) 02214 return; 02215 02216 newsize = matching->size; 02217 02218 /* 02219 * dir struct should be something like 02220 * /usr/share/snmp/tls/certs 02221 * /usr/share/snmp/tls/private 02222 * 02223 * so we want to backup up on directory for compares.. 02224 */ 02225 strncpy(dir,directory,sizeof(dir)); 02226 pos = strrchr(dir, '/'); 02227 if (NULL == pos) { 02228 DEBUGMSGTL(("cert:subset:dir", "no '/' in directory %s\n", directory)); 02229 return; 02230 } 02231 *pos = '\0'; 02232 dir_len = strlen(dir); 02233 02234 for( ; i < matching->size; ) { 02235 /* 02236 * if we've shifted matches down we'll hit a NULL entry before 02237 * we hit the end of the array. 02238 */ 02239 if (NULL == matching->array[i]) 02240 break; 02241 /* 02242 * skip over valid matches. 02243 */ 02244 cc = (netsnmp_cert_common*)matching->array[i]; 02245 if (strncmp(dir, cc->dir, dir_len) == 0) { 02246 ++i; 02247 continue; 02248 } 02249 /* 02250 * shrink array by shifting everything down a spot. Might not be 02251 * the most efficient soloution, but this is just happening at 02252 * startup and hopefully most certs won't have common prefixes. 02253 */ 02254 --newsize; 02255 for ( j=i; j < newsize; ++j ) 02256 matching->array[j] = matching->array[j+1]; 02257 matching->array[j] = NULL; 02259 } 02260 /* 02261 * if we shifted, set the new size 02262 */ 02263 if (newsize != matching->size) { 02264 DEBUGMSGT(("9:cert:subset:dir", "shrank from %" NETSNMP_PRIz "d to %d\n", 02265 matching->size, newsize)); 02266 matching->size = newsize; 02267 } 02268 } 02269 02270 static netsnmp_void_array * 02271 _cert_find_subset_common(const char *filename, netsnmp_container *container) 02272 { 02273 netsnmp_cert_common search; 02274 netsnmp_void_array *matching; 02275 02276 netsnmp_assert(filename && container); 02277 02278 memset(&search, 0x00, sizeof(search)); /* clear search key */ 02279 02280 search.filename = NETSNMP_REMOVE_CONST(char*,filename); 02281 02282 matching = CONTAINER_GET_SUBSET(container, &search); 02283 DEBUGMSGT(("9:cert:subset:found", "%" NETSNMP_PRIz "d matches\n", matching ? 02284 matching->size : 0)); 02285 if (matching && matching->size > 1) { 02286 _reduce_subset(matching, filename); 02287 if (0 == matching->size) { 02288 free(matching->array); 02289 SNMP_FREE(matching); 02290 } 02291 } 02292 return matching; 02293 } 02294 02295 static netsnmp_void_array * 02296 _cert_find_subset_fn(const char *filename, const char *directory) 02297 { 02298 netsnmp_container *fn_container; 02299 netsnmp_void_array *matching; 02300 02302 fn_container = SUBCONTAINER_FIND(_certs, "certs_fn"); 02303 netsnmp_assert(fn_container); 02304 02305 matching = _cert_find_subset_common(filename, fn_container); 02306 if (matching && (matching->size > 1) && directory) { 02307 _reduce_subset_dir(matching, directory); 02308 if (0 == matching->size) { 02309 free(matching->array); 02310 SNMP_FREE(matching); 02311 } 02312 } 02313 return matching; 02314 } 02315 02316 static netsnmp_void_array * 02317 _cert_find_subset_sn(const char *subject) 02318 { 02319 netsnmp_cert search; 02320 netsnmp_void_array *matching; 02321 netsnmp_container *sn_container; 02322 02324 sn_container = SUBCONTAINER_FIND(_certs, "certs_sn"); 02325 netsnmp_assert(sn_container); 02326 02327 memset(&search, 0x00, sizeof(search)); /* clear search key */ 02328 02329 search.subject = NETSNMP_REMOVE_CONST(char*,subject); 02330 02331 matching = CONTAINER_GET_SUBSET(sn_container, &search); 02332 DEBUGMSGT(("9:cert:subset:found", "%" NETSNMP_PRIz "d matches\n", matching ? 02333 matching->size : 0)); 02334 return matching; 02335 } 02336 02337 static netsnmp_void_array * 02338 _key_find_subset(const char *filename) 02339 { 02340 return _cert_find_subset_common(filename, _keys); 02341 } 02342 02344 static netsnmp_void_array * 02345 _find_subset_fp(netsnmp_container *certs, const char *fp) 02346 { 02347 netsnmp_cert_map entry; 02348 netsnmp_container *fp_container; 02349 netsnmp_void_array *va; 02350 02351 if ((NULL == certs) || (NULL == fp)) 02352 return NULL; 02353 02354 fp_container = SUBCONTAINER_FIND(certs, "cert2sn_fp"); 02355 netsnmp_assert_or_msgreturn(fp_container, "cert2sn_fp container missing", 02356 NULL); 02357 02358 memset(&entry, 0x0, sizeof(entry)); 02359 02360 entry.fingerprint = NETSNMP_REMOVE_CONST(char*,fp); 02361 02362 va = CONTAINER_GET_SUBSET(fp_container, &entry); 02363 return va; 02364 } 02365 02366 #if 0 /* not used yet */ 02367 static netsnmp_key * 02368 _key_find_fn(const char *filename) 02369 { 02370 netsnmp_key key, *result = NULL; 02371 02372 netsnmp_assert(NULL != filename); 02373 02374 memset(&key, 0x00, sizeof(key)); /* clear search key */ 02375 key.info.filename = NETSNMP_REMOVE_CONST(char*,filename); 02376 result = CONTAINER_FIND(_keys,&key); 02377 return result; 02378 } 02379 #endif 02380 02381 static int 02382 _time_filter(netsnmp_file *f, struct stat *idx) 02383 { 02385 if (f && idx && f->stats && 02386 ((f->stats->st_mtime >= idx->st_mtime) || 02387 (f->stats->st_ctime >= idx->st_mtime))) 02388 return NETSNMP_DIR_INCLUDE; 02389 02390 return NETSNMP_DIR_EXCLUDE; 02391 } 02392 02393 /* *************************************************************************** 02394 * *************************************************************************** 02395 * 02396 * 02397 * cert map functions 02398 * 02399 * 02400 * *************************************************************************** 02401 * ***************************************************************************/ 02402 #define MAP_CONFIG_TOKEN "certSecName" 02403 static void _parse_map(const char *token, char *line); 02404 static void _map_free(netsnmp_cert_map* entry, void *ctx); 02405 static void _purge_config_entries(void); 02406 02407 static void 02408 _init_tlstmCertToTSN(void) 02409 { 02410 const char *certSecName_help = MAP_CONFIG_TOKEN " PRIORITY FINGERPRINT " 02411 "[--shaNN|md5] <--sn SECNAME | --rfc822 | --dns | --ip | --cn | --any>"; 02412 02413 /* 02414 * container for cert to fingerprint mapping, with fingerprint key 02415 */ 02416 _maps = netsnmp_cert_map_container_create(1); 02417 02418 register_config_handler(NULL, MAP_CONFIG_TOKEN, _parse_map, _purge_config_entries, 02419 certSecName_help); 02420 } 02421 02422 netsnmp_cert_map * 02423 netsnmp_cert_map_alloc(char *fingerprint, X509 *ocert) 02424 { 02425 netsnmp_cert_map *cert_map = SNMP_MALLOC_TYPEDEF(netsnmp_cert_map); 02426 if (NULL == cert_map) { 02427 snmp_log(LOG_ERR, "could not allocate netsnmp_cert_map\n"); 02428 return NULL; 02429 } 02430 02431 if (fingerprint) { 02433 if (strlen(fingerprint) > (SNMPADMINLENGTH * 2)) { 02434 snmp_log(LOG_ERR, "fingerprint %s exceeds max length %d\n", 02435 fingerprint, (SNMPADMINLENGTH * 2)); 02436 free(cert_map); 02437 return NULL; 02438 } 02439 cert_map->fingerprint = strdup(fingerprint); 02440 } 02441 if (ocert) { 02442 cert_map->hashType = netsnmp_openssl_cert_get_hash_type(ocert); 02443 cert_map->ocert = ocert; 02444 } 02445 02446 return cert_map; 02447 } 02448 02449 void 02450 netsnmp_cert_map_free(netsnmp_cert_map *cert_map) 02451 { 02452 if (NULL == cert_map) 02453 return; 02454 02455 SNMP_FREE(cert_map->fingerprint); 02456 SNMP_FREE(cert_map->data); 02458 free(cert_map); /* SNMP_FREE wasted on param */ 02459 } 02460 02461 int 02462 netsnmp_cert_map_add(netsnmp_cert_map *map) 02463 { 02464 int rc; 02465 02466 if (NULL == map) 02467 return -1; 02468 02469 DEBUGMSGTL(("cert:map:add", "pri %d, fp %s\n", 02470 map->priority, map->fingerprint)); 02471 02472 if ((rc = CONTAINER_INSERT(_maps, map)) != 0) 02473 snmp_log(LOG_ERR, "could not insert new certificate map"); 02474 02475 return rc; 02476 } 02477 02478 #ifndef NETSNMP_FEATURE_REMOVE_CERT_MAP_REMOVE 02479 int 02480 netsnmp_cert_map_remove(netsnmp_cert_map *map) 02481 { 02482 int rc; 02483 02484 if (NULL == map) 02485 return -1; 02486 02487 DEBUGMSGTL(("cert:map:remove", "pri %d, fp %s\n", 02488 map->priority, map->fingerprint)); 02489 02490 if ((rc = CONTAINER_REMOVE(_maps, map)) != 0) 02491 snmp_log(LOG_ERR, "could not remove certificate map"); 02492 02493 return rc; 02494 } 02495 #endif /* NETSNMP_FEATURE_REMOVE_CERT_MAP_REMOVE */ 02496 02497 #ifndef NETSNMP_FEATURE_REMOVE_CERT_MAP_FIND 02498 netsnmp_cert_map * 02499 netsnmp_cert_map_find(netsnmp_cert_map *map) 02500 { 02501 if (NULL == map) 02502 return NULL; 02503 02504 return CONTAINER_FIND(_maps, map); 02505 } 02506 #endif /* NETSNMP_FEATURE_REMOVE_CERT_MAP_FIND */ 02507 02508 static void 02509 _map_free(netsnmp_cert_map *map, void *context) 02510 { 02511 netsnmp_cert_map_free(map); 02512 } 02513 02514 static int 02515 _map_compare(netsnmp_cert_map *lhs, netsnmp_cert_map *rhs) 02516 { 02517 netsnmp_assert((lhs != NULL) && (rhs != NULL)); 02518 02519 if (lhs->priority < rhs->priority) 02520 return -1; 02521 else if (lhs->priority > rhs->priority) 02522 return 1; 02523 02524 return strcmp(lhs->fingerprint, rhs->fingerprint); 02525 } 02526 02527 static int 02528 _map_fp_compare(netsnmp_cert_map *lhs, netsnmp_cert_map *rhs) 02529 { 02530 int rc; 02531 netsnmp_assert((lhs != NULL) && (rhs != NULL)); 02532 02533 if ((rc = strcmp(lhs->fingerprint, rhs->fingerprint)) != 0) 02534 return rc; 02535 02536 if (lhs->priority < rhs->priority) 02537 return -1; 02538 else if (lhs->priority > rhs->priority) 02539 return 1; 02540 02541 return 0; 02542 } 02543 02544 static int 02545 _map_fp_ncompare(netsnmp_cert_map *lhs, netsnmp_cert_map *rhs) 02546 { 02547 netsnmp_assert((lhs != NULL) && (rhs != NULL)); 02548 02549 return strncmp(lhs->fingerprint, rhs->fingerprint, 02550 strlen(rhs->fingerprint)); 02551 } 02552 02553 netsnmp_container * 02554 netsnmp_cert_map_container_create(int with_fp) 02555 { 02556 netsnmp_container *chain_map, *fp; 02557 02558 chain_map = netsnmp_container_find("cert_map:stack:binary_array"); 02559 if (NULL == chain_map) { 02560 snmp_log(LOG_ERR, "could not allocate container for cert_map\n"); 02561 return NULL; 02562 } 02563 02564 chain_map->container_name = strdup("cert_map"); 02565 chain_map->free_item = (netsnmp_container_obj_func*)_map_free; 02566 chain_map->compare = (netsnmp_container_compare*)_map_compare; 02567 02568 if (!with_fp) 02569 return chain_map; 02570 02571 /* 02572 * add a secondary index to the table container 02573 */ 02574 fp = netsnmp_container_find("cert2sn_fp:binary_array"); 02575 if (NULL == fp) { 02576 snmp_log(LOG_ERR, 02577 "error creating sub-container for tlstmCertToTSNTable\n"); 02578 CONTAINER_FREE(chain_map); 02579 return NULL; 02580 } 02581 fp->container_name = strdup("cert2sn_fp"); 02582 fp->compare = (netsnmp_container_compare*)_map_fp_compare; 02583 fp->ncompare = (netsnmp_container_compare*)_map_fp_ncompare; 02584 netsnmp_container_add_index(chain_map, fp); 02585 02586 return chain_map; 02587 } 02588 02589 int 02590 netsnmp_cert_parse_hash_type(const char *str) 02591 { 02592 int rc = se_find_value_in_slist("cert_hash_alg", str); 02593 if (SE_DNE == rc) 02594 return NS_HASH_NONE; 02595 return rc; 02596 } 02597 02598 void 02599 netsnmp_cert_map_container_free(netsnmp_container *c) 02600 { 02601 if (NULL == c) 02602 return; 02603 02604 CONTAINER_FREE_ALL(c, NULL); 02605 CONTAINER_FREE(c); 02606 } 02607 02611 static void 02612 _purge_config_entries(void) 02613 { 02620 netsnmp_iterator *itr; 02621 netsnmp_cert_map *cert_map; 02622 netsnmp_container *cert_maps = netsnmp_cert_map_container(); 02623 netsnmp_container *tmp_maps = NULL; 02624 02625 if ((NULL == cert_maps) || (CONTAINER_SIZE(cert_maps) == 0)) 02626 return; 02627 02628 DEBUGMSGT(("cert:map:reconfig", "removing locally configured rows\n")); 02629 02630 /* 02631 * duplicate cert_maps and then iterate over the copy. That way we can 02632 * add/remove to cert_maps without distrubing the iterator. 02633 xx 02634 */ 02635 tmp_maps = CONTAINER_DUP(cert_maps, NULL, 0); 02636 if (NULL == tmp_maps) { 02637 snmp_log(LOG_ERR, "could not duplicate maps for reconfig\n"); 02638 return; 02639 } 02640 02641 itr = CONTAINER_ITERATOR(tmp_maps); 02642 if (NULL == itr) { 02643 snmp_log(LOG_ERR, "could not get iterator for reconfig\n"); 02644 CONTAINER_FREE(tmp_maps); 02645 return; 02646 } 02647 cert_map = ITERATOR_FIRST(itr); 02648 for( ; cert_map; cert_map = ITERATOR_NEXT(itr)) { 02649 02650 if (!(cert_map->flags & NSCM_FROM_CONFIG)) 02651 continue; 02652 02653 if (CONTAINER_REMOVE(cert_maps, cert_map) == 0) 02654 netsnmp_cert_map_free(cert_map); 02655 } 02656 ITERATOR_RELEASE(itr); 02657 CONTAINER_FREE(tmp_maps); 02658 02659 return; 02660 } 02661 02662 /* 02663 certSecName PRIORITY [--shaNN|md5] FINGERPRINT <--sn SECNAME | --rfc822 | --dns | --ip | --cn | --any> 02664 02665 certSecName 100 ff:..11 --sn Wes 02666 certSecName 200 ee:..:22 --sn JohnDoe 02667 certSecName 300 ee:..:22 --rfc822 02668 */ 02669 netsnmp_cert_map * 02670 netsnmp_certToTSN_parse_common(char **line) 02671 { 02672 netsnmp_cert_map *map; 02673 char *tmp, buf[SNMP_MAXBUF_SMALL]; 02674 size_t len; 02675 netsnmp_cert *tmpcert; 02676 02677 if ((NULL == line) || (NULL == *line)) 02678 return NULL; 02679 02681 if (NULL == _maps) { 02682 NETSNMP_LOGONCE((LOG_ERR, "no container for certificate mappings\n")); 02683 return NULL; 02684 } 02685 02686 DEBUGMSGT(("cert:util:config", "parsing %s\n", *line)); 02687 02688 /* read the priority */ 02689 len = sizeof(buf); 02690 tmp = buf; 02691 *line = read_config_read_octet_string(*line, (u_char **)&tmp, &len); 02692 tmp[len] = 0; 02693 if (!isdigit(tmp[0])) { 02694 netsnmp_config_error("could not parse priority"); 02695 return NULL; 02696 } 02697 map = netsnmp_cert_map_alloc(NULL, NULL); 02698 if (NULL == map) { 02699 netsnmp_config_error("could not allocate cert map struct"); 02700 return NULL; 02701 } 02702 map->flags |= NSCM_FROM_CONFIG; 02703 map->priority = atoi(buf); 02704 02705 /* read the flag or the fingerprint */ 02706 len = sizeof(buf); 02707 tmp = buf; 02708 *line = read_config_read_octet_string(*line, (u_char **)&tmp, &len); 02709 tmp[len] = 0; 02710 if ((buf[0] == '-') && (buf[1] == '-')) { 02711 map->hashType = netsnmp_cert_parse_hash_type(&buf[2]); 02712 if (NS_HASH_NONE == map->hashType) { 02713 netsnmp_config_error("invalid hash type"); 02714 goto end; 02715 } 02716 02718 len = sizeof(buf); 02719 tmp = buf; 02720 *line = read_config_read_octet_string(*line, (u_char **)&tmp, &len); 02721 tmp[len] = 0; 02722 } 02723 else 02724 map->hashType = NS_HASH_SHA1; 02725 02726 /* look up the fingerprint */ 02727 tmpcert = netsnmp_cert_find(NS_CERT_REMOTE_PEER, NS_CERTKEY_MULTIPLE, buf); 02728 if (NULL == tmpcert) { 02729 /* assume it's a raw fingerprint we don't have */ 02730 netsnmp_fp_lowercase_and_strip_colon(buf); 02731 map->fingerprint = strdup(buf); 02732 } else { 02733 map->fingerprint = 02734 netsnmp_openssl_cert_get_fingerprint(tmpcert->ocert, -1); 02735 } 02736 02737 if (NULL == *line) { 02738 netsnmp_config_error("must specify map type"); 02739 goto end; 02740 } 02741 02742 /* read the mapping type */ 02743 len = sizeof(buf); 02744 tmp = buf; 02745 *line = read_config_read_octet_string(*line, (u_char **)&tmp, &len); 02746 tmp[len] = 0; 02747 if ((buf[0] != '-') || (buf[1] != '-')) { 02748 netsnmp_config_error("unexpected fromat: %s\n", *line); 02749 goto end; 02750 } 02751 if (strcmp(&buf[2], "sn") == 0) { 02752 if (NULL == *line) { 02753 netsnmp_config_error("must specify secName for --sn"); 02754 goto end; 02755 } 02756 len = sizeof(buf); 02757 tmp = buf; 02758 *line = read_config_read_octet_string(*line, (u_char **)&tmp, &len); 02759 map->data = strdup(buf); 02760 if (map->data) 02761 map->mapType = TSNM_tlstmCertSpecified; 02762 } 02763 else if (strcmp(&buf[2], "cn") == 0) 02764 map->mapType = TSNM_tlstmCertCommonName; 02765 else if (strcmp(&buf[2], "ip") == 0) 02766 map->mapType = TSNM_tlstmCertSANIpAddress; 02767 else if (strcmp(&buf[2], "rfc822") == 0) 02768 map->mapType = TSNM_tlstmCertSANRFC822Name; 02769 else if (strcmp(&buf[2], "dns") == 0) 02770 map->mapType = TSNM_tlstmCertSANDNSName; 02771 else if (strcmp(&buf[2], "any") == 0) 02772 map->mapType = TSNM_tlstmCertSANAny; 02773 else 02774 netsnmp_config_error("unknown argument %s\n", buf); 02775 02776 end: 02777 if (0 == map->mapType) { 02778 netsnmp_cert_map_free(map); 02779 map = NULL; 02780 } 02781 02782 return map; 02783 } 02784 02785 static void 02786 _parse_map(const char *token, char *line) 02787 { 02788 netsnmp_cert_map *map = netsnmp_certToTSN_parse_common(&line); 02789 if (NULL == map) 02790 return; 02791 02792 if (netsnmp_cert_map_add(map) != 0) { 02793 netsnmp_cert_map_free(map); 02794 netsnmp_config_error(MAP_CONFIG_TOKEN 02795 ": duplicate priority for certificate map"); 02796 } 02797 } 02798 02799 static int 02800 _fill_cert_map(netsnmp_cert_map *cert_map, netsnmp_cert_map *entry) 02801 { 02802 DEBUGMSGT(("cert:map:secname", "map: pri %d type %d data %s\n", 02803 entry->priority, entry->mapType, entry->data)); 02804 cert_map->priority = entry->priority; 02805 cert_map->mapType = entry->mapType; 02806 cert_map->hashType = entry->hashType; 02807 if (entry->data) { 02808 cert_map->data = strdup(entry->data); 02809 if (NULL == cert_map->data ) { 02810 snmp_log(LOG_ERR, "secname map data dup failed\n"); 02811 return -1; 02812 } 02813 } 02814 02815 return 0; 02816 } 02817 02818 /* 02819 * get secname map(s) for fingerprints 02820 */ 02821 int 02822 netsnmp_cert_get_secname_maps(netsnmp_container *cert_maps) 02823 { 02824 netsnmp_iterator *itr; 02825 netsnmp_cert_map *cert_map, *new_cert_map, *entry; 02826 netsnmp_container *new_maps = NULL; 02827 netsnmp_void_array *results; 02828 int j; 02829 02830 if ((NULL == cert_maps) || (CONTAINER_SIZE(cert_maps) == 0)) 02831 return -1; 02832 02833 DEBUGMSGT(("cert:map:secname", "looking for matches for %" NETSNMP_PRIz "d fingerprints\n", 02834 CONTAINER_SIZE(cert_maps))); 02835 02836 /* 02837 * duplicate cert_maps and then iterate over the copy. That way we can 02838 * add/remove to cert_maps without distrubing the iterator. 02839 */ 02840 new_maps = CONTAINER_DUP(cert_maps, NULL, 0); 02841 if (NULL == new_maps) { 02842 snmp_log(LOG_ERR, "could not duplicate maps for secname mapping\n"); 02843 return -1; 02844 } 02845 02846 itr = CONTAINER_ITERATOR(new_maps); 02847 if (NULL == itr) { 02848 snmp_log(LOG_ERR, "could not get iterator for secname mappings\n"); 02849 CONTAINER_FREE(new_maps); 02850 return -1; 02851 } 02852 cert_map = ITERATOR_FIRST(itr); 02853 for( ; cert_map; cert_map = ITERATOR_NEXT(itr)) { 02854 02855 results = _find_subset_fp( netsnmp_cert_map_container(), 02856 cert_map->fingerprint ); 02857 if (NULL == results) { 02858 DEBUGMSGT(("cert:map:secname", "no match for %s\n", 02859 cert_map->fingerprint)); 02860 if (CONTAINER_REMOVE(cert_maps, cert_map) != 0) 02861 goto fail; 02862 continue; 02863 } 02864 DEBUGMSGT(("cert:map:secname", "%" NETSNMP_PRIz "d matches for %s\n", 02865 results->size, cert_map->fingerprint)); 02866 /* 02867 * first entry is a freebie 02868 */ 02869 entry = (netsnmp_cert_map*)results->array[0]; 02870 if (_fill_cert_map(cert_map, entry) != 0) 02871 goto fail; 02872 02873 /* 02874 * additional entries must be allocated/inserted 02875 */ 02876 if (results->size > 1) { 02877 for(j=1; j < results->size; ++j) { 02878 entry = (netsnmp_cert_map*)results->array[j]; 02879 new_cert_map = netsnmp_cert_map_alloc(entry->fingerprint, 02880 entry->ocert); 02881 if (NULL == new_cert_map) { 02882 snmp_log(LOG_ERR, 02883 "could not allocate new cert map entry\n"); 02884 goto fail; 02885 } 02886 if (_fill_cert_map(new_cert_map, entry) != 0) { 02887 netsnmp_cert_map_free(new_cert_map); 02888 goto fail; 02889 } 02890 new_cert_map->ocert = cert_map->ocert; 02891 if (CONTAINER_INSERT(cert_maps,new_cert_map) != 0) { 02892 netsnmp_cert_map_free(new_cert_map); 02893 goto fail; 02894 } 02895 } /* for results */ 02896 } /* results size > 1 */ 02897 02898 free(results->array); 02899 SNMP_FREE(results); 02900 } 02901 ITERATOR_RELEASE(itr); 02902 CONTAINER_FREE(new_maps); 02903 02904 DEBUGMSGT(("cert:map:secname", 02905 "found %" NETSNMP_PRIz "d matches for fingerprints\n", 02906 CONTAINER_SIZE(cert_maps))); 02907 return 0; 02908 02909 fail: 02910 if (results) { 02911 free(results->array); 02912 free(results); 02913 } 02914 ITERATOR_RELEASE(itr); 02915 CONTAINER_FREE(new_maps); 02916 return -1; 02917 } 02918 02919 /* *************************************************************************** 02920 * *************************************************************************** 02921 * 02922 * 02923 * snmpTlstmParmsTable data 02924 * 02925 * 02926 * *************************************************************************** 02927 * ***************************************************************************/ 02928 #define PARAMS_CONFIG_TOKEN "snmpTlstmParams" 02929 static void _parse_params(const char *token, char *line); 02930 02931 static void 02932 _init_tlstmParams(void) 02933 { 02934 const char *params_help = 02935 PARAMS_CONFIG_TOKEN " targetParamsName hashType:fingerPrint"; 02936 02937 /* 02938 * container for snmpTlstmParamsTable data 02939 */ 02940 _tlstmParams = netsnmp_container_find("tlstmParams:string"); 02941 if (NULL == _tlstmParams) 02942 snmp_log(LOG_ERR, 02943 "error creating sub-container for tlstmParamsTable\n"); 02944 else 02945 _tlstmParams->container_name = strdup("tlstmParams"); 02946 02947 register_config_handler(NULL, PARAMS_CONFIG_TOKEN, _parse_params, NULL, 02948 params_help); 02949 } 02950 02951 #ifndef NETSNMP_FEATURE_REMOVE_TLSTMPARAMS_CONTAINER 02952 netsnmp_container * 02953 netsnmp_tlstmParams_container(void) 02954 { 02955 return _tlstmParams; 02956 } 02957 #endif /* NETSNMP_FEATURE_REMOVE_TLSTMPARAMS_CONTAINER */ 02958 02959 snmpTlstmParams * 02960 netsnmp_tlstmParams_create(const char *name, int hashType, const char *fp, 02961 int fp_len) 02962 { 02963 snmpTlstmParams *stp = SNMP_MALLOC_TYPEDEF(snmpTlstmParams); 02964 if (NULL == stp) 02965 return NULL; 02966 02967 if (name) 02968 stp->name = strdup(name); 02969 stp->hashType = hashType; 02970 if (fp) 02971 stp->fingerprint = strdup(fp); 02972 DEBUGMSGT(("9:tlstmParams:create", "0x%lx: %s\n", (u_long)stp, 02973 stp->name ? stp->name : "null")); 02974 02975 return stp; 02976 } 02977 02978 void 02979 netsnmp_tlstmParams_free(snmpTlstmParams *stp) 02980 { 02981 if (NULL == stp) 02982 return; 02983 02984 DEBUGMSGT(("9:tlstmParams:release", "0x%lx %s\n", (u_long)stp, 02985 stp->name ? stp->name : "null")); 02986 SNMP_FREE(stp->name); 02987 SNMP_FREE(stp->fingerprint); 02988 free(stp); /* SNMP_FREE pointless on parameter */ 02989 } 02990 02991 snmpTlstmParams * 02992 netsnmp_tlstmParams_restore_common(char **line) 02993 { 02994 snmpTlstmParams *stp; 02995 char *tmp, buf[SNMP_MAXBUF_SMALL]; 02996 size_t len; 02997 02998 if ((NULL == line) || (NULL == *line)) 02999 return NULL; 03000 03002 netsnmp_assert(_tlstmParams); 03003 03004 stp = netsnmp_tlstmParams_create(NULL, 0, NULL, 0); 03005 if (NULL == stp) 03006 return NULL; 03007 03009 len = sizeof(buf); 03010 tmp = buf; 03011 *line = read_config_read_octet_string(*line, (u_char **)&tmp, &len); 03012 tmp[len] = 0; 03014 if (len) 03015 stp->name = strdup(buf); 03016 03018 len = sizeof(buf); 03019 tmp = buf; 03020 *line = read_config_read_octet_string(*line, (u_char **)&tmp, &len); 03021 tmp[len] = 0; 03022 if ((buf[0] == '-') && (buf[1] == '-')) { 03023 stp->hashType = netsnmp_cert_parse_hash_type(&buf[2]); 03024 03026 len = sizeof(buf); 03027 tmp = buf; 03028 *line = read_config_read_octet_string(*line, (u_char **)&tmp, &len); 03029 tmp[len] = 0; 03030 } 03031 else 03032 stp->hashType =NS_HASH_SHA1; 03033 03034 netsnmp_fp_lowercase_and_strip_colon(buf); 03035 stp->fingerprint = strdup(buf); 03036 stp->fingerprint_len = strlen(buf); 03037 03038 DEBUGMSGTL(("tlstmParams:restore:common", "name '%s'\n", stp->name)); 03039 03040 return stp; 03041 } 03042 03043 int 03044 netsnmp_tlstmParams_add(snmpTlstmParams *stp) 03045 { 03046 if (NULL == stp) 03047 return -1; 03048 03049 DEBUGMSGTL(("tlstmParams:add", "adding entry 0x%lx %s\n", (u_long)stp, 03050 stp->name)); 03051 03052 if (CONTAINER_INSERT(_tlstmParams, stp) != 0) { 03053 netsnmp_tlstmParams_free(stp); 03054 snmp_log(LOG_ERR, "error inserting tlstmParams %s", stp->name); 03055 return -1; 03056 } 03057 03058 return 0; 03059 } 03060 03061 #ifndef NETSNMP_FEATURE_REMOVE_TLSTMPARAMS_REMOVE 03062 int 03063 netsnmp_tlstmParams_remove(snmpTlstmParams *stp) 03064 { 03065 if (NULL == stp) 03066 return -1; 03067 03068 DEBUGMSGTL(("tlstmParams:remove", "removing entry 0x%lx %s\n", (u_long)stp, 03069 stp->name)); 03070 03071 if (CONTAINER_REMOVE(_tlstmParams, stp) != 0) { 03072 snmp_log(LOG_ERR, "error removing tlstmParams %s", stp->name); 03073 return -1; 03074 } 03075 03076 return 0; 03077 } 03078 #endif /* NETSNMP_FEATURE_REMOVE_TLSTMPARAMS_REMOVE */ 03079 03080 #ifndef NETSNMP_FEATURE_REMOVE_TLSTMPARAMS_FIND 03081 snmpTlstmParams * 03082 netsnmp_tlstmParams_find(snmpTlstmParams *stp) 03083 { 03084 snmpTlstmParams *found; 03085 03086 if (NULL == stp) 03087 return NULL; 03088 03089 found = CONTAINER_FIND(_tlstmParams, stp); 03090 return found; 03091 } 03092 #endif /* NETSNMP_FEATURE_REMOVE_TLSTMPARAMS_FIND */ 03093 03094 static void 03095 _parse_params(const char *token, char *line) 03096 { 03097 snmpTlstmParams *stp = netsnmp_tlstmParams_restore_common(&line); 03098 03099 if (!stp) 03100 return; 03101 03102 stp->flags = TLSTM_PARAMS_FROM_CONFIG | TLSTM_PARAMS_NONVOLATILE; 03103 03104 netsnmp_tlstmParams_add(stp); 03105 } 03106 03107 static char * 03108 _find_tlstmParams_fingerprint(const char *name) 03109 { 03110 snmpTlstmParams lookup_key, *result; 03111 03112 if (NULL == name) 03113 return NULL; 03114 03115 lookup_key.name = NETSNMP_REMOVE_CONST(char*, name); 03116 03117 result = CONTAINER_FIND(_tlstmParams, &lookup_key); 03118 if ((NULL == result) || (NULL == result->fingerprint)) 03119 return NULL; 03120 03121 return strdup(result->fingerprint); 03122 } 03123 /* 03124 * END snmpTlstmParmsTable data 03125 * ***************************************************************************/ 03126 03127 /* *************************************************************************** 03128 * *************************************************************************** 03129 * 03130 * 03131 * snmpTlstmAddrTable data 03132 * 03133 * 03134 * *************************************************************************** 03135 * ***************************************************************************/ 03136 #define ADDR_CONFIG_TOKEN "snmpTlstmAddr" 03137 static void _parse_addr(const char *token, char *line); 03138 03139 static void 03140 _init_tlstmAddr(void) 03141 { 03142 const char *addr_help = 03143 ADDR_CONFIG_TOKEN " targetAddrName hashType:fingerprint serverIdentity"; 03144 03145 /* 03146 * container for snmpTlstmAddrTable data 03147 */ 03148 _tlstmAddr = netsnmp_container_find("tlstmAddr:string"); 03149 if (NULL == _tlstmAddr) 03150 snmp_log(LOG_ERR, 03151 "error creating sub-container for tlstmAddrTable\n"); 03152 else 03153 _tlstmAddr->container_name = strdup("tlstmAddr"); 03154 03155 register_config_handler(NULL, ADDR_CONFIG_TOKEN, _parse_addr, NULL, 03156 addr_help); 03157 } 03158 03159 #ifndef NETSNMP_FEATURE_REMOVE_TLSTMADDR_CONTAINER 03160 netsnmp_container * 03161 netsnmp_tlstmAddr_container(void) 03162 { 03163 return _tlstmAddr; 03164 } 03165 #endif /* NETSNMP_FEATURE_REMOVE_TLSTMADDR_CONTAINER */ 03166 03167 /* 03168 * create a new row in the table 03169 */ 03170 snmpTlstmAddr * 03171 netsnmp_tlstmAddr_create(char *targetAddrName) 03172 { 03173 snmpTlstmAddr *entry; 03174 03175 if (NULL == targetAddrName) 03176 return NULL; 03177 03178 entry = SNMP_MALLOC_TYPEDEF(snmpTlstmAddr); 03179 if (!entry) 03180 return NULL; 03181 03182 DEBUGMSGT(("tlstmAddr:entry:create", "entry %p %s\n", entry, 03183 targetAddrName ? targetAddrName : "NULL")); 03184 03185 entry->name = strdup(targetAddrName); 03186 03187 return entry; 03188 } 03189 03190 void 03191 netsnmp_tlstmAddr_free(snmpTlstmAddr *entry) 03192 { 03193 if (!entry) 03194 return; 03195 03196 SNMP_FREE(entry->name); 03197 SNMP_FREE(entry->fingerprint); 03198 SNMP_FREE(entry->identity); 03199 free(entry); 03200 } 03201 03202 int 03203 netsnmp_tlstmAddr_restore_common(char **line, char *name, size_t *name_len, 03204 char *id, size_t *id_len, char *fp, 03205 size_t *fp_len, u_char *ht) 03206 { 03207 size_t fp_len_save = *fp_len; 03208 03209 *line = read_config_read_octet_string(*line, (u_char **)&name, name_len); 03210 if (NULL == *line) { 03211 config_perror("incomplete line"); 03212 return -1; 03213 } 03214 name[*name_len] = 0; 03215 03216 *line = read_config_read_octet_string(*line, (u_char **)&fp, fp_len); 03217 if (NULL == *line) { 03218 config_perror("incomplete line"); 03219 return -1; 03220 } 03221 fp[*fp_len] = 0; 03222 if ((fp[0] == '-') && (fp[1] == '-')) { 03223 *ht = netsnmp_cert_parse_hash_type(&fp[2]); 03224 03226 *fp_len = fp_len_save; 03227 *line = read_config_read_octet_string(*line, (u_char **)&fp, fp_len); 03228 fp[*fp_len] = 0; 03229 } 03230 else 03231 *ht = NS_HASH_SHA1; 03232 netsnmp_fp_lowercase_and_strip_colon(fp); 03233 *fp_len = strlen(fp); 03234 03235 *line = read_config_read_octet_string(*line, (u_char **)&id, id_len); 03236 id[*id_len] = 0; 03237 03238 if (*ht <= NS_HASH_NONE || *ht > NS_HASH_MAX) { 03239 config_perror("invalid algorithm for fingerprint"); 03240 return -1; 03241 } 03242 03243 if ((0 == *fp_len) && ((0 == *id_len || (*id_len == 1 && id[0] == '*')))) { 03244 /* 03245 * empty fingerprint not allowed with '*' identity 03246 */ 03247 config_perror("must specify fingerprint for '*' identity"); 03248 return -1; 03249 } 03250 03251 return 0; 03252 } 03253 03254 int 03255 netsnmp_tlstmAddr_add(snmpTlstmAddr *entry) 03256 { 03257 if (!entry) 03258 return -1; 03259 03260 DEBUGMSGTL(("tlstmAddr:add", "adding entry 0x%lx %s %s\n", 03261 (u_long)entry, entry->name, entry->fingerprint)); 03262 if (CONTAINER_INSERT(_tlstmAddr, entry) != 0) { 03263 netsnmp_tlstmAddr_free(entry); 03264 snmp_log(LOG_ERR, "could not insert addr %s", entry->name); 03265 return -1; 03266 } 03267 03268 return 0; 03269 } 03270 03271 #ifndef NETSNMP_FEATURE_REMOVE_TLSTMADDR_REMOVE 03272 int 03273 netsnmp_tlstmAddr_remove(snmpTlstmAddr *entry) 03274 { 03275 if (!entry) 03276 return -1; 03277 03278 if (CONTAINER_REMOVE(_tlstmAddr, entry) != 0) { 03279 snmp_log(LOG_ERR, "could not remove addr %s", entry->name); 03280 return -1; 03281 } 03282 03283 return 0; 03284 } 03285 #endif /* NETSNMP_FEATURE_REMOVE_TLSTMADDR_REMOVE */ 03286 03287 static void 03288 _parse_addr(const char *token, char *line) 03289 { 03290 snmpTlstmAddr *entry; 03291 char name[SNMPADMINLENGTH + 1], id[SNMPADMINLENGTH + 1], 03292 fingerprint[SNMPTLSFINGERPRINT_MAX_LEN + 1]; 03293 size_t name_len = sizeof(name), id_len = sizeof(id), 03294 fp_len = sizeof(fingerprint); 03295 u_char hashType; 03296 int rc; 03297 03298 rc = netsnmp_tlstmAddr_restore_common(&line, name, &name_len, id, &id_len, 03299 fingerprint, &fp_len, &hashType); 03300 if (rc < 0) 03301 return; 03302 03303 if (NULL != line) 03304 config_pwarn("ignore extra tokens on line"); 03305 03306 entry = netsnmp_tlstmAddr_create(name); 03307 if (NULL == entry) 03308 return; 03309 03310 entry->flags |= TLSTM_ADDR_FROM_CONFIG; 03311 entry->hashType = hashType; 03312 if (fp_len) 03313 entry->fingerprint = strdup(fingerprint); 03314 if (id_len) 03315 entry->identity = strdup(id); 03316 03317 if (netsnmp_tlstmAddr_add(entry) != 0) 03318 netsnmp_tlstmAddr_free(entry); 03319 03320 return; 03321 } 03322 03323 static char * 03324 _find_tlstmAddr_fingerprint(const char *name) 03325 { 03326 snmpTlstmAddr lookup_key, *result; 03327 03328 if (NULL == name) 03329 return NULL; 03330 03331 lookup_key.name = NETSNMP_REMOVE_CONST(char*, name); 03332 03333 result = CONTAINER_FIND(_tlstmAddr, &lookup_key); 03334 if (NULL == result) 03335 return NULL; 03336 03337 return result->fingerprint; 03338 } 03339 03340 #ifndef NETSNMP_FEATURE_REMOVE_TLSTMADDR_GET_SERVERID 03341 char * 03342 netsnmp_tlstmAddr_get_serverId(const char *name) 03343 { 03344 snmpTlstmAddr lookup_key, *result; 03345 03346 if (NULL == name) 03347 return NULL; 03348 03349 lookup_key.name = NETSNMP_REMOVE_CONST(char*, name); 03350 03351 result = CONTAINER_FIND(_tlstmAddr, &lookup_key); 03352 if (NULL == result) 03353 return NULL; 03354 03355 return result->identity; 03356 } 03357 #endif /* NETSNMP_FEATURE_REMOVE_TLSTMADDR_GET_SERVERID */ 03358 /* 03359 * END snmpTlstmAddrTable data 03360 * ***************************************************************************/ 03361 03362 #else 03363 netsnmp_feature_unused(cert_util); 03364 #endif /* NETSNMP_FEATURE_REMOVE_CERT_UTIL */ 03365 netsnmp_feature_unused(cert_util); 03366 #endif /* defined(NETSNMP_USE_OPENSSL) && defined(HAVE_LIBSSL) */