/*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at https://curl.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * * SPDX-License-Identifier: curl * ***************************************************************************/ #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" /* The maximum line length for an ecoded session ticket */ #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 = fopen(filename, FOPEN_READTEXT); if(!fp) { /* ok if it does not exist */ 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) 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 = 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) fclose(ctx.fp); return r; }