osafeloader: new util for extracting partitions from SafeLoader
[openwrt/openwrt.git] / package / utils / osafeloader / src / osafeloader.c
1 /*
2 * osafeloader
3 *
4 * Copyright (C) 2016 Rafał Miłecki <zajec5@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11 #include <byteswap.h>
12 #include <endian.h>
13 #include <errno.h>
14 #include <stdint.h>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <unistd.h>
19
20 #include "md5.h"
21
22 #if !defined(__BYTE_ORDER)
23 #error "Unknown byte order"
24 #endif
25
26 #if __BYTE_ORDER == __BIG_ENDIAN
27 #define cpu_to_be32(x) (x)
28 #define be32_to_cpu(x) (x)
29 #define cpu_to_be16(x) (x)
30 #define be16_to_cpu(x) (x)
31 #elif __BYTE_ORDER == __LITTLE_ENDIAN
32 #define cpu_to_be32(x) bswap_32(x)
33 #define be32_to_cpu(x) bswap_32(x)
34 #define cpu_to_be16(x) bswap_16(x)
35 #define be16_to_cpu(x) bswap_16(x)
36 #else
37 #error "Unsupported endianness"
38 #endif
39
40 struct safeloader_header {
41 uint32_t imagesize;
42 uint8_t md5[16];
43 } __attribute__ ((packed));
44
45 char *safeloader_path;
46 char *partition_name;
47 char *out_path;
48
49 static inline size_t osafeloader_min(size_t x, size_t y) {
50 return x < y ? x : y;
51 }
52
53 static const uint8_t md5_salt[16] = {
54 0x7a, 0x2b, 0x15, 0xed,
55 0x9b, 0x98, 0x59, 0x6d,
56 0xe5, 0x04, 0xab, 0x44,
57 0xac, 0x2a, 0x9f, 0x4e,
58 };
59
60 /**************************************************
61 * Info
62 **************************************************/
63
64 static int osafeloader_info(int argc, char **argv) {
65 FILE *safeloader;
66 struct safeloader_header hdr;
67 MD5_CTX ctx;
68 size_t bytes, imagesize;
69 uint8_t buf[1024];
70 uint8_t md5[16];
71 char name[32];
72 int base, size, i;
73 int err = 0;
74
75 if (argc < 3) {
76 fprintf(stderr, "No SafeLoader file passed\n");
77 err = -EINVAL;
78 goto out;
79 }
80 safeloader_path = argv[2];
81
82 safeloader = fopen(safeloader_path, "r");
83 if (!safeloader) {
84 fprintf(stderr, "Couldn't open %s\n", safeloader_path);
85 err = -EACCES;
86 goto out;
87 }
88
89 bytes = fread(&hdr, 1, sizeof(hdr), safeloader);
90 if (bytes != sizeof(hdr)) {
91 fprintf(stderr, "Couldn't read %s header\n", safeloader_path);
92 err = -EIO;
93 goto err_close;
94 }
95 imagesize = be32_to_cpu(hdr.imagesize);
96
97 MD5_Init(&ctx);
98 MD5_Update(&ctx, md5_salt, sizeof(md5_salt));
99 while ((bytes = fread(buf, 1, osafeloader_min(sizeof(buf), imagesize), safeloader)) > 0) {
100 MD5_Update(&ctx, buf, bytes);
101 imagesize -= bytes;
102 }
103 MD5_Final(md5, &ctx);
104
105 if (memcmp(md5, hdr.md5, 16)) {
106 fprintf(stderr, "Broken SafeLoader file with invalid MD5\n");
107 err = -EIO;
108 goto err_close;
109 }
110
111 printf("%10s: %d\n", "Image size", be32_to_cpu(hdr.imagesize));
112 printf("%10s: ", "MD5");
113 for (i = 0; i < 16; i++)
114 printf("%02x", md5[i]);
115 printf("\n");
116
117 /* Skip header & vendor info */
118 fseek(safeloader, 0x1014, SEEK_SET);
119
120 while (fscanf(safeloader, "fwup-ptn %s base 0x%x size 0x%x\t\r\n", name, &base, &size) == 3) {
121 printf("%10s: %s (0x%x - 0x%x)\n", "Partition", name, base, base + size);
122 }
123
124 err_close:
125 fclose(safeloader);
126 out:
127 return err;
128 }
129
130 /**************************************************
131 * Extract
132 **************************************************/
133
134 static void osafeloader_extract_parse_options(int argc, char **argv) {
135 int c;
136
137 while ((c = getopt(argc, argv, "p:o:")) != -1) {
138 switch (c) {
139 case 'p':
140 partition_name = optarg;
141 break;
142 case 'o':
143 out_path = optarg;
144 break;
145 }
146 }
147 }
148
149 static int osafeloader_extract(int argc, char **argv) {
150 FILE *safeloader;
151 FILE *out;
152 struct safeloader_header hdr;
153 size_t bytes;
154 char name[32];
155 int base, size;
156 int err = 0;
157
158 if (argc < 3) {
159 fprintf(stderr, "No SafeLoader file passed\n");
160 err = -EINVAL;
161 goto out;
162 }
163 safeloader_path = argv[2];
164
165 optind = 3;
166 osafeloader_extract_parse_options(argc, argv);
167 if (!partition_name) {
168 fprintf(stderr, "No partition name specified\n");
169 err = -EINVAL;
170 goto out;
171 } else if (!out_path) {
172 fprintf(stderr, "No output file specified\n");
173 err = -EINVAL;
174 goto out;
175 }
176
177 safeloader = fopen(safeloader_path, "r");
178 if (!safeloader) {
179 fprintf(stderr, "Couldn't open %s\n", safeloader_path);
180 err = -EACCES;
181 goto out;
182 }
183
184 out = fopen(out_path, "w");
185 if (!out) {
186 fprintf(stderr, "Couldn't open %s\n", out_path);
187 err = -EACCES;
188 goto err_close_safeloader;
189 }
190
191 bytes = fread(&hdr, 1, sizeof(hdr), safeloader);
192 if (bytes != sizeof(hdr)) {
193 fprintf(stderr, "Couldn't read %s header\n", safeloader_path);
194 err = -EIO;
195 goto err_close_out;
196 }
197
198 /* Skip vendor info */
199 fseek(safeloader, 0x1000, SEEK_CUR);
200
201 err = -ENOENT;
202 while (fscanf(safeloader, "fwup-ptn %s base 0x%x size 0x%x\t\r\n", name, &base, &size) == 3) {
203 uint8_t buf[1024];
204
205 if (strcmp(name, partition_name))
206 continue;
207
208 err = 0;
209
210 fseek(safeloader, sizeof(hdr) + 0x1000 + base, SEEK_SET);
211
212 while ((bytes = fread(buf, 1, osafeloader_min(sizeof(buf), size), safeloader)) > 0) {
213 if (fwrite(buf, 1, bytes, out) != bytes) {
214 fprintf(stderr, "Couldn't write %zu B to %s\n", bytes, out_path);
215 err = -EIO;
216 break;
217 }
218 size -= bytes;
219 }
220
221 if (size) {
222 fprintf(stderr, "Couldn't extract whole partition %s from %s (%d B left)\n", partition_name, safeloader_path, size);
223 err = -EIO;
224 }
225
226 break;
227 }
228
229 err_close_out:
230 fclose(out);
231 err_close_safeloader:
232 fclose(safeloader);
233 out:
234 return err;
235 }
236
237 /**************************************************
238 * Start
239 **************************************************/
240
241 static void usage() {
242 printf("Usage:\n");
243 printf("\n");
244 printf("Info about SafeLoader:\n");
245 printf("\tosafeloader info <file>\n");
246 printf("\n");
247 printf("Extract from SafeLoader:\n");
248 printf("\tosafeloader extract <file> [options]\n");
249 printf("\t-p name\t\t\t\tname of partition to extract\n");
250 printf("\t-o file\t\t\t\toutput file\n");
251 }
252
253 int main(int argc, char **argv) {
254 if (argc > 1) {
255 if (!strcmp(argv[1], "info"))
256 return osafeloader_info(argc, argv);
257 else if (!strcmp(argv[1], "extract"))
258 return osafeloader_extract(argc, argv);
259 }
260
261 usage();
262 return 0;
263 }