xref: /netbsd-src/external/bsd/pam-u2f/dist/fuzz/fuzz_format_parsers.c (revision d16b7486a53dcb8072b60ec6fcb4373a2d0c27b7)
1 /*
2  * Copyright (C) 2020 Yubico AB - See COPYING
3  */
4 #include <string.h>
5 #include <stdlib.h>
6 #include <stdint.h>
7 #include <stdio.h>
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <unistd.h>
11 
12 #include "fuzz/fuzz.h"
13 #include "util.c"
14 #include "b64.c"
15 
16 static void cleanup(device_t *devs, unsigned int n_devs) {
17   for (unsigned int i = 0; i < n_devs; i++) {
18     free(devs[i].keyHandle);
19     free(devs[i].publicKey);
20     free(devs[i].coseType);
21     free(devs[i].attributes);
22     devs[i].keyHandle = NULL;
23     devs[i].publicKey = NULL;
24     devs[i].coseType = NULL;
25     devs[i].attributes = NULL;
26   }
27 }
28 
29 #define DEV_MAX_SIZE 10
30 
31 int LLVMFuzzerTestOneInput(const uint8_t *, size_t);
32 int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
33   char buf[DEVSIZE * DEV_MAX_SIZE]; /* DEVSIZE * cfg.max_size */
34   device_t devs[12] = {0};
35   unsigned int n_devs = 12;
36   FILE *fp = NULL;
37   size_t fp_len = 0;
38   size_t offset = 0;
39   char username[256] = {0};
40   size_t username_len = 0;
41   uint8_t ssh_format = 1;
42   cfg_t cfg = {0};
43   cfg.max_devs = DEV_MAX_SIZE;
44 
45   /* first 6 byte decides which parser we should call, if
46    * we want to run with debug and also sets the initial seed */
47   if (size < 6) {
48     return -1;
49   }
50   /* do not always run with debug, only 8/255 times */
51   if (data[offset++] < 9) {
52     cfg.debug = 1;
53   }
54 
55   /* predictable random for this seed */
56   prng_init((uint32_t) data[offset] << 24 | (uint32_t) data[offset + 1] << 16 |
57             (uint32_t) data[offset + 2] << 8 | (uint32_t) data[offset + 3]);
58   offset += 4;
59 
60   /* choose which format parser to run, even == native, odd == ssh */
61   if (data[offset++] % 2) {
62     ssh_format = 0;
63     /* native format, get a random username first */
64     if (size < 7) {
65       return -1;
66     }
67     username_len = data[offset++];
68     if (username_len > (size - offset)) {
69       username_len = (size - offset);
70     }
71     memcpy(username, &data[offset], username_len);
72     offset += username_len;
73   }
74 
75   fp_len = size - offset;
76   fp = tmpfile();
77   if (fp == NULL || (fwrite(&data[offset], 1, fp_len, fp)) != fp_len) {
78     fprintf(stderr, "failed to create file for parser: %s\n", strerror(errno));
79     if (fp != NULL) {
80       fclose(fp);
81     }
82     return -1;
83   }
84   (void) fseek(fp, 0L, SEEK_SET);
85 
86   if (ssh_format) {
87     parse_ssh_format(&cfg, buf, sizeof(buf), fp, size, devs, &n_devs);
88   } else {
89     parse_native_format(&cfg, username, buf, fp, devs, &n_devs);
90   }
91 
92   cleanup(devs, n_devs);
93 
94   fclose(fp);
95 
96   return 0;
97 }
98