/* Return the certificate matching SUBJECT_DN. SEQ should initially be set to 0 and bumped up to get the next subject with that DN. */ ksba_cert_t get_cert_bysubject (const char *subject_dn, unsigned int seq) { /* Simple and very inefficient implementation and API. fixme! */ cert_item_t ci; int i; if (!subject_dn) return NULL; acquire_cache_read_lock (); for (i=0; i < 256; i++) { for (ci=cert_cache[i]; ci; ci = ci->next) if (ci->cert && ci->subject_dn && !strcmp (ci->subject_dn, subject_dn)) if (!seq--) { ksba_cert_ref (ci->cert); release_cache_lock (); return ci->cert; } } release_cache_lock (); return NULL; }
/* Deinitialize the certificate cache. With FULL set to true even the unused certificate slots are released. */ void cert_cache_deinit (int full) { cert_item_t ci, ci2; int i; if (!initialization_done) return; acquire_cache_write_lock (); for (i=0; i < 256; i++) for (ci=cert_cache[i]; ci; ci = ci->next) clean_cache_slot (ci); if (full) { for (i=0; i < 256; i++) { for (ci=cert_cache[i]; ci; ci = ci2) { ci2 = ci->next; xfree (ci); } cert_cache[i] = NULL; } } total_loaded_certificates = 0; total_extra_certificates = 0; initialization_done = 0; release_cache_lock (); }
/* Return a certificate object for the given fingerprint. FPR is expected to be a 20 byte binary SHA-1 fingerprint. If no matching certificate is available in the cache NULL is returned. The caller must release a returned certificate. Note that although we are using reference counting the caller should not just compare the pointers to check for identical certificates. */ ksba_cert_t get_cert_byfpr (const unsigned char *fpr) { cert_item_t ci; acquire_cache_read_lock (); for (ci=cert_cache[*fpr]; ci; ci = ci->next) if (ci->cert && !memcmp (ci->fpr, fpr, 20)) { ksba_cert_ref (ci->cert); release_cache_lock (); return ci->cert; } release_cache_lock (); return NULL; }
/* Return the certificate matching ISSUER_DN and SERIALNO. */ ksba_cert_t get_cert_bysn (const char *issuer_dn, ksba_sexp_t serialno) { /* Simple and inefficient implementation. fixme! */ cert_item_t ci; int i; acquire_cache_read_lock (); for (i=0; i < 256; i++) { for (ci=cert_cache[i]; ci; ci = ci->next) if (ci->cert && !strcmp (ci->issuer_dn, issuer_dn) && !compare_serialno (ci->sn, serialno)) { ksba_cert_ref (ci->cert); release_cache_lock (); return ci->cert; } } release_cache_lock (); return NULL; }
/* Put CERT into the certificate cache and store the fingerprint of the certificate into FPR_BUFFER. If the certificate is already in the cache do not print a warning; just store the fingerprint. FPR_BUFFER needs to be at least 20 bytes. */ gpg_error_t cache_cert_silent (ksba_cert_t cert, void *fpr_buffer) { gpg_error_t err; acquire_cache_write_lock (); err = put_cert (cert, 0, 0, fpr_buffer); release_cache_lock (); if (gpg_err_code (err) == GPG_ERR_DUP_VALUE) err = 0; if (err) log_error (_("error caching certificate: %s\n"), gpg_strerror (err)); return err; }
/* Return 0 if the certificate is a trusted certificate. Returns GPG_ERR_NOT_TRUSTED if it is not trusted or other error codes in case of systems errors. */ gpg_error_t is_trusted_cert (ksba_cert_t cert) { unsigned char fpr[20]; cert_item_t ci; cert_compute_fpr (cert, fpr); acquire_cache_read_lock (); for (ci=cert_cache[*fpr]; ci; ci = ci->next) if (ci->cert && !memcmp (ci->fpr, fpr, 20)) { if (ci->flags.trusted) { release_cache_lock (); return 0; /* Yes, it is trusted. */ } break; } release_cache_lock (); return gpg_error (GPG_ERR_NOT_TRUSTED); }
/* Put CERT into the certificate cache. */ gpg_error_t cache_cert (ksba_cert_t cert) { gpg_error_t err; acquire_cache_write_lock (); err = put_cert (cert, 0, 0, NULL); release_cache_lock (); if (gpg_err_code (err) == GPG_ERR_DUP_VALUE) log_info (_("certificate already cached\n")); else if (!err) log_info (_("certificate cached\n")); else log_error (_("error caching certificate: %s\n"), gpg_strerror (err)); return err; }
/* Initialize the certificate cache if not yet done. */ void cert_cache_init (void) { char *dname; if (initialization_done) return; init_cache_lock (); acquire_cache_write_lock (); dname = make_filename (gnupg_sysconfdir (), "trusted-certs", NULL); load_certs_from_dir (dname, 1); xfree (dname); dname = make_filename (gnupg_sysconfdir (), "extra-certs", NULL); load_certs_from_dir (dname, 0); xfree (dname); initialization_done = 1; release_cache_lock (); cert_cache_print_stats (); }
/* Return the certificate matching SUBJECT_DN and (if not NULL) KEYID. If it is not already in the cache, try to find it from other resources. Note, that the external search does not work for user certificates because the LDAP lookup is on the caCertificate attribute. For our purposes this is just fine. */ ksba_cert_t find_cert_bysubject (ctrl_t ctrl, const char *subject_dn, ksba_sexp_t keyid) { gpg_error_t err; int seq; ksba_cert_t cert = NULL; cert_fetch_context_t context = NULL; ksba_sexp_t subj; /* If we have certificates from an OCSP request we first try to use them. This is because these certificates will really be the required ones and thus even in the case that they can't be uniquely located by the following code we can use them. This is for example required by Telesec certificates where a keyId is used but the issuer certificate comes without a subject keyId! */ if (ctrl->ocsp_certs && subject_dn) { cert_item_t ci; cert_ref_t cr; int i; /* For efficiency reasons we won't use get_cert_bysubject here. */ acquire_cache_read_lock (); for (i=0; i < 256; i++) for (ci=cert_cache[i]; ci; ci = ci->next) if (ci->cert && ci->subject_dn && !strcmp (ci->subject_dn, subject_dn)) for (cr=ctrl->ocsp_certs; cr; cr = cr->next) if (!memcmp (ci->fpr, cr->fpr, 20)) { ksba_cert_ref (ci->cert); release_cache_lock (); return ci->cert; /* We use this certificate. */ } release_cache_lock (); if (DBG_LOOKUP) log_debug ("find_cert_bysubject: certificate not in ocsp_certs\n"); } /* First we check whether the certificate is cached. */ for (seq=0; (cert = get_cert_bysubject (subject_dn, seq)); seq++) { if (!keyid) break; /* No keyid requested, so return the first one found. */ if (!ksba_cert_get_subj_key_id (cert, NULL, &subj) && !cmp_simple_canon_sexp (keyid, subj)) { xfree (subj); break; /* Found matching cert. */ } xfree (subj); ksba_cert_release (cert); } if (cert) return cert; /* Done. */ if (DBG_LOOKUP) log_debug ("find_cert_bysubject: certificate not in cache\n"); /* Ask back to the service requester to return the certificate. This is because we can assume that he already used the certificate while checking for the CRL. */ if (keyid) cert = get_cert_local_ski (ctrl, subject_dn, keyid); else { /* In contrast to get_cert_local_ski, get_cert_local uses any passed pattern, so we need to make sure that an exact subject search is done. */ char *buf; buf = xtrymalloc (1 + strlen (subject_dn) + 1); if (!buf) { log_error ("can't allocate enough memory: %s\n", strerror (errno)); return NULL; } strcpy (stpcpy (buf, "/"), subject_dn); cert = get_cert_local (ctrl, buf); xfree (buf); } if (cert) { cache_cert (cert); return cert; /* Done. */ } if (DBG_LOOKUP) log_debug ("find_cert_bysubject: certificate not returned by caller" " - doing lookup\n"); /* Locate the certificate using external resources. */ while (!cert) { char *subjdn; if (!context) { err = ca_cert_fetch (ctrl, &context, subject_dn); if (err) { log_error (_("error fetching certificate by subject: %s\n"), gpg_strerror (err)); break; } } err = fetch_next_ksba_cert (context, &cert); if (err) { log_error (_("error fetching certificate by subject: %s\n"), gpg_strerror (err) ); break; } subjdn = ksba_cert_get_subject (cert, 0); if (strcmp (subject_dn, subjdn)) { log_info ("find_cert_bysubject: subject DN does not match\n"); ksba_cert_release (cert); cert = NULL; ksba_free (subjdn); continue; } if (DBG_LOOKUP) { log_debug (" considering certificate (/"); dump_string (subjdn); log_printf (")\n"); } ksba_free (subjdn); /* If no key ID has been provided, we return the first match. */ if (!keyid) { cache_cert (cert); if (DBG_LOOKUP) log_debug (" found\n"); break; /* Ready. */ } /* With the key ID given we need to compare it. */ if (!ksba_cert_get_subj_key_id (cert, NULL, &subj)) { if (!cmp_simple_canon_sexp (keyid, subj)) { ksba_free (subj); cache_cert (cert); if (DBG_LOOKUP) log_debug (" found\n"); break; /* Ready. */ } } ksba_free (subj); ksba_cert_release (cert); cert = NULL; } end_cert_fetch (context); return cert; }
int main(int argc, char *argv[]) { int retc = 0; int ret; char *hostfile = NULL; char *cachefile = NULL; char *xml = NULL; char *xmlfile = NULL; int cachefd = -1; char value[64]; char units[64]; int retry_count = 0, ret2; if (get_config(argc, argv) < 0) { retc = 2; goto cleanup; } if (config.heartbeat > 0) { debug("Checking heartbeat for %s with threshold %d\n", config.host, config.heartbeat); } else { debug("Checking %s for %s metric\n", config.host, config.metric); } hostfile = get_full_cache_path(config.cachepath, config.host); cachefile = get_full_cache_path(config.cachepath, config.cachename); retry: debug("Checking cache at %s\n", cachefile); ret = check_cache_age(cachefile); if (ret < 0) { printf("ERROR: Unable to check cache age.\n"); retc = 2; goto cleanup; } if (ret < config.max_age) { debug("Cache age is %d\n", ret); } else { debug("Cache age greater than configured max (%d >= %d)\n", ret, config.max_age); debug("Connecting to %s on port %d\n", config.gmetad_host, config.gmetad_port); ret = fetch_xml(config.gmetad_host, config.gmetad_port, &xml); if (ret < 0) { printf("ERROR: Unable to get XML data: %d.\n", ret); retc = 2; goto cleanup; } debug("Read %d bytes from %s\n", ret, config.gmetad_host); if (config.dump) { xmlfile = calloc((strlen(config.cachepath) + 9), sizeof(char)); sprintf(xmlfile, "%s/dump.xml", config.cachepath); debug("Dumping XML to %s\n", xmlfile); if (write_xml(xml, ret, xmlfile) < 0) { printf("ERROR: Unable to dump XML.\n"); retc = 2; goto cleanup; } } ret2 = get_cache_lock(cachefile, &cachefd); if (ret2 < 0) { if (retry_count == MAX_RETRY) { printf("ERROR: Unable to get cache lock after retrying %d times. Stale lock?", retry_count); retc = 2; goto cleanup; } else { backoff(retry_count / 2.0); } retry_count++; goto retry; } debug("Parsing XML into %s\n", config.cachepath); ret = parse_xml_to_cache(xml, ret, config.cachepath, cachefile); if (ret < 0) { printf("ERROR: Unable to parse XML.\n"); retc = 2; goto cleanup; } touch_cache_lock(cachefile); release_cache_lock(cachefile, &cachefd); } if (config.heartbeat > 0) { strcpy(config.metric, "#REPORTED"); } if (locate_hostfile(hostfile) < 0) { printf("CRITICAL - Unable to locate cache file for %s\n", config.host); retc = 2; goto cleanup; } debug("Fetching %s metric from cache at %s\n", config.metric, hostfile); ret = fetch_value_from_cache(hostfile, config.metric, value, units); if (ret < 0) { printf("CRITICAL - Unable to read cache at %s\n", hostfile); retc = 2; goto cleanup; } else if (ret == 0) { printf("CRITICAL - Metric %s not found\n", config.metric); retc = 2; goto cleanup; } debug("Checking...\n"); if (config.heartbeat > 0) { int diff = time(NULL) - strtol(value, NULL, 10); if (diff > config.heartbeat) { printf("CRITICAL - %d over threshold %d\n", diff, config.heartbeat); retc = 2; goto cleanup; } else { printf("OK - %d\n", diff); goto cleanup; } } if (threshold_check(config.critical, value)) { printf("CRITICAL - %s %s\n", value, units); retc = 2; } else { if (threshold_check(config.warning, value)) { printf("WARNING - %s %s\n", value, units); retc = 1; } else { printf("OK - %s %s\n", value, units); } } cleanup: if (cachefd >= 0) { release_cache_lock(cachefile, &cachefd); } if (xml != NULL) free(xml); if (xmlfile != NULL) free(xmlfile); if (hostfile != NULL) free(hostfile); if (cachefile != NULL) free(cachefile); exit(retc); }