Skip to content
nss.c 38.9 KiB
Newer Older
  struct ssl_connect_data *connssl = &conn->ssl[sockindex];
  SECStatus rv;
  PRSocketOptionData sock_opt;
  PRUint32 timeout;
  if (connssl->state == ssl_connection_complete)
    return CURLE_OK;

  connssl->cacert[0] = NULL;
  connssl->cacert[1] = NULL;
  connssl->key = NULL;
  /* FIXME. NSS doesn't support multiple databases open at the same time. */
    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))) {
    /* Now we check if the default location is a valid dir */
         (S_ISDIR(st.st_mode))) {
    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 {
                                     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();
    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);

  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) != SECSuccess)
    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;

  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;

  /* 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(set_ciphers(data, model, data->set.ssl.cipher_list) != SECSuccess) {
      curlerr = CURLE_SSL_CIPHER;
  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) {
  if(SSL_HandshakeCallback(model, (SSLHandshakeCallback) HandshakeCallback,
                           NULL) != SECSuccess)
    goto error;

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


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

          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! */
Daniel Stenberg's avatar
Daniel Stenberg committed
        /* This is purposefully tolerant of errors so non-PEM files
         * can be in the same directory */
      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");
  }

    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() */
    /* 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;

  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 */
  timeout = PR_MillisecondsToInterval((PRUint32)Curl_timeleft(conn, NULL, TRUE));
  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)
    goto error;
  display_conn_info(conn, connssl->handle);
  if (data->set.str[STRING_SSL_ISSUERCERT]) {
    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)

    if(SECFailure == ret) {
      infof(data,"SSL certificate issuer check failed\n");
      infof(data, "SSL certificate issuer check ok\n");
  /* reset the flag to avoid an infinite loop */
  data->state.ssl_connect_retry = FALSE;

  infof(data, "NSS error %d\n", err);

  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;
  }

/* for documentation see Curl_ssl_send() in sslgen.h */
int Curl_nss_send(struct connectdata *conn,  /* connection data */
                  int sockindex,             /* socketindex */
                  const void *mem,           /* send this data */
                  size_t len,                /* amount to write */
                  int *curlcode)
  rc = PR_Send(conn->ssl[sockindex].handle, mem, (int)len, 0, -1);
    PRInt32 err = PR_GetError();
    if(err == PR_WOULD_BLOCK_ERROR)
      *curlcode = -1; /* EWOULDBLOCK */
    else {
      failf(conn->data, "SSL write: error %d", err);
      *curlcode = CURLE_SEND_ERROR;
    }
/* for documentation see Curl_ssl_recv() in sslgen.h */
ssize_t Curl_nss_recv(struct connectdata * conn, /* connection data */
                      int num,                   /* socketindex */
                      char *buf,                 /* store read data here */
                      size_t buffersize,         /* max amount to read */
  nread = PR_Recv(conn->ssl[num].handle, buf, (int)buffersize, 0, -1);
  if(nread < 0) {
    /* failed SSL read */
    PRInt32 err = PR_GetError();

    if(err == PR_WOULD_BLOCK_ERROR)
      *curlcode = -1; /* EWOULDBLOCK */
    else {
      failf(conn->data, "SSL read: errno %d", err);
      *curlcode = CURLE_RECV_ERROR;
    }
    return -1;
  }
  return nread;
}

size_t Curl_nss_version(char *buffer, size_t size)
{
  return snprintf(buffer, size, "NSS/%s", NSS_VERSION);