1/* 2 * Copyright (c) 2006 Apple Computer, 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#include <CoreFoundation/CoreFoundation.h> 24#include <CoreFoundation/CFBundlePriv.h> 25 26#include <IOKit/kext/OSKext.h> 27#include <IOKit/kext/OSKextPrivate.h> 28#include <IOKit/kext/fat_util.h> 29#include <IOKit/kext/macho_util.h> 30 31#include <sys/types.h> 32#include <sys/wait.h> 33 34#include "QEQuery.h" 35 36#include "kextfind_main.h" 37 38#include "kextfind_query.h" 39#include "kextfind_commands.h" 40 41/***** 42 * Version expressions on the command line get parsed into an operator 43 * and one or two binary versions. 44 */ 45typedef enum { 46 kVersionNone, 47 kVersionEqual, 48 kVersionNotEqual, 49 kVersionGreaterThan, 50 kVersionGreaterOrEqual, 51 kVersionLessThan, 52 kVersionLessOrEqual, 53 kVersionRange 54} VersionOperator; 55 56 57/******************************************************************************* 58* Predicate option processing 59* 60* Include the query predicates and commands so that we can intelligently 61* handle sub-options. 62*******************************************************************************/ 63struct option echo_opt_info[] = { 64 { kPredOptNameNoNewline, no_argument, NULL, kPredOptNoNewline }, 65 { kOptNameNulTerminate, no_argument, NULL, kOptNulTerminate }, 66 QUERY_PREDICATES 67 QUERY_COMMANDS 68 { NULL, 0, NULL, 0 } // sentinel to terminate list 69}; 70 71#define ECHO_OPTS "0n" 72 73/**********/ 74 75struct option property_opt_info[] = { 76 { kPredOptNameCaseInsensitive, no_argument, NULL, kPredOptCaseInsensitive }, 77 { kPredOptNameSubstring, no_argument, NULL, kPredOptSubstring }, 78 QUERY_PREDICATES 79 QUERY_COMMANDS 80 { NULL, 0, NULL, 0 } // sentinel to terminate list 81}; 82 83#define PROPERTY_OPTS "is" 84 85/**********/ 86 87struct option print_opt_info[] = { 88 { kOptNameNulTerminate, no_argument, NULL, kOptNulTerminate }, 89 QUERY_PREDICATES 90 QUERY_COMMANDS 91 { NULL, 0, NULL, 0 } // sentinel to terminate list 92}; 93 94#define PRINT_OPTS "0" 95 96 97/******************************************************************************* 98* Module-private functions. 99*******************************************************************************/ 100static int parseVersionArg(const char * string, 101 VersionOperator * versionOperator, 102 OSKextVersion * version1, 103 OSKextVersion * version2, 104 uint32_t * error); 105 106 107Boolean parseStringElementOptions( 108 CFMutableDictionaryRef element, 109 int argc, 110 char * const argv[], 111 uint32_t * index, 112 QueryContext * context, 113 QEQueryError * error); 114 115Boolean parseEchoOptions( 116 CFMutableDictionaryRef element, 117 int argc, 118 char * const argv[], 119 uint32_t * index, 120 QueryContext * context, 121 QEQueryError * error); 122 123Boolean parsePrintOptions( 124 CFMutableDictionaryRef element, 125 int argc, 126 char * const argv[], 127 uint32_t * index, 128 QueryContext * context, 129 QEQueryError * error); 130 131Boolean handleNonOption(int opt_char, 132 char * const argv[], 133 QueryContext * context, 134 QEQueryError * error); 135 136/******************************************************************************* 137* 138*******************************************************************************/ 139Boolean parseArgument( 140 CFMutableDictionaryRef element, 141 char * const argv[], 142 uint32_t * num_used, 143 void * user_data __unused, 144 QEQueryError * error) 145{ 146 Boolean result = false; 147 uint32_t index = 0; // this function starts with the arg itself 148 CFStringRef arg = NULL; // must release 149 150 if (!argv[index]) { 151 *error = kQEQueryErrorInvalidOrMissingArgument; 152 goto finish; 153 } 154 155 arg = CFStringCreateWithCString(kCFAllocatorDefault, argv[index], 156 kCFStringEncodingUTF8); 157 index++; 158 159 *num_used += index; // this is not a QE callback! :-) 160 161 QEQueryElementAppendArgument(element, arg); 162 163 result = true; 164finish: 165 if (arg) CFRelease(arg); 166 return result; 167} 168 169/******************************************************************************* 170* 171*******************************************************************************/ 172Boolean parseProperty( 173 CFMutableDictionaryRef element, 174 int argc, 175 char * const argv[], 176 uint32_t * num_used, 177 void * user_data, 178 QEQueryError * error) 179{ 180 Boolean result = false; 181 QueryContext * context = (QueryContext *)user_data; 182 CFStringRef predicate = NULL; // don't release 183 Boolean needValue = true; 184 uint32_t index = 1; 185 186 predicate = QEQueryElementGetPredicate(element); 187 188 if (CFEqual(predicate, CFSTR(kPredNamePropertyExists))) { 189 190 CFDictionarySetValue(element, CFSTR(kSearchStyleKeyExists), kCFBooleanTrue); 191 needValue = false; 192 193 } else if (!parseStringElementOptions(element, argc, argv, &index, 194 context, error)) { 195 goto finish; 196 } 197 198 /* Fudge the predicate so we can use one eval callback. 199 */ 200 QEQueryElementSetPredicate(element, CFSTR(kPredNameProperty)); 201 202 if (!parseArgument(element, &argv[index], &index, user_data, error)) { 203 goto finish; 204 } 205 if (needValue) { 206 if (!parseArgument(element, &argv[index], &index, user_data, error)) { 207 goto finish; 208 } 209 } 210 211 result = true; 212finish: 213 *num_used += index; 214 return result; 215} 216 217/******************************************************************************* 218* 219*******************************************************************************/ 220Boolean parseBundleName( 221 CFMutableDictionaryRef element, 222 int argc, 223 char * const argv[], 224 uint32_t * num_used, 225 void * user_data, 226 QEQueryError * error) 227{ 228 Boolean result = false; 229 QueryContext * context = (QueryContext *)user_data; 230 uint32_t index = 1; 231 232 if (!parseStringElementOptions(element, argc, argv, &index, 233 context, error)) { 234 goto finish; 235 } 236 237 if (!parseArgument(element, &argv[index], &index, user_data, error)) { 238 goto finish; 239 } 240 241 QEQueryElementSetPredicate(element, CFSTR(kPredNameBundleName)); 242 result = true; 243 244finish: 245 *num_used += index; 246 return result; 247} 248 249/******************************************************************************* 250* 251*******************************************************************************/ 252Boolean evalBundleName( 253 CFDictionaryRef element, 254 void * object, 255 void * user_data, 256 QEQueryError * error __unused) 257{ 258 Boolean result = false; 259 QueryContext * context = (QueryContext *)user_data; 260 OSKextRef theKext = (OSKextRef)object; 261 CFStringRef queryName = QEQueryElementGetArgumentAtIndex(element, 0); 262 CFURLRef kextURL = NULL; // do not release 263 CFStringRef bundleName = NULL; // must release 264 265 kextURL = OSKextGetURL(theKext); 266 if (!kextURL) { 267 OSKextLog(/* kext */ NULL, 268 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 269 "Kext has no URL!"); 270 goto finish; 271 } 272 bundleName = CFURLCopyLastPathComponent(kextURL); 273 if (!bundleName) { 274 OSKextLog(/* kext */ NULL, 275 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 276 "Kext has bad URL."); 277 goto finish; 278 } 279 280 CFOptionFlags searchOptions = 0; 281 282 if (context->caseInsensitive || 283 CFDictionaryGetValue(element, 284 CFSTR(kSearchStyleCaseInsensitive))) { 285 286 searchOptions |= kCFCompareCaseInsensitive; 287 } 288 289 /* If the global or predicate substring flag was set, do a substring 290 * match. 291 */ 292 if (context->substrings || 293 CFDictionaryGetValue(element, CFSTR(kSearchStyleSubstring))) { 294 CFRange findResult = CFStringFind(bundleName, 295 queryName, searchOptions); 296 297 if (findResult.location != kCFNotFound) { 298 result = true; 299 } 300 301 } else { 302 CFComparisonResult compareResult = CFStringCompareWithOptions( 303 bundleName, queryName, 304 CFRangeMake(0, CFStringGetLength(bundleName)), 305 searchOptions); 306 307 if (compareResult == kCFCompareEqualTo) { 308 result = true; 309 } 310 } 311 312finish: 313 SAFE_RELEASE(bundleName); 314 return result; 315} 316 317/******************************************************************************* 318* 319*******************************************************************************/ 320Boolean parseShorthand( 321 CFMutableDictionaryRef element, 322 int argc, 323 char * const argv[], 324 uint32_t * num_used, 325 void * user_data, 326 QEQueryError * error) 327{ 328 Boolean result = false; 329 QueryContext * context = (QueryContext *)user_data; 330 CFStringRef predicate = QEQueryElementGetPredicate(element); 331 uint32_t index = 1; 332 333 if (CFEqual(predicate, CFSTR(kPredNameBundleID))) { 334 335 if (!parseStringElementOptions(element, argc, argv, &index, 336 context, error)) { 337 goto finish; 338 } 339 340 QEQueryElementAppendArgument(element, kCFBundleIdentifierKey); 341 if (!parseArgument(element, &argv[index], &index, user_data, error)) { 342 goto finish; 343 } 344 } else { 345 346 if (CFEqual(predicate, CFSTR(kPredNameRoot))) { 347 348 QEQueryElementAppendArgument(element, CFSTR(kOSBundleRequired)); 349 QEQueryElementAppendArgument(element, CFSTR(kOSBundleRequiredRoot)); 350 CFDictionarySetValue(element, CFSTR(kSearchStyleExact), kCFBooleanTrue); 351 352 } else if (CFEqual(predicate, CFSTR(kPredNameConsole))) { 353 354 QEQueryElementAppendArgument(element, CFSTR(kOSBundleRequired)); 355 QEQueryElementAppendArgument(element, CFSTR(kOSBundleRequiredConsole)); 356 CFDictionarySetValue(element, CFSTR(kSearchStyleExact), kCFBooleanTrue); 357 358 } else if (CFEqual(predicate, CFSTR(kPredNameLocalRoot))) { 359 360 QEQueryElementAppendArgument(element, CFSTR(kOSBundleRequired)); 361 QEQueryElementAppendArgument(element, CFSTR(kOSBundleRequiredLocalRoot)); 362 CFDictionarySetValue(element, CFSTR(kSearchStyleExact), kCFBooleanTrue); 363 364 } else if (CFEqual(predicate, CFSTR(kPredNameNetworkRoot))) { 365 366 QEQueryElementAppendArgument(element, CFSTR(kOSBundleRequired)); 367 QEQueryElementAppendArgument(element, CFSTR(kOSBundleRequiredNetworkRoot)); 368 CFDictionarySetValue(element, CFSTR(kSearchStyleExact), kCFBooleanTrue); 369 370 } else if (CFEqual(predicate, CFSTR(kPredNameSafeBoot))) { 371 372 QEQueryElementAppendArgument(element, CFSTR(kOSBundleRequired)); 373 QEQueryElementAppendArgument(element, CFSTR(kOSBundleRequiredSafeBoot)); 374 CFDictionarySetValue(element, CFSTR(kSearchStyleExact), kCFBooleanTrue); 375 } else { 376 goto finish; 377 } 378 } 379 380 QEQueryElementSetPredicate(element, CFSTR(kPredNameProperty)); 381 result = true; 382 383finish: 384 *num_used += index; 385 return result; 386} 387 388/******************************************************************************* 389* 390*******************************************************************************/ 391Boolean _evalPropertyInDict( 392 CFDictionaryRef element, 393 void * object, 394 CFDictionaryRef searchDict, 395 void * user_data, 396 QEQueryError * error) 397{ 398 Boolean result = false; 399 OSKextRef theKext = (OSKextRef)object; 400 QueryContext * context = (QueryContext *)user_data; 401 CFStringRef propName = NULL; 402 CFStringRef queryValue = NULL; 403 CFTypeRef foundValue = NULL; 404 CFTypeID foundType = 0; 405 CFLocaleRef locale = NULL; // must release 406 CFNumberFormatterRef formatter = NULL; // must release 407 CFNumberRef searchNumber = NULL; // must release 408 char * scratchCString = NULL; // must free 409 410 propName = QEQueryElementGetArgumentAtIndex(element, 0); 411 if (searchDict) { 412 foundValue = CFDictionaryGetValue(searchDict, propName); 413 } else { 414 foundValue = OSKextGetValueForInfoDictionaryKey(theKext, propName); 415 } 416 if (!foundValue) { 417 goto finish; 418 } 419 foundType = CFGetTypeID(foundValue); 420 421 if (CFDictionaryGetValue(element, CFSTR(kSearchStyleKeyExists))) { 422 if (foundValue) { 423 result = true; 424 } 425 goto finish; 426 } 427 428 queryValue = QEQueryElementGetArgumentAtIndex(element, 1); 429 430 if (foundType == CFStringGetTypeID()) { 431 432 /* Exact searches trump any global settings in the query context. */ 433 if (CFDictionaryGetValue(element, CFSTR(kSearchStyleExact))) { 434 result = CFEqual(foundValue, queryValue); 435 goto finish; 436 } else { 437 CFOptionFlags searchOptions = 0; 438 if (context->caseInsensitive || 439 CFDictionaryGetValue(element, 440 CFSTR(kSearchStyleCaseInsensitive))) { 441 442 searchOptions |= kCFCompareCaseInsensitive; 443 } 444 445 /* If the global or predicate substring flag was set, do a substring 446 * match. 447 */ 448 if (context->substrings || 449 CFDictionaryGetValue(element, CFSTR(kSearchStyleSubstring))) { 450 CFRange findResult = CFStringFind(foundValue, 451 queryValue, searchOptions); 452 453 if (findResult.location != kCFNotFound) { 454 result = true; 455 } 456 457 } else { 458 CFComparisonResult compareResult = CFStringCompareWithOptions( 459 foundValue, queryValue, 460 CFRangeMake(0, CFStringGetLength(foundValue)), 461 searchOptions); 462 463 if (compareResult == kCFCompareEqualTo) { 464 result = true; 465 } 466 } 467 goto finish; 468 } 469 } else if (foundType == CFNumberGetTypeID()) { 470 /* Don't apply the global query context's substrings flag to numbers. */ 471 if (CFDictionaryGetValue(element, CFSTR(kSearchStyleSubstring))) { 472 473 /* Substrings on numeric values doesn't really make sense. 474 */ 475 goto finish; 476 } else { 477 CFNumberRef foundNumber = NULL; // do not release 478 CFRange stringRange; 479 480 foundNumber = (CFNumberRef)foundValue; 481 482 locale = CFLocaleCopyCurrent(); 483 if (!locale) { 484 *error = kQEQueryErrorEvaluationCallbackFailed; 485 OSKextLog(/* kext */ NULL, 486 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 487 "Can't get current locale."); 488 goto finish; 489 } 490 491 formatter = CFNumberFormatterCreate(kCFAllocatorDefault, locale, 492 kCFNumberFormatterNoStyle); 493 if (!formatter) { 494 *error = kQEQueryErrorEvaluationCallbackFailed; 495 goto finish; 496 } 497 498 stringRange = CFRangeMake(0, CFStringGetLength(queryValue)); 499 500 searchNumber = CFNumberFormatterCreateNumberFromString( 501 kCFAllocatorDefault, formatter, queryValue, 502 &stringRange, 0); 503 if (!formatter) { 504 *error = kQEQueryErrorEvaluationCallbackFailed; 505 scratchCString = createUTF8CStringForCFString(queryValue); 506 OSKextLog(/* kext */ NULL, 507 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 508 "Failed to parse number string '%s'.", 509 scratchCString); 510 SAFE_FREE_NULL(scratchCString); 511 goto finish; 512 } 513 514 if (CFEqual(foundNumber, searchNumber)) { 515 result = true; 516 goto finish; 517 } 518 } 519 520 521 } else if (foundType == CFBooleanGetTypeID()) { 522 /* Don't apply the global query context's substrings flag to numbers. */ 523 if (CFDictionaryGetValue(element, CFSTR(kSearchStyleSubstring))) { 524 525 /* Substrings on boolean values really don't make sense. 526 */ 527 goto finish; 528 } else { 529 CFBooleanRef foundBoolean = (CFBooleanRef)foundValue; 530 if (CFBooleanGetValue(foundBoolean) == true) { 531 if (CFEqual(queryValue, CFSTR(kWordTrue)) || 532 CFEqual(queryValue, CFSTR(kWordYes)) || 533 CFEqual(queryValue, CFSTR(kWord1))) { 534 535 result = true; 536 goto finish; 537 } 538 } else { 539 if (CFEqual(queryValue, CFSTR(kWordFalse)) || 540 CFEqual(queryValue, CFSTR(kWordNo)) || 541 CFEqual(queryValue, CFSTR(kWord0))) { 542 result = true; 543 goto finish; 544 } 545 } 546 } 547 } 548 549finish: 550 551 SAFE_RELEASE(locale); 552 SAFE_RELEASE(formatter); 553 SAFE_RELEASE(searchNumber); 554 SAFE_FREE(scratchCString); 555 556 return result; 557} 558 559/******************************************************************************* 560* 561*******************************************************************************/ 562Boolean evalProperty( 563 CFDictionaryRef element, 564 void * object, 565 void * user_data, 566 QEQueryError * error) 567{ 568 Boolean result = false; 569 570 result = _evalPropertyInDict(element, object, /* dict */ NULL, 571 user_data, error); 572 573 return result; 574} 575 576/******************************************************************************* 577* 578*******************************************************************************/ 579Boolean parseMatchProperty( 580 CFMutableDictionaryRef element, 581 int argc, 582 char * const argv[], 583 uint32_t * num_used, 584 void * user_data, 585 QEQueryError * error) 586{ 587 Boolean result = false; 588 QueryContext * context = (QueryContext *)user_data; 589 CFStringRef predicate = NULL; // don't release 590 Boolean needValue = true; 591 uint32_t index = 1; 592 593 predicate = QEQueryElementGetPredicate(element); 594 595 if (CFEqual(predicate, CFSTR(kPredNameMatchPropertyExists))) { 596 597 CFDictionarySetValue(element, CFSTR(kSearchStyleKeyExists), kCFBooleanTrue); 598 needValue = false; 599 600 } else { 601 if (!parseStringElementOptions(element, argc, argv, &index, 602 context, error)) { 603 goto finish; 604 } 605 } 606 607 /* Fudge the predicate so we can use one eval callback. 608 */ 609 QEQueryElementSetPredicate(element, CFSTR(kPredNameMatchProperty)); 610 611 if (!parseArgument(element, &argv[index], &index, user_data, error)) { 612 goto finish; 613 } 614 if (needValue) { 615 if (!parseArgument(element, &argv[index], &index, user_data, error)) { 616 goto finish; 617 } 618 } 619 620 result = true; 621finish: 622 *num_used += index; 623 return result; 624} 625 626/******************************************************************************* 627* 628*******************************************************************************/ 629Boolean evalMatchProperty( 630 CFDictionaryRef element, 631 void * object, 632 void * user_data, 633 QEQueryError * error) 634{ 635 Boolean result = false; 636 OSKextRef theKext = (OSKextRef)object; 637 CFArrayRef personalities = OSKextCopyPersonalitiesArray(theKext); 638 CFIndex count, i; 639 640 if (!personalities) { 641 goto finish; 642 } 643 644 count = CFArrayGetCount(personalities); 645 for (i = 0; i < count; i++) { 646 CFDictionaryRef personality = CFArrayGetValueAtIndex(personalities, i); 647 if (_evalPropertyInDict(element, object, personality, 648 user_data, error)) { 649 650 result = true; 651 goto finish; 652 } 653 } 654 655finish: 656 if (personalities) CFRelease(personalities); 657 return result; 658} 659 660/******************************************************************************* 661* 662*******************************************************************************/ 663Boolean parseIntegrity( 664 CFMutableDictionaryRef element, 665 int argc __unused, 666 char * const argv[], 667 uint32_t * num_used, 668 void * user_data, 669 QEQueryError * error) 670{ 671 Boolean result = false; 672 QueryContext * context = (QueryContext *)user_data; 673 uint32_t index = 1; // don't care about predicate 674 CFStringRef name = NULL; // must release 675 CFStringRef value = NULL; // must release 676 677 if (!argv[index]) { 678 *error = kQEQueryErrorInvalidOrMissingArgument; 679 goto finish; 680 } 681 682 if (strcmp(argv[index], kIntegrityCorrect) && 683 strcmp(argv[index], kIntegrityUnknown) && 684 strcmp(argv[index], kIntegrityNotApple) && 685 strcmp(argv[index], kIntegrityNoReceipt) && 686 strcmp(argv[index], kIntegrityModified)) { 687 688 *error = kQEQueryErrorInvalidOrMissingArgument; 689 goto finish; 690 } 691 692 value = CFStringCreateWithCString(kCFAllocatorDefault, argv[index], 693 kCFStringEncodingUTF8); 694 index++; 695 696 *num_used += index; 697 698 QEQueryElementAppendArgument(element, value); 699 700 context->checkIntegrity = true; 701 702 /* Kext integrity is no longer used on SnowLeopard. 703 */ 704 OSKextLog(/* kext */ NULL, 705 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 706 "Integrity states are no longer used; no kexts will match."); 707 708 result = true; 709finish: 710 if (name) CFRelease(name); 711 if (value) CFRelease(value); 712 return result; 713} 714 715/******************************************************************************* 716* 717*******************************************************************************/ 718Boolean evalIntegrity( 719 CFDictionaryRef element __unused, 720 void * object __unused, 721 void * user_data __unused, 722 QEQueryError * error __unused) 723{ 724 Boolean result = false; 725 726 /* Kext integrity is no longer used on SnowLeopard. 727 */ 728 return result; 729} 730 731/******************************************************************************* 732* 733*******************************************************************************/ 734Boolean parseFlag( 735 CFMutableDictionaryRef element, 736 int argc __unused, 737 char * const argv[] __unused, 738 uint32_t * num_used, 739 void * user_data, 740 QEQueryError * error __unused) 741{ 742 CFStringRef flag = QEQueryElementGetPredicate(element); 743 QueryContext * context = (QueryContext *)user_data; 744 745 QEQueryElementSetPredicate(element, CFSTR(kPredNameFlag)); 746 CFDictionarySetValue(element, CFSTR(kKeywordFlag), flag); 747 748 if (CFEqual(flag, CFSTR(kPredNameLoaded))) { 749 context->checkLoaded = true; 750 } 751 752 *num_used += 1; 753 754 return true; 755} 756 757/******************************************************************************* 758* 759*******************************************************************************/ 760Boolean evalFlag( 761 CFDictionaryRef element, 762 void * object, 763 void * user_data __unused, 764 QEQueryError * error __unused) 765{ 766 Boolean result = false; 767 OSKextRef theKext = (OSKextRef)object; 768 CFStringRef flag = CFDictionaryGetValue(element, CFSTR(kKeywordFlag)); 769 770 if (CFEqual(flag, CFSTR(kPredNameLoaded))) { 771 return OSKextIsLoaded(theKext); 772 } else if (CFEqual(flag, CFSTR(kPredNameDuplicate))) { 773 CFStringRef kextIdentifier = OSKextGetIdentifier(theKext); 774 if (kextIdentifier) { 775 CFArrayRef kexts = OSKextCopyKextsWithIdentifier(kextIdentifier); 776 if (kexts && CFArrayGetCount(kexts) > 1) { 777 result = true; 778 } 779 SAFE_RELEASE(kexts); 780 } 781 } else if (CFEqual(flag, CFSTR(kPredNameValid))) { 782 return OSKextIsValid(theKext); 783 } if (CFEqual(flag, CFSTR(kPredNameInvalid))) { 784 return !OSKextIsValid(theKext); 785 } else if (CFEqual(flag, CFSTR(kPredNameAuthentic))) { 786 return OSKextIsAuthentic(theKext); 787 } else if (CFEqual(flag, CFSTR(kPredNameInauthentic))) { 788 return !OSKextIsAuthentic(theKext); 789 } else if (CFEqual(flag, CFSTR(kPredNameDependenciesMet))) { 790 return OSKextResolveDependencies(theKext); 791 } else if (CFEqual(flag, CFSTR(kPredNameDependenciesMissing))) { 792 return !OSKextResolveDependencies(theKext); 793 } else if (CFEqual(flag, CFSTR(kPredNameLoadable))) { 794 return OSKextIsLoadable(theKext); 795 } else if (CFEqual(flag, CFSTR(kPredNameNonloadable))) { 796 return !OSKextIsLoadable(theKext); 797 } else if (CFEqual(flag, CFSTR(kPredNameWarnings))) { 798 CFDictionaryRef warnings = OSKextCopyDiagnostics(theKext, 799 kOSKextDiagnosticsFlagWarnings); 800 if (warnings && CFDictionaryGetCount(warnings)) { 801 result = true; 802 } 803 SAFE_RELEASE(warnings); 804 } else if (CFEqual(flag, CFSTR(kPredNameIsLibrary))) { 805 if (OSKextGetCompatibleVersion(theKext) > 0) { 806 result = true; 807 } 808 } else if (CFEqual(flag, CFSTR(kPredNameHasPlugins))) { 809 CFArrayRef plugins = OSKextCopyPlugins(theKext); 810 if (plugins && CFArrayGetCount(plugins)) { 811 result = true; 812 } 813 SAFE_RELEASE(plugins); 814 } else if (CFEqual(flag, CFSTR(kPredNameIsPlugin))) { 815 return OSKextIsPlugin(theKext); 816 } else if (CFEqual(flag, CFSTR(kPredNameHasDebugProperties))) { 817 return OSKextHasLogOrDebugFlags(theKext); 818 } else if (CFEqual(flag, CFSTR(kPredNameIsKernelResource))) { 819 return OSKextIsKernelComponent(theKext); 820 } else if (CFEqual(flag, CFSTR(kPredNameExecutable))) { 821 return OSKextDeclaresExecutable(theKext); 822 } else if (CFEqual(flag, CFSTR(kPredNameNoExecutable))) { 823 return !OSKextDeclaresExecutable(theKext); 824 } 825 826 return result; 827} 828 829/******************************************************************************* 830* 831*******************************************************************************/ 832Boolean parseVersion( 833 CFMutableDictionaryRef element, 834 int argc __unused, 835 char * const argv[], 836 uint32_t * num_used, 837 void * user_data __unused, 838 QEQueryError * error) 839{ 840 Boolean result = false; 841 uint32_t index = 1; // don't care about predicate 842 VersionOperator versionOperator; 843 OSKextVersion version1 = 0; 844 OSKextVersion version2 = 0; 845 CFNumberRef vOpNum = NULL; // must release 846 CFDataRef version1Data = NULL; // must release 847 CFDataRef version2Data = NULL; // must release 848 849 if (!argv[index]) { 850 *error = kQEQueryErrorInvalidOrMissingArgument; 851 goto finish; 852 } 853 854 if (!parseVersionArg(argv[index], &versionOperator, &version1, &version2, error)) { 855 goto finish; 856 } 857 index++; 858 859 vOpNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, 860 &versionOperator); 861 if (!vOpNum) { 862 *error = kQEQueryErrorNoMemory; 863 goto finish; 864 } 865 866 version1Data = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&version1, sizeof(version1)); 867 if (!version1Data) { 868 *error = kQEQueryErrorNoMemory; 869 goto finish; 870 } 871 872 version2Data = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&version2, sizeof(version2)); 873 if (!version2Data) { 874 *error = kQEQueryErrorNoMemory; 875 goto finish; 876 } 877 878 QEQueryElementAppendArgument(element, vOpNum); 879 QEQueryElementAppendArgument(element, version1Data); 880 QEQueryElementAppendArgument(element, version2Data); 881 882 result = true; 883finish: 884 *num_used += index; 885 886 if (vOpNum) CFRelease(vOpNum); 887 if (version1Data) CFRelease(version1Data); 888 if (version2Data) CFRelease(version2Data); 889 return result; 890} 891 892/******************************************************************************* 893* 894*******************************************************************************/ 895Boolean evalVersion( 896 CFDictionaryRef element, 897 void * object, 898 void * user_data __unused, 899 QEQueryError * error) 900{ 901 Boolean result = false; 902 OSKextRef theKext = (OSKextRef)object; 903 OSKextVersion kextVers = OSKextGetVersion(theKext); 904 CFNumberRef vOpNum = NULL; // must release 905 CFDataRef version1Data = NULL; // must release 906 CFDataRef version2Data = NULL; // must release 907 VersionOperator versionOperator; 908 OSKextVersion version1; 909 OSKextVersion version2; 910 911 vOpNum = QEQueryElementGetArgumentAtIndex(element, 0); 912 version1Data = QEQueryElementGetArgumentAtIndex(element, 1); 913 version2Data = QEQueryElementGetArgumentAtIndex(element, 2); 914 915 if (!CFNumberGetValue(vOpNum,kCFNumberSInt32Type,&versionOperator)) { 916 OSKextLog(/* kext */ NULL, 917 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 918 "Internal error getting version operator."); 919 *error = kQEQueryErrorEvaluationCallbackFailed; 920 goto finish; 921 } 922 923 version1 = *(OSKextVersion *)CFDataGetBytePtr(version1Data); 924 version2 = *(OSKextVersion *)CFDataGetBytePtr(version2Data); 925 926 switch (versionOperator) { 927 case kVersionEqual: 928 if (kextVers == version1) { 929 result = true; 930 } 931 break; 932 case kVersionNotEqual: 933 if (kextVers != version1) { 934 result = true; 935 } 936 break; 937 case kVersionGreaterThan: 938 if (kextVers > version1) { 939 result = true; 940 } 941 break; 942 case kVersionGreaterOrEqual: 943 if (kextVers >= version1) { 944 result = true; 945 } 946 break; 947 case kVersionLessThan: 948 if (kextVers < version1) { 949 result = true; 950 } 951 break; 952 case kVersionLessOrEqual: 953 if (kextVers <= version1) { 954 result = true; 955 } 956 break; 957 case kVersionRange: 958 if ((kextVers >= version1) && (kextVers <= version2)) { 959 result = true; 960 } 961 break; 962 default: 963 OSKextLog(/* kext */ NULL, 964 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 965 "Internal evaluation error."); 966 *error = kQEQueryErrorEvaluationCallbackFailed; 967 break; 968 } 969finish: 970 return result; 971} 972 973/******************************************************************************* 974* 975*******************************************************************************/ 976Boolean parseCompatibleWithVersion( 977 CFMutableDictionaryRef element, 978 int argc __unused, 979 char * const argv[], 980 uint32_t * num_used, 981 void * user_data __unused, 982 QEQueryError * error) 983{ 984 Boolean result = false; 985 uint32_t index = 1; // don't care about predicate 986 OSKextVersion compatible_version = 0; 987 CFDataRef versionData = NULL; // must release 988 989 if (!argv[index]) { 990 *error = kQEQueryErrorInvalidOrMissingArgument; 991 goto finish; 992 } 993 994 compatible_version = OSKextParseVersionString(argv[index]); 995 if (compatible_version == -1) { 996 OSKextLog(/* kext */ NULL, 997 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 998 "Invalid version string '%s'.", argv[index]); 999 goto finish; 1000 } 1001 index++; 1002 1003 versionData = CFDataCreate(kCFAllocatorDefault, 1004 (UInt8 *)&compatible_version, sizeof(compatible_version)); 1005 if (!versionData) { 1006 *error = kQEQueryErrorNoMemory; 1007 goto finish; 1008 } 1009 1010 QEQueryElementAppendArgument(element, versionData); 1011 1012 result = true; 1013finish: 1014 *num_used += index; 1015 if (versionData) CFRelease(versionData); 1016 return result; 1017} 1018 1019/******************************************************************************* 1020* 1021*******************************************************************************/ 1022Boolean evalCompatibleWithVersion( 1023 CFDictionaryRef element, 1024 void * object, 1025 void * user_data __unused, 1026 QEQueryError * error __unused) 1027{ 1028 Boolean result = false; 1029 OSKextRef theKext = (OSKextRef)object; 1030 CFDataRef versionData = NULL; // must release 1031 OSKextVersion compatible_version = 0; 1032 1033 versionData = QEQueryElementGetArgumentAtIndex(element, 0); 1034 1035 compatible_version = *(OSKextVersion *)CFDataGetBytePtr(versionData); 1036 1037 if (OSKextIsCompatibleWithVersion(theKext, compatible_version)) { 1038 result = true; 1039 } 1040 1041 return result; 1042} 1043 1044/******************************************************************************* 1045* parseVersionArg() 1046*******************************************************************************/ 1047static int parseVersionArg(const char * string, 1048 VersionOperator * versionOperator, 1049 OSKextVersion * version1, 1050 OSKextVersion * version2, 1051 uint32_t * error) 1052{ 1053 int result = -1; // assume parse error 1054 char * scratch = NULL; // must free 1055 char * v1string = NULL; // don't free 1056 char * hyphen = NULL; // don't free 1057 char * v2string = NULL; // don't free 1058 1059 scratch = strdup(string); 1060 if (!scratch) { 1061 OSKextLogMemError(); 1062 result = 0; 1063 goto finish; 1064 } 1065 1066 v1string = scratch; 1067 1068 *versionOperator = kVersionNone; 1069 1070 switch (scratch[0]) { 1071 case 'e': 1072 *versionOperator = kVersionEqual; 1073 v1string = &scratch[1]; 1074 break; 1075 case 'n': 1076 if (scratch[1] != 'e') { 1077 goto finish; 1078 } 1079 *versionOperator = kVersionNotEqual; 1080 v1string = &scratch[2]; 1081 break; 1082 case 'g': 1083 if (scratch[1] == 'e') { 1084 *versionOperator = kVersionGreaterOrEqual; 1085 } else if (scratch[1] == 't') { 1086 *versionOperator = kVersionGreaterThan; 1087 } else { 1088 goto finish; 1089 } 1090 v1string = &scratch[2]; 1091 1092 break; 1093 case 'l': 1094 if (scratch[1] == 'e') { 1095 *versionOperator = kVersionLessOrEqual; 1096 } else if (scratch[1] == 't') { 1097 result = kVersionLessThan; 1098 } else { 1099 goto finish; 1100 } 1101 v1string = &scratch[2]; 1102 break; 1103 } 1104 1105 hyphen = index(v1string, '-'); 1106 if (hyphen) { 1107 if (*versionOperator != kVersionNone) { 1108 goto finish; 1109 } 1110 *versionOperator = kVersionRange; 1111 v2string = hyphen + 1; 1112 *hyphen = '\0'; 1113 } 1114 1115 *version1 = OSKextParseVersionString(v1string); 1116 if (*version1 == -1) { 1117 goto finish; 1118 } 1119 1120 if (hyphen) { 1121 *version2 = OSKextParseVersionString(v2string); 1122 if (*version2 == -1) { 1123 goto finish; 1124 } 1125 } 1126 1127 if (*versionOperator == kVersionNone) { 1128 *versionOperator = kVersionEqual; 1129 } 1130 1131 result = 1; 1132 1133finish: 1134 if (scratch) { 1135 free(scratch); 1136 scratch = NULL; 1137 } 1138 if (result == -1) { 1139 *error = kQEQueryErrorInvalidOrMissingArgument; 1140 OSKextLog(/* kext */ NULL, 1141 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1142 "Invalid version string '%s'.", string); 1143 result = 0; 1144 } 1145 return result; 1146} 1147 1148/******************************************************************************* 1149* 1150*******************************************************************************/ 1151Boolean parseArch( 1152 CFMutableDictionaryRef element, 1153 int argc __unused, 1154 char * const argv[], 1155 uint32_t * num_used, 1156 void * user_data __unused, 1157 QEQueryError * error) 1158{ 1159 Boolean result = false; 1160 uint32_t index = 1; // don't care about predicate 1161 CFStringRef archString = NULL; // must release 1162 CFArrayRef arches = NULL; // must release 1163 CFIndex count, i; 1164 char * arch = NULL; // must free 1165 1166 if (!argv[index]) { 1167 *error = kQEQueryErrorInvalidOrMissingArgument; 1168 goto finish; 1169 } 1170 1171 archString = CFStringCreateWithCString(kCFAllocatorDefault, 1172 argv[index], kCFStringEncodingUTF8); 1173 if (!archString) { 1174 *error = kQEQueryErrorNoMemory; 1175 goto finish; 1176 } 1177 index++; 1178 1179 arches = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault,archString, CFSTR(",")); 1180 if (!arches) { 1181 goto finish; 1182 } 1183 1184 count = CFArrayGetCount(arches); 1185 for (i = 0; i < count; i++) { 1186 CFStringRef archString = NULL; 1187 const NXArchInfo * archinfo = NULL; 1188 1189 archString = CFArrayGetValueAtIndex(arches, i); 1190 arch = createUTF8CStringForCFString(archString); 1191 if (!arch) { 1192 OSKextLogStringError(/* kext */ NULL); 1193 goto finish; 1194 } 1195 archinfo = NXGetArchInfoFromName(arch); 1196 if (!archinfo) { 1197 OSKextLog(/* kext */ NULL, 1198 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1199 "Unknown architecture %s.", arch); 1200 *error = kQEQueryErrorInvalidOrMissingArgument; 1201 goto finish; 1202 } 1203 free(arch); 1204 arch = NULL; 1205 } 1206 1207 QEQueryElementSetArgumentsArray(element, arches); 1208 result = true; 1209finish: 1210 *num_used += index; 1211 if (archString) CFRelease(archString); 1212 if (arches) CFRelease(arches); 1213 if (arch) free(arch); 1214 return result; 1215} 1216 1217/******************************************************************************* 1218* 1219*******************************************************************************/ 1220Boolean _checkArches( 1221 OSKextRef theKext, 1222 CFArrayRef arches) 1223{ 1224 Boolean result = false; 1225 CFIndex count, i; 1226 char * arch = NULL; // must free 1227 1228 count = CFArrayGetCount(arches); 1229 for (i = 0; i < count; i++) { 1230 CFStringRef archString = NULL; 1231 const NXArchInfo * archinfo = NULL; 1232 1233 archString = CFArrayGetValueAtIndex(arches, i); 1234 arch = createUTF8CStringForCFString(archString); 1235 if (!arch) { 1236 OSKextLogStringError(theKext); 1237 goto finish; 1238 } 1239 archinfo = NXGetArchInfoFromName(arch); 1240 if (!archinfo) { 1241 goto finish; 1242 } 1243 free(arch); 1244 arch = NULL; 1245 if (!OSKextSupportsArchitecture(theKext, archinfo)) { 1246 goto finish; 1247 } 1248 } 1249 1250 result = true; 1251 1252finish: 1253 SAFE_FREE(arch); 1254 return result; 1255} 1256 1257/******************************************************************************* 1258* 1259*******************************************************************************/ 1260Boolean evalArch( 1261 CFDictionaryRef element, 1262 void * object, 1263 void * user_data __unused, 1264 QEQueryError * error __unused) 1265{ 1266 OSKextRef theKext = (OSKextRef)object; 1267 CFArrayRef arches = NULL; // do not release 1268 1269 arches = QEQueryElementGetArguments(element); 1270 if (!arches) { 1271 return false; 1272 } 1273 return _checkArches(theKext, arches); 1274} 1275 1276/******************************************************************************* 1277* 1278*******************************************************************************/ 1279typedef struct { 1280 struct fat_header fat_hdr; 1281 struct fat_arch fat_arch; 1282} FakeFatHeader; 1283 1284Boolean evalArchExact( 1285 CFDictionaryRef element, 1286 void * object, 1287 void * user_data __unused, 1288 QEQueryError * error __unused) 1289{ 1290 Boolean result = false; 1291 OSKextRef theKext = (OSKextRef)object; 1292 CFArrayRef arches = NULL; // do not release 1293 CFIndex count, i; 1294 fat_iterator fiter = NULL; // must close 1295 char * arch = NULL; // must free 1296 struct mach_header * farch; 1297 const NXArchInfo * archinfo = NULL; 1298 1299 fiter = createFatIteratorForKext(theKext); 1300 if (!fiter) { 1301 goto finish; 1302 } 1303 1304 arches = QEQueryElementGetArguments(element); 1305 if (!arches) { 1306 goto finish; 1307 } 1308 count = CFArrayGetCount(arches); 1309 1310 /* First make sure every architecture requested exists in the executable. 1311 */ 1312 if (!_checkArches(theKext, arches)) { 1313 goto finish; 1314 } 1315 1316 /* Now make sure every arch in the executable is represtented by at least 1317 * one requested arch. 1318 */ 1319 while ((farch = (struct mach_header *)fat_iterator_next_arch(fiter, NULL))) { 1320 int swap = 0; 1321 struct fat_arch fakeFatArch; 1322 Boolean thisArchFound = false; 1323#if DEBUG 1324 const NXArchInfo * info; 1325#endif 1326 1327 if (farch->magic == MH_CIGAM || farch->magic == MH_CIGAM_64) { 1328 swap = 1; 1329 } 1330 fakeFatArch.cputype = CondSwapInt32(swap, farch->cputype); 1331 fakeFatArch.cpusubtype = CondSwapInt32(swap, farch->cpusubtype); 1332 1333#if DEBUG 1334info = NXGetArchInfoFromCpuType(fakeFatArch.cputype, fakeFatArch.cpusubtype); 1335fprintf(stderr, " checking architecture %s\n", info->name); 1336#endif 1337 1338 /* Find at least one requested arch that matches our faked-up fat 1339 * header. 1340 */ 1341 for (i = 0; i < count; i++) { 1342 CFStringRef archString = CFArrayGetValueAtIndex(arches, i); 1343 arch = createUTF8CStringForCFString(archString); 1344 if (!arch) { 1345 OSKextLogStringError(theKext); 1346 goto finish; 1347 } 1348 archinfo = NXGetArchInfoFromName(arch); 1349 if (!archinfo) { 1350 goto finish; 1351 } 1352 free(arch); 1353 arch = NULL; 1354#if DEBUG 1355fprintf(stderr, " %s? ", archinfo->name); 1356#endif 1357 if (NXFindBestFatArch(archinfo->cputype, archinfo->cpusubtype, 1358 &fakeFatArch, 1)) { 1359 thisArchFound = true; 1360 break; 1361 1362#if DEBUG 1363fprintf(stderr, "yes\n"); 1364#endif 1365 } else { 1366#if DEBUG 1367fprintf(stderr, "no\n"); 1368#endif 1369 } 1370 } 1371 1372 if (!thisArchFound) { 1373 goto finish; 1374 } 1375 } 1376 1377 result = true; 1378 1379finish: 1380 if (arch) free(arch); 1381 if (fiter) fat_iterator_close(fiter); 1382 return result; 1383} 1384 1385/******************************************************************************* 1386* 1387*******************************************************************************/ 1388Boolean parseDefinesOrReferencesSymbol( 1389 CFMutableDictionaryRef element, 1390 int argc __unused, 1391 char * const argv[], 1392 uint32_t * num_used, 1393 void * user_data, 1394 QEQueryError * error) 1395{ 1396 Boolean result = false; 1397 uint32_t index = 1; // don't care about predicate 1398 1399 if (!parseArgument(element, &argv[index], &index, user_data, error)) { 1400 goto finish; 1401 } 1402 result = true; 1403finish: 1404 *num_used += index; 1405 return result; 1406} 1407 1408/******************************************************************************* 1409* 1410*******************************************************************************/ 1411Boolean evalDefinesOrReferencesSymbol( 1412 CFDictionaryRef element, 1413 void * object, 1414 void * user_data __unused, 1415 QEQueryError * error __unused) 1416{ 1417 Boolean result = false; 1418 OSKextRef theKext = (OSKextRef)object; 1419 CFStringRef predicate = NULL; // don't release 1420 Boolean seekingReference = false; 1421 char * symbol = NULL; // must free 1422 fat_iterator fiter = NULL; // must close 1423 struct mach_header * farch = NULL; 1424 void * farch_end = NULL; 1425 Boolean isKernelComponent; 1426 uint8_t nlist_type; 1427 1428 predicate = QEQueryElementGetPredicate(element); 1429 if (CFEqual(predicate, CFSTR(kPredNameReferencesSymbol))) { 1430 seekingReference = true; 1431 } 1432 1433 symbol = createUTF8CStringForCFString( 1434 QEQueryElementGetArgumentAtIndex(element, 0)); 1435 if (!symbol) { 1436 goto finish; 1437 } 1438 fiter = createFatIteratorForKext(theKext); 1439 if (!fiter) { 1440 goto finish; 1441 } 1442 1443 /* KPI kexts have the symbols listed as undefined, and won't have 1444 * any unresolved references to anything. So, if seekingReference 1445 * is true, we have nothing to do. 1446 */ 1447 isKernelComponent = OSKextIsKernelComponent(theKext); 1448 if (isKernelComponent && seekingReference) { 1449 goto finish; 1450 } 1451 1452 while ((farch = fat_iterator_next_arch(fiter, &farch_end))) { 1453 macho_seek_result seek_result = macho_find_symbol( 1454 farch, farch_end, symbol, &nlist_type, NULL); 1455 1456 if (seek_result == macho_seek_result_found_no_value || 1457 seek_result == macho_seek_result_found) { 1458 1459 uint8_t n_type = N_TYPE & nlist_type; 1460 1461 /* A non-kernel component symbol matches if we're seeking: 1462 * - a reference (-rsym) and the symbol n_type is N_UNDF, or 1463 * - a definition (-dsym) and the symbol n_type is anything else. 1464 * 1465 * For kernel components we only care about defined symbols, 1466 * and in a KPI file those will be either N_UNDF or N_INDR. 1467 */ 1468 if (!isKernelComponent) { 1469 if ((seekingReference && (n_type == N_UNDF)) || 1470 (!seekingReference && (n_type != N_UNDF))) { 1471 1472 result = true; 1473 } 1474 } else { 1475 if ((!seekingReference && (n_type == N_UNDF || n_type == N_INDR))) { 1476 result = true; 1477 } 1478 } 1479 1480 if (result) { 1481 goto finish; 1482 } 1483 1484 if ((seekingReference && (n_type == N_UNDF || n_type == N_INDR)) || 1485 (!seekingReference && (n_type != N_UNDF))) { 1486 1487 result = true; 1488 goto finish; 1489 } 1490 } 1491 } 1492finish: 1493 if (fiter) fat_iterator_close(fiter); 1494 if (symbol) free(symbol); 1495 return result; 1496} 1497 1498/******************************************************************************* 1499* 1500*******************************************************************************/ 1501Boolean parseCommand( 1502 CFMutableDictionaryRef element, 1503 int argc, 1504 char * const argv[], 1505 uint32_t * num_used, 1506 void * user_data, 1507 QEQueryError * error) 1508{ 1509 Boolean result = false; 1510 QueryContext * context = (QueryContext *)user_data; 1511 CFStringRef predicate = QEQueryElementGetPredicate(element); 1512 uint32_t index = 1; 1513 1514 CFDictionarySetValue(element, CFSTR(kKeywordCommand), predicate); 1515 QEQueryElementSetPredicate(element, CFSTR(kPredNameCommand)); 1516 1517 /* For the echo command, look for a -n option or -- to end options. 1518 */ 1519 if (CFEqual(predicate, CFSTR(kPredNameEcho))) { 1520 if (!parseEchoOptions(element, argc, argv, &index, 1521 context, error)) { 1522 goto finish; 1523 } 1524 1525 } else if (CFStringHasPrefix(predicate, CFSTR(kPredPrefixPrint)) && 1526 !CFEqual(predicate, CFSTR(kPredNamePrint0)) && 1527 !CFEqual(predicate, CFSTR(kPredNamePrintDiagnostics))) { 1528 1529 if (!parsePrintOptions(element, argc, argv, &index, 1530 context, error)) { 1531 goto finish; 1532 } 1533 1534 } else if (CFEqual(predicate, CFSTR(kPredNamePrint0))) { 1535 CFDictionarySetValue(element, CFSTR(kKeywordCommand), 1536 CFSTR(kPredNamePrint)); 1537 CFDictionarySetValue(element, CFSTR(kOptNameNulTerminate), 1538 kCFBooleanTrue); 1539 } 1540 1541 if (CFEqual(predicate, CFSTR(kPredNameEcho)) || 1542 CFEqual(predicate, CFSTR(kPredNamePrintProperty)) || 1543 CFEqual(predicate, CFSTR(kPredNamePrintMatchProperty))) { 1544 if (!parseArgument(element, &argv[index], &index, user_data, error)) { 1545 goto finish; 1546 } 1547 } 1548 context->commandSpecified = true; 1549 1550 result = true; 1551finish: 1552 *num_used += index; 1553 return result; 1554} 1555 1556/******************************************************************************* 1557* 1558*******************************************************************************/ 1559char terminatorForElement(CFDictionaryRef element) 1560{ 1561 if (CFDictionaryGetValue(element, CFSTR(kOptNameNulTerminate))) { 1562 return '\0'; 1563 } 1564 return '\n'; 1565} 1566 1567/******************************************************************************* 1568* 1569*******************************************************************************/ 1570Boolean evalCommand( 1571 CFDictionaryRef element, 1572 void * object, 1573 void * user_data, 1574 QEQueryError * error) 1575{ 1576 OSKextRef theKext = (OSKextRef)object; 1577 QueryContext * context = (QueryContext *)user_data; 1578 CFStringRef command = CFDictionaryGetValue(element, CFSTR(kKeywordCommand)); 1579 CFArrayRef arguments = NULL; 1580 CFStringRef arg = NULL; 1581 char * string = NULL; // must free 1582 1583 if (CFEqual(command, CFSTR(kPredNameEcho))) { 1584 1585 1586 arguments = QEQueryElementGetArguments(element); 1587 arg = CFArrayGetValueAtIndex(arguments, 0); 1588 string = createUTF8CStringForCFString(arg); 1589 printf("%s", string); 1590 if (!CFDictionaryGetValue(element, CFSTR(kPredOptNameNoNewline))) { 1591 printf("%c", terminatorForElement(element)); 1592 } 1593 goto finish; 1594 } else if (CFEqual(command, CFSTR(kPredNamePrint))) { 1595 printKext(theKext, context->pathSpec, context->extraInfo, 1596 terminatorForElement(element)); 1597 } else if (CFEqual(command, CFSTR(kPredNameBundleName))) { 1598 printKext(theKext, kPathsNone, context->extraInfo, 1599 terminatorForElement(element)); 1600 } else if (CFEqual(command, CFSTR(kPredNamePrintDiagnostics))) { 1601 g_log_stream = stdout; 1602 OSKextLogDiagnostics(theKext, kOSKextDiagnosticsFlagAll); 1603 g_log_stream = stderr; 1604 } else if (CFEqual(command, CFSTR(kPredNamePrintProperty))) { 1605 arguments = QEQueryElementGetArguments(element); 1606 arg = CFArrayGetValueAtIndex(arguments, 0); 1607 printKextProperty(theKext, arg, terminatorForElement(element)); 1608 } else if (CFEqual(command, CFSTR(kPredNamePrintMatchProperty))) { 1609 arguments = QEQueryElementGetArguments(element); 1610 arg = CFArrayGetValueAtIndex(arguments, 1); 1611 printKextMatchProperty(theKext, arg, terminatorForElement(element)); 1612 } else if (CFEqual(command, CFSTR(kPredNamePrintArches))) { 1613 printKextArches(theKext, terminatorForElement(element), true); 1614 } else if (CFEqual(command, CFSTR(kPredNamePrintDependencies))) { 1615 printKextDependencies(theKext, context->pathSpec, 1616 context->extraInfo, terminatorForElement(element)); 1617 } else if (CFEqual(command, CFSTR(kPredNamePrintDependents))) { 1618 printKextDependents(theKext, context->pathSpec, 1619 context->extraInfo, terminatorForElement(element)); 1620 } else if (CFEqual(command, CFSTR(kPredNamePrintPlugins))) { 1621 printKextPlugins(theKext, context->pathSpec, context->extraInfo, 1622 terminatorForElement(element)); 1623 } else if (CFEqual(command, CFSTR(kPredNamePrintIntegrity))) { 1624 /* Kext integrity is no longer used on SnowLeopard. 1625 */ 1626 printf("%s%c", "n/a", terminatorForElement(element)); 1627 } else if (CFEqual(command, CFSTR(kPredNamePrintInfoDictionary))) { 1628 printKextInfoDictionary(theKext, context->pathSpec, 1629 terminatorForElement(element)); 1630 } else if (CFEqual(command, CFSTR(kPredNamePrintExecutable))) { 1631 printKextExecutable(theKext, context->pathSpec, 1632 terminatorForElement(element)); 1633 } else { 1634 *error = kQEQueryErrorEvaluationCallbackFailed; 1635 goto finish; 1636 } 1637 1638finish: 1639 if (string) free(string); 1640 return true; 1641} 1642 1643/******************************************************************************* 1644* 1645*******************************************************************************/ 1646Boolean parseExec( 1647 CFMutableDictionaryRef element, 1648 int argc __unused, 1649 char * const argv[], 1650 uint32_t * num_used, 1651 void * user_data, 1652 QEQueryError * error) 1653{ 1654 Boolean result = false; 1655 QueryContext * context = (QueryContext *)user_data; 1656 uint32_t index = 1; // don't care about predicate 1657 CFStringRef arg = NULL; // must release 1658 1659 context->commandSpecified = true; 1660 1661 while (argv[index]) { 1662 if (!strcmp(argv[index], kExecTerminator)) { 1663 result = true; 1664 index++; 1665 goto finish; 1666 } 1667 SAFE_RELEASE_NULL(arg); 1668 arg = CFStringCreateWithCString(kCFAllocatorDefault, 1669 argv[index], kCFStringEncodingUTF8); 1670 1671 if (!arg) { 1672 *error = kQEQueryErrorNoMemory; 1673 goto finish; 1674 } 1675 QEQueryElementAppendArgument(element, arg); 1676 index++; 1677 } 1678 1679 OSKextLog(/* kext */ NULL, 1680 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1681 "No terminating ; for %s.", kPredNameExec); 1682 *error = kQEQueryErrorInvalidOrMissingArgument; 1683finish: 1684 SAFE_RELEASE(arg); 1685 *num_used += index; 1686 return result; 1687} 1688 1689/******************************************************************************* 1690* 1691*******************************************************************************/ 1692Boolean evalExec( 1693 CFDictionaryRef element, 1694 void * object, 1695 void * user_data __unused, 1696 QEQueryError * error) 1697{ 1698 Boolean result = false; 1699 pid_t pid; 1700 int status; 1701 OSKextRef theKext = (OSKextRef)object; 1702 CFURLRef kextURL = NULL; // do not release 1703 CFStringRef kextPath = NULL; // must release 1704 CFBundleRef kextBundle = NULL; // must release 1705 CFURLRef infoDictURL = NULL; // must release 1706 CFStringRef infoDictPath = NULL; // must release 1707 CFURLRef executableURL = NULL; // must release 1708 CFStringRef executablePath = NULL; // must release 1709 char ** command_argv = NULL; // must free each, and whole 1710 CFArrayRef arguments = QEQueryElementGetArguments(element); 1711 CFMutableStringRef scratch = NULL; // must release 1712 char kextPathBuffer[PATH_MAX]; 1713 CFIndex count, i; 1714 1715 *error = kQEQueryErrorEvaluationCallbackFailed; 1716 1717 if (!arguments) { 1718 goto finish; 1719 } 1720 1721 count = CFArrayGetCount(arguments); 1722 1723 kextURL = OSKextGetURL(theKext); 1724 if (!kextURL) { 1725 OSKextLog(theKext, 1726 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1727 "Kext has no URL!"); 1728 *error = kQEQueryErrorUnspecified; 1729 goto finish; 1730 } 1731 if (!CFURLGetFileSystemRepresentation(kextURL, /* resolveToBase */ false, 1732 (UInt8 *)kextPathBuffer, sizeof(kextPathBuffer))) { 1733 1734 OSKextLogStringError(theKext); 1735 *error = kQEQueryErrorUnspecified; 1736 goto finish; 1737 } 1738 kextPath = CFURLCopyFileSystemPath(kextURL, kCFURLPOSIXPathStyle); 1739 if (!kextPath) { 1740 OSKextLogMemError(); 1741 *error = kQEQueryErrorNoMemory; 1742 goto finish; 1743 } 1744 kextBundle = CFBundleCreate(kCFAllocatorDefault, kextURL); 1745 if (!kextBundle) { 1746 OSKextLog(/* kext */ NULL, 1747 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 1748 "Can't create bundle for %s.", kextPathBuffer); 1749 *error = kQEQueryErrorNoMemory; 1750 goto finish; 1751 } 1752 1753 infoDictURL = _CFBundleCopyInfoPlistURL(kextBundle); 1754 if (infoDictURL) { 1755 infoDictPath = CFURLCopyFileSystemPath(infoDictURL, kCFURLPOSIXPathStyle); 1756 } 1757 executableURL = CFBundleCopyExecutableURL(kextBundle); 1758 if (executableURL) { 1759 executablePath = CFURLCopyFileSystemPath(executableURL, kCFURLPOSIXPathStyle); 1760 } 1761 1762 /* Not having these two is an error. The executable may not exist. 1763 */ 1764 if (!kextPath || !infoDictPath) { 1765 goto finish; 1766 } 1767 1768 command_argv = (char **)malloc((1 + count) * sizeof(char *)); 1769 if (!command_argv) { 1770 goto finish; 1771 } 1772 bzero(&command_argv, sizeof((1 + count) * sizeof(char *))); 1773 for (i = 0; i < count; i++) { 1774 scratch = CFStringCreateMutableCopy(kCFAllocatorDefault, 1775 0, CFArrayGetValueAtIndex(arguments, i)); 1776 if (!scratch) { 1777 goto finish; 1778 } 1779 1780 CFStringFindAndReplace(scratch, CFSTR(kExecInfoDictionaryReplace), 1781 infoDictPath, CFRangeMake(0, CFStringGetLength(scratch)), 0); 1782 1783 if (executablePath) { 1784 CFStringFindAndReplace(scratch, CFSTR(kExecExecutableReplace), 1785 executablePath, CFRangeMake(0, CFStringGetLength(scratch)), 0); 1786 } 1787 1788 CFStringFindAndReplace(scratch, CFSTR(kExecBundlePathReplace), 1789 kextPath, CFRangeMake(0, CFStringGetLength(scratch)), 0); 1790 1791 1792 command_argv[i] = createUTF8CStringForCFString(scratch); 1793 1794 if (!command_argv[i]) { 1795 goto finish; 1796 } 1797 CFRelease(scratch); 1798 scratch = NULL; 1799 } 1800 1801 command_argv[i] = NULL; 1802 1803 pid = fork(); 1804 switch (pid) { 1805 case 0: // child 1806 execvp(command_argv[0], command_argv); 1807 break; 1808 case -1: // error 1809 perror("error forking for -exec"); 1810 goto finish; 1811 break; 1812 default: // parent 1813 waitpid(-1, &status, 0); 1814 if (WIFEXITED(status)) { 1815 // Zero exit status is true 1816 result = WEXITSTATUS(status) ? false : true; 1817 } 1818 1819 break; 1820 } 1821 1822 *error = kQEQueryErrorNone; 1823 1824finish: 1825 SAFE_RELEASE(scratch); 1826 SAFE_RELEASE(kextPath); 1827 SAFE_RELEASE(kextBundle); 1828 SAFE_RELEASE(infoDictURL); 1829 SAFE_RELEASE(infoDictPath); 1830 SAFE_RELEASE(executableURL); 1831 SAFE_RELEASE(executablePath); 1832 1833 if (command_argv) { 1834 char ** arg = command_argv; 1835 while (*arg) { 1836 free(*arg); 1837 arg++; 1838 } 1839 free(command_argv); 1840 } 1841 1842 return result; 1843} 1844 1845/******************************************************************************* 1846* 1847*******************************************************************************/ 1848static int last_optind; 1849 1850void reset_getopt(void) 1851{ 1852 optreset = 1; 1853 opterr = 0; 1854 optind = 1; 1855 last_optind = optind; 1856 1857 return; 1858} 1859 1860/******************************************************************************* 1861* 1862*******************************************************************************/ 1863Boolean 1864parseStringElementOptions( 1865 CFMutableDictionaryRef element, 1866 int argc, 1867 char * const argv[], 1868 uint32_t * index, 1869 QueryContext * context, 1870 QEQueryError * error) 1871{ 1872 Boolean result = true; // have to assume success until we hit a bad one 1873 int opt_char; 1874 1875 reset_getopt(); 1876 while ((opt_char = getopt_long_only(argc, argv, PROPERTY_OPTS, 1877 property_opt_info, NULL)) != -1) { 1878 1879 switch (opt_char) { 1880 1881 case kPredOptCaseInsensitive: 1882 CFDictionarySetValue(element, CFSTR(kSearchStyleCaseInsensitive), 1883 kCFBooleanTrue); 1884 last_optind = optind; // save optind for a subsequent mismatch 1885 break; 1886 1887 case kPredOptSubstring: 1888 CFDictionarySetValue(element, CFSTR(kSearchStyleSubstring), 1889 kCFBooleanTrue); 1890 last_optind = optind; // save optind for a subsequent mismatch 1891 break; 1892 1893 case 0: 1894 /* Fall through. */ 1895 default: 1896 result = handleNonOption(opt_char, argv, context, error); 1897 goto finish; 1898 break; 1899 } 1900 } 1901 1902finish: 1903 if (index) { 1904 *index = optind; // we set rather than adding cause of getopt 1905 } 1906 return result; 1907} 1908 1909/******************************************************************************* 1910* 1911*******************************************************************************/ 1912Boolean parsePrintOptions( 1913 CFMutableDictionaryRef element, 1914 int argc, 1915 char * const argv[], 1916 uint32_t * index, 1917 QueryContext * context, 1918 QEQueryError * error) 1919{ 1920 Boolean result = true; // have to assume success until we hit a bad one 1921 int opt_char; 1922 1923 reset_getopt(); 1924 while ((opt_char = getopt_long_only(argc, argv, PRINT_OPTS, 1925 print_opt_info, NULL)) != -1) { 1926 1927 switch (opt_char) { 1928 1929 case kOptNulTerminate: 1930 CFDictionarySetValue(element, CFSTR(kOptNameNulTerminate), 1931 kCFBooleanTrue); 1932 last_optind = optind; 1933 break; 1934 1935 case 0: 1936 /* -print takes no arguments so if we see a query predicate we 1937 * are good to go. 1938 */ 1939 if (longopt == kLongOptQueryPredicate) { 1940 optind = last_optind; 1941 goto finish; 1942 } 1943 default: 1944 result = handleNonOption(opt_char, argv, context, error); 1945 goto finish; 1946 break; 1947 } 1948 } 1949finish: 1950 if (index) { 1951 *index = optind; // we set rather than adding cause of getopt 1952 } 1953 return result; 1954} 1955 1956/******************************************************************************* 1957* 1958*******************************************************************************/ 1959Boolean 1960parseEchoOptions( 1961 CFMutableDictionaryRef element, 1962 int argc, 1963 char * const argv[], 1964 uint32_t * index, 1965 QueryContext * context, 1966 QEQueryError * error) 1967{ 1968 Boolean result = true; // have to assume success until we hit a bad one 1969 int opt_char; 1970 1971 reset_getopt(); 1972 while ((opt_char = getopt_long_only(argc, argv, ECHO_OPTS, 1973 echo_opt_info, NULL)) != -1) { 1974 1975 switch (opt_char) { 1976 1977 case kPredOptNoNewline: 1978 CFDictionarySetValue(element, CFSTR(kPredOptNameNoNewline), 1979 kCFBooleanTrue); 1980 last_optind = optind; // save optind for a subsequent mismatch 1981 break; 1982 1983 case kOptNulTerminate: 1984 CFDictionarySetValue(element, CFSTR(kOptNameNulTerminate), 1985 kCFBooleanTrue); 1986 last_optind = optind; 1987 break; 1988 1989 case 0: 1990 /* Fall through. */ 1991 default: 1992 result = handleNonOption(opt_char, argv, context, error); 1993 goto finish; 1994 break; 1995 } 1996 } 1997finish: 1998 if (index) { 1999 *index = optind; // we set rather than adding cause of getopt 2000 } 2001 return result; 2002} 2003 2004/******************************************************************************* 2005* 2006*******************************************************************************/ 2007#define QUIBBLE_PRED "%s looks like a predicate, " \ 2008 "but is being used as a property flag argument." 2009#define QUIBBLE_OPT "%s looks like an (illegal) option, " \ 2010 "but is being used as a property flag argument." 2011#define PICKY_PRED "%s is a predicate keyword (use -- to end options " \ 2012 "before arguments starting with a hyphen)." 2013#define PICKY_OPT "Invalid option %s for %s (use -- to end options " \ 2014 "before arguments starting with a hyphen)." 2015 2016Boolean 2017handleNonOption(int opt_char, 2018 char * const argv[], 2019 QueryContext * context, 2020 QEQueryError * error) 2021{ 2022 Boolean result = false; 2023 2024 /* getopt_long's lookahead is SO NOT THE RIGHT BEHAVIOR. 2025 * Nor is its handling of empty arguments. 2026 */ 2027 if ( (argv[last_optind][0] != '-') || !argv[last_optind][0]) { 2028 optind = last_optind; 2029 result = true; 2030 goto finish; 2031 } 2032 2033 if (opt_char) { 2034 if (context->assertiveness == kKextfindQuibbling) { 2035 OSKextLog(/* kext */ NULL, 2036 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 2037 QUIBBLE_OPT, argv[last_optind]); 2038 optind = last_optind; 2039 } else if (context->assertiveness == kKextfindPicky) { 2040 OSKextLog(/* kext */ NULL, 2041 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 2042 PICKY_OPT, argv[last_optind], argv[0]); 2043 *error = kQEQueryErrorInvalidOrMissingArgument; 2044 goto finish; 2045 } 2046 optind = last_optind; 2047 } else { 2048 switch (longopt) { 2049 case kLongOptQueryPredicate: 2050 if (context->assertiveness == kKextfindQuibbling) { 2051 OSKextLog(/* kext */ NULL, 2052 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 2053 QUIBBLE_PRED, argv[last_optind]); 2054 optind = last_optind; 2055 } else if (context->assertiveness == kKextfindPicky) { 2056 OSKextLog(/* kext */ NULL, 2057 kOSKextLogErrorLevel | kOSKextLogGeneralFlag, 2058 PICKY_PRED, argv[last_optind]); 2059 *error = kQEQueryErrorInvalidOrMissingArgument; 2060 goto finish; 2061 } 2062 optind = last_optind; 2063 break; 2064 2065 default: // should never see this 2066 *error = kQEQueryErrorParseCallbackFailed; 2067 optind = last_optind; 2068 goto finish; 2069 break; 2070 } 2071 } 2072 result = true; 2073finish: 2074 return result; 2075} 2076