1/***************************************************************************
2 *                                  _   _ ____  _
3 *  Project                     ___| | | |  _ \| |
4 *                             / __| | | | |_) | |
5 *                            | (__| |_| |  _ <| |___
6 *                             \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 1998 - 2013, Daniel Stenberg, <daniel@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 http://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
27#define _MPRINTF_REPLACE /* use our functions only */
28#include <curl/mprintf.h>
29
30#include "urldata.h" /* for the SessionHandle definition */
31#include "warnless.h"
32#include "curl_base64.h"
33#include "curl_memory.h"
34#include "non-ascii.h"
35
36/* include memdebug.h last */
37#include "memdebug.h"
38
39/* ---- Base64 Encoding/Decoding Table --- */
40static const char table64[]=
41  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
42
43static size_t decodeQuantum(unsigned char *dest, const char *src)
44{
45  size_t padding = 0;
46  const char *s, *p;
47  unsigned long i, v, x = 0;
48
49  for(i = 0, s = src; i < 4; i++, s++) {
50    v = 0;
51
52    if(*s == '=') {
53      x = (x << 6);
54      padding++;
55    }
56    else {
57      p = table64;
58
59      while(*p && (*p != *s)) {
60        v++;
61        p++;
62      }
63
64      if(*p == *s)
65        x = (x << 6) + v;
66      else
67        return 0;
68    }
69  }
70
71  if(padding < 1)
72    dest[2] = curlx_ultouc(x & 0xFFUL);
73
74  x >>= 8;
75  if(padding < 2)
76    dest[1] = curlx_ultouc(x & 0xFFUL);
77
78  x >>= 8;
79  dest[0] = curlx_ultouc(x & 0xFFUL);
80
81  return 3 - padding;
82}
83
84/*
85 * Curl_base64_decode()
86 *
87 * Given a base64 NUL-terminated string at src, decode it and return a
88 * pointer in *outptr to a newly allocated memory area holding decoded
89 * data. Size of decoded data is returned in variable pointed by outlen.
90 *
91 * Returns CURLE_OK on success, otherwise specific error code. Function
92 * output shall not be considered valid unless CURLE_OK is returned.
93 *
94 * When decoded data length is 0, returns NULL in *outptr.
95 *
96 * @unittest: 1302
97 */
98CURLcode Curl_base64_decode(const char *src,
99                            unsigned char **outptr, size_t *outlen)
100{
101  size_t srclen = 0;
102  size_t length = 0;
103  size_t padding = 0;
104  size_t i;
105  size_t result;
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    result = decodeQuantum(pos, src);
150    if(!result) {
151      Curl_safefree(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/*
171 * Curl_base64_encode()
172 *
173 * Given a pointer to an input buffer and an input size, encode it and
174 * return a pointer in *outptr to a newly allocated memory area holding
175 * encoded data. Size of encoded data is returned in variable pointed by
176 * outlen.
177 *
178 * Input length of 0 indicates input buffer holds a NUL-terminated string.
179 *
180 * Returns CURLE_OK on success, otherwise specific error code. Function
181 * output shall not be considered valid unless CURLE_OK is returned.
182 *
183 * When encoded data length is 0, returns NULL in *outptr.
184 *
185 * @unittest: 1302
186 */
187CURLcode Curl_base64_encode(struct SessionHandle *data,
188                            const char *inputbuff, size_t insize,
189                            char **outptr, size_t *outlen)
190{
191  CURLcode error;
192  unsigned char ibuf[3];
193  unsigned char obuf[4];
194  int i;
195  int inputparts;
196  char *output;
197  char *base64data;
198  char *convbuf = NULL;
199
200  const char *indata = inputbuff;
201
202  *outptr = NULL;
203  *outlen = 0;
204
205  if(0 == insize)
206    insize = strlen(indata);
207
208  base64data = output = malloc(insize*4/3+4);
209  if(NULL == output)
210    return CURLE_OUT_OF_MEMORY;
211
212  /*
213   * The base64 data needs to be created using the network encoding
214   * not the host encoding.  And we can't change the actual input
215   * so we copy it to a buffer, translate it, and use that instead.
216   */
217  error = Curl_convert_clone(data, indata, insize, &convbuf);
218  if(error) {
219    free(output);
220    return error;
221  }
222
223  if(convbuf)
224    indata = (char *)convbuf;
225
226  while(insize > 0) {
227    for(i = inputparts = 0; i < 3; i++) {
228      if(insize > 0) {
229        inputparts++;
230        ibuf[i] = (unsigned char) *indata;
231        indata++;
232        insize--;
233      }
234      else
235        ibuf[i] = 0;
236    }
237
238    obuf[0] = (unsigned char)  ((ibuf[0] & 0xFC) >> 2);
239    obuf[1] = (unsigned char) (((ibuf[0] & 0x03) << 4) | \
240                               ((ibuf[1] & 0xF0) >> 4));
241    obuf[2] = (unsigned char) (((ibuf[1] & 0x0F) << 2) | \
242                               ((ibuf[2] & 0xC0) >> 6));
243    obuf[3] = (unsigned char)   (ibuf[2] & 0x3F);
244
245    switch(inputparts) {
246    case 1: /* only one byte read */
247      snprintf(output, 5, "%c%c==",
248               table64[obuf[0]],
249               table64[obuf[1]]);
250      break;
251    case 2: /* two bytes read */
252      snprintf(output, 5, "%c%c%c=",
253               table64[obuf[0]],
254               table64[obuf[1]],
255               table64[obuf[2]]);
256      break;
257    default:
258      snprintf(output, 5, "%c%c%c%c",
259               table64[obuf[0]],
260               table64[obuf[1]],
261               table64[obuf[2]],
262               table64[obuf[3]] );
263      break;
264    }
265    output += 4;
266  }
267  *output = '\0';
268  *outptr = base64data; /* return pointer to new data, allocated memory */
269
270  if(convbuf)
271    free(convbuf);
272
273  *outlen = strlen(base64data); /* return the length of the new data */
274
275  return CURLE_OK;
276}
277/* ---- End of Base64 Encoding ---- */
278