cURL / Mailing Lists / curl-users / Single Mail

curl-users

[ curl-Bugs-595426 ] curl bad verify SSL certificates

From: <noreply_at_sourceforge.net>
Date: Thu, 15 Aug 2002 00:18:16 -0700

Bugs item #595426, was opened at 2002-08-15 03:18
You can respond by visiting:
https://sourceforge.net/tracker/?func=detail&atid=100976&aid=595426&group_id=976

Category: https
Group: wrong behaviour
Status: Open
Resolution: None
Priority: 5
Submitted By: Tom Zerucha (tomz)
Assigned to: Daniel Stenberg (bagder)
Summary: curl bad verify SSL certificates

Initial Comment:
http://www.thoughtcrime.org/ie-ssl-chain.txt

The above describes the problem with IE, which curl is also vulnerable to.

There are bits indicating if a cert can or cannot be used to sign other certs in the chain. In this case a cert which isn't allowed to sign other certs, but is used anyway, won't be detected, allowing "man in the middle" attacks.

OpenSSL properly detects the anomaly if called properly, but the returned error needs to prevent a connection or return an error.

The command line test app, openssl, s_client -connect www.amazon.com:443 returns an error 20 for a dnsspoof-ed www.amazon.com -> thoughtcrime.org, (it establishes the session anyway so you can connect) but curl https://www.amazon.com give no warning or error.

Netscape and/or Mozilla don't have this problem, neither do some versions of Opera.

Here is some verify callback code I used (several versions ago for an encrypting proxy for lynx). The key routine is the verify callback, but I've included the rest for completeness. Also the file for the encrypting proxy (edssl83.tgz) is attached.

I have not verified this specifically, but it does contain the test.

#define XBUFSIZ 16384
static char xferbuf[XBUFSIZ];
static char goodcerts[128] = "goodcerts";
static int debugflag = 0;
static char hostname[256] = "";
static char thisuser[256];
static char error[256] = "";
static FILE *errlog = stderr;

/* match common names using shell expressions */
int shellexp(char *t, char *m)
{
  static char *lastx;
  int good;
  char *lastt, *tref;

  tref = t;
  lastt = t;
  while (*m) {
    good = 0;
    switch (*m) {
    case '~':
      return (!shellexp(tref, ++m));
    case '$':
      return (!(*t));
#ifdef ALLOWILD
    case '*':
      while (*m && *m == '*')
        m++;
      if (!*m)
        return 1;
      while (*t)
        if (shellexp(t++, m))
          return 1;
      return 0;
    case '[':
      {
        int revflag;

        if ((revflag = (*++m == '^')))
          m++;
        while (!good && *m && *m != ']') {
          switch (*m) {
          case '-':
            if (*t >= m[-1] && *t <= m[1])
              good = 1;
            m++;
            m++;
            break;
          case '\':
            if (!*++m)
              return 0;
          default:
            if (*++m == *t)
              good = 1;
            break;
          }
        }
        if (good == revflag)
          return 0;
        while (*m != ']')
          if (!*++m)
            return 0;
      }
    case '?': /* with fallthrough */
      m++, t++;
      break;
#endif
    case '(':
      do {
        m++;
        if (shellexp(t, m))
          good = 1, lastt = lastx;
        while (*m && *m != ')' && *m != '|')
          m++;
      } while (*m == '|');
      if (!good || *m++ != ')')
        return 0;
      t = lastt;
      break;
    case '|':
    case ')':
      lastx = t;
      return 1;
    case '\':
      if (!*++m)
        return 0;
    default: /* with fallthrough */
      if (tolower(*m++) != tolower(*t++))
        return 0;
    }
  }
  return (!(*t));
}

/* override bad cert */
int goodover(char *tstcname)
{
  char cbuf[255];
  int n;
  FILE *tmpfd;

  /* look for good cert override: file format is oneline-space-hostname */
  cbuf[0] = 0;
  n = strlen(tstcname);
  tmpfd = fopen(goodcerts, "r");
  if (tmpfd != NULL) {
    while (!feof(tmpfd)) {
      fgets(cbuf, 255, tmpfd);
      if (!strncasecmp(tstcname, cbuf, n)
          && !strncasecmp(hostname, &cbuf[n + 1], strlen(hostname)))
        break;
    }
    fclose(tmpfd);
  }
  return (!strncasecmp(tstcname, cbuf, n)
          && !strncasecmp(hostname, &cbuf[n + 1], strlen(hostname)));
}

