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