Home | History | Annotate | Download | only in lib
      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   base64data = output = malloc(insize * 4 / 3 + 4);
    194   if(!output)
    195     return CURLE_OUT_OF_MEMORY;
    196 
    197   /*
    198    * The base64 data needs to be created using the network encoding
    199    * not the host encoding.  And we can't change the actual input
    200    * so we copy it to a buffer, translate it, and use that instead.
    201    */
    202   result = Curl_convert_clone(data, indata, insize, &convbuf);
    203   if(result) {
    204     free(output);
    205     return result;
    206   }
    207 
    208   if(convbuf)
    209     indata = (char *)convbuf;
    210 
    211   while(insize > 0) {
    212     for(i = inputparts = 0; i < 3; i++) {
    213       if(insize > 0) {
    214         inputparts++;
    215         ibuf[i] = (unsigned char) *indata;
    216         indata++;
    217         insize--;
    218       }
    219       else
    220         ibuf[i] = 0;
    221     }
    222 
    223     obuf[0] = (unsigned char)  ((ibuf[0] & 0xFC) >> 2);
    224     obuf[1] = (unsigned char) (((ibuf[0] & 0x03) << 4) | \
    225                                ((ibuf[1] & 0xF0) >> 4));
    226     obuf[2] = (unsigned char) (((ibuf[1] & 0x0F) << 2) | \
    227                                ((ibuf[2] & 0xC0) >> 6));
    228     obuf[3] = (unsigned char)   (ibuf[2] & 0x3F);
    229 
    230     switch(inputparts) {
    231     case 1: /* only one byte read */
    232       snprintf(output, 5, "%c%c==",
    233                table64[obuf[0]],
    234                table64[obuf[1]]);
    235       break;
    236 
    237     case 2: /* two bytes read */
    238       snprintf(output, 5, "%c%c%c=",
    239                table64[obuf[0]],
    240                table64[obuf[1]],
    241                table64[obuf[2]]);
    242       break;
    243 
    244     default:
    245       snprintf(output, 5, "%c%c%c%c",
    246                table64[obuf[0]],
    247                table64[obuf[1]],
    248                table64[obuf[2]],
    249                table64[obuf[3]]);
    250       break;
    251     }
    252     output += 4;
    253   }
    254 
    255   /* Zero terminate */
    256   *output = '\0';
    257 
    258   /* Return the pointer to the new data (allocated memory) */
    259   *outptr = base64data;
    260 
    261   free(convbuf);
    262 
    263   /* Return the length of the new data */
    264   *outlen = strlen(base64data);
    265 
    266   return CURLE_OK;
    267 }
    268 
    269 /*
    270  * Curl_base64_encode()
    271  *
    272  * Given a pointer to an input buffer and an input size, encode it and
    273  * return a pointer in *outptr to a newly allocated memory area holding
    274  * encoded data. Size of encoded data is returned in variable pointed by
    275  * outlen.
    276  *
    277  * Input length of 0 indicates input buffer holds a NUL-terminated string.
    278  *
    279  * Returns CURLE_OK on success, otherwise specific error code. Function
    280  * output shall not be considered valid unless CURLE_OK is returned.
    281  *
    282  * When encoded data length is 0, returns NULL in *outptr.
    283  *
    284  * @unittest: 1302
    285  */
    286 CURLcode Curl_base64_encode(struct Curl_easy *data,
    287                             const char *inputbuff, size_t insize,
    288                             char **outptr, size_t *outlen)
    289 {
    290   return base64_encode(base64, data, inputbuff, insize, outptr, outlen);
    291 }
    292 
    293 /*
    294  * Curl_base64url_encode()
    295  *
    296  * Given a pointer to an input buffer and an input size, encode it and
    297  * return a pointer in *outptr to a newly allocated memory area holding
    298  * encoded data. Size of encoded data is returned in variable pointed by
    299  * outlen.
    300  *
    301  * Input length of 0 indicates input buffer holds a NUL-terminated string.
    302  *
    303  * Returns CURLE_OK on success, otherwise specific error code. Function
    304  * output shall not be considered valid unless CURLE_OK is returned.
    305  *
    306  * When encoded data length is 0, returns NULL in *outptr.
    307  *
    308  * @unittest: 1302
    309  */
    310 CURLcode Curl_base64url_encode(struct Curl_easy *data,
    311                                const char *inputbuff, size_t insize,
    312                                char **outptr, size_t *outlen)
    313 {
    314   return base64_encode(base64url, data, inputbuff, insize, outptr, outlen);
    315 }
    316