/* match cert oneline v.s. hostname */
int namematch(char *tstcname, char *hostname)
{
  char *cp;
  char cbuf[255];
  int n;

  if (goodover(tstcname))
    return 1;
  cp = strstr(tstcname, "/CN="); /* move to common name */
  if (cp == NULL)
    return 0;
  cbuf[0] = '\0';
  cp += 4;
  n = strlen(hostname);
  strcpy(cbuf, cp);
  if ((cp = strchr(cbuf, '/')))
    *cp = '\0';
  return (shellexp(hostname, cbuf));
}

#include "buffer.h"
#include "ssl.h"
#include "err.h"
#include "pem.h"
#include "x509.h"

static struct hostent *hostres;

static int verify_depth = 0;
static int verify_error = X509_V_OK;

/* should be X509 * but we can just have them as char *. */
int verify_callback(ok, ctx)
     int ok;
     X509_STORE_CTX *ctx;
{
  char buf[256];
  X509 *err_cert;
  int err, depth;

  BIO *bio_err = BIO_new(BIO_s_file());

  BIO_set_fp(bio_err, errlog, BIO_NOCLOSE);

  err_cert = X509_STORE_CTX_get_current_cert(ctx);
  err = X509_STORE_CTX_get_error(ctx);
  depth = X509_STORE_CTX_get_error_depth(ctx);

  X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256);

  if (depth == 0)
    strncpy(thisuser, buf, 255);

  if (ok && depth == 0 && !goodover(thisuser)
      && !namematch(thisuser, hostname)) {
    /* proxy SSL to remote secure server - verify server host */
    sprintf(error, "Host:%s != Cert:%s", hostname, thisuser);
    BIO_printf(bio_err, "ERROR: Hostname [%s] != Name in cert\n", hostname);
    ok = 0;
  }
  if (!ok && goodover(thisuser))
    ok = 1;

  if (!debugflag && ok == 1 && err < 2)
    return (ok);

  ERR_load_crypto_strings(), SSL_load_error_strings();

  if (hostres)
    BIO_printf(bio_err, "Reverse DNS hostname: %s\n", hostres->h_name);
  BIO_printf(bio_err, "SSL CERTIFICATE STATUS: ok=%d depth=%d err=%s(%d)\n",
             ok, depth, X509_verify_cert_error_string(err), err);
  BIO_printf(bio_err, "Subject Cert Oneline:\n%s %s\n", thisuser, hostname);
  if (!ok) {
    if (verify_depth > depth)
      verify_error = X509_V_ERR_CERT_CHAIN_TOO_LONG;
    else
      ok++, verify_error = X509_V_OK;
  }
  switch (ctx->error) {
  case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
    X509_NAME_oneline(X509_get_issuer_name(ctx->current_cert), buf, 256);
    BIO_printf(bio_err, "unknown issuer= %s\n", buf);
    break;
  case X509_V_ERR_CERT_NOT_YET_VALID:
  case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
    BIO_printf(bio_err, "notBefore=");
    ASN1_UTCTIME_print(bio_err, X509_get_notBefore(ctx->current_cert));
    BIO_printf(bio_err, "\n");
    break;
  case X509_V_ERR_CERT_HAS_EXPIRED:
  case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
    BIO_printf(bio_err, "notAfter=");
    ASN1_UTCTIME_print(bio_err, X509_get_notAfter(ctx->current_cert));
    BIO_printf(bio_err, "\n");
    break;
  }
  return (ok);
}

/* convert string (dns or dotted decimal) to address */
int getaddr(char *toaddr, struct sockaddr_in *sin)
{
  long n;

  n = inet_addr(toaddr);
  if (n != INADDR_NONE)
    memcpy(&sin->sin_addr, &n, sizeof(n));
  else {
    hostres = gethostbyname(toaddr);
    if (hostres == NULL)
      return 1;
    memcpy(&sin->sin_addr, hostres->h_addr, hostres->h_length);
  }
  hostres = gethostbyaddr((char *) &sin->sin_addr, sizeof(sin->sin_addr),
                          AF_INET);
  return 0;
}

----------------------------------------------------------------------

You can respond by visiting:
https://sourceforge.net/tracker/?func=detail&atid=100976&aid=595426&group_id=976

-------------------------------------------------------
This sf.net email is sponsored by: OSDN - Tired of that same old
cell phone? Get a new here for FREE!
https://www.inphonic.com/r.asp?r=sourceforge1&refcode1=vs3390
Received on 2002-08-15