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