1 | /*************************************************************************** |
2 | * _ _ ____ _ |
3 | * Project ___| | | | _ \| | |
4 | * / __| | | | |_) | | |
5 | * | (__| |_| | _ <| |___ |
6 | * \___|\___/|_| \_\_____| |
7 | * |
8 | * Copyright (C) 1998 - 2020, 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 | ***************************************************************************/ |
22 | |
23 | /* Base64 encoding/decoding */ |
24 | |
25 | #include "curl_setup.h" |
26 | |
27 | #if !defined(CURL_DISABLE_HTTP_AUTH) || defined(USE_SSH) || \ |
28 | !defined(CURL_DISABLE_LDAP) || \ |
29 | !defined(CURL_DISABLE_SMTP) || \ |
30 | !defined(CURL_DISABLE_POP3) || \ |
31 | !defined(CURL_DISABLE_IMAP) || \ |
32 | !defined(CURL_DISABLE_DOH) || defined(USE_SSL) |
33 | |
34 | #include "urldata.h" /* for the Curl_easy definition */ |
35 | #include "warnless.h" |
36 | #include "curl_base64.h" |
37 | #include "non-ascii.h" |
38 | |
39 | /* The last 3 #include files should be in this order */ |
40 | #include "curl_printf.h" |
41 | #include "curl_memory.h" |
42 | #include "memdebug.h" |
43 | |
44 | /* ---- Base64 Encoding/Decoding Table --- */ |
45 | static const char base64[]= |
46 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ; |
47 | |
48 | /* The Base 64 encoding with an URL and filename safe alphabet, RFC 4648 |
49 | section 5 */ |
50 | static const char base64url[]= |
51 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" ; |
52 | |
53 | static size_t decodeQuantum(unsigned char *dest, const char *src) |
54 | { |
55 | size_t padding = 0; |
56 | const char *s, *p; |
57 | unsigned long i, x = 0; |
58 | |
59 | for(i = 0, s = src; i < 4; i++, s++) { |
60 | if(*s == '=') { |
61 | x = (x << 6); |
62 | padding++; |
63 | } |
64 | else { |
65 | unsigned long v = 0; |
66 | p = base64; |
67 | |
68 | while(*p && (*p != *s)) { |
69 | v++; |
70 | p++; |
71 | } |
72 | |
73 | if(*p == *s) |
74 | x = (x << 6) + v; |
75 | else |
76 | return 0; |
77 | } |
78 | } |
79 | |
80 | if(padding < 1) |
81 | dest[2] = curlx_ultouc(x & 0xFFUL); |
82 | |
83 | x >>= 8; |
84 | if(padding < 2) |
85 | dest[1] = curlx_ultouc(x & 0xFFUL); |
86 | |
87 | x >>= 8; |
88 | dest[0] = curlx_ultouc(x & 0xFFUL); |
89 | |
90 | return 3 - padding; |
91 | } |
92 | |
93 | /* |
94 | * Curl_base64_decode() |
95 | * |
96 | * Given a base64 NUL-terminated string at src, decode it and return a |
97 | * pointer in *outptr to a newly allocated memory area holding decoded |
98 | * data. Size of decoded data is returned in variable pointed by outlen. |
99 | * |
100 | * Returns CURLE_OK on success, otherwise specific error code. Function |
101 | * output shall not be considered valid unless CURLE_OK is returned. |
102 | * |
103 | * When decoded data length is 0, returns NULL in *outptr. |
104 | * |
105 | * @unittest: 1302 |
106 | */ |
107 | CURLcode Curl_base64_decode(const char *src, |
108 | unsigned char **outptr, size_t *outlen) |
109 | { |
110 | size_t srclen = 0; |
111 | size_t length = 0; |
112 | size_t padding = 0; |
113 | size_t i; |
114 | size_t numQuantums; |
115 | size_t rawlen = 0; |
116 | unsigned char *pos; |
117 | unsigned char *newstr; |
118 | |
119 | *outptr = NULL; |
120 | *outlen = 0; |
121 | srclen = strlen(src); |
122 | |
123 | /* Check the length of the input string is valid */ |
124 | if(!srclen || srclen % 4) |
125 | return CURLE_BAD_CONTENT_ENCODING; |
126 | |
127 | /* Find the position of any = padding characters */ |
128 | while((src[length] != '=') && src[length]) |
129 | length++; |
130 | |
131 | /* A maximum of two = padding characters is allowed */ |
132 | if(src[length] == '=') { |
133 | padding++; |
134 | if(src[length + 1] == '=') |
135 | padding++; |
136 | } |
137 | |
138 | /* Check the = padding characters weren't part way through the input */ |
139 | if(length + padding != srclen) |
140 | return CURLE_BAD_CONTENT_ENCODING; |
141 | |
142 | /* Calculate the number of quantums */ |
143 | numQuantums = srclen / 4; |
144 | |
145 | /* Calculate the size of the decoded string */ |
146 | rawlen = (numQuantums * 3) - padding; |
147 | |
148 | /* Allocate our buffer including room for a zero terminator */ |
149 | newstr = malloc(rawlen + 1); |
150 | if(!newstr) |
151 | return CURLE_OUT_OF_MEMORY; |
152 | |
153 | pos = newstr; |
154 | |
155 | /* Decode the quantums */ |
156 | for(i = 0; i < numQuantums; i++) { |
157 | size_t result = decodeQuantum(pos, src); |
158 | if(!result) { |
159 | free(newstr); |
160 | |
161 | return CURLE_BAD_CONTENT_ENCODING; |
162 | } |
163 | |
164 | pos += result; |
165 | src += 4; |
166 | } |
167 | |
168 | /* Zero terminate */ |
169 | *pos = '\0'; |
170 | |
171 | /* Return the decoded data */ |
172 | *outptr = newstr; |
173 | *outlen = rawlen; |
174 | |
175 | return CURLE_OK; |
176 | } |
177 | |
178 | static CURLcode base64_encode(const char *table64, |
179 | struct Curl_easy *data, |
180 | const char *inputbuff, size_t insize, |
181 | char **outptr, size_t *outlen) |
182 | { |
183 | CURLcode result; |
184 | unsigned char ibuf[3]; |
185 | unsigned char obuf[4]; |
186 | int i; |
187 | int inputparts; |
188 | char *output; |
189 | char *base64data; |
190 | char *convbuf = NULL; |
191 | |
192 | const char *indata = inputbuff; |
193 | |
194 | *outptr = NULL; |
195 | *outlen = 0; |
196 | |
197 | if(!insize) |
198 | insize = strlen(indata); |
199 | |
200 | #if SIZEOF_SIZE_T == 4 |
201 | if(insize > UINT_MAX/4) |
202 | return CURLE_OUT_OF_MEMORY; |
203 | #endif |
204 | |
205 | base64data = output = malloc(insize * 4 / 3 + 4); |
206 | if(!output) |
207 | return CURLE_OUT_OF_MEMORY; |
208 | |
209 | /* |
210 | * The base64 data needs to be created using the network encoding |
211 | * not the host encoding. And we can't change the actual input |
212 | * so we copy it to a buffer, translate it, and use that instead. |
213 | */ |
214 | result = Curl_convert_clone(data, indata, insize, &convbuf); |
215 | if(result) { |
216 | free(output); |
217 | return result; |
218 | } |
219 | |
220 | if(convbuf) |
221 | indata = (char *)convbuf; |
222 | |
223 | while(insize > 0) { |
224 | for(i = inputparts = 0; i < 3; i++) { |
225 | if(insize > 0) { |
226 | inputparts++; |
227 | ibuf[i] = (unsigned char) *indata; |
228 | indata++; |
229 | insize--; |
230 | } |
231 | else |
232 | ibuf[i] = 0; |
233 | } |
234 | |
235 | obuf[0] = (unsigned char) ((ibuf[0] & 0xFC) >> 2); |
236 | obuf[1] = (unsigned char) (((ibuf[0] & 0x03) << 4) | \ |
237 | ((ibuf[1] & 0xF0) >> 4)); |
238 | obuf[2] = (unsigned char) (((ibuf[1] & 0x0F) << 2) | \ |
239 | ((ibuf[2] & 0xC0) >> 6)); |
240 | obuf[3] = (unsigned char) (ibuf[2] & 0x3F); |
241 | |
242 | switch(inputparts) { |
243 | case 1: /* only one byte read */ |
244 | msnprintf(output, 5, "%c%c==" , |
245 | table64[obuf[0]], |
246 | table64[obuf[1]]); |
247 | break; |
248 | |
249 | case 2: /* two bytes read */ |
250 | msnprintf(output, 5, "%c%c%c=" , |
251 | table64[obuf[0]], |
252 | table64[obuf[1]], |
253 | table64[obuf[2]]); |
254 | break; |
255 | |
256 | default: |
257 | msnprintf(output, 5, "%c%c%c%c" , |
258 | table64[obuf[0]], |
259 | table64[obuf[1]], |
260 | table64[obuf[2]], |
261 | table64[obuf[3]]); |
262 | break; |
263 | } |
264 | output += 4; |
265 | } |
266 | |
267 | /* Zero terminate */ |
268 | *output = '\0'; |
269 | |
270 | /* Return the pointer to the new data (allocated memory) */ |
271 | *outptr = base64data; |
272 | |
273 | free(convbuf); |
274 | |
275 | /* Return the length of the new data */ |
276 | *outlen = strlen(base64data); |
277 | |
278 | return CURLE_OK; |
279 | } |
280 | |
281 | /* |
282 | * Curl_base64_encode() |
283 | * |
284 | * Given a pointer to an input buffer and an input size, encode it and |
285 | * return a pointer in *outptr to a newly allocated memory area holding |
286 | * encoded data. Size of encoded data is returned in variable pointed by |
287 | * outlen. |
288 | * |
289 | * Input length of 0 indicates input buffer holds a NUL-terminated string. |
290 | * |
291 | * Returns CURLE_OK on success, otherwise specific error code. Function |
292 | * output shall not be considered valid unless CURLE_OK is returned. |
293 | * |
294 | * When encoded data length is 0, returns NULL in *outptr. |
295 | * |
296 | * @unittest: 1302 |
297 | */ |
298 | CURLcode Curl_base64_encode(struct Curl_easy *data, |
299 | const char *inputbuff, size_t insize, |
300 | char **outptr, size_t *outlen) |
301 | { |
302 | return base64_encode(base64, data, inputbuff, insize, outptr, outlen); |
303 | } |
304 | |
305 | /* |
306 | * Curl_base64url_encode() |
307 | * |
308 | * Given a pointer to an input buffer and an input size, encode it and |
309 | * return a pointer in *outptr to a newly allocated memory area holding |
310 | * encoded data. Size of encoded data is returned in variable pointed by |
311 | * outlen. |
312 | * |
313 | * Input length of 0 indicates input buffer holds a NUL-terminated string. |
314 | * |
315 | * Returns CURLE_OK on success, otherwise specific error code. Function |
316 | * output shall not be considered valid unless CURLE_OK is returned. |
317 | * |
318 | * When encoded data length is 0, returns NULL in *outptr. |
319 | * |
320 | * @unittest: 1302 |
321 | */ |
322 | CURLcode Curl_base64url_encode(struct Curl_easy *data, |
323 | const char *inputbuff, size_t insize, |
324 | char **outptr, size_t *outlen) |
325 | { |
326 | return base64_encode(base64url, data, inputbuff, insize, outptr, outlen); |
327 | } |
328 | |
329 | #endif /* no users so disabled */ |
330 | |