/**
 * @brief Add days or seconds to an ISO time string.
 * @naslfn{isotime_add}
 *
 * This function adds days or seconds to an ISO time string and
 * returns the resulting time string.  The number of days or seconds
 * are given using the named parameters; if none are given nothing is
 * added; if both are given both additions are performed.  This
 * function won't work for dates before the Gregorian calendar switch.
 *
 * @nasluparam
 *
 * - An ISO time string
 *
 * @naslnparam
 *
 * - @a years An integer with the number of years to add to the timestamp.
 *
 * - @a days An integer with the number of days to add to the timestamp.
 *
 * - @a seconds An integer with the number of seconds to add to the
 *              timestamp.
 *
 * @naslret The resulting ISO time string or NULL if the provided ISO
 *          time string is not valid or the result would overflow
 *          (i.e. year > 9999).
 *
 * @param[in] lexic  Lexical context of the NASL interpreter.
 *
 * @return A tree cell.
 */
tree_cell *
nasl_isotime_add (lex_ctxt *lexic)
{
  tree_cell *retc;
  my_isotime_t timebuf;
  const char *string;
  int nyears, ndays, nseconds;

  string = get_str_var_by_num (lexic, 0);
  if (!string
      || get_var_size_by_num (lexic, 0) < ISOTIME_SIZE -1
      || check_isotime (string))
    return NULL;
  memcpy (timebuf, string, ISOTIME_SIZE -1);
  timebuf[ISOTIME_SIZE - 1] = 0;

  nyears = get_int_local_var_by_name (lexic, "years", 0);
  ndays = get_int_local_var_by_name (lexic, "days", 0);
  nseconds = get_int_local_var_by_name (lexic, "seconds", 0);

  if (nyears && add_years_to_isotime (timebuf, nyears))
    return NULL;
  if (ndays && add_days_to_isotime (timebuf, ndays))
    return NULL;
  if (nseconds && add_seconds_to_isotime (timebuf, nseconds))
    return NULL;
  /* If nothing was added, explicitly add 0 years.  */
  if (!nyears && !ndays && !nseconds && add_years_to_isotime (timebuf, 0))
    return NULL;

  retc = alloc_typed_cell (CONST_STR);
  retc->x.str_val = estrdup (timebuf);
  retc->size = strlen (timebuf);
  return retc;
}
Beispiel #2
0
/* Check whether the certificate either given by fingerprint CERT_FPR
   or directly through the CERT object is valid by running an OCSP
   transaction.  With FORCE_DEFAULT_RESPONDER set only the configured
   default responder is used. */
