Add support for GPG signature enforcement on booted
[petitboot] / discover / yaboot-parser.c
1 #if defined(HAVE_CONFIG_H)
2 #include "config.h"
3 #endif
4
5 #include <assert.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <ctype.h>
9
10 #include "log/log.h"
11 #include "talloc/talloc.h"
12 #include "types/types.h"
13 #include "parser-conf.h"
14 #include "parser-utils.h"
15 #include "resource.h"
16
17 struct yaboot_state {
18         int globals_done;
19
20         /* current option data */
21         struct discover_boot_option *opt;
22         const char *device;
23         const char *partition;
24         const char *boot_image;
25         const char *initrd;
26         const char *initrd_size;
27         const char *literal;
28         const char *ramdisk;
29         const char *root;
30         bool read_only;
31         bool read_write;
32 };
33
34 static struct discover_boot_option *state_start_new_option(
35                 struct conf_context *conf,
36                 struct yaboot_state *state)
37 {
38         state->opt = discover_boot_option_create(conf->dc, conf->dc->device);
39         state->opt->option->boot_args = talloc_strdup(state->opt->option, "");
40
41         /* old allocated values will get freed with the state */
42         state->device = conf_get_global_option(conf, "device");
43         state->partition = conf_get_global_option(conf, "partition");
44         state->initrd_size = conf_get_global_option(conf, "initrd_size");
45         state->literal = conf_get_global_option(conf, "literal");
46         state->ramdisk = conf_get_global_option(conf, "ramdisk");
47         state->root = conf_get_global_option(conf, "root");
48
49         return state->opt;
50 }
51
52 static struct resource *create_yaboot_devpath_resource(
53                 struct yaboot_state *state,
54                 struct conf_context *conf,
55                 const char *path)
56 {
57         struct discover_boot_option *opt = state->opt;
58         const char *dev, *part, *devpos;
59         struct resource *res;
60         char *devpath, *devstr;
61
62         dev = state->device;
63         part = state->partition;
64
65         if (!dev)
66                 dev = conf_get_global_option(conf, "device");
67         if (!part)
68                 part = conf_get_global_option(conf, "partition");
69
70         if (strchr(path, ':')) {
71                 devpath = talloc_strdup(conf, path);
72
73         } else if (dev && part) {
74                 devpos = &dev[strlen(dev) - 1];
75                 if (isdigit(*devpos)) {
76                         while (isdigit(*devpos))
77                                 devpos--;
78
79                         devstr = talloc_strndup(conf, dev, devpos - dev + 1);
80                         devpath = talloc_asprintf(conf, "%s%s:%s", devstr,
81                                         part, path);
82                         talloc_free(devstr);
83                 } else {
84                         devpath = talloc_asprintf(conf,
85                                         "%s%s:%s", dev, part, path);
86                 }
87         } else if (dev) {
88                 devpath = talloc_asprintf(conf, "%s:%s", dev, path);
89         } else {
90                 devpath = talloc_strdup(conf, path);
91         }
92
93         res = create_devpath_resource(opt, conf->dc->device, devpath);
94
95         talloc_free(devpath);
96
97         return res;
98 }
99
100 static void yaboot_finish(struct conf_context *conf)
101 {
102         struct yaboot_state *state = conf->parser_info;
103         const char *default_label;
104         struct boot_option *opt;
105
106         if (!state->opt)
107                 return;
108
109         opt = state->opt->option;
110         assert(opt);
111         assert(opt->name);
112         assert(opt->boot_args);
113
114         /* populate the boot option from state data */
115         state->opt->boot_image = create_yaboot_devpath_resource(state,
116                                 conf, state->boot_image);
117
118         char* args_sigfile_default = talloc_asprintf(opt,
119                 "%s.cmdline.sig", state->boot_image);
120         state->opt->args_sig_file = create_yaboot_devpath_resource(state,
121                                 conf, args_sigfile_default);
122         talloc_free(args_sigfile_default);
123
124         if (state->initrd) {
125                 state->opt->initrd = create_yaboot_devpath_resource(state,
126                                 conf, state->initrd);
127         }
128
129         if (state->initrd_size) {
130                 opt->boot_args = talloc_asprintf(opt, "ramdisk_size=%s %s",
131                                         state->initrd_size, opt->boot_args);
132         }
133
134         if (state->ramdisk) {
135                 opt->boot_args = talloc_asprintf(opt, "ramdisk=%s %s",
136                                         state->initrd_size, opt->boot_args);
137         }
138
139         if (state->root) {
140                 opt->boot_args = talloc_asprintf(opt, "root=%s %s",
141                                         state->root, opt->boot_args);
142         }
143
144         if (state->read_only && state->read_write) {
145                 pb_log("boot option %s specified both 'ro' and 'rw', "
146                                 "using 'rw'\n", opt->name);
147                 state->read_only = false;
148         }
149
150         if (state->read_only || state->read_write) {
151                 opt->boot_args = talloc_asprintf(opt, "%s %s",
152                                         state->read_only ? "ro" : "rw",
153                                         opt->boot_args);
154         }
155
156         if (state->literal) {
157                 opt->boot_args = talloc_strdup(opt, state->literal);
158         }
159
160         opt->description = talloc_asprintf(opt, "%s %s %s",
161                 state->boot_image,
162                 (state->initrd ? state->initrd : ""),
163                 opt->boot_args ? opt->boot_args : "");
164
165         conf_strip_str(opt->boot_args);
166         conf_strip_str(opt->description);
167
168         default_label = conf_get_global_option(conf, "default");
169         if (default_label &&
170                         !strcasecmp(state->opt->option->name, default_label))
171                 state->opt->option->is_default = true;
172
173         discover_context_add_boot_option(conf->dc, state->opt);
174 }
175
176 static void yaboot_process_pair(struct conf_context *conf, const char *name,
177                 char *value)
178 {
179         struct yaboot_state *state = conf->parser_info;
180         struct discover_boot_option *opt = state->opt;
181         struct fixed_pair {
182                 const char *image;
183                 const char *initrd;
184         };
185         static const struct fixed_pair suse_fp32 = {
186                 .image = "/suseboot/vmlinux32",
187                 .initrd = "/suseboot/initrd32",
188         };
189         static const struct fixed_pair suse_fp64 = {
190                 .image = "/suseboot/vmlinux64",
191                 .initrd = "/suseboot/initrd64",
192         };
193         const struct fixed_pair *suse_fp;
194
195         /* fixup for bare values */
196
197         if (!name)
198                 name = value;
199
200         if (!state->globals_done && conf_set_global_option(conf, name, value))
201                 return;
202
203         /* image */
204         if (streq(name, "image")) {
205                 /* an image section finishes our global defintions */
206                 state->globals_done = 1;
207
208                 /* First finish any previous image. */
209                 if (opt)
210                         yaboot_finish(conf);
211
212                 /* Then start the new image. */
213                 opt = state_start_new_option(conf, state);
214
215                 state->boot_image = talloc_strdup(state, value);
216
217                 return;
218         }
219
220         /* Special processing for SUSE install CD. */
221
222         if (streq(name, "image[32bit]"))
223                 suse_fp = &suse_fp32;
224         else if (streq(name, "image[64bit]"))
225                 suse_fp = &suse_fp64;
226         else
227                 suse_fp = NULL;
228
229         if (suse_fp) {
230                 /* First finish any previous image. */
231                 if (opt)
232                         yaboot_finish(conf);
233
234                 /* Then start the new image. */
235                 opt = state_start_new_option(conf, state);
236
237                 if (*value == '/') {
238                         state->boot_image = talloc_strdup(state, value);
239                 } else {
240                         state->boot_image = talloc_strdup(state,
241                                                         suse_fp->image);
242                         state->initrd = talloc_strdup(state, suse_fp->initrd);
243                 }
244
245                 return;
246         }
247
248         /* all other processing requires an image */
249         if (!opt) {
250                 pb_debug("%s: unknown name: %s\n", __func__, name);
251                 return;
252         }
253
254         /* initrd */
255         if (streq(name, "initrd")) {
256                 state->initrd = talloc_strdup(state, value);
257                 return;
258         }
259
260         /* label */
261         if (streq(name, "label")) {
262                 opt->option->id = talloc_asprintf(opt->option, "%s#%s",
263                         conf->dc->device->device->id, value);
264                 opt->option->name = talloc_strdup(opt->option, value);
265                 return;
266         }
267
268         /* args */
269         if (streq(name, "device")) {
270                 printf("option device : %s", value);
271                 state->device = talloc_strdup(state, value);
272                 return;
273         }
274
275         if (streq(name, "parititon")) {
276                 state->partition = talloc_strdup(state, value);
277                 return;
278         }
279
280         if (streq(name, "append")) {
281                 opt->option->boot_args = talloc_asprintf_append(
282                         opt->option->boot_args, "%s ", value);
283                 return;
284         }
285
286         if (streq(name, "initrd-size")) {
287                 state->initrd_size = talloc_strdup(state, value);
288                 return;
289         }
290
291         if (streq(name, "literal")) {
292                 state->literal = talloc_strdup(state, value);
293                 return;
294         }
295
296         if (streq(name, "ramdisk")) {
297                 state->ramdisk = talloc_strdup(state, value);
298                 return;
299         }
300
301         if (streq(name, "read-only")) {
302                 state->read_only = true;
303                 return;
304         }
305
306         if (streq(name, "read-write")) {
307                 state->read_write = true;
308                 return;
309         }
310
311         if (streq(name, "root")) {
312                 state->root = talloc_strdup(state, value);
313                 return;
314         }
315
316         pb_debug("%s: unknown name: %s\n", __func__, name);
317 }
318
319 static struct conf_global_option yaboot_global_options[] = {
320         { .name = "root" },
321         { .name = "device" },
322         { .name = "partition" },
323         { .name = "initrd" },
324         { .name = "initrd_size" },
325         { .name = "video" },
326         { .name = "literal" },
327         { .name = "ramdisk" },
328         { .name = "default" },
329         { .name = NULL },
330 };
331
332 static const char *const yaboot_conf_files[] = {
333         "/yaboot.conf",
334         "/yaboot.cnf",
335         "/etc/lilo.conf",
336         "/etc/silo.conf",
337         "/etc/yaboot.conf",
338         "/etc/yaboot.cnf",
339         "/suseboot/yaboot.cnf",
340         "/YABOOT.CONF",
341         "/YABOOT.CNF",
342         "/ETC/LILO.CONF",
343         "/ETC/SILO.CONF",
344         "/ETC/YABOOT.CONF",
345         "/ETC/YABOOT.CNF",
346         "/SUSEBOOT/YABOOT.CNF",
347         NULL
348 };
349
350 static int yaboot_parse(struct discover_context *dc)
351 {
352         const char * const *filename;
353         struct yaboot_state *state;
354         struct conf_context *conf;
355         int len, rc;
356         char *buf;
357
358         /* Support block device boot only at present */
359         if (dc->event)
360                 return -1;
361
362         conf = talloc_zero(dc, struct conf_context);
363
364         if (!conf)
365                 return -1;
366
367         conf->dc = dc;
368         conf->global_options = yaboot_global_options,
369         conf_init_global_options(conf);
370         conf->get_pair = conf_get_pair_equal;
371         conf->process_pair = yaboot_process_pair;
372         conf->finish = yaboot_finish;
373         conf->parser_info = state = talloc_zero(conf, struct yaboot_state);
374
375         state->opt = NULL;
376
377         for (filename = yaboot_conf_files; *filename; filename++) {
378                 rc = parser_request_file(dc, dc->device, *filename, &buf, &len);
379                 if (rc)
380                         continue;
381
382                 conf_parse_buf(conf, buf, len);
383                 talloc_free(buf);
384         }
385
386         talloc_free(conf);
387         return 0;
388 }
389
390 static struct parser yaboot_parser = {
391         .name                   = "yaboot",
392         .parse                  = yaboot_parse,
393         .resolve_resource       = resolve_devpath_resource,
394 };
395
396 register_parser(yaboot_parser);