Exemple #1
0
CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
{
  PRInt32 err;
  PRFileDesc *model = NULL;
  PRBool ssl2, ssl3, tlsv1;
  struct SessionHandle *data = conn->data;
  curl_socket_t sockfd = conn->sock[sockindex];
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
  SECStatus rv;
  char *certDir = NULL;
  int curlerr;
  const int *cipher_to_enable;

  curlerr = CURLE_SSL_CONNECT_ERROR;

  if (connssl->state == ssl_connection_complete)
    return CURLE_OK;

  connssl->data = data;

#ifdef HAVE_PK11_CREATEGENERICOBJECT
  connssl->cacert[0] = NULL;
  connssl->cacert[1] = NULL;
  connssl->key = NULL;
#endif

  /* FIXME. NSS doesn't support multiple databases open at the same time. */
  PR_Lock(nss_initlock);
  if(!initialized) {
    struct_stat st;

    /* First we check if $SSL_DIR points to a valid dir */
    certDir = getenv("SSL_DIR");
    if(certDir) {
      if((stat(certDir, &st) != 0) ||
              (!S_ISDIR(st.st_mode))) {
        certDir = NULL;
      }
    }

    /* Now we check if the default location is a valid dir */
    if(!certDir) {
      if((stat(SSL_DIR, &st) == 0) &&
              (S_ISDIR(st.st_mode))) {
        certDir = (char *)SSL_DIR;
      }
    }

    if (!NSS_IsInitialized()) {
      initialized = 1;
      infof(conn->data, "Initializing NSS with certpath: %s\n",
            certDir ? certDir : "none");
      if(!certDir) {
        rv = NSS_NoDB_Init(NULL);
      }
      else {
        char *certpath = PR_smprintf("%s%s",
                         NSS_VersionCheck("3.12.0") ? "sql:" : "",
                         certDir);
        rv = NSS_Initialize(certpath, "", "", "", NSS_INIT_READONLY);
        PR_smprintf_free(certpath);
      }
      if(rv != SECSuccess) {
        infof(conn->data, "Unable to initialize NSS database\n");
        curlerr = CURLE_SSL_CACERT_BADFILE;
        initialized = 0;
        PR_Unlock(nss_initlock);
        goto error;
      }
    }

    if(num_enabled_ciphers() == 0)
      NSS_SetDomesticPolicy();

#ifdef HAVE_PK11_CREATEGENERICOBJECT
    if(!mod) {
      char *configstring = aprintf("library=%s name=PEM", pem_library);
      if(!configstring) {
        PR_Unlock(nss_initlock);
        goto error;
      }
      mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE);
      free(configstring);

      if(!mod || !mod->loaded) {
        if(mod) {
          SECMOD_DestroyModule(mod);
          mod = NULL;
        }
        infof(data, "WARNING: failed to load NSS PEM library %s. Using OpenSSL "
              "PEM certificates will not work.\n", pem_library);
      }
    }
#endif

    PK11_SetPasswordFunc(nss_get_password);

  }
  PR_Unlock(nss_initlock);

  model = PR_NewTCPSocket();
  if(!model)
    goto error;
  model = SSL_ImportFD(NULL, model);

  if(SSL_OptionSet(model, SSL_SECURITY, PR_TRUE) != SECSuccess)
    goto error;
  if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != SECSuccess)
    goto error;
  if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != SECSuccess)
    goto error;

  ssl2 = ssl3 = tlsv1 = PR_FALSE;

  switch (data->set.ssl.version) {
  default:
  case CURL_SSLVERSION_DEFAULT:
    ssl3 = tlsv1 = PR_TRUE;
    break;
  case CURL_SSLVERSION_TLSv1:
    tlsv1 = PR_TRUE;
    break;
  case CURL_SSLVERSION_SSLv2:
    ssl2 = PR_TRUE;
    break;
  case CURL_SSLVERSION_SSLv3:
    ssl3 = PR_TRUE;
    break;
  }

  if(SSL_OptionSet(model, SSL_ENABLE_SSL2, ssl2) != SECSuccess)
    goto error;
  if(SSL_OptionSet(model, SSL_ENABLE_SSL3, ssl3) != SECSuccess)
    goto error;
  if(SSL_OptionSet(model, SSL_ENABLE_TLS, tlsv1) != SECSuccess)
    goto error;

  if(SSL_OptionSet(model, SSL_V2_COMPATIBLE_HELLO, ssl2) != SECSuccess)
    goto error;

  /* enable all ciphers from enable_ciphers_by_default */
  cipher_to_enable = enable_ciphers_by_default;
  while (SSL_NULL_WITH_NULL_NULL != *cipher_to_enable) {
    if (SSL_CipherPrefSet(model, *cipher_to_enable, PR_TRUE) != SECSuccess) {
      curlerr = CURLE_SSL_CIPHER;
      goto error;
    }
    cipher_to_enable++;
  }

  if(data->set.ssl.cipher_list) {
    if(set_ciphers(data, model, data->set.ssl.cipher_list) != SECSuccess) {
      curlerr = CURLE_SSL_CIPHER;
      goto error;
    }
  }

  if(data->set.ssl.verifyhost == 1)
    infof(data, "warning: ignoring unsupported value (1) of ssl.verifyhost\n");

  data->set.ssl.certverifyresult=0; /* not checked yet */
  if(SSL_BadCertHook(model, (SSLBadCertHandler) BadCertHandler, conn)
     != SECSuccess) {
    goto error;
  }
  if(SSL_HandshakeCallback(model, (SSLHandshakeCallback) HandshakeCallback,
                           NULL) != SECSuccess)
    goto error;

  if(!data->set.ssl.verifypeer)
    /* skip the verifying of the peer */
    ;
  else if(data->set.ssl.CAfile) {
    int rc = nss_load_cert(&conn->ssl[sockindex], data->set.ssl.CAfile,
                           PR_TRUE);
    if(!rc) {
      curlerr = CURLE_SSL_CACERT_BADFILE;
      goto error;
    }
  }
  else if(data->set.ssl.CApath) {
    struct_stat st;
    PRDir      *dir;
    PRDirEntry *entry;

    if(stat(data->set.ssl.CApath, &st) == -1) {
      curlerr = CURLE_SSL_CACERT_BADFILE;
      goto error;
    }

    if(S_ISDIR(st.st_mode)) {
      int rc;

      dir = PR_OpenDir(data->set.ssl.CApath);
      do {
        entry = PR_ReadDir(dir, PR_SKIP_BOTH | PR_SKIP_HIDDEN);

        if(entry) {
          char fullpath[PATH_MAX];

          snprintf(fullpath, sizeof(fullpath), "%s/%s", data->set.ssl.CApath,
                   entry->name);
          rc = nss_load_cert(&conn->ssl[sockindex], fullpath, PR_TRUE);
          /* FIXME: check this return value! */
        }
        /* This is purposefully tolerant of errors so non-PEM files
         * can be in the same directory */
      } while(entry != NULL);
      PR_CloseDir(dir);
    }
  }
  infof(data,
        "  CAfile: %s\n"
        "  CApath: %s\n",
        data->set.ssl.CAfile ? data->set.ssl.CAfile : "none",
        data->set.ssl.CApath ? data->set.ssl.CApath : "none");

  if (data->set.ssl.CRLfile) {
    int rc = nss_load_crl(data->set.ssl.CRLfile, PR_FALSE);
    if (!rc) {
      curlerr = CURLE_SSL_CRL_BADFILE;
      goto error;
    }
    infof(data,
          "  CRLfile: %s\n",
          data->set.ssl.CRLfile ? data->set.ssl.CRLfile : "none");
  }

  if(data->set.str[STRING_CERT]) {
    bool nickname_alloc = FALSE;
    char *nickname = fmt_nickname(data->set.str[STRING_CERT], &nickname_alloc);
    if(!nickname)
      return CURLE_OUT_OF_MEMORY;

    if(!cert_stuff(conn, sockindex, data->set.str[STRING_CERT],
                    data->set.str[STRING_KEY])) {
      /* failf() is already done in cert_stuff() */
      if(nickname_alloc)
        free(nickname);
      return CURLE_SSL_CERTPROBLEM;
    }

    /* this "takes over" the pointer to the allocated name or makes a
       dup of it */
    connssl->client_nickname = nickname_alloc?nickname:strdup(nickname);
    if(!connssl->client_nickname)
      return CURLE_OUT_OF_MEMORY;

  }
  else
    connssl->client_nickname = NULL;

  if(SSL_GetClientAuthDataHook(model, SelectClientCert,
                               (void *)connssl) != SECSuccess) {
    curlerr = CURLE_SSL_CERTPROBLEM;
    goto error;
  }

  /* Import our model socket  onto the existing file descriptor */
  connssl->handle = PR_ImportTCPSocket(sockfd);
  connssl->handle = SSL_ImportFD(model, connssl->handle);
  if(!connssl->handle)
    goto error;
  PR_Close(model); /* We don't need this any more */

  /* This is the password associated with the cert that we're using */
  if (data->set.str[STRING_KEY_PASSWD]) {
      SSL_SetPKCS11PinArg(connssl->handle, data->set.str[STRING_KEY_PASSWD]);
  }

  /* Force handshake on next I/O */
  SSL_ResetHandshake(connssl->handle, /* asServer */ PR_FALSE);

  SSL_SetURL(connssl->handle, conn->host.name);

  /* Force the handshake now */
  if(SSL_ForceHandshakeWithTimeout(connssl->handle,
                                    PR_SecondsToInterval(HANDSHAKE_TIMEOUT))
      != SECSuccess) {
    if(conn->data->set.ssl.certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN)
      curlerr = CURLE_PEER_FAILED_VERIFICATION;
    else if(conn->data->set.ssl.certverifyresult!=0)
      curlerr = CURLE_SSL_CACERT;
    goto error;
  }

  connssl->state = ssl_connection_complete;

  display_conn_info(conn, connssl->handle);

  if (data->set.str[STRING_SSL_ISSUERCERT]) {
    SECStatus ret;
    bool nickname_alloc = FALSE;
    char *nickname = fmt_nickname(data->set.str[STRING_SSL_ISSUERCERT],
                                  &nickname_alloc);

    if(!nickname)
      return CURLE_OUT_OF_MEMORY;

    ret = check_issuer_cert(connssl->handle, nickname);

    if(nickname_alloc)
      free(nickname);

    if(SECFailure == ret) {
      infof(data,"SSL certificate issuer check failed\n");
      curlerr = CURLE_SSL_ISSUER_ERROR;
      goto error;
    }
    else {
      infof(data, "SSL certificate issuer check ok\n");
    }
  }

  return CURLE_OK;

