blob: make blob_parse_untrusted more permissive
[project/libubox.git] / tests / test-blob-parse.c
1 /*
2 * Based on certificate dump functionality from ucert.c:
3 *
4 * Copyright (C) 2018 Daniel Golle <daniel@makrotopia.org>
5 * SPDX-License-Identifier: GPL-3.0
6 *
7 */
8
9 #include <stdio.h>
10 #include <stdint.h>
11 #include <stddef.h>
12 #include <libgen.h>
13
14 #include "blob.h"
15 #include "list.h"
16 #include "blobmsg_json.h"
17
18 #define CERT_BUF_LEN 4096
19
20 /*
21 * ucert structure
22 * | BLOB |
23 * | SIGNATURE | PAYLOAD |
24 * | |[ BLOBMSG CONTAINER ]|
25 * | |[[T,i,v,e,f,pubkey ]]|
26 */
27 enum cert_attr {
28 CERT_ATTR_SIGNATURE,
29 CERT_ATTR_PAYLOAD,
30 CERT_ATTR_MAX
31 };
32
33 static const struct blob_attr_info cert_policy[CERT_ATTR_MAX] = {
34 [CERT_ATTR_SIGNATURE] = { .type = BLOB_ATTR_BINARY },
35 [CERT_ATTR_PAYLOAD] = { .type = BLOB_ATTR_NESTED },
36 };
37
38 enum cert_cont_attr {
39 CERT_CT_ATTR_PAYLOAD,
40 CERT_CT_ATTR_MAX
41 };
42
43 enum cert_payload_attr {
44 CERT_PL_ATTR_CERTTYPE,
45 CERT_PL_ATTR_CERTID,
46 CERT_PL_ATTR_VALIDFROMTIME,
47 CERT_PL_ATTR_EXPIRETIME,
48 CERT_PL_ATTR_PUBKEY,
49 CERT_PL_ATTR_KEY_FINGERPRINT,
50 CERT_PL_ATTR_MAX
51 };
52
53 enum certtype_id {
54 CERTTYPE_UNSPEC,
55 CERTTYPE_AUTH,
56 CERTTYPE_REVOKE
57 };
58
59 /* list to store certificate chain at runtime */
60 struct cert_object {
61 struct list_head list;
62 struct blob_attr *cert[CERT_ATTR_MAX];
63 };
64
65 static int cert_load(const char *certfile, struct list_head *chain)
66 {
67 FILE *f;
68 struct blob_attr *certtb[CERT_ATTR_MAX];
69 struct blob_attr *bufpt;
70 struct cert_object *cobj;
71 char *filebuf = NULL;
72 int ret = 0, pret = 0;
73 size_t len, pos = 0;
74
75 f = fopen(certfile, "r");
76 if (!f)
77 return 1;
78
79 filebuf = malloc(CERT_BUF_LEN+1);
80 if (!filebuf)
81 return 1;
82
83 len = fread(filebuf, 1, CERT_BUF_LEN, f);
84 if (len < 64) {
85 free(filebuf);
86 return 1;
87 }
88
89 ret = ferror(f) || !feof(f);
90 fclose(f);
91 if (ret) {
92 free(filebuf);
93 return 1;
94 }
95
96 bufpt = (struct blob_attr *)filebuf;
97 do {
98 pret = blob_parse_untrusted(bufpt, len, certtb, cert_policy, CERT_ATTR_MAX);
99 if (pret <= 0)
100 /* no attributes found */
101 break;
102
103 if (pos + blob_pad_len(bufpt) > len)
104 /* blob exceeds filebuffer */
105 break;
106 else
107 pos += blob_pad_len(bufpt);
108
109 if (!certtb[CERT_ATTR_SIGNATURE])
110 /* no signature -> drop */
111 break;
112
113 cobj = calloc(1, sizeof(*cobj));
114 cobj->cert[CERT_ATTR_SIGNATURE] = blob_memdup(certtb[CERT_ATTR_SIGNATURE]);
115 if (certtb[CERT_ATTR_PAYLOAD])
116 cobj->cert[CERT_ATTR_PAYLOAD] = blob_memdup(certtb[CERT_ATTR_PAYLOAD]);
117
118 list_add_tail(&cobj->list, chain);
119 ret += pret;
120 /* repeat parsing while there is still enough remaining data in buffer */
121 } while(len > pos + sizeof(struct blob_attr) && (bufpt = blob_next(bufpt)));
122
123 free(filebuf);
124 return (ret <= 0);
125 }
126
127 /* dump single chain element to console */
128 static void cert_dump_blob(struct blob_attr *cert[CERT_ATTR_MAX])
129 {
130 int i;
131 char *json = NULL;
132
133 for (i = 0; i < CERT_ATTR_MAX; i++) {
134 struct blob_attr *v = cert[i];
135
136 if (!v)
137 continue;
138
139 switch(cert_policy[i].type) {
140 case BLOB_ATTR_BINARY:
141 fprintf(stdout, "signature:\n---\n%s---\n", (char *) blob_data(v));
142 break;
143 case BLOB_ATTR_NESTED:
144 json = blobmsg_format_json_indent(blob_data(v), false, 0);
145 if (!json)
146 continue;
147
148 fprintf(stdout, "payload:\n---\n%s\n---\n", json);
149 free(json);
150 break;
151 }
152 }
153 }
154
155 static int cert_dump(const char *certfile)
156 {
157 struct cert_object *cobj;
158 static LIST_HEAD(certchain);
159 unsigned int count = 0;
160
161 if (cert_load(certfile, &certchain)) {
162 fprintf(stderr, "cannot parse cert %s\n", basename((char *) certfile));
163 return 1;
164 }
165
166 list_for_each_entry(cobj, &certchain, list) {
167 fprintf(stdout, "=== CHAIN ELEMENT %02u ===\n", ++count);
168 cert_dump_blob(cobj->cert);
169 }
170
171 return 0;
172 }
173
174 int main(int argc, char *argv[])
175 {
176 if (argc != 2) {
177 fprintf(stderr, "Usage: %s <cert.ucert>\n", argv[0]);
178 return 3;
179 }
180
181 cert_dump(argv[1]);
182
183 return 0;
184 }