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