diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/getinfo.c | 5 | ||||
-rw-r--r-- | lib/sendf.c | 4 | ||||
-rw-r--r-- | lib/sslgen.c | 12 | ||||
-rw-r--r-- | lib/sslgen.h | 8 | ||||
-rw-r--r-- | lib/ssluse.c | 434 | ||||
-rw-r--r-- | lib/ssluse.h | 2 | ||||
-rw-r--r-- | lib/url.c | 4 | ||||
-rw-r--r-- | lib/urldata.h | 4 |
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 */ @@ -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 */ }; |