Commit 06e6755e authored by Kamil Dudka's avatar Kamil Dudka
Browse files

nss: big cleanup in nss_load_cert() and cert_stuff()

parent 052a08ff
Loading
Loading
Loading
Loading
+58 −93
Original line number Diff line number Diff line
@@ -319,6 +319,9 @@ static CURLcode nss_create_object(struct ssl_connect_data *ssl,
  CK_BBOOL ckfalse = CK_FALSE;
  CK_ATTRIBUTE attrs[/* max count of attributes */ 4];
  int attr_cnt = 0;
  CURLcode err = (cacert)
    ? CURLE_SSL_CACERT_BADFILE
    : CURLE_SSL_CERTPROBLEM;

  const int slot_id = (cacert) ? 0 : 1;
  char *slot_name = aprintf("PEM Token #%d", slot_id);
@@ -328,7 +331,7 @@ static CURLcode nss_create_object(struct ssl_connect_data *ssl,
  slot = PK11_FindSlotByName(slot_name);
  free(slot_name);
  if(!slot)
    return CURLE_SSL_CERTPROBLEM;
    return err;

  PK11_SETATTRS(attrs, attr_cnt, CKA_CLASS, &obj_class, sizeof(obj_class));
  PK11_SETATTRS(attrs, attr_cnt, CKA_TOKEN, &cktrue, sizeof(CK_BBOOL));
@@ -343,7 +346,7 @@ static CURLcode nss_create_object(struct ssl_connect_data *ssl,
  obj = PK11_CreateGenericObject(slot, attrs, attr_cnt, PR_FALSE);
  PK11_FreeSlot(slot);
  if(!obj)
    return CURLE_SSL_CERTPROBLEM;
    return err;

  if(!Curl_llist_insert_next(ssl->obj_list, ssl->obj_list->tail, obj)) {
    PK11_DestroyGenericObject(obj);
@@ -368,77 +371,21 @@ static void nss_destroy_object(void *user, void *ptr)
}
#endif

static int nss_load_cert(struct ssl_connect_data *ssl,
static CURLcode nss_load_cert(struct ssl_connect_data *ssl,
                              const char *filename, PRBool cacert)
{
#ifdef HAVE_PK11_CREATEGENERICOBJECT
  /* All CA and trust objects go into slot 0. Other slots are used
   * for storing certificates.
   */
  const int slot_id = (cacert) ? 0 : 1;
#endif
  CERTCertificate *cert;
  char *nickname = NULL;
  char *n = NULL;

  /* If there is no slash in the filename it is assumed to be a regular
   * NSS nickname.
   */
  if(is_file(filename)) {
    n = strrchr(filename, '/');
    if(n)
      n++;
    if(!mod)
      return 1;
  }
  else {
    /* A nickname from the NSS internal database */
    if(cacert)
      return 0; /* You can't specify an NSS CA nickname this way */
    nickname = strdup(filename);
    if(!nickname)
      return 0;

    goto done;
  }
  CURLcode err = (cacert)
    ? CURLE_SSL_CACERT_BADFILE
    : CURLE_SSL_CERTPROBLEM;

#ifdef HAVE_PK11_CREATEGENERICOBJECT
  nickname = aprintf("PEM Token #%d:%s", slot_id, n);
  if(!nickname)
    return 0;

  if(CURLE_OK != nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert)) {
    free(nickname);
    return 0;
  }

#else
  /* We don't have PK11_CreateGenericObject but a file-based cert was passed
   * in. We need to fail.
   */
  return 0;
  /* libnsspem.so leaks memory if the requested file does not exist.  For more
   * details, go to <https://bugzilla.redhat.com/734760>. */
  if(is_file(filename))
    return nss_create_object(ssl, CKO_CERTIFICATE, filename, cacert);
#endif

  done:
  /* Double-check that the certificate or nickname requested exists in
   * either the token or the NSS certificate database.
   */
  if(!cacert) {
    cert = PK11_FindCertFromNickname((char *)nickname, NULL);

    /* An invalid nickname was passed in */
    if(cert == NULL) {
      free(nickname);
      PR_SetError(SEC_ERROR_UNKNOWN_CERT, 0);
      return 0;
    }

    CERT_DestroyCertificate(cert);
  }

  free(nickname);

  return 1;
  return err;
}

/* add given CRL to cache if it is not already there */
@@ -527,23 +474,23 @@ fail:
  return SECFailure;
}

static int nss_load_key(struct connectdata *conn, int sockindex,
static CURLcode nss_load_key(struct connectdata *conn, int sockindex,
                             char *key_file)
{
#ifdef HAVE_PK11_CREATEGENERICOBJECT
  PK11SlotInfo *slot;
  SECStatus status;
  struct ssl_connect_data *ssl = conn->ssl;
  (void)sockindex; /* unused */

  if(CURLE_OK != nss_create_object(ssl, CKO_PRIVATE_KEY, key_file, FALSE)) {
  CURLcode rv = nss_create_object(ssl, CKO_PRIVATE_KEY, key_file, FALSE);
  if(CURLE_OK != rv) {
    PR_SetError(SEC_ERROR_BAD_KEY, 0);
    return 0;
    return rv;
  }

  slot = PK11_FindSlotByName("PEM Token #1");
  if(!slot)
    return 0;
    return CURLE_SSL_CERTPROBLEM;

  /* This will force the token to be seen as re-inserted */
  SECMOD_WaitForAnyTokenEvent(mod, 0, 0);
@@ -552,16 +499,18 @@ static int nss_load_key(struct connectdata *conn, int sockindex,
  status = PK11_Authenticate(slot, PR_TRUE,
                             conn->data->set.str[STRING_KEY_PASSWD]);
  PK11_FreeSlot(slot);
  return (SECSuccess == status) ? 1 : 0;
  return (SECSuccess == status)
    ? CURLE_OK
    : CURLE_SSL_CERTPROBLEM;
#else
  /* If we don't have PK11_CreateGenericObject then we can't load a file-based
   * key.
   */
  (void)conn; /* unused */
  (void)key_file; /* unused */
  (void)sockindex; /* unused */
  return 0;
  return CURLE_SSL_CERTPROBLEM;
#endif
  (void)sockindex; /* unused */
}

static int display_error(struct connectdata *conn, PRInt32 err,
@@ -580,34 +529,37 @@ static int display_error(struct connectdata *conn, PRInt32 err,
  return 0; /* The caller will print a generic error */
}

static int cert_stuff(struct connectdata *conn,
                      int sockindex, char *cert_file, char *key_file)
static CURLcode cert_stuff(struct connectdata *conn, int sockindex,
                           char *cert_file, char *key_file)
{
  struct SessionHandle *data = conn->data;
  int rv = 0;
  CURLcode rv;

  if(cert_file) {
    rv = nss_load_cert(&conn->ssl[sockindex], cert_file, PR_FALSE);
    if(!rv) {
    if(CURLE_OK != rv) {
      if(!display_error(conn, PR_GetError(), cert_file))
        failf(data, "Unable to load client cert %d.", PR_GetError());
      return 0;

      return rv;
    }
  }

  if(key_file || (is_file(cert_file))) {
    if(key_file)
      rv = nss_load_key(conn, sockindex, key_file);
    else
      /* In case the cert file also has the key */
      rv = nss_load_key(conn, sockindex, cert_file);
    if(!rv) {
    if(CURLE_OK != rv) {
      if(!display_error(conn, PR_GetError(), key_file))
        failf(data, "Unable to load client key %d.", PR_GetError());

      return 0;
      return rv;
    }
  }
  return 1;

  return CURLE_OK;
}

static char * nss_get_password(PK11SlotInfo * slot, PRBool retry, void *arg)
@@ -774,7 +726,6 @@ static SECStatus check_issuer_cert(PRFileDesc *sock,
  cert_issuer = CERT_FindCertIssuer(cert,PR_Now(),certUsageObjectSigner);

  proto_win = SSL_RevealPinArg(sock);
  issuer = NULL;
  issuer = PK11_FindCertFromNickname(issuer_nickname, proto_win);

  if((!cert_issuer) || (!issuer))
@@ -1132,8 +1083,11 @@ static CURLcode nss_load_ca_certificates(struct connectdata *conn,
  const char *cafile = data->set.ssl.CAfile;
  const char *capath = data->set.ssl.CApath;

  if(cafile && !nss_load_cert(&conn->ssl[sockindex], cafile, PR_TRUE))
    return CURLE_SSL_CACERT_BADFILE;
  if(cafile) {
    CURLcode rv = nss_load_cert(&conn->ssl[sockindex], cafile, PR_TRUE);
    if(CURLE_OK != rv)
      return rv;
  }

  if(capath) {
    struct_stat st;
@@ -1153,7 +1107,7 @@ static CURLcode nss_load_ca_certificates(struct connectdata *conn,
          return CURLE_OUT_OF_MEMORY;
        }

        if(!nss_load_cert(&conn->ssl[sockindex], fullpath, PR_TRUE))
        if(CURLE_OK != nss_load_cert(&conn->ssl[sockindex], fullpath, PR_TRUE))
          /* This is purposefully tolerant of errors so non-PEM files can
           * be in the same directory */
          infof(data, "failed to load '%s' from CURLOPT_CAPATH\n", fullpath);
@@ -1349,10 +1303,21 @@ CURLcode Curl_nss_connect(struct connectdata *conn, int sockindex)

  if(data->set.str[STRING_CERT]) {
    char *nickname = dup_nickname(data, STRING_CERT);
    if(!nickname && !cert_stuff(conn, sockindex, data->set.str[STRING_CERT],
                                data->set.str[STRING_KEY]))
    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() */
      return CURLE_SSL_CERTPROBLEM;
        curlerr = rv;
        goto error;
      }
    }

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