xref: /freebsd/sbin/veriexec/manifest_parser.y (revision b9f654b163bce26de79705e77b872427c9f2afa1)
1 %{
2 /*-
3  * Copyright (c) 2004-2018, Juniper Networks, Inc.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
15  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
16  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
17  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
18  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
20  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include <sys/stat.h>
30 #include <stdio.h>
31 #include <ctype.h>
32 #include <err.h>
33 #include <sysexits.h>
34 #include <libsecureboot.h>
35 
36 #include "veriexec.h"
37 
38 int yylex(void);
39 void yyerror(const char *);
40 
41 /* function prototypes */
42 static int convert(char *fp, unsigned int count, unsigned char *out);
43 static void do_ioctl(void);
44 static int get_fingerprint_type(const char *fp_type);
45 
46 /* ioctl parameter struct */
47 #ifdef MAXLABELLEN
48 static struct verified_exec_label_params lparams;
49 static struct verified_exec_params *params = &lparams.params;
50 #else
51 static struct verified_exec_params oparams;
52 static struct verified_exec_params *params = &oparams;
53 #endif
54 
55 #ifndef SHA256_DIGEST_LENGTH
56 # define SHA_DIGEST_LENGTH br_sha1_SIZE
57 # define SHA256_DIGEST_LENGTH br_sha256_SIZE
58 # define SHA384_DIGEST_LENGTH br_sha384_SIZE
59 #endif
60 
61 static int fmode;
62 
63 extern int lineno;
64 extern int dev_fd;
65 
66 struct fingerprint_type {
67 	const char *fp_type;
68 	int fp_size;
69 };
70 
71 /* static globals */
72 static const struct fingerprint_type fingerprint_table[] = {
73 	{ "sha1", SHA_DIGEST_LENGTH },
74 	{ "sha256", SHA256_DIGEST_LENGTH },
75 #if MAXFINGERPRINTLEN > 32
76 	{ "sha384", SHA384_DIGEST_LENGTH },
77 #endif
78 	{ NULL, 0 }
79 };
80 
81 /*
82  * Indicate to lexer our version.
83  * A token #>NUMBER will be consumed (and discared)
84  * by lexer if parser_version > NUMBER
85  * Otherwise the rest of the line will be discared
86  * as for a comment.
87  */
88 int parser_version = 1;
89 
90 %}
91 
92 %union {
93 	char *string;
94 	int  intval;
95 }
96 
97 %token EOL
98 %token <string> EQ
99 %token <string> PATH
100 %token <string> STRING
101 
102 %%
103 
104 statement: /* empty */
105 	| statement path attributes eol
106 	| statement error eol {
107 		yyclearin; /* discard lookahead */
108 		yyerrok;   /* no more error */
109 		fprintf(stderr,
110 		    "skipping to next fingerprint\n");
111 	}
112 	;
113 
114 attributes: /* empty */
115 	| attributes flag
116 	| attributes attr
117 	;
118 
119 attr: STRING EQ STRING
120 {
121 	int fptype;
122 
123 	fptype = get_fingerprint_type($1);
124 
125 	/*
126 	 * There's only one attribute we care about
127 	 */
128 	if (fingerprint_table[fptype].fp_size) {
129 		strlcpy(params->fp_type, $1, sizeof(params->fp_type));
130 		if (convert($3, fingerprint_table[fptype].fp_size,
131 			params->fingerprint) < 0) {
132 			yyerror("bad fingerprint");
133 			YYERROR;
134 		}
135 	} else if (strcmp($1, "label") == 0) {
136 		static int warned_labels = 0;
137 
138 #ifdef VERIEXEC_LABEL
139 		strlcpy(lparams.label, $3, sizeof(lparams.label));
140 		VERBOSE(3, ("version=%d label=%s\n", VeriexecVersion,
141 			lparams.label));
142 		if (VeriexecVersion > 1) {
143 			params->flags |= VERIEXEC_LABEL;
144 		} else
145 #endif
146 		if (!warned_labels) {
147 			warnx("ignoring labels");
148 			warned_labels = 1;
149 		}
150 	} else if (strcmp($1, "mode") == 0) {
151 		fmode = (int)strtol($3, NULL, 8);
152 	}
153 };
154 
155 flag: STRING
156 {
157 	/*
158 	 * indirect only matters if the interpreter itself is not
159 	 * executable.
160 	 */
161 	if (!strcmp($1, "indirect")) {
162 		params->flags |= VERIEXEC_INDIRECT;
163 	} else if (!strcmp($1, "no_ptrace")) {
164 		params->flags |= VERIEXEC_NOTRACE;
165 	} else if (!strcmp($1, "trusted")) {
166 		params->flags |= VERIEXEC_TRUSTED;
167 	} else if (!strcmp($1, "no_fips")) {
168 #ifdef VERIEXEC_NOFIPS
169 		params->flags |= VERIEXEC_NOFIPS;
170 #endif
171 	}
172 }
173 ;
174 
175 path: PATH
176 {
177 	if (strlen($1) >= MAXPATHLEN) {
178 		yyerror("Path >= MAXPATHLEN");
179 		YYERROR;
180 	}
181 	/*
182 	 * The majority of files in the manifest are relative
183 	 * to the package mount point, but we want absolute paths.
184 	 * Prepending '/' is actually all we need.
185 	 */
186 	if (snprintf(params->file, sizeof(params->file), "%s%s%s",
187 		Cdir ? Cdir : "",
188 		($1[0] == '/') ? "" : "/",
189 		$1) >= (int)sizeof(params->file)) {
190 		errx(EX_DATAERR, "cannot form pathname");
191 	}
192 	params->flags = 0;
193 	fmode = -1;			/* unknown */
194 };
195 
196 eol: EOL
197 {
198 	if (!YYRECOVERING()) { /* Don't do the ioctl if we saw an error */
199 		do_ioctl();
200 	}
201 	params->fp_type[0] = '\0';	/* invalidate it */
202 };
203 
204 %%
205 
206 void
207 manifest_parser_init(void)
208 {
209 	params->fp_type[0] = '\0';      /* invalidate it */
210 }
211 
212 int
213 get_fingerprint_type(const char *fp_type)
214 {
215 	int i;
216 
217 	for (i = 0; fingerprint_table[i].fp_type; i++)
218 		if (!strcmp(fp_type, fingerprint_table[i].fp_type))
219 			break;
220 
221 	return (i);
222 }
223 
224 /*
225  * Convert: takes the hexadecimal string pointed to by fp and converts
226  * it to a "count" byte binary number which is stored in the array pointed to
227  * by out.  Returns -1 if the conversion fails.
228  */
229 static int
230 convert(char *fp, unsigned int count, unsigned char *out)
231 {
232         unsigned int i;
233         int value;
234 
235         for (i = 0; i < count; i++) {
236 		value = 0;
237 		if (isdigit(fp[i * 2]))
238 			value += fp[i * 2] - '0';
239 		else if (isxdigit(fp[i * 2]))
240 			value += 10 + tolower(fp[i * 2]) - 'a';
241 		else
242 			return (-1);
243 		value <<= 4;
244 		if (isdigit(fp[i * 2 + 1]))
245 			value += fp[i * 2 + 1] - '0';
246 		else if (isxdigit(fp[i * 2 + 1]))
247 			value += 10 + tolower(fp[i * 2 + 1]) - 'a';
248 		else
249 			return (-1);
250 		out[i] = value;
251 	}
252 
253 	return (i);
254 }
255 
256 /*
257  * Perform the load of the fingerprint.  Assumes that the fingerprint
258  * pseudo-device is opened and the file handle is in fd.
259  */
260 static void
261 do_ioctl(void)
262 {
263 	struct stat st;
264 
265 	if (params->fp_type[0] == '\0') {
266 		VERBOSE(1,("skipping %s\n", params->file));
267 		return;
268 	}
269 
270 	/*
271 	 * See if the path is executable, if not put it on the FILE list.
272 	 */
273 	if (fmode > 0) {
274 		if (!(fmode & (S_IXUSR|S_IXGRP|S_IXOTH))) {
275 			params->flags |= VERIEXEC_FILE;
276 		}
277 	} else if (stat(params->file, &st) == 0) {
278 		if (!(st.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))) {
279 			params->flags |= VERIEXEC_FILE;
280 		}
281 	}
282 	/*
283 	 * We may be forcing some flags...
284 	 */
285 	params->flags |= ForceFlags;
286 	VERBOSE(1, ("loading %s for %s %s flags=%#x\n",
287 		params->fp_type,
288 		(params->flags == VERIEXEC_FILE) ? "file" : "executable",
289 		params->file, params->flags));
290 
291 #ifdef VERIEXEC_LABEL
292 	if (params->flags & VERIEXEC_LABEL) {
293 		if (ioctl(dev_fd, VERIEXEC_LABEL_LOAD, &lparams) < 0)
294 			warn("cannot update veriexec label for %s",
295 			    params->file);
296 	} else
297 #endif
298 	if (ioctl(dev_fd, VERIEXEC_SIGNED_LOAD, params) < 0)
299 		warn("cannot update veriexec for %s", params->file);
300 	params->fp_type[0] = '\0';
301 }
302