1
0
mirror of https://github.com/moparisthebest/curl synced 2024-12-22 08:08:50 -05:00

- 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!
This commit is contained in:
Daniel Stenberg 2008-09-05 14:29:21 +00:00
parent 873e734c39
commit 4c9768565e
15 changed files with 542 additions and 41 deletions

12
CHANGES
View File

@ -7,6 +7,18 @@
Changelog Changelog
Daniel Stenberg (5 Sep 2008) 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 - Dmitriy Sergeyev pointed out that curl_easy_pause() didn't unpause properly
during certain conditions. I also changed this code to use realloc() based during certain conditions. I also changed this code to use realloc() based
on Daniel Fandrich's suggestion. on Daniel Fandrich's suggestion.

View File

@ -2,7 +2,7 @@ Curl and libcurl 7.19.1
Public curl releases: 107 Public curl releases: 107
Command line options: 127 Command line options: 127
curl_easy_setopt() options: 153 curl_easy_setopt() options: 154
Public functions in libcurl: 58 Public functions in libcurl: 58
Known libcurl bindings: 36 Known libcurl bindings: 36
Contributors: 672 Contributors: 672
@ -10,6 +10,7 @@ Curl and libcurl 7.19.1
This release includes the following changes: This release includes the following changes:
o pkg-config can now show supported_protocols and supported_features o pkg-config can now show supported_protocols and supported_features
o Added CURLOPT_CERTINFO and CURLINFO_CERTINFO
This release includes the following bugfixes: 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 This release would not have looked like this without help, code, reports and
advice from friends like these: 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) Thanks! (and sorry if I forgot to mention someone)

View File

@ -1,9 +1,6 @@
To be addressed before 7.19.1 (planned release: October/November 2008) 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: 158 - Martin Drasar's CURLOPT_POSTREDIR work:
http://curl.haxx.se/mail/lib-2008-08/0170.html http://curl.haxx.se/mail/lib-2008-08/0170.html

View File

@ -5,7 +5,7 @@ check_PROGRAMS = 10-at-a-time anyauthput cookie_interface \
https multi-app multi-debugcallback multi-double \ https multi-app multi-debugcallback multi-double \
multi-post multi-single persistant post-callback \ multi-post multi-single persistant post-callback \
postit2 sepheaders simple simplepost simplessl \ postit2 sepheaders simple simplepost simplessl \
sendrecv httpcustomheader sendrecv httpcustomheader certinfo
# These examples require external dependencies that may not be commonly # These examples require external dependencies that may not be commonly
# available on POSIX systems, so don't bother attempting to compile them here. # 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 \ ghiper.c hiperfifo.c htmltidy.c multithread.c \
opensslthreadlock.c sampleconv.c synctime.c threaded-ssl.c opensslthreadlock.c sampleconv.c synctime.c threaded-ssl.c

62
docs/examples/certinfo.c Normal file
View File

@ -0,0 +1,62 @@
/*****************************************************************************
*/
#include <stdio.h>
#include <curl/curl.h>
#include <curl/types.h>
#include <curl/easy.h>
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; i<ci->num_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;
}

View File

@ -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. 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 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) 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 .IP CURLOPT_RANDOM_FILE
Pass a char * to a zero terminated file name. The file will be used to read 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, from to seed the random engine for SSL. The more random the specified file is,

View File

@ -1135,6 +1135,11 @@ typedef enum {
/* (IPv6) Address scope */ /* (IPv6) Address scope */
CINIT(ADDRESS_SCOPE, LONG, 171), 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 */ CURLOPT_LASTENTRY /* the last unused */
} CURLoption; } 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); 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_STRING 0x100000
#define CURLINFO_LONG 0x200000 #define CURLINFO_LONG 0x200000
#define CURLINFO_DOUBLE 0x300000 #define CURLINFO_DOUBLE 0x300000
@ -1533,9 +1547,10 @@ typedef enum {
CURLINFO_REDIRECT_URL = CURLINFO_STRING + 31, CURLINFO_REDIRECT_URL = CURLINFO_STRING + 31,
CURLINFO_PRIMARY_IP = CURLINFO_STRING + 32, CURLINFO_PRIMARY_IP = CURLINFO_STRING + 32,
CURLINFO_APPCONNECT_TIME = CURLINFO_DOUBLE + 33, CURLINFO_APPCONNECT_TIME = CURLINFO_DOUBLE + 33,
CURLINFO_CERTINFO = CURLINFO_SLIST + 34,
/* Fill in new entries below here! */ /* Fill in new entries below here! */
CURLINFO_LASTONE = 33 CURLINFO_LASTONE = 34
} CURLINFO; } CURLINFO;
/* CURLINFO_RESPONSE_CODE is the new name for the option previously known as /* CURLINFO_RESPONSE_CODE is the new name for the option previously known as

View File

@ -217,6 +217,11 @@ CURLcode Curl_getinfo(struct SessionHandle *data, CURLINFO info, ...)
/* Return the ip address of the most recent (primary) connection */ /* Return the ip address of the most recent (primary) connection */
*param_charp = data->info.ip; *param_charp = data->info.ip;
break; 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: default:
return CURLE_BAD_FUNCTION_ARGUMENT; return CURLE_BAD_FUNCTION_ARGUMENT;
} }

View File

@ -227,9 +227,9 @@ void Curl_infof(struct SessionHandle *data, const char *fmt, ...)
if(data && data->set.verbose) { if(data && data->set.verbose) {
va_list ap; va_list ap;
size_t len; size_t len;
char print_buffer[1024 + 1]; char print_buffer[2048 + 1];
va_start(ap, fmt); va_start(ap, fmt);
vsnprintf(print_buffer, 1024, fmt, ap); vsnprintf(print_buffer, sizeof(print_buffer), fmt, ap);
va_end(ap); va_end(ap);
len = strlen(print_buffer); len = strlen(print_buffer);
Curl_debug(data, CURLINFO_TEXT, print_buffer, len, NULL); Curl_debug(data, CURLINFO_TEXT, print_buffer, len, NULL);

View File

@ -465,3 +465,15 @@ bool Curl_ssl_data_pending(const struct connectdata *conn,
} }
#endif /* USE_SSL */ #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; i<ci->num_of_certs; i++)
curl_slist_free_all(ci->certinfo[i]);
free(ci->certinfo); /* free the actual array too */
ci->num_of_certs = 0;
}
}

View File

@ -59,7 +59,7 @@ size_t Curl_ssl_version(char *buffer, size_t size);
bool Curl_ssl_data_pending(const struct connectdata *conn, bool Curl_ssl_data_pending(const struct connectdata *conn,
int connindex); int connindex);
int Curl_ssl_check_cxn(struct connectdata *conn); int Curl_ssl_check_cxn(struct connectdata *conn);
void Curl_ssl_free_certinfo(struct SessionHandle *data);
#else #else
/* When SSL support is not present, just define away these function calls */ /* When SSL support is not present, just define away these function calls */
#define Curl_ssl_init() 1 #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_version(x,y) 0
#define Curl_ssl_data_pending(x,y) 0 #define Curl_ssl_data_pending(x,y) 0
#define Curl_ssl_check_cxn(x) 0 #define Curl_ssl_check_cxn(x) 0
#define Curl_ssl_free_certinfo(x)
#endif #endif
@ -90,11 +91,6 @@ CURLcode Curl_ssl_addsessionid(struct connectdata *conn,
void *ssl_sessionid, void *ssl_sessionid,
size_t idsize); 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 */ #define SSL_SHUTDOWN_TIMEOUT 10000 /* ms */
#endif #endif

View File

@ -554,6 +554,33 @@ int cert_stuff(struct connectdata *conn,
return(1); 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 static
int cert_verify_callback(int ok, X509_STORE_CTX *ctx) 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]; char buf[256];
err_cert=X509_STORE_CTX_get_current_cert(ctx); 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; return ok;
} }
@ -886,23 +913,19 @@ int Curl_ossl_close_all(struct SessionHandle *data)
return 0; return 0;
} }
static int asn1_output(struct connectdata *conn, static int asn1_output(const ASN1_UTCTIME *tm,
const char *prefix, char *buf,
const ASN1_UTCTIME *tm) size_t sizeofbuf)
{ {
const char *asn1_string; const char *asn1_string;
int gmt=FALSE; int gmt=FALSE;
int i; int i;
int year=0,month=0,day=0,hour=0,minute=0,second=0; int year=0,month=0,day=0,hour=0,minute=0,second=0;
struct SessionHandle *data = conn->data;
#ifdef CURL_DISABLE_VERBOSE_STRINGS #ifdef CURL_DISABLE_VERBOSE_STRINGS
(void)prefix; (void)prefix;
#endif #endif
if(!data->set.verbose)
return 0;
i=tm->length; i=tm->length;
asn1_string=(const char *)tm->data; 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')) (asn1_string[11] >= '0') && (asn1_string[11] <= '9'))
second= (asn1_string[10]-'0')*10+(asn1_string[11]-'0'); second= (asn1_string[10]-'0')*10+(asn1_string[11]-'0');
infof(data, snprintf(buf, sizeofbuf,
"%s%04d-%02d-%02d %02d:%02d:%02d %s\n", "%04d-%02d-%02d %02d:%02d:%02d %s",
prefix, year+1900, month, day, hour, minute, second, (gmt?"GMT":"")); year+1900, month, day, hour, minute, second, (gmt?"GMT":""));
return 0; 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; i<sk_X509_EXTENSION_num(exts); i++) {
ASN1_OBJECT *obj;
X509_EXTENSION *ext = sk_X509_EXTENSION_value(exts, i);
BIO *bio_out = BIO_new(BIO_s_mem());
BUF_MEM *biomem;
char buf[512];
char *ptr=buf;
char namebuf[128];
obj = X509_EXTENSION_get_object(ext);
asn1_object_dump(obj, namebuf, sizeof(namebuf));
infof(data, "%s: %s\n", namebuf,
X509_EXTENSION_get_critical(ext)?"(critical)":"");
if(!X509V3_EXT_print(bio_out, ext, 0, 0))
M_ASN1_OCTET_STRING_print(bio_out, ext->value);
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; j<biomem->length; j++) {
const char *sep="";
if(biomem->data[j] == '\n') {
sep=", ";
j++; /* skip the newline */
};
while((biomem->data[j] == ' ') && (j<biomem->length))
j++;
if(j<biomem->length)
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; i<sig->length; 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; i<numcerts; i++) {
long value;
ASN1_INTEGER *num;
ASN1_TIME *certdate;
/* get the certs in "importance order" */
#if 0
X509 *x = sk_X509_value(sk, numcerts - i - 1);
#else
X509 *x = sk_X509_value(sk, i);
#endif
X509_CINF *cinf;
EVP_PKEY *pubkey=NULL;
int j;
char *ptr;
(void)x509_name_oneline(X509_get_subject_name(x), buf, sizeof(buf));
infof(data, "%2d Subject: %s\n",i,buf);
push_certinfo(data, i, "Subject", buf);
(void)x509_name_oneline(X509_get_issuer_name(x), buf, sizeof(buf));
infof(data, " Issuer: %s\n",buf);
push_certinfo(data, i, "Issuer", buf);
value = X509_get_version(x);
infof(data, " Version: %lu (0x%lx)\n", value+1, value);
snprintf(buf, sizeof(buf), "%lx", value);
push_certinfo(data, i, "Version", buf); /* hex */
num=X509_get_serialNumber(x);
if (num->length <= 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; j<num->length; 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 * 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 * 'strict' argument is TRUE as otherwise all this is for informational
@ -1632,12 +2013,17 @@ static CURLcode servercert(struct connectdata *conn,
bool strict) bool strict)
{ {
CURLcode retcode = CURLE_OK; CURLcode retcode = CURLE_OK;
char *str; int rc;
long lerr; long lerr;
ASN1_TIME *certdate; ASN1_TIME *certdate;
struct SessionHandle *data = conn->data; struct SessionHandle *data = conn->data;
X509 *issuer; X509 *issuer;
FILE *fp; 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; data->set.ssl.certverifyresult = !X509_V_OK;
@ -1649,23 +2035,24 @@ static CURLcode servercert(struct connectdata *conn,
} }
infof (data, "Server certificate:\n"); infof (data, "Server certificate:\n");
str = X509_NAME_oneline(X509_get_subject_name(connssl->server_cert), rc = x509_name_oneline(X509_get_subject_name(connssl->server_cert),
NULL, 0); buffer, sizeof(buffer));
if(!str) { if(rc) {
if(strict) if(strict)
failf(data, "SSL: couldn't get X509-subject!"); failf(data, "SSL: couldn't get X509-subject!");
X509_free(connssl->server_cert); X509_free(connssl->server_cert);
connssl->server_cert = NULL; connssl->server_cert = NULL;
return CURLE_SSL_CONNECT_ERROR; return CURLE_SSL_CONNECT_ERROR;
} }
infof(data, "\t subject: %s\n", str); infof(data, "\t subject: %s\n", buffer);
CRYPTO_free(str);
certdate = X509_get_notBefore(connssl->server_cert); 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); 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) { if(data->set.ssl.verifyhost) {
retcode = verifyhost(conn, connssl->server_cert); 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), rc = x509_name_oneline(X509_get_issuer_name(connssl->server_cert),
NULL, 0); buffer, sizeof(buffer));
if(!str) { if(rc) {
if(strict) if(strict)
failf(data, "SSL: couldn't get X509-issuer name!"); failf(data, "SSL: couldn't get X509-issuer name!");
retcode = CURLE_SSL_CONNECT_ERROR; retcode = CURLE_SSL_CONNECT_ERROR;
} }
else { else {
infof(data, "\t issuer: %s\n", str); infof(data, "\t issuer: %s\n", buffer);
CRYPTO_free(str);
/* We could do all sorts of certificate verification stuff here before /* We could do all sorts of certificate verification stuff here before
deallocating the certificate. */ deallocating the certificate. */

View File

@ -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) #define curlssl_data_pending(x,y) Curl_ossl_data_pending(x,y)
#endif /* USE_SSLEAY */ #endif /* USE_SSLEAY */
#endif #endif /* __SSLUSE_H */

View File

@ -481,6 +481,7 @@ CURLcode Curl_close(struct SessionHandle *data)
Curl_ssl_close_all(data); Curl_ssl_close_all(data);
Curl_safefree(data->state.first_host); Curl_safefree(data->state.first_host);
Curl_safefree(data->state.scratch); Curl_safefree(data->state.scratch);
Curl_ssl_free_certinfo(data);
if(data->change.referer_alloc) if(data->change.referer_alloc)
free(data->change.referer); free(data->change.referer);
@ -1800,6 +1801,9 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option,
*/ */
data->set.ssl.fsslctxp = va_arg(param, void *); data->set.ssl.fsslctxp = va_arg(param, void *);
break; break;
case CURLOPT_CERTINFO:
data->set.ssl.certinfo = (bool)(0 != va_arg(param, long));
break;
#endif #endif
case CURLOPT_CAINFO: case CURLOPT_CAINFO:
/* /*

View File

@ -227,6 +227,7 @@ struct ssl_config_data {
curl_ssl_ctx_callback fsslctx; /* function to initialize ssl ctx */ curl_ssl_ctx_callback fsslctx; /* function to initialize ssl ctx */
void *fsslctxp; /* parameter for call back */ void *fsslctxp; /* parameter for call back */
bool sessionid; /* cache session IDs or not */ bool sessionid; /* cache session IDs or not */
bool certinfo; /* gather lots of cert info */
}; };
/* information stored about one single SSL session */ /* 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 char ip[MAX_IPADR_LEN]; /* this buffer gets the numerical ip version stored
at the connect *attempt* so it will get the last at the connect *attempt* so it will get the last
tried connect IP even on failures */ 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 */
}; };