/ SecurityTool / sharedTool / keychain_util.c
keychain_util.c
  1  /*
  2   * Copyright (c) 2014 Apple Inc. All Rights Reserved.
  3   *
  4   * @APPLE_LICENSE_HEADER_START@
  5   * 
  6   * This file contains Original Code and/or Modifications of Original Code
  7   * as defined in and that are subject to the Apple Public Source License
  8   * Version 2.0 (the 'License'). You may not use this file except in
  9   * compliance with the License. Please obtain a copy of the License at
 10   * http://www.opensource.apple.com/apsl/ and read it before using this
 11   * file.
 12   * 
 13   * The Original Code and all software distributed under the License are
 14   * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 15   * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 16   * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 17   * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 18   * Please see the License for the specific language governing rights and
 19   * limitations under the License.
 20   * 
 21   * @APPLE_LICENSE_HEADER_END@
 22   */
 23  
 24  
 25  #include <stdio.h>
 26  #include <AssertMacros.h>
 27  #include <utilities/SecCFWrappers.h>
 28  #include <ACMDefs.h>
 29  #include <ACMAclDefs.h>
 30  #include <libaks_acl_cf_keys.h>
 31  
 32  #include "security.h"
 33  #include "keychain_util.h"
 34  #include <Security/SecAccessControlPriv.h>
 35  
 36  static CFStringRef createStringForValue(CFTypeRef value)
 37  {
 38      CFStringRef result = NULL;
 39      if (CFDataGetTypeID() == CFGetTypeID(value)) {
 40          CFMutableStringRef stringData = CFStringCreateMutable(kCFAllocatorDefault, 0);
 41          CFStringAppend(stringData, CFSTR("<"));
 42          const UInt8 *dataPtr = CFDataGetBytePtr(value);
 43          for (int i = 0; i < CFDataGetLength(value); ++i) {
 44              CFStringAppendFormat(stringData, NULL, CFSTR("%02x"), dataPtr[i]);
 45          }
 46          CFStringAppend(stringData, CFSTR(">"));
 47          result = stringData;
 48      } else if (CFBooleanGetTypeID() == CFGetTypeID(value)) {
 49          if (CFEqual(value, kCFBooleanTrue))
 50              result = CFStringCreateWithCString(kCFAllocatorDefault, "true", kCFStringEncodingUTF8);
 51          else
 52              result = CFStringCreateWithCString(kCFAllocatorDefault, "false", kCFStringEncodingUTF8);
 53      } else if (CFNumberGetTypeID() == CFGetTypeID(value))
 54          result = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, CFSTR("%@"), value);
 55      else if (CFStringGetTypeID() == CFGetTypeID(value))
 56          result = CFStringCreateCopy(kCFAllocatorDefault, value);
 57      else
 58          result = CFStringCreateWithCString(kCFAllocatorDefault, "unrecognized value", kCFStringEncodingUTF8);
 59  
 60      return result;
 61  }
 62  
 63  static CFStringRef createStringForKofn(CFDictionaryRef kofn)
 64  {
 65      CFMutableStringRef result = NULL;
 66      if (kofn != NULL && CFDictionaryGetTypeID() == CFGetTypeID(kofn))
 67      {
 68          result = CFStringCreateMutable(kCFAllocatorDefault, 0);
 69          CFDictionaryForEach(kofn, ^(const void *key, const void *value) {
 70  
 71              CFStringAppend(result, key);
 72              CFStringAppend(result, CFSTR("("));
 73  
 74              CFStringRef valueString = createStringForKofn(value) ?: createStringForValue(value);
 75              CFStringAppend(result, valueString);
 76              CFStringAppend(result, CFSTR(")"));
 77              CFReleaseSafe(valueString);
 78          });
 79      }
 80  
 81      return result;
 82  }
 83  
 84  static CFStringRef createStringForOps(CFDictionaryRef constraints)
 85  {
 86      CFMutableStringRef result = NULL;
 87      if (constraints != NULL)
 88      {
 89          result = CFStringCreateMutable(kCFAllocatorDefault, 0);
 90          CFDictionaryForEach(constraints, ^(const void *key, const void *value) {
 91              if (CFStringGetLength(result) > 0)
 92                  CFStringAppend(result, CFSTR(";"));
 93              CFStringAppend(result, key);
 94              CFStringAppend(result, CFSTR("("));
 95              CFStringRef valueString = createStringForKofn(value) ?: createStringForValue(value);
 96              CFStringAppend(result, valueString);
 97              CFStringAppend(result, CFSTR(")"));
 98              CFReleaseSafe(valueString);
 99          });
100      }
101  
102      return result;
103  }
104  
105  void
106  display_sac_line(SecAccessControlRef sac, CFMutableStringRef line)
107  {
108      CFTypeRef protection = SecAccessControlGetProtection(sac);
109      if (CFDictionaryGetTypeID() == CFGetTypeID(protection)) {
110          CFStringRef protectionStr = createStringForOps(protection);
111          CFStringAppend(line, protectionStr);
112          CFRelease(protectionStr);
113      } else if (CFStringGetTypeID() == CFGetTypeID(protection))
114          CFStringAppend(line, protection);
115      else
116          CFStringAppend(line, CFSTR("??"));
117  
118      CFDictionaryRef constraints = SecAccessControlGetConstraints(sac);
119      CFStringRef constraintsString = createStringForOps(constraints);
120      if (constraintsString) {
121          CFStringAppend(line, CFSTR(";"));
122          CFStringAppend(line, constraintsString);
123      }
124      CFReleaseSafe(constraintsString);
125  }
126  
127  bool
128  keychain_query_parse_cstring(CFMutableDictionaryRef q, const char *query) {
129      CFStringRef s;
130      s = CFStringCreateWithCStringNoCopy(0, query, kCFStringEncodingUTF8, kCFAllocatorNull);
131      bool result = keychain_query_parse_string(q, s);
132      CFRelease(s);
133      return result;
134  }
135  
136  /* Parse a string of the form attr=value,attr=value,attr=value */
137  bool
138  keychain_query_parse_string(CFMutableDictionaryRef q, CFStringRef s) {
139      bool inkey = true;
140      bool escaped = false;
141      bool error = false;
142      CFStringRef key = NULL;
143      CFMutableStringRef str = CFStringCreateMutable(0, 0);
144      CFRange rng = { .location = 0, .length = CFStringGetLength(s) };
145      CFCharacterSetRef cs_key = CFCharacterSetCreateWithCharactersInString(0, CFSTR("=\\"));
146      CFCharacterSetRef cs_value = CFCharacterSetCreateWithCharactersInString(0, CFSTR(",\\"));
147      while (rng.length) {
148          CFRange r;
149          CFStringRef sub;
150          bool complete = false;
151          if (escaped) {
152              r.location = rng.location;
153              r.length = 1;
154              sub = CFStringCreateWithSubstring(0, s, r);
155              escaped = false;
156          } else if (CFStringFindCharacterFromSet(s, inkey ? cs_key : cs_value, rng, 0, &r)) {
157              if (CFStringGetCharacterAtIndex(s, r.location) == '\\') {
158                  escaped = true;
159              } else {
160                  complete = true;
161              }
162              CFIndex next = r.location + 1;
163              r.length = r.location - rng.location;
164              r.location = rng.location;
165              sub = CFStringCreateWithSubstring(0, s, r);
166              rng.length -= next - rng.location;
167              rng.location = next;
168          } else {
169              sub = CFStringCreateWithSubstring(0, s, rng);
170              rng.location += rng.length;
171              rng.length = 0;
172              complete = true;
173          }
174          CFStringAppend(str, sub);
175          CFRelease(sub);
176          
177          if (complete) {
178              CFStringRef value = CFStringCreateCopy(0, str);
179              CFStringReplaceAll(str, CFSTR(""));
180              if (inkey) {
181                  key = value;
182              } else {
183                  if(key && CFStringCompare(key, kSecAttrAccessControl, kCFCompareCaseInsensitive) == kCFCompareEqualTo) {
184                      SecAccessControlRef sac = keychain_query_parse_sac(value);
185                      if(sac) {
186                          CFDictionarySetValue(q, key, sac);
187                      } else {
188                          fprintf(stderr, "failed to parse access control\n");
189                          error = true;
190                      }
191                  } else {
192                      CFDictionarySetValue(q, key, value);
193                  }
194                  CFReleaseNull(value);
195                  CFReleaseNull(key);
196              }
197              inkey = !inkey;
198          }
199          if(error)
200              break;
201      }
202      if (key) {
203          /* Dangeling key value is true?. */
204          CFDictionarySetValue(q, key, kCFBooleanTrue);
205          CFReleaseNull(key);
206      }
207      CFRelease(str);
208      CFRelease(cs_key);
209      CFRelease(cs_value);
210      return error == false;
211  }
212  
213  static uint32_t findLeft(CFStringRef s, uint32_t off)
214  {
215      for (int i = off; i < CFStringGetLength(s); ++i) {
216          if (CFStringGetCharacterAtIndex(s, i) == '(')
217              return i;
218      }
219  
220      return 0;
221  }
222  
223  static uint32_t findRight(CFStringRef s, uint32_t off)
224  {
225      int bracersCount = 0;
226      for (int i = off; i < CFStringGetLength(s); ++i) {
227          if (CFStringGetCharacterAtIndex(s, i) == '(')
228              ++bracersCount;
229  
230          if (CFStringGetCharacterAtIndex(s, i) == ')') {
231              --bracersCount;
232              if (bracersCount == 0) {
233                  return i;
234              }
235          }
236      }
237  
238      return 0;
239  }
240  
241  static bool parseKeyAndValue(CFStringRef string, CFTypeRef *key, CFTypeRef *value);
242  
243  CF_RETURNS_RETAINED
244  static CFTypeRef parseValue(CFStringRef string)
245  {
246      CFTypeRef result = NULL, key = NULL, value = NULL;
247      CFMutableDictionaryRef resultDictionary = NULL;
248      CFStringRef subString = NULL;
249  
250      uint32_t left = findLeft(string, 0);
251      if (left > 0) {
252          uint32_t offset = 0;
253          resultDictionary = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
254          for (;;) {
255              uint32_t right = findRight(string, left);
256              if (!right)
257                  break;
258              CFAssignRetained(subString, CFStringCreateWithSubstring(kCFAllocatorDefault, string, CFRangeMake(offset, (right + 1) - offset)));
259              require_quiet(parseKeyAndValue(subString, &key, &value), out);
260              CFDictionarySetValue(resultDictionary, key, value);
261              offset = right + 1;
262              left = findLeft(string, offset);
263              if (!left)
264                  break;
265          }
266          result = CFRetain(resultDictionary);
267      } else if (CFStringGetCharacterAtIndex(string, 0) == '<' && CFStringGetCharacterAtIndex(string, CFStringGetLength(string) - 1) == '>') {
268          CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, 0);
269          if (CFStringGetLength(string) > 2) {
270              CFAssignRetained(subString, CFStringCreateWithSubstring(kCFAllocatorDefault, string, CFRangeMake(1, CFStringGetLength(string) - 2)));
271              const char *asciiString = CFStringGetCStringPtr(subString, kCFStringEncodingASCII);
272              uint8_t byte;
273              for(uint32_t i = 0; i < strlen(asciiString); i += 2) {
274                  sscanf(&asciiString[i], "%02hhx", &byte);
275                  CFDataAppendBytes(data, &byte, sizeof(byte));
276              }
277          }
278          result = data;
279      } else if (CFStringCompare(string, CFSTR("true"), 0) == kCFCompareEqualTo) {
280          CFRetainAssign(result, kCFBooleanTrue);
281      } else if (CFStringCompare(string, CFSTR("false"), 0) == kCFCompareEqualTo) {
282          CFRetainAssign(result, kCFBooleanFalse);
283      } else if (CFStringCompare(string, CFSTR(kACMPolicyDeviceOwnerAuthentication), 0) == kCFCompareEqualTo) {
284          CFRetainAssign(result, CFSTR(kACMPolicyDeviceOwnerAuthentication));
285      } else {
286          CFLocaleRef currentLocale = CFLocaleCopyCurrent();
287          CFNumberFormatterRef formaterRef = CFNumberFormatterCreate(kCFAllocatorDefault, currentLocale, kCFNumberFormatterDecimalStyle);
288          result = CFNumberFormatterCreateNumberFromString(kCFAllocatorDefault, formaterRef, string, NULL, kCFNumberFormatterParseIntegersOnly);
289          CFReleaseSafe(currentLocale);
290          CFReleaseSafe(formaterRef);
291      }
292  
293      if (!result)
294          fprintf(stderr, "Failed to parse value: %s\n", CFStringGetCStringPtr(string, kCFStringEncodingUTF8));
295  
296  out:
297      CFReleaseSafe(key);
298      CFReleaseSafe(value);
299      CFReleaseSafe(subString);
300      CFReleaseSafe(resultDictionary);
301  
302      return result;
303  }
304  
305  static bool parseKeyAndValue(CFStringRef string, CFTypeRef *key, CFTypeRef *value)
306  {
307      bool ok = false;
308      CFStringRef keyString = NULL;
309      CFStringRef valueString = NULL;
310      CFTypeRef parsedValue = NULL;
311  
312      uint32_t left = findLeft(string, 0);
313      require_action_quiet(left != 0, out, fprintf(stderr, "Failed to find '(' in: %s\n", CFStringGetCStringPtr(string, kCFStringEncodingUTF8)));
314      uint32_t right = findRight(string, left);
315      require_action_quiet(right != 0, out, fprintf(stderr, "Failed to find ')' in: %s\n", CFStringGetCStringPtr(string, kCFStringEncodingUTF8)));
316      require_action_quiet(right == ((uint32_t)CFStringGetLength(string) - 1), out, fprintf(stderr, "Failed to find ')' in: %s\n", CFStringGetCStringPtr(string, kCFStringEncodingUTF8)));
317  
318      keyString  = CFStringCreateWithSubstring(kCFAllocatorDefault, string, CFRangeMake(0, left));
319      valueString = CFStringCreateWithSubstring(kCFAllocatorDefault, string, CFRangeMake(left + 1, right - left - 1));
320      require_quiet(parsedValue = parseValue(valueString), out);
321      CFRetainAssign(*key, keyString);
322      CFRetainAssign(*value, parsedValue);
323      ok = true;
324  
325  out:
326      CFReleaseSafe(parsedValue);
327      CFReleaseSafe(keyString);
328      CFReleaseSafe(valueString);
329  
330      return ok;
331  }
332  
333  SecAccessControlRef
334  keychain_query_parse_sac(CFStringRef s) {
335      SecAccessControlRef sac = NULL, result = NULL;
336      CFTypeRef key = NULL, value = NULL;
337      CFArrayRef tokens = CFStringCreateArrayBySeparatingStrings(NULL, s, CFSTR(";"));
338  
339      // process protection part
340      CFStringRef protection = CFArrayGetValueAtIndex(tokens, 0);
341      
342      CFErrorRef error = NULL;
343      require_quiet(sac = SecAccessControlCreate(kCFAllocatorDefault, &error), out);
344      require_quiet(SecAccessControlSetProtection(sac, protection, &error), out);
345  
346      CFIndex tokensCnt = CFArrayGetCount(tokens);
347      for(CFIndex i = 1; i < tokensCnt; ++i) {
348          require_action_quiet(parseKeyAndValue(CFArrayGetValueAtIndex(tokens, i), &key, &value), out, fprintf(stderr, "Error constructing SecAccessConstraint object\n") );
349  
350          if (CFEqual(key, CFSTR(kACMKeyAclParamRequirePasscode)))
351              SecAccessControlSetRequirePassword(sac, CFEqual(value, kCFBooleanTrue)?true:false);
352          else
353              SecAccessControlAddConstraintForOperation(sac, key, value, NULL);
354      }
355  
356      SecAccessConstraintRef constraintForDelete = SecAccessControlGetConstraint(sac, kAKSKeyOpDelete);
357      if (!constraintForDelete) {
358          if(!SecAccessControlAddConstraintForOperation(sac, kAKSKeyOpDelete, kCFBooleanTrue, &error)) {
359              fprintf(stderr, "adding delete operation to sac object failed \n");
360          }
361      }
362  
363      SecAccessConstraintRef constraintForEncrypt = SecAccessControlGetConstraint(sac, kAKSKeyOpEncrypt);
364      if (!constraintForEncrypt) {
365          if(!SecAccessControlAddConstraintForOperation(sac, kAKSKeyOpEncrypt, kCFBooleanTrue, &error)) {
366              fprintf(stderr, "adding encrypt operation to sac object failed \n");
367          }
368      }
369  
370      CFRetainAssign(result, sac);
371  
372  out:
373      CFReleaseSafe(tokens);
374      CFReleaseSafe(key);
375      CFReleaseSafe(value);
376      CFReleaseSafe(sac);
377  
378      return result;
379  }