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