Add support for GPG signature enforcement on booted
[petitboot] / discover / grub2 / builtins.c
1
2 #include <stdio.h>
3 #include <string.h>
4
5 #include <log/log.h>
6 #include <types/types.h>
7 #include <talloc/talloc.h>
8 #include <util/util.h>
9 #include <url/url.h>
10
11 #include "discover/resource.h"
12 #include "discover/parser.h"
13 #include "grub2.h"
14
15
16 static int builtin_set(struct grub2_script *script,
17                 void *data __attribute__((unused)),
18                 int argc, char *argv[])
19 {
20         char *name, *value, *p;
21         int i;
22
23         if (argc < 2)
24                 return -1;
25
26         p = strchr(argv[1], '=');
27         if (!p)
28                 return -1;
29
30         name = talloc_strndup(script, argv[1], p - argv[1]);
31         value = talloc_strdup(script, p+1);
32
33         for (i = 2; i < argc; i++)
34                 value = talloc_asprintf_append(value, " %s", argv[i]);
35
36         script_env_set(script, name, value);
37
38         return 0;
39 }
40
41 static int builtin_linux(struct grub2_script *script,
42                 void *data __attribute__((unused)),
43                 int argc, char *argv[])
44 {
45         struct discover_boot_option *opt = script->opt;
46         const char *root;
47         int i;
48
49         if (!opt) {
50                 pb_log("grub2 syntax error: 'linux' statement outside "
51                                 "a menuentry.\n");
52                 return -1;
53         }
54
55         if (argc < 2) {
56                 pb_log("grub2 syntax error: no filename provided to "
57                                 "linux statement\n");
58                 return -1;
59         }
60
61         root = script_env_get(script, "root");
62
63         opt->boot_image = create_grub2_resource(opt, script->ctx->device,
64                                                 root, argv[1]);
65         opt->option->boot_args = NULL;
66
67         if (argc > 2)
68                 opt->option->boot_args = talloc_strdup(opt, argv[2]);
69
70         for (i = 3; i < argc; i++)
71                 opt->option->boot_args = talloc_asprintf_append(
72                                                 opt->option->boot_args,
73                                                 " %s", argv[i]);
74
75         char* args_sigfile_default = talloc_asprintf(opt,
76                 "%s.cmdline.sig", argv[1]);
77         opt->args_sig_file = create_grub2_resource(opt, script->ctx->device,
78                                                 root, args_sigfile_default);
79         talloc_free(args_sigfile_default);
80         return 0;
81 }
82
83 static int builtin_initrd(struct grub2_script *script,
84                 void *data __attribute__((unused)),
85                 int argc, char *argv[])
86 {
87         struct discover_boot_option *opt = script->opt;
88         const char *root;
89
90         if (!opt) {
91                 pb_log("grub2 syntax error: 'initrd' statement outside "
92                                 "a menuentry.\n");
93                 return -1;
94         }
95
96         if (argc < 2) {
97                 pb_log("grub2 syntax error: no filename provided to "
98                                 "initrd statement\n");
99                 return -1;
100         }
101
102         root = script_env_get(script, "root");
103         opt->initrd = create_grub2_resource(opt, script->ctx->device,
104                                                 root, argv[1]);
105
106         return 0;
107 }
108
109 static int builtin_search(struct grub2_script *script,
110                 void *data __attribute__((unused)),
111                 int argc, char *argv[])
112 {
113         const char *env_var, *spec;
114         int i;
115
116         env_var = NULL;
117
118         for (i = 1; i < argc - 1; i++) {
119                 if (!strncmp(argv[i], "--set=", strlen("--set="))) {
120                         env_var = argv[i] + strlen("--set=");
121                         break;
122                 }
123         }
124
125         if (!env_var)
126                 return 0;
127
128         spec = argv[argc - 1];
129
130         script_env_set(script, env_var, spec);
131
132         return 0;
133 }
134
135 /* Note that GRUB does not follow symlinks in evaluating all file
136  * tests but -s, unlike below. However, it seems like a bad idea to
137  * emulate GRUB's behavior (e.g., it would take extra work), so we
138  * implement the behavior that coreutils' test binary has. */
139 static bool builtin_test_op_file(struct grub2_script *script, char op,
140                 const char *file)
141 {
142         bool result;
143         int rc;
144         struct stat statbuf;
145
146         rc = parser_stat_path(script->ctx, script->ctx->device,
147                         file, &statbuf);
148         if (rc)
149                 return false;
150
151         switch (op) {
152         case 's':
153                 /* -s: return true if file exists and has non-zero size */
154                 result = statbuf.st_size > 0;
155                 break;
156         case 'f':
157                 /* -f: return true if file exists and is not a directory. This is
158                  * different than the behavior of "test", but is what GRUB does
159                  * (though note as above that we follow symlinks unlike GRUB). */
160                 result = !S_ISDIR(statbuf.st_mode);
161                 break;
162         default:
163                 result = false;
164
165         }
166
167         return result;
168 }
169
170 /* See comment at builtin_test_op_file for differences between how
171  * GRUB implements file tests versus Petitboot's GRUB parser. */
172 static bool builtin_test_op_dir(struct grub2_script *script, char op,
173                 const char *dir)
174 {
175         int rc;
176         struct stat statbuf;
177
178         if (op != 'd')
179                 return false;
180
181         rc = parser_stat_path(script->ctx, script->ctx->device, dir, &statbuf);
182         if (rc) {
183                 return false;
184         }
185
186         return S_ISDIR(statbuf.st_mode);
187 }
188
189 static bool builtin_test_op(struct grub2_script *script,
190                 int argc, char **argv, int *consumed)
191 {
192         char *op;
193
194         if (argc >= 3) {
195                 const char *a1, *a2;
196
197                 a1 = argv[0];
198                 op = argv[1];
199                 a2 = argv[2];
200
201                 if (!strcmp(op, "=") || !strcmp(op, "==")) {
202                         *consumed = 3;
203                         return !strcmp(a1, a2);
204                 }
205
206                 if (!strcmp(op, "!=")) {
207                         *consumed = 3;
208                         return strcmp(a1, a2);
209                 }
210
211                 if (!strcmp(op, "<")) {
212                         *consumed = 3;
213                         return strcmp(a1, a2) < 0;
214                 }
215
216                 if (!strcmp(op, ">")) {
217                         *consumed = 3;
218                         return strcmp(a1, a2) > 0;
219                 }
220         }
221
222         if (argc >= 2) {
223                 const char *a1;
224
225                 op = argv[0];
226                 a1 = argv[1];
227
228                 if (!strcmp(op, "-z")) {
229                         *consumed = 2;
230                         return strlen(a1) == 0;
231                 }
232
233                 if (!strcmp(op, "-n")) {
234                         *consumed = 2;
235                         return strlen(a1) != 0;
236                 }
237
238                 if (!strcmp(op, "-s") || !strcmp(op, "-f")) {
239                         *consumed = 2;
240                         return builtin_test_op_file(script, op[1], a1);
241                 }
242
243                 if (!strcmp(op, "-d")) {
244                         *consumed = 2;
245                         return builtin_test_op_dir(script, op[1], a1);
246                 }
247         }
248
249         op = argv[0];
250         *consumed = 1;
251         return strlen(op) > 0;
252 }
253
254 static int builtin_test(struct grub2_script *script,
255                 void *data __attribute__((unused)),
256                 int argc, char *argv[])
257 {
258         int consumed;
259         bool not, rc;
260
261         if (!strcmp(argv[0], "[") && !strcmp(argv[argc - 1], "]"))
262                 argc--;
263
264         /* skip command name */
265         argc--;
266         argv++;
267
268         not = false;
269         rc = false;
270
271         for (consumed = 0; argc > 0; argv += consumed, argc -= consumed) {
272
273                 if (!strcmp(argv[0], "!")) {
274                         not = true;
275                         consumed = 1;
276                         continue;
277                 }
278
279                 if (!strcmp(argv[0], "-a")) {
280                         if (!rc)
281                                 return 1;
282                         consumed = 1;
283                         continue;
284                 }
285
286                 if (!strcmp(argv[0], "-o")) {
287                         if (rc)
288                                 return 0;
289                         consumed = 1;
290                         continue;
291                 }
292
293                 rc = builtin_test_op(script, argc, argv, &consumed);
294                 if (not) {
295                         rc = !rc;
296                         not = false;
297                 }
298         }
299
300         return rc ? 0 : 1;
301 }
302
303 static int builtin_true(struct grub2_script *script __attribute__((unused)),
304                 void *data __attribute__((unused)),
305                 int argc __attribute__((unused)),
306                 char *argv[] __attribute__((unused)))
307 {
308         return 0;
309 }
310
311 static int builtin_false(struct grub2_script *script __attribute__((unused)),
312                 void *data __attribute__((unused)),
313                 int argc __attribute__((unused)),
314                 char *argv[] __attribute__((unused)))
315 {
316         return 1;
317 }
318
319 static int builtin_nop(struct grub2_script *script __attribute__((unused)),
320                 void *data __attribute__((unused)),
321                 int argc __attribute__((unused)),
322                 char *argv[] __attribute__((unused)))
323 {
324         return 0;
325 }
326
327 extern int builtin_load_env(struct grub2_script *script,
328                 void *data __attribute__((unused)),
329                 int argc, char *argv[]);
330 int builtin_save_env(struct grub2_script *script,
331                 void *data __attribute__((unused)),
332                 int argc, char *argv[]);
333
334
335 static struct {
336         const char *name;
337         grub2_function fn;
338 } builtins[] = {
339         {
340                 .name = "set",
341                 .fn = builtin_set,
342         },
343         {
344                 .name = "linux",
345                 .fn = builtin_linux,
346         },
347         {
348                 .name = "linux16",
349                 .fn = builtin_linux,
350         },
351         {
352                 .name = "initrd",
353                 .fn = builtin_initrd,
354         },
355         {
356                 .name = "search",
357                 .fn = builtin_search,
358         },
359         {
360                 .name = "[",
361                 .fn = builtin_test,
362         },
363         {
364                 .name = "test",
365                 .fn = builtin_test,
366         },
367         {
368                 .name = "true",
369                 .fn = builtin_true,
370         },
371         {
372                 .name = "false",
373                 .fn = builtin_false,
374         },
375         {
376                 .name = "load_env",
377                 .fn = builtin_load_env,
378         },
379         {
380                 .name = "save_env",
381                 .fn = builtin_save_env,
382         },
383 };
384
385 static const char *nops[] = {
386         "echo", "export", "insmod", "loadfont", "terminfo",
387 };
388
389 void register_builtins(struct grub2_script *script)
390 {
391         unsigned int i;
392
393         for (i = 0; i < ARRAY_SIZE(builtins); i++)
394                 script_register_function(script, builtins[i].name,
395                                 builtins[i].fn, NULL);
396
397         for (i = 0; i < ARRAY_SIZE(nops); i++)
398                 script_register_function(script, nops[i], builtin_nop, NULL);
399 }