gpg_error_t
ocsp_isvalid (ctrl_t ctrl, ksba_cert_t cert, const char *cert_fpr,
              int force_default_responder)
{
  gpg_error_t err;
  ksba_ocsp_t ocsp = NULL;
  ksba_cert_t issuer_cert = NULL;
  ksba_sexp_t sigval = NULL;
  gcry_sexp_t s_sig = NULL;
  ksba_isotime_t current_time;
  ksba_isotime_t this_update, next_update, revocation_time, produced_at;
  ksba_isotime_t tmp_time;
  ksba_status_t status;
  ksba_crl_reason_t reason;
  char *url_buffer = NULL;
  const char *url;
  gcry_md_hd_t md = NULL;
  int i, idx;
  char *oid;
  ksba_name_t name;
  fingerprint_list_t default_signer = NULL;

  /* Get the certificate.  */
  if (cert)
    {
      ksba_cert_ref (cert);

      err = find_issuing_cert (ctrl, cert, &issuer_cert);
      if (err)
        {
          log_error (_("issuer certificate not found: %s\n"),
                     gpg_strerror (err));
          goto leave;
        }
    }
  else
    {
      cert = get_cert_local (ctrl, cert_fpr);
      if (!cert)
        {
          log_error (_("caller did not return the target certificate\n"));
          err = gpg_error (GPG_ERR_GENERAL);
          goto leave;
        }
      issuer_cert = get_issuing_cert_local (ctrl, NULL);
      if (!issuer_cert)
        {
          log_error (_("caller did not return the issuing certificate\n"));
          err = gpg_error (GPG_ERR_GENERAL);
          goto leave;
        }
    }

  /* Create an OCSP instance.  */
  err = ksba_ocsp_new (&ocsp);
  if (err)
    {
      log_error (_("failed to allocate OCSP context: %s\n"),
                 gpg_strerror (err));
      goto leave;
    }



  /* Figure out the OCSP responder to use.
     1. Try to get the reponder from the certificate.
        We do only take http and https style URIs into account.
     2. If this fails use the default responder, if any.
   */
  url = NULL;
  for (idx=0; !url && !opt.ignore_ocsp_service_url && !force_default_responder
         && !(err=ksba_cert_get_authority_info_access (cert, idx,
                                                       &oid, &name)); idx++)
    {
      if ( !strcmp (oid, oidstr_ocsp) )
        {
          for (i=0; !url && ksba_name_enum (name, i); i++)
            {
              char *p = ksba_name_get_uri (name, i);
              if (p && (!ascii_strncasecmp (p, "http:", 5)
                        || !ascii_strncasecmp (p, "https:", 6)))
                url = url_buffer = p;
              else
                xfree (p);
            }
        }
      ksba_name_release (name);
      ksba_free (oid);
    }
  if (err && gpg_err_code (err) != GPG_ERR_EOF)
    {
      log_error (_("can't get authorityInfoAccess: %s\n"), gpg_strerror (err));
      goto leave;
    }
  if (!url)
    {
      if (!opt.ocsp_responder || !*opt.ocsp_responder)
        {
          log_info (_("no default OCSP responder defined\n"));
          err = gpg_error (GPG_ERR_CONFIGURATION);
          goto leave;
        }
      if (!opt.ocsp_signer)
        {
          log_info (_("no default OCSP signer defined\n"));
          err = gpg_error (GPG_ERR_CONFIGURATION);
          goto leave;
        }
      url = opt.ocsp_responder;
      default_signer = opt.ocsp_signer;
      if (opt.verbose)
        log_info (_("using default OCSP responder '%s'\n"), url);
    }
  else
    {
      if (opt.verbose)
        log_info (_("using OCSP responder '%s'\n"), url);
    }

  /* Ask the OCSP responder. */
  err = gcry_md_open (&md, GCRY_MD_SHA1, 0);
  if (err)
    {
      log_error (_("failed to establish a hashing context for OCSP: %s\n"),
                 gpg_strerror (err));
      goto leave;
    }
  err = do_ocsp_request (ctrl, ocsp, md, url, cert, issuer_cert);
  if (err)
    goto leave;

  /* We got a useful answer, check that the answer has a valid signature. */
  sigval = ksba_ocsp_get_sig_val (ocsp, produced_at);
  if (!sigval || !*produced_at)
    {
      err = gpg_error (GPG_ERR_INV_OBJ);
      goto leave;
    }
  if ( (err = canon_sexp_to_gcry (sigval, &s_sig)) )
    goto leave;
  xfree (sigval);
  sigval = NULL;
  err = check_signature (ctrl, ocsp, s_sig, md, default_signer);
  if (err)
    goto leave;

  /* We only support one certificate per request.  Check that the
     answer matches the right certificate. */
  err = ksba_ocsp_get_status (ocsp, cert,
                              &status, this_update, next_update,
                              revocation_time, &reason);
  if (err)
    {
      log_error (_("error getting OCSP status for target certificate: %s\n"),
                 gpg_strerror (err));
      goto leave;
    }

  /* In case the certificate has been revoked, we better invalidate
     our cached validation status. */
  if (status == KSBA_STATUS_REVOKED)
    {
      time_t validated_at = 0; /* That is: No cached validation available. */
      err = ksba_cert_set_user_data (cert, "validated_at",
                                     &validated_at, sizeof (validated_at));
      if (err)
        {
          log_error ("set_user_data(validated_at) failed: %s\n",
                     gpg_strerror (err));
          err = 0; /* The certificate is anyway revoked, and that is a
                      more important message than the failure of our
                      cache. */
        }
    }


  if (opt.verbose)
    {
      log_info (_("certificate status is: %s  (this=%s  next=%s)\n"),
                status == KSBA_STATUS_GOOD? _("good"):
                status == KSBA_STATUS_REVOKED? _("revoked"):
                status == KSBA_STATUS_UNKNOWN? _("unknown"):
                status == KSBA_STATUS_NONE? _("none"): "?",
                this_update, next_update);
      if (status == KSBA_STATUS_REVOKED)
        log_info (_("certificate has been revoked at: %s due to: %s\n"),
                  revocation_time,
                  reason == KSBA_CRLREASON_UNSPECIFIED?   "unspecified":
                  reason == KSBA_CRLREASON_KEY_COMPROMISE? "key compromise":
                  reason == KSBA_CRLREASON_CA_COMPROMISE?   "CA compromise":
                  reason == KSBA_CRLREASON_AFFILIATION_CHANGED?
                                                      "affiliation changed":
                  reason == KSBA_CRLREASON_SUPERSEDED?   "superseeded":
                  reason == KSBA_CRLREASON_CESSATION_OF_OPERATION?
                                                  "cessation of operation":
                  reason == KSBA_CRLREASON_CERTIFICATE_HOLD?
                                                  "certificate on hold":
                  reason == KSBA_CRLREASON_REMOVE_FROM_CRL?
                                                  "removed from CRL":
                  reason == KSBA_CRLREASON_PRIVILEGE_WITHDRAWN?
                                                  "privilege withdrawn":
                  reason == KSBA_CRLREASON_AA_COMPROMISE? "AA compromise":
                  reason == KSBA_CRLREASON_OTHER?   "other":"?");

    }


  if (status == KSBA_STATUS_REVOKED)
    err = gpg_error (GPG_ERR_CERT_REVOKED);
  else if (status == KSBA_STATUS_UNKNOWN)
    err = gpg_error (GPG_ERR_NO_DATA);
  else if (status != KSBA_STATUS_GOOD)
    err = gpg_error (GPG_ERR_GENERAL);

  /* Allow for some clock skew. */
  gnupg_get_isotime (current_time);
  add_seconds_to_isotime (current_time, opt.ocsp_max_clock_skew);

  if (strcmp (this_update, current_time) > 0 )
    {
      log_error (_("OCSP responder returned a status in the future\n"));
      log_info ("used now: %s  this_update: %s\n", current_time, this_update);
      if (!err)
        err = gpg_error (GPG_ERR_TIME_CONFLICT);
    }

  /* Check that THIS_UPDATE is not too far back in the past. */
  gnupg_copy_time (tmp_time, this_update);
  add_seconds_to_isotime (tmp_time,
                          opt.ocsp_max_period+opt.ocsp_max_clock_skew);
  if (!*tmp_time || strcmp (tmp_time, current_time) < 0 )
    {
      log_error (_("OCSP responder returned a non-current status\n"));
      log_info ("used now: %s  this_update: %s\n",
                current_time, this_update);
      if (!err)
        err = gpg_error (GPG_ERR_TIME_CONFLICT);
    }

  /* Check that we are not beyound NEXT_UPDATE  (plus some extra time). */
  if (*next_update)
    {
      gnupg_copy_time (tmp_time, next_update);
      add_seconds_to_isotime (tmp_time,
                              opt.ocsp_current_period+opt.ocsp_max_clock_skew);
      if (!*tmp_time && strcmp (tmp_time, current_time) < 0 )
        {
          log_error (_("OCSP responder returned an too old status\n"));
          log_info ("used now: %s  next_update: %s\n",
                    current_time, next_update);
          if (!err)
            err = gpg_error (GPG_ERR_TIME_CONFLICT);
        }
    }


 leave:
  gcry_md_close (md);
  gcry_sexp_release (s_sig);
  xfree (sigval);
  ksba_cert_release (issuer_cert);
  ksba_cert_release (cert);
  ksba_ocsp_release (ocsp);
  xfree (url_buffer);
  return err;
}