Skip to content

Commit

Permalink
writeout: add %{certs} and %{num_certs}
Browse files Browse the repository at this point in the history
Let users get the server certificate chain using the command line

Closes curl#10019
  • Loading branch information
bagder committed Dec 27, 2022
1 parent db5f833 commit c6aa19c
Show file tree
Hide file tree
Showing 7 changed files with 77 additions and 2 deletions.
9 changes: 9 additions & 0 deletions docs/cmdline-opts/write-out.d
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ occurrences of % must be doubled when using this option.
The variables available are:
.RS
.TP 15
.B certs
Output the certificate chain with details. Supported only by the OpenSSL,
GnuTLS, Schannel, NSS, GSKit and Secure Transport backends (Added in 7.88.0)
.TP
.B content_type
The Content-Type of the requested document, if there was any.
.TP
Expand Down Expand Up @@ -89,6 +93,11 @@ The local port number of the most recently done connection. (Added in 7.29.0)
.B method
The http method used in the most recent HTTP request. (Added in 7.72.0)
.TP
.B num_certs
Number of server certificates received in the TLS handshake. Supported only by
the OpenSSL, GnuTLS, Schannel, NSS, GSKit and Secure Transport backends (Added
in 7.88.0)
.TP
.B num_connects
Number of new connects made in the recent transfer. (Added in 7.12.3)
.TP
Expand Down
3 changes: 3 additions & 0 deletions src/tool_operate.c
Original file line number Diff line number Diff line change
Expand Up @@ -1553,6 +1553,9 @@ static CURLcode single_transfer(struct GlobalConfig *global,
if(config->ssl_ec_curves)
my_setopt_str(curl, CURLOPT_SSL_EC_CURVES, config->ssl_ec_curves);

if(config->writeout)
my_setopt_str(curl, CURLOPT_CERTINFO, 1L);

if(feature_ssl) {
/* Check if config->cert is a PKCS#11 URI and set the
* config->cert_type if necessary */
Expand Down
1 change: 1 addition & 0 deletions src/tool_operate.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ struct per_transfer {
struct per_transfer *next;
struct per_transfer *prev;
struct OperationConfig *config; /* for this transfer */
struct curl_certinfo *certinfo;
CURL *curl;
long retry_numretries;
long retry_sleep_default;
Expand Down
60 changes: 60 additions & 0 deletions src/tool_writeout.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "tool_cfgable.h"
#include "tool_writeout.h"
#include "tool_writeout_json.h"
#include "dynbuf.h"

#include "memdebug.h" /* keep this as LAST include */

Expand Down Expand Up @@ -72,6 +73,7 @@ static const struct httpmap http_version[] = {
Variable names should be in alphabetical order.
*/
static const struct writeoutvar variables[] = {
{"certs", VAR_CERT, CURLINFO_NONE, writeString},
{"content_type", VAR_CONTENT_TYPE, CURLINFO_CONTENT_TYPE, writeString},
{"errormsg", VAR_ERRORMSG, CURLINFO_NONE, writeString},
{"exitcode", VAR_EXITCODE, CURLINFO_NONE, writeLong},
Expand All @@ -85,6 +87,7 @@ static const struct writeoutvar variables[] = {
{"local_ip", VAR_LOCAL_IP, CURLINFO_LOCAL_IP, writeString},
{"local_port", VAR_LOCAL_PORT, CURLINFO_LOCAL_PORT, writeLong},
{"method", VAR_EFFECTIVE_METHOD, CURLINFO_EFFECTIVE_METHOD, writeString},
{"num_certs", VAR_NUM_CERTS, CURLINFO_NONE, writeLong},
{"num_connects", VAR_NUM_CONNECTS, CURLINFO_NUM_CONNECTS, writeLong},
{"num_headers", VAR_NUM_HEADERS, CURLINFO_NONE, writeLong},
{"num_redirects", VAR_REDIRECT_COUNT, CURLINFO_REDIRECT_COUNT, writeLong},
Expand Down Expand Up @@ -168,6 +171,8 @@ static int writeString(FILE *stream, const struct writeoutvar *wovar,
{
bool valid = false;
const char *strinfo = NULL;
struct dynbuf buf;
curlx_dyn_init(&buf, 256*1024);

DEBUGASSERT(wovar->writefunc == writeString);

Expand All @@ -193,6 +198,51 @@ static int writeString(FILE *stream, const struct writeoutvar *wovar,
}
else {
switch(wovar->id) {
case VAR_CERT:
if(per->certinfo) {
int i;
bool error = FALSE;
for(i = 0; (i < per->certinfo->num_of_certs) && !error; i++) {
struct curl_slist *slist;

for(slist = per->certinfo->certinfo[i]; slist; slist = slist->next) {
size_t len;
if(curl_strnequal(slist->data, "cert:", 5)) {
if(curlx_dyn_add(&buf, &slist->data[5])) {
error = TRUE;
break;
}
}
else {
if(curlx_dyn_add(&buf, slist->data)) {
error = TRUE;
break;
}
}
len = curlx_dyn_len(&buf);
if(len) {
char *ptr = curlx_dyn_ptr(&buf);
if(ptr[len -1] != '\n') {
/* add a newline to make things look better */
if(curlx_dyn_addn(&buf, "\n", 1)) {
error = TRUE;
break;
}
}
}
}
}
if(!error) {
strinfo = curlx_dyn_ptr(&buf);
if(!strinfo)
/* maybe not a TLS protocol */
strinfo = "";
valid = true;
}
}
else
strinfo = ""; /* no cert info */
break;
case VAR_ERRORMSG:
if(per_result) {
strinfo = (per->errorbuffer && per->errorbuffer[0]) ?
Expand Down Expand Up @@ -232,6 +282,7 @@ static int writeString(FILE *stream, const struct writeoutvar *wovar,
fprintf(stream, "\"%s\":null", wovar->name);
}

curlx_dyn_free(&buf);
return 1; /* return 1 if anything was written */
}

Expand All @@ -250,6 +301,10 @@ static int writeLong(FILE *stream, const struct writeoutvar *wovar,
}
else {
switch(wovar->id) {
case VAR_NUM_CERTS:
longinfo = per->certinfo ? per->certinfo->num_of_certs : 0;
valid = true;
break;
case VAR_NUM_HEADERS:
longinfo = per->num_headers;
valid = true;
Expand Down Expand Up @@ -327,6 +382,11 @@ void ourWriteOut(const char *writeinfo, struct per_transfer *per,
FILE *stream = stdout;
const char *ptr = writeinfo;
bool done = FALSE;
struct curl_certinfo *certinfo;
CURLcode res = curl_easy_getinfo(per->curl, CURLINFO_CERTINFO, &certinfo);

if(!res && certinfo)
per->certinfo = certinfo;

while(ptr && *ptr && !done) {
if('%' == *ptr && ptr[1]) {
Expand Down
2 changes: 2 additions & 0 deletions src/tool_writeout.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
typedef enum {
VAR_NONE, /* must be the first */
VAR_APPCONNECT_TIME,
VAR_CERT,
VAR_CONNECT_TIME,
VAR_CONTENT_TYPE,
VAR_EFFECTIVE_FILENAME,
Expand All @@ -47,6 +48,7 @@ typedef enum {
VAR_LOCAL_IP,
VAR_LOCAL_PORT,
VAR_NAMELOOKUP_TIME,
VAR_NUM_CERTS,
VAR_NUM_CONNECTS,
VAR_NUM_HEADERS,
VAR_ONERROR,
Expand Down
2 changes: 1 addition & 1 deletion tests/data/test970
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Accept: */*

</protocol>
<stdout nonewline="yes">
{"content_type":"text/html","errormsg":null,"exitcode":0,"filename_effective":"log/out%TESTNUMBER","ftp_entry_path":null,"http_code":200,"http_connect":0,"http_version":"1.1","local_ip":"127.0.0.1","local_port":13,"method":"GET","num_connects":1,"num_headers":9,"num_redirects":0,"proxy_ssl_verify_result":0,"redirect_url":null,"referer":null,"remote_ip":"%HOSTIP","remote_port":%HTTPPORT,"response_code":200,"scheme":"HTTP","size_download":445,"size_header":4019,"size_request":4019,"size_upload":0,"speed_download":13,"speed_upload":13,"ssl_verify_result":0,"time_appconnect":0.000013,"time_connect":0.000013,"time_namelookup":0.000013,"time_pretransfer":0.000013,"time_redirect":0.000013,"time_starttransfer":0.000013,"time_total":0.000013,"url":"http://%HOSTIP:%HTTPPORT/%TESTNUMBER","url_effective":"http://%HOSTIP:%HTTPPORT/%TESTNUMBER","urlnum":0,"curl_version":"curl-unit-test-fake-version"}
{"certs":"","content_type":"text/html","errormsg":null,"exitcode":0,"filename_effective":"log/out%TESTNUMBER","ftp_entry_path":null,"http_code":200,"http_connect":0,"http_version":"1.1","local_ip":"127.0.0.1","local_port":13,"method":"GET","num_certs":0,"num_connects":1,"num_headers":9,"num_redirects":0,"proxy_ssl_verify_result":0,"redirect_url":null,"referer":null,"remote_ip":"%HOSTIP","remote_port":%HTTPPORT,"response_code":200,"scheme":"HTTP","size_download":445,"size_header":4019,"size_request":4019,"size_upload":0,"speed_download":13,"speed_upload":13,"ssl_verify_result":0,"time_appconnect":0.000013,"time_connect":0.000013,"time_namelookup":0.000013,"time_pretransfer":0.000013,"time_redirect":0.000013,"time_starttransfer":0.000013,"time_total":0.000013,"url":"http://%HOSTIP:%HTTPPORT/%TESTNUMBER","url_effective":"http://%HOSTIP:%HTTPPORT/%TESTNUMBER","urlnum":0,"curl_version":"curl-unit-test-fake-version"}
</stdout>
</verify>
</testcase>
2 changes: 1 addition & 1 deletion tests/data/test972
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Accept: */*

</protocol>
<stdout mode="text">
{"content_type":"text/html","errormsg":null,"exitcode":0,"filename_effective":"log/out972","ftp_entry_path":null,"http_code":200,"http_connect":0,"http_version":"1.1","local_ip":"%HOSTIP","local_port":13,"method":"GET","num_connects":1,"num_headers":9,"num_redirects":0,"proxy_ssl_verify_result":0,"redirect_url":null,"referer":null,"remote_ip":"%HOSTIP","remote_port":%HTTPPORT,"response_code":200,"scheme":"HTTP","size_download":445,"size_header":4019,"size_request":4019,"size_upload":0,"speed_download":13,"speed_upload":13,"ssl_verify_result":0,"time_appconnect":0.000013,"time_connect":0.000013,"time_namelookup":0.000013,"time_pretransfer":0.000013,"time_redirect":0.000013,"time_starttransfer":0.000013,"time_total":0.000013,"url":"http://%HOSTIP:%HTTPPORT/%TESTNUMBER","url_effective":"http://%HOSTIP:%HTTPPORT/%TESTNUMBER","urlnum":0,"curl_version":"curl-unit-test-fake-version"}
{"certs":"","content_type":"text/html","errormsg":null,"exitcode":0,"filename_effective":"log/out972","ftp_entry_path":null,"http_code":200,"http_connect":0,"http_version":"1.1","local_ip":"%HOSTIP","local_port":13,"method":"GET","num_certs":0,"num_connects":1,"num_headers":9,"num_redirects":0,"proxy_ssl_verify_result":0,"redirect_url":null,"referer":null,"remote_ip":"%HOSTIP","remote_port":%HTTPPORT,"response_code":200,"scheme":"HTTP","size_download":445,"size_header":4019,"size_request":4019,"size_upload":0,"speed_download":13,"speed_upload":13,"ssl_verify_result":0,"time_appconnect":0.000013,"time_connect":0.000013,"time_namelookup":0.000013,"time_pretransfer":0.000013,"time_redirect":0.000013,"time_starttransfer":0.000013,"time_total":0.000013,"url":"http://%HOSTIP:%HTTPPORT/%TESTNUMBER","url_effective":"http://%HOSTIP:%HTTPPORT/%TESTNUMBER","urlnum":0,"curl_version":"curl-unit-test-fake-version"}
</stdout>
</verify>
</testcase>

0 comments on commit c6aa19c

Please sign in to comment.