xref: /titanic_51/usr/src/common/net/wanboot/bootconf.c (revision 80e2ca8596e3435bc3b76f3c597833ea0a87f85e)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2003 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Functions for accessing the wanboot.conf(4) file.
30  */
31 
32 #include <stdio.h>
33 #include <string.h>
34 #include <sys/types.h>
35 #include <parseURL.h>
36 #include <netboot_paths.h>
37 #include <wanboot_conf.h>
38 
39 /*
40  * Parser helper macros:
41  */
42 #define	is_whitespace(c)	((c) == ' ' || (c) == '\t')
43 #define	skip_whitespace(p)	while (is_whitespace(*(p))) ++p
44 
45 /*
46  * Table of valid wanboot.conf(4) names:
47  */
48 static const char *bootconf_names[] = {
49 	BC_BOOT_FILE,
50 	BC_ROOT_SERVER,
51 	BC_ROOT_FILE,
52 	BC_ENCRYPTION_TYPE,
53 	BC_SIGNATURE_TYPE,
54 	BC_CLIENT_AUTHENTICATION,
55 	BC_SERVER_AUTHENTICATION,
56 	BC_BOOT_LOGGER,
57 	BC_RESOLVE_HOSTS,
58 	BC_SYSTEM_CONF,
59 	NULL
60 };
61 
62 /*
63  * Check whether 'name' is valid within wanboot.conf(4).
64  */
65 static boolean_t
66 valid_name(const char *name)
67 {
68 	int	i;
69 
70 	for (i = 0; bootconf_names[i] != NULL; ++i) {
71 		if (strcmp(name, bootconf_names[i]) == 0) {
72 			return (B_TRUE);
73 		}
74 	}
75 
76 	return (B_FALSE);
77 }
78 
79 /*
80  * parse_bootconf() parses a wanboot.conf(4) file and, if there are no
81  * errors, creates an nvpair list of the name-value pairs defined therein.
82  *
83  * Lines must be blank or of the form:
84  *	[name=value] [# comment]
85  *
86  * Returns:
87  *	B_TRUE	- success
88  *	B_FALSE	- error (return code in handle->bc_error_code, line number
89  *		  on which the error occurred in handle->bc_error_pos)
90  */
91 static boolean_t
92 parse_bootconf(bc_handle_t *handle, const char *bootconf)
93 {
94 	FILE		*fp = NULL;
95 	nvlist_t	*nvl = NULL;
96 	char		line[BC_MAX_LINE_LENGTH];
97 
98 	if ((fp = fopen(bootconf, "r")) == NULL) {
99 		handle->bc_error_code = BC_E_ACCESS;
100 		goto cleanup;
101 	}
102 
103 	if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0) {
104 		handle->bc_error_code = BC_E_NVLIST;
105 		goto cleanup;
106 	}
107 
108 	while (fgets(line, sizeof (line), fp) != NULL) {
109 		int	i;
110 		char	*p = line;
111 		char	*ks, *ke, *vs, *ve;
112 		char	quote;
113 
114 		++(handle->bc_error_pos);
115 
116 		/*
117 		 * Strip off the '\n' at the end of the line.
118 		 */
119 		if ((i = strlen(line)) < 1) {
120 			handle->bc_error_code = BC_E_IOERR;
121 			goto cleanup;
122 		} else if (line[i - 1] != '\n') {
123 			handle->bc_error_code = BC_E_TOO_LONG;
124 			goto cleanup;
125 		}
126 		line[i - 1] = '\0';
127 
128 		/*
129 		 * Skip leading whitespace.
130 		 */
131 		skip_whitespace(p);
132 
133 		/*
134 		 * Blank line/comment-only line?
135 		 */
136 		if (*p == '\0' || *p == '#') {
137 			continue;
138 		}
139 
140 		/*
141 		 * Get start and end pointers to the 'name'.
142 		 */
143 		ks = p;
144 		while (!is_whitespace(*p) && *p != '=') {
145 			++p;
146 		}
147 		ke = p;
148 
149 		/*
150 		 * Must be of the form "name=value"; skip leading and
151 		 * trailing whitespace.
152 		 */
153 		skip_whitespace(p);
154 		if (*p == '=') {
155 			++p;		/* skip '=' */
156 			skip_whitespace(p);
157 		} else {
158 			handle->bc_error_code = BC_E_SYNTAX;
159 			goto cleanup;
160 		}
161 
162 		/*
163 		 * The 'value' may be quoted.
164 		 */
165 		if (*p == '"' || *p == '\'') {
166 			quote = *p;
167 			++p;		/* skip '"' */
168 		} else {
169 			quote = '\0';
170 		}
171 
172 		/*
173 		 * Get start and end pointers to the 'value' string.
174 		 * Note that 'value' may be the empty string.
175 		 */
176 		vs = p;
177 		if (quote != '\0' || *p != '#') {
178 			while (*p != '\0' && *p != quote) {
179 				/*
180 				 * White space that is not part of a quoted
181 				 * value signals end of value.
182 				 */
183 				if (is_whitespace(*p) && quote == '\0') {
184 					break;
185 				}
186 				++p;
187 			}
188 		}
189 		ve = p;
190 
191 		/*
192 		 * If 'value' string was quoted, ensure that there is a
193 		 * balancing close-quote and skip it.
194 		 */
195 		if (quote != '\0') {
196 			if (*p == quote) {
197 				++p;
198 			} else {
199 				handle->bc_error_code = BC_E_SYNTAX;
200 				goto cleanup;
201 			}
202 		}
203 
204 		/*
205 		 * Verify line is well-formed; the rest of the line should
206 		 * be blank or comment.
207 		 */
208 		skip_whitespace(p);
209 		if (*p != '\0' && *p != '#') {
210 			handle->bc_error_code = BC_E_SYNTAX;
211 			goto cleanup;
212 		}
213 
214 		/*
215 		 * Nul-terminate both the 'name' and the 'value' string.
216 		 */
217 		*ke = '\0';
218 		*ve = '\0';
219 
220 		/*
221 		 * Check that this is a valid parameter name.
222 		 */
223 		if (!valid_name(ks)) {
224 			handle->bc_error_code = BC_E_UNKNOWN_NAME;
225 			goto cleanup;
226 		}
227 
228 		/*
229 		 * Add the name-value pair to the nvpair list.
230 		 */
231 		if (nvlist_add_string(nvl, ks, vs) != 0) {
232 			handle->bc_error_code = BC_E_NVLIST;
233 			goto cleanup;
234 		}
235 	}
236 
237 	/*
238 	 * Verify that we didn't exit the parsing loop because of an
239 	 * input error.
240 	 */
241 	if (ferror(fp)) {
242 		handle->bc_error_code = BC_E_IOERR;
243 		goto cleanup;
244 	}
245 
246 cleanup:
247 	/*
248 	 * Close the file if open and free the nvlist if an error occurred.
249 	 */
250 	if (fp != NULL && fclose(fp) != 0) {
251 		handle->bc_error_code = BC_E_IOERR;
252 	}
253 	if (handle->bc_error_code != BC_E_NOERROR) {
254 		if (nvl != NULL) {
255 			nvlist_free(nvl);
256 		}
257 		return (B_FALSE);
258 	}
259 
260 	/*
261 	 * All is well.
262 	 */
263 	handle->bc_nvl = nvl;
264 
265 	return (B_TRUE);
266 }
267 
268 /*
269  * valid_encryption() validitate the encryption type value
270  *
271  * Returns:
272  *	B_TRUE	- success
273  *	B_FALSE	- error (return code in handle->bc_error_code)
274  */
275 static boolean_t
276 valid_encryption(bc_handle_t *handle, boolean_t *is_encrypted)
277 {
278 	nvlist_t	*nvl = handle->bc_nvl;
279 	char		*strval;
280 
281 	/*
282 	 * Until proven otherwise, encryption is not enabled.
283 	 */
284 	*is_encrypted = B_FALSE;
285 
286 	/*
287 	 * If encryption_type was specified then it must be either
288 	 * "3des", "aes" or "".
289 	 */
290 	if (nvlist_lookup_string(nvl, BC_ENCRYPTION_TYPE, &strval) == 0) {
291 		if (strlen(strval) > 0) {
292 			if (strcmp(strval, BC_ENCRYPTION_3DES) != 0 &&
293 			    strcmp(strval, BC_ENCRYPTION_AES) != 0) {
294 				handle->bc_error_code = BC_E_ENCRYPTION_ILLEGAL;
295 				return (B_FALSE);
296 			}
297 			*is_encrypted = B_TRUE;
298 		}
299 	}
300 	return (B_TRUE);
301 }
302 
303 /*
304  * valid_signature() validates the signature type value
305  *
306  * Returns:
307  *	B_TRUE	- success
308  *	B_FALSE	- error (return code in handle->bc_error_code)
309  */
310 static boolean_t
311 valid_signature(bc_handle_t *handle, boolean_t *is_signed)
312 {
313 	nvlist_t	*nvl = handle->bc_nvl;
314 	char		*strval;
315 
316 	/*
317 	 * Until proven otherwise, signing is not enabled.
318 	 */
319 	*is_signed = B_FALSE;
320 
321 	/*
322 	 * If signature_type was specified then it must be either
323 	 * "sha1" or "".
324 	 */
325 	if (nvlist_lookup_string(nvl, BC_SIGNATURE_TYPE, &strval) == 0) {
326 		if (strlen(strval) > 0) {
327 			if (strcmp(strval, BC_SIGNATURE_SHA1) != 0) {
328 				handle->bc_error_code = BC_E_SIGNATURE_ILLEGAL;
329 				return (B_FALSE);
330 			}
331 			*is_signed = B_TRUE;
332 		}
333 	}
334 
335 	return (B_TRUE);
336 }
337 
338 /*
339  * valid_client_authentication() validates the client authentication value
340  *
341  * Returns:
342  *	B_TRUE	- success
343  *	B_FALSE	- error (return code in handle->bc_error_code)
344  */
345 static boolean_t
346 valid_client_authentication(bc_handle_t *handle, boolean_t *is_authenticated)
347 {
348 	nvlist_t	*nvl = handle->bc_nvl;
349 	char		*strval;
350 
351 	/*
352 	 * Until proven otherwise, authentication is not enabled.
353 	 */
354 	*is_authenticated = B_FALSE;
355 
356 	/*
357 	 * If client_authentication was specified then it must be either
358 	 * "yes" or "no".
359 	 */
360 	if (nvlist_lookup_string(nvl, BC_CLIENT_AUTHENTICATION, &strval) == 0) {
361 		if (strcmp(strval, BC_YES) == 0) {
362 			*is_authenticated = B_TRUE;
363 		} else if (strcmp(strval, BC_NO) != 0) {
364 			handle->bc_error_code = BC_E_CLIENT_AUTH_ILLEGAL;
365 			return (B_FALSE);
366 		}
367 	}
368 
369 	return (B_TRUE);
370 }
371 
372 /*
373  * valid_server_authentication() validates the server authentication value
374  *
375  * Returns:
376  *	B_TRUE	- success
377  *	B_FALSE	- error (return code in handle->bc_error_code)
378  */
379 static boolean_t
380 valid_server_authentication(bc_handle_t *handle, boolean_t *is_authenticated)
381 {
382 	nvlist_t	*nvl = handle->bc_nvl;
383 	char		*strval;
384 
385 	/*
386 	 * Until proven otherwise, authentication is not enabled.
387 	 */
388 	*is_authenticated = B_FALSE;
389 
390 	/*
391 	 * If server_authentication was specified then it must be either
392 	 * "yes" or"no".
393 	 */
394 	if (nvlist_lookup_string(nvl, BC_SERVER_AUTHENTICATION, &strval) == 0) {
395 		if (strcmp(strval, BC_YES) == 0) {
396 			*is_authenticated = B_TRUE;
397 		} else if (strcmp(strval, BC_NO) != 0) {
398 			handle->bc_error_code = BC_E_SERVER_AUTH_ILLEGAL;
399 			return (B_FALSE);
400 		}
401 	}
402 
403 	return (B_TRUE);
404 }
405 
406 /*
407  * valid_root_server() validates the root server and root file values
408  *
409  * Returns:
410  *	B_TRUE	- success
411  *	B_FALSE	- error (return code in handle->bc_error_code)
412  */
413 static boolean_t
414 valid_root_server(bc_handle_t *handle, boolean_t *is_https)
415 {
416 	nvlist_t	*nvl = handle->bc_nvl;
417 	char		*strval;
418 	url_t		url;
419 
420 	/*
421 	 * Until proven otherwise, assume not https.
422 	 */
423 	*is_https = B_FALSE;
424 
425 	/*
426 	 * Check whether a root_server URL was specified, and if so whether
427 	 * it is a secure URL (of the form https://...).
428 	 */
429 	if (nvlist_lookup_string(nvl, BC_ROOT_SERVER, &strval) == 0) {
430 		if (url_parse(strval, &url) != URL_PARSE_SUCCESS) {
431 			handle->bc_error_code = BC_E_ROOT_SERVER_BAD;
432 			return (B_FALSE);
433 		}
434 		*is_https = url.https;
435 
436 		/*
437 		 * Ensure that a root_file was also specified.
438 		 */
439 		if (nvlist_lookup_string(nvl, BC_ROOT_FILE, &strval) != 0 ||
440 		    strlen(strval) == 0) {
441 			handle->bc_error_code = BC_E_ROOT_FILE_ABSENT;
442 			return (B_FALSE);
443 		}
444 	} else {
445 		handle->bc_error_code = BC_E_ROOT_SERVER_ABSENT;
446 		return (B_FALSE);
447 	}
448 
449 	return (B_TRUE);
450 }
451 
452 /*
453  * valid_boot_logger() validates the boot_logger value
454  *
455  * Returns:
456  *	B_TRUE	- success
457  *	B_FALSE	- error (return code in handle->bc_error_code)
458  */
459 static boolean_t
460 valid_boot_logger(bc_handle_t *handle, boolean_t *is_https)
461 {
462 	nvlist_t	*nvl = handle->bc_nvl;
463 	char		*strval;
464 	url_t		url;
465 
466 	/*
467 	 * Until proven otherwise, assume not https.
468 	 */
469 	*is_https = B_FALSE;
470 
471 	/*
472 	 * If boot_logger was specified, make sure that it is a valid URL.
473 	 */
474 	if (nvlist_lookup_string(nvl, BC_BOOT_LOGGER, &strval) == 0 &&
475 	    strlen(strval) > 0) {
476 		if (url_parse(strval, &url) != URL_PARSE_SUCCESS) {
477 			handle->bc_error_code = BC_E_BOOT_LOGGER_BAD;
478 			return (B_FALSE);
479 		}
480 		*is_https = url.https;
481 	}
482 
483 	return (B_TRUE);
484 }
485 
486 /*
487  * validate_bootconf() checks the consistency of the nvpair list representation
488  * of a wanboot.conf(4) file as returned by the parse_bootconf() function.
489  *
490  * Returns:
491  *	B_TRUE	- success
492  *	B_FALSE	- error (return code in handle->bc_error_code)
493  */
494 static boolean_t
495 validate_bootconf(bc_handle_t *handle)
496 {
497 	boolean_t	is_encrypted;
498 	boolean_t	is_signed;
499 	boolean_t	client_is_authenticated;
500 	boolean_t	server_is_authenticated;
501 	boolean_t	rootserver_is_https;
502 	boolean_t	bootlogger_is_https;
503 
504 	/*
505 	 * Check to make sure option values are valid.
506 	 */
507 	if (!valid_encryption(handle, &is_encrypted) ||
508 	    !valid_signature(handle, &is_signed) ||
509 	    !valid_client_authentication(handle, &client_is_authenticated) ||
510 	    !valid_server_authentication(handle, &server_is_authenticated) ||
511 	    !valid_root_server(handle, &rootserver_is_https) ||
512 	    !valid_boot_logger(handle, &bootlogger_is_https))
513 		return (B_FALSE);
514 
515 	/*
516 	 * Now do consistency checking between bootconf settings.
517 	 */
518 	if (is_encrypted && !is_signed) {
519 		handle->bc_error_code = BC_E_ENCRYPTED_NOT_SIGNED;
520 		return (B_FALSE);
521 	}
522 	if (client_is_authenticated) {
523 		if (!(is_encrypted && is_signed)) {
524 			handle->bc_error_code = BC_E_CLIENT_AUTH_NOT_ENCRYPTED;
525 			return (B_FALSE);
526 		}
527 
528 		if (!server_is_authenticated) {
529 			handle->bc_error_code = BC_E_CLIENT_AUTH_NOT_SERVER;
530 			return (B_FALSE);
531 		}
532 	}
533 	if (server_is_authenticated) {
534 		if (!is_signed) {
535 			handle->bc_error_code = BC_E_SERVER_AUTH_NOT_SIGNED;
536 			return (B_FALSE);
537 		}
538 
539 		if (!rootserver_is_https) {
540 			handle->bc_error_code = BC_E_SERVER_AUTH_NOT_HTTPS;
541 			return (B_FALSE);
542 		}
543 	} else if (rootserver_is_https) {
544 		handle->bc_error_code = BC_E_SERVER_AUTH_NOT_HTTP;
545 		return (B_FALSE);
546 	} else if (bootlogger_is_https) {
547 		handle->bc_error_code = BC_E_BOOTLOGGER_AUTH_NOT_HTTP;
548 		return (B_FALSE);
549 	}
550 
551 	return (B_TRUE);
552 }
553 
554 
555 /*
556  * bootconf_end() cleans up once we're done accessing the nvpair list
557  * representation of wanboot.conf(4).
558  */
559 void
560 bootconf_end(bc_handle_t *handle)
561 {
562 	if (handle->bc_nvl != NULL) {
563 		nvlist_free(handle->bc_nvl);
564 		handle->bc_nvl = NULL;
565 	}
566 }
567 
568 /*
569  * bootconf_init() must be called to initialize 'handle' before bootconf_get()
570  * can be used to access values from the wanboot.conf(4) file.
571  */
572 int
573 bootconf_init(bc_handle_t *handle, const char *bootconf)
574 {
575 	/*
576 	 * Initalise the handle's fields to sensible values.
577 	 */
578 	handle->bc_nvl = NULL;
579 	handle->bc_error_code = BC_E_NOERROR;
580 	handle->bc_error_pos = 0;
581 
582 	/*
583 	 * Provide a default path for the bootconf file if none was given.
584 	 */
585 	if (bootconf == NULL) {
586 		bootconf = NB_WANBOOT_CONF_PATH;
587 	}
588 
589 	/*
590 	 * Check that we can successfully parse and validate the file.
591 	 */
592 	if (parse_bootconf(handle, bootconf) && validate_bootconf(handle)) {
593 		return (BC_SUCCESS);
594 	}
595 
596 	/*
597 	 * Parse/validate error; free any allocated resources.
598 	 */
599 	bootconf_end(handle);
600 
601 	return (BC_FAILURE);
602 }
603 
604 /*
605  * bootconf_get() returns the value of a parameter in the wanboot.conf(4) file.
606  *
607  * Returns:
608  *	!= NULL	- the given value
609  *	== NULL	- value not found or is empty
610  */
611 char *
612 bootconf_get(bc_handle_t *handle, const char *name)
613 {
614 	char	*strval;
615 
616 	/*
617 	 * Look up the name in bc_nvl and return its value if found.
618 	 */
619 	if (handle->bc_nvl != NULL &&
620 	    nvlist_lookup_string(handle->bc_nvl, (char *)name, &strval) == 0) {
621 		return (strlen(strval) == 0 ? NULL : strval);
622 	}
623 
624 	return (NULL);
625 }
626