#include "tool_setup.h"
#include "tool_cfgable.h"
#include "tool_cb_dbg.h"
#include "tool_msgs.h"
#include "tool_setopt.h"
#include "tool_ssls.h"
#include "tool_parsecfg.h"
#define MAX_SSLS_LINE (64 * 1024)
static CURLcode tool_ssls_easy(struct OperationConfig *config,
CURLSH *share, CURL **peasy)
{
CURLcode result = CURLE_OK;
*peasy = curl_easy_init();
if(!*peasy)
return CURLE_OUT_OF_MEMORY;
result = curl_easy_setopt(*peasy, CURLOPT_SHARE, share);
if(!result && (global->tracetype != TRACE_NONE)) {
my_setopt(*peasy, CURLOPT_DEBUGFUNCTION, tool_debug_cb);
my_setopt(*peasy, CURLOPT_DEBUGDATA, config);
my_setopt_long(*peasy, CURLOPT_VERBOSE, 1L);
}
return result;
}
CURLcode tool_ssls_load(struct OperationConfig *config,
CURLSH *share, const char *filename)
{
FILE *fp;
CURL *easy = NULL;
struct dynbuf buf;
unsigned char *shmac = NULL, *sdata = NULL;
char *c, *line, *end;
size_t shmac_len, sdata_len;
CURLcode r = CURLE_OK;
int i, imported;
bool error = FALSE;
curlx_dyn_init(&buf, MAX_SSLS_LINE);
fp = curlx_fopen(filename, FOPEN_READTEXT);
if(!fp) {
notef("SSL session file does not exist (yet?): %s", filename);
goto out;
}
r = tool_ssls_easy(config, share, &easy);
if(r)
goto out;
i = imported = 0;
while(my_get_line(fp, &buf, &error)) {
++i;
curl_free(shmac);
curl_free(sdata);
line = curlx_dyn_ptr(&buf);
c = memchr(line, ':', strlen(line));
if(!c) {
warnf("unrecognized line %d in ssl session file %s", i, filename);
continue;
}
*c = '\0';
r = curlx_base64_decode(line, &shmac, &shmac_len);
if(r) {
warnf("invalid shmax base64 encoding in line %d", i);
continue;
}
line = c + 1;
end = line + strlen(line) - 1;
while((end > line) && (*end == '\n' || *end == '\r' || ISBLANK(*line))) {
*end = '\0';
--end;
}
r = curlx_base64_decode(line, &sdata, &sdata_len);
if(r) {
warnf("invalid sdata base64 encoding in line %d: %s", i, line);
continue;
}
r = curl_easy_ssls_import(easy, NULL, shmac, shmac_len, sdata, sdata_len);
if(r) {
warnf("import of session from line %d rejected(%d)", i, r);
continue;
}
++imported;
}
if(error)
r = CURLE_FAILED_INIT;
else
r = CURLE_OK;
out:
if(easy)
curl_easy_cleanup(easy);
if(fp)
curlx_fclose(fp);
curlx_dyn_free(&buf);
curl_free(shmac);
curl_free(sdata);
return r;
}
struct tool_ssls_ctx {
FILE *fp;
int exported;
};
static CURLcode tool_ssls_exp(CURL *easy, void *userptr,
const char *session_key,
const unsigned char *shmac, size_t shmac_len,
const unsigned char *sdata, size_t sdata_len,
curl_off_t valid_until, int ietf_tls_id,
const char *alpn, size_t earlydata_max)
{
struct tool_ssls_ctx *ctx = userptr;
char *enc = NULL;
size_t enc_len;
CURLcode r;
(void)easy;
(void)valid_until;
(void)ietf_tls_id;
(void)alpn;
(void)earlydata_max;
if(!ctx->exported)
fputs("# Your SSL session cache. https://curl.se/docs/ssl-sessions.html\n"
"# This file was generated by libcurl! Edit at your own risk.\n",
ctx->fp);
r = curlx_base64_encode((const char *)shmac, shmac_len, &enc, &enc_len);
if(r)
goto out;
r = CURLE_WRITE_ERROR;
if(enc_len != fwrite(enc, 1, enc_len, ctx->fp))
goto out;
if(EOF == fputc(':', ctx->fp))
goto out;
curl_free(enc);
r = curlx_base64_encode((const char *)sdata, sdata_len, &enc, &enc_len);
if(r)
goto out;
r = CURLE_WRITE_ERROR;
if(enc_len != fwrite(enc, 1, enc_len, ctx->fp))
goto out;
if(EOF == fputc('\n', ctx->fp))
goto out;
r = CURLE_OK;
ctx->exported++;
out:
if(r)
warnf("Warning: error saving SSL session for '%s': %d", session_key, r);
curl_free(enc);
return r;
}
CURLcode tool_ssls_save(struct OperationConfig *config,
CURLSH *share, const char *filename)
{
struct tool_ssls_ctx ctx;
CURL *easy = NULL;
CURLcode r = CURLE_OK;
ctx.exported = 0;
ctx.fp = curlx_fopen(filename, FOPEN_WRITETEXT);
if(!ctx.fp) {
warnf("Warning: Failed to create SSL session file %s",
filename);
goto out;
}
r = tool_ssls_easy(config, share, &easy);
if(r)
goto out;
r = curl_easy_ssls_export(easy, tool_ssls_exp, &ctx);
out:
if(easy)
curl_easy_cleanup(easy);
if(ctx.fp)
curlx_fclose(ctx.fp);
return r;
}