Print this page
first pass
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/psm/stand/boot/sparc/common/wanboot.c
+++ new/usr/src/psm/stand/boot/sparc/common/wanboot.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
↓ open down ↓ |
16 lines elided |
↑ open up ↑ |
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
19 19 * CDDL HEADER END
20 20 */
21 21 /*
22 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 23 * Use is subject to license terms.
24 24 */
25 25
26 26 #include <sys/types.h>
27 -/* EXPORT DELETE START */
28 27 #include <sys/promif.h>
29 28 #include <sys/obpdefs.h>
30 29 #include <sys/bootvfs.h>
31 30 #include <sys/bootconf.h>
32 31 #include <netinet/in.h>
33 32 #include <sys/wanboot_impl.h>
34 33 #include <boot_http.h>
35 34 #include <aes.h>
36 35 #include <des3.h>
37 36 #include <cbc.h>
38 37 #include <hmac_sha1.h>
39 38 #include <sys/sha1.h>
40 39 #include <sys/sha1_consts.h>
41 40 #include <bootlog.h>
42 41 #include <parseURL.h>
43 42 #include <netboot_paths.h>
44 43 #include <netinet/inetutil.h>
45 44 #include <sys/salib.h>
46 45 #include <inet/mac.h>
47 46 #include <inet/ipv4.h>
48 47 #include <dhcp_impl.h>
49 48 #include <inet/dhcpv4.h>
50 49 #include <bootinfo.h>
51 50 #include <wanboot_conf.h>
52 51 #include "boot_plat.h"
53 52 #include "ramdisk.h"
54 53 #include "wbcli.h"
55 54
56 55 /*
57 56 * Types of downloads
58 57 */
59 58 #define MINIINFO "miniinfo"
60 59 #define MINIROOT "miniroot"
61 60 #define WANBOOTFS "wanbootfs"
62 61
63 62 #define WANBOOT_RETRY_NOMAX -1
64 63 #define WANBOOT_RETRY_ROOT_MAX 50
65 64 #define WANBOOT_RETRY_MAX 5
66 65 #define WANBOOT_RETRY_SECS 5
67 66 #define WANBOOT_RETRY_MAX_SECS 30
68 67
69 68 /*
70 69 * Our read requests should timeout after 25 seconds
71 70 */
72 71 #define SOCKET_READ_TIMEOUT 25
73 72
74 73 /*
75 74 * Experimentation has shown that an 8K download buffer is optimal
76 75 */
77 76 #define HTTP_XFER_SIZE 8192
78 77 static char buffer[HTTP_XFER_SIZE];
79 78
80 79 bc_handle_t bc_handle;
81 80
82 81 extern int determine_fstype_and_mountroot(char *);
83 82 extern uint64_t get_ticks(void);
84 83
85 84 /*
86 85 * The following is used to determine whether the certs and private key
87 86 * files will be in PEM format or PKCS12 format. 'use_p12' is zero
88 87 * to use PEM format, and 1 when PKCS12 format is to be used. It is
89 88 * done this way, as a global, so that it can be patched if needs be
90 89 * using the OBP debugger.
91 90 */
92 91 uint32_t use_p12 = 1;
93 92
94 93 #define CONTENT_LENGTH "Content-Length"
95 94
96 95 #define NONCELEN (2 * HMAC_DIGEST_LEN) /* two hex nibbles/byte */
97 96 #define WANBOOTFS_NONCE_FILE "/nonce"
98 97
99 98 static char nonce[NONCELEN + 1];
100 99
101 100 enum URLtype {
102 101 URLtype_wanbootfs = 0,
103 102 URLtype_miniroot = 1
104 103 };
105 104
106 105 static char *URLtoCGIcontent[] = {
107 106 "bootfs",
108 107 "rootfs"
109 108 };
110 109 #define CGIcontent(urltype) URLtoCGIcontent[urltype]
111 110
112 111 /* Encryption algorithms */
113 112 typedef enum {
114 113 ENCR_NONE,
115 114 ENCR_3DES,
116 115 ENCR_AES
117 116 } encr_type_t;
118 117
119 118 /* Hash algorithms */
120 119 typedef enum {
121 120 HASH_NONE,
122 121 HASH_HMAC_SHA1
123 122 } hash_type_t;
124 123
125 124 /*
126 125 * Keys ...
127 126 */
128 127 static encr_type_t encr_type = ENCR_NONE;
129 128 static unsigned char *g_encr_key = NULL;
130 129
131 130 static hash_type_t hash_type = HASH_NONE;
132 131 static unsigned char *g_hash_key = NULL;
133 132
134 133 void
135 134 print_errors(const char *func, http_handle_t handle)
136 135 {
137 136 char const *msg;
138 137 ulong_t err;
139 138 uint_t src;
140 139
141 140 while ((err = http_get_lasterr(handle, &src)) != 0) {
142 141 msg = http_errorstr(src, err);
143 142 bootlog("wanboot", BOOTLOG_ALERT,
144 143 "%s: errsrc %u, err %lu (0x%lx)", func, src, err, err);
145 144 bootlog("wanboot", BOOTLOG_ALERT, "%s", msg);
146 145 }
147 146 }
148 147
149 148 /*
150 149 * This routine is called by a consumer to determine whether or not a
151 150 * retry should be attempted. If a retry is in order (depends upon the
152 151 * 'retry_cnt' and 'retry_max' arguments), then this routine will print a
153 152 * message indicating this is the case and will determine an appropriate
154 153 * "sleep" time before retrying. The "sleep" time will depend upon the
155 154 * 'retry_cnt' and will max out at WANBOOT_RETRY_MAX_SECS.
156 155 *
157 156 * Returns:
158 157 * B_TRUE = retry is in order
159 158 * B_FALSE = retry limit exceeded
160 159 */
161 160 boolean_t
162 161 wanboot_retry(int retry_cnt, int retry_max)
163 162 {
164 163 unsigned int seconds;
165 164
166 165 if (retry_max == WANBOOT_RETRY_NOMAX || retry_cnt <= retry_max) {
167 166 seconds = WANBOOT_RETRY_SECS * retry_cnt;
168 167 if (seconds > WANBOOT_RETRY_MAX_SECS) {
169 168 seconds = WANBOOT_RETRY_MAX_SECS;
170 169 }
171 170 bootlog("wanboot", BOOTLOG_INFO,
172 171 "Will retry in %d seconds ...", seconds);
173 172 (void) sleep(seconds);
174 173 return (B_TRUE);
175 174 } else {
176 175 bootlog("wanboot", BOOTLOG_INFO,
177 176 "Maximum retries exceeded.");
178 177 return (B_FALSE);
179 178 }
180 179 }
181 180
182 181 /*
183 182 * Determine which encryption algorithm the client is configured to use.
184 183 * WAN boot determines which key to use by order of priority. That is
185 184 * multiple encryption keys may exist in the PROM, but the first one found
186 185 * (while searching in a preferred order) is the one that will be used.
187 186 */
188 187 static void
189 188 init_encryption(void)
190 189 {
191 190 static unsigned char key[WANBOOT_MAXKEYLEN];
192 191 size_t len = sizeof (key);
193 192
194 193 if (bootinfo_get(BI_AES_KEY, (char *)&key, &len, NULL) ==
195 194 BI_E_SUCCESS) {
196 195 encr_type = ENCR_AES;
197 196 g_encr_key = key;
198 197 } else if (bootinfo_get(BI_3DES_KEY, (char *)&key, &len, NULL) ==
199 198 BI_E_SUCCESS) {
200 199 encr_type = ENCR_3DES;
201 200 g_encr_key = key;
202 201 }
203 202 }
204 203
205 204 /*
206 205 * Determine whether the client is configured to use hashing.
207 206 */
208 207 static void
209 208 init_hashing(void)
210 209 {
211 210 static unsigned char key[WANBOOT_HMAC_KEY_SIZE];
212 211 size_t len = sizeof (key);
213 212
214 213 if (bootinfo_get(BI_SHA1_KEY, (char *)&key, &len, NULL) ==
215 214 BI_E_SUCCESS) {
216 215 hash_type = HASH_HMAC_SHA1;
217 216 g_hash_key = key;
218 217 }
219 218 }
220 219
221 220 /*
222 221 * Read some CPU-specific rapidly-varying data (assumed to be of length
223 222 * sizeof (hrtime_t) in the non-SPARC case), and digestify it to further
224 223 * randomize the output.
225 224 */
226 225 char *
227 226 generate_nonce(void)
228 227 {
229 228 uint64_t t;
230 229 SHA1_CTX c;
231 230 unsigned char digest[HMAC_DIGEST_LEN];
232 231 uint_t nlen = sizeof (nonce);
233 232
234 233 int err;
235 234
236 235 /*
237 236 * Read SPARC %tick register or x86 TSC
238 237 */
239 238 t = get_ticks();
240 239 SHA1Init(&c);
241 240 SHA1Update(&c, (const uint8_t *)&t, sizeof (t));
242 241 SHA1Final(digest, &c);
243 242
244 243 err = octet_to_hexascii(digest, sizeof (digest), nonce, &nlen);
245 244 if (err != 0) {
246 245 bootlog("wanboot", BOOTLOG_CRIT,
247 246 "cannot convert nonce to ASCII: error %d", err);
248 247 return (NULL);
249 248 }
250 249 nonce[NONCELEN] = '\0';
251 250 return (nonce);
252 251 }
253 252
254 253 /*
255 254 * Given a server URL, builds a URL to request one of the wanboot
256 255 * datastreams.
257 256 *
258 257 * Returns:
259 258 * -1 = Non-recoverable error
260 259 * 0 = Success
261 260 */
262 261 static int
263 262 build_request_url(url_t *req_url, enum URLtype ut, const url_t *server_url)
264 263 {
265 264 char clid[WB_MAX_CID_LEN];
266 265 size_t clen;
267 266 char wid[WB_MAX_CID_LEN * 2 + 1];
268 267 uint_t wlen;
269 268 struct in_addr ip;
270 269 struct in_addr mask;
271 270 char *netstr;
272 271 char *ppath;
273 272 size_t plen;
274 273 const char reqstr[] = "/?CONTENT=%s&IP=%s&CID=%s";
275 274
276 275 /*
277 276 * Initialize the request
278 277 */
279 278 *req_url = *server_url;
280 279
281 280 /*
282 281 * Build the network number string
283 282 */
284 283 ipv4_getipaddr(&ip);
285 284 ipv4_getnetmask(&mask);
286 285 ip.s_addr = ip.s_addr & mask.s_addr;
287 286 netstr = inet_ntoa(ip);
288 287
289 288 /*
290 289 * Get the wan id
291 290 */
292 291 clen = sizeof (clid);
293 292 if (bootinfo_get(BI_CLIENT_ID, clid, &clen, NULL) != BI_E_SUCCESS) {
294 293 bootlog("wanboot", BOOTLOG_CRIT,
295 294 "Cannot retrieve the client ID");
296 295 return (-1);
297 296 }
298 297 wlen = sizeof (wid);
299 298 (void) octet_to_hexascii(clid, clen, wid, &wlen);
300 299
301 300 /*
302 301 * Build the request, making sure that the length of the
303 302 * constructed URL falls within the supported maximum.
304 303 */
305 304 plen = strlen(req_url->abspath);
306 305 ppath = req_url->abspath + plen;
307 306 if (snprintf(ppath, URL_MAX_PATHLEN - plen, reqstr,
308 307 CGIcontent(ut), netstr, wid) >= URL_MAX_PATHLEN - plen) {
309 308 bootlog("wanboot", BOOTLOG_CRIT,
310 309 "The URL path length of the %s request is greater than "
311 310 "the maximum of %d", CGIcontent(ut), URL_MAX_PATHLEN);
312 311 return (-1);
313 312 }
314 313
315 314 /*
316 315 * If the URL type requires a nonce, then supply it.
317 316 * It will be returned in the reply to detect attempted
318 317 * replays.
319 318 */
320 319 if (ut == URLtype_wanbootfs) {
321 320 char *n = generate_nonce();
322 321
323 322 if (n != NULL) {
324 323 plen += strlen("&NONCE=") + NONCELEN;
325 324 if (plen > URL_MAX_PATHLEN)
326 325 return (-1);
327 326 (void) strcat(req_url->abspath, "&NONCE=");
328 327 (void) strcat(req_url->abspath, n);
329 328 }
330 329 }
331 330
332 331 return (0);
333 332 }
334 333
335 334 /*
336 335 * This routine reads data from an HTTP connection into a buffer.
337 336 *
338 337 * Returns:
339 338 * 0 = Success
340 339 * 1 = HTTP download error
341 340 */
342 341 static int
343 342 read_bytes(http_handle_t handle, char *buffer, size_t cnt)
344 343 {
345 344 int len;
346 345 size_t i;
347 346
348 347 for (i = 0; i < cnt; i += len) {
349 348 len = http_read_body(handle, &buffer[i], cnt - i);
350 349 if (len <= 0) {
351 350 print_errors("http_read_body", handle);
352 351 return (1);
353 352 }
354 353 }
355 354 return (0);
356 355 }
357 356
358 357 /*
359 358 * This routine compares two hash digests, one computed by the server and
360 359 * the other computed by the client to verify that a transmitted message
361 360 * was received without corruption.
362 361 *
363 362 * Notes:
364 363 * The client only computes a digest if it is configured with a
365 364 * hash key. If it is not, then the server should not have a hash
366 365 * key for the client either and therefore should have sent a
367 366 * zero filled digest.
368 367 *
369 368 * Returns:
370 369 * B_TRUE = digest was verified
371 370 * B_FALSE = digest did not verify
372 371 */
373 372 static boolean_t
374 373 verify_digests(const char *what, unsigned char *cdigest, unsigned char *sdigest)
375 374 {
376 375 static char null_digest[HMAC_DIGEST_LEN];
377 376
378 377 if (bcmp(sdigest, cdigest, HMAC_DIGEST_LEN) != 0) {
379 378 bootlog("wanboot", BOOTLOG_CRIT,
380 379 "%s: invalid hash digest", what);
381 380 bootlog("wanboot", BOOTLOG_CRIT,
382 381 "This may signify a client/server key mismatch");
383 382 if (bcmp(sdigest, null_digest, HMAC_DIGEST_LEN) == 0) {
384 383 bootlog("wanboot", BOOTLOG_CRIT,
385 384 "(client has key but wrong signature_type?)");
386 385 } else if (bcmp(cdigest, null_digest, HMAC_DIGEST_LEN) == 0) {
387 386 bootlog("wanboot", BOOTLOG_CRIT,
388 387 "(signature_type specified but no client key?)");
389 388 }
390 389 bootlog("wanboot", BOOTLOG_CRIT,
391 390 "or possible corruption of the image in transit");
392 391 return (B_FALSE);
393 392 }
394 393
395 394 return (B_TRUE);
396 395 }
397 396
398 397 /*
399 398 * This routine reads the part of a multipart message that contains a
400 399 * hash digest. Errors in reading the digest are differentiated from
401 400 * other kinds of errors so that the caller can decide whether or
402 401 * not a retry is worthwhile.
403 402 *
404 403 * Note:
405 404 * The hash digest can either be an HMAC digest or it can be
406 405 * a zero length message (representing no hash digest).
407 406 *
408 407 * Returns:
409 408 * -1 = Non-recoverable error
410 409 * 0 = Success
411 410 * 1 = HTTP download error
412 411 */
413 412 static int
414 413 read_digest(const char *what, http_handle_t handle, unsigned char *sdigest)
415 414 {
416 415 char *lenstr;
417 416 size_t digest_size;
418 417
419 418 /*
420 419 * Process the HMAC digest header.
421 420 */
422 421 if (http_process_part_headers(handle, NULL) != 0) {
423 422 print_errors("http_process_part_headers", handle);
424 423 return (1);
425 424 }
426 425 lenstr = http_get_header_value(handle, CONTENT_LENGTH);
427 426 if (lenstr == NULL) {
428 427 bootlog("wanboot", BOOTLOG_ALERT,
429 428 "%s: error getting digest length", what);
430 429 return (1);
431 430 }
432 431 digest_size = (size_t)strtol(lenstr, NULL, 10);
433 432 free(lenstr);
434 433
435 434 /*
436 435 * Validate the HMAC digest length.
437 436 */
438 437 if (digest_size != HMAC_DIGEST_LEN) {
439 438 bootlog("wanboot", BOOTLOG_CRIT,
440 439 "%s: error validating response - invalid digest size",
441 440 what);
442 441 return (-1);
443 442 }
444 443
445 444 /*
446 445 * Read the HMAC digest.
447 446 */
448 447 if (read_bytes(handle, (char *)sdigest, digest_size) != 0) {
449 448 bootlog("wanboot", BOOTLOG_ALERT,
450 449 "%s: error reading digest", what);
451 450 return (1);
452 451 }
453 452
454 453 return (0);
455 454 }
456 455
457 456 /*
458 457 * This routine reads data from an HTTP connection and writes the data
459 458 * to a ramdisk. It also, optionally computes a hash digest of the processed
460 459 * data. This routine may be called to continue writing a previously aborted
461 460 * write. If this is the case, then the offset will be non-zero and the write
462 461 * pointer into the ramdisk will be positioned correctly by the caller.
463 462 *
464 463 * Returns:
465 464 * -1 = Non-recoverable error
466 465 * 0 = Success
467 466 * 1 = HTTP download error
468 467 */
469 468 static int
470 469 write_msg_to_ramdisk(const char *what, caddr_t addr, http_handle_t handle,
471 470 size_t ramdisk_size, off_t *offset, SHA1_CTX *sha)
472 471 {
473 472 int len;
474 473 long nleft;
475 474 static int bootlog_message_interval;
476 475 static int bootlog_progress;
477 476 int ret;
478 477
479 478 /*
480 479 * Read the data and write it to the ramdisk.
481 480 */
482 481 if (*offset == 0) {
483 482 bootlog_progress = 0;
484 483 bootlog_message_interval = ramdisk_size / sizeof (buffer);
485 484 if (bootlog_message_interval < 500)
486 485 bootlog_message_interval /= 5;
487 486 else
488 487 bootlog_message_interval /= 50;
489 488
490 489 bootlog("wanboot", BOOTLOG_VERBOSE,
491 490 "Reading %s file system (%ld kB)",
492 491 what, ramdisk_size / 1024);
493 492 } else {
494 493 bootlog("wanboot", BOOTLOG_VERBOSE,
495 494 "Continuing read of %s file system (%ld kB)",
496 495 what, ramdisk_size / 1024);
497 496 }
498 497 for (ret = 0; ret == 0 && *offset < ramdisk_size;
499 498 *offset += len, addr += len) {
500 499 nleft = ramdisk_size - *offset;
501 500
502 501 if (nleft > sizeof (buffer))
503 502 nleft = sizeof (buffer);
504 503
505 504 len = http_read_body(handle, addr, nleft);
506 505 if (len <= 0) {
507 506 print_errors("http_read_body", handle);
508 507 /*
509 508 * In the case of a partial failure, http_read_body()
510 509 * returns into 'len', 1 - the number of bytes read.
511 510 * So, a -65 means 64 bytes read and an error occurred.
512 511 */
513 512 if (len != 0) {
514 513 len = -(len + 1);
515 514 }
516 515 ret = 1;
517 516 }
518 517 if (sha != NULL) {
519 518 HMACUpdate(sha, (uchar_t *)addr, (size_t)len);
520 519 }
521 520 if (bootlog_progress == bootlog_message_interval) {
522 521 bootlog("wanboot", BOOTLOG_PROGRESS,
523 522 "%s: Read %ld of %ld kB (%ld%%)", what,
524 523 *offset / 1024, ramdisk_size / 1024,
525 524 *offset * 100 / ramdisk_size);
526 525 bootlog_progress = 0;
527 526 } else {
528 527 bootlog_progress++;
529 528 }
530 529 }
531 530 if (ret == 0) {
532 531 bootlog("wanboot", BOOTLOG_PROGRESS,
533 532 "%s: Read %ld of %ld kB (%ld%%)", what,
534 533 *offset / 1024, ramdisk_size / 1024,
535 534 *offset * 100 / ramdisk_size);
536 535 bootlog("wanboot", BOOTLOG_INFO, "%s: Download complete", what);
537 536 }
538 537 return (ret);
539 538 }
540 539
541 540 /*
542 541 * This routine is called with a bootinfo parameter name. If the parameter
543 542 * has a value it should be a URL, and this will be used to initialize the
544 543 * http_url structure.
545 544 *
546 545 * Returns:
547 546 * -1 = Non-recoverable error
548 547 * 0 = Success
549 548 * 1 = DHCP option not set
550 549 */
551 550 static int
552 551 get_url(char *name, url_t *url)
553 552 {
554 553 char buf[URL_MAX_STRLEN];
555 554 size_t len;
556 555 int ret;
557 556
558 557 bzero(buf, sizeof (buf));
559 558 len = sizeof (buf) - 1;
560 559 if (bootinfo_get(name, buf, &len, NULL) != BI_E_SUCCESS || len == 0) {
561 560 return (1);
562 561 }
563 562
564 563 /*
565 564 * Parse the URL.
566 565 */
567 566 ret = url_parse(buf, url);
568 567 if (ret != URL_PARSE_SUCCESS) {
569 568 bootlog("wanboot", BOOTLOG_CRIT,
570 569 "Unable to parse URL %s", buf);
571 570 return (-1);
572 571 }
573 572
574 573 return (0);
575 574 }
576 575
577 576 /*
578 577 * This routine initiates an HTTP request and returns a handle so that
579 578 * the caller can process the response.
580 579 *
581 580 * Notes:
582 581 * Requests may be either secure or not. If the request is secure, then
583 582 * this routine assumes that a wanboot file system exists and
584 583 * uses its contents to provide the HTTP library with the information
585 584 * that will be required by SSL.
586 585 *
587 586 * In order to facilitate transmission retries, this routine supports
588 587 * range requests. A caller may request a range by providing a non-zero
589 588 * offset. In which case, a range request is made that ranges from the
590 589 * offet to the end of the file.
591 590 *
592 591 * If the client is configured to use an HTTP proxy, then this routine
593 592 * will make the HTTP library aware of the proxy.
594 593 *
595 594 * Any HTTP errors encountered in downloading or processing the message
596 595 * are not deemed unrecoverable errors. The caller can simply try the
597 596 * request once again.
598 597 *
599 598 * Returns:
600 599 * -1 = Non-recoverable error
601 600 * 0 = Success
602 601 * 1 = HTTP download error
603 602 */
604 603 static int
605 604 establish_http_connection(const char *what, http_handle_t *handlep,
606 605 url_t *url, offset_t offset)
607 606 {
608 607 static boolean_t is_auth_file_init = B_FALSE;
609 608 static boolean_t is_proxy_init = B_FALSE;
610 609 static boolean_t proxy_exists = B_FALSE;
611 610 static url_hport_t proxy_hp;
612 611 http_respinfo_t *resp;
613 612 char buf[URL_MAX_STRLEN];
614 613 size_t len = sizeof (buf) - 1;
615 614 int ret;
616 615
617 616 /* Check for HTTP proxy */
618 617 if (!is_proxy_init &&
619 618 bootinfo_get(BI_HTTP_PROXY, buf, &len, NULL) == BI_E_SUCCESS &&
620 619 strlen(buf) > 0) {
621 620 /*
622 621 * Parse the hostport.
623 622 */
624 623 ret = url_parse_hostport(buf, &proxy_hp, URL_DFLT_PROXY_PORT);
625 624 if (ret == URL_PARSE_SUCCESS) {
626 625 proxy_exists = B_TRUE;
627 626 } else {
628 627 bootlog("wanboot", BOOTLOG_CRIT,
629 628 "%s is not set to a valid hostport value",
630 629 BI_HTTP_PROXY);
631 630 return (-1);
632 631 }
633 632 is_proxy_init = B_TRUE;
634 633 }
635 634
636 635 http_set_p12_format(use_p12);
637 636
638 637 /*
639 638 * Initialize the handle that will be used for the request.
640 639 */
641 640 *handlep = http_srv_init(url);
642 641 if (*handlep == NULL) {
643 642 print_errors("http_srv_init", NULL);
644 643 return (-1);
645 644 }
646 645
647 646 /*
648 647 * Is the request a secure one? If it is, then we need to do further
649 648 * setup. Search the wanboot file system for files that will be
650 649 * needed by SSL.
651 650 */
652 651 if (url->https) {
653 652 char *cas;
654 653 boolean_t client_authentication = B_FALSE;
655 654
656 655 if (http_set_random_file(*handlep, "/dev/urandom") < 0) {
657 656 print_errors("http_set_random_file", *handlep);
658 657 (void) http_srv_close(*handlep);
659 658 return (-1);
660 659 }
661 660
662 661 /*
663 662 * We only need to initialize the CA once as it is not handle
664 663 * specific.
665 664 */
666 665 if (!is_auth_file_init) {
667 666 if (http_set_certificate_authority_file(NB_CA_CERT_PATH)
668 667 < 0) {
669 668 print_errors(
670 669 "http_set_certificate_authority_file",
671 670 *handlep);
672 671 (void) http_srv_close(*handlep);
673 672 return (-1);
674 673 }
675 674
676 675 is_auth_file_init = B_TRUE;
677 676 }
678 677
679 678 /*
680 679 * The client certificate and key will not exist unless
681 680 * client authentication has been configured. If it is
682 681 * configured then the webserver will have added these
683 682 * files to the wanboot file system and the HTTP library
684 683 * needs to be made aware of their existence.
685 684 */
686 685 if ((cas = bootconf_get(&bc_handle,
687 686 BC_CLIENT_AUTHENTICATION)) != NULL &&
688 687 strcmp(cas, "yes") == 0) {
689 688 client_authentication = B_TRUE;
690 689
691 690 if (http_set_client_certificate_file(*handlep,
692 691 NB_CLIENT_CERT_PATH) < 0) {
693 692 print_errors("http_set_client_certificate_file",
694 693 *handlep);
695 694 (void) http_srv_close(*handlep);
696 695 return (-1);
697 696 }
698 697
699 698 if (http_set_private_key_file(*handlep,
700 699 NB_CLIENT_KEY_PATH) < 0) {
701 700 print_errors("http_set_private_key_file",
702 701 *handlep);
703 702 (void) http_srv_close(*handlep);
704 703 return (-1);
705 704 }
706 705 }
707 706
708 707 /*
709 708 * We do not really need to set this unless client
710 709 * authentication is configured or unless pkcs12 files
711 710 * are used.
712 711 */
713 712 if ((client_authentication || use_p12) &&
714 713 http_set_password(*handlep, WANBOOT_PASSPHRASE) < 0) {
715 714 print_errors("http_set_password", *handlep);
716 715 (void) http_srv_close(*handlep);
717 716 return (-1);
718 717 }
719 718 }
720 719
721 720 /*
722 721 * If the client is using a proxy, tell the library.
723 722 */
724 723 if (proxy_exists) {
725 724 if (http_set_proxy(*handlep, &proxy_hp) != 0) {
726 725 print_errors("http_set_proxy", *handlep);
727 726 (void) http_srv_close(*handlep);
728 727 return (-1);
729 728 }
730 729 }
731 730
732 731 (void) http_set_socket_read_timeout(*handlep, SOCKET_READ_TIMEOUT);
733 732
734 733 /*
735 734 * Ok, connect to the webserver.
736 735 */
737 736 if (http_srv_connect(*handlep) == -1) {
738 737 print_errors("http_srv_connect", *handlep);
739 738 (void) http_srv_close(*handlep);
740 739 return (1);
741 740 }
742 741
743 742 /*
744 743 * If the offset is 0, then we assume that we want the entire
745 744 * message. If the offset is not 0, then we assume that we are
746 745 * retrying a previously interrupted transfer and thus we make
747 746 * a range request.
748 747 */
749 748 if (offset == 0) {
750 749 if ((ret = http_get_request(*handlep, url->abspath)) == 0) {
751 750 bootlog("wanboot", BOOTLOG_VERBOSE,
752 751 "%s: http_get_request: sent", what);
753 752 } else {
754 753 print_errors("http_get_request", *handlep);
755 754 (void) http_srv_close(*handlep);
756 755 return (1);
757 756 }
758 757 } else {
759 758 if ((ret = http_get_range_request(*handlep, url->abspath,
760 759 offset, 0)) == 0) {
761 760 bootlog("wanboot", BOOTLOG_VERBOSE,
762 761 "%s: http_get_range_request: sent", what);
763 762 } else {
764 763 print_errors("http_get_range_request", *handlep);
765 764 (void) http_srv_close(*handlep);
766 765 return (1);
767 766 }
768 767 }
769 768
770 769 /*
771 770 * Tell the library to read in the response headers.
772 771 */
773 772 ret = http_process_headers(*handlep, &resp);
774 773 if (ret == -1) {
775 774 print_errors("http_process_headers", *handlep);
776 775 (void) http_srv_close(*handlep);
777 776 return (1);
778 777 }
779 778
780 779 /*
781 780 * Check for a valid response code.
782 781 */
783 782 if ((offset == 0 && resp->code != 200) ||
784 783 (offset != 0 && resp->code != 206)) {
785 784 bootlog("wanboot", BOOTLOG_ALERT,
786 785 "%s: Request returned code %d", what, resp->code);
787 786 if (resp->statusmsg != NULL && resp->statusmsg[0] != '\0')
788 787 bootlog("wanboot", BOOTLOG_ALERT,
789 788 "%s", resp->statusmsg);
790 789 http_free_respinfo(resp);
791 790 (void) http_srv_close(*handlep);
792 791 return (1);
793 792 }
794 793 http_free_respinfo(resp);
795 794
796 795 /*
797 796 * Success.
798 797 */
799 798 return (0);
800 799 }
801 800
802 801 /*
803 802 * This routine is called by get_miniinfo() to receive the reply
804 803 * to the request for the miniroot metadata. The reply is a two
805 804 * part multipart message. The first part of the message contains
806 805 * the miniroot file size. The second part of the message contains
807 806 * a hash digest of the miniroot as computed by the server. This
808 807 * routine receives both message parts and returns them to the caller.
809 808 *
810 809 * Notes:
811 810 * If the miniroot is going to be downloaded securely or if the
812 811 * the server has no hash key for the client, then the hash digest
813 812 * downloaded contains all zeros.
814 813 *
815 814 * Any HTTP errors encountered in downloading or processing the message
816 815 * are not deemed unrecoverable errors. That is, get_miniinfo()
817 816 * tries re-requesting the message and tries processing it again.
818 817 *
819 818 * Returns:
820 819 * -1 = Non-recoverable error
821 820 * 0 = Success
822 821 * 1 = HTTP download error
823 822 */
824 823 static int
825 824 process_miniinfo(http_handle_t handle, size_t *mini_size,
826 825 unsigned char *sdigest)
827 826 {
828 827 char *lenstr;
829 828 size_t cnt;
830 829
831 830 /*
832 831 * Process the file size header.
833 832 */
834 833 if (http_process_part_headers(handle, NULL) != 0) {
835 834 print_errors("http_process_part_headers", handle);
836 835 return (1);
837 836 }
838 837 lenstr = http_get_header_value(handle, CONTENT_LENGTH);
839 838 if (lenstr == NULL) {
840 839 bootlog("wanboot", BOOTLOG_ALERT, "%s: error getting length "
841 840 "of first part of multipart message", MINIINFO);
842 841 return (1);
843 842 }
844 843 cnt = (size_t)strtol(lenstr, NULL, 10);
845 844 free(lenstr);
846 845 if (cnt == 0 || cnt >= sizeof (buffer)) {
847 846 bootlog("wanboot", BOOTLOG_ALERT, "%s: length of first part "
848 847 "of multipart message not a legal size", MINIINFO);
849 848 return (1);
850 849 }
851 850
852 851 if (read_bytes(handle, buffer, cnt) != 0) {
853 852 bootlog("wanboot", BOOTLOG_ALERT,
854 853 "%s: error reading miniroot size", MINIINFO);
855 854 return (1);
856 855 }
857 856 buffer[cnt] = '\0';
858 857
859 858 *mini_size = (size_t)strtol(buffer, NULL, 10);
860 859 if (*mini_size == 0) {
861 860 bootlog("wanboot", BOOTLOG_ALERT, "%s: body of first part "
862 861 "of multipart message not a legal size", MINIINFO);
863 862 return (1);
864 863 }
865 864
866 865 return (read_digest(MINIINFO, handle, sdigest));
867 866 }
868 867
869 868 /*
870 869 * This routine is called by get_miniroot() to retrieve the miniroot
871 870 * metadata (miniroot size and a hash digest). This routine sends an
872 871 * HTTP GET request to the webserver to request the download of the
873 872 * miniroot metadata and relies on process_miniinfo() to receive the
874 873 * reply, process it and ultimately return to it the miniroot size and
875 874 * the hash digest.
876 875 *
877 876 * Note:
878 877 * Any HTTP errors encountered in downloading or processing the message
879 878 * are not deemed unrecoverable errors. That is, get_miniinfo() should
880 879 * try re-requesting the message and try processing again.
881 880 *
882 881 * Returns:
883 882 * -1 = Non-recoverable error
884 883 * 0 = Success
885 884 */
886 885 int
887 886 get_miniinfo(const url_t *server_url, size_t *mini_size,
888 887 unsigned char *sdigest)
889 888 {
890 889 http_handle_t handle;
891 890 url_t req_url;
892 891 int retry_cnt = 0;
893 892 int retry_max = WANBOOT_RETRY_MAX;
894 893 int ret;
895 894
896 895 /*
897 896 * Build the URL to request the miniroot info.
898 897 */
899 898 if (build_request_url(&req_url, URLtype_miniroot, server_url) == -1) {
900 899 bootlog("wanboot", BOOTLOG_CRIT,
901 900 "Can't build the URL to make the %s request",
902 901 CGIcontent(URLtype_miniroot));
903 902 return (-1);
904 903 }
905 904
906 905 /*
907 906 * Go get the miniroot info. If we fail reading the
908 907 * response we re-request the info in its entirety.
909 908 */
910 909 bootlog("wanboot", BOOTLOG_VERBOSE, "Downloading miniroot info");
911 910
912 911 do {
913 912 if ((ret = establish_http_connection(MINIINFO, &handle,
914 913 &req_url, 0)) < 0) {
915 914 break;
916 915 } else if (ret > 0) {
917 916 if (wanboot_retry(++retry_cnt, retry_max)) {
918 917 continue;
919 918 } else {
920 919 break;
921 920 }
922 921 }
923 922
924 923 if ((ret = process_miniinfo(handle, mini_size,
925 924 sdigest)) > 0) {
926 925 if (!wanboot_retry(++retry_cnt, retry_max)) {
927 926 (void) http_srv_close(handle);
928 927 break;
929 928 }
930 929 }
931 930
932 931 (void) http_srv_close(handle);
933 932
934 933 } while (ret > 0);
935 934
936 935 /*
937 936 * Success.
938 937 */
939 938 if (ret == 0) {
940 939 bootlog("wanboot", BOOTLOG_VERBOSE,
941 940 "Miniroot info download successful");
942 941 return (0);
943 942 } else {
944 943 bootlog("wanboot", BOOTLOG_CRIT,
945 944 "Miniroot info download aborted");
946 945 return (-1);
947 946 }
948 947 }
949 948
950 949 /*
951 950 * This routine is called by get_miniroot() to receive the reply to
952 951 * the request for the miniroot download. The miniroot is written
953 952 * to ramdisk as it is received and a hash digest is optionally computed
954 953 * as it does so. The miniroot is downloaded as one large message.
955 954 * Because the message is so large, this routine is prepared to deal
956 955 * with errors in the middle of download. If an error occurs during
957 956 * download, then this message processes all received data up to the
958 957 * point of the error and returns to get_miniroot() an error signifying
959 958 * that a download error has occurred. Presumably, get_miniroot()
960 959 * re-requests the remaining part of the miniroot not yet processed and
961 960 * calls this routine back to process the reply. When this routine
962 961 * returns succesfully, it returns a devpath to the ramdisk and the
963 962 * computed hash (if computed).
964 963 *
965 964 * Note:
966 965 * In order to facilitate reentry, the ramdisk is left open
967 966 * and the original miniroot_size and HMAC handle are kept
968 967 * static.
969 968 *
970 969 * Returns:
971 970 * -1 = Non-recoverable error
972 971 * 0 = Success
973 972 * 1 = HTTP download error
974 973 */
975 974 static int
976 975 process_miniroot(http_handle_t handle, hash_type_t htype,
977 976 size_t length, char **devpath, off_t *offset, unsigned char *cdigest)
978 977 {
979 978 static SHA1_CTX sha;
980 979 static size_t miniroot_size;
981 980 static caddr_t miniroot_vaddr = NULL;
982 981 int ret;
983 982
984 983 if (miniroot_vaddr == NULL) {
985 984 if (htype == HASH_HMAC_SHA1) {
986 985 bootlog("wanboot", BOOTLOG_INFO,
987 986 "%s: Authentication will use HMAC-SHA1", MINIROOT);
988 987 HMACInit(&sha, g_hash_key, WANBOOT_HMAC_KEY_SIZE);
989 988 }
990 989
991 990 miniroot_size = length;
992 991
993 992 miniroot_vaddr = create_ramdisk(RD_ROOTFS, miniroot_size,
994 993 devpath);
995 994 }
996 995
997 996 miniroot_vaddr += *offset;
998 997
999 998 if ((ret = write_msg_to_ramdisk(MINIROOT, miniroot_vaddr, handle,
1000 999 miniroot_size, offset, (htype == HASH_NONE) ? NULL : &sha)) != 0) {
1001 1000 return (ret);
1002 1001 }
1003 1002
1004 1003 if (htype != HASH_NONE) {
1005 1004 HMACFinal(&sha, g_hash_key, WANBOOT_HMAC_KEY_SIZE, cdigest);
1006 1005 }
1007 1006
1008 1007 return (0);
1009 1008 }
1010 1009
1011 1010 /*
1012 1011 * This routine retrieves the miniroot from the webserver. The miniroot
1013 1012 * is retrieved in two steps. First a request is made to the server
1014 1013 * to retrieve miniroot metadata (miniroot size and a hash digest).
1015 1014 * The second request actually results in the download of the miniroot.
1016 1015 *
1017 1016 * This routine relies on get_miniinfo() to make and process
1018 1017 * the request for the miniroot metadata and returns the
1019 1018 * miniroot size and the hash digest of the miniroot as computed by
1020 1019 * the server.
1021 1020 *
1022 1021 * If get_miniinfo() returns successfully, then this routine sends
1023 1022 * an HTTP GET request to the webserver to request download of the
1024 1023 * miniroot. This routine relies on process_miniroot() to receive
1025 1024 * the reply, process it and ultimately return to it a device path to
1026 1025 * a ramdisk containing the miniroot and a client computed hash digest.
1027 1026 * This routine verifies that the client computed hash digest matches
1028 1027 * the one retrieved by get_miniinfo().
1029 1028 *
1030 1029 * If an error occurs in the transfer of the miniroot from the server
1031 1030 * to the client, then the client re-requests the download of the
1032 1031 * miniroot using a range request and only requests the part of the
1033 1032 * miniroot not previously downloaded and written to ramdisk. The
1034 1033 * process_miniroot() routine has the intelligence to recognize that
1035 1034 * it is processing a range request. Errors not related to the actual
1036 1035 * message download are deemed unrecoverable.
1037 1036 *
1038 1037 * Note:
1039 1038 * If the client request for the miniroot is a secure request or
1040 1039 * if the server is not configured with a hash key for the client,
1041 1040 * then the hash digest downloaded from the server will contain
1042 1041 * all zeros. This routine verifies that the server and client are
1043 1042 * in-sync with respect to the need for hash verification.
1044 1043 *
1045 1044 * Returns:
1046 1045 * -1 = Non-recoverable error
1047 1046 * 0 = Success
1048 1047 */
1049 1048 int
1050 1049 get_miniroot(char **devpath)
1051 1050 {
1052 1051 http_handle_t handle;
1053 1052 unsigned char cdigest[HMAC_DIGEST_LEN];
1054 1053 unsigned char sdigest[HMAC_DIGEST_LEN];
1055 1054 char *urlstr;
1056 1055 url_t server_url;
1057 1056 size_t mini_size;
1058 1057 off_t offset;
1059 1058 int plen;
1060 1059 int retry_cnt = 0;
1061 1060 int retry_max = WANBOOT_RETRY_ROOT_MAX;
1062 1061 int ret;
1063 1062
1064 1063 /*
1065 1064 * Get the miniroot URL.
1066 1065 */
1067 1066 if ((urlstr = bootconf_get(&bc_handle, BC_ROOT_SERVER)) == NULL) {
1068 1067 bootlog("wanboot", BOOTLOG_CRIT,
1069 1068 "Missing root_server URL");
1070 1069 return (-1);
1071 1070 } else if (url_parse(urlstr, &server_url) != URL_PARSE_SUCCESS) {
1072 1071 bootlog("wanboot", BOOTLOG_CRIT,
1073 1072 "Unable to parse URL %s", urlstr);
1074 1073 return (-1);
1075 1074 }
1076 1075
1077 1076 /*
1078 1077 * We must get the miniroot info before we can request
1079 1078 * the miniroot itself.
1080 1079 */
1081 1080 if (get_miniinfo(&server_url, &mini_size, sdigest) != 0) {
1082 1081 return (-1);
1083 1082 }
1084 1083
1085 1084 plen = sizeof (server_url.abspath);
1086 1085 if ((urlstr = bootconf_get(&bc_handle, BC_ROOT_FILE)) == NULL ||
1087 1086 strlcpy(server_url.abspath, urlstr, plen) >= plen) {
1088 1087 bootlog("wanboot", BOOTLOG_CRIT,
1089 1088 "Cannot retrieve the miniroot path");
1090 1089 return (-1);
1091 1090 }
1092 1091
1093 1092 /*
1094 1093 * Go get the miniroot. If we fail reading the response
1095 1094 * then we re-request only the range we have yet to read,
1096 1095 * unless the error was "unrecoverable" in which case we
1097 1096 * re-request the entire file system.
1098 1097 */
1099 1098 bootlog("wanboot", BOOTLOG_VERBOSE, "Downloading miniroot");
1100 1099
1101 1100 bzero(cdigest, sizeof (cdigest));
1102 1101 offset = 0;
1103 1102 do {
1104 1103 if ((ret = establish_http_connection(MINIROOT, &handle,
1105 1104 &server_url, offset)) < 0) {
1106 1105 break;
1107 1106 } else if (ret > 0) {
1108 1107 if (wanboot_retry(++retry_cnt, retry_max)) {
1109 1108 continue;
1110 1109 } else {
1111 1110 break;
1112 1111 }
1113 1112 }
1114 1113
1115 1114 if ((ret = process_miniroot(handle,
1116 1115 server_url.https ? HASH_NONE : hash_type,
1117 1116 mini_size, devpath, &offset, cdigest)) > 0) {
1118 1117 if (!wanboot_retry(++retry_cnt, retry_max)) {
1119 1118 (void) http_srv_close(handle);
1120 1119 break;
1121 1120 }
1122 1121 }
1123 1122
1124 1123 (void) http_srv_close(handle);
1125 1124
1126 1125 } while (ret > 0);
1127 1126
1128 1127 /*
1129 1128 * Validate the computed digest against the one received.
1130 1129 */
1131 1130 if (ret != 0 || !verify_digests(MINIROOT, cdigest, sdigest)) {
1132 1131 bootlog("wanboot", BOOTLOG_CRIT,
1133 1132 "Miniroot download aborted");
1134 1133 return (-1);
1135 1134 }
1136 1135
1137 1136 bootlog("wanboot", BOOTLOG_VERBOSE, "Miniroot download successful");
1138 1137 return (0);
1139 1138 }
1140 1139
1141 1140 /*
1142 1141 * This routine is called to finish the decryption process.
1143 1142 * Its purpose is to free the resources allocated by the
1144 1143 * encryption init routines.
1145 1144 */
1146 1145 static void
1147 1146 encr_fini(encr_type_t etype, void *eh)
1148 1147 {
1149 1148 switch (etype) {
1150 1149 case ENCR_3DES:
1151 1150 des3_fini(eh);
1152 1151 break;
1153 1152 case ENCR_AES:
1154 1153 aes_fini(eh);
1155 1154 break;
1156 1155 default:
1157 1156 break;
1158 1157 }
1159 1158 }
1160 1159
1161 1160 /*
1162 1161 * This routine is called by process_wanbootfs() to decrypt the encrypted
1163 1162 * file system from ramdisk in place. The method of decryption
1164 1163 * (algorithm) will have already been determined by process_wanbootfs()
1165 1164 * and the cbc_handle passed to this routine will already have been
1166 1165 * initialized appropriately.
1167 1166 *
1168 1167 * Returns:
1169 1168 * -1 = Non-recoverable error
1170 1169 * 0 = Success
1171 1170 */
1172 1171 static int
1173 1172 decrypt_wanbootfs(caddr_t addr, cbc_handle_t *ch, uint8_t *iv,
1174 1173 size_t wanbootfs_size)
1175 1174 {
1176 1175 if (!cbc_decrypt(ch, (uint8_t *)addr, wanbootfs_size, iv)) {
1177 1176 bootlog("wanboot", BOOTLOG_CRIT,
1178 1177 "%s: cbc decrypt error", WANBOOTFS);
1179 1178 return (-1);
1180 1179 }
1181 1180 return (0);
1182 1181 }
1183 1182
1184 1183 /*
1185 1184 * This routine is called by get_wanbootfs() to receive the reply to
1186 1185 * the request for the wanboot file system. The reply is a multipart message.
1187 1186 * The first part of the message is the file system (which may or may
1188 1187 * not be encrypted). If encrypted, then the first block of the message
1189 1188 * part is the CBC IV value used by the server to encrypt the remaining
1190 1189 * part of the message part and is used by the client to decrypt it. The
1191 1190 * second message part is a hash digest of the first part (the file
1192 1191 * system) as computed by the server. If no hash key is configured
1193 1192 * for the client, then the hash digest simply contains all zeros. This
1194 1193 * routine receives both message parts. The file system is written to ramdisk
1195 1194 * as it is received and simultaneously computes a hash digest (if a hash
1196 1195 * key exists). Once the entire part is received, if the file system is
1197 1196 * encrypted, it is read from ramdisk, decrypted and rewritten back to
1198 1197 * ramdisk. The server computed hash digest is then read and along with the
1199 1198 * ramdisk device path and the client computed hash digest is returned to the
1200 1199 * caller.
1201 1200 *
1202 1201 * Notes:
1203 1202 * In order to decrypt the file system and to compute the client
1204 1203 * hash digest, an encryption key and a hash key is retrieved from
1205 1204 * the PROM (or the wanboot interpreter). The non-existence of these
1206 1205 * keys has implications on how the message response is processed and
1207 1206 * it is assumed that the server is configured identically.
1208 1207 *
1209 1208 * Any HTTP errors encountered in downloading or processing the message
1210 1209 * are not deemed unrecoverable errors. That is, get_wanbootfs() will
1211 1210 * try re-requesting the message and will try processing it again.
1212 1211 *
1213 1212 * Returns:
1214 1213 * -1 = Non-recoverable error
1215 1214 * 0 = Success
1216 1215 * 1 = HTTP download error
1217 1216 */
1218 1217 static int
1219 1218 process_wanbootfs(http_handle_t handle, char **devpath,
1220 1219 unsigned char *cdigest, unsigned char *sdigest)
1221 1220 {
1222 1221 /* iv[] must be sized to store the largest possible encryption block */
1223 1222 uint8_t iv[WANBOOT_MAXBLOCKLEN];
1224 1223 cbc_handle_t ch;
1225 1224 void *eh;
1226 1225 SHA1_CTX sha;
1227 1226 char *lenstr;
1228 1227 size_t wanbootfs_size;
1229 1228 size_t block_size;
1230 1229 off_t offset;
1231 1230 static caddr_t bootfs_vaddr = NULL;
1232 1231 int ret;
1233 1232
1234 1233 switch (hash_type) {
1235 1234 case HASH_HMAC_SHA1:
1236 1235 bootlog("wanboot", BOOTLOG_INFO,
1237 1236 "%s: Authentication will use HMAC-SHA1", WANBOOTFS);
1238 1237 HMACInit(&sha, g_hash_key, WANBOOT_HMAC_KEY_SIZE);
1239 1238 break;
1240 1239 case HASH_NONE:
1241 1240 break;
1242 1241 default:
1243 1242 bootlog("wanboot", BOOTLOG_CRIT,
1244 1243 "%s: unrecognized hash type", WANBOOTFS);
1245 1244 return (-1);
1246 1245 }
1247 1246
1248 1247 switch (encr_type) {
1249 1248 case ENCR_3DES:
1250 1249 bootlog("wanboot",
1251 1250 BOOTLOG_INFO, "%s: Decryption will use 3DES", WANBOOTFS);
1252 1251 if (des3_init(&eh) != 0) {
1253 1252 return (-1);
1254 1253 }
1255 1254 block_size = DES3_BLOCK_SIZE;
1256 1255 des3_key(eh, g_encr_key);
1257 1256 cbc_makehandle(&ch, eh, DES3_KEY_SIZE, block_size,
1258 1257 DES3_IV_SIZE, des3_encrypt, des3_decrypt);
1259 1258
1260 1259 break;
1261 1260 case ENCR_AES:
1262 1261 bootlog("wanboot",
1263 1262 BOOTLOG_INFO, "%s: Decryption will use AES", WANBOOTFS);
1264 1263 if (aes_init(&eh) != 0) {
1265 1264 return (-1);
1266 1265 }
1267 1266 block_size = AES_BLOCK_SIZE;
1268 1267 aes_key(eh, g_encr_key, AES_128_KEY_SIZE);
1269 1268 cbc_makehandle(&ch, eh, AES_128_KEY_SIZE, block_size,
1270 1269 AES_IV_SIZE, aes_encrypt, aes_decrypt);
1271 1270 break;
1272 1271 case ENCR_NONE:
1273 1272 break;
1274 1273 default:
1275 1274 bootlog("wanboot", BOOTLOG_CRIT,
1276 1275 "%s: unrecognized encryption type", WANBOOTFS);
1277 1276 return (-1);
1278 1277 }
1279 1278
1280 1279 /*
1281 1280 * Process the header.
1282 1281 */
1283 1282 if (http_process_part_headers(handle, NULL) != 0) {
1284 1283 print_errors("http_process_part_headers", handle);
1285 1284 return (1);
1286 1285 }
1287 1286 lenstr = http_get_header_value(handle, CONTENT_LENGTH);
1288 1287 if (lenstr == NULL) {
1289 1288 bootlog("wanboot", BOOTLOG_ALERT, "%s: error getting length "
1290 1289 "of first part of multipart message", WANBOOTFS);
1291 1290 return (1);
1292 1291 }
1293 1292 wanbootfs_size = (size_t)strtol(lenstr, NULL, 10);
1294 1293 free(lenstr);
1295 1294 if (wanbootfs_size == 0) {
1296 1295 bootlog("wanboot", BOOTLOG_ALERT, "%s: length of first part "
1297 1296 "of multipart message not a legal size", WANBOOTFS);
1298 1297 return (1);
1299 1298 }
1300 1299
1301 1300 /*
1302 1301 * If encrypted, then read the iv.
1303 1302 */
1304 1303 if (encr_type != ENCR_NONE) {
1305 1304 if (read_bytes(handle, (char *)iv, block_size) != 0) {
1306 1305 bootlog("wanboot", BOOTLOG_ALERT,
1307 1306 "%s: error reading hash iv", WANBOOTFS);
1308 1307 return (1);
1309 1308 }
1310 1309 wanbootfs_size -= block_size;
1311 1310 if (hash_type != HASH_NONE) {
1312 1311 HMACUpdate(&sha, (uchar_t *)iv, block_size);
1313 1312 }
1314 1313 }
1315 1314
1316 1315 /*
1317 1316 * We can only create the ramdisk once. So, if we've
1318 1317 * already created it, then it means we've re-entered
1319 1318 * this routine from an earlier partial failure. Use
1320 1319 * the already existing ramdisk and seek back to the
1321 1320 * beginning of the file.
1322 1321 */
1323 1322 if (bootfs_vaddr == NULL) {
1324 1323 bootfs_vaddr = create_ramdisk(RD_BOOTFS, wanbootfs_size,
1325 1324 devpath);
1326 1325 }
1327 1326
1328 1327 offset = 0;
1329 1328
1330 1329 if ((ret = write_msg_to_ramdisk(WANBOOTFS, bootfs_vaddr, handle,
1331 1330 wanbootfs_size, &offset, (hash_type == HASH_NONE) ? NULL : &sha))
1332 1331 != 0) {
1333 1332 return (ret);
1334 1333 }
1335 1334
1336 1335 if (hash_type != HASH_NONE) {
1337 1336 HMACFinal(&sha, g_hash_key, WANBOOT_HMAC_KEY_SIZE, cdigest);
1338 1337 }
1339 1338
1340 1339 /*
1341 1340 * If encrypted, then decrypt it.
1342 1341 */
1343 1342 if (encr_type != ENCR_NONE) {
1344 1343 ret = decrypt_wanbootfs(bootfs_vaddr, &ch, iv, wanbootfs_size);
1345 1344 if (ret != 0) {
1346 1345 encr_fini(encr_type, eh);
1347 1346 return (-1);
1348 1347 }
1349 1348 encr_fini(encr_type, eh);
1350 1349 }
1351 1350
1352 1351 return (read_digest(WANBOOTFS, handle, sdigest));
1353 1352 }
1354 1353
1355 1354 /*
1356 1355 * This routine sends an HTTP GET request to the webserver to
1357 1356 * request the wanboot file system for the client. The server
1358 1357 * will reply by sending a multipart message. This routine will rely
1359 1358 * on process_wanbootfs() to receive the multipart message, process it
1360 1359 * and ultimately return to it a device path to a ramdisk containing
1361 1360 * the wanboot file system, a client computed hash digest and a
1362 1361 * server computed hash digest. This routine will verify that the
1363 1362 * client computed hash digest matches the one sent by the server. This
1364 1363 * routine will also verify that the nonce received in the reply matches
1365 1364 * the one sent in the request.
1366 1365 *
1367 1366 * If an error occurs in the transfer of the message from the server
1368 1367 * to the client, then the client re-requests the download in its
1369 1368 * entirety. Errors not related to the actual message download are
1370 1369 * deemed unrecoverable.
1371 1370 *
1372 1371 * Returns:
1373 1372 * -1 = Non-recoverable error
1374 1373 * 0 = Success
1375 1374 */
1376 1375 int
1377 1376 get_wanbootfs(const url_t *server_url)
1378 1377 {
1379 1378 http_handle_t handle;
1380 1379 unsigned char cdigest[HMAC_DIGEST_LEN];
1381 1380 unsigned char sdigest[HMAC_DIGEST_LEN];
1382 1381 url_t req_url;
1383 1382 char *devpath;
1384 1383 int ret;
1385 1384 int fd;
1386 1385 char buf[NONCELEN + 1];
1387 1386 int retry_cnt = 0;
1388 1387 int retry_max = WANBOOT_RETRY_MAX;
1389 1388
1390 1389 /*
1391 1390 * Build the URL to request the wanboot file system. This URL
1392 1391 * will include the CGI script name and the IP, CID, and
1393 1392 * NONCE parameters.
1394 1393 */
1395 1394 if (build_request_url(&req_url, URLtype_wanbootfs, server_url) == -1) {
1396 1395 bootlog("wanboot", BOOTLOG_CRIT,
1397 1396 "Can't build the URL to make the %s request",
1398 1397 CGIcontent(URLtype_wanbootfs));
1399 1398 return (-1);
1400 1399 }
1401 1400
1402 1401 /*
1403 1402 * Go get the wanboot file system. If we fail reading the
1404 1403 * response we re-request the entire file system.
1405 1404 */
1406 1405 bootlog("wanboot", BOOTLOG_VERBOSE, "Downloading wanboot file system");
1407 1406
1408 1407 bzero(cdigest, sizeof (cdigest));
1409 1408 do {
1410 1409 if ((ret = establish_http_connection(WANBOOTFS, &handle,
1411 1410 &req_url, 0)) < 0) {
1412 1411 break;
1413 1412 } else if (ret > 0) {
1414 1413 if (wanboot_retry(++retry_cnt, retry_max)) {
1415 1414 continue;
1416 1415 } else {
1417 1416 break;
1418 1417 }
1419 1418 }
1420 1419
1421 1420 if ((ret = process_wanbootfs(handle, &devpath,
1422 1421 cdigest, sdigest)) > 0) {
1423 1422 if (!wanboot_retry(++retry_cnt, retry_max)) {
1424 1423 (void) http_srv_close(handle);
1425 1424 break;
1426 1425 }
1427 1426 }
1428 1427
1429 1428 (void) http_srv_close(handle);
1430 1429
1431 1430 } while (ret > 0);
1432 1431
1433 1432 /*
1434 1433 * Validate the computed digest against the one received.
1435 1434 */
1436 1435 if (ret != 0 ||
1437 1436 !verify_digests(WANBOOTFS, cdigest, sdigest)) {
1438 1437 bootlog("wanboot", BOOTLOG_CRIT,
1439 1438 "The wanboot file system download aborted");
1440 1439 return (-1);
1441 1440 }
1442 1441
1443 1442 /*
1444 1443 * Mount the wanboot file system.
1445 1444 */
1446 1445 if (determine_fstype_and_mountroot(devpath) != VFS_SUCCESS) {
1447 1446 bootlog("wanboot", BOOTLOG_CRIT,
1448 1447 "Could not mount the wanboot filesystem.");
1449 1448 bootlog("wanboot", BOOTLOG_CRIT,
1450 1449 "This may signify a client/server key mismatch");
1451 1450 if (encr_type != ENCR_NONE) {
1452 1451 bootlog("wanboot", BOOTLOG_CRIT,
1453 1452 "(client has key but wrong encryption_type?)");
1454 1453 } else {
1455 1454 bootlog("wanboot", BOOTLOG_CRIT,
1456 1455 "(encryption_type specified but no client key?)");
1457 1456 }
1458 1457 return (-1);
1459 1458 }
1460 1459 bootlog("wanboot", BOOTLOG_VERBOSE,
1461 1460 "The wanboot file system has been mounted");
1462 1461
1463 1462 /*
1464 1463 * The wanboot file system should contain a nonce. Read it
1465 1464 * and compare it against the nonce sent in the request.
1466 1465 */
1467 1466 if ((fd = open(WANBOOTFS_NONCE_FILE, O_RDONLY)) == -1) {
1468 1467 bootlog("wanboot", BOOTLOG_CRIT,
1469 1468 "No nonce found in the wanboot file system");
1470 1469 bootlog("wanboot", BOOTLOG_CRIT,
1471 1470 "The wanboot file system download aborted");
1472 1471 return (-1);
1473 1472 }
1474 1473
1475 1474 if (read(fd, buf, NONCELEN) != NONCELEN ||
1476 1475 bcmp(nonce, buf, NONCELEN) != 0) {
1477 1476 (void) close(fd);
1478 1477 bootlog("wanboot", BOOTLOG_CRIT,
1479 1478 "Invalid nonce found in the wanboot file system");
1480 1479 bootlog("wanboot", BOOTLOG_CRIT,
1481 1480 "The wanboot file system download aborted");
1482 1481 return (-1);
1483 1482 }
1484 1483
1485 1484 (void) close(fd);
1486 1485
1487 1486 bootlog("wanboot", BOOTLOG_VERBOSE,
1488 1487 "The wanboot file system download was successful");
1489 1488 return (0);
1490 1489 }
1491 1490
1492 1491 static boolean_t
1493 1492 init_netdev(char *bpath)
1494 1493 {
1495 1494 pnode_t anode;
1496 1495 int proplen;
1497 1496 char netalias[OBP_MAXPATHLEN];
1498 1497 static char devpath[OBP_MAXPATHLEN];
1499 1498 char *p;
1500 1499
1501 1500 bzero(netalias, sizeof (netalias));
1502 1501 bzero(devpath, sizeof (devpath));
1503 1502
1504 1503 /*
1505 1504 * Wanboot will either have loaded over the network (in which case
1506 1505 * bpath will name a network device), or from CD-ROM or disk. In
1507 1506 * either case ensure that the 'net' alias corresponds to a network
1508 1507 * device, and that if a network boot was performed that it is
1509 1508 * identical to bpath. This is so that the interface name can always
1510 1509 * be determined for CD-ROM or disk boots, and for manually-configured
1511 1510 * network boots. The latter restriction may be relaxed in the future.
1512 1511 */
1513 1512 anode = prom_alias_node();
1514 1513 if ((proplen = prom_getproplen(anode, "net")) <= 0 ||
1515 1514 proplen > sizeof (netalias)) {
1516 1515 goto error;
1517 1516 }
1518 1517 (void) prom_getprop(anode, "net", (caddr_t)netalias);
1519 1518
1520 1519 /*
1521 1520 * Strip boot arguments from the net device to form
1522 1521 * the boot device path, returned as netdev_path.
1523 1522 */
1524 1523 if (strlcpy(devpath, netalias, sizeof (devpath)) >= sizeof (devpath))
1525 1524 goto error;
1526 1525 if ((p = strchr(devpath, ':')) != NULL) {
1527 1526 *p = '\0';
1528 1527 }
1529 1528
1530 1529 if (!is_netdev(netalias)) {
1531 1530 bootlog("wanboot", BOOTLOG_CRIT, "'net'=%s\n", netalias);
1532 1531 goto error;
1533 1532 }
1534 1533
1535 1534 if (is_netdev(bpath)) {
1536 1535 /*
1537 1536 * If bpath is a network device path, then v2path
1538 1537 * will be a copy of this sans device arguments.
1539 1538 */
1540 1539 if (strcmp(v2path, devpath) != 0) {
1541 1540 bootlog("wanboot", BOOTLOG_CRIT,
1542 1541 "'net'=%s\n", netalias);
1543 1542 bootlog("wanboot", BOOTLOG_CRIT,
1544 1543 "wanboot requires that the 'net' alias refers to ");
1545 1544 bootlog("wanboot", BOOTLOG_CRIT,
1546 1545 "the network device path from which it loaded");
1547 1546 return (B_FALSE);
1548 1547 }
1549 1548 } else {
1550 1549 bpath = netalias;
1551 1550 }
1552 1551
1553 1552 /*
1554 1553 * Configure the network and return the network device.
1555 1554 */
1556 1555 bootlog("wanboot", BOOTLOG_INFO, "configuring %s\n", bpath);
1557 1556 netdev_path = devpath;
1558 1557 mac_init(bpath);
1559 1558 return (B_TRUE);
1560 1559
1561 1560 error:
1562 1561 /*
1563 1562 * If we haven't established a device path for a network interface,
1564 1563 * then we're doomed.
1565 1564 */
1566 1565 bootlog("wanboot", BOOTLOG_CRIT,
1567 1566 "No network device available for wanboot!");
1568 1567 bootlog("wanboot", BOOTLOG_CRIT,
1569 1568 "(Ensure that the 'net' alias is set correctly)");
1570 1569 return (B_FALSE);
1571 1570 }
1572 1571
1573 1572 /*
1574 1573 * This implementation of bootprog() is used solely by wanboot.
1575 1574 *
1576 1575 * The basic algorithm is as follows:
1577 1576 *
1578 1577 * - The wanboot options (those specified using the "-o" flag) are processed,
1579 1578 * and if necessary the wanboot interpreter is invoked to collect other
↓ open down ↓ |
1542 lines elided |
↑ open up ↑ |
1580 1579 * options.
1581 1580 *
1582 1581 * - The wanboot filesystem (containing certificates, wanboot.conf file, etc.)
1583 1582 * is then downloaded into the bootfs ramdisk, which is mounted for use
1584 1583 * by OpenSSL, access to wanboot.conf, etc.
1585 1584 *
1586 1585 * - The wanboot miniroot is downloaded over http/https into the rootfs
1587 1586 * ramdisk. The bootfs filesystem is unmounted, and the rootfs filesystem
1588 1587 * is booted.
1589 1588 */
1590 -/* EXPORT DELETE END */
1591 1589 /*ARGSUSED*/
1592 1590 int
1593 1591 bootprog(char *bpath, char *bargs, boolean_t user_specified_filename)
1594 1592 {
1595 -/* EXPORT DELETE START */
1596 1593 char *miniroot_path;
1597 1594 url_t server_url;
1598 1595 int ret;
1599 1596
1600 1597 if (!init_netdev(bpath)) {
1601 1598 return (-1);
1602 1599 }
1603 1600
1604 1601 if (!bootinfo_init()) {
1605 1602 bootlog("wanboot", BOOTLOG_CRIT, "Cannot initialize bootinfo");
1606 1603 return (-1);
1607 1604 }
1608 1605
1609 1606 /*
1610 1607 * Get default values from PROM, etc., process any boot arguments
1611 1608 * (specified with the "-o" option), and initialize the interface.
1612 1609 */
1613 1610 if (!wanboot_init_interface(wanboot_arguments)) {
1614 1611 return (-1);
1615 1612 }
1616 1613
1617 1614 /*
1618 1615 * Determine which encryption and hashing algorithms the client
1619 1616 * is configured to use.
1620 1617 */
1621 1618 init_encryption();
1622 1619 init_hashing();
1623 1620
1624 1621 /*
1625 1622 * Get the bootserver value. Should be of the form:
1626 1623 * http://host[:port]/abspath.
1627 1624 */
1628 1625 ret = get_url(BI_BOOTSERVER, &server_url);
1629 1626 if (ret != 0) {
1630 1627 bootlog("wanboot", BOOTLOG_CRIT,
1631 1628 "Unable to retrieve the bootserver URL");
1632 1629 return (-1);
1633 1630 }
1634 1631
1635 1632 /*
1636 1633 * Get the wanboot file system and mount it. Contains metdata
1637 1634 * needed by wanboot.
1638 1635 */
1639 1636 if (get_wanbootfs(&server_url) != 0) {
1640 1637 return (-1);
1641 1638 }
1642 1639
1643 1640 /*
1644 1641 * Check that there is a valid wanboot.conf file in the wanboot
1645 1642 * file system.
1646 1643 */
1647 1644 if (bootconf_init(&bc_handle, NULL) != BC_E_NOERROR) {
1648 1645 bootlog("wanboot", BOOTLOG_CRIT,
1649 1646 "wanboot.conf error (code=%d)", bc_handle.bc_error_code);
1650 1647 return (-1);
1651 1648 }
1652 1649
1653 1650 /*
1654 1651 * Set the time
1655 1652 */
1656 1653 init_boot_time();
1657 1654
1658 1655 /*
1659 1656 * Verify that URLs in wanboot.conf can be reached, etc.
1660 1657 */
1661 1658 if (!wanboot_verify_config()) {
1662 1659 return (-1);
1663 1660 }
1664 1661
1665 1662 /*
1666 1663 * Retrieve the miniroot.
1667 1664 */
1668 1665 if (get_miniroot(&miniroot_path) != 0) {
1669 1666 return (-1);
↓ open down ↓ |
64 lines elided |
↑ open up ↑ |
1670 1667 }
1671 1668
1672 1669 /*
1673 1670 * We don't need the wanboot file system mounted anymore and
1674 1671 * should unmount it so that we can mount the miniroot.
1675 1672 */
1676 1673 (void) unmountroot();
1677 1674
1678 1675 boot_ramdisk(RD_ROOTFS);
1679 1676
1680 -/* EXPORT DELETE END */
1681 1677 return (0);
1682 1678 }
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX