diff --git a/CHANGES b/CHANGES index 7bcb6db7f..1908387ce 100644 --- a/CHANGES +++ b/CHANGES @@ -7,6 +7,18 @@ Changelog Daniel Stenberg (5 Sep 2008) +- Introducing CURLOPT_CERTINFO and the corresponding CURLINFO_CERTINFO. By + enabling this feature with CURLOPT_CERTINFO for a request using SSL (HTTPS + or FTPS), libcurl will gather lots of server certificate info and that info + can then get extracted by a client after the request has completed with + curl_easy_getinfo()'s CURLINFO_CERTINFO option. Linus Nielsen Feltzing + helped me test and smoothen out this feature. + + Unfortunately, this feature currently only works with libcurl built to use + OpenSSL. + + This feature was sponsored by networking4all.com - thanks! + - Dmitriy Sergeyev pointed out that curl_easy_pause() didn't unpause properly during certain conditions. I also changed this code to use realloc() based on Daniel Fandrich's suggestion. diff --git a/RELEASE-NOTES b/RELEASE-NOTES index b8ea1972b..c1f3bfcca 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -2,7 +2,7 @@ Curl and libcurl 7.19.1 Public curl releases: 107 Command line options: 127 - curl_easy_setopt() options: 153 + curl_easy_setopt() options: 154 Public functions in libcurl: 58 Known libcurl bindings: 36 Contributors: 672 @@ -10,6 +10,7 @@ Curl and libcurl 7.19.1 This release includes the following changes: o pkg-config can now show supported_protocols and supported_features + o Added CURLOPT_CERTINFO and CURLINFO_CERTINFO This release includes the following bugfixes: @@ -28,6 +29,7 @@ Other curl-related news: This release would not have looked like this without help, code, reports and advice from friends like these: - Keith Mok, Yang Tse, Daniel Fandrich, Guenter Knauf, Dmitriy Sergeyev + Keith Mok, Yang Tse, Daniel Fandrich, Guenter Knauf, Dmitriy Sergeyev, + Linus Nielsen Feltzing Thanks! (and sorry if I forgot to mention someone) diff --git a/TODO-RELEASE b/TODO-RELEASE index a3a0baf80..aa6cb55cc 100644 --- a/TODO-RELEASE +++ b/TODO-RELEASE @@ -1,9 +1,6 @@ To be addressed before 7.19.1 (planned release: October/November 2008) ============================= -157 - the CERTINFO patch as posted to: - http://curl.haxx.se/mail/lib-2008-08/0105.html - 158 - Martin Drasar's CURLOPT_POSTREDIR work: http://curl.haxx.se/mail/lib-2008-08/0170.html diff --git a/docs/examples/Makefile.inc b/docs/examples/Makefile.inc index 68b66c3e3..c3ee0228b 100644 --- a/docs/examples/Makefile.inc +++ b/docs/examples/Makefile.inc @@ -5,7 +5,7 @@ check_PROGRAMS = 10-at-a-time anyauthput cookie_interface \ https multi-app multi-debugcallback multi-double \ multi-post multi-single persistant post-callback \ postit2 sepheaders simple simplepost simplessl \ - sendrecv httpcustomheader + sendrecv httpcustomheader certinfo # These examples require external dependencies that may not be commonly # available on POSIX systems, so don't bother attempting to compile them here. @@ -14,4 +14,3 @@ COMPLICATED_EXAMPLES = \ ghiper.c hiperfifo.c htmltidy.c multithread.c \ opensslthreadlock.c sampleconv.c synctime.c threaded-ssl.c - diff --git a/docs/examples/certinfo.c b/docs/examples/certinfo.c new file mode 100644 index 000000000..b0e9759f4 --- /dev/null +++ b/docs/examples/certinfo.c @@ -0,0 +1,62 @@ +/***************************************************************************** + */ + +#include + +#include +#include +#include + +static size_t wrfu(void *ptr, size_t size, size_t nmemb, void *stream) +{ + return size * nmemb; +} +int main(int argc, char **argv) +{ + CURL *curl; + CURLcode res; + + curl_global_init(CURL_GLOBAL_DEFAULT); + + curl = curl_easy_init(); + if(curl) { + curl_easy_setopt(curl, CURLOPT_URL, "https://www.networking4all.com/"); + + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, wrfu); + + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); + curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); + + curl_easy_setopt(curl, CURLOPT_VERBOSE, 0L); + curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L); + + res = curl_easy_perform(curl); + + if(!res) { + struct curl_certinfo *ci = NULL; + + res = curl_easy_getinfo(curl, CURLINFO_CERTINFO, &ci); + + if(!res && ci) { + int i; + printf("%d certs!\n", ci->num_of_certs); + + for(i=0; inum_of_certs; i++) { + struct curl_slist *slist; + + for(slist = ci->certinfo[i]; slist; slist = slist->next) + printf("%s\n", slist->data); + + } + } + + } + + + curl_easy_cleanup(curl); + } + + curl_global_cleanup(); + + return 0; +} diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3 index 6059e38bf..e07776ccf 100644 --- a/docs/libcurl/curl_easy_setopt.3 +++ b/docs/libcurl/curl_easy_setopt.3 @@ -1496,6 +1496,13 @@ A specific error code (CURLE_SSL_CRL_BADFILE) is defined with the option. It is returned when the SSL exchange fails because the CRL file cannot be loaded. Note that a failure in certificate verification due to a revocation information found in the CRL does not trigger this specific error. (Added in 7.19.0) +.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 +and data about the certificate's in the certificate chain used in the SSL +connection. This data is then possible to extract after a transfer using +\fIcurl_easy_getinfo(3)\fP and its option \fICURLINFO_CERTINFO\fP. (Added in +7.19.0) .IP CURLOPT_RANDOM_FILE Pass a char * to a zero terminated file name. The file will be used to read from to seed the random engine for SSL. The more random the specified file is, diff --git a/include/curl/curl.h b/include/curl/curl.h index c9a763537..2b574c9d7 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -1135,6 +1135,11 @@ typedef enum { /* (IPv6) Address scope */ CINIT(ADDRESS_SCOPE, LONG, 171), + /* Collect certificate chain info and allow it to get retrievable with + CURLINFO_CERTINFO after the transfer is complete. (Unfortunately) only + working with OpenSSL-powered builds. */ + CINIT(CERTINFO, LONG, 172), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; @@ -1491,6 +1496,15 @@ CURL_EXTERN void curl_slist_free_all(struct curl_slist *); */ CURL_EXTERN time_t curl_getdate(const char *p, const time_t *unused); +/* info about the certificate chain, only for OpenSSL builds. Asked + for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */ +struct curl_certinfo { + int num_of_certs; /* number of certificates with information */ + struct curl_slist **certinfo; /* for each index in this array, there's a + linked list with textual information in the + format "name: value" */ +}; + #define CURLINFO_STRING 0x100000 #define CURLINFO_LONG 0x200000 #define CURLINFO_DOUBLE 0x300000 @@ -1533,9 +1547,10 @@ typedef enum { CURLINFO_REDIRECT_URL = CURLINFO_STRING + 31, CURLINFO_PRIMARY_IP = CURLINFO_STRING + 32, CURLINFO_APPCONNECT_TIME = CURLINFO_DOUBLE + 33, + CURLINFO_CERTINFO = CURLINFO_SLIST + 34, /* Fill in new entries below here! */ - CURLINFO_LASTONE = 33 + CURLINFO_LASTONE = 34 } CURLINFO; /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as diff --git a/lib/getinfo.c b/lib/getinfo.c index 2b7b08aee..6f8ebb87e 100644 --- a/lib/getinfo.c +++ b/lib/getinfo.c @@ -217,6 +217,11 @@ CURLcode Curl_getinfo(struct SessionHandle *data, CURLINFO info, ...) /* Return the ip address of the most recent (primary) connection */ *param_charp = data->info.ip; break; + case CURLINFO_CERTINFO: + /* Return the a pointer to the certinfo struct. Not really an slist + pointer but we can pretend it is here */ + *param_slistp = (struct curl_slist *)&data->info.certs; + break; default: return CURLE_BAD_FUNCTION_ARGUMENT; } diff --git a/lib/sendf.c b/lib/sendf.c index bba1bc726..9a1c157c0 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -227,9 +227,9 @@ void Curl_infof(struct SessionHandle *data, const char *fmt, ...) if(data && data->set.verbose) { va_list ap; size_t len; - char print_buffer[1024 + 1]; + char print_buffer[2048 + 1]; va_start(ap, fmt); - vsnprintf(print_buffer, 1024, fmt, ap); + vsnprintf(print_buffer, sizeof(print_buffer), fmt, ap); va_end(ap); len = strlen(print_buffer); Curl_debug(data, CURLINFO_TEXT, print_buffer, len, NULL); diff --git a/lib/sslgen.c b/lib/sslgen.c index 0001cd8d3..df24fbdd4 100644 --- a/lib/sslgen.c +++ b/lib/sslgen.c @@ -465,3 +465,15 @@ bool Curl_ssl_data_pending(const struct connectdata *conn, } #endif /* USE_SSL */ +void Curl_ssl_free_certinfo(struct SessionHandle *data) +{ + int i; + struct curl_certinfo *ci = &data->info.certs; + if(ci->num_of_certs) { + /* free all individual lists used */ + for(i=0; inum_of_certs; i++) + curl_slist_free_all(ci->certinfo[i]); + free(ci->certinfo); /* free the actual array too */ + ci->num_of_certs = 0; + } +} diff --git a/lib/sslgen.h b/lib/sslgen.h index f6eff057a..a32dfb82f 100644 --- a/lib/sslgen.h +++ b/lib/sslgen.h @@ -59,7 +59,7 @@ size_t Curl_ssl_version(char *buffer, size_t size); bool Curl_ssl_data_pending(const struct connectdata *conn, int connindex); int Curl_ssl_check_cxn(struct connectdata *conn); - +void Curl_ssl_free_certinfo(struct SessionHandle *data); #else /* When SSL support is not present, just define away these function calls */ #define Curl_ssl_init() 1 @@ -78,6 +78,7 @@ int Curl_ssl_check_cxn(struct connectdata *conn); #define Curl_ssl_version(x,y) 0 #define Curl_ssl_data_pending(x,y) 0 #define Curl_ssl_check_cxn(x) 0 +#define Curl_ssl_free_certinfo(x) #endif @@ -90,11 +91,6 @@ CURLcode Curl_ssl_addsessionid(struct connectdata *conn, void *ssl_sessionid, size_t idsize); -#if !defined(USE_SSL) && !defined(SSLGEN_C) -/* set up blank macros for none-SSL builds */ -#define Curl_ssl_close_all(x) -#endif - #define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */ #endif diff --git a/lib/ssluse.c b/lib/ssluse.c index cee78bbcc..cfa761e62 100644 --- a/lib/ssluse.c +++ b/lib/ssluse.c @@ -554,6 +554,33 @@ int cert_stuff(struct connectdata *conn, return(1); } +/* returns non-zero on failure */ +static int x509_name_oneline(X509_NAME *a, char *buf, int size) +{ +#if 0 + return X509_NAME_oneline(a, buf, size); +#else + BIO *bio_out = BIO_new(BIO_s_mem()); + BUF_MEM *biomem; + int rc; + + rc = X509_NAME_print_ex(bio_out, a, 0, XN_FLAG_SEP_CPLUS_SPC); + BIO_get_mem_ptr(bio_out, &biomem); + + if(biomem->length < size) + size = biomem->length; + else + size--; /* don't overwrite the buffer end */ + + memcpy(buf, biomem->data, size); + buf[size]=0; + + BIO_free(bio_out); + + return !rc; +#endif +} + static int cert_verify_callback(int ok, X509_STORE_CTX *ctx) { @@ -561,7 +588,7 @@ int cert_verify_callback(int ok, X509_STORE_CTX *ctx) char buf[256]; err_cert=X509_STORE_CTX_get_current_cert(ctx); - X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf)); + (void)x509_name_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf)); return ok; } @@ -886,23 +913,19 @@ int Curl_ossl_close_all(struct SessionHandle *data) return 0; } -static int asn1_output(struct connectdata *conn, - const char *prefix, - const ASN1_UTCTIME *tm) +static int asn1_output(const ASN1_UTCTIME *tm, + char *buf, + size_t sizeofbuf) { const char *asn1_string; int gmt=FALSE; int i; int year=0,month=0,day=0,hour=0,minute=0,second=0; - struct SessionHandle *data = conn->data; #ifdef CURL_DISABLE_VERBOSE_STRINGS (void)prefix; #endif - if(!data->set.verbose) - return 0; - i=tm->length; asn1_string=(const char *)tm->data; @@ -930,9 +953,9 @@ static int asn1_output(struct connectdata *conn, (asn1_string[11] >= '0') && (asn1_string[11] <= '9')) second= (asn1_string[10]-'0')*10+(asn1_string[11]-'0'); - infof(data, - "%s%04d-%02d-%02d %02d:%02d:%02d %s\n", - prefix, year+1900, month, day, hour, minute, second, (gmt?"GMT":"")); + snprintf(buf, sizeofbuf, + "%04d-%02d-%02d %02d:%02d:%02d %s", + year+1900, month, day, hour, minute, second, (gmt?"GMT":"")); return 0; } @@ -1619,6 +1642,364 @@ ossl_connect_step2(struct connectdata *conn, int sockindex) } } +static int asn1_object_dump(ASN1_OBJECT *a, char *buf, size_t len) +{ + int i = i2t_ASN1_OBJECT(buf, len, a); + if (i >= (int)len) + return 1; /* too small buffer! */ + return 0; +} + +static CURLcode push_certinfo_len(struct SessionHandle *data, + int certnum, + const char *label, + const char *value, + size_t valuelen) +{ + struct curl_certinfo *ci = &data->info.certs; + char *outp; + struct curl_slist *nl; + CURLcode res = CURLE_OK; + size_t labellen = strlen(label); + size_t outlen = labellen + 1 + valuelen + 1; /* label:value\0 */ + + outp = malloc(outlen); + if(!outp) + return CURLE_OUT_OF_MEMORY; + + /* sprintf the label and colon */ + snprintf(outp, outlen, "%s:", label); + + /* memcpy the value (it might not be zero terminated) */ + memcpy(&outp[labellen+1], value, valuelen); + + /* zero terminate the output */ + outp[labellen + 1 + valuelen] = 0; + + /* TODO: we should rather introduce an internal API that can do the + equivalent of curl_slist_append but doesn't strdup() the given data as + like in this place the extra malloc/free is totally pointless */ + nl = curl_slist_append(ci->certinfo[certnum], outp); + if(!nl) { + curl_slist_free_all(ci->certinfo[certnum]); + res = CURLE_OUT_OF_MEMORY; + } + else + ci->certinfo[certnum] = nl; + + free(outp); + + return res; +} + +/* this is a convenience function for push_certinfo_len that takes a zero + terminated value */ +static CURLcode push_certinfo(struct SessionHandle *data, + int certnum, + const char *label, + const char *value) +{ + size_t valuelen = strlen(value); + + return push_certinfo_len(data, certnum, label, value, valuelen); +} + +static void pubkey_show(struct SessionHandle *data, + int num, + const char *type, + const char *name, + unsigned char *raw, + int len) +{ + char buffer[1024]; + size_t left = sizeof(buffer); + int i; + char *ptr=buffer; + char namebuf[32]; + + snprintf(namebuf, sizeof(namebuf), "%s(%s)", type, name); + + for(i=0; i< len; i++) { + snprintf(ptr, left, "%02x:", raw[i]); + ptr += 3; + left -= 3; + } + infof(data, " %s: %s\n", namebuf, buffer); + push_certinfo(data, num, namebuf, buffer); +} + +#define print_pubkey_BN(_type, _name, _num) \ +do { \ + if (pubkey->pkey._type->_name != NULL) { \ + int len = BN_num_bytes(pubkey->pkey._type->_name); \ + if(len < (int)sizeof(buf)) { \ + BN_bn2bin(pubkey->pkey._type->_name, (unsigned char*)buf); \ + buf[len] = 0; \ + pubkey_show(data, _num, #_type, #_name, (unsigned char*)buf, len); \ + } \ + } \ +} while (0) + +static int X509V3_ext(struct SessionHandle *data, + int certnum, + STACK_OF(X509_EXTENSION) *exts) +{ + int i, j; + + if(sk_X509_EXTENSION_num(exts) <= 0) + /* no extensions, bail out */ + return 1; + + for (i=0; ivalue); + + BIO_get_mem_ptr(bio_out, &biomem); + + /* biomem->length bytes at biomem->data, this little loop here is only + done for the infof() call, we send the "raw" data to the certinfo + function */ + for(j=0; jlength; j++) { + const char *sep=""; + if(biomem->data[j] == '\n') { + sep=", "; + j++; /* skip the newline */ + }; + while((biomem->data[j] == ' ') && (jlength)) + j++; + if(jlength) + ptr+=snprintf(ptr, sizeof(buf)-(ptr-buf), "%s%c", sep, biomem->data[j]); + } + infof(data, " %s\n", buf); + + push_certinfo(data, certnum, namebuf, buf); + + BIO_free(bio_out); + + } + return 0; /* all is fine */ +} + + +static void X509_signature(struct SessionHandle *data, + int numcert, + ASN1_STRING *sig) +{ + char buf[1024]; + char *ptr = buf; + int i; + for (i=0; ilength; i++) + ptr+=snprintf(ptr, sizeof(buf)-(ptr-buf), "%02x:", sig->data[i]); + + infof(data, " Signature: %s\n", buf); + push_certinfo(data, numcert, "Signature", buf); +} + +static void dumpcert(struct SessionHandle *data, X509 *x, int numcert) +{ + BIO *bio_out = BIO_new(BIO_s_mem()); + BUF_MEM *biomem; + + /* this outputs the cert in this 64 column wide style with newlines and + -----BEGIN CERTIFICATE----- texts and more */ + PEM_write_bio_X509(bio_out, x); + + BIO_get_mem_ptr(bio_out, &biomem); + + infof(data, "%s\n", biomem->data); + + push_certinfo_len(data, numcert, "Cert", biomem->data, biomem->length); + + BIO_free(bio_out); + +} + + +static int init_certinfo(struct SessionHandle *data, + int num) +{ + struct curl_certinfo *ci = &data->info.certs; + struct curl_slist **table; + + Curl_ssl_free_certinfo(data); + + ci->num_of_certs = num; + table = calloc(sizeof(struct curl_slist *) * num, 1); + if(!table) + return 1; + + ci->certinfo = table; + return 0; +} + +static CURLcode get_cert_chain(struct connectdata *conn, + struct ssl_connect_data *connssl) + +{ + STACK_OF(X509) *sk; + int i; + char buf[512]; + struct SessionHandle *data = conn->data; + int numcerts; + + sk = SSL_get_peer_cert_chain(connssl->handle); + + if(!sk) + return CURLE_OUT_OF_MEMORY; + + numcerts = sk_X509_num(sk); + + if(init_certinfo(data, numcerts)) + return CURLE_OUT_OF_MEMORY; + + infof(data, "--- Certificate chain\n"); + for (i=0; ilength <= 4) { + value = ASN1_INTEGER_get(num); + infof(data," Serial Number: %ld (0x%lx)\n", value, value); + snprintf(buf, sizeof(buf), "%lx", value); + } + else { + + ptr = buf; + *ptr++ = 0; + if(num->type == V_ASN1_NEG_INTEGER) + *ptr++='-'; + + for (j=0; jlength; j++) { + /* TODO: length restrictions */ + snprintf(ptr, 3, "%02x%c",num->data[j], + ((j+1 == num->length)?'\n':':')); + ptr += 3; + } + if(num->length) + infof(data," Serial Number: %s\n", buf); + else + buf[0]=0; + } + if(buf[0]) + push_certinfo(data, i, "Serial Number", buf); /* hex */ + + cinf = x->cert_info; + + j = asn1_object_dump(cinf->signature->algorithm, buf, sizeof(buf)); + if(!j) { + infof(data, " Signature Algorithm: %s\n", buf); + push_certinfo(data, i, "Signature Algorithm", buf); + } + + certdate = X509_get_notBefore(x); + asn1_output(certdate, buf, sizeof(buf)); + infof(data, " Start date: %s\n", buf); + push_certinfo(data, i, "Start date", buf); + + certdate = X509_get_notAfter(x); + asn1_output(certdate, buf, sizeof(buf)); + infof(data, " Expire date: %s\n", buf); + push_certinfo(data, i, "Expire date", buf); + + j = asn1_object_dump(cinf->key->algor->algorithm, buf, sizeof(buf)); + if(!j) { + infof(data, " Public Key Algorithm: %s\n", buf); + push_certinfo(data, i, "Public Key Algorithm", buf); + } + + pubkey = X509_get_pubkey(x); + if(!pubkey) + infof(data, " Unable to load public key\n"); + else { + switch(pubkey->type) { + case EVP_PKEY_RSA: + infof(data, " RSA Public Key (%d bits)\n", + BN_num_bits(pubkey->pkey.rsa->n)); + snprintf(buf, sizeof(buf), "%d", BN_num_bits(pubkey->pkey.rsa->n)); + push_certinfo(data, i, "RSA Public Key", buf); + + print_pubkey_BN(rsa, n, i); + print_pubkey_BN(rsa, e, i); + print_pubkey_BN(rsa, d, i); + print_pubkey_BN(rsa, p, i); + print_pubkey_BN(rsa, q, i); + print_pubkey_BN(rsa, dmp1, i); + print_pubkey_BN(rsa, dmq1, i); + print_pubkey_BN(rsa, iqmp, i); + break; + case EVP_PKEY_DSA: + print_pubkey_BN(dsa, p, i); + print_pubkey_BN(dsa, q, i); + print_pubkey_BN(dsa, g, i); + print_pubkey_BN(dsa, priv_key, i); + print_pubkey_BN(dsa, pub_key, i); + break; + case EVP_PKEY_DH: + print_pubkey_BN(dh, p, i); + print_pubkey_BN(dh, g, i); + print_pubkey_BN(dh, priv_key, i); + print_pubkey_BN(dh, pub_key, i); + break; +#if 0 + case EVP_PKEY_EC: /* symbol not present in OpenSSL 0.9.6 */ + /* left TODO */ + break; +#endif + } + } + + X509V3_ext(data, i, cinf->extensions); + + X509_signature(data, i, x->signature); + + dumpcert(data, x, i); + } + + return CURLE_OK; +} + /* * Get the server cert, verify it and show it etc, only call failf() if the * 'strict' argument is TRUE as otherwise all this is for informational @@ -1632,12 +2013,17 @@ static CURLcode servercert(struct connectdata *conn, bool strict) { CURLcode retcode = CURLE_OK; - char *str; + int rc; long lerr; ASN1_TIME *certdate; struct SessionHandle *data = conn->data; X509 *issuer; FILE *fp; + char buffer[256]; + + if(data->set.ssl.certinfo) + /* we've been asked to gather certificate info! */ + (void)get_cert_chain(conn, connssl); data->set.ssl.certverifyresult = !X509_V_OK; @@ -1649,23 +2035,24 @@ static CURLcode servercert(struct connectdata *conn, } infof (data, "Server certificate:\n"); - str = X509_NAME_oneline(X509_get_subject_name(connssl->server_cert), - NULL, 0); - if(!str) { + rc = x509_name_oneline(X509_get_subject_name(connssl->server_cert), + buffer, sizeof(buffer)); + if(rc) { if(strict) 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); + infof(data, "\t subject: %s\n", buffer); certdate = X509_get_notBefore(connssl->server_cert); - asn1_output(conn, "\t start date: ", certdate); + asn1_output(certdate, buffer, sizeof(buffer)); + infof(data, "\t start date: %s\n", buffer); certdate = X509_get_notAfter(connssl->server_cert); - asn1_output(conn, "\t expire date: ", certdate); + asn1_output(certdate, buffer, sizeof(buffer)); + infof(data, "\t expire date: %s\n", buffer); if(data->set.ssl.verifyhost) { retcode = verifyhost(conn, connssl->server_cert); @@ -1676,16 +2063,15 @@ static CURLcode servercert(struct connectdata *conn, } } - str = X509_NAME_oneline(X509_get_issuer_name(connssl->server_cert), - NULL, 0); - if(!str) { + rc = x509_name_oneline(X509_get_issuer_name(connssl->server_cert), + buffer, sizeof(buffer)); + if(rc) { if(strict) 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); + infof(data, "\t issuer: %s\n", buffer); /* We could do all sorts of certificate verification stuff here before deallocating the certificate. */ diff --git a/lib/ssluse.h b/lib/ssluse.h index 851548902..f0dd2beb6 100644 --- a/lib/ssluse.h +++ b/lib/ssluse.h @@ -94,4 +94,4 @@ bool Curl_ossl_data_pending(const struct connectdata *conn, #define curlssl_data_pending(x,y) Curl_ossl_data_pending(x,y) #endif /* USE_SSLEAY */ -#endif +#endif /* __SSLUSE_H */ diff --git a/lib/url.c b/lib/url.c index b000fe2e3..d6dec0d0a 100644 --- a/lib/url.c +++ b/lib/url.c @@ -481,6 +481,7 @@ CURLcode Curl_close(struct SessionHandle *data) Curl_ssl_close_all(data); Curl_safefree(data->state.first_host); Curl_safefree(data->state.scratch); + Curl_ssl_free_certinfo(data); if(data->change.referer_alloc) free(data->change.referer); @@ -1800,6 +1801,9 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, */ data->set.ssl.fsslctxp = va_arg(param, void *); break; + case CURLOPT_CERTINFO: + data->set.ssl.certinfo = (bool)(0 != va_arg(param, long)); + break; #endif case CURLOPT_CAINFO: /* diff --git a/lib/urldata.h b/lib/urldata.h index 1f0f63b41..f1a001aa0 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -227,6 +227,7 @@ struct ssl_config_data { curl_ssl_ctx_callback fsslctx; /* function to initialize ssl ctx */ void *fsslctxp; /* parameter for call back */ bool sessionid; /* cache session IDs or not */ + bool certinfo; /* gather lots of cert info */ }; /* information stored about one single SSL session */ @@ -1051,6 +1052,9 @@ struct PureInfo { char ip[MAX_IPADR_LEN]; /* this buffer gets the numerical ip version stored at the connect *attempt* so it will get the last tried connect IP even on failures */ + struct curl_certinfo certs; /* info about the certs, only populated in + OpenSSL builds. Asked for with + CURLOPT_CERTINFO / CURLINFO_CERTINFO */ };