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, &current_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(&current_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(&current_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