static int _netsnmp_tlsbase_verify_remote_fingerprint (X509 * remote_cert, _netsnmpTLSBaseData * tlsdata, int try_default) { char *fingerprint; fingerprint = netsnmp_openssl_cert_get_fingerprint (remote_cert, -1); if (!fingerprint) { /* no peer cert */ snmp_log (LOG_ERR, "failed to get fingerprint of remote certificate\n"); return FAILED_FINGERPRINT_VERIFY; } if (!tlsdata->their_fingerprint && tlsdata->their_identity) { /* we have an identity; try and find it's fingerprint */ netsnmp_cert *peer_cert; peer_cert = netsnmp_cert_find (NS_CERT_REMOTE_PEER, NS_CERTKEY_MULTIPLE, tlsdata->their_identity); if (peer_cert) tlsdata->their_fingerprint = netsnmp_openssl_cert_get_fingerprint (peer_cert->ocert, -1); } if (!tlsdata->their_fingerprint && try_default) { /* try for the default instead */ netsnmp_cert *peer_cert; peer_cert = netsnmp_cert_find (NS_CERT_REMOTE_PEER, NS_CERTKEY_DEFAULT, NULL); if (peer_cert) tlsdata->their_fingerprint = netsnmp_openssl_cert_get_fingerprint (peer_cert->ocert, -1); } if (tlsdata->their_fingerprint) { netsnmp_fp_lowercase_and_strip_colon (tlsdata->their_fingerprint); if (0 != strcmp (tlsdata->their_fingerprint, fingerprint)) { snmp_log (LOG_ERR, "The fingerprint from the remote side's certificate didn't match the expected\n"); snmp_log (LOG_ERR, " got %s, expected %s\n", fingerprint, tlsdata->their_fingerprint); free (fingerprint); return FAILED_FINGERPRINT_VERIFY; } } else { DEBUGMSGTL (("tls_x509:verify", "No fingerprint for the remote entity available to verify\n")); free (fingerprint); return NO_FINGERPRINT_AVAILABLE; } free (fingerprint); return VERIFIED_FINGERPRINT; }
int _trust_this_cert (SSL_CTX * the_ctx, char *certspec) { netsnmp_cert *trustcert; DEBUGMSGTL (("sslctx_client", "Trying to load a trusted certificate: %s\n", certspec)); /* load this identifier into the trust chain */ trustcert = netsnmp_cert_find (NS_CERT_CA, NS_CERTKEY_MULTIPLE, certspec); if (!trustcert) trustcert = netsnmp_cert_find (NS_CERT_REMOTE_PEER, NS_CERTKEY_MULTIPLE, certspec); if (!trustcert) LOGANDDIE ("failed to find requested certificate to trust"); /* Add the certificate to the context */ if (netsnmp_cert_trust_ca (the_ctx, trustcert) != SNMPERR_SUCCESS) LOGANDDIE ("failed to load trust certificate"); return 1; }
SSL_CTX * sslctx_server_setup(const SSL_METHOD *method) { netsnmp_cert *id_cert; /*********************************************************************** * Set up the server context */ /* setting up for ssl */ SSL_CTX *the_ctx = SSL_CTX_new(NETSNMP_REMOVE_CONST(SSL_METHOD *, method)); if (!the_ctx) { LOGANDDIE("can't create a new context"); } id_cert = netsnmp_cert_find(NS_CERT_IDENTITY, NS_CERTKEY_DEFAULT, NULL); if (!id_cert) LOGANDDIE ("error finding server identity keys"); if (!id_cert->key || !id_cert->key->okey) LOGANDDIE("failed to load private key"); DEBUGMSGTL(("sslctx_server", "using public key: %s\n", id_cert->info.filename)); DEBUGMSGTL(("sslctx_server", "using private key: %s\n", id_cert->key->info.filename)); if (SSL_CTX_use_certificate(the_ctx, id_cert->ocert) <= 0) LOGANDDIE("failed to set the certificate to use"); if (SSL_CTX_use_PrivateKey(the_ctx, id_cert->key->okey) <= 0) LOGANDDIE("failed to set the private key to use"); if (!SSL_CTX_check_private_key(the_ctx)) LOGANDDIE("public and private keys incompatible"); SSL_CTX_set_read_ahead(the_ctx, 1); /* XXX: DTLS only? */ SSL_CTX_set_verify(the_ctx, SSL_VERIFY_PEER| SSL_VERIFY_FAIL_IF_NO_PEER_CERT| SSL_VERIFY_CLIENT_ONCE, &verify_callback); return _sslctx_common_setup(the_ctx, NULL); }
netsnmp_session *get_target_sessions (char *taglist, TargetFilterFunction * filterfunct, void *filterArg) { netsnmp_session *ret = NULL, thissess; struct targetAddrTable_struct *targaddrs; char buf[SPRINT_MAX_LEN]; char tags[MAX_TAGS][SPRINT_MAX_LEN], *cp; int numtags = 0, i; #if defined(NETSNMP_TRANSPORT_DTLSUDP_DOMAIN) || defined(NETSNMP_TRANSPORT_TLSTCP_DOMAIN) int tls = 0; #endif static struct targetParamTable_struct *param; DEBUGMSGTL (("target_sessions", "looking for: %s\n", taglist)); for (cp = taglist; cp && numtags < MAX_TAGS;) { cp = copy_nword (cp, tags[numtags], sizeof (tags[numtags])); DEBUGMSGTL (("target_sessions", " for: %d=%s\n", numtags, tags[numtags])); numtags++; } for (targaddrs = get_addrTable (); targaddrs; targaddrs = targaddrs->next) { /* * legal row? */ if (targaddrs->tDomain == NULL || targaddrs->tAddress == NULL || targaddrs->rowStatus != SNMP_ROW_ACTIVE) { DEBUGMSGTL (("target_sessions", " which is not ready yet\n")); continue; } if (netsnmp_tdomain_support (targaddrs->tDomain, targaddrs->tDomainLen, NULL, NULL) == 0) { snmp_log (LOG_ERR, "unsupported domain for target address table entry %s\n", targaddrs->name); } /* * check tag list to see if we match */ if (targaddrs->tagList) { int matched = 0; /* * loop through tag list looking for requested tags */ for (cp = targaddrs->tagList; cp && !matched;) { cp = copy_nword (cp, buf, sizeof (buf)); for (i = 0; i < numtags && !matched; i++) { if (strcmp (buf, tags[i]) == 0) { /* * found a valid target table entry */ DEBUGMSGTL (("target_sessions", "found one: %s\n", tags[i])); if (targaddrs->params) { param = get_paramEntry (targaddrs->params); if (!param || param->rowStatus != SNMP_ROW_ACTIVE) { /* * parameter entry must exist and be active */ continue; } } else { /* * parameter entry must be specified */ continue; } /* * last chance for caller to opt-out. Call * filtering function */ if (filterfunct && (*(filterfunct)) (targaddrs, param, filterArg)) { continue; } /* * Only one notification per TargetAddrEntry, * rather than one per tag */ matched = 1; if (targaddrs->storageType != ST_READONLY && targaddrs->sess && param->updateTime >= targaddrs->sessionCreationTime) { /* * parameters have changed, nuke the old session */ snmp_close (targaddrs->sess); targaddrs->sess = NULL; } /* * target session already exists? */ if (targaddrs->sess == NULL) { /* * create an appropriate snmp session and add * it to our return list */ netsnmp_transport *t = NULL; t = netsnmp_tdomain_transport_oid (targaddrs->tDomain, targaddrs->tDomainLen, targaddrs->tAddress, targaddrs->tAddressLen, 0); if (t == NULL) { DEBUGMSGTL (("target_sessions", "bad dest \"")); DEBUGMSGOID (("target_sessions", targaddrs->tDomain, targaddrs->tDomainLen)); DEBUGMSG (("target_sessions", "\", \"")); DEBUGMSGHEX (("target_sessions", targaddrs->tAddress, targaddrs->tAddressLen)); DEBUGMSG (("target_sessions", "\n")); continue; } else { char *dst_str = t->f_fmtaddr (t, NULL, 0); if (dst_str != NULL) { DEBUGMSGTL (("target_sessions", " to: %s\n", dst_str)); free (dst_str); } } /* * if tDomain is tls related, check for tls config */ #ifdef NETSNMP_TRANSPORT_DTLSUDP_DOMAIN tls = snmp_oid_compare (targaddrs->tDomain, targaddrs->tDomainLen, netsnmpDTLSUDPDomain, netsnmpDTLSUDPDomain_len); #endif #ifdef NETSNMP_TRANSPORT_TLSTCP_DOMAIN if (tls) tls = snmp_oid_compare (targaddrs->tDomain, targaddrs->tDomainLen, netsnmpTLSTCPDomain, netsnmpTLSTCPDomain_len); #endif #if defined(NETSNMP_TRANSPORT_DTLSUDP_DOMAIN) || defined(NETSNMP_TRANSPORT_TLSTCP_DOMAIN) if (!tls) { netsnmp_cert *cert; char *server_id = NULL; DEBUGMSGTL (("target_sessions", " looking up our id: %s\n", targaddrs->params)); cert = netsnmp_cert_find (NS_CERT_IDENTITY, NS_CERTKEY_TARGET_PARAM, targaddrs->params); netsnmp_assert (t->f_config); if (cert) { DEBUGMSGTL (("target_sessions", " found fingerprint: %s\n", cert->fingerprint)); t->f_config (t, "localCert", cert->fingerprint); } DEBUGMSGTL (("target_sessions", " looking up their id: %s\n", targaddrs->name)); cert = netsnmp_cert_find (NS_CERT_REMOTE_PEER, NS_CERTKEY_TARGET_ADDR, targaddrs->name); if (cert) { DEBUGMSGTL (("target_sessions", " found fingerprint: %s\n", cert->fingerprint)); t->f_config (t, "peerCert", cert->fingerprint); } #ifndef NETSNMP_FEATURE_REMOVE_TLSTMADDR_GET_SERVERID server_id = netsnmp_tlstmAddr_get_serverId (targaddrs->name); #endif /* NETSNMP_FEATURE_REMOVE_TLSTMADDR_GET_SERVERID */ if (server_id) { DEBUGMSGTL (("target_sessions", " found serverId: %s\n", server_id)); t->f_config (t, "their_hostname", server_id); } } #endif memset (&thissess, 0, sizeof (thissess)); thissess.timeout = (targaddrs->timeout) * 1000; thissess.retries = targaddrs->retryCount; DEBUGMSGTL (("target_sessions", "timeout: %d -> %ld\n", targaddrs->timeout, thissess.timeout)); if (param->mpModel == SNMP_VERSION_3 && param->secModel != SNMP_SEC_MODEL_USM && param->secModel != SNMP_SEC_MODEL_TSM) { snmp_log (LOG_ERR, "unsupported mpModel/secModel combo %d/%d for target %s\n", param->mpModel, param->secModel, targaddrs->name); /* * XXX: memleak */ netsnmp_transport_free (t); continue; } thissess.paramName = strdup (param->paramName); thissess.version = param->mpModel; if (param->mpModel == SNMP_VERSION_3) { thissess.securityName = strdup (param->secName); thissess.securityNameLen = strlen (thissess.securityName); thissess.securityLevel = param->secLevel; thissess.securityModel = param->secModel; #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) } else { thissess.community = (u_char *) strdup (param->secName); thissess.community_len = strlen ((char *) thissess.community); #endif } thissess.flags |= SNMP_FLAGS_DONT_PROBE; targaddrs->sess = snmp_add (&thissess, t, NULL, NULL); thissess.flags &= ~SNMP_FLAGS_DONT_PROBE; targaddrs->sessionCreationTime = time (NULL); } if (targaddrs->sess) { if (NULL == targaddrs->sess->paramName) targaddrs->sess->paramName = strdup (param->paramName); targaddrs->sess->next = ret; ret = targaddrs->sess; } else { snmp_sess_perror ("target session", &thissess); } } } } } } return ret; }
/* this is called during negotiationn */ int verify_callback (int ok, X509_STORE_CTX * ctx) { int err, depth; char buf[1024], *fingerprint; X509 *thecert; netsnmp_cert *cert; _netsnmp_verify_info *verify_info; SSL *ssl; thecert = X509_STORE_CTX_get_current_cert (ctx); err = X509_STORE_CTX_get_error (ctx); depth = X509_STORE_CTX_get_error_depth (ctx); /* things to do: */ X509_NAME_oneline (X509_get_subject_name (thecert), buf, sizeof (buf)); fingerprint = netsnmp_openssl_cert_get_fingerprint (thecert, -1); DEBUGMSGTL (("tls_x509:verify", "Cert: %s\n", buf)); DEBUGMSGTL (("tls_x509:verify", " fp: %s\n", fingerprint ? fingerprint : "unknown")); ssl = X509_STORE_CTX_get_ex_data (ctx, SSL_get_ex_data_X509_STORE_CTX_idx ()); verify_info = SSL_get_ex_data (ssl, openssl_local_index); if (verify_info && ok && depth > 0) { /* remember that a parent certificate has been marked as trusted */ verify_info->flags |= VRFY_PARENT_WAS_OK; } /* this ensures for self-signed certificates we have a valid locally known fingerprint and then accept it */ if (!ok && (X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT == err || X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY == err || X509_V_ERR_CERT_UNTRUSTED == err || X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE == err || X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN == err)) { cert = netsnmp_cert_find (NS_CERT_REMOTE_PEER, NS_CERTKEY_FINGERPRINT, (void *) fingerprint); if (cert) DEBUGMSGTL (("tls_x509:verify", " Found locally: %s/%s\n", cert->info.dir, cert->info.filename)); if (cert) { DEBUGMSGTL (("tls_x509:verify", "verify_callback called with: ok=%d ctx=%p depth=%d err=%i:%s\n", ok, ctx, depth, err, X509_verify_cert_error_string (err))); DEBUGMSGTL (("tls_x509:verify", " accepting matching fp of self-signed certificate found in: %s\n", cert->info.filename)); SNMP_FREE (fingerprint); return 1; } else { DEBUGMSGTL (("tls_x509:verify", " no matching fp found\n")); /* log where we are and why called */ snmp_log (LOG_ERR, "tls verification failure: ok=%d ctx=%p depth=%d err=%i:%s\n", ok, ctx, depth, err, X509_verify_cert_error_string (err)); SNMP_FREE (fingerprint); return 0; } if (0 == depth && verify_info && (verify_info->flags & VRFY_PARENT_WAS_OK)) { DEBUGMSGTL (("tls_x509:verify", "verify_callback called with: ok=%d ctx=%p depth=%d err=%i:%s\n", ok, ctx, depth, err, X509_verify_cert_error_string (err))); DEBUGMSGTL (("tls_x509:verify", " a parent was ok, so returning ok for this child certificate\n")); SNMP_FREE (fingerprint); return 1; /* we'll check the hostname later at this level */ } } if (0 == ok) snmp_log (LOG_ERR, "tls verification failure: ok=%d ctx=%p depth=%d err=%i:%s\n", ok, ctx, depth, err, X509_verify_cert_error_string (err)); else DEBUGMSGTL (("tls_x509:verify", "verify_callback called with: ok=%d ctx=%p depth=%d err=%i:%s\n", ok, ctx, depth, err, X509_verify_cert_error_string (err))); DEBUGMSGTL (("tls_x509:verify", " returning the passed in value of %d\n", ok)); SNMP_FREE (fingerprint); return (ok); }
SSL_CTX *sslctx_client_setup (const SSL_METHOD * method, _netsnmpTLSBaseData * tlsbase) { netsnmp_cert *id_cert, *peer_cert; SSL_CTX *the_ctx; /*********************************************************************** * Set up the client context */ the_ctx = SSL_CTX_new (NETSNMP_REMOVE_CONST (SSL_METHOD *, method)); if (!the_ctx) { snmp_log (LOG_ERR, "ack: %p\n", the_ctx); LOGANDDIE ("can't create a new context"); } SSL_CTX_set_read_ahead (the_ctx, 1); /* Required for DTLS */ SSL_CTX_set_verify (the_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE, &verify_callback); if (tlsbase->our_identity) { DEBUGMSGTL (("sslctx_client", "looking for local id: %s\n", tlsbase->our_identity)); id_cert = netsnmp_cert_find (NS_CERT_IDENTITY, NS_CERTKEY_MULTIPLE, tlsbase->our_identity); } else { DEBUGMSGTL (("sslctx_client", "looking for default local id: %s\n", tlsbase->our_identity)); id_cert = netsnmp_cert_find (NS_CERT_IDENTITY, NS_CERTKEY_DEFAULT, NULL); } if (!id_cert) LOGANDDIE ("error finding client identity keys"); if (!id_cert->key || !id_cert->key->okey) LOGANDDIE ("failed to load private key"); DEBUGMSGTL (("sslctx_client", "using public key: %s\n", id_cert->info.filename)); DEBUGMSGTL (("sslctx_client", "using private key: %s\n", id_cert->key->info.filename)); if (SSL_CTX_use_certificate (the_ctx, id_cert->ocert) <= 0) LOGANDDIE ("failed to set the certificate to use"); if (SSL_CTX_use_PrivateKey (the_ctx, id_cert->key->okey) <= 0) LOGANDDIE ("failed to set the private key to use"); if (!SSL_CTX_check_private_key (the_ctx)) LOGANDDIE ("public and private keys incompatible"); if (tlsbase->their_identity) peer_cert = netsnmp_cert_find (NS_CERT_REMOTE_PEER, NS_CERTKEY_MULTIPLE, tlsbase->their_identity); else peer_cert = netsnmp_cert_find (NS_CERT_REMOTE_PEER, NS_CERTKEY_DEFAULT, NULL); if (peer_cert) { DEBUGMSGTL (("sslctx_client", "server's expected public key: %s\n", peer_cert ? peer_cert->info.filename : "none")); /* Trust the expected certificate */ if (netsnmp_cert_trust_ca (the_ctx, peer_cert) != SNMPERR_SUCCESS) LOGANDDIE ("failed to set verify paths"); } /* trust a certificate (possibly a CA) aspecifically passed in */ if (tlsbase->trust_cert) { if (!_trust_this_cert (the_ctx, tlsbase->trust_cert)) return 0; } return _sslctx_common_setup (the_ctx, tlsbase); }