1 /*
2  * Copyright 2014-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 "fwup_apply.h"
18 #include "util.h"
19 #include "cfgfile.h"
20 
21 #include <archive.h>
22 #include <archive_entry.h>
23 #include <confuse.h>
24 #include <errno.h>
25 #include <string.h>
26 #include <stdbool.h>
27 #include <stdlib.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <fcntl.h>
31 #include "monocypher.h"
32 
33 #include "requirement.h"
34 #include "functions.h"
35 #include "fatfs.h"
36 #include "mbr.h"
37 #include "fwfile.h"
38 #include "archive_open.h"
39 #include "sparse_file.h"
40 #include "progress.h"
41 #include "resources.h"
42 #include "block_cache.h"
43 #include "fwup_xdelta3.h"
44 
deprecated_task_is_applicable(cfg_t * task,struct block_cache * output)45 static bool deprecated_task_is_applicable(cfg_t *task, struct block_cache *output)
46 {
47     // Handle legacy require-partition1-offset=x constraint
48     int part1_offset = cfg_getint(task, "require-partition1-offset");
49     if (part1_offset >= 0) {
50         // Try to read the MBR. This won't work if the output
51         // isn't seekable, but that's ok, since this constraint would
52         // fail anyway.
53         uint8_t buffer[FWUP_BLOCK_SIZE];
54         if (block_cache_pread(output, buffer, FWUP_BLOCK_SIZE, 0) < 0)
55             return false;
56 
57         struct mbr_partition partitions[4];
58         if (mbr_decode(buffer, partitions) < 0)
59             return false;
60 
61         if (partitions[1].block_offset != (uint32_t) part1_offset)
62             return false;
63     }
64 
65     // all constraints pass, therefore, it's ok.
66     return true;
67 }
68 
task_is_applicable(struct fun_context * fctx,cfg_t * task)69 static bool task_is_applicable(struct fun_context *fctx, cfg_t *task)
70 {
71     cfg_opt_t *reqlist = cfg_getopt(task, "reqlist");
72     if (reqlist) {
73         if (req_apply_reqlist(fctx, reqlist, req_requirement_met) < 0) {
74             // Error indicates that one or more requirements weren't met or
75             // something was messed up in the requirement. Either way, the
76             // task isn't applicable.
77             return false;
78         }
79     }
80 
81     // If we get here, then it's ok to apply this task.
82     return true;
83 }
84 
find_task(struct fun_context * fctx,const char * task_prefix)85 static cfg_t *find_task(struct fun_context *fctx, const char *task_prefix)
86 {
87     size_t task_len = strlen(task_prefix);
88     cfg_t *task;
89 
90     int i;
91     for (i = 0; (task = cfg_getnsec(fctx->cfg, "task", i)) != NULL; i++) {
92         const char *name = cfg_title(task);
93         if (strlen(name) >= task_len &&
94                 memcmp(task_prefix, name, task_len) == 0 &&
95                 deprecated_task_is_applicable(task, fctx->output) &&
96                 task_is_applicable(fctx, task))
97             return task;
98     }
99     return 0;
100 }
101 
apply_event(struct fun_context * fctx,cfg_t * task,const char * event_type,const char * event_parameter,int (* fun)(struct fun_context * fctx))102 static int apply_event(struct fun_context *fctx, cfg_t *task, const char *event_type, const char *event_parameter, int (*fun)(struct fun_context *fctx))
103 {
104     if (event_parameter)
105         fctx->on_event = cfg_gettsec(task, event_type, event_parameter);
106     else
107         fctx->on_event = cfg_getsec(task, event_type);
108 
109     if (fctx->on_event) {
110         cfg_opt_t *funlist = cfg_getopt(fctx->on_event, "funlist");
111         if (funlist) {
112             if (fun_apply_funlist(fctx, funlist, fun) < 0) {
113                 fctx->on_event = NULL;
114                 return -1;
115             }
116         }
117     }
118     fctx->on_event = NULL;
119     return 0;
120 }
121 
122 struct fwup_apply_data
123 {
124     struct archive *a;
125     bool reading_stdin;
126 
127     // Sparse file handling
128     struct sparse_file_map sfm;
129     int sparse_map_ix;
130     off_t sparse_block_offset;
131     off_t actual_offset;
132     const void *sparse_leftover;
133     off_t sparse_leftover_len;
134 };
135 
read_callback_normal(struct fun_context * fctx,const void ** buffer,size_t * len,off_t * offset)136 static int read_callback_normal(struct fun_context *fctx, const void **buffer, size_t *len, off_t *offset)
137 {
138     struct fwup_apply_data *p = (struct fwup_apply_data *) fctx->cookie;
139 
140     // Even though libarchive's API supports sparse files, the ZIP file format
141     // does not support them, so it can't be used. To workaround this, all of the data
142     // chunks of a sparse file are concatenated together. This function breaks them
143     // apart.
144 
145     if (p->sparse_map_ix == p->sfm.map_len) {
146         // End of file
147         *len = 0;
148         *buffer = NULL;
149         *offset = 0;
150         return 0;
151     }
152     off_t sparse_file_chunk_len = p->sfm.map[p->sparse_map_ix];
153     off_t remaining_data_in_sparse_file_chunk =
154             sparse_file_chunk_len - p->sparse_block_offset;
155 
156     if (p->sparse_leftover_len > 0) {
157         // Handle the case where a previous call had data remaining
158         *buffer = p->sparse_leftover;
159         *offset = p->actual_offset;
160         if (remaining_data_in_sparse_file_chunk >= p->sparse_leftover_len)
161             *len = p->sparse_leftover_len;
162         else
163             *len = remaining_data_in_sparse_file_chunk;
164 
165         p->sparse_leftover += *len;
166         p->sparse_leftover_len -= *len;
167         p->actual_offset += *len;
168         p->sparse_block_offset += *len;
169         if (p->sparse_block_offset == sparse_file_chunk_len) {
170             // Advance over hole (unless this is the end)
171             p->sparse_map_ix++;
172             p->sparse_block_offset = 0;
173             if (p->sparse_map_ix != p->sfm.map_len) {
174                 p->actual_offset += p->sfm.map[p->sparse_map_ix];
175 
176                 // Advance to next data block
177                 p->sparse_map_ix++;
178             }
179         }
180         return 0;
181     }
182 
183     // Decompress more data
184     int64_t ignored;
185     int rc = fwup_archive_read_data_block(p->a, buffer, len, &ignored);
186 
187     if (rc == ARCHIVE_EOF) {
188         *len = 0;
189         *buffer = NULL;
190         *offset = 0;
191         return 0;
192     } else if (rc != ARCHIVE_OK)
193         ERR_RETURN(archive_error_string(p->a));
194 
195     *offset = p->actual_offset;
196 
197     if (remaining_data_in_sparse_file_chunk > (off_t) *len) {
198         // The amount decompressed doesn't cross a sparse file hole
199         p->actual_offset += *len;
200         p->sparse_block_offset += *len;
201     } else {
202         // The amount decompressed crosses a hole in a sparse file,
203         // so return the contiguous chunk and save the leftovers.
204         p->actual_offset += remaining_data_in_sparse_file_chunk;
205         p->sparse_leftover_len = *len - remaining_data_in_sparse_file_chunk;
206         p->sparse_leftover = *buffer + remaining_data_in_sparse_file_chunk;
207 
208         *len = remaining_data_in_sparse_file_chunk;
209 
210         // Advance over hole (unless this is the end)
211         p->sparse_map_ix++;
212         p->sparse_block_offset = 0;
213         if (p->sparse_map_ix != p->sfm.map_len) {
214             p->actual_offset += p->sfm.map[p->sparse_map_ix];
215 
216             // Advance to next data block
217             p->sparse_map_ix++;
218         }
219     }
220 
221     return 0;
222 }
223 
xdelta_read_patch_callback(void * cookie,const void ** buffer,size_t * len)224 static int xdelta_read_patch_callback(void* cookie, const void **buffer, size_t *len)
225 {
226     struct fun_context *fctx = (struct fun_context *) cookie;
227     struct fwup_apply_data *p = (struct fwup_apply_data *) fctx->cookie;
228 
229     int64_t ignored;
230     int rc = fwup_archive_read_data_block(p->a, buffer, len, &ignored);
231 
232     if (rc == ARCHIVE_OK) {
233         return 0;
234     } else if (rc == ARCHIVE_EOF) {
235         *len = 0;
236         *buffer = NULL;
237         return 0;
238     } else {
239         *len = 0;
240         *buffer = NULL;
241         ERR_RETURN(archive_error_string(p->a));
242     }
243 }
244 
xdelta_read_source_callback(void * cookie,void * buf,size_t count,off_t offset)245 static int xdelta_read_source_callback(void *cookie, void *buf, size_t count, off_t offset)
246 {
247     struct fun_context *fctx = (struct fun_context *) cookie;
248 
249     if (offset < 0 ||
250         offset + count > fctx->xd_source_count)
251         ERR_RETURN("xdelta tried to load outside of allowed byte range (0-%" PRId64 "): offset: %" PRId64 ", count: %d", fctx->xd_source_count, offset, count);
252 
253     return block_cache_pread(fctx->output, buf, count, fctx->xd_source_offset + offset);
254 }
255 
read_callback_xdelta(struct fun_context * fctx,const void ** buffer,size_t * len,off_t * offset)256 static int read_callback_xdelta(struct fun_context *fctx, const void **buffer, size_t *len, off_t *offset)
257 {
258     struct fwup_apply_data *p = (struct fwup_apply_data *) fctx->cookie;
259 
260     // This would be ridiculous...
261     if (p->sfm.map_len != 1)
262         ERR_RETURN("Sparse xdelta not supported");
263 
264     OK_OR_RETURN(xdelta_read(fctx->xd, buffer, len));
265 
266     // xdelta_read's output has no holes
267     *offset = p->actual_offset;
268     p->actual_offset += *len;
269 
270     return 0;
271 }
272 
read_callback(struct fun_context * fctx,const void ** buffer,size_t * len,off_t * offset)273 static int read_callback(struct fun_context *fctx, const void **buffer, size_t *len, off_t *offset)
274 {
275     if (fctx->xd)
276         return read_callback_xdelta(fctx, buffer, len, offset);
277     else
278         return read_callback_normal(fctx, buffer, len, offset);
279 }
280 
initialize_timestamps()281 static void initialize_timestamps()
282 {
283     // The purpose of this function is to set all timestamps that we create
284     // (e.g., FATFS timestamps) to a fixed date. This is needed
285     // to make sure that the images that we create are bit-for-bit identical.
286 
287     // Set the timestamp to FAT time 0
288     struct tm tmp;
289     tmp.tm_year = 80;
290     tmp.tm_mon = 0;
291     tmp.tm_mday = 1;
292     tmp.tm_hour = 0;
293     tmp.tm_min = 0;
294     tmp.tm_sec = 0;
295 
296     fatfs_set_time(&tmp);
297 }
298 
compute_progress(struct fun_context * fctx)299 static int compute_progress(struct fun_context *fctx)
300 {
301     fctx->type = FUN_CONTEXT_INIT;
302     OK_OR_RETURN(apply_event(fctx, fctx->task, "on-init", NULL, fun_compute_progress));
303 
304     fctx->type = FUN_CONTEXT_FILE;
305     cfg_t *sec;
306     int i = 0;
307     while ((sec = cfg_getnsec(fctx->task, "on-resource", i++)) != NULL) {
308         cfg_t *resource = cfg_gettsec(fctx->cfg, "file-resource", sec->title);
309         if (!resource) {
310             // This really shouldn't happen, but failing to calculate
311             // progress for a missing file-resource seems harsh.
312             INFO("Can't find file-resource for %s", sec->title);
313             continue;
314         }
315 
316         OK_OR_RETURN(apply_event(fctx, fctx->task, "on-resource", sec->title, fun_compute_progress));
317     }
318 
319     fctx->type = FUN_CONTEXT_FINISH;
320     OK_OR_RETURN(apply_event(fctx, fctx->task, "on-finish", NULL, fun_compute_progress));
321 
322     return 0;
323 }
324 
run_task(struct fun_context * fctx,struct fwup_apply_data * pd)325 static int run_task(struct fun_context *fctx, struct fwup_apply_data *pd)
326 {
327     int rc = 0;
328 
329     struct resource_list *resources = NULL;
330     OK_OR_CLEANUP(rlist_get_from_task(fctx->cfg, fctx->task, &resources));
331 
332     fctx->type = FUN_CONTEXT_INIT;
333     OK_OR_CLEANUP(apply_event(fctx, fctx->task, "on-init", NULL, fun_run));
334 
335     fctx->type = FUN_CONTEXT_FILE;
336     fctx->read = read_callback;
337     struct archive_entry *ae;
338     while (archive_read_next_header(pd->a, &ae) == ARCHIVE_OK) {
339         const char *filename = archive_entry_pathname(ae);
340         char resource_name[FWFILE_MAX_ARCHIVE_PATH];
341 
342         OK_OR_CLEANUP(archive_filename_to_resource(filename, resource_name, sizeof(resource_name)));
343 
344         // Skip an empty filename. This is easy to get when you run 'zip'
345         // on the command line to create a firmware update file and include
346         // the 'data' directory. It's annoying when it creates an error
347         // (usually when debugging something else), so ignore it.
348         if (resource_name[0] == '\0')
349             continue;
350 
351         // See if this resource is used by this task
352         struct resource_list *item = rlist_find_by_name(resources, resource_name);
353         if (item == NULL)
354             continue;
355 
356         // See if there's metadata associated with this resource
357         if (item->resource == NULL)
358             ERR_CLEANUP_MSG("Resource '%s' used, but metadata is missing. Archive is corrupt.", resource_name);
359 
360         OK_OR_CLEANUP(sparse_file_get_map_from_resource(item->resource, &pd->sfm));
361         pd->sparse_map_ix = 0;
362         pd->sparse_block_offset = 0;
363         pd->actual_offset = 0;
364         pd->sparse_leftover = NULL;
365         pd->sparse_leftover_len = 0;
366         if (pd->sfm.map[0] == 0) {
367             if (pd->sfm.map_len > 2) {
368                 // This is the case where there's a hole at the beginning. Advance to
369                 // the offset of the data.
370                 pd->sparse_map_ix = 2;
371                 pd->actual_offset = pd->sfm.map[1];
372             } else {
373                 // sparse map has a 0 length data block and possibly a hole,
374                 // but it doesn't have another data block. This means that it's
375                 // either a 0-length file or it's all sparse. Signal EOF. This
376                 // might be a bug, but I can't think of a real use case for a completely
377                 // sparse file.
378                 pd->sparse_map_ix = pd->sfm.map_len;
379             }
380         }
381 
382 // MOVE ME!!!
383 {
384     cfg_t *on_resource = cfg_gettsec(fctx->task, "on-resource", resource_name);
385     if (on_resource) {
386         off_t size_in_archive = archive_entry_size(ae);
387         off_t expected_size_in_archive = sparse_file_data_size(&pd->sfm);
388 
389         if (pd->sfm.map_len == 1 && archive_entry_size_is_set(ae) && size_in_archive != expected_size_in_archive) {
390             const char *source_raw_offset_str = cfg_getstr(on_resource, "delta-source-raw-offset");
391             int source_raw_count = cfg_getint(on_resource, "delta-source-raw-count");
392             if (source_raw_count > 0 && source_raw_offset_str != NULL) {
393                 off_t source_raw_offset = strtoul(source_raw_offset_str, NULL, 0);
394 
395                 fctx->xd = malloc(sizeof(struct xdelta_state));
396                 xdelta_init(fctx->xd, xdelta_read_patch_callback, xdelta_read_source_callback, fctx);
397                 fctx->xd_source_offset = source_raw_offset * FWUP_BLOCK_SIZE;
398                 fctx->xd_source_count = source_raw_count * FWUP_BLOCK_SIZE;
399             } else {
400                 ERR_CLEANUP_MSG("File '%s' isn't expected size (%d vs %d) and xdelta3 patch support not enabled on it. (Add delta-source-raw-offset or delta-source-raw-count at least)", resource_name, (int) size_in_archive, (int) expected_size_in_archive);
401             }
402         }
403     }
404 }
405         OK_OR_CLEANUP(apply_event(fctx, fctx->task, "on-resource", resource_name, fun_run));
406 
407         item->processed = true;
408         sparse_file_free(&pd->sfm);
409 
410 { // MOVE ME!!!
411     if (fctx->xd) {
412         xdelta_free(fctx->xd);
413         free(fctx->xd);
414         fctx->xd = NULL;
415     }
416 }
417     }
418 
419     // Make sure that all "on-resource" blocks have been run.
420     for (const struct resource_list *r = resources; r != NULL; r = r->next) {
421         if (!r->processed)
422             ERR_CLEANUP_MSG("Resource %s not found in archive", cfg_title(r->resource));
423     }
424 
425     // Flush any fatfs filesystem updates before the on-finish. The on-finish
426     // generally has A/B partition swaps or other critical operations that
427     // assume the prior data has already been written. The block_cache will
428     // write output in the right order, but fatfs's caching will not so it
429     // needs to be flushed here.
430     fatfs_closefs();
431 
432     fctx->type = FUN_CONTEXT_FINISH;
433     OK_OR_CLEANUP(apply_event(fctx, fctx->task, "on-finish", NULL, fun_run));
434 
435 cleanup:
436     if (rc != 0) {
437         // Do a best attempt at running any error handling code
438         fctx->type = FUN_CONTEXT_ERROR;
439 
440         // Since there's an error, throw out anything that's in the cache so
441         // that the error handler can start fresh.
442         fatfs_closefs();
443         block_cache_reset(fctx->output);
444 
445         if (apply_event(fctx, fctx->task, "on-error", NULL, fun_run) < 0) {
446             // Yet another error so throw out the cache again.
447             fatfs_closefs();
448             block_cache_reset(fctx->output);
449         }
450     }
451     rlist_free(resources);
452     return rc;
453 }
454 
fwup_apply(const char * fw_filename,const char * task_prefix,int output_fd,off_t end_offset,struct fwup_progress * progress,unsigned char * const * public_keys,bool enable_trim,bool verify_writes)455 int fwup_apply(const char *fw_filename,
456                const char *task_prefix,
457                int output_fd,
458                off_t end_offset,
459                struct fwup_progress *progress,
460                unsigned char *const*public_keys,
461                bool enable_trim,
462                bool verify_writes)
463 {
464     int rc = 0;
465     unsigned char *meta_conf_signature = NULL;
466     struct fun_context fctx;
467     memset(&fctx, 0, sizeof(fctx));
468     fctx.progress = progress;
469 
470     // Report 0 progress before doing anything
471     progress_report(fctx.progress, 0);
472 
473     struct fwup_apply_data pd;
474     memset(&pd, 0, sizeof(pd));
475     fctx.cookie = &pd;
476     pd.a = archive_read_new();
477 
478     archive_read_support_format_zip(pd.a);
479     int arc = fwup_archive_open_filename(pd.a, fw_filename, progress);
480     if (arc != ARCHIVE_OK)
481         ERR_CLEANUP_MSG("%s", archive_error_string(pd.a));
482 
483     struct archive_entry *ae;
484     arc = archive_read_next_header(pd.a, &ae);
485     if (arc != ARCHIVE_OK)
486         ERR_CLEANUP_MSG("%s", archive_error_string(pd.a));
487 
488     if (strcmp(archive_entry_pathname(ae), "meta.conf.ed25519") == 0) {
489         off_t total_size;
490         if (archive_read_all_data(pd.a, ae, (char **) &meta_conf_signature, FWUP_SIGNATURE_LEN, &total_size) < 0)
491             ERR_CLEANUP_MSG("Error reading meta.conf.ed25519 from archive.\n"
492                             "Check for file corruption or libarchive built without zlib support");
493 
494         if (total_size != FWUP_SIGNATURE_LEN)
495             ERR_CLEANUP_MSG("Unexpected meta.conf.ed25519 size: %d", total_size);
496 
497         arc = archive_read_next_header(pd.a, &ae);
498         if (arc != ARCHIVE_OK)
499             ERR_CLEANUP_MSG("Expecting more than meta.conf.ed25519 in archive");
500     }
501     if (strcmp(archive_entry_pathname(ae), "meta.conf") != 0)
502         ERR_CLEANUP_MSG("Expecting meta.conf to be at the beginning of %s", fw_filename);
503 
504     OK_OR_CLEANUP(cfgfile_parse_fw_ae(pd.a, ae, &fctx.cfg, meta_conf_signature, public_keys));
505 
506     initialize_timestamps();
507 
508     // Initialize the output. Nothing should have been written before now
509     // and waiting to initialize the output until now forces the point.
510     fctx.output = (struct block_cache *) malloc(sizeof(struct block_cache));
511     OK_OR_CLEANUP(block_cache_init(fctx.output, output_fd, end_offset, enable_trim, verify_writes));
512 
513     // Go through all of the tasks and find a matcher
514     fctx.task = find_task(&fctx, task_prefix);
515     if (fctx.task == 0)
516         ERR_CLEANUP_MSG("Couldn't find applicable task '%s'. If task is available, the task's requirements may not be met.", task_prefix);
517 
518     // Compute the total progress units
519     OK_OR_CLEANUP(compute_progress(&fctx));
520 
521     // Run
522     OK_OR_CLEANUP(run_task(&fctx, &pd));
523 
524     // Flush everything
525     fatfs_closefs();
526     OK_OR_CLEANUP(block_cache_flush(fctx.output));
527 
528     // Close everything before reporting 100% just in case the OS blocks on the close call.
529     block_cache_free(fctx.output);
530     free(fctx.output);
531     fctx.output = NULL;
532     close(output_fd);
533 
534     // Success -> report 100%
535     progress_report_complete(fctx.progress);
536 
537 cleanup:
538     // Close the output
539     if (fctx.output) {
540         // In the error case, flush in case the on-error
541         // handler left something. Errors in on-error are
542         // handled with the on-error call.
543         fatfs_closefs();
544         block_cache_flush(fctx.output); // Ignore errors
545 
546         block_cache_free(fctx.output);
547         free(fctx.output);
548         fctx.output = NULL;
549         close(output_fd);
550     }
551 
552     sparse_file_free(&pd.sfm);
553 
554     archive_read_free(pd.a);
555 
556     if (meta_conf_signature)
557         free(meta_conf_signature);
558 
559     if (fctx.cfg) {
560         cfg_free(fctx.cfg);
561         fctx.cfg = NULL;
562     }
563 
564     return rc;
565 }
566