error:
  err = PR_GetError();
  infof(data, "NSS error %d\n", err);
  if(model)
    PR_Close(model);
  return curlerr;
}
Exemple #2
0
CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)
{
  PRInt32 err;
  PRFileDesc *model = NULL;
  PRBool ssl2 = PR_FALSE;
  PRBool ssl3 = PR_FALSE;
  PRBool tlsv1 = PR_FALSE;
  PRBool ssl_no_cache;
  PRBool ssl_cbc_random_iv;
  struct SessionHandle *data = conn->data;
  curl_socket_t sockfd = conn->sock[sockindex];
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
  CURLcode curlerr;
  const int *cipher_to_enable;
  PRSocketOptionData sock_opt;
  long time_left;
  PRUint32 timeout;

  if(connssl->state == ssl_connection_complete)
    return CURLE_OK;

  connssl->data = data;

#ifdef HAVE_PK11_CREATEGENERICOBJECT
  /* list of all NSS objects we need to destroy in Curl_nss_close() */
  connssl->obj_list = Curl_llist_alloc(nss_destroy_object);
  if(!connssl->obj_list)
    return CURLE_OUT_OF_MEMORY;
#endif

  /* FIXME. NSS doesn't support multiple databases open at the same time. */
  PR_Lock(nss_initlock);
  curlerr = nss_init(conn->data);
  if(CURLE_OK != curlerr) {
    PR_Unlock(nss_initlock);
    goto error;
  }

  curlerr = CURLE_SSL_CONNECT_ERROR;

#ifdef HAVE_PK11_CREATEGENERICOBJECT
  if(!mod) {
    char *configstring = aprintf("library=%s name=PEM", pem_library);
    if(!configstring) {
      PR_Unlock(nss_initlock);
      goto error;
    }
    mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE);
    free(configstring);

    if(!mod || !mod->loaded) {
      if(mod) {
        SECMOD_DestroyModule(mod);
        mod = NULL;
      }
      infof(data, "WARNING: failed to load NSS PEM library %s. Using "
            "OpenSSL PEM certificates will not work.\n", pem_library);
    }
  }
