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 #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