1 /*
2 * Copyright 2017 Frank Hunleth
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "cfgfile.h"
18 #include "functions.h"
19 #include "requirement.h"
20 #include "mbr.h"
21 #include "gpt.h"
22 #include "uboot_env.h"
23 #include "util.h"
24 #include "archive_open.h"
25 #include "eval_math.h"
min(size_t a,size_t b)26 #include "3rdparty/semver.c/semver.h"
27 #include "monocypher-ed25519.h"
28
29 #include <stdlib.h>
30 #include <string.h>
31 #include <archive.h>
32 #include <archive_entry.h>
33
34 // Global variable for passing the top level cfg pointer through libconfuse
35 // This is needed for validating some of the function calls.
36 static cfg_t *toplevel_cfg;
37
38 /* function callback
39 */
40 static int cb_func(enum fun_context_type ctype, cfg_t *cfg, cfg_opt_t *opt, int argc, const char **argv)
41 {
42 struct fun_context fctx;
43 memset(&fctx, 0, sizeof(fctx));
44 fctx.type = ctype;
45 fctx.cfg = toplevel_cfg;
46 fctx.task = cfg;
47
48 // Convert to the normal argc/argv
is_trimmed(struct block_cache * bc,off_t offset)49 fctx.argc = argc + 1;
50 if (fctx.argc > FUN_MAX_ARGS || fctx.argc < 1) {
51 cfg_error(cfg, "Too many arguments passed to '%s'", opt->name);
52 return -1;
53 }
54
55 fctx.argv[0] = opt->name;
56 memcpy(&fctx.argv[1], argv, sizeof(const char *) * argc);
57
58 if (fun_validate(&fctx) < 0) {
59 cfg_error(cfg, last_error());
60 return -1;
clear_trimmed(struct block_cache * bc,off_t offset)61 }
62
63 char str_argc[5];
64 snprintf(str_argc, sizeof(str_argc), "%d", fctx.argc);
65
66 cfg_addlist(cfg, "funlist", 2, str_argc, fctx.argv[0]);
67 int i;
68 for (i = 1; i < fctx.argc; i++)
69 cfg_addlist(cfg, "funlist", 1, fctx.argv[i]);
70
71 return 0;
72 }
73
74 static int cb_on_init_func(cfg_t *cfg, cfg_opt_t *opt, int argc, const char **argv)
75 {
76 return cb_func(FUN_CONTEXT_INIT, cfg, opt, argc, argv);
77 }
78
79 static int cb_on_finish_func(cfg_t *cfg, cfg_opt_t *opt, int argc, const char **argv)
80 {
81 return cb_func(FUN_CONTEXT_FINISH, cfg, opt, argc, argv);
set_dirty(struct block_cache_segment * seg,int block)82 }
83
84 static int cb_on_error_func(cfg_t *cfg, cfg_opt_t *opt, int argc, const char **argv)
85 {
86 return cb_func(FUN_CONTEXT_ERROR, cfg, opt, argc, argv);
87 }
clear_all_dirty(struct block_cache_segment * seg)88
89 static int cb_on_resource_func(cfg_t *cfg, cfg_opt_t *opt, int argc, const char **argv)
90 {
91 return cb_func(FUN_CONTEXT_FILE, cfg, opt, argc, argv);
92 }
is_segment_dirty(struct block_cache_segment * seg)93
94 /* requirement callback
95 */
96 static int cb_task_require_func(cfg_t *cfg, cfg_opt_t *opt, int argc, const char **argv)
97 {
98 struct fun_context fctx;
99 memset(&fctx, 0, sizeof(fctx));
100 fctx.cfg = toplevel_cfg;
101 fctx.task = cfg;
102
103 // Convert to the normal argc/argv
104 fctx.argc = argc + 1;
105 if (fctx.argc > FUN_MAX_ARGS || fctx.argc < 1) {
106 cfg_error(cfg, "Too many arguments passed to '%s'", opt->name);
107 return -1;
108 }
109
110 fctx.argv[0] = opt->name;
111 memcpy(&fctx.argv[1], argv, sizeof(const char *) * argc);
112
113 if (req_validate(&fctx) < 0) {
114 cfg_error(cfg, last_error());
115 return -1;
116 }
117
118 char str_argc[5];
119 snprintf(str_argc, sizeof(str_argc), "%d", fctx.argc);
120
121 cfg_addlist(cfg, "reqlist", 2, str_argc, fctx.argv[0]);
122 int i;
123 for (i = 1; i < fctx.argc; i++)
124 cfg_addlist(cfg, "reqlist", 1, fctx.argv[i]);
125
126 return 0;
127 }
128
129 static int check_param_count(cfg_t *cfg, cfg_opt_t *opt, int argc, int required)
set_all_valid(struct block_cache_segment * seg)130 {
131 if (argc != required) {
132 cfg_error(cfg, "'%s' requires %d parameters",
133 opt->name, required);
134 return -1;
135 }
136 return 0;
137 }
138
139 static int define_helper(cfg_t *cfg, const char *key, const char *value, bool override)
init_segment(struct block_cache * bc,off_t offset,struct block_cache_segment * seg)140 {
141 INFO("Defining '%s'='%s'", key, value);
142
143 if (override || !get_environment(key)) {
144 if (set_environment(key, value) < 0) {
145 cfg_error(cfg, "set_environment failed");
146 return -1;
147 }
148 } else {
149 INFO("Not defining '%s'. Already set to '%s'", key, getenv(key));
150 }
151 return 0;
152 }
153
154 static int cb_define_bang(cfg_t *cfg, cfg_opt_t *opt, int argc, const char **argv)
read_segment(struct block_cache * bc,struct block_cache_segment * seg,void * data)155 {
156 if (check_param_count(cfg, opt, argc, 2) < 0 ||
157 define_helper(cfg, argv[0], argv[1], true) < 0)
158 return -1;
159 else
160 return 0;
161 }
162
163 static int cb_define(cfg_t *cfg, cfg_opt_t *opt, int argc, const char **argv)
164 {
165 if (check_param_count(cfg, opt, argc, 2) < 0 ||
166 define_helper(cfg, argv[0], argv[1], false) < 0)
167 return -1;
168 else
169 return 0;
170 }
171
172 static int cb_define_eval(cfg_t *cfg, cfg_opt_t *opt, int argc, const char **argv)
173 {
174 if (check_param_count(cfg, opt, argc, 2) < 0)
make_segment_valid(struct block_cache * bc,struct block_cache_segment * seg)175 return -1;
176
177 char result_str[24]; // Large enough to hold 64 bit int
178 if (eval_math_str(argv[1], result_str, sizeof(result_str)) < 0) {
179 cfg_error(cfg, "error evaluating '%s'", argv[1]);
180 return -1;
181 }
182
183 return define_helper(cfg, argv[0], result_str, false);
184 }
185
186 static int cb_define_eval_bang(cfg_t *cfg, cfg_opt_t *opt, int argc, const char **argv)
187 {
188 // Overriding version of define_eval
189 if (check_param_count(cfg, opt, argc, 2) < 0)
190 return -1;
191
192 char result_str[24]; // Large enough to hold 64 bit int
193 if (eval_math_str(argv[1], result_str, sizeof(result_str)) < 0) {
194 cfg_error(cfg, "error evaluating '%s'", argv[1]);
195 return -1;
196 }
197
198 return define_helper(cfg, argv[0], result_str, true);
199 }
verified_segment_write(struct block_cache * bc,volatile struct block_cache_segment * seg,uint8_t * temp)200
201 static int cb_include(cfg_t *cfg, cfg_opt_t *opt, int argc, const char **argv)
202 {
203 if (check_param_count(cfg, opt, argc, 1) < 0)
204 return -1;
205
206 const char *path = argv[0];
207 if (path[0] == '\0') {
208 cfg_error(cfg, "Cannot include the contents of an undefined variable");
209 return -1;
210 }
211
212 char *updated_path;
213 update_relative_path(cfg->filename, path, &updated_path);
214
215 // The default cfg_include gives lexer errors for some bad inputs. E.g.,
216 // specifying a directory. Check the file here to give better error
217 // messages.
218 if (!file_exists(updated_path)) {
219 cfg_error(cfg, "Cannot include '%s': File not found", path);
220 return -1;
221 }
222
223 if (!is_regular_file(updated_path)) {
224 cfg_error(cfg, "Cannot include '%s': Not a regular file", path);
225 return -1;
226 }
227
228 const char *newargv[2];
229 newargv[0] = updated_path;
230 newargv[1] = NULL;
231
232 int rc = cfg_include(cfg, opt, 1, newargv);
233 free(updated_path);
234
235 return rc;
236 }
237
238 // libconfuse calls the validators every time an item is added
239 // to a section. The idiom is to validate the most recently
240 // added item each time. This one is also the last one in the list.
241 // It is a coding error if cfg_opt_getnsec returns error, so errors
242 // aren't checked. (They segfault.)
243 #define MOST_RECENTLY_ADDED_SECTION(opt) \
244 cfg_opt_getnsec(opt, cfg_opt_size(opt) - 1)
245
check_async_error(struct block_cache * bc)246 // Automatically add environment variables when we validate a file resource
247 //
248 // Currently we generate:
249 // FWUP_SIZE_<resource_name> - The size of the resource
250 static int add_file_resource_variables(const char *path, cfg_t *sec)
251 {
252 int rc = 0;
253
254 // FWUP_SIZE_<resource_name>
255 if (path) {
256 // Create the Variable Name
257 // strlen("FWUP_SIZE_") == 10
258 size_t key_len = strlen(cfg_title(sec)) + 10 + 1;
259 char *key = (char *) malloc(key_len);
260
261 snprintf(key, key_len, "FWUP_SIZE_%s", cfg_title(sec));
262 for (size_t i = 0; i < key_len; i++) {
263 switch (key[i]) {
264 case '-':
265 case '/':
266 case '.':
267 key[i] = '_';
268 break;
269 default:
270 break;
271 }
272 }
273
274 struct stat st;
275
276 // Special files may not have length
277 // Only return an error if we fail to set the variable
278 // after we know its size
279 if (stat(path, &st) == 0) {
280 char size_str[24]; // Large enough to hold 64 bit int
281 snprintf(size_str, 24, "%" PRId64, (int64_t) st.st_size);
282
283 INFO("Defining '%s'='%s'", key, size_str);
284
285 if (set_environment(key, size_str) < 0)
286 rc = -1;
287 }
288 free(key);
289 }
290
291 return rc;
292 }
293
294 static int cb_validate_require_fwup_version(cfg_t *cfg, cfg_opt_t *opt)
295 {
296 semver_t current_version;
297 if (semver_parse(VERSION, ¤t_version) < 0)
298 fwup_errx(EXIT_FAILURE, "Invalid fwup version: " VERSION);
299
300 semver_t version_req;
do_sync_write(struct block_cache * bc,struct block_cache_segment * seg)301 const char *version_req_str = cfg_opt_getnstr(opt, 0);
302 if (semver_parse(version_req_str, &version_req)) {
303 cfg_error(cfg, "Invalid version '%s' passed to require-fwup-version", version_req_str);
304 semver_free(¤t_version);
do_async_write(struct block_cache * bc,struct block_cache_segment * seg)305 return -1;
306 }
307
308 int rc;
wait_for_write_completion(struct block_cache * bc,struct block_cache_segment * seg)309 if (semver_lte(version_req, current_version)) {
310 // Required version is less than or equal to this version, so ok.
311 rc = 0;
312 } else {
313 cfg_error(cfg, "fwup version '%s' required. This is fwup version '%s'.", version_req_str, VERSION);
314 rc = -1;
315 }
316
flush_segment(struct block_cache * bc,struct block_cache_segment * seg)317 semver_free(¤t_version);
318 semver_free(&version_req);
319 return rc;
320 }
321
322 static int cb_validate_file_resource(cfg_t *cfg, cfg_opt_t *opt)
323 {
324 cfg_t *sec = MOST_RECENTLY_ADDED_SECTION(opt);
325 const char *path = cfg_getstr(sec, "host-path");
326 const char *contents = cfg_getstr(sec, "contents");
327 if (!path && !contents) {
328 cfg_error(cfg, "host-path or contents must be set for file-resource '%s'", cfg_title(sec));
329 return -1;
330 }
331 if (path && contents) {
332 cfg_error(cfg, "only one of host-path or contents should be set for file-resource '%s'", cfg_title(sec));
333 return -1;
334 }
335
336 return add_file_resource_variables(path, sec);
337 }
338
339 static int cb_validate_mbr(cfg_t *cfg, cfg_opt_t *opt)
340 {
341 cfg_t *sec = MOST_RECENTLY_ADDED_SECTION(opt);
342
343 const char *path = cfg_getstr(sec, "bootstrap-code-host-path");
344 const char *bootstrap_hex = cfg_getstr(sec, "bootstrap-code");
345 if (path && !bootstrap_hex) {
346 FILE *fp = fopen(path, "rb");
347 if (!fp) {
block_cache_init(struct block_cache * bc,int fd,off_t end_offset,bool enable_trim,bool verify_writes)348 cfg_error(cfg, "mbr bootstrap code path '%s' required, but can't be read", path);
349 return -1;
350 }
351
352 uint8_t bootstrap[440];
353 if (fread(bootstrap, 1, sizeof(bootstrap), fp) != sizeof(bootstrap)) {
354 cfg_error(cfg, "mbr bootstrap code in '%s' should be %d bytes", path, sizeof(bootstrap));
355 fclose(fp);
356 return -1;
357 }
358 fclose(fp);
359
360 char bootstrap_str[sizeof(bootstrap) * 2 + 1];
361 bytes_to_hex(bootstrap, bootstrap_str, sizeof(bootstrap));
362 cfg_setstr(sec, "bootstrap-code", bootstrap_str);
363 }
364 if (mbr_verify_cfg(sec) < 0) {
365 cfg_error(cfg, last_error());
366 return -1;
367 }
368
369 return 0;
370 }
371
372 static int cb_validate_gpt(cfg_t *cfg, cfg_opt_t *opt)
373 {
374 cfg_t *sec = MOST_RECENTLY_ADDED_SECTION(opt);
375
376 if (gpt_verify_cfg(sec) < 0) {
377 cfg_error(cfg, last_error());
378 return -1;
379 }
380
381 return 0;
382 }
383
384 static int cb_validate_uboot(cfg_t *cfg, cfg_opt_t *opt)
385 {
386 cfg_t *sec = MOST_RECENTLY_ADDED_SECTION(opt);
387
388 if (uboot_env_verify_cfg(sec) < 0) {
389 cfg_error(cfg, last_error());
390 return -1;
391 }
392
393 return 0;
394 }
395
396 static int cb_validate_on_resource(cfg_t *cfg, cfg_opt_t *opt)
397 {
398 cfg_t *sec = MOST_RECENTLY_ADDED_SECTION(opt);
399
400 const char *resource_name = cfg_title(sec);
401
402 cfg_t *resource;
403 unsigned int i = 0;
404 while ((resource = cfg_getnsec(toplevel_cfg, "file-resource", i)) != NULL) {
405 if (strcmp(cfg_title(resource), resource_name) == 0)
406 return 0;
407 i++;
408 }
lrucompare(const void * pa,const void * pb)409
410 cfg_error(cfg, "unknown resource: %s", resource_name);
411 return -1;
412 }
413
414 // libconfuse v3.0 ignore unknown options
415 #ifdef CFGF_IGNORE_UNKNOWN
416 #define CFG_IGNORE_UNKNOWN \
417 CFG_STR("__unknown", 0, CFGF_NONE),
418 #else
419 #define CFG_IGNORE_UNKNOWN
420 #endif
421
422 static cfg_opt_t file_resource_opts[] = {
423 CFG_FUNC("include", &cb_include),
424 CFG_STR("host-path", 0, CFGF_NONE),
block_cache_reset(struct block_cache * bc)425 CFG_BOOL("skip-holes", cfg_false, CFGF_NONE),
426
427 #if (SIZEOF_INT == 4 && SIZEOF_OFF_T > 4)
428 // If we're on a 32-bit machine that has large file offsets,
429 // use "doubles" to store offsets in the config file to get
430 // more bits. This enables support of updates well past
431 // gigabytes. Search for "length" (with quotes) to verify
432 // that the other uses of the parameter are ok.
433 CFG_FLOAT_LIST("length", 0, CFGF_NONE),
434 #else
435 CFG_INT_LIST("length", 0, CFGF_NONE),
436 #endif
437 CFG_STR("contents", 0, CFGF_NONE),
438 CFG_STR("blake2b-256", 0, CFGF_NONE),
439 CFG_STR("sha256", 0, CFGF_NONE), // Old hash for files - use blake2b-256 now
440 CFG_INT("assert-size-lte", -1, CFGF_NONE),
441 CFG_INT("assert-size-gte", -1, CFGF_NONE),
442 CFG_IGNORE_UNKNOWN
443 CFG_END()
444 };
445 static cfg_opt_t mbr_partition_opts[] = {
446 CFG_STR("block-offset", 0, CFGF_NONE), // Special case: use a string to support unsigned 32-bit offsets on 32-bit machines
447 CFG_INT("block-count", INT32_MAX, CFGF_NONE),
448 CFG_INT("type", -1, CFGF_NONE),
449 CFG_BOOL("boot", cfg_false, CFGF_NONE),
450 CFG_BOOL("expand", cfg_false, CFGF_NONE),
451 CFG_IGNORE_UNKNOWN
452 CFG_END()
453 };
454 static cfg_opt_t mbr_osii_opts[] = {
455 CFG_INT("os-major", 0, CFGF_NONE),
456 CFG_INT("os-minor", 0, CFGF_NONE),
457 CFG_INT("start-block-offset", 0, CFGF_NONE),
458 CFG_INT("ddr-load-address", 0, CFGF_NONE),
459 CFG_INT("entry-point", 0, CFGF_NONE),
460 CFG_INT("image-size-blocks", 0, CFGF_NONE),
461 CFG_INT("attribute", 0xf, CFGF_NONE),
462 CFG_IGNORE_UNKNOWN
463 CFG_END()
464 };
465 static cfg_opt_t mbr_opts[] = {
466 CFG_STR("bootstrap-code-host-path", 0, CFGF_NONE),
467 CFG_STR("bootstrap-code", 0, CFGF_NONE),
468 CFG_BOOL("include-osip", cfg_false, CFGF_NONE),
469 CFG_INT("osip-major", 1, CFGF_NONE),
470 CFG_INT("osip-minor", 0, CFGF_NONE),
471 CFG_INT("osip-num-pointers", 1, CFGF_NONE),
472 CFG_STR("signature", 0, CFGF_NONE), // Special case: use a string to support unsigned 32-bit offsets on 32-bit machines
473 CFG_SEC("partition", mbr_partition_opts, CFGF_MULTI | CFGF_TITLE | CFGF_NO_TITLE_DUPES),
474 CFG_SEC("osii", mbr_osii_opts, CFGF_MULTI | CFGF_TITLE | CFGF_NO_TITLE_DUPES),
475 CFG_IGNORE_UNKNOWN
476 CFG_END()
477 };
478 static cfg_opt_t gpt_partition_opts[] = {
479 CFG_STR("block-offset", 0, CFGF_NONE), // Special case: use a string to support unsigned 32-bit offsets on 32-bit machines
480 CFG_INT("block-count", INT32_MAX, CFGF_NONE),
481 CFG_STR("type", 0, CFGF_NONE), // UUID
482 CFG_STR("guid", 0, CFGF_NONE), // UUID
483 CFG_STR("name", "", CFGF_NONE),
484 CFG_STR("flags", "0", CFGF_NONE), // Special case: use a string to support unsigned 64-bit number
block_cache_free(struct block_cache * bc)485 CFG_BOOL("expand", cfg_false, CFGF_NONE),
486 CFG_BOOL("boot", cfg_false, CFGF_NONE),
487 CFG_IGNORE_UNKNOWN
488 CFG_END()
489 };
490 static cfg_opt_t gpt_opts[] = {
491 CFG_STR("guid", 0, CFGF_NONE), // UUID
492 CFG_SEC("partition", gpt_partition_opts, CFGF_MULTI | CFGF_TITLE | CFGF_NO_TITLE_DUPES),
493 CFG_IGNORE_UNKNOWN
494 CFG_END()
495 };
496 static cfg_opt_t uboot_environment_opts[] = {
497 CFG_INT("block-offset", -1, CFGF_NONE),
498 CFG_INT("block-count", INT32_MAX, CFGF_NONE),
499 CFG_INT("block-offset-redund", -1, CFGF_NONE),
500 CFG_IGNORE_UNKNOWN
501 CFG_END()
502 };
503
504 #define CFG_ON_EVENT_FUNCTIONS(CB) \
505 CFG_STR_LIST("funlist", 0, CFGF_NONE), \
506 CFG_FUNC("include", &cb_include), \
507 CFG_FUNC("raw_memset", CB), \
508 CFG_FUNC("raw_write", CB), \
509 CFG_FUNC("fat_mkfs", CB), \
510 CFG_FUNC("fat_attrib", CB), \
511 CFG_FUNC("fat_write", CB), \
512 CFG_FUNC("fat_cp", CB), \
513 CFG_FUNC("fat_mv", CB), \
514 CFG_FUNC("fat_mv!", CB), \
515 CFG_FUNC("fat_rm", CB), \
516 CFG_FUNC("fat_rm!", CB), \
517 CFG_FUNC("fat_mkdir", CB), \
518 CFG_FUNC("fat_setlabel", CB), \
519 CFG_FUNC("fat_touch", CB), \
520 CFG_FUNC("gpt_write", CB), \
521 CFG_FUNC("mbr_write", CB), \
522 CFG_FUNC("trim", CB), \
523 CFG_FUNC("uboot_clearenv", CB), \
524 CFG_FUNC("uboot_recover", CB), \
525 CFG_FUNC("uboot_setenv", CB), \
526 CFG_FUNC("uboot_unsetenv", CB), \
527 CFG_FUNC("error", CB), \
528 CFG_FUNC("info", CB), \
529 CFG_FUNC("path_write", CB), \
530 CFG_FUNC("pipe_write", CB), \
531 CFG_FUNC("execute", CB)
532
533 static cfg_opt_t task_on_init_opts[] = {
get_segment(struct block_cache * bc,off_t offset,struct block_cache_segment ** segment)534 CFG_ON_EVENT_FUNCTIONS(cb_on_init_func),
535 CFG_END()
536 };
537 static cfg_opt_t task_on_finish_opts[] = {
538 CFG_ON_EVENT_FUNCTIONS(cb_on_finish_func),
539 CFG_END()
540 };
541 static cfg_opt_t task_on_error_opts[] = {
542 CFG_ON_EVENT_FUNCTIONS(cb_on_error_func),
543 CFG_END()
544 };
545 static cfg_opt_t task_on_resource_opts[] = {
546 CFG_BOOL("verify-on-the-fly", cfg_false, CFGF_NONE), // Deprecated
547 CFG_STR("delta-source-raw-offset", 0, CFGF_NONE), // Special case: use a string to support unsigned 32-bit offsets on 32-bit machines
548 CFG_INT("delta-source-raw-count", INT32_MAX, CFGF_NONE),
549 CFG_STR("delta-source-fat-offset", 0, CFGF_NONE), // Special case: use a string to support unsigned 32-bit offsets on 32-bit machines
550 CFG_STR("delta-source-fat-path", 0, CFGF_NONE),
551 CFG_ON_EVENT_FUNCTIONS(cb_on_resource_func),
552 CFG_END()
553 };
554 static cfg_opt_t task_opts[] = {
555 CFG_FUNC("include", &cb_include), \
556
557 CFG_STR_LIST("reqlist", 0, CFGF_NONE), // Internal - use functions below
558 CFG_FUNC("require-partition-offset", cb_task_require_func),
559 CFG_FUNC("require-fat-file-exists", cb_task_require_func),
560 CFG_FUNC("require-uboot-variable", cb_task_require_func),
561 CFG_FUNC("require-path-on-device", cb_task_require_func),
562 CFG_FUNC("require-path-at-offset", cb_task_require_func),
563 CFG_FUNC("require-fat-file-match", cb_task_require_func),
564
565 CFG_INT("require-partition1-offset", -1, CFGF_NONE), // Deprecated
566 CFG_BOOL("require-unmounted-destination", cfg_false, CFGF_NONE), // Deprecated
567 CFG_BOOL("verify-on-the-fly", cfg_false, CFGF_NONE), // Deprecated
568 CFG_SEC("on-init", task_on_init_opts, CFGF_NONE),
569 CFG_SEC("on-finish", task_on_finish_opts, CFGF_NONE),
570 CFG_SEC("on-error", task_on_error_opts, CFGF_NONE),
571 CFG_SEC("on-resource", task_on_resource_opts, CFGF_MULTI | CFGF_TITLE | CFGF_NO_TITLE_DUPES),
572 CFG_IGNORE_UNKNOWN
573 CFG_END()
574 };
575 static cfg_opt_t opts[] = {
576 CFG_STR("meta-product", 0, CFGF_NONE),
577 CFG_STR("meta-description", 0, CFGF_NONE),
578 CFG_STR("meta-version", 0, CFGF_NONE),
579 CFG_STR("meta-author", 0, CFGF_NONE),
580 CFG_STR("meta-platform", 0, CFGF_NONE),
581 CFG_STR("meta-architecture", 0, CFGF_NONE),
582 CFG_STR("meta-creation-date", 0, CFGF_NONE),
583 CFG_STR("meta-fwup-version", 0, CFGF_NONE),
584 CFG_STR("meta-vcs-identifier", 0, CFGF_NONE),
585 CFG_STR("meta-misc", 0, CFGF_NONE),
586 CFG_STR("meta-uuid", 0, CFGF_NONE),
block_cache_trim(struct block_cache * bc,off_t offset,off_t count,bool hwtrim)587
588 CFG_STR("require-fwup-version", "0", CFGF_NONE),
589 CFG_FUNC("define", cb_define),
590 CFG_FUNC("define!", cb_define_bang),
591 CFG_FUNC("define-eval", cb_define_eval),
592 CFG_FUNC("define-eval!", cb_define_eval_bang),
593 CFG_SEC("file-resource", file_resource_opts, CFGF_MULTI | CFGF_TITLE | CFGF_NO_TITLE_DUPES),
594 CFG_SEC("mbr", mbr_opts, CFGF_MULTI | CFGF_TITLE | CFGF_NO_TITLE_DUPES),
595 CFG_SEC("gpt", gpt_opts, CFGF_MULTI | CFGF_TITLE | CFGF_NO_TITLE_DUPES),
596 CFG_SEC("task", task_opts, CFGF_MULTI | CFGF_TITLE | CFGF_NO_TITLE_DUPES),
597 CFG_SEC("uboot-environment", uboot_environment_opts, CFGF_MULTI | CFGF_TITLE | CFGF_NO_TITLE_DUPES),
598 CFG_FUNC("include", &cb_include),
599 CFG_IGNORE_UNKNOWN
600 CFG_END()
601 };
602
603 int cfgfile_parse_file(const char *filename, cfg_t **cfg)
604 {
605 if (fwup_verbose) {
606 extern char **environ;
607 int e = 0;
608
609 INFO("Config environment:");
610 while (environ[e] != NULL) {
611 INFO(" %s", environ[e]);
612 e++;
613 }
614 }
615
616 int rc = 0;
617
618 // Patch over a trivially easy error to make of not escaping the $
619 // on the FWUP_META_UUID variable. This variable needs to be resolved
620 // at "apply" time since it can't be calculated at creation time without
621 // affecting itself.
622 set_environment("FWUP_META_UUID", "${FWUP_META_UUID}");
623
624 // libconfuse 3.0 note: This function is called when creating
625 // archives. If there's an unknown option, we want to throw an
626 // error so that the user can fix it. I.e., don't pass
627 // CFGF_IGNORE_UNKNOWN here.
628 toplevel_cfg = cfg_init(opts, 0);
629
630 // Set a validating callback function for sections
631 cfg_set_validate_func(toplevel_cfg, "require-fwup-version", cb_validate_require_fwup_version);
632 cfg_set_validate_func(toplevel_cfg, "file-resource", cb_validate_file_resource);
633 cfg_set_validate_func(toplevel_cfg, "mbr", cb_validate_mbr);
634 cfg_set_validate_func(toplevel_cfg, "gpt", cb_validate_gpt);
635 cfg_set_validate_func(toplevel_cfg, "uboot-environment", cb_validate_uboot);
636 cfg_set_validate_func(toplevel_cfg, "task|on-resource", cb_validate_on_resource);
637
638 if (strcmp(filename, "-") == 0) {
639 if (cfg_parse_fp(toplevel_cfg, stdin) != 0)
640 ERR_CLEANUP_MSG("Error parsing configuration from stdin");
641 } else {
642 switch (cfg_parse(toplevel_cfg, filename)) {
643 case CFG_SUCCESS:
644 break;
645 case CFG_FILE_ERROR:
646 ERR_CLEANUP_MSG("Error opening configuration file '%s'", filename);
647 default:
648 case CFG_PARSE_ERROR:
649 ERR_CLEANUP_MSG("Error parsing configuration file '%s'", filename);
650 }
651 }
652 *cfg = toplevel_cfg;
653 return 0;
654
655 cleanup:
656 cfg_free(toplevel_cfg);
657 return rc;
658 }
659
660 /**
661 * Helper function for reading all data in a file in the archive
662 *
663 * @param a the archive
664 * @param ae the archive entry so that we can figure out the size if in seeking mode
665 * @param buffer where to save the malloc'd buffer pointer
666 * @param max_size read no more than this. If more exists, the rest isn't read.
667 * @param size_read how much was actually read
668 * @return 0 on success
669 */
670 int archive_read_all_data(struct archive *a, struct archive_entry *ae, char **buffer, off_t max_size, off_t *size_read)
671 {
672 if (archive_entry_size_is_set(ae)) {
673 // Reading off disk case - we know the size a priori
674 off_t total_size = archive_entry_size(ae);
675 if (total_size < 0)
676 return -1;
677
678 // Only read up to max_size or as much as we have.
679 if (total_size < max_size)
680 max_size = total_size;
681 }
682
683 char *buf = (char *) malloc(max_size + 1);
684
685 off_t size_left = max_size;
block_cache_trim_after(struct block_cache * bc,off_t offset,bool hwtrim)686 while (size_left > 0) {
687 off_t len = archive_read_data(a, &buf[max_size - size_left], size_left);
688 if (len <= 0)
689 break;
690 size_left -= len;
691 }
692
693 *size_read = max_size - size_left;
694 buf[*size_read] = 0; // NULL terminate for convenience
695 *buffer = buf;
696
697 return 0;
698 }
699
700 int cfgfile_parse_fw_ae(struct archive *a,
701 struct archive_entry *ae,
702 cfg_t **cfg,
703 unsigned char *meta_conf_signature,
704 unsigned char * const *public_keys)
705 {
706 int rc = 0;
707 char *meta_conf = NULL;
708 off_t max_meta_conf_size = 50000;
709 off_t total_size;
710
711 if (archive_read_all_data(a, ae, &meta_conf, max_meta_conf_size, &total_size) < 0)
712 ERR_CLEANUP_MSG("Error reading meta.conf from archive.\n"
713 "Check for file corruption or libarchive built without zlib support");
714 if (total_size < 10 || total_size >= max_meta_conf_size)
block_segment_pwrite(struct block_cache * bc,struct block_cache_segment * seg,const void * buf,size_t count,size_t offset_into_segment,bool streamed)715 ERR_CLEANUP_MSG("Unexpected meta.conf size: %d", total_size);
716
717 // Check the signature on meta.conf if it has been signed
718 if (*public_keys) {
719 if (!meta_conf_signature)
720 ERR_CLEANUP_MSG("Expecting signed firmware archive.");
721
722 bool worked = false;
723 while (*public_keys) {
724 if (crypto_ed25519_check(meta_conf_signature, *public_keys, (const uint8_t *) meta_conf, total_size) == 0) {
725 worked = true;
726 break;
727 }
728 public_keys++;
729 }
730 if (!worked)
731 ERR_CLEANUP_MSG("Firmware archive's meta.conf fails digital signature verification.");
732 } else if (meta_conf_signature) {
733 INFO("Firmware archive is signed, but signature verification is off.");
734 }
735
736 // Parse the configuration, but do minimal validity checking of configuration
737 // since many things are only used on the creation of the firmware update.
738 #ifdef CFGF_IGNORE_UNKNOWN
739 // libconfuse 3.0 - ignore unknown options so that new options don't necessarily cause
740 // the firmware update to fail. For example, this allows metadata to be added to the
741 // meta.conf file in future firmware updates. Without it, the new metadata option
742 // would result in libconfuse throwing a parse error.
743 *cfg = cfg_init(opts, CFGF_IGNORE_UNKNOWN);
block_cache_pwrite(struct block_cache * bc,const void * buf,size_t count,off_t offset,bool streamed)744 #else
745 #warning It is highly recommended to compile against libconfuse 3.0+.
746 #warning Forward compatibility with .fw files from future fwup versions may be more limited without this.
747 #warning If you are just creating .fw files, then you may ignore this warning. If not, consider
748 #warning building a statically linked version of fwup if your distro does not have a newer version.
749 #warning See scripts/download_deps.sh.
750 *cfg = cfg_init(opts, 0);
751 #endif
752 cfg_set_validate_func(*cfg, "require-fwup-version", cb_validate_require_fwup_version);
753
754 // Set automatically determined metadata
755
756 // fwup 1.2 and later base the creation date off meta.conf timestamp which
757 // may or may not be accurate. Set it here. If it's overridden, then we have
758 // an older .fw file and that's ok.
759 time_t creation_time = 0;
760 if (archive_entry_mtime_is_set(ae))
761 creation_time = archive_entry_mtime(ae);
762 char str[64];
763 time_t_to_string(creation_time, str, sizeof(str));
764 cfg_setstr(*cfg, "meta-creation-date", str);
765
766 // meta-uuid is always calculated and cannot be overriden
767 calculate_fwup_uuid(meta_conf, total_size, str);
768 cfg_setstr(*cfg, "meta-uuid", str);
769 set_environment("FWUP_META_UUID", str);
770
771 if (cfg_parse_buf(*cfg, meta_conf) != 0)
772 ERR_CLEANUP_MSG("Unexpected error parsing meta.conf");
773
774 // Verify that meta-uuid wasn't changed when loading the file.
block_segment_pread(struct block_cache * bc,struct block_cache_segment * seg,void * buf,size_t count,size_t offset_into_segment)775 if (strcmp(str, cfg_getstr(*cfg, "meta-uuid")) != 0 ||
776 strcmp(str, get_environment("FWUP_META_UUID")) != 0)
777 ERR_CLEANUP_MSG("meta.conf isn't allowed to change 'meta-uuid' or '$FWUP_META_UUID'");
778
779 cleanup:
780 if (meta_conf)
781 free(meta_conf);
782
783 return rc;
784 }
785
block_cache_pread(struct block_cache * bc,void * buf,size_t count,off_t offset)786 int cfgfile_parse_fw_meta_conf(const char *filename, cfg_t **cfg, unsigned char * const *public_keys)
787 {
788 int rc = 0;
789 unsigned char *meta_conf_signature = NULL;
790 struct archive *a = archive_read_new();
791 archive_read_support_format_zip(a);
792 rc = fwup_archive_open_filename(a, filename, NULL);
793 if (rc != ARCHIVE_OK)
794 ERR_CLEANUP_MSG("%s", archive_error_string(a));
795
796 struct archive_entry *ae;
797 rc = archive_read_next_header(a, &ae);
798 if (rc != ARCHIVE_OK)
799 ERR_CLEANUP_MSG("Corrupt archive '%s'", filename);
800
801 if (strcmp(archive_entry_pathname(ae), "meta.conf.ed25519") == 0) {
802 off_t total_size;
803 if (archive_read_all_data(a, ae, (char **) &meta_conf_signature, 64, &total_size) < 0)
804 ERR_CLEANUP_MSG("Error reading meta.conf.ed25519 from archive.\n"
805 "Check for file corruption or libarchive built without zlib support");
806
807 if (total_size != 64)
808 ERR_CLEANUP_MSG("Unexpected meta.conf.ed25519 size: %d", total_size);
809
810 rc = archive_read_next_header(a, &ae);
811 if (rc != ARCHIVE_OK)
812 ERR_CLEANUP_MSG("Expecting more than meta.conf.ed25519 in archive");
813 }
814 if (strcmp(archive_entry_pathname(ae), "meta.conf") != 0)
815 ERR_CLEANUP_MSG("Expecting meta.conf to be at beginning of %s", filename);
816
817 OK_OR_CLEANUP(cfgfile_parse_fw_ae(a, ae, cfg, meta_conf_signature, public_keys));
818
819 cleanup:
820 archive_read_free(a);
821 if (meta_conf_signature)
822 free(meta_conf_signature);
823
824 return rc;
825 }
826
827 void cfgfile_free(cfg_t *cfg)
828 {
829 cfg_free(cfg);
830 }
831