1/*
2 * Copyright (c) 2009 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_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. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <stdio.h>
30
31#include <CoreFoundation/CoreFoundation.h>
32
33#include <kxld.h>
34
35#define kCFBundleGetInfoStringKey CFSTR("CFBundleGetInfoString")
36#define kNSHumanReadableCopyrightKey CFSTR("NSHumanReadableCopyright")
37
38const char *gProgname = NULL;
39
40static void usage(void);
41static void printFormat(void);
42static char *convert_cfstring(CFStringRef the_string);
43
44/******************************************************************************
45******************************************************************************/
46static void
47usage(void)
48{
49    printf("usage: %s [path to kext]\n\n"
50           "This program validates the copyright string in a kext's info "
51           "dictionary.\n\n", gProgname);
52
53    printFormat();
54}
55
56/******************************************************************************
57******************************************************************************/
58static void
59printFormat(void)
60{
61    fprintf(stderr,
62        "The copyright string should be contained in the NSHumanReadableCopyright key.\n"
63        "It should be of the format:\n"
64        "\tCopyright © [year(s) of publication] Apple Inc. All rights reserved.\n\n"
65        "where [year(s) of publication] is a comma-separated list of years and/or\n"
66        "year ranges, e.g., 2004, 2006-2008.  Years must be four digits.  Year ranges\n"
67        "may not contain spaces and must use four digits for both years.\n\n"
68        "The following are examples of valid copyright strings:\n"
69        "\tCopyright © 2008 Apple Inc. All rights reserved.\n"
70        "\tCopyright © 2004-2008 Apple Inc. All rights reserved.\n"
71        "\tCopyright © 1998,2000-2002,2004,2006-2008 Apple Inc. All rights reserved.\n");
72}
73
74/******************************************************************************
75******************************************************************************/
76char *
77convert_cfstring(CFStringRef the_string)
78{
79    char *result = NULL;
80    CFDataRef the_data = NULL;
81    const UInt8 *data_bytes = NULL;
82    char *converted_string = NULL;
83    u_long converted_len = 0;
84    u_long bytes_copied = 0;
85
86    the_data = CFStringCreateExternalRepresentation(kCFAllocatorDefault,
87        the_string, kCFStringEncodingUTF8, 0);
88    if (!the_data) {
89        fprintf(stderr, "Failed to convert string\n");
90        goto finish;
91    }
92
93    data_bytes = CFDataGetBytePtr(the_data);
94    if (!data_bytes) {
95        fprintf(stderr, "Failed to get converted string bytes\n");
96        goto finish;
97    }
98
99    converted_len = strlen((const char *)data_bytes) + 1; // +1 for nul
100    converted_string = malloc(converted_len);
101    if (!converted_string) {
102        fprintf(stderr, "Failed to allocate memory\n");
103        goto finish;
104    }
105
106    bytes_copied = strlcpy(converted_string, (const char *) data_bytes,
107        converted_len) + 1; // +1 for nul
108    if (bytes_copied != converted_len) {
109        fprintf(stderr, "Failed to copy converted string\n");
110        goto finish;
111    }
112
113    result = converted_string;
114finish:
115    CFRelease(the_data);
116    return result;
117}
118
119/******************************************************************************
120******************************************************************************/
121int
122main(int argc, const char *argv[])
123{
124    int result = 1;
125    boolean_t infoCopyrightIsValid = false;
126    boolean_t readableCopyrightIsValid = false;
127    CFURLRef anURL = NULL;                      // must release
128    CFBundleRef aBundle = NULL;                 // must release
129    CFDictionaryRef aDict = NULL;               // do not release
130    CFStringRef infoCopyrightString = NULL;     // do not release
131    CFStringRef readableCopyrightString = NULL; // do not release
132    char *infoStr = NULL;                       // must free
133    char *readableStr = NULL;                   // must free
134
135    gProgname = argv[0];
136
137    if (argc != 2) {
138        usage();
139        goto finish;
140    }
141
142    anURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
143        (const UInt8 *) argv[1], strlen(argv[1]), /* isDirectory */ FALSE);
144    if (!anURL) {
145        fprintf(stderr, "Can't create path from %s\n", argv[1]);
146        goto finish;
147    }
148
149    aBundle = CFBundleCreate(kCFAllocatorDefault, anURL);
150    if (!aBundle) {
151        fprintf(stderr, "Can't create bundle at path %s\n", argv[1]);
152        goto finish;
153    }
154
155    aDict = CFBundleGetInfoDictionary(aBundle);
156    if (!aDict) {
157        fprintf(stderr, "Can't get info dictionary from bundle\n");
158        goto finish;
159    }
160
161    infoCopyrightString = CFDictionaryGetValue(aDict, kCFBundleGetInfoStringKey);
162    readableCopyrightString = CFDictionaryGetValue(aDict, kNSHumanReadableCopyrightKey);
163
164    if (!infoCopyrightString && !readableCopyrightString) {
165        fprintf(stderr, "This kext does not have a value for NSHumanReadableCopyright");
166        goto finish;
167    }
168
169    if (infoCopyrightString) {
170        fprintf(stderr, "Warning: This kext has a value for CFBundleGetInfoString.\n"
171            "This key is obsolete, and may be removed from the kext's Info.plist.\n"
172            "It has been replaced by CFBundleVersion and NSHumanReadableCopyright.\n\n");
173
174        infoStr = convert_cfstring(infoCopyrightString);
175        if (!infoStr) goto finish;
176
177        infoCopyrightIsValid = kxld_validate_copyright_string(infoStr);
178    }
179
180    if (readableCopyrightString) {
181        readableStr = convert_cfstring(readableCopyrightString);
182        if (!readableStr) goto finish;
183
184        readableCopyrightIsValid = kxld_validate_copyright_string(readableStr);
185    }
186
187    if (!readableCopyrightIsValid) {
188        if (infoCopyrightIsValid) {
189            fprintf(stderr, "Warning: The copyright string in NSHumanReadableCopyright is invalid,\n"
190                    "but the string in CFBundleGetInfoString is valid.  CFBundleGetInfoString is\n"
191                    "obsolete.  Please migrate your copyright string to NSHumanReadableCopyright.\n\n");
192        } else {
193            fprintf(stderr, "Error: There is no valid copyright string for this kext.\n\n");
194            printFormat();
195            goto finish;
196        }
197    }
198
199    result = 0;
200finish:
201    if (anURL) CFRelease(anURL);
202    if (aBundle) CFRelease(aBundle);
203    if (infoStr) free(infoStr);
204    if (readableStr) free(readableStr);
205
206    return result;
207}
208
209