1/* 2 * Copyright (c) 2005-2007 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23/* 24 * modeNetboot.c 25 * bless 26 * 27 * Created by Shantonu Sen on 10/10/05. 28 * Copyright 2005-2007 Apple Inc. All Rights Reserved. 29 * 30 */ 31 32#include <stdlib.h> 33#include <unistd.h> 34#include <string.h> 35#include <sys/socket.h> 36#include <net/if.h> 37#include <arpa/nameser.h> 38#include <netinet/in.h> 39#include <arpa/inet.h> 40 41 42#include "enums.h" 43#include "structs.h" 44 45#include "bless.h" 46#include "bless_private.h" 47#include "protos.h" 48 49static bool validateAddress(const char *host); 50 51static int parseURL(BLContextPtr context, 52 const char *url, 53 char * scheme, 54 char * interface, 55 char * host, 56 char * path, 57 bool requiresPath, 58 const char *requiresScheme, 59 bool useBackslash); 60 61 62int modeNetboot(BLContextPtr context, struct clarg actargs[klast]) 63{ 64 int ret; 65 BLPreBootEnvType preboot; 66 char interface[IF_NAMESIZE]; 67 char host[NS_MAXDNAME]; 68 char path[MAXPATHLEN]; 69 char scheme[128]; 70 bool useBackslash = false; 71 72 if(!( actargs[kserver].present || actargs[kbooter].present)) { 73 blesscontextprintf(context, kBLLogLevelError, 74 "No NetBoot server specification provided\n"); 75 return 1; 76 } 77 78 79 ret = BLGetPreBootEnvironmentType(context, &preboot); 80 if(ret) 81 return 2; 82 83 if(preboot == kBLPreBootEnvType_OpenFirmware) { 84 useBackslash = true; 85 } 86 87 88 if (actargs[kserver].present) { 89 blesscontextprintf(context, kBLLogLevelVerbose, 90 "NetBoot server specification given as: %s\n", 91 actargs[kserver].argument); 92 93 ret = parseURL(context, 94 actargs[kserver].argument, 95 scheme, 96 interface, 97 host, 98 path, 99 false, 100 NULL, 101 useBackslash); 102 103 } else { 104 blesscontextprintf(context, kBLLogLevelVerbose, 105 "NetBoot booter given as: %s\n", 106 actargs[kbooter].argument); 107 108 ret = parseURL(context, 109 actargs[kbooter].argument, 110 scheme, 111 interface, 112 host, 113 path, 114 true, 115 "tftp", 116 useBackslash); 117 118 } 119 120 if(ret) 121 return 1; 122 123 if(preboot == kBLPreBootEnvType_OpenFirmware) { 124 // XXX temporary stub 125#define NVRAM "/usr/sbin/nvram" 126 char * OFSettings[6]; 127 128 char bootdevice[1024]; 129 char bootfile[1024]; 130 char bootcommand[1024]; 131 char bootargs[1024]; 132 char ofstring[1024]; 133 134 pid_t p; 135 int status; 136 137 if (0 != strcmp(scheme, "bsdp") && 0 != strcmp(scheme, "tftp")) { 138 blesscontextprintf(context, kBLLogLevelError, 139 "Netboot scheme %s not supported on Open Firmware systems\n", 140 scheme); 141 } 142 143 ret = BLGetOpenFirmwareBootDeviceForNetworkPath(context, 144 interface, 145 strcmp(host, "255.255.255.255") == 0 ? NULL : host, 146 path, 147 ofstring); 148 if(ret) 149 return 3; 150 151 sprintf(bootdevice, "boot-device=%s", ofstring); 152 153 if(actargs[kkernel].present) { 154 ret = parseURL(context, 155 actargs[kkernel].argument, 156 scheme, 157 interface, 158 host, 159 path, 160 true, 161 "tftp", 162 useBackslash); 163 if(ret) 164 return 1; 165 166 ret = BLGetOpenFirmwareBootDeviceForNetworkPath(context, 167 interface, 168 strcmp(host, "255.255.255.255") == 0 ? NULL : host, 169 path, 170 ofstring); 171 if(ret) 172 return 4; 173 174 sprintf(bootfile, "boot-file=%s", ofstring); 175 } else { 176 sprintf(bootfile, "boot-file="); 177 } 178 179 if(actargs[kmkext].present) { 180 blesscontextprintf(context, kBLLogLevelError, 181 "mkext option not supported on Open Firmware systems\n"); 182 return 5; 183 } 184 185 sprintf(bootcommand, "boot-command=mac-boot"); 186 sprintf(bootargs, "boot-args="); 187 188 OFSettings[0] = NVRAM; 189 OFSettings[1] = bootdevice; 190 OFSettings[2] = bootfile; 191 OFSettings[3] = bootcommand; 192 OFSettings[4] = bootargs; 193 OFSettings[5] = NULL; 194 195 blesscontextprintf(context, kBLLogLevelVerbose, "OF Setings:\n" ); 196 blesscontextprintf(context, kBLLogLevelVerbose, "\t\tprogram: %s\n", OFSettings[0] ); 197 blesscontextprintf(context, kBLLogLevelVerbose, "\t\t%s\n", OFSettings[1] ); 198 blesscontextprintf(context, kBLLogLevelVerbose, "\t\t%s\n", OFSettings[2] ); 199 blesscontextprintf(context, kBLLogLevelVerbose, "\t\t%s\n", OFSettings[3] ); 200 blesscontextprintf(context, kBLLogLevelVerbose, "\t\t%s\n", OFSettings[4] ); 201 202 p = fork(); 203 if (p == 0) { 204 int ret = execv(NVRAM, OFSettings); 205 if(ret == -1) { 206 blesscontextprintf(context, kBLLogLevelError, "Could not exec %s\n", NVRAM ); 207 } 208 _exit(1); 209 } 210 211 do { 212 p = wait(&status); 213 } while (p == -1 && errno == EINTR); 214 215 if(p == -1 || status) { 216 blesscontextprintf(context, kBLLogLevelError, "%s returned non-0 exit status\n", NVRAM ); 217 return 3; 218 } 219 220 } else if(preboot == kBLPreBootEnvType_EFI) { 221 CFStringRef booterXML = NULL, kernelXML = NULL, mkextXML = NULL, kernelcacheXML = NULL; 222 BLNetBootProtocolType protocol = kBLNetBootProtocol_Unknown; 223 224 if (0 == strcmp(scheme, "bsdp") || 0 == strcmp(scheme, "tftp")) { 225 protocol = kBLNetBootProtocol_BSDP; 226 } else if (0 == strcmp(scheme, "pxe")) { 227 protocol = kBLNetBootProtocol_PXE; 228 229 if (0 != strcmp(host, "255.255.255.255")) { 230 blesscontextprintf(context, kBLLogLevelError, 231 "PXE requires broadcast address 255.255.255.255\n"); 232 return 3; 233 } 234 235 } else { 236 blesscontextprintf(context, kBLLogLevelError, 237 "Netboot scheme %s not supported on EFI systems\n", 238 scheme); 239 return 3; 240 } 241 242 243 ret = BLCreateEFIXMLRepresentationForNetworkPath(context, 244 protocol, 245 interface, 246 strcmp(host, "255.255.255.255") == 0 ? NULL : host, 247 strlen(path) > 0 ? path : NULL, 248 actargs[koptions].present ? actargs[koptions].argument : NULL, 249 &booterXML); 250 if(ret) 251 return 3; 252 253 254 if(actargs[kkernel].present) { 255 ret = parseURL(context, 256 actargs[kkernel].argument, 257 scheme, 258 interface, 259 host, 260 path, 261 true, 262 "tftp", 263 false); 264 if(ret) 265 return 1; 266 267 ret = BLCreateEFIXMLRepresentationForNetworkPath(context, 268 protocol, 269 interface, 270 host, 271 path, 272 NULL, 273 &kernelXML); 274 if(ret) 275 return 3; 276 } 277 278 279 if(actargs[kmkext].present) { 280 ret = parseURL(context, 281 actargs[kmkext].argument, 282 scheme, 283 interface, 284 host, 285 path, 286 true, 287 "tftp", 288 false); 289 if(ret) 290 return 1; 291 292 ret = BLCreateEFIXMLRepresentationForNetworkPath(context, 293 protocol, 294 interface, 295 host, 296 path, 297 NULL, 298 &mkextXML); 299 if(ret) 300 return 3; 301 } 302 303 if(actargs[kkernelcache].present) { 304 ret = parseURL(context, 305 actargs[kkernelcache].argument, 306 scheme, 307 interface, 308 host, 309 path, 310 true, 311 "tftp", 312 false); 313 if(ret) 314 return 1; 315 316 ret = BLCreateEFIXMLRepresentationForNetworkPath(context, 317 protocol, 318 interface, 319 host, 320 path, 321 NULL, 322 &kernelcacheXML); 323 if(ret) 324 return 3; 325 } 326 327 328 ret = setefinetworkpath(context, booterXML, kernelXML, mkextXML, kernelcacheXML, 329 actargs[knextonly].present); 330 331 if(ret) { 332 blesscontextprintf(context, kBLLogLevelError, "Can't set EFI\n" ); 333 return 1; 334 } else { 335 blesscontextprintf(context, kBLLogLevelVerbose, "EFI set successfully\n" ); 336 } 337 } else { 338 blesscontextprintf(context, kBLLogLevelError, 339 "NetBoot not supported on this system\n"); 340 return 3; 341 } 342 343 return 0; 344} 345 346static bool validateAddress(const char *host) 347{ 348 in_addr_t addr; 349 int ret; 350 351 ret = inet_pton(PF_INET, host, &addr); 352 if(ret == 1) 353 return true; 354 else 355 return false; 356} 357 358static int parseURL(BLContextPtr context, 359 const char *url, 360 char * scheme, 361 char * interface, 362 char * host, 363 char * path, 364 bool requiresPath, 365 const char *requiresScheme, 366 bool useBackslash) 367{ 368 int ret; 369 370 CFURLRef serverURL; 371 CFStringRef schemeString, interfaceString, pathString, hostString; 372 CFStringRef requiredScheme = NULL; 373 374 serverURL = CFURLCreateAbsoluteURLWithBytes(kCFAllocatorDefault, 375 (const UInt8 *)url, 376 strlen(url), 377 kCFStringEncodingUTF8, 378 NULL, false); 379 380 if(!serverURL || !CFURLCanBeDecomposed(serverURL)) { 381 if(serverURL) CFRelease(serverURL); 382 383 blesscontextprintf(context, kBLLogLevelError, 384 "Could not interpret %s as a URL\n", url); 385 return 2; 386 } 387 388 schemeString = CFURLCopyScheme(serverURL); 389 if (requiresScheme) { 390 requiredScheme = CFStringCreateWithCString(kCFAllocatorDefault,requiresScheme,kCFStringEncodingUTF8); 391 } 392 if(!schemeString || (requiredScheme && !CFEqual(schemeString, requiredScheme))) { 393 394 blesscontextprintf(context, kBLLogLevelError, 395 "Unrecognized scheme %s\n", BLGetCStringDescription(schemeString)); 396 397 if(schemeString) CFRelease(schemeString); 398 if(requiredScheme) CFRelease(requiredScheme); 399 400 return 2; 401 } 402 403 if(!CFStringGetCString(schemeString,scheme,128,kCFStringEncodingUTF8)) { 404 blesscontextprintf(context, kBLLogLevelError, 405 "Can't interpret scheme as string\n"); 406 return 3; 407 } 408 409 if(requiredScheme) CFRelease(requiredScheme); 410 CFRelease(schemeString); 411 412 interfaceString = CFURLCopyUserName(serverURL); 413 414 if(interfaceString == NULL) { 415 416 ret = BLGetPreferredNetworkInterface(context, interface); 417 if(ret) { 418 blesscontextprintf(context, kBLLogLevelError, 419 "Failed to determine preferred network interface\n"); 420 return 1; 421 } else { 422 blesscontextprintf(context, kBLLogLevelVerbose, 423 "Preferred network interface is %s\n", interface); 424 } 425 } else { 426 if(!CFStringGetCString(interfaceString,interface,IF_NAMESIZE,kCFStringEncodingUTF8)) { 427 CFRelease(interfaceString); 428 blesscontextprintf(context, kBLLogLevelError, 429 "Can't interpret interface as string\n"); 430 return 3; 431 } 432 433 if(!BLIsValidNetworkInterface(context, interface)) { 434 blesscontextprintf(context, kBLLogLevelError, 435 "%s is not a valid interface\n", interface); 436 return 4; 437 } 438 439 CFRelease(interfaceString); 440 } 441 442 blesscontextprintf(context, kBLLogLevelVerbose, 443 "Using interface %s\n", interface); 444 445 pathString = CFURLCopyStrictPath(serverURL, NULL); 446 447 // path component must be NULL or empty 448 if(requiresPath) { 449 if(pathString == NULL || CFEqual(pathString, CFSTR(""))) { 450 if(pathString) CFRelease(pathString); 451 blesscontextprintf(context, kBLLogLevelError, 452 "Specification must contain a path\n"); 453 return 5; 454 } 455 if(!CFStringGetCString(pathString,path,MAXPATHLEN,kCFStringEncodingUTF8)) { 456 CFRelease(pathString); 457 blesscontextprintf(context, kBLLogLevelError, 458 "Can't interpret path as string\n"); 459 return 5; 460 } 461 } else { 462 if(!(pathString == NULL || CFEqual(pathString, CFSTR("")))) { 463 CFRelease(pathString); 464 blesscontextprintf(context, kBLLogLevelError, 465 "Specification can't contain a path\n"); 466 return 5; 467 } 468 path[0] = '\0'; 469 } 470 471 if(pathString) CFRelease(pathString); 472 473 if(useBackslash) { 474 int i, len; 475 for(i=0, len=strlen(path); i < len; i++) { 476 if(path[i] == '/') { 477 path[i] = '\\'; 478 } 479 } 480 } 481 482 483 hostString = CFURLCopyHostName(serverURL); 484 485 // host must be present 486 if(hostString == NULL || CFEqual(hostString, CFSTR(""))) { 487 if(hostString) CFRelease(hostString); 488 blesscontextprintf(context, kBLLogLevelError, 489 "Specification doesn't contain host\n"); 490 return 5; 491 } 492 493 if(!CFStringGetCString(hostString,host,NS_MAXDNAME,kCFStringEncodingUTF8)) { 494 CFRelease(hostString); 495 blesscontextprintf(context, kBLLogLevelError, 496 "Can't interpret host as string\n"); 497 return 5; 498 } 499 500 CFRelease(hostString); 501 502 if(!validateAddress(host)) { 503 blesscontextprintf(context, kBLLogLevelError, 504 "Can't interpret host %s as an IPv4 address\n", 505 host); 506 return 6; 507 } 508 509 return 0; 510} 511