#endif

  PK11_SetPasswordFunc(nss_get_password);
  PR_Unlock(nss_initlock);

  model = PR_NewTCPSocket();
  if(!model)
    goto error;
  model = SSL_ImportFD(NULL, model);

  /* make the socket nonblocking */
  sock_opt.option = PR_SockOpt_Nonblocking;
  sock_opt.value.non_blocking = PR_TRUE;
  if(PR_SetSocketOption(model, &sock_opt) != PR_SUCCESS)
    goto error;

  if(SSL_OptionSet(model, SSL_SECURITY, PR_TRUE) != SECSuccess)
    goto error;
  if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != SECSuccess)
    goto error;
  if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != SECSuccess)
    goto error;

  /* do not use SSL cache if we are not going to verify peer */
  ssl_no_cache = (data->set.ssl.verifypeer) ? PR_FALSE : PR_TRUE;
  if(SSL_OptionSet(model, SSL_NO_CACHE, ssl_no_cache) != SECSuccess)
    goto error;

  switch (data->set.ssl.version) {
  default:
  case CURL_SSLVERSION_DEFAULT:
    ssl3 = PR_TRUE;
    if(data->state.ssl_connect_retry)
      infof(data, "TLS disabled due to previous handshake failure\n");
    else
      tlsv1 = PR_TRUE;
    break;
  case CURL_SSLVERSION_TLSv1:
    tlsv1 = PR_TRUE;
    break;
  case CURL_SSLVERSION_SSLv2:
    ssl2 = PR_TRUE;
    break;
  case CURL_SSLVERSION_SSLv3:
    ssl3 = PR_TRUE;
    break;
  }

  if(SSL_OptionSet(model, SSL_ENABLE_SSL2, ssl2) != SECSuccess)
    goto error;
  if(SSL_OptionSet(model, SSL_ENABLE_SSL3, ssl3) != SECSuccess)
    goto error;
  if(SSL_OptionSet(model, SSL_ENABLE_TLS, tlsv1) != SECSuccess)
    goto error;

  if(SSL_OptionSet(model, SSL_V2_COMPATIBLE_HELLO, ssl2) != SECSuccess)
    goto error;

  ssl_cbc_random_iv = !data->set.ssl_enable_beast;
#ifdef SSL_CBC_RANDOM_IV
  /* unless the user explicitly asks to allow the protocol vulnerability, we
     use the work-around */
  if(SSL_OptionSet(model, SSL_CBC_RANDOM_IV, ssl_cbc_random_iv) != SECSuccess)
    infof(data, "warning: failed to set SSL_CBC_RANDOM_IV = %d\n",
          ssl_cbc_random_iv);
#else
  if(ssl_cbc_random_iv)
    infof(data, "warning: support for SSL_CBC_RANDOM_IV not compiled in\n");
#endif

  /* reset the flag to avoid an infinite loop */
  data->state.ssl_connect_retry = FALSE;

  /* enable all ciphers from enable_ciphers_by_default */
  cipher_to_enable = enable_ciphers_by_default;
  while(SSL_NULL_WITH_NULL_NULL != *cipher_to_enable) {
    if(SSL_CipherPrefSet(model, *cipher_to_enable, PR_TRUE) != SECSuccess) {
      curlerr = CURLE_SSL_CIPHER;
      goto error;
    }
    cipher_to_enable++;
  }

  if(data->set.ssl.cipher_list) {
    if(set_ciphers(data, model, data->set.ssl.cipher_list) != SECSuccess) {
      curlerr = CURLE_SSL_CIPHER;
      goto error;
    }
  }

  if(!data->set.ssl.verifypeer && data->set.ssl.verifyhost)
    infof(data, "warning: ignoring value of ssl.verifyhost\n");
  else if(data->set.ssl.verifyhost == 1)
    infof(data, "warning: ignoring unsupported value (1) of ssl.verifyhost\n");

  /* bypass the default SSL_AuthCertificate() hook in case we do not want to
   * verify peer */
  if(SSL_AuthCertificateHook(model, nss_auth_cert_hook, conn) != SECSuccess)
    goto error;

  data->set.ssl.certverifyresult=0; /* not checked yet */
  if(SSL_BadCertHook(model, (SSLBadCertHandler) BadCertHandler, conn)
     != SECSuccess) {
    goto error;
  }
  if(SSL_HandshakeCallback(model, (SSLHandshakeCallback) HandshakeCallback,
                           NULL) != SECSuccess)
    goto error;

  if(data->set.ssl.verifypeer) {
    const CURLcode rv = nss_load_ca_certificates(conn, sockindex);
    if(CURLE_OK != rv) {
      curlerr = rv;
      goto error;
    }
  }

  if(data->set.ssl.CRLfile) {
    if(SECSuccess != nss_load_crl(data->set.ssl.CRLfile)) {
      curlerr = CURLE_SSL_CRL_BADFILE;
      goto error;
    }
    infof(data,
          "  CRLfile: %s\n",
          data->set.ssl.CRLfile ? data->set.ssl.CRLfile : "none");
  }

  if(data->set.str[STRING_CERT]) {
    char *nickname = dup_nickname(data, STRING_CERT);
    if(nickname) {
      /* we are not going to use libnsspem.so to read the client cert */
#ifdef HAVE_PK11_CREATEGENERICOBJECT
      connssl->obj_clicert = NULL;
#endif
    }
    else {
      CURLcode rv = cert_stuff(conn, sockindex, data->set.str[STRING_CERT],
                               data->set.str[STRING_KEY]);
      if(CURLE_OK != rv) {
        /* failf() is already done in cert_stuff() */
        curlerr = rv;
        goto error;
      }
    }

    /* store the nickname for SelectClientCert() called during handshake */
    connssl->client_nickname = nickname;
  }
  else
    connssl->client_nickname = NULL;

  if(SSL_GetClientAuthDataHook(model, SelectClientCert,
                               (void *)connssl) != SECSuccess) {
    curlerr = CURLE_SSL_CERTPROBLEM;
    goto error;
  }

  /* Import our model socket  onto the existing file descriptor */
  connssl->handle = PR_ImportTCPSocket(sockfd);
  connssl->handle = SSL_ImportFD(model, connssl->handle);
  if(!connssl->handle)
    goto error;

  PR_Close(model); /* We don't need this any more */
  model = NULL;

  /* This is the password associated with the cert that we're using */
  if(data->set.str[STRING_KEY_PASSWD]) {
    SSL_SetPKCS11PinArg(connssl->handle, data->set.str[STRING_KEY_PASSWD]);
  }

  /* Force handshake on next I/O */
  SSL_ResetHandshake(connssl->handle, /* asServer */ PR_FALSE);

  SSL_SetURL(connssl->handle, conn->host.name);

  /* check timeout situation */
  time_left = Curl_timeleft(data, NULL, TRUE);
  if(time_left < 0L) {
    failf(data, "timed out before SSL handshake");
    goto error;
  }
  timeout = PR_MillisecondsToInterval((PRUint32) time_left);

  /* Force the handshake now */
  if(SSL_ForceHandshakeWithTimeout(connssl->handle, timeout) != SECSuccess) {
    if(conn->data->set.ssl.certverifyresult == SSL_ERROR_BAD_CERT_DOMAIN)
      curlerr = CURLE_PEER_FAILED_VERIFICATION;
    else if(conn->data->set.ssl.certverifyresult!=0)
      curlerr = CURLE_SSL_CACERT;
    goto error;
  }

  connssl->state = ssl_connection_complete;
  conn->recv[sockindex] = nss_recv;
  conn->send[sockindex] = nss_send;

  display_conn_info(conn, connssl->handle);

  if(data->set.str[STRING_SSL_ISSUERCERT]) {
    SECStatus ret = SECFailure;
    char *nickname = dup_nickname(data, STRING_SSL_ISSUERCERT);
    if(nickname) {
      /* we support only nicknames in case of STRING_SSL_ISSUERCERT for now */
      ret = check_issuer_cert(connssl->handle, nickname);
      free(nickname);
    }

    if(SECFailure == ret) {
      infof(data,"SSL certificate issuer check failed\n");
      curlerr = CURLE_SSL_ISSUER_ERROR;
      goto error;
    }
    else {
      infof(data, "SSL certificate issuer check ok\n");
    }
  }

  return CURLE_OK;

  error:
  /* reset the flag to avoid an infinite loop */
  data->state.ssl_connect_retry = FALSE;

  err = PR_GetError();
  if(handle_cc_error(err, data))
    curlerr = CURLE_SSL_CERTPROBLEM;
  else
    infof(data, "NSS error %d\n", err);

  if(model)
    PR_Close(model);

