/src/xmlsec/apps/oss-fuzz/xmlsec_keyinfo_target.c
Line | Count | Source |
1 | | /* |
2 | | * xmlsec <dsig:KeyInfo /> reader fuzz target. |
3 | | * |
4 | | * xmlsec_target.c only parses XML (xmlSecParseMemory), and the dsig target |
5 | | * needs a whole well-formed signature before it reaches any key handling. This |
6 | | * target calls xmlSecKeyInfoNodeRead() on a <KeyInfo> element directly, which |
7 | | * is reachable from a very small document, so a mutator makes progress from the |
8 | | * first input. |
9 | | * |
10 | | * The code it drives is the largest cold area in the library: keysdata_helpers.c |
11 | | * (the KeyValue, X509Data and EncryptedKey structure readers), keyinfo.c, |
12 | | * x509_helpers.c and the OpenSSL key-data implementations. |
13 | | * |
14 | | * The keys manager is NULL on purpose. A manager only adds trusted-key lookup, |
15 | | * which needs key material this target does not supply, and it brings in |
16 | | * application-level initialisation that this target does not need. Structure |
17 | | * parsing, the part that reads attacker-supplied bytes, runs either way. |
18 | | * |
19 | | * External fetches are disabled, so the target stays offline. |
20 | | */ |
21 | | #include <stdint.h> |
22 | | #include <stddef.h> |
23 | | #include <limits.h> |
24 | | |
25 | | int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size); |
26 | | |
27 | | #include <libxml/parser.h> |
28 | | #include <libxml/tree.h> |
29 | | #include <libxml/xmlerror.h> |
30 | | |
31 | | #include <xmlsec/xmlsec.h> |
32 | | #include <xmlsec/xmltree.h> |
33 | | #include <xmlsec/keys.h> |
34 | | #include <xmlsec/keyinfo.h> |
35 | | #include <xmlsec/keysdata.h> |
36 | | #include <xmlsec/transforms.h> |
37 | | #include <xmlsec/errors.h> |
38 | | #include <xmlsec/strings.h> |
39 | | |
40 | | #include <xmlsec/openssl/app.h> |
41 | | #include <xmlsec/openssl/crypto.h> |
42 | | |
43 | | static int g_initialized = 0; |
44 | | /* Set when do_init() fails, so a failed one-time init is not retried on |
45 | | * every input. */ |
46 | | static int g_init_failed = 0; |
47 | | |
48 | 3.79M | static void ignore_error(void* ctx, const char* msg, ...) { |
49 | 3.79M | (void)ctx; (void)msg; |
50 | 3.79M | } |
51 | | |
52 | | static void ignore_xmlsec_error(const char* file, int line, const char* func, |
53 | | const char* errorObject, const char* errorSubject, |
54 | 98.1k | int reason, const char* msg) { |
55 | 98.1k | (void)file; (void)line; (void)func; |
56 | 98.1k | (void)errorObject; (void)errorSubject; (void)reason; (void)msg; |
57 | 98.1k | } |
58 | | |
59 | 1 | static int do_init(void) { |
60 | 1 | xmlInitParser(); |
61 | | |
62 | 1 | if (xmlSecInit() < 0) { |
63 | 0 | return -1; |
64 | 0 | } |
65 | 1 | if (xmlSecCheckVersion() != 1) { |
66 | 0 | return -1; |
67 | 0 | } |
68 | 1 | if (xmlSecOpenSSLAppInit(NULL) < 0) { |
69 | 0 | return -1; |
70 | 0 | } |
71 | 1 | if (xmlSecOpenSSLInit() < 0) { |
72 | 0 | return -1; |
73 | 0 | } |
74 | | |
75 | 1 | xmlSetGenericErrorFunc(NULL, &ignore_error); |
76 | 1 | xmlSecErrorsSetCallback(&ignore_xmlsec_error); |
77 | 1 | return 0; |
78 | 1 | } |
79 | | |
80 | 24.2k | int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { |
81 | 24.2k | xmlDocPtr doc = NULL; |
82 | 24.2k | xmlNodePtr root = NULL; |
83 | 24.2k | xmlNodePtr node = NULL; |
84 | 24.2k | xmlSecKeyPtr key = NULL; |
85 | 24.2k | xmlSecKeyInfoCtxPtr keyInfoCtx = NULL; |
86 | | |
87 | 24.2k | if (!g_initialized) { |
88 | 1 | g_init_failed = (do_init() < 0); |
89 | 1 | g_initialized = 1; |
90 | 1 | } |
91 | | /* Skip inputs that cannot be represented as the int length expected by |
92 | | * xmlReadMemory(). */ |
93 | 24.2k | if (g_init_failed || size == 0 || size > (size_t)INT_MAX) { |
94 | 0 | return 0; |
95 | 0 | } |
96 | | |
97 | | /* NONET and NOENT stop external fetches and entity expansion. */ |
98 | 24.2k | doc = xmlReadMemory((const char*)data, (int)size, "fuzz.xml", NULL, |
99 | 24.2k | XML_PARSE_NONET | XML_PARSE_NOENT); |
100 | 24.2k | if (doc == NULL) { |
101 | 17.0k | return 0; |
102 | 17.0k | } |
103 | 7.15k | root = xmlDocGetRootElement(doc); |
104 | 7.15k | if (root == NULL) { |
105 | 0 | xmlFreeDoc(doc); |
106 | 0 | return 0; |
107 | 0 | } |
108 | | |
109 | | /* Accept a bare <KeyInfo> document as well as one that contains it, so a |
110 | | * seed does not have to carry a signature wrapper. */ |
111 | 7.15k | if (xmlSecCheckNodeName(root, xmlSecNodeKeyInfo, xmlSecDSigNs)) { |
112 | 2.59k | node = root; |
113 | 4.55k | } else { |
114 | 4.55k | node = xmlSecFindNode(root, xmlSecNodeKeyInfo, xmlSecDSigNs); |
115 | 4.55k | } |
116 | 7.15k | if (node == NULL) { |
117 | 114 | xmlFreeDoc(doc); |
118 | 114 | return 0; |
119 | 114 | } |
120 | | |
121 | 7.03k | key = xmlSecKeyCreate(); |
122 | 7.03k | if (key == NULL) { |
123 | 0 | xmlFreeDoc(doc); |
124 | 0 | return 0; |
125 | 0 | } |
126 | | |
127 | 7.03k | keyInfoCtx = xmlSecKeyInfoCtxCreate(NULL); |
128 | 7.03k | if (keyInfoCtx == NULL) { |
129 | 0 | xmlSecKeyDestroy(key); |
130 | 0 | xmlFreeDoc(doc); |
131 | 0 | return 0; |
132 | 0 | } |
133 | | |
134 | 7.03k | keyInfoCtx->mode = xmlSecKeyInfoModeRead; |
135 | | /* Accept any key so the first successfully read child element satisfies |
136 | | * the requirement (the reader then stops, skipping later siblings). */ |
137 | 7.03k | keyInfoCtx->keyReq.keyId = xmlSecKeyDataIdUnknown; |
138 | 7.03k | keyInfoCtx->keyReq.keyType = xmlSecKeyDataTypeAny; |
139 | 7.03k | keyInfoCtx->keyReq.keyUsage = xmlSecKeyUsageAny; |
140 | | /* <RetrievalMethod> and <KeyInfoReference> must not fetch remote or local |
141 | | * data. This is the no-network guard. */ |
142 | 7.03k | keyInfoCtx->retrievalMethodCtx.enabledUris = |
143 | 7.03k | xmlSecTransformUriTypeEmpty | xmlSecTransformUriTypeSameDocument; |
144 | 7.03k | keyInfoCtx->keyInfoReferenceCtx.enabledUris = |
145 | 7.03k | xmlSecTransformUriTypeEmpty | xmlSecTransformUriTypeSameDocument; |
146 | | |
147 | | /* The return value does not matter. The parsing paths are the target. */ |
148 | 7.03k | (void)xmlSecKeyInfoNodeRead(node, key, keyInfoCtx); |
149 | | |
150 | 7.03k | xmlSecKeyInfoCtxDestroy(keyInfoCtx); |
151 | 7.03k | xmlSecKeyDestroy(key); |
152 | 7.03k | xmlFreeDoc(doc); |
153 | 7.03k | return 0; |
154 | 7.03k | } |