1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 Kern Sibbald
5 
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8 
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13 
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16 
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *   Generic base 64 input and output routines
21  *
22  *    Written by Kern E. Sibbald, March MM.
23  */
24 
25 
26 #include "bacula.h"
27 
28 
29 #ifdef TEST_MODE
30 #include <glob.h>
31 #endif
32 
33 
34 static uint8_t const base64_digits[64] =
35 {
36   'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
37   'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
38   'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
39   'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
40   '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
41 };
42 
43 static int base64_inited = 0;
44 static uint8_t base64_map[256];
45 
46 
47 /* Initialize the Base 64 conversion routines */
48 void
base64_init(void)49 base64_init(void)
50 {
51    int i;
52    memset(base64_map, 0, sizeof(base64_map));
53    for (i=0; i<64; i++)
54       base64_map[(uint8_t)base64_digits[i]] = i;
55    base64_inited = 1;
56 }
57 
58 /* Convert a value to base64 characters.
59  * The result is stored in where, which
60  * must be at least 8 characters long.
61  *
62  * Returns the number of characters
63  * stored (not including the EOS).
64  */
65 int
to_base64(int64_t value,char * where)66 to_base64(int64_t value, char *where)
67 {
68    uint64_t val;
69    int i = 0;
70    int n;
71 
72    /* Handle negative values */
73    if (value < 0) {
74       where[i++] = '-';
75       value = -value;
76    }
77 
78    /* Determine output size */
79    val = value;
80    do {
81       val >>= 6;
82       i++;
83    } while (val);
84    n = i;
85 
86    /* Output characters */
87    val = value;
88    where[i] = 0;
89    do {
90       where[--i] = base64_digits[val & (uint64_t)0x3F];
91       val >>= 6;
92    } while (val);
93    return n;
94 }
95 
96 /*
97  * Convert the Base 64 characters in where to
98  * a value. No checking is done on the validity
99  * of the characters!!
100  *
101  * Returns the value.
102  */
103 int
from_base64(int64_t * value,char * where)104 from_base64(int64_t *value, char *where)
105 {
106    uint64_t val = 0;
107    int i, neg;
108 
109    if (!base64_inited)
110       base64_init();
111    /* Check if it is negative */
112    i = neg = 0;
113    if (where[i] == '-') {
114       i++;
115       neg = 1;
116    }
117    /* Construct value */
118    while (where[i] != 0 && where[i] != ' ') {
119       val <<= 6;
120       val += base64_map[(uint8_t)where[i++]];
121    }
122 
123    *value = neg ? -(int64_t)val : (int64_t)val;
124    return i;
125 }
126 
127 
128 /*
129  * Encode binary data in bin of len bytes into
130  * buf as base64 characters.
131  *
132  * If compatible is true, the bin_to_base64 routine will be compatible
133  * with what the rest of the world uses.
134  *
135  *  Returns: the number of characters stored not
136  *           including the EOS
137  */
138 int
bin_to_base64(char * buf,int buflen,char * bin,int binlen,int compatible)139 bin_to_base64(char *buf, int buflen, char *bin, int binlen, int compatible)
140 {
141    uint32_t reg, save, mask;
142    int rem, i;
143    int j = 0;
144 
145    reg = 0;
146    rem = 0;
147    buflen--;                       /* allow for storing EOS */
148    for (i=0; i < binlen; ) {
149       if (rem < 6) {
150          reg <<= 8;
151          if (compatible) {
152             reg |= (uint8_t)bin[i++];
153          } else {
154             reg |= (int8_t)bin[i++];
155          }
156          rem += 8;
157       }
158       save = reg;
159       reg >>= (rem - 6);
160       if (j < buflen) {
161          buf[j++] = base64_digits[reg & 0x3F];
162       }
163       reg = save;
164       rem -= 6;
165    }
166    if (rem && j < buflen) {
167       mask = (1 << rem) - 1;
168       if (compatible) {
169          buf[j++] = base64_digits[(reg & mask) << (6 - rem)];
170       } else {
171          buf[j++] = base64_digits[reg & mask];
172       }
173    }
174    buf[j] = 0;
175    return j;
176 }
177 
178 /*
179  * Decode base64 data in bin of len bytes into
180  * buf as binary characters.
181  *
182  * the base64_to_bin routine is compatible with what the rest of the world
183  * uses.
184  *
185  * 'dest_size' must be big enough! Giving the right size here could fail as
186  * we consider 'srclen' as an unpadded size, even if 'src' is padded
187  * we suggest to use dest_size=srclen for easiness or at least
188  * dest_size=((srclen + 3) / 4) * 3) for optimization lovers
189  *
190  *  Returns: the number of characters stored not
191  *           including the EOS
192  */
base64_to_bin(char * dest,int dest_size,char * src,int srclen)193 int base64_to_bin(char *dest, int dest_size, char *src, int srclen)
194 {
195    int nprbytes;
196    uint8_t *bufout;
197    uint8_t *bufplain = (uint8_t*) dest;
198    const uint8_t *bufin;
199 
200    if (!base64_inited)
201       base64_init();
202 
203    if (dest_size < (((srclen + 3) / 4) * 3)) {
204       /* dest buffer too small */
205       *dest = 0;
206       return 0;
207    }
208 
209    bufin = (const uint8_t *) src;
210    while ((*bufin != ' ') && (srclen != 0)) {
211       bufin++;
212       srclen--;
213    }
214 
215    nprbytes = bufin - (const uint8_t *) src;
216    bufin = (const uint8_t *) src;
217    bufout = (uint8_t *) bufplain;
218 
219    while (nprbytes > 4)
220    {
221       *(bufout++) = (base64_map[bufin[0]] << 2 | base64_map[bufin[1]] >> 4);
222       *(bufout++) = (base64_map[bufin[1]] << 4 | base64_map[bufin[2]] >> 2);
223       *(bufout++) = (base64_map[bufin[2]] << 6 | base64_map[bufin[3]]);
224       bufin += 4;
225       nprbytes -= 4;
226    }
227 
228    /* Bacula base64 strings are not always padded with = */
229    if (nprbytes > 1) {
230       *(bufout++) = (base64_map[bufin[0]] << 2 | base64_map[bufin[1]] >> 4);
231    }
232    if (nprbytes > 2) {
233       *(bufout++) = (base64_map[bufin[1]] << 4 | base64_map[bufin[2]] >> 2);
234    }
235    if (nprbytes > 3) {
236       *(bufout++) = (base64_map[bufin[2]] << 6 | base64_map[bufin[3]]);
237    }
238    *bufout = 0;
239 
240    return (bufout - (uint8_t *) dest);
241 }
242 
243 #ifdef BIN_TEST
main(int argc,char * argv[])244 int main(int argc, char *argv[])
245 {
246    int len;
247    char buf[100];
248    char junk[100];
249    int i;
250 
251 #ifdef xxxx
252    int xx = 0;
253    for (i=0; i < 1000; i++) {
254       bin_to_base64(buf, sizeof(buf), (char *)&xx, 4, true);
255       printf("xx=%s\n", buf);
256       xx++;
257    }
258 #endif
259    junk[0] = 0xFF;
260    for (i=1; i<100; i++) {
261       junk[i] = junk[i-1]-1;
262    }
263    len = bin_to_base64(buf, sizeof(buf), junk, 16, true);
264    printf("len=%d junk=%s\n", len, buf);
265 
266    strcpy(junk, "This is a sample stringa");
267    len = bin_to_base64(buf, sizeof(buf), junk, strlen(junk), true);
268    buf[len] = 0;
269    base64_to_bin(junk, sizeof(junk), buf, len);
270    printf("buf=<%s>\n", junk);
271    return 0;
272 }
273 #endif
274 
275 #ifdef TEST_MODE
errfunc(const char * epath,int eernoo)276 static int errfunc(const char *epath, int eernoo)
277 {
278   printf("in errfunc\n");
279   return 1;
280 }
281 
282 
283 /*
284  * Test the base64 routines by encoding and decoding
285  * lstat() packets.
286  */
main(int argc,char * argv[])287 int main(int argc, char *argv[])
288 {
289    char where[500];
290    int i;
291    glob_t my_glob;
292    char *fname;
293    struct stat statp;
294    struct stat statn;
295    int debug_level = 0;
296    char *p;
297    int32_t j;
298    time_t t = 1028712799;
299 
300    if (argc > 1 && strcmp(argv[1], "-v") == 0)
301       debug_level++;
302 
303    base64_init();
304 
305    my_glob.gl_offs = 0;
306    glob("/etc/grub.conf", GLOB_MARK, errfunc, &my_glob);
307 
308    for (i=0; my_glob.gl_pathv[i]; i++) {
309       fname = my_glob.gl_pathv[i];
310       if (lstat(fname, &statp) < 0) {
311          berrno be;
312          printf("Cannot stat %s: %s\n", fname, be.bstrerror(errno));
313          continue;
314       }
315       encode_stat(where, &statp, sizeof(statp), 0, 0);
316 
317       printf("Encoded stat=%s\n", where);
318 
319 #ifdef xxx
320       p = where;
321       p += to_base64((int64_t)(statp.st_atime), p);
322       *p++ = ' ';
323       p += to_base64((int64_t)t, p);
324       printf("%s %s\n", fname, where);
325 
326       printf("%s %lld\n", "st_dev", (int64_t)statp.st_dev);
327       printf("%s %lld\n", "st_ino", (int64_t)statp.st_ino);
328       printf("%s %lld\n", "st_mode", (int64_t)statp.st_mode);
329       printf("%s %lld\n", "st_nlink", (int64_t)statp.st_nlink);
330       printf("%s %lld\n", "st_uid", (int64_t)statp.st_uid);
331       printf("%s %lld\n", "st_gid", (int64_t)statp.st_gid);
332       printf("%s %lld\n", "st_rdev", (int64_t)statp.st_rdev);
333       printf("%s %lld\n", "st_size", (int64_t)statp.st_size);
334       printf("%s %lld\n", "st_blksize", (int64_t)statp.st_blksize);
335       printf("%s %lld\n", "st_blocks", (int64_t)statp.st_blocks);
336       printf("%s %lld\n", "st_atime", (int64_t)statp.st_atime);
337       printf("%s %lld\n", "st_mtime", (int64_t)statp.st_mtime);
338       printf("%s %lld\n", "st_ctime", (int64_t)statp.st_ctime);
339 #endif
340 
341       if (debug_level)
342          printf("%s: len=%d val=%s\n", fname, strlen(where), where);
343 
344       decode_stat(where, &statn, sizeof(statn), &j);
345 
346       if (statp.st_dev != statn.st_dev ||
347           statp.st_ino != statn.st_ino ||
348           statp.st_mode != statn.st_mode ||
349           statp.st_nlink != statn.st_nlink ||
350           statp.st_uid != statn.st_uid ||
351           statp.st_gid != statn.st_gid ||
352           statp.st_rdev != statn.st_rdev ||
353           statp.st_size != statn.st_size ||
354           statp.st_blksize != statn.st_blksize ||
355           statp.st_blocks != statn.st_blocks ||
356           statp.st_atime != statn.st_atime ||
357           statp.st_mtime != statn.st_mtime ||
358           statp.st_ctime != statn.st_ctime) {
359 
360          printf("%s: %s\n", fname, where);
361          encode_stat(where, &statn, sizeof(statn), 0, 0);
362          printf("%s: %s\n", fname, where);
363          printf("NOT EQAL\n");
364       }
365 
366    }
367    globfree(&my_glob);
368 
369    printf("%d files examined\n", i);
370 
371    to_base64(UINT32_MAX, where);
372    printf("UINT32_MAX=%s\n", where);
373 
374    return 0;
375 }
376 #endif
377 
378 #ifndef TEST_PROGRAM
379 #define TEST_PROGRAM_A
380 #endif
381 
382 #ifdef TEST_PROGRAM
383 #include "unittests.h"
384 
385 static const unsigned char rnddata[16] = {
386    0xa5, 0x7d, 0xa3, 0xc4, 0x2c, 0xa0, 0x08, 0xe9, 0x32, 0xb9, 0xc7, 0x84, 0xf6, 0xd3, 0xdf, 0x4f
387 };
388 static const char *resb16 = "pX2jxCygCOkyuceE9tPfTw";
389 static const char *resb8 = "q83v7c";
390 #define VARREF    0xABCDEFEDC
391 
main()392 int main()
393 {
394    Unittests base64_test("base64_test");
395    char buf[30];
396    char binbuf[30];
397    uint len;
398    bool check_cont;
399    int64_t var;
400 
401    base64_init();
402 /*
403    for (int a=0; a < 16; a++){
404       fprintf(stderr, "%c", rnddata[a]);
405    }
406 */
407    /* encode reference binary data to base64 */
408    len = bin_to_base64(buf, 30, (char*)rnddata, 16, true);
409    ok(len == strlen(resb16), "Checking bin_to_base64 encoded length");
410    ok(strcmp(resb16, buf) == 0, "Checking bin_to_base64 encoded data");
411    /* decode reference base64 data to bin*/
412    len = base64_to_bin(binbuf, 30, (char*)resb16, strlen(resb16));
413    ok(len == 16, "Checking base64_to_bin decoded length");
414    check_cont = true;
415    for (uint a = 0; a < len; a++){
416       if ((unsigned char)binbuf[a] != rnddata[a]){
417          check_cont = false;
418       }
419    }
420    ok(check_cont, "Checking base64_to_bin decoded data");
421    /* decode the encoded base64 data to bin */
422    len = base64_to_bin(binbuf, 30, buf, strlen(buf));
423    ok(len == 16, "Checking base64_to_bin decoded length - encoded");
424    check_cont = true;
425    for (uint a = 0; a < len; a++){
426       if ((unsigned char)binbuf[a] != rnddata[a]){
427          check_cont = false;
428       }
429    }
430    ok(check_cont, "Checking base64_to_bin decoded data - encoded");
431    /* encode reference variable to base64 */
432    len = to_base64(VARREF, buf);
433    ok(len == 6, "Checking to_base64 encode length");
434    ok(strcmp(resb8, buf) == 0, "Checking to_base64 encoded data");
435    /* decode reference data to bin */
436    len = from_base64(&var, (char*)resb8);
437    ok(var == VARREF, "Checking from_base64 decoded data");
438    ok(len == 6, "Checking from_base64 decoded length");
439    /* decode encoded data to bin */
440    len = from_base64(&var, buf);
441    ok(var == VARREF, "Checking from_base64 decoded data - encoded");
442    ok(len == 6, "Checking from_base64 decoded length - encoded");
443    return report();
444 };
445 #endif /* TEST_PROGRAM */
446