#ifdef HAVE_PK11_CREATEGENERICOBJECT
    /* cleanup on connection failure */
    Curl_llist_destroy(connssl->obj_list, NULL);
    connssl->obj_list = NULL;
#endif

  if(ssl3 && tlsv1 && isTLSIntoleranceError(err)) {
    /* schedule reconnect through Curl_retry_request() */
    data->state.ssl_connect_retry = TRUE;
    infof(data, "Error in TLS handshake, trying SSLv3...\n");
    return CURLE_OK;
  }

  return curlerr;
}
Exemple #3
0
/* ====================================================== */
CURLcode
Curl_SSLConnect(struct connectdata *conn)
{
  CURLcode retcode = CURLE_OK;

#ifdef USE_SSLEAY
  struct SessionHandle *data = conn->data;
  int err;
  char * str;
  SSL_METHOD *req_method;
  SSL_SESSION *ssl_sessionid=NULL;
  ASN1_TIME *certdate;

  /* mark this is being ssl enabled from here on out. */
  conn->ssl.use = TRUE;

  if(!ssl_seeded || data->set.ssl.random_file || data->set.ssl.egdsocket) {
    /* Make funny stuff to get random input */
    random_the_seed(data);

    ssl_seeded = TRUE;
  }

  /* check to see if we've been told to use an explicit SSL/TLS version */
  switch(data->set.ssl.version) {
  default:
  case CURL_SSLVERSION_DEFAULT:
    /* we try to figure out version */
    req_method = SSLv23_client_method();
    break;
  case CURL_SSLVERSION_TLSv1:
    req_method = TLSv1_client_method();
    break;
  case CURL_SSLVERSION_SSLv2:
    req_method = SSLv2_client_method();
    break;
  case CURL_SSLVERSION_SSLv3:
    req_method = SSLv3_client_method();
    break;
  }
    
  conn->ssl.ctx = SSL_CTX_new(req_method);

  if(!conn->ssl.ctx) {
    failf(data, "SSL: couldn't create a context!");
    return CURLE_OUT_OF_MEMORY;
  }
    
  if(data->set.cert) {
    if (!cert_stuff(conn,
                    data->set.cert,
                    data->set.cert_type,
                    data->set.key,
                    data->set.key_type)) {
      /* failf() is already done in cert_stuff() */
      return CURLE_SSL_CONNECT_ERROR;
    }
  }

  if(data->set.ssl.cipher_list) {
    if (!SSL_CTX_set_cipher_list(conn->ssl.ctx,
                                 data->set.ssl.cipher_list)) {
      failf(data, "failed setting cipher list");
      return CURLE_SSL_CONNECT_ERROR;
    }
  }

  if(data->set.ssl.verifypeer){
    SSL_CTX_set_verify(conn->ssl.ctx,
                       SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT|
                       SSL_VERIFY_CLIENT_ONCE,
                       cert_verify_callback);
    if (!SSL_CTX_load_verify_locations(conn->ssl.ctx,
                                       data->set.ssl.CAfile,
                                       data->set.ssl.CApath)) {
      failf(data,"error setting cerficate verify locations");
      return CURLE_SSL_CONNECT_ERROR;
    }
  }
  else
    SSL_CTX_set_verify(conn->ssl.ctx, SSL_VERIFY_NONE, cert_verify_callback);


  /* Lets make an SSL structure */
  conn->ssl.handle = SSL_new (conn->ssl.ctx);
  SSL_set_connect_state (conn->ssl.handle);

  conn->ssl.server_cert = 0x0;

  if(!conn->bits.reuse) {
    /* We're not re-using a connection, check if there's a cached ID we
       can/should use here! */
    if(!Get_SSL_Session(conn, &ssl_sessionid)) {
      /* we got a session id, use it! */
      SSL_set_session(conn->ssl.handle, ssl_sessionid);
      /* Informational message */
      infof (data, "SSL re-using session ID\n");
    }
  }

  /* pass the raw socket into the SSL layers */
  SSL_set_fd(conn->ssl.handle, conn->firstsocket);

  do {
    int what;
    fd_set writefd;
    fd_set readfd;
    struct timeval interval;
    long timeout_ms;

    err = SSL_connect(conn->ssl.handle);

    what = SSL_get_error(conn->ssl.handle, err);

    FD_ZERO(&writefd);
    FD_ZERO(&readfd);

    if(SSL_ERROR_WANT_READ == what)
      FD_SET(conn->firstsocket, &readfd);
    else if(SSL_ERROR_WANT_WRITE == what)
      FD_SET(conn->firstsocket, &writefd);
    else
      break; /* untreated error */

    /* Find out if any timeout is set. If not, use 300 seconds.
       Otherwise, figure out the most strict timeout of the two possible one
       and then how much time that has elapsed to know how much time we
       allow for the connect call */
    if(data->set.timeout || data->set.connecttimeout) {
      double has_passed;

      /* Evaluate in milliseconds how much time that has passed */
      has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.start);

#ifndef min
#define min(a, b)   ((a) < (b) ? (a) : (b))
#endif

      /* get the most strict timeout of the ones converted to milliseconds */
      if(data->set.timeout &&
         (data->set.timeout>data->set.connecttimeout))
        timeout_ms = data->set.timeout*1000;
      else
        timeout_ms = data->set.connecttimeout*1000;
      
      /* subtract the passed time */
      timeout_ms -= (long)has_passed;
      
      if(timeout_ms < 0) {
        /* a precaution, no need to continue if time already is up */
        failf(data, "SSL connection timeout");
        return CURLE_OPERATION_TIMEOUTED;
      }
    }
    else
      /* no particular time-out has been set */
      timeout_ms=300000; /* milliseconds, default to five minutes */

    interval.tv_sec = timeout_ms/1000;
    timeout_ms -= interval.tv_sec*1000;

    interval.tv_usec = timeout_ms*1000;

    what = select(conn->firstsocket+1, &readfd, &writefd, NULL, &interval);
    if(what > 0)
      /* reabable or writable, go loop yourself */
      continue;
    else if(0 == what) {
      /* timeout */
      failf(data, "SSL connection timeout");
      return CURLE_OPERATION_TIMEOUTED;
    }
    else
      break; /* get out of loop */
  } while(1);

  /* 1  is fine
     0  is "not successful but was shut down controlled"
     <0 is "handshake was not successful, because a fatal error occurred" */
  if (err <= 0) {
    err = ERR_get_error(); 
    failf(data, "SSL: %s", ERR_error_string(err, NULL));
    return CURLE_SSL_CONNECT_ERROR;
  }

  /* Informational message */
  infof (data, "SSL connection using %s\n",
         SSL_get_cipher(conn->ssl.handle));

  if(!ssl_sessionid) {
    /* Since this is not a cached session ID, then we want to stach this one
       in the cache! */
    Store_SSL_Session(conn);
  }

  
  /* Get server's certificate (note: beware of dynamic allocation) - opt */
  /* major serious hack alert -- we should check certificates
   * to authenticate the server; otherwise we risk man-in-the-middle
   * attack
   */

  conn->ssl.server_cert = SSL_get_peer_certificate (conn->ssl.handle);
  if(!conn->ssl.server_cert) {
    failf(data, "SSL: couldn't get peer certificate!");
    return CURLE_SSL_PEER_CERTIFICATE;
  }
  infof (data, "Server certificate:\n");
  
  str = X509_NAME_oneline (X509_get_subject_name (conn->ssl.server_cert),
                           NULL, 0);
  if(!str) {
    failf(data, "SSL: couldn't get X509-subject!");
    X509_free(conn->ssl.server_cert);
    return CURLE_SSL_CONNECT_ERROR;
  }
  infof(data, "\t subject: %s\n", str);
  CRYPTO_free(str);

  certdate = X509_get_notBefore(conn->ssl.server_cert);
  Curl_ASN1_UTCTIME_output(conn, "\t start date: ", certdate);

  certdate = X509_get_notAfter(conn->ssl.server_cert);
  Curl_ASN1_UTCTIME_output(conn, "\t expire date: ", certdate);

  if (data->set.ssl.verifyhost) {
    char peer_CN[257];
    if (X509_NAME_get_text_by_NID(X509_get_subject_name(conn->ssl.server_cert),
                                  NID_commonName,
                                  peer_CN,
                                  sizeof(peer_CN)) < 0) {
      failf(data, "SSL: unable to obtain common name from peer certificate");
      X509_free(conn->ssl.server_cert);
      return CURLE_SSL_PEER_CERTIFICATE;
    }

    if (!strequal(peer_CN, conn->hostname)) {
      if (data->set.ssl.verifyhost > 1) {
        failf(data, "SSL: certificate subject name '%s' does not match "
              "target host name '%s'",
              peer_CN, conn->hostname);
        X509_free(conn->ssl.server_cert);
        return CURLE_SSL_PEER_CERTIFICATE;
      }
      else
        infof(data,
              "\t common name: %s (does not match '%s')\n",
              peer_CN, conn->hostname);
    }
    else
      infof(data, "\t common name: %s (matched)\n", peer_CN);
  }

  str = X509_NAME_oneline (X509_get_issuer_name  (conn->ssl.server_cert),
                           NULL, 0);
  if(!str) {
    failf(data, "SSL: couldn't get X509-issuer name!");
    X509_free(conn->ssl.server_cert);
    return CURLE_SSL_CONNECT_ERROR;
  }
  infof(data, "\t issuer: %s\n", str);
  CRYPTO_free(str);

  /* We could do all sorts of certificate verification stuff here before
     deallocating the certificate. */

  if(data->set.ssl.verifypeer) {
    data->set.ssl.certverifyresult=SSL_get_verify_result(conn->ssl.handle);
    if (data->set.ssl.certverifyresult != X509_V_OK) {
      failf(data, "SSL certificate verify result: %d",
            data->set.ssl.certverifyresult);
      retcode = CURLE_SSL_PEER_CERTIFICATE;
    }
  }
  else
    data->set.ssl.certverifyresult=0;

  X509_free(conn->ssl.server_cert);
