cURL / Mailing Lists / curl-library / Single Mail

curl-library

curl bad verify SSL certificates (fwd)

From: Daniel Stenberg <daniel_at_haxx.se>
Date: Thu, 15 Aug 2002 09:48:38 +0200 (MET DST)

Hey crowd.

I would like to show you all what popped in on the bug tracker just now, and
I think my comments below illustrate my cluelessness in this issue. I'd
appreciate some assistance in this matter.

-- 
 Daniel Stenberg -- curl related mails on curl related mailing lists please
---------- Forwarded message ----------
Date: Thu, 15 Aug 2002 00:38:31 -0700
From: noreply_at_sourceforge.net
To: noreply_at_sourceforge.net
Subject: [ curl-Bugs-595426 ] curl bad verify SSL certificates
Bugs item #595426, was opened at 2002-08-15 09: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: 9
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;
}
----------------------------------------------------------------------
>Comment By: Daniel Stenberg (bagder)
Date: 2002-08-15 09:38
Message:
Logged In: YES
user_id=1110
I'm sorry, but I don't understand much of this talk. I'm
quite far away from an expert on SSL or certificate issues,
I'm probably stupid and I think I need the problem more
spelled out in my face.
I did however see the posting on bugtraq previously when
this issue came up, saying that curl _isn't_ vulnerable:
http://online.securityfocus.com/archive/1/286892
Your submitted code does a lot of various things. Are you
suggesting that reading files, matching lines using a
miniature regex engine etc is what this issue *requires* in
order to get solved?
I'm puzzeled, but of course dedicated to have any existing
problems of this nature fixed ASAP.
I would appreciate if we could move further dicussions of
this issue to the libcurl mailing list, as there might be
more cluefull people around there than that read this single
bug report.
Thanks for bringing the issue up. Let's make a fix.
----------------------------------------------------------------------
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