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
valid_name(const char * name)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
parse_bootconf(bc_handle_t * handle,const char * bootconf)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
valid_encryption(bc_handle_t * handle,boolean_t * is_encrypted)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
valid_signature(bc_handle_t * handle,boolean_t * is_signed)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
valid_client_authentication(bc_handle_t * handle,boolean_t * is_authenticated)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
valid_server_authentication(bc_handle_t * handle,boolean_t * is_authenticated)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
valid_root_server(bc_handle_t * handle,boolean_t * is_https)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
valid_boot_logger(bc_handle_t * handle,boolean_t * is_https)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
validate_bootconf(bc_handle_t * handle)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
bootconf_end(bc_handle_t * handle)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
bootconf_init(bc_handle_t * handle,const char * bootconf)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 *
bootconf_get(bc_handle_t * handle,const char * name)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