diff --git a/docs/libcurl/curl_easy_getinfo.3 b/docs/libcurl/curl_easy_getinfo.3 index 62d8ae482..db0f4d62a 100644 --- a/docs/libcurl/curl_easy_getinfo.3 +++ b/docs/libcurl/curl_easy_getinfo.3 @@ -5,7 +5,7 @@ .\" * | (__| |_| | _ <| |___ .\" * \___|\___/|_| \_\_____| .\" * -.\" * Copyright (C) 1998 - 2012, Daniel Stenberg, , et al. +.\" * Copyright (C) 1998 - 2013, Daniel Stenberg, , et al. .\" * .\" * This software is licensed as described in the file COPYING, which .\" * you should have received as part of this distribution. The terms @@ -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 diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3 index f58c8fbfe..2887483af 100644 --- a/docs/libcurl/curl_easy_setopt.3 +++ b/docs/libcurl/curl_easy_setopt.3 @@ -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 diff --git a/lib/hostcheck.c b/lib/hostcheck.c index abd1fa0c3..4be5baa92 100644 --- a/lib/hostcheck.c +++ b/lib/hostcheck.c @@ -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" @@ -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 */ diff --git a/lib/nss.c b/lib/nss.c index 43576e6d5..2562fcfdb 100644 --- a/lib/nss.c +++ b/lib/nss.c @@ -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 && @@ -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; } diff --git a/lib/url.c b/lib/url.c index e86fbc252..03c76078b 100644 --- a/lib/url.c +++ b/lib/url.c @@ -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; diff --git a/lib/x509asn1.c b/lib/x509asn1.c index 94b89b2be..d6aa04596 100644 --- a/lib/x509asn1.c +++ b/lib/x509asn1.c @@ -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 #include "urldata.h" @@ -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; @@ -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); } } @@ -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); @@ -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. */ @@ -905,35 +914,44 @@ 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 .*/ @@ -941,24 +959,30 @@ CURLcode Curl_extract_certinfo(struct connectdata * conn, 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. */ @@ -966,8 +990,10 @@ CURLcode Curl_extract_certinfo(struct connectdata * conn, 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, ¶m, &cert.subjectPublicKey); free((char *) ccp); @@ -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. */ @@ -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 \n . @@ -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; } @@ -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 */ diff --git a/lib/x509asn1.h b/lib/x509asn1.h index 2276b5b64..1741d6dca 100644 --- a/lib/x509asn1.h +++ b/lib/x509asn1.h @@ -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" @@ -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 */