/src/barebox/common/state/backend_format_raw.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2012-2014 Pengutronix, Jan Luebbe <j.luebbe@pengutronix.de> |
3 | | * Copyright (C) 2013-2014 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de> |
4 | | * Copyright (C) 2015 Pengutronix, Marc Kleine-Budde <mkl@pengutronix.de> |
5 | | * Copyright (C) 2016 Pengutronix, Markus Pargmann <mpa@pengutronix.de> |
6 | | * |
7 | | * This program is free software; you can redistribute it and/or |
8 | | * modify it under the terms of the GNU General Public License |
9 | | * version 2, as published by the Free Software Foundation. |
10 | | * |
11 | | * This program is distributed in the hope that it will be useful, |
12 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | * GNU General Public License for more details. |
15 | | * |
16 | | */ |
17 | | |
18 | | #include <common.h> |
19 | | #include <crypto/keystore.h> |
20 | | #include <fuzz.h> |
21 | | #include <digest.h> |
22 | | #include <linux/kernel.h> |
23 | | #include <malloc.h> |
24 | | #include <crc.h> |
25 | | #include <of.h> |
26 | | |
27 | | #include "state.h" |
28 | | |
29 | | struct state_backend_format_raw { |
30 | | struct state_backend_format format; |
31 | | |
32 | | struct digest *digest; |
33 | | unsigned int digest_length; |
34 | | |
35 | | /* For outputs */ |
36 | | struct device *dev; |
37 | | |
38 | | char *secret_name; |
39 | | int needs_secret; |
40 | | char *algo; |
41 | | }; |
42 | | |
43 | | struct __attribute__((__packed__)) backend_raw_header { |
44 | | uint32_t magic; |
45 | | uint16_t reserved; |
46 | | uint16_t data_len; |
47 | | uint32_t data_crc; |
48 | | uint32_t header_crc; |
49 | | }; |
50 | | |
51 | | const int format_raw_min_length = sizeof(struct backend_raw_header); |
52 | | |
53 | | static inline struct state_backend_format_raw *get_format_raw( |
54 | | struct state_backend_format *format) |
55 | 384 | { |
56 | 384 | return container_of(format, struct state_backend_format_raw, format); |
57 | 384 | } |
58 | | |
59 | | static int backend_raw_digest_init(struct state_backend_format_raw *raw) |
60 | 93 | { |
61 | 93 | const unsigned char *key; |
62 | 93 | int key_len; |
63 | 93 | int ret; |
64 | | |
65 | 93 | if (!raw->digest) { |
66 | 1 | raw->digest = digest_alloc(raw->algo); |
67 | 1 | if (!raw->digest) { |
68 | 0 | dev_err(raw->dev, "algo %s not found\n", |
69 | 0 | raw->algo); |
70 | 0 | return -ENODEV; |
71 | 0 | } |
72 | 1 | raw->digest_length = digest_length(raw->digest); |
73 | 1 | } |
74 | | |
75 | 93 | ret = keystore_get_secret(raw->secret_name, &key, &key_len); |
76 | 93 | if (ret) { |
77 | 0 | dev_err(raw->dev, "Could not get secret '%s'\n", |
78 | 0 | raw->secret_name); |
79 | 0 | return ret; |
80 | 0 | } |
81 | | |
82 | 93 | ret = digest_set_key(raw->digest, key, key_len); |
83 | 93 | if (ret) |
84 | 0 | return ret; |
85 | | |
86 | 93 | ret = digest_init(raw->digest); |
87 | 93 | if (ret) { |
88 | 0 | dev_err(raw->dev, "Failed to initialize digest: %pe\n", |
89 | 0 | ERR_PTR(ret)); |
90 | 0 | return ret; |
91 | 0 | } |
92 | | |
93 | 93 | return 0; |
94 | 93 | } |
95 | | |
96 | | static int backend_format_raw_verify(struct state_backend_format *format, |
97 | | uint32_t magic, const void * buf, |
98 | | ssize_t *lenp, enum state_flags flags) |
99 | 358 | { |
100 | 358 | uint32_t crc; |
101 | 358 | struct backend_raw_header *header; |
102 | 358 | int d_len = 0; |
103 | 358 | int ret; |
104 | 358 | const void *data; |
105 | 358 | ssize_t len = *lenp; |
106 | 358 | struct state_backend_format_raw *backend_raw = get_format_raw(format); |
107 | 358 | ssize_t complete_len; |
108 | | |
109 | 358 | if (len < format_raw_min_length) { |
110 | 12 | dev_err(backend_raw->dev, "Error, buffer length (%zd) is shorter than the minimum required header length\n", |
111 | 12 | len); |
112 | 12 | return -EINVAL; |
113 | 12 | } |
114 | | |
115 | 346 | header = (struct backend_raw_header *)buf; |
116 | 346 | crc = crc32(0, header, sizeof(*header) - sizeof(uint32_t)); |
117 | 346 | if (crc != header->header_crc && |
118 | 267 | !fuzz_insecure_checksum_accepted(crc, header->header_crc)) |
119 | 65 | return dev_err_state_init(backend_raw->dev, |
120 | 346 | header->header_crc ? -EINVAL : -ENOMEDIUM, |
121 | 346 | "header crc in raw format, calculated 0x%08x, found 0x%08x\n", |
122 | 346 | crc, header->header_crc); |
123 | | |
124 | 281 | if (magic && magic != header->magic) { |
125 | 0 | dev_err(backend_raw->dev, "Error, invalid magic in raw format 0x%08x, should be 0x%08x\n", |
126 | 0 | header->magic, magic); |
127 | 0 | return -EINVAL; |
128 | 0 | } |
129 | | |
130 | 281 | if (backend_raw->algo && !(flags & STATE_FLAG_NO_AUTHENTICATION)) { |
131 | 93 | ret = backend_raw_digest_init(backend_raw); |
132 | 93 | if (ret) |
133 | 0 | return ret; |
134 | | |
135 | 93 | d_len = digest_length(backend_raw->digest); |
136 | 93 | } |
137 | | |
138 | 281 | complete_len = header->data_len + d_len + format_raw_min_length; |
139 | 281 | if (complete_len > len) { |
140 | 37 | dev_err(backend_raw->dev, "Error, invalid data_len %u in header, have data of len %zu\n", |
141 | 37 | header->data_len, len); |
142 | 37 | return -EINVAL; |
143 | 37 | } |
144 | | |
145 | 244 | data = buf + sizeof(*header); |
146 | | |
147 | 244 | crc = crc32(0, data, header->data_len); |
148 | 244 | if (crc != header->data_crc && |
149 | 159 | !fuzz_insecure_checksum_accepted(crc, header->data_crc)) { |
150 | 36 | dev_err(backend_raw->dev, "invalid data crc, calculated 0x%08x, found 0x%08x\n", |
151 | 36 | crc, header->data_crc); |
152 | 36 | return -EINVAL; |
153 | 36 | } |
154 | | |
155 | 208 | *lenp = header->data_len + sizeof(*header); |
156 | | |
157 | 208 | if (backend_raw->algo && !(flags & STATE_FLAG_NO_AUTHENTICATION)) { |
158 | 61 | const void *hmac = data + header->data_len; |
159 | | |
160 | | /* hmac over header and data */ |
161 | 61 | ret = digest_update(backend_raw->digest, buf, sizeof(*header) + header->data_len); |
162 | 61 | if (ret) { |
163 | 0 | dev_err(backend_raw->dev, "Failed to update digest, %d\n", |
164 | 0 | ret); |
165 | 0 | return ret; |
166 | 0 | } |
167 | | |
168 | 61 | ret = digest_verify(backend_raw->digest, hmac); |
169 | 61 | if (ret < 0) { |
170 | 31 | dev_err(backend_raw->dev, "Failed to verify data, hmac, %d\n", |
171 | 31 | ret); |
172 | 31 | return ret; |
173 | 31 | } |
174 | 61 | } |
175 | | |
176 | 177 | return 0; |
177 | 208 | } |
178 | | |
179 | | static int backend_format_raw_unpack(struct state_backend_format *format, |
180 | | struct state *state, const void * buf, |
181 | | ssize_t len) |
182 | 26 | { |
183 | 26 | struct state_variable *sv; |
184 | 26 | const struct backend_raw_header *header; |
185 | 26 | const void *data; |
186 | 26 | struct state_backend_format_raw *backend_raw = get_format_raw(format); |
187 | 26 | int ret = 0; |
188 | | |
189 | 26 | header = (const struct backend_raw_header *)buf; |
190 | 26 | data = buf + sizeof(*header); |
191 | | |
192 | 52 | list_for_each_entry(sv, &state->variables, list) { |
193 | 52 | if (sv->start + sv->size > header->data_len) { |
194 | 0 | dev_err(backend_raw->dev, "State variable ends behind valid data, %s\n", |
195 | 0 | sv->name); |
196 | 0 | ret = -ENOSPC; |
197 | 0 | continue; |
198 | 0 | } |
199 | 52 | memcpy(sv->raw, data + sv->start, sv->size); |
200 | 52 | } |
201 | | |
202 | 26 | return ret; |
203 | 26 | } |
204 | | |
205 | | static inline size_t state_data_size(struct state *state) |
206 | 0 | { |
207 | 0 | const struct state_variable *sv; |
208 | | |
209 | | /* Make use of the fact that the list is sorted in ascending order */ |
210 | 0 | sv = list_last_entry_or_null(&state->variables, struct state_variable, list); |
211 | 0 | if (!sv) |
212 | 0 | return 0; |
213 | | |
214 | 0 | return sv->start + sv->size; |
215 | 0 | } |
216 | | |
217 | | static int backend_format_raw_pack(struct state_backend_format *format, |
218 | | struct state *state, void ** buf_out, |
219 | | ssize_t * len_out) |
220 | 0 | { |
221 | 0 | struct state_backend_format_raw *backend_raw = get_format_raw(format); |
222 | 0 | void *buf, *data, *hmac; |
223 | 0 | struct backend_raw_header *header; |
224 | 0 | struct state_variable *sv; |
225 | 0 | unsigned int size_full; |
226 | 0 | unsigned int size_data; |
227 | 0 | int ret; |
228 | |
|
229 | 0 | if (backend_raw->algo) { |
230 | 0 | ret = backend_raw_digest_init(backend_raw); |
231 | 0 | if (ret) |
232 | 0 | return ret; |
233 | 0 | } |
234 | | |
235 | 0 | size_data = state_data_size(state); |
236 | |
|
237 | 0 | size_full = size_data + sizeof(*header) + backend_raw->digest_length; |
238 | |
|
239 | 0 | buf = xzalloc(size_full); |
240 | |
|
241 | 0 | header = buf; |
242 | 0 | data = buf + sizeof(*header); |
243 | 0 | hmac = data + size_data; |
244 | |
|
245 | 0 | list_for_each_entry(sv, &state->variables, list) |
246 | 0 | memcpy(data + sv->start, sv->raw, sv->size); |
247 | |
|
248 | 0 | header->magic = state->magic; |
249 | 0 | header->data_len = size_data; |
250 | 0 | header->data_crc = crc32(0, data, size_data); |
251 | 0 | header->header_crc = crc32(0, header, |
252 | 0 | sizeof(*header) - sizeof(uint32_t)); |
253 | |
|
254 | 0 | if (backend_raw->algo) { |
255 | | /* hmac over header and data */ |
256 | 0 | ret = digest_update(backend_raw->digest, buf, sizeof(*header) + size_data); |
257 | 0 | if (ret) { |
258 | 0 | dev_err(backend_raw->dev, "Failed to update digest for packing, %d\n", |
259 | 0 | ret); |
260 | 0 | goto out_free; |
261 | 0 | } |
262 | | |
263 | 0 | ret = digest_final(backend_raw->digest, hmac); |
264 | 0 | if (ret < 0) { |
265 | 0 | dev_err(backend_raw->dev, "Failed to finish digest for packing, %d\n", |
266 | 0 | ret); |
267 | 0 | goto out_free; |
268 | 0 | } |
269 | 0 | } |
270 | | |
271 | 0 | *buf_out = buf; |
272 | 0 | *len_out = size_full; |
273 | |
|
274 | 0 | return 0; |
275 | | |
276 | 0 | out_free: |
277 | 0 | free(buf); |
278 | |
|
279 | 0 | return ret; |
280 | 0 | } |
281 | | |
282 | | static void backend_format_raw_free(struct state_backend_format *format) |
283 | 0 | { |
284 | 0 | struct state_backend_format_raw *backend_raw = get_format_raw(format); |
285 | |
|
286 | 0 | free(backend_raw); |
287 | 0 | } |
288 | | |
289 | | static int backend_format_raw_init_digest(struct state_backend_format_raw *raw, |
290 | | struct device_node *root, |
291 | | const char *secret_name) |
292 | 30 | { |
293 | 30 | struct property *p; |
294 | 30 | const char *algo; |
295 | 30 | int ret; |
296 | | |
297 | 30 | p = of_find_property(root, "algo", NULL); |
298 | 30 | if (!p) /* does not exist */ |
299 | 28 | return 0; |
300 | | |
301 | 2 | ret = of_property_read_string(root, "algo", &algo); |
302 | 2 | if (ret) |
303 | 0 | return ret; |
304 | | |
305 | 2 | if (!IS_ENABLED(CONFIG_STATE_CRYPTO) && IS_ENABLED(__BAREBOX__)) { |
306 | 0 | dev_err(raw->dev, "algo %s specified, but crypto support for state framework (CONFIG_STATE_CRYPTO) not enabled.\n", |
307 | 0 | algo); |
308 | 0 | return -EINVAL; |
309 | 0 | } |
310 | | |
311 | 2 | raw->algo = xstrdup(algo); |
312 | | |
313 | 2 | return 0; |
314 | 2 | } |
315 | | |
316 | | int backend_format_raw_create(struct state_backend_format **format, |
317 | | struct device_node *node, const char *secret_name, |
318 | | struct device *dev) |
319 | 30 | { |
320 | 30 | struct state_backend_format_raw *raw; |
321 | 30 | int ret; |
322 | | |
323 | 30 | raw = xzalloc(sizeof(*raw)); |
324 | | |
325 | 30 | raw->dev = dev; |
326 | 30 | ret = backend_format_raw_init_digest(raw, node, secret_name); |
327 | 30 | if (ret) { |
328 | 0 | dev_err(raw->dev, "Failed initializing digest for raw format, %d\n", |
329 | 0 | ret); |
330 | 0 | free(raw); |
331 | 0 | return ret; |
332 | 0 | } |
333 | | |
334 | 30 | raw->secret_name = xstrdup(secret_name); |
335 | 30 | raw->format.pack = backend_format_raw_pack; |
336 | 30 | raw->format.unpack = backend_format_raw_unpack; |
337 | 30 | raw->format.verify = backend_format_raw_verify; |
338 | 30 | raw->format.free = backend_format_raw_free; |
339 | 30 | raw->format.name = "raw"; |
340 | 30 | *format = &raw->format; |
341 | | |
342 | 30 | return 0; |
343 | 30 | } |
344 | | |
345 | | struct digest *state_backend_format_raw_get_digest(struct state_backend_format |
346 | | *format) |
347 | 0 | { |
348 | 0 | struct state_backend_format_raw *backend_raw = get_format_raw(format); |
349 | |
|
350 | 0 | return backend_raw->digest; |
351 | 0 | } |