#else /* USE_SSLEAY */
  /* this is for "-ansi -Wall -pedantic" to stop complaining!   (rabe) */
  (void) conn;
#endif
  return retcode;
}
Exemple #4
0
/* ====================================================== */
CURLcode
Curl_ossl_connect(struct connectdata *conn,
                  int sockindex)
{
  CURLcode retcode = CURLE_OK;

  struct SessionHandle *data = conn->data;
  int err;
  long lerr;
  int what;
  char * str;
  SSL_METHOD_QUAL SSL_METHOD *req_method=NULL;
  void *ssl_sessionid=NULL;
  ASN1_TIME *certdate;
  curl_socket_t sockfd = conn->sock[sockindex];
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];

  if(!ssl_seeded || data->set.ssl.random_file || data->set.ssl.egdsocket) {
    /* Make funny stuff to get random input */
    random_the_seed(data);

    ssl_seeded = TRUE;
  }

  /* check to see if we've been told to use an explicit SSL/TLS version */
  switch(data->set.ssl.version) {
  default:
  case CURL_SSLVERSION_DEFAULT:
    /* we try to figure out version */
    req_method = SSLv23_client_method();
    break;
  case CURL_SSLVERSION_TLSv1:
    req_method = TLSv1_client_method();
    break;
  case CURL_SSLVERSION_SSLv2:
    req_method = SSLv2_client_method();
    break;
  case CURL_SSLVERSION_SSLv3:
    req_method = SSLv3_client_method();
    break;
  }

  connssl->ctx = SSL_CTX_new(req_method);

  if(!connssl->ctx) {
    failf(data, "SSL: couldn't create a context!");
    return CURLE_OUT_OF_MEMORY;
  }

#ifdef SSL_CTRL_SET_MSG_CALLBACK
  if (data->set.fdebug) {
    if (!SSL_CTX_callback_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK,
                               (void (*)(void))ssl_tls_trace)) {
      failf(data, "SSL: couldn't set callback!");
      return CURLE_SSL_CONNECT_ERROR;
    }

    if (!SSL_CTX_ctrl(connssl->ctx, SSL_CTRL_SET_MSG_CALLBACK_ARG, 0, conn)) {
      failf(data, "SSL: couldn't set callback argument!");
      return CURLE_SSL_CONNECT_ERROR;
    }
  }
#endif

  /* OpenSSL contains code to work-around lots of bugs and flaws in various
     SSL-implementations. SSL_CTX_set_options() is used to enabled those
     work-arounds. The man page for this option states that SSL_OP_ALL enables
     ll the work-arounds and that "It is usually safe to use SSL_OP_ALL to
     enable the bug workaround options if compatibility with somewhat broken
     implementations is desired."

  */
  SSL_CTX_set_options(connssl->ctx, SSL_OP_ALL);

#if 0
  /*
   * Not sure it's needed to tell SSL_connect() that socket is
   * non-blocking. It doesn't seem to care, but just return with
   * SSL_ERROR_WANT_x.
   */
  if (data->state.used_interface == Curl_if_multi)
    SSL_CTX_ctrl(connssl->ctx, BIO_C_SET_NBIO, 1, NULL);
#endif

  if(data->set.cert) {
    if(!cert_stuff(conn,
                   connssl->ctx,
                   data->set.cert,
                   data->set.cert_type,
                   data->set.key,
                   data->set.key_type)) {
      /* failf() is already done in cert_stuff() */
      return CURLE_SSL_CERTPROBLEM;
    }
  }

  if(data->set.ssl.cipher_list) {
    if(!SSL_CTX_set_cipher_list(connssl->ctx,
                                data->set.ssl.cipher_list)) {
      failf(data, "failed setting cipher list");
      return CURLE_SSL_CIPHER;
    }
  }

  if (data->set.ssl.CAfile || data->set.ssl.CApath) {
    /* tell SSL where to find CA certificates that are used to verify
       the servers certificate. */
    if (!SSL_CTX_load_verify_locations(connssl->ctx, data->set.ssl.CAfile,
                                       data->set.ssl.CApath)) {
      if (data->set.ssl.verifypeer) {
        /* Fail if we insist on successfully verifying the server. */
        failf(data,"error setting certificate verify locations:\n"
              "  CAfile: %s\n  CApath: %s\n",
              data->set.ssl.CAfile ? data->set.ssl.CAfile : "none",
              data->set.ssl.CApath ? data->set.ssl.CApath : "none");
        return CURLE_SSL_CACERT;
      }
      else {
        /* Just continue with a warning if no strict  certificate verification
           is required. */
        infof(data, "error setting certificate verify locations,"
              " continuing anyway:\n");
      }
    }
    else {
      /* Everything is fine. */
      infof(data, "successfully set certificate verify locations:\n");
    }
    infof(data,
          "  CAfile: %s\n"
          "  CApath: %s\n",
          data->set.ssl.CAfile ? data->set.ssl.CAfile : "none",
          data->set.ssl.CApath ? data->set.ssl.CApath : "none");
  }
  /* SSL always tries to verify the peer, this only says whether it should
   * fail to connect if the verification fails, or if it should continue
   * anyway. In the latter case the result of the verification is checked with
   * SSL_get_verify_result() below. */
  SSL_CTX_set_verify(connssl->ctx,
                     data->set.ssl.verifypeer?SSL_VERIFY_PEER:SSL_VERIFY_NONE,
                     cert_verify_callback);

  /* give application a chance to interfere with SSL set up. */
  if(data->set.ssl.fsslctx) {
    retcode = (*data->set.ssl.fsslctx)(data, connssl->ctx,
                                       data->set.ssl.fsslctxp);
    if(retcode) {
      failf(data,"error signaled by ssl ctx callback");
      return retcode;
    }
  }

  /* Lets make an SSL structure */
  connssl->handle = SSL_new(connssl->ctx);
  if (!connssl->handle) {
    failf(data, "SSL: couldn't create a context (handle)!");
    return CURLE_OUT_OF_MEMORY;
  }
  SSL_set_connect_state(connssl->handle);

  connssl->server_cert = 0x0;

  /* Check if there's a cached ID we can/should use here! */
  if(!Curl_ssl_getsessionid(conn, &ssl_sessionid, NULL)) {
    /* we got a session id, use it! */
    if (!SSL_set_session(connssl->handle, ssl_sessionid)) {
      failf(data, "SSL: SSL_set_session failed: %s",
            ERR_error_string(ERR_get_error(),NULL));
      return CURLE_SSL_CONNECT_ERROR;
    }
    /* Informational message */
    infof (data, "SSL re-using session ID\n");
  }

  /* pass the raw socket into the SSL layers */
  if (!SSL_set_fd(connssl->handle, sockfd)) {
     failf(data, "SSL: SSL_set_fd failed: %s",
           ERR_error_string(ERR_get_error(),NULL));
     return CURLE_SSL_CONNECT_ERROR;
  }

  while(1) {
    int writefd;
    int readfd;
    long timeout_ms;
    long has_passed;

    /* Find out if any timeout is set. If not, use 300 seconds.
       Otherwise, figure out the most strict timeout of the two possible one
       and then how much time that has elapsed to know how much time we
       allow for the connect call */
    if(data->set.timeout || data->set.connecttimeout) {

      /* get the most strict timeout of the ones converted to milliseconds */
      if(data->set.timeout &&
         (data->set.timeout>data->set.connecttimeout))
        timeout_ms = data->set.timeout*1000;
      else
        timeout_ms = data->set.connecttimeout*1000;
    }
    else
      /* no particular time-out has been set */
      timeout_ms= DEFAULT_CONNECT_TIMEOUT;

    /* Evaluate in milliseconds how much time that has passed */
    has_passed = Curl_tvdiff(Curl_tvnow(), data->progress.t_startsingle);

    /* subtract the passed time */
    timeout_ms -= has_passed;

    if(timeout_ms < 0) {
      /* a precaution, no need to continue if time already is up */
      failf(data, "SSL connection timeout");
      return CURLE_OPERATION_TIMEOUTED;
    }

    readfd = CURL_SOCKET_BAD;
    writefd = CURL_SOCKET_BAD;

    err = SSL_connect(connssl->handle);

    /* 1  is fine
       0  is "not successful but was shut down controlled"
       <0 is "handshake was not successful, because a fatal error occurred" */
    if(1 != err) {
      int detail = SSL_get_error(connssl->handle, err);

      if(SSL_ERROR_WANT_READ == detail)
        readfd = sockfd;
      else if(SSL_ERROR_WANT_WRITE == detail)
        writefd = sockfd;
      else {
        /* untreated error */
        unsigned long errdetail;
        char error_buffer[120]; /* OpenSSL documents that this must be at least
                                   120 bytes long. */
        CURLcode rc;
        const char *cert_problem = NULL;

        errdetail = ERR_get_error(); /* Gets the earliest error code from the
                                        thread's error queue and removes the
                                        entry. */

        switch(errdetail) {
        case 0x1407E086:
          /* 1407E086:
             SSL routines:
             SSL2_SET_CERTIFICATE:
             certificate verify failed */
          /* fall-through */
        case 0x14090086:
          /* 14090086:
             SSL routines:
             SSL3_GET_SERVER_CERTIFICATE:
             certificate verify failed */
          cert_problem = "SSL certificate problem, verify that the CA cert is"
                         " OK. Details:\n";
          rc = CURLE_SSL_CACERT;
          break;
        default:
          rc = CURLE_SSL_CONNECT_ERROR;
          break;
        }

          /* detail is already set to the SSL error above */

        /* If we e.g. use SSLv2 request-method and the server doesn't like us
         * (RST connection etc.), OpenSSL gives no explanation whatsoever and
         * the SO_ERROR is also lost.
         */
        if (CURLE_SSL_CONNECT_ERROR == rc && errdetail == 0) {
          failf(data, "Unknown SSL protocol error in connection to %s:%d ",
                conn->host.name, conn->port);
          return rc;
        }
        /* Could be a CERT problem */

        SSL_strerror(errdetail, error_buffer, sizeof(error_buffer));
        failf(data, "%s%s", cert_problem ? cert_problem : "", error_buffer);
        return rc;
      }
    }
    else
      /* we have been connected fine, get out of the connect loop */
      break;

    while(1) {
      what = Curl_select(readfd, writefd, (int)timeout_ms);
      if(what > 0)
        /* reabable or writable, go loop in the outer loop */
        break;
      else if(0 == what) {
        /* timeout */
        failf(data, "SSL connection timeout");
        return CURLE_OPERATION_TIMEDOUT;
      }
      else {
        /* anything that gets here is fatally bad */
        failf(data, "select on SSL socket, errno: %d", Curl_ourerrno());
        return CURLE_SSL_CONNECT_ERROR;
      }
    } /* while()-loop for the select() */
  } /* while()-loop for the SSL_connect() */

  /* Informational message */
  infof (data, "SSL connection using %s\n",
         SSL_get_cipher(connssl->handle));

  if(!ssl_sessionid) {
    /* Since this is not a cached session ID, then we want to stach this one
       in the cache! */
    SSL_SESSION *ssl_sessionid;
#ifdef HAVE_SSL_GET1_SESSION
    ssl_sessionid = SSL_get1_session(connssl->handle);

    /* SSL_get1_session() will increment the reference
       count and the session will stay in memory until explicitly freed with
       SSL_SESSION_free(3), regardless of its state.
       This function was introduced in openssl 0.9.5a. */
#else
    ssl_sessionid = SSL_get_session(connssl->handle);

    /* if SSL_get1_session() is unavailable, use SSL_get_session().
       This is an inferior option because the session can be flushed
       at any time by openssl. It is included only so curl compiles
       under versions of openssl < 0.9.5a.

       WARNING: How curl behaves if it's session is flushed is
       untested.
    */
#endif
    retcode = Curl_ssl_addsessionid(conn, ssl_sessionid,
                                    0 /* unknown size */);
    if(retcode) {
      failf(data, "failed to store ssl session");
      return retcode;
    }
  }


  /* Get server's certificate (note: beware of dynamic allocation) - opt */
  /* major serious hack alert -- we should check certificates
   * to authenticate the server; otherwise we risk man-in-the-middle
   * attack
   */

  connssl->server_cert = SSL_get_peer_certificate(connssl->handle);
  if(!connssl->server_cert) {
    failf(data, "SSL: couldn't get peer certificate!");
    return CURLE_SSL_PEER_CERTIFICATE;
  }
  infof (data, "Server certificate:\n");

  str = X509_NAME_oneline(X509_get_subject_name(connssl->server_cert),
                          NULL, 0);
  if(!str) {
    failf(data, "SSL: couldn't get X509-subject!");
    X509_free(connssl->server_cert);
    connssl->server_cert = NULL;
    return CURLE_SSL_CONNECT_ERROR;
  }
  infof(data, "\t subject: %s\n", str);
  CRYPTO_free(str);

  certdate = X509_get_notBefore(connssl->server_cert);
  Curl_ASN1_UTCTIME_output(conn, "\t start date: ", certdate);

  certdate = X509_get_notAfter(connssl->server_cert);
  Curl_ASN1_UTCTIME_output(conn, "\t expire date: ", certdate);

  if(data->set.ssl.verifyhost) {
    retcode = verifyhost(conn, connssl->server_cert);
    if(retcode) {
      X509_free(connssl->server_cert);
      connssl->server_cert = NULL;
      return retcode;
    }
  }

  str = X509_NAME_oneline(X509_get_issuer_name(connssl->server_cert),
                          NULL, 0);
  if(!str) {
    failf(data, "SSL: couldn't get X509-issuer name!");
    retcode = CURLE_SSL_CONNECT_ERROR;
  }
  else {
    infof(data, "\t issuer: %s\n", str);
    CRYPTO_free(str);

    /* We could do all sorts of certificate verification stuff here before
       deallocating the certificate. */

    lerr = data->set.ssl.certverifyresult=
      SSL_get_verify_result(connssl->handle);
    if(data->set.ssl.certverifyresult != X509_V_OK) {
      if(data->set.ssl.verifypeer) {
        /* We probably never reach this, because SSL_connect() will fail
           and we return earlyer if verifypeer is set? */
        failf(data, "SSL certificate verify result: %s (%ld)",
              X509_verify_cert_error_string(lerr), lerr);
        retcode = CURLE_SSL_PEER_CERTIFICATE;
      }
      else
        infof(data, "SSL certificate verify result: %s (%ld),"
              " continuing anyway.\n",
              X509_verify_cert_error_string(err), lerr);
    }
    else
      infof(data, "SSL certificate verify ok.\n");
  }

  X509_free(connssl->server_cert);
  connssl->server_cert = NULL;
  return retcode;
}
Exemple #5
0
CURLcode Curl_nss_connect(struct connectdata * conn, int sockindex)
{
  PRInt32 err;
  PRFileDesc *model = NULL;
  PRBool ssl2, ssl3, tlsv1;
  struct SessionHandle *data = conn->data;
  curl_socket_t sockfd = conn->sock[sockindex];
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
  SECStatus rv;
#ifdef HAVE_PK11_CREATEGENERICOBJECT
  char *configstring = NULL;
#endif
  char *certDir = NULL;
  int curlerr;

  curlerr = CURLE_SSL_CONNECT_ERROR;

  /* FIXME. NSS doesn't support multiple databases open at the same time. */
  if(!initialized) {
    initialized = 1;

    certDir = getenv("SSL_DIR"); /* Look in $SSL_DIR */

    if (!certDir) {
      struct stat st;

      if (stat(SSL_DIR, &st) == 0)
        if (S_ISDIR(st.st_mode)) {
          certDir = (char *)SSL_DIR;
        }
    }

    if(!certDir) {
      rv = NSS_NoDB_Init(NULL);
    }
    else {
      rv = NSS_Initialize(certDir, NULL, NULL, "secmod.db",
                          NSS_INIT_READONLY);
    }
    if(rv != SECSuccess) {
      infof(conn->data, "Unable to initialize NSS database\n");
      curlerr = CURLE_SSL_CACERT_BADFILE;
      goto error;
    }

    NSS_SetDomesticPolicy();

#ifdef HAVE_PK11_CREATEGENERICOBJECT
    configstring = (char *)malloc(PATH_MAX);

    PR_snprintf(configstring, PATH_MAX, "library=%s name=PEM", pem_library);

    mod = SECMOD_LoadUserModule(configstring, NULL, PR_FALSE);
    free(configstring);
    if (!mod || !mod->loaded) {
      if (mod) {
        SECMOD_DestroyModule(mod);
        mod = NULL;
      }
      infof(data, "WARNING: failed to load NSS PEM library %s. Using OpenSSL "
            "PEM certificates will not work.\n", pem_library);
    }
#endif
  }

  model = PR_NewTCPSocket();
  if(!model)
    goto error;
  model = SSL_ImportFD(NULL, model);

  if(SSL_OptionSet(model, SSL_SECURITY, PR_TRUE) != SECSuccess)
    goto error;
  if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != SECSuccess)
    goto error;
  if(SSL_OptionSet(model, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != SECSuccess)
    goto error;

  ssl2 = ssl3 = tlsv1 = PR_FALSE;

  switch (data->set.ssl.version) {
  default:
  case CURL_SSLVERSION_DEFAULT:
    ssl2 = ssl3 = tlsv1 = PR_TRUE;
    break;
  case CURL_SSLVERSION_TLSv1:
    tlsv1 = PR_TRUE;
    break;
  case CURL_SSLVERSION_SSLv2:
    ssl2 = PR_TRUE;
    break;
  case CURL_SSLVERSION_SSLv3:
    ssl3 = PR_TRUE;
    break;
  }

  if(SSL_OptionSet(model, SSL_ENABLE_SSL2, ssl2) != SECSuccess)
    goto error;
  if(SSL_OptionSet(model, SSL_ENABLE_SSL3, ssl3) != SECSuccess)
    goto error;
  if(SSL_OptionSet(model, SSL_ENABLE_TLS, tlsv1) != SECSuccess)
    goto error;

  if(data->set.ssl.cipher_list) {
    if(set_ciphers(data, model, data->set.ssl.cipher_list) != SECSuccess) {
      curlerr = CURLE_SSL_CIPHER;
      goto error;
    }
  }

  data->set.ssl.certverifyresult=0; /* not checked yet */
  if(SSL_BadCertHook(model, (SSLBadCertHandler) BadCertHandler, conn)
     != SECSuccess) {
    goto error;
  }
  if(SSL_HandshakeCallback(model, (SSLHandshakeCallback) HandshakeCallback,
                           NULL) != SECSuccess)
    goto error;

  if(!data->set.ssl.verifypeer)
    /* skip the verifying of the peer */
    ;
  else if (data->set.ssl.CAfile) {
    int rc = nss_load_cert(data->set.ssl.CAfile, PR_TRUE);
    if (!rc) {
      curlerr = CURLE_SSL_CACERT_BADFILE;
      goto error;
    }
  }
  else if (data->set.ssl.CApath) {
    struct stat st;
    PRDir      *dir;
    PRDirEntry *entry;

    if (stat(data->set.ssl.CApath, &st) == -1) {
      curlerr = CURLE_SSL_CACERT_BADFILE;
      goto error;
    }

    if (S_ISDIR(st.st_mode)) {
      int rc;

      dir = PR_OpenDir(data->set.ssl.CApath);
      do {
        entry = PR_ReadDir(dir, PR_SKIP_BOTH | PR_SKIP_HIDDEN);

        if (entry) {
          char fullpath[PATH_MAX];

          snprintf(fullpath, sizeof(fullpath), "%s/%s", data->set.ssl.CApath,
                   entry->name);
          rc = nss_load_cert(fullpath, PR_TRUE);
          /* FIXME: check this return value! */
        }
      /* This is purposefully tolerant of errors so non-PEM files
       * can be in the same directory */
      } while (entry != NULL);
      PR_CloseDir(dir);
    }
  }
  infof(data,
        "  CAfile: %s\n"
        "  CApath: %s\n",
        data->set.ssl.CAfile ? data->set.ssl.CAfile : "none",
        data->set.ssl.CApath ? data->set.ssl.CApath : "none");

  if(data->set.str[STRING_CERT]) {
    char *n;
    char *nickname;

    nickname = (char *)malloc(PATH_MAX);
    if(is_file(data->set.str[STRING_CERT])) {
      n = strrchr(data->set.str[STRING_CERT], '/');
      if (n) {
        n++; /* skip last slash */
        snprintf(nickname, PATH_MAX, "PEM Token #%ld:%s", 1, n);
      }
    }
    else {
      strncpy(nickname, data->set.str[STRING_CERT], PATH_MAX);
    }
    if(nss_Init_Tokens(conn) != SECSuccess) {
      free(nickname);
      goto error;
    }
    if (!cert_stuff(conn, data->set.str[STRING_CERT],
                    data->set.str[STRING_KEY])) {
      /* failf() is already done in cert_stuff() */
      free(nickname);
      return CURLE_SSL_CERTPROBLEM;
    }

    connssl->client_nickname = strdup(nickname);
    if(SSL_GetClientAuthDataHook(model,
                                 (SSLGetClientAuthData) SelectClientCert,
                                 (void *)connssl->client_nickname) !=
       SECSuccess) {
      curlerr = CURLE_SSL_CERTPROBLEM;
      goto error;
    }

    free(nickname);

    PK11_SetPasswordFunc(nss_no_password);
  }
  else
    connssl->client_nickname = NULL;

  /* Import our model socket  onto the existing file descriptor */
  connssl->handle = PR_ImportTCPSocket(sockfd);
  connssl->handle = SSL_ImportFD(model, connssl->handle);
  if(!connssl->handle)
    goto error;
  PR_Close(model); /* We don't need this any more */

  /* Force handshake on next I/O */
  SSL_ResetHandshake(connssl->handle, /* asServer */ PR_FALSE);

  SSL_SetURL(connssl->handle, conn->host.name);

  /* Force the handshake now */
  if (SSL_ForceHandshakeWithTimeout(connssl->handle,
                                    PR_SecondsToInterval(HANDSHAKE_TIMEOUT))
      != SECSuccess) {
    if (conn->data->set.ssl.certverifyresult!=0)
      curlerr = CURLE_SSL_CACERT;
    goto error;
  }

  display_conn_info(conn, connssl->handle);

  return CURLE_OK;

error:
  err = PR_GetError();
  infof(data, "NSS error %d\n", err);
  if(model)
    PR_Close(model);
  return curlerr;
}