/ SecurityTool / sharedTool / ca_revocation_additions.m
ca_revocation_additions.m
  1  /*
  2   * Copyright (c) 2020 Apple Inc. All Rights Reserved.
  3   *
  4   * @APPLE_LICENSE_HEADER_START@
  5   *
  6   * This file contains Original Code and/or Modifications of Original Code
  7   * as defined in and that are subject to the Apple Public Source License
  8   * Version 2.0 (the 'License'). You may not use this file except in
  9   * compliance with the License. Please obtain a copy of the License at
 10   * http://www.opensource.apple.com/apsl/ and read it before using this
 11   * file.
 12   *
 13   * The Original Code and all software distributed under the License are
 14   * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 15   * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 16   * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 17   * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 18   * Please see the License for the specific language governing rights and
 19   * limitations under the License.
 20   *
 21   * @APPLE_LICENSE_HEADER_END@
 22   *
 23   * ca_revocation_additions.m
 24   */
 25  
 26  #import <Foundation/Foundation.h>
 27  #include <Security/SecTrustSettingsPriv.h>
 28  #include <Security/SecCertificatePriv.h>
 29  #include <utilities/fileIo.h>
 30  #include <utilities/SecCFWrappers.h>
 31  
 32  #include "SecurityCommands.h"
 33  
 34  NSString* secToolAppID = @"com.apple.security";
 35  
 36  static int addCertFile(const char *fileName, NSMutableArray *array) {
 37      SecCertificateRef certRef = NULL;
 38      NSData *data = NULL;
 39      unsigned char *buf = NULL;
 40      size_t numBytes;
 41      int rtn = 0;
 42  
 43      if (readFileSizet(fileName, &buf, &numBytes)) {
 44          rtn = -1;
 45          goto errOut;
 46      }
 47  
 48      data = [NSData dataWithBytes:buf length:numBytes];
 49      certRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)data);
 50      if (!certRef) {
 51          certRef = SecCertificateCreateWithPEM(NULL, (__bridge CFDataRef)data);
 52          if (!certRef) {
 53              rtn = -1;
 54              goto errOut;
 55          }
 56      }
 57  
 58      [array addObject:(__bridge id)certRef];
 59  
 60  errOut:
 61      /* Cleanup */
 62      free(buf);
 63      CFReleaseNull(certRef);
 64      return rtn;
 65  }
 66  
 67  static int returnCFError(CFErrorRef CF_CONSUMED error) {
 68      CFStringRef errorString = CFErrorCopyDescription(error);
 69      CFStringPerformWithCString(errorString, ^(const char *utf8Str) {
 70          fprintf(stderr, "Failed to copy CA revocation additions: %s\n", utf8Str);
 71      });
 72      CFIndex errCode = CFErrorGetCode(error);
 73      CFReleaseNull(error);
 74      return (int)errCode;
 75  }
 76  
 77  static int resetRevocationAdditions(bool resetCerts) {
 78      bool result = false;
 79      CFErrorRef error = NULL;
 80      if (resetCerts) {
 81          NSDictionary *resetCertsDict = @{ (__bridge NSString*)kSecCARevocationAdditionsKey: @[] };
 82          result = SecTrustStoreSetCARevocationAdditions(NULL, (__bridge CFDictionaryRef)resetCertsDict, &error);
 83      }
 84      if (!result) {
 85          return returnCFError(error);
 86      }
 87      return 0;
 88  }
 89  
 90  static int addRevocationAdditions(CFStringRef key, NSArray *newAdditions) {
 91      CFErrorRef error = NULL;
 92      NSDictionary *currentAdditions = CFBridgingRelease(SecTrustStoreCopyCARevocationAdditions((__bridge CFStringRef)secToolAppID, &error));
 93      if (!currentAdditions && error) {
 94          return returnCFError(error);
 95      }
 96  
 97      NSMutableArray *additionsForKey = nil;
 98      if (currentAdditions && currentAdditions[(__bridge NSString*)key]) {
 99          additionsForKey = [currentAdditions[(__bridge NSString*)key] mutableCopy];
100          [additionsForKey addObjectsFromArray:newAdditions];
101      } else {
102          additionsForKey = [newAdditions copy];
103      }
104  
105      NSDictionary *newAdditionsDict = @{ (__bridge NSString*)key: additionsForKey };
106      bool result = SecTrustStoreSetCARevocationAdditions(NULL, (__bridge CFDictionaryRef)newAdditionsDict, &error);
107      if (!result) {
108          return returnCFError(error);
109      }
110  
111      return 0;
112  }
113  
114  int add_ca_revocation_checking(int argc, char * const *argv) {
115      int arg;
116  
117      bool resetCerts = false;
118  
119      NSMutableArray *certs = [NSMutableArray array];
120      NSDictionary *plist = nil;
121  
122      /* parse args */
123      if (argc == 1) {
124          return SHOW_USAGE_MESSAGE;
125      }
126  
127      while ((arg = getopt(argc, argv, "c:r:p:")) != -1) {
128          switch(arg) {
129              case 'c':
130                  if (addCertFile(optarg, certs)) {
131                      fprintf(stderr, "Failed to read cert file\n");
132                      return 1;
133                  }
134                  break;
135              case 'r':
136                  if (!strcmp(optarg, "all")) {
137                      resetCerts = true;
138                  } else if (!strcmp(optarg, "cert")) {
139                      resetCerts = true;
140                  } else {
141                      return SHOW_USAGE_MESSAGE;
142                  }
143                  break;
144              case 'p':
145                  plist = [NSDictionary dictionaryWithContentsOfFile:[NSString stringWithCString:optarg encoding:NSUTF8StringEncoding]];
146                  break;
147              case '?':
148              default:
149                  return SHOW_USAGE_MESSAGE;
150          }
151      }
152  
153      /* handle reset operation */
154      if (resetCerts) {
155          return resetRevocationAdditions(resetCerts);
156      }
157  
158      /* set plist */
159      if (plist) {
160          CFErrorRef error = NULL;
161          bool result = SecTrustStoreSetCARevocationAdditions(NULL, (__bridge CFDictionaryRef)plist, &error);
162          if (!result) {
163              return returnCFError(error);
164          } else {
165              return 0;
166          }
167      }
168  
169      /* add certs */
170      int status = 0;
171      if ([certs count]) {
172          NSMutableArray<NSDictionary *>*valuesForCAsKey = [NSMutableArray arrayWithCapacity:[certs count]];
173          [certs enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
174              SecCertificateRef cert = (__bridge SecCertificateRef)obj;
175              NSData* hash = CFBridgingRelease(SecCertificateCopySubjectPublicKeyInfoSHA256Digest(cert));
176              NSDictionary *value = @{ (__bridge NSString*)kSecCARevocationHashAlgorithmKey:@"sha256",
177                                       (__bridge NSString*)kSecCARevocationSPKIHashKey:hash };
178              [valuesForCAsKey addObject:value];
179          }];
180          status = addRevocationAdditions(kSecCARevocationAdditionsKey, valuesForCAsKey);
181      }
182      if (status != 0) {
183          fprintf(stderr, "failed to add cert revocation additions\n");
184          return status;
185      }
186  
187      return 0;
188  }
189  
190  static int printRevocationAdditions(CFStringRef key, NSDictionary *allAdditions) {
191      if (!allAdditions || !allAdditions[(__bridge NSString*)key] ||
192          [allAdditions[(__bridge NSString*)key] count] == 0) {
193          CFStringPerformWithCString(key, ^(const char *utf8Str) {
194              fprintf(stdout, "No revocation additions for %s\n", utf8Str);
195          });
196          return 0;
197      }
198  
199      NSArray *additionsForKey = allAdditions[(__bridge NSString*)key];
200      NSMutableString *additionsString = [NSMutableString stringWithFormat:@"\t%@ : [",key];
201      [additionsForKey enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
202          if ([obj isKindOfClass:[NSString class]]) {
203              if (idx == 0) {
204                  [additionsString appendFormat:@"\"%@\"",obj];
205              } else {
206                  [additionsString appendFormat:@", \"%@\"", obj];
207              }
208          } else if ([obj isKindOfClass:[NSDictionary class]]) {
209              if (idx == 0) {
210                  [additionsString appendString:@"\n\t    "];
211              } else {
212                  [additionsString appendString:@"\t    "];
213              }
214              [additionsString appendFormat:@"\"%@:", obj[(__bridge NSString*)kSecCARevocationHashAlgorithmKey]];
215              NSString *hashHex = CFBridgingRelease(CFDataCopyHexString((__bridge CFDataRef)obj[(__bridge NSString*)kSecCARevocationSPKIHashKey]));
216              [additionsString appendString:hashHex];
217              if ([additionsForKey count] == idx + 1) { // last entry
218                  [additionsString appendString:@"\"\n"];
219              } else {
220                  [additionsString appendString:@"\",\n"];
221              }
222          }
223      }];
224      [additionsString appendString:@"]\n"];
225      CFStringPerformWithCString((__bridge CFStringRef)additionsString, ^(const char *utf8Str) {
226          fprintf(stdout, "\n%s\n", utf8Str);
227      });
228  
229      return 0;
230  }
231  
232  int show_ca_revocation_checking(int argc, char * const *argv) {
233      int arg;
234      bool allAdditions = false;
235      NSString *identifier = nil;
236      bool certAdditions = false;
237  
238      /* parse args */
239      while ((arg = getopt(argc, argv, "ai:c")) != -1) {
240          switch(arg) {
241              case 'a':
242                  allAdditions = true;
243                  break;
244              case 'i':
245                  identifier = [NSString stringWithCString:optarg encoding:NSUTF8StringEncoding];
246                  break;
247              case 'c':
248                  certAdditions = true;
249                  break;
250              case '?':
251              default:
252                  return SHOW_USAGE_MESSAGE;
253          }
254      }
255  
256      if (allAdditions) {
257          identifier = nil;
258          fprintf(stdout, "Showing revocation additions for all apps\n");
259      } else if (!identifier) {
260          identifier = secToolAppID;
261      }
262  
263      if (identifier) {
264          CFStringPerformWithCString((__bridge CFStringRef)identifier, ^(const char *utf8Str) {
265              fprintf(stdout, "Showing revocation additions for %s\n", utf8Str);
266          });
267      }
268  
269      CFErrorRef error = NULL;
270      NSDictionary *results = CFBridgingRelease(SecTrustStoreCopyCARevocationAdditions((__bridge CFStringRef)identifier, &error));
271  
272      /* Copy failed, return error */
273      if (!results && error) {
274          return returnCFError(error);
275      }
276  
277      /* print cert revocation additions */
278      int status = 0;
279      if (certAdditions) {
280          status = printRevocationAdditions(kSecCARevocationAdditionsKey, results);
281      }
282      if (status != 0) {
283          fprintf(stderr, "failed to print revocation additions\n");
284          return status;
285      }
286  
287  
288      return 0;
289  }