1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 1998 - 2016, 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 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 } 321