summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDaniel Stenberg <daniel@haxx.se>2022-12-27 12:00:12 +0100
committerDaniel Stenberg <daniel@haxx.se>2022-12-27 22:41:17 +0100
commitc6aa19c1dadb082c00f9b693dfda0559556aeba8 (patch)
tree9febf378c69f4452285f494bd9706062c6902617 /src
parentdb5f833cc72a1ceb812dde55cf926858f61c086b (diff)
downloadcurl-c6aa19c1dadb082c00f9b693dfda0559556aeba8.tar.gz
writeout: add %{certs} and %{num_certs}
Let users get the server certificate chain using the command line Closes #10019
Diffstat (limited to 'src')
-rw-r--r--src/tool_operate.c3
-rw-r--r--src/tool_operate.h1
-rw-r--r--src/tool_writeout.c60
-rw-r--r--src/tool_writeout.h2
4 files changed, 66 insertions, 0 deletions
diff --git a/src/tool_operate.c b/src/tool_operate.c
index 2b0cc083c..6d0ba68cd 100644
--- a/src/tool_operate.c
+++ b/src/tool_operate.c
@@ -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 */
diff --git a/src/tool_operate.h b/src/tool_operate.h
index 5c08b99ae..ce3304cb1 100644
--- a/src/tool_operate.h
+++ b/src/tool_operate.h
@@ -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;
diff --git a/src/tool_writeout.c b/src/tool_writeout.c
index 2789ee20b..e99f1fc13 100644
--- a/src/tool_writeout.c
+++ b/src/tool_writeout.c
@@ -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 */
@@ -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},
@@ -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},
@@ -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);
@@ -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]) ?
@@ -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 */
}
@@ -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;
@@ -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]) {
diff --git a/src/tool_writeout.h b/src/tool_writeout.h
index c7cdb9771..1d6572020 100644
--- a/src/tool_writeout.h
+++ b/src/tool_writeout.h
@@ -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,
@@ -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,