aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/getinfo.c5
-rw-r--r--lib/sendf.c4
-rw-r--r--lib/sslgen.c12
-rw-r--r--lib/sslgen.h8
-rw-r--r--lib/ssluse.c434
-rw-r--r--lib/ssluse.h2
-rw-r--r--lib/url.c4
-rw-r--r--lib/urldata.h4
8 files changed, 440 insertions, 33 deletions
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; 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;
+ }
+}
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; 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
* '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 */
};