Skip to content

Commit

Permalink
NSS: support for CERTINFO feature
Browse files Browse the repository at this point in the history
  • Loading branch information
Patrick Monnerat committed Oct 30, 2013
1 parent 2bd72fa commit f6c335d
Show file tree
Hide file tree
Showing 7 changed files with 119 additions and 49 deletions.
6 changes: 3 additions & 3 deletions docs/libcurl/curl_easy_getinfo.3
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
.\" * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" *
.\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms
Expand Down Expand Up @@ -219,8 +219,8 @@ done. The struct reports how many certs it found and then you can extract info
for each of those certs by following the linked lists. The info chain is
provided in a series of data in the format "name:content" where the content is
for the specific named data. See also the certinfo.c example. NOTE: this
option is only available in libcurl built with OpenSSL support. (Added in
7.19.1)
option is only available in libcurl built with OpenSSL, NSS, GSKit or QsoSSL
support. (Added in 7.19.1)
.IP CURLINFO_CONDITION_UNMET
Pass a pointer to a long to receive the number 1 if the condition provided in
the previous request didn't match (see \fICURLOPT_TIMECONDITION\fP). Alas, if
Expand Down
5 changes: 3 additions & 2 deletions docs/libcurl/curl_easy_setopt.3
Original file line number Diff line number Diff line change
Expand Up @@ -2549,9 +2549,10 @@ is ignored.

.IP CURLOPT_CERTINFO
Pass a long set to 1 to enable libcurl's certificate chain info gatherer. With
this enabled, libcurl (if built with OpenSSL) will extract lots of information
this enabled, libcurl (if built with OpenSSL, NSS, GSKit or QsoSSL) will
extract lots of information
and data about the certificates in the certificate chain used in the SSL
connection. This data is then possible to extract after a transfer using
connection. This data may then be retrieved after a transfer using
\fIcurl_easy_getinfo(3)\fP and its option \fICURLINFO_CERTINFO\fP. (Added in
7.19.1)
.IP CURLOPT_RANDOM_FILE
Expand Down
4 changes: 2 additions & 2 deletions lib/hostcheck.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
#include "curl_setup.h"

#if defined(USE_SSLEAY) || defined(USE_AXTLS) || defined(USE_QSOSSL) || \
defined(USE_GSKIT)
defined(USE_GSKIT) || defined(USE_NSS)
/* these backends use functions from this file */

#include "hostcheck.h"
Expand Down Expand Up @@ -94,4 +94,4 @@ int Curl_cert_hostcheck(const char *match_pattern, const char *hostname)
return 0;
}

#endif /* SSLEAY or AXTLS or QSOSSL or GSKIT */
#endif /* SSLEAY or AXTLS or QSOSSL or GSKIT or NSS */
46 changes: 42 additions & 4 deletions lib/nss.c
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,10 @@ static void display_conn_info(struct connectdata *conn, PRFileDesc *sock)
SSLChannelInfo channel;
SSLCipherSuiteInfo suite;
CERTCertificate *cert;
CERTCertificate *cert2;
CERTCertificate *cert3;
PRTime now;
int i;

if(SSL_GetChannelInfo(sock, &channel, sizeof channel) ==
SECSuccess && channel.length == sizeof channel &&
Expand All @@ -663,11 +667,45 @@ static void display_conn_info(struct connectdata *conn, PRFileDesc *sock)
}
}

infof(conn->data, "Server certificate:\n");

cert = SSL_PeerCertificate(sock);
display_cert_info(conn->data, cert);
CERT_DestroyCertificate(cert);

if(cert) {
infof(conn->data, "Server certificate:\n");

if(!conn->data->set.ssl.certinfo) {
display_cert_info(conn->data, cert);
CERT_DestroyCertificate(cert);
}
else {
/* Count certificates in chain. */
now = PR_Now();
i = 1;
if(!cert->isRoot) {
cert2 = CERT_FindCertIssuer(cert, now, certUsageSSLCA);
while(cert2) {
i++;
if(cert2->isRoot) {
CERT_DestroyCertificate(cert2);
break;
}
cert3 = CERT_FindCertIssuer(cert2, now, certUsageSSLCA);
CERT_DestroyCertificate(cert2);
cert2 = cert3;
}
}
Curl_ssl_init_certinfo(conn->data, i);
for(i = 0; cert; cert = cert2) {
Curl_extract_certinfo(conn, i++, cert->derCert.data,
cert->derCert.data + cert->derCert.len);
if(cert->isRoot) {
CERT_DestroyCertificate(cert);
break;
}
cert2 = CERT_FindCertIssuer(cert, now, certUsageSSLCA);
CERT_DestroyCertificate(cert);
}
}
}

return;
}
Expand Down
3 changes: 2 additions & 1 deletion lib/url.c
Original file line number Diff line number Diff line change
Expand Up @@ -1926,7 +1926,8 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
data->set.ssl.fsslctxp = va_arg(param, void *);
break;
#endif
#if defined(USE_SSLEAY) || defined(USE_QSOSSL) || defined(USE_GSKIT)
#if defined(USE_SSLEAY) || defined(USE_QSOSSL) || defined(USE_GSKIT) || \
defined(USE_NSS)
case CURLOPT_CERTINFO:
data->set.ssl.certinfo = (0 != va_arg(param, long))?TRUE:FALSE;
break;
Expand Down
100 changes: 65 additions & 35 deletions lib/x509asn1.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

#include "curl_setup.h"

#if defined(USE_QSOSSL) || defined(USE_GSKIT)
#if defined(USE_QSOSSL) || defined(USE_GSKIT) || defined(USE_NSS)

#include <curl/curl.h>
#include "urldata.h"
Expand Down Expand Up @@ -803,7 +803,7 @@ static const char * dumpAlgo(curl_asn1Element * param,
return OID2str(oid.beg, oid.end, TRUE);
}

static void do_pubkey_field(struct SessionHandle *data, int certnum,
static void do_pubkey_field(struct SessionHandle * data, int certnum,
const char * label, curl_asn1Element * elem)
{
const char * output;
Expand All @@ -812,8 +812,10 @@ static void do_pubkey_field(struct SessionHandle *data, int certnum,

output = Curl_ASN1tostr(elem, 0);
if(output) {
Curl_ssl_push_certinfo(data, certnum, label, output);
infof(data, " %s: %s\n", label, output);
if(data->set.ssl.certinfo)
Curl_ssl_push_certinfo(data, certnum, label, output);
if(!certnum)
infof(data, " %s: %s\n", label, output);
free((char *) output);
}
}
Expand Down Expand Up @@ -845,11 +847,14 @@ static void do_pubkey(struct SessionHandle * data, int certnum,
len--;
if(len > 32)
elem.beg = q; /* Strip leading zero bytes. */
infof(data, " RSA Public Key (%lu bits)\n", len);
q = curl_maprintf("%lu", len);
if(q) {
Curl_ssl_push_certinfo(data, certnum, "RSA Public Key", q);
free((char *) q);
if(!certnum)
infof(data, " RSA Public Key (%lu bits)\n", len);
if(data->set.ssl.certinfo) {
q = curl_maprintf("%lu", len);
if(q) {
Curl_ssl_push_certinfo(data, certnum, "RSA Public Key", q);
free((char *) q);
}
}
/* Generate coefficients. */
do_pubkey_field(data, certnum, "rsa(n)", &elem);
Expand Down Expand Up @@ -896,6 +901,10 @@ CURLcode Curl_extract_certinfo(struct connectdata * conn,
size_t i;
size_t j;

if(!data->set.ssl.certinfo)
if(certnum)
return CURLE_OK;

/* Prepare the certificate information for curl_easy_getinfo(). */

/* Extract the certificate ASN.1 elements. */
Expand All @@ -905,69 +914,86 @@ CURLcode Curl_extract_certinfo(struct connectdata * conn,
ccp = Curl_DNtostr(&cert.subject);
if(!ccp)
return CURLE_OUT_OF_MEMORY;
Curl_ssl_push_certinfo(data, certnum, "Subject", ccp);
infof(data, "%2d Subject: %s\n", certnum, ccp);
if(data->set.ssl.certinfo)
Curl_ssl_push_certinfo(data, certnum, "Subject", ccp);
if(!certnum)
infof(data, "%2d Subject: %s\n", certnum, ccp);
free((char *) ccp);

/* Issuer. */
ccp = Curl_DNtostr(&cert.issuer);
if(!ccp)
return CURLE_OUT_OF_MEMORY;
Curl_ssl_push_certinfo(data, certnum, "Issuer", ccp);
infof(data, " Issuer: %s\n", ccp);
if(data->set.ssl.certinfo)
Curl_ssl_push_certinfo(data, certnum, "Issuer", ccp);
if(!certnum)
infof(data, " Issuer: %s\n", ccp);
free((char *) ccp);

/* Version (always fits in less than 32 bits). */
version = 0;
for(ccp = cert.version.beg; ccp < cert.version.end; ccp++)
version = (version << 8) | *(const unsigned char *) ccp;
ccp = curl_maprintf("%lx", version);
if(!ccp)
return CURLE_OUT_OF_MEMORY;
Curl_ssl_push_certinfo(data, certnum, "Version", ccp);
free((char *) ccp);
infof(data, " Version: %lu (0x%lx)\n", version + 1, version);
if(data->set.ssl.certinfo) {
ccp = curl_maprintf("%lx", version);
if(!ccp)
return CURLE_OUT_OF_MEMORY;
Curl_ssl_push_certinfo(data, certnum, "Version", ccp);
free((char *) ccp);
}
if(!certnum)
infof(data, " Version: %lu (0x%lx)\n", version + 1, version);

/* Serial number. */
ccp = Curl_ASN1tostr(&cert.serialNumber, 0);
if(!ccp)
return CURLE_OUT_OF_MEMORY;
Curl_ssl_push_certinfo(data, certnum, "Serial Number", ccp);
infof(data, " Serial Number: %s\n", ccp);
if(data->set.ssl.certinfo)
Curl_ssl_push_certinfo(data, certnum, "Serial Number", ccp);
if(!certnum)
infof(data, " Serial Number: %s\n", ccp);
free((char *) ccp);

/* Signature algorithm .*/
ccp = dumpAlgo(&param, cert.signatureAlgorithm.beg,
cert.signatureAlgorithm.end);
if(!ccp)
return CURLE_OUT_OF_MEMORY;
Curl_ssl_push_certinfo(data, certnum, "Signature Algorithm", ccp);
infof(data, " Signature Algorithm: %s\n", ccp);
if(data->set.ssl.certinfo)
Curl_ssl_push_certinfo(data, certnum, "Signature Algorithm", ccp);
if(!certnum)
infof(data, " Signature Algorithm: %s\n", ccp);
free((char *) ccp);

/* Start Date. */
ccp = Curl_ASN1tostr(&cert.notBefore, 0);
if(!ccp)
return CURLE_OUT_OF_MEMORY;
Curl_ssl_push_certinfo(data, certnum, "Start Date", ccp);
infof(data, " Start Date: %s\n", ccp);
if(data->set.ssl.certinfo)
Curl_ssl_push_certinfo(data, certnum, "Start Date", ccp);
if(!certnum)
infof(data, " Start Date: %s\n", ccp);
free((char *) ccp);

/* Expire Date. */
ccp = Curl_ASN1tostr(&cert.notAfter, 0);
if(!ccp)
return CURLE_OUT_OF_MEMORY;
Curl_ssl_push_certinfo(data, certnum, "Expire Date", ccp);
infof(data, " Expire Date: %s\n", ccp);
if(data->set.ssl.certinfo)
Curl_ssl_push_certinfo(data, certnum, "Expire Date", ccp);
if(!certnum)
infof(data, " Expire Date: %s\n", ccp);
free((char *) ccp);

/* Public Key Algorithm. */
ccp = dumpAlgo(&param, cert.subjectPublicKeyAlgorithm.beg,
cert.subjectPublicKeyAlgorithm.end);
if(!ccp)
return CURLE_OUT_OF_MEMORY;
Curl_ssl_push_certinfo(data, certnum, "Public Key Algorithm", ccp);
infof(data, " Public Key Algorithm: %s\n", ccp);
if(data->set.ssl.certinfo)
Curl_ssl_push_certinfo(data, certnum, "Public Key Algorithm", ccp);
if(!certnum)
infof(data, " Public Key Algorithm: %s\n", ccp);
do_pubkey(data, certnum, ccp, &param, &cert.subjectPublicKey);
free((char *) ccp);

Expand All @@ -977,8 +1003,10 @@ CURLcode Curl_extract_certinfo(struct connectdata * conn,
ccp = Curl_ASN1tostr(&cert.signature, 0);
if(!ccp)
return CURLE_OUT_OF_MEMORY;
Curl_ssl_push_certinfo(data, certnum, "Signature", ccp);
infof(data, " Signature: %s\n", ccp);
if(data->set.ssl.certinfo)
Curl_ssl_push_certinfo(data, certnum, "Signature", ccp);
if(!certnum)
infof(data, " Signature: %s\n", ccp);
free((char *) ccp);

/* Generate PEM certificate. */
Expand All @@ -987,7 +1015,7 @@ CURLcode Curl_extract_certinfo(struct connectdata * conn,
&cp1, &cl1);
if(cc != CURLE_OK)
return cc;
/* Compute the number of charaters in final certificate string. Format is:
/* Compute the number of characters in final certificate string. Format is:
-----BEGIN CERTIFICATE-----\n
<max 64 base64 characters>\n
.
Expand All @@ -1008,8 +1036,10 @@ CURLcode Curl_extract_certinfo(struct connectdata * conn,
i += copySubstring(cp2 + i, "-----END CERTIFICATE-----");
cp2[i] = '\0';
free(cp1);
Curl_ssl_push_certinfo(data, certnum, "Cert", cp2);
infof(data, "%s\n", cp2);
if(data->set.ssl.certinfo)
Curl_ssl_push_certinfo(data, certnum, "Cert", cp2);
if(!certnum)
infof(data, "%s\n", cp2);
free(cp2);
return CURLE_OK;
}
Expand Down Expand Up @@ -1148,4 +1178,4 @@ CURLcode Curl_verifyhost(struct connectdata * conn,
return CURLE_PEER_FAILED_VERIFICATION;
}

#endif /* USE_QSOSSL or USE_GSKIT */
#endif /* USE_QSOSSL or USE_GSKIT or USE_NSS */
4 changes: 2 additions & 2 deletions lib/x509asn1.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

#include "curl_setup.h"

#if defined(USE_QSOSSL) || defined(USE_GSKIT)
#if defined(USE_QSOSSL) || defined(USE_GSKIT) || defined(USE_NSS)

#include "urldata.h"

Expand Down Expand Up @@ -125,5 +125,5 @@ CURLcode Curl_extract_certinfo(struct connectdata * conn, int certnum,
CURLcode Curl_verifyhost(struct connectdata * conn,
const char * beg, const char * end);

#endif /* USE_QSOSSL or USE_GSKIT */
#endif /* USE_QSOSSL or USE_GSKIT or USE_NSS */
#endif /* HEADER_CURL_X509ASN1_H */

0 comments on commit f6c335d

Please sign in to comment.