1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24 #include "tool_setup.h"
25
26 #include "curlx.h"
27 #include "tool_cfgable.h"
28 #include "tool_cb_dbg.h"
29 #include "tool_msgs.h"
30 #include "tool_setopt.h"
31 #include "tool_ssls.h"
32 #include "dynbuf.h"
33 #include "curl_base64.h"
34 #include "curl_get_line.h"
35
36 /* The maximum line length for an ecoded session ticket */
37 #define MAX_SSLS_LINE (64 * 1024)
38
39
tool_ssls_easy(struct GlobalConfig * global,struct OperationConfig * config,CURLSH * share,CURL ** peasy)40 static CURLcode tool_ssls_easy(struct GlobalConfig *global,
41 struct OperationConfig *config,
42 CURLSH *share, CURL **peasy)
43 {
44 CURLcode result = CURLE_OK;
45
46 *peasy = curl_easy_init();
47 if(!*peasy)
48 return CURLE_OUT_OF_MEMORY;
49
50 result = curl_easy_setopt(*peasy, CURLOPT_SHARE, share);
51 if(!result && (global->tracetype != TRACE_NONE)) {
52 my_setopt(*peasy, CURLOPT_DEBUGFUNCTION, tool_debug_cb);
53 my_setopt(*peasy, CURLOPT_DEBUGDATA, config);
54 my_setopt(*peasy, CURLOPT_VERBOSE, 1L);
55 }
56 return result;
57 }
58
tool_ssls_load(struct GlobalConfig * global,struct OperationConfig * config,CURLSH * share,const char * filename)59 CURLcode tool_ssls_load(struct GlobalConfig *global,
60 struct OperationConfig *config,
61 CURLSH *share, const char *filename)
62 {
63 FILE *fp;
64 CURL *easy = NULL;
65 struct dynbuf buf;
66 unsigned char *shmac = NULL, *sdata = NULL;
67 char *c, *line, *end;
68 size_t shmac_len, sdata_len;
69 CURLcode r = CURLE_OK;
70 int i, imported;
71
72 curlx_dyn_init(&buf, MAX_SSLS_LINE);
73 fp = fopen(filename, FOPEN_READTEXT);
74 if(!fp) { /* ok if it does not exist */
75 notef(global, "SSL session file does not exist (yet?): %s", filename);
76 goto out;
77 }
78
79 r = tool_ssls_easy(global, config, share, &easy);
80 if(r)
81 goto out;
82
83 i = imported = 0;
84 while(Curl_get_line(&buf, fp)) {
85 ++i;
86 curl_free(shmac);
87 curl_free(sdata);
88 line = Curl_dyn_ptr(&buf);
89 while(*line && ISBLANK(*line))
90 line++;
91 if(*line == '#')
92 /* skip commented lines */
93 continue;
94
95 c = memchr(line, ':', strlen(line));
96 if(!c) {
97 warnf(global, "unrecognized line %d in ssl session file %s",
98 i, filename);
99 continue;
100 }
101 *c = '\0';
102 r = curlx_base64_decode(line, &shmac, &shmac_len);
103 if(r) {
104 warnf(global, "invalid shmax base64 encoding in line %d", i);
105 continue;
106 }
107 line = c + 1;
108 end = line + strlen(line) - 1;
109 while((end > line) && (*end == '\n' || *end == '\r' || ISBLANK(*line))) {
110 *end = '\0';
111 --end;
112 }
113 r = curlx_base64_decode(line, &sdata, &sdata_len);
114 if(r) {
115 warnf(global, "invalid sdata base64 encoding in line %d: %s", i, line);
116 continue;
117 }
118
119 r = curl_easy_ssls_import(easy, NULL, shmac, shmac_len, sdata, sdata_len);
120 if(r) {
121 warnf(global, "import of session from line %d rejected(%d)", i, r);
122 continue;
123 }
124 ++imported;
125 }
126 r = CURLE_OK;
127
128 out:
129 if(easy)
130 curl_easy_cleanup(easy);
131 if(fp)
132 fclose(fp);
133 curlx_dyn_free(&buf);
134 curl_free(shmac);
135 curl_free(sdata);
136 return r;
137 }
138
139 struct tool_ssls_ctx {
140 struct GlobalConfig *global;
141 FILE *fp;
142 int exported;
143 };
144
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)145 static CURLcode tool_ssls_exp(CURL *easy, void *userptr,
146 const char *session_key,
147 const unsigned char *shmac, size_t shmac_len,
148 const unsigned char *sdata, size_t sdata_len,
149 curl_off_t valid_until, int ietf_tls_id,
150 const char *alpn, size_t earlydata_max)
151 {
152 struct tool_ssls_ctx *ctx = userptr;
153 char *enc = NULL;
154 size_t enc_len;
155 CURLcode r;
156
157 (void)easy;
158 (void)valid_until;
159 (void)ietf_tls_id;
160 (void)alpn;
161 (void)earlydata_max;
162 if(!ctx->exported)
163 fputs("# Your SSL session cache. https://curl.se/docs/ssl-sessions.html\n"
164 "# This file was generated by libcurl! Edit at your own risk.\n",
165 ctx->fp);
166
167 r = curlx_base64_encode((const char *)shmac, shmac_len, &enc, &enc_len);
168 if(r)
169 goto out;
170 r = CURLE_WRITE_ERROR;
171 if(enc_len != fwrite(enc, 1, enc_len, ctx->fp))
172 goto out;
173 if(EOF == fputc(':', ctx->fp))
174 goto out;
175 curl_free(enc);
176 r = curlx_base64_encode((const char *)sdata, sdata_len, &enc, &enc_len);
177 if(r)
178 goto out;
179 r = CURLE_WRITE_ERROR;
180 if(enc_len != fwrite(enc, 1, enc_len, ctx->fp))
181 goto out;
182 if(EOF == fputc('\n', ctx->fp))
183 goto out;
184 r = CURLE_OK;
185 ctx->exported++;
186 out:
187 if(r)
188 warnf(ctx->global, "Warning: error saving SSL session for '%s': %d",
189 session_key, r);
190 curl_free(enc);
191 return r;
192 }
193
tool_ssls_save(struct GlobalConfig * global,struct OperationConfig * config,CURLSH * share,const char * filename)194 CURLcode tool_ssls_save(struct GlobalConfig *global,
195 struct OperationConfig *config,
196 CURLSH *share, const char *filename)
197 {
198 struct tool_ssls_ctx ctx;
199 CURL *easy = NULL;
200 CURLcode r = CURLE_OK;
201
202 ctx.global = global;
203 ctx.exported = 0;
204 ctx.fp = fopen(filename, FOPEN_WRITETEXT);
205 if(!ctx.fp) {
206 warnf(global, "Warning: Failed to create SSL session file %s", filename);
207 goto out;
208 }
209
210 r = tool_ssls_easy(global, config, share, &easy);
211 if(r)
212 goto out;
213
214 r = curl_easy_ssls_export(easy, tool_ssls_exp, &ctx);
215
216 out:
217 if(easy)
218 curl_easy_cleanup(easy);
219 if(ctx.fp)
220 fclose(ctx.fp);
221 return r;
222 }
223