Home | History | Annotate | Download | only in lib
      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