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