]> git.saurik.com Git - apple/security.git/blame - libsecurity_codesigning/lib/SecAssessment.cpp
Security-55471.14.4.tar.gz
[apple/security.git] / libsecurity_codesigning / lib / SecAssessment.cpp
CommitLineData
b1ab9ed8
A
1/*
2 * Copyright (c) 2011 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#include "cs.h"
24#include "SecAssessment.h"
25#include "policydb.h"
26#include "policyengine.h"
27#include "xpcengine.h"
28#include "csutilities.h"
29#include <CoreFoundation/CFRuntime.h>
30#include <security_utilities/globalizer.h>
31#include <security_utilities/unix++.h>
32#include <security_utilities/cfmunge.h>
33#include <notify.h>
427c49bc 34#include <esp.h>
b1ab9ed8
A
35
36using namespace CodeSigning;
37
38
427c49bc
A
39static void esp_do_check(const char *op, CFDictionaryRef dict)
40{
41 OSStatus result = __esp_check_ns(op, (void *)(CFDictionaryRef)dict);
42 if (result != noErr)
43 MacOSError::throwMe(result);
44}
45
b1ab9ed8
A
46//
47// CF Objects
48//
49struct _SecAssessment : private CFRuntimeBase {
50public:
313fa17b 51 _SecAssessment(CFURLRef p, AuthorityType typ, CFDictionaryRef r) : path(p), type(typ), result(r) { }
b1ab9ed8
A
52
53 CFCopyRef<CFURLRef> path;
313fa17b 54 AuthorityType type;
b1ab9ed8
A
55 CFRef<CFDictionaryRef> result;
56
57public:
58 static _SecAssessment &ref(SecAssessmentRef r)
59 { return *(_SecAssessment *)r; }
60
61 // CF Boiler-plate
62 void *operator new (size_t size)
63 {
64 return (void *)_CFRuntimeCreateInstance(NULL, SecAssessmentGetTypeID(),
65 sizeof(_SecAssessment) - sizeof(CFRuntimeBase), NULL);
66 }
67
68 static void finalize(CFTypeRef obj)
69 { ((_SecAssessment *)obj)->~_SecAssessment(); }
70};
71
72typedef _SecAssessment SecAssessment;
73
74
75static const CFRuntimeClass assessmentClass = {
76 0, // version
77 "SecAssessment", // name
78 NULL, // init
79 NULL, // copy
80 SecAssessment::finalize, // finalize
81 NULL, // equal
82 NULL, // hash
83 NULL, // formatting
84 NULL // debug string
85};
86
87
88static dispatch_once_t assessmentOnce;
89CFTypeID assessmentType = _kCFRuntimeNotATypeID;
90
91CFTypeID SecAssessmentGetTypeID()
92{
b1ab9ed8
A
93 dispatch_once(&assessmentOnce, ^void() {
94 if ((assessmentType = _CFRuntimeRegisterClass(&assessmentClass)) == _kCFRuntimeNotATypeID)
95 abort();
96 });
97 return assessmentType;
98}
99
100
101//
102// Common dictionary constants
103//
104CFStringRef kSecAssessmentContextKeyOperation = CFSTR("operation");
105CFStringRef kSecAssessmentOperationTypeExecute = CFSTR("operation:execute");
106CFStringRef kSecAssessmentOperationTypeInstall = CFSTR("operation:install");
107CFStringRef kSecAssessmentOperationTypeOpenDocument = CFSTR("operation:lsopen");
108
109
110//
111// Read-only in-process access to the policy database
112//
113class ReadPolicy : public PolicyDatabase {
114public:
115 ReadPolicy() : PolicyDatabase(defaultDatabase) { }
116};
117ModuleNexus<ReadPolicy> gDatabase;
118
119
120//
121// An on-demand instance of the policy engine
122//
123ModuleNexus<PolicyEngine> gEngine;
124
125
126//
127// Policy evaluation ("assessment") operations
128//
129CFStringRef kSecAssessmentAssessmentVerdict = CFSTR("assessment:verdict");
130CFStringRef kSecAssessmentAssessmentOriginator = CFSTR("assessment:originator");
131CFStringRef kSecAssessmentAssessmentAuthority = CFSTR("assessment:authority");
132CFStringRef kSecAssessmentAssessmentSource = CFSTR("assessment:authority:source");
133CFStringRef kSecAssessmentAssessmentAuthorityRow = CFSTR("assessment:authority:row");
134CFStringRef kSecAssessmentAssessmentAuthorityOverride = CFSTR("assessment:authority:override");
427c49bc 135CFStringRef kSecAssessmentAssessmentAuthorityOriginalVerdict = CFSTR("assessment:authority:verdict");
b1ab9ed8
A
136CFStringRef kSecAssessmentAssessmentFromCache = CFSTR("assessment:authority:cached");
137
138CFStringRef kDisabledOverride = CFSTR("security disabled");
139
b1ab9ed8
A
140SecAssessmentRef SecAssessmentCreate(CFURLRef path,
141 SecAssessmentFlags flags,
142 CFDictionaryRef context,
143 CFErrorRef *errors)
144{
145 BEGIN_CSAPI
146
147 if (flags & kSecAssessmentFlagAsynchronous)
148 MacOSError::throwMe(errSecCSUnimplemented);
149
150 AuthorityType type = typeFor(context, kAuthorityExecute);
151 CFRef<CFMutableDictionaryRef> result = makeCFMutableDictionary();
152
153 SYSPOLICY_ASSESS_API(cfString(path).c_str(), int(type), flags);
154
155 try {
427c49bc
A
156 if (__esp_enabled() && (flags & kSecAssessmentFlagDirect)) {
157 CFTemp<CFDictionaryRef> dict("{path=%O, flags=%d, context=%O, override=%d}", path, flags, context, overrideAssessment());
158 esp_do_check("cs-assessment-evaluate", dict);
159 }
160
b1ab9ed8
A
161 // check the object cache first unless caller denied that or we need extended processing
162 if (!(flags & (kSecAssessmentFlagRequestOrigin | kSecAssessmentFlagIgnoreCache))) {
427c49bc 163 if (gDatabase().checkCache(path, type, flags, result))
313fa17b 164 return new SecAssessment(path, type, result.yield());
b1ab9ed8
A
165 }
166
167 if (flags & kSecAssessmentFlagDirect) {
168 // ask the engine right here to do its thing
169 SYSPOLICY_ASSESS_LOCAL();
170 gEngine().evaluate(path, type, flags, context, result);
171 } else {
172 // relay the question to our daemon for consideration
173 SYSPOLICY_ASSESS_REMOTE();
174 xpcEngineAssess(path, flags, context, result);
175 }
176 } catch (CommonError &error) {
177 switch (error.osStatus()) {
178 case CSSMERR_TP_CERT_REVOKED:
179 throw;
180 default:
427c49bc 181 if (!overrideAssessment(flags))
b1ab9ed8
A
182 throw; // let it go as an error
183 break;
184 }
185 // record the error we would have returned
186 cfadd(result, "{%O=#F,'assessment:error'=%d}}", kSecAssessmentAssessmentVerdict, error.osStatus());
187 } catch (...) {
188 // catch stray errors not conforming to the CommonError scheme
427c49bc 189 if (!overrideAssessment(flags))
b1ab9ed8
A
190 throw; // let it go as an error
191 cfadd(result, "{%O=#F}", kSecAssessmentAssessmentVerdict);
192 }
427c49bc
A
193
194 if (__esp_enabled() && (flags & kSecAssessmentFlagDirect)) {
195 CFTemp<CFDictionaryRef> dict("{path=%O, flags=%d, context=%O, override=%d, result=%O}", path, flags, context, overrideAssessment(), (CFDictionaryRef)result);
196 __esp_notify_ns("cs-assessment-evaluate", (void *)(CFDictionaryRef)dict);
197 }
198
313fa17b 199 return new SecAssessment(path, type, result.yield());
b1ab9ed8
A
200
201 END_CSAPI_ERRORS1(NULL)
202}
203
204
427c49bc 205static void traceResult(CFURLRef target, MessageTrace &trace, std::string &sanitized)
b1ab9ed8 206{
427c49bc
A
207 static const char *interestingBundles[] = {
208 "UNBUNDLED",
209 "com.apple.",
210 "com.install4j.",
211 "com.MindVision.",
212 "com.yourcompany.",
213
214 "com.adobe.flashplayer.installmanager",
215 "com.adobe.Installers.Setup",
216 "com.adobe.PDApp.setup",
217 "com.bittorrent.uTorrent",
218 "com.divx.divx6formacinstaller",
219 "com.getdropbox.dropbox",
220 "com.google.Chrome",
221 "com.Google.GoogleEarthPlugin.plugin",
222 "com.Google.GoogleEarthPlus",
223 "com.hp.Installer",
224 "com.macpaw.CleanMyMac",
225 "com.microsoft.SilverlightInstaller",
226 "com.paragon-software.filesystems.NTFS.pkg",
227 "com.RealNetworks.RealPlayer",
228 "com.skype.skype",
229 "it.alfanet.squared5.MPEGStreamclip",
230 "org.mozilla.firefox",
231 "org.videolan.vlc",
232
233 NULL // sentinel
234 };
b1ab9ed8
A
235
236 string identifier = "UNBUNDLED";
427c49bc
A
237 string version = "UNKNOWN";
238 if (CFRef<CFBundleRef> bundle = CFBundleCreate(NULL, target)) {
b1ab9ed8
A
239 if (CFStringRef ident = CFBundleGetIdentifier(bundle))
240 identifier = cfString(ident);
427c49bc
A
241 if (CFStringRef vers = CFStringRef(CFBundleGetValueForInfoDictionaryKey(bundle, CFSTR("CFBundleShortVersionString"))))
242 version = cfString(vers);
243 }
244
245 CFRef<CFURLRef> url = CFURLCopyAbsoluteURL(target);
246 sanitized = cfString(url);
247 string::size_type rslash = sanitized.rfind('/');
248 if (rslash != string::npos)
249 sanitized = sanitized.substr(rslash+1);
250 bool keepFilename = false;
251 for (const char **pfx = interestingBundles; *pfx; pfx++) {
252 size_t pfxlen = strlen(*pfx);
253 if (identifier.compare(0, pfxlen, *pfx, pfxlen) == 0)
254 if (pfxlen == identifier.size() || (*pfx)[pfxlen-1] == '.') {
255 keepFilename = true;
256 break;
257 }
258 }
259 if (!keepFilename) {
260 string::size_type dot = sanitized.rfind('.');
261 if (dot != string::npos)
262 sanitized = sanitized.substr(dot);
263 else
264 sanitized = "(none)";
265 }
266
267 trace.add("signature2", "bundle:%s", identifier.c_str());
268 trace.add("signature3", "%s", sanitized.c_str());
269 trace.add("signature5", "%s", version.c_str());
270}
271
272static void traceAssessment(SecAssessment &assessment, AuthorityType type, CFDictionaryRef result)
273{
274 if (CFDictionaryGetValue(result, CFSTR("assessment:remote")))
275 return; // just traced in syspolicyd
b1ab9ed8
A
276
277 string authority = "UNSPECIFIED";
278 bool overridden = false;
427c49bc 279 bool old_overridden = false;
b1ab9ed8
A
280 if (CFDictionaryRef authdict = CFDictionaryRef(CFDictionaryGetValue(result, kSecAssessmentAssessmentAuthority))) {
281 if (CFStringRef auth = CFStringRef(CFDictionaryGetValue(authdict, kSecAssessmentAssessmentSource)))
282 authority = cfString(auth);
283 else
284 authority = "no authority";
285 if (CFTypeRef override = CFDictionaryGetValue(authdict, kSecAssessmentAssessmentAuthorityOverride))
427c49bc
A
286 if (CFEqual(override, kDisabledOverride)) {
287 old_overridden = true;
288 if (CFDictionaryGetValue(authdict, kSecAssessmentAssessmentAuthorityOriginalVerdict) == kCFBooleanFalse)
289 overridden = true;
290 }
b1ab9ed8 291 }
427c49bc
A
292
293 MessageTrace trace("com.apple.security.assessment.outcome2", NULL);
294 std::string sanitized;
295 traceResult(assessment.path, trace, sanitized);
313fa17b 296 trace.add("signature4", "%d", type);
427c49bc 297
b1ab9ed8
A
298 if (CFDictionaryGetValue(result, kSecAssessmentAssessmentVerdict) == kCFBooleanFalse) {
299 trace.add("signature", "denied:%s", authority.c_str());
b1ab9ed8 300 trace.send("assessment denied for %s", sanitized.c_str());
427c49bc
A
301 } else if (overridden) { // would have failed except for override
302 trace.add("signature", "defeated:%s", authority.c_str());
b1ab9ed8 303 trace.send("assessment denied for %s but overridden", sanitized.c_str());
427c49bc
A
304 } else if (old_overridden) { // would have succeeded even without override
305 trace.add("signature", "override:%s", authority.c_str());
306 trace.send("assessment granted for %s and overridden", sanitized.c_str());
b1ab9ed8
A
307 } else {
308 trace.add("signature", "granted:%s", authority.c_str());
b1ab9ed8
A
309 trace.send("assessment granted for %s by %s", sanitized.c_str(), authority.c_str());
310 }
311}
312
427c49bc
A
313static void traceUpdate(CFTypeRef target, CFDictionaryRef context, CFDictionaryRef result)
314{
315 // only trace add operations on URL targets
316 if (target == NULL || CFGetTypeID(target) != CFURLGetTypeID())
317 return;
318 CFStringRef edit = CFStringRef(CFDictionaryGetValue(context, kSecAssessmentContextKeyUpdate));
319 if (!CFEqual(edit, kSecAssessmentUpdateOperationAdd))
320 return;
321 MessageTrace trace("com.apple.security.assessment.update", NULL);
322 std::string sanitized;
323 traceResult(CFURLRef(target), trace, sanitized);
324 trace.send("added rule for %s", sanitized.c_str());
325}
326
b1ab9ed8
A
327
328//
329// At present, CopyResult simply retrieves the result already formed by Create.
330// In the future, this will be more lazy.
331//
332CFDictionaryRef SecAssessmentCopyResult(SecAssessmentRef assessmentRef,
333 SecAssessmentFlags flags,
334 CFErrorRef *errors)
335{
336 BEGIN_CSAPI
337
338 SecAssessment &assessment = SecAssessment::ref(assessmentRef);
339 CFCopyRef<CFDictionaryRef> result = assessment.result;
427c49bc 340 if (overrideAssessment(flags)) {
b1ab9ed8 341 // turn rejections into approvals, but note that we did that
427c49bc
A
342 CFTypeRef verdict = CFDictionaryGetValue(result, kSecAssessmentAssessmentVerdict);
343 if (verdict == kCFBooleanFalse) {
b1ab9ed8
A
344 CFRef<CFMutableDictionaryRef> adulterated = makeCFMutableDictionary(result.get());
345 CFDictionarySetValue(adulterated, kSecAssessmentAssessmentVerdict, kCFBooleanTrue);
346 if (CFDictionaryRef authority = CFDictionaryRef(CFDictionaryGetValue(adulterated, kSecAssessmentAssessmentAuthority))) {
347 CFRef<CFMutableDictionaryRef> authority2 = makeCFMutableDictionary(authority);
348 CFDictionarySetValue(authority2, kSecAssessmentAssessmentAuthorityOverride, kDisabledOverride);
427c49bc 349 CFDictionarySetValue(authority2, kSecAssessmentAssessmentAuthorityOriginalVerdict, verdict);
b1ab9ed8
A
350 CFDictionarySetValue(adulterated, kSecAssessmentAssessmentAuthority, authority2);
351 } else {
352 cfadd(adulterated, "{%O={%O=%O}}",
353 kSecAssessmentAssessmentAuthority, kSecAssessmentAssessmentAuthorityOverride, kDisabledOverride);
354 }
355 result = adulterated.get();
356 }
357 }
427c49bc 358 traceAssessment(assessment, assessment.type, result);
b1ab9ed8
A
359 return result.yield();
360
361 END_CSAPI_ERRORS1(NULL)
362}
363
364
365//
366// Policy editing operations.
367// These all make permanent changes to the system-wide authority records.
368//
369CFStringRef kSecAssessmentContextKeyUpdate = CFSTR("update");
370CFStringRef kSecAssessmentUpdateOperationAdd = CFSTR("update:add");
371CFStringRef kSecAssessmentUpdateOperationRemove = CFSTR("update:remove");
372CFStringRef kSecAssessmentUpdateOperationEnable = CFSTR("update:enable");
373CFStringRef kSecAssessmentUpdateOperationDisable = CFSTR("update:disable");
374CFStringRef kSecAssessmentUpdateOperationFind = CFSTR("update:find");
375
376CFStringRef kSecAssessmentUpdateKeyAuthorization = CFSTR("update:authorization");
377CFStringRef kSecAssessmentUpdateKeyPriority = CFSTR("update:priority");
378CFStringRef kSecAssessmentUpdateKeyLabel = CFSTR("update:label");
379CFStringRef kSecAssessmentUpdateKeyExpires = CFSTR("update:expires");
380CFStringRef kSecAssessmentUpdateKeyAllow = CFSTR("update:allow");
381CFStringRef kSecAssessmentUpdateKeyRemarks = CFSTR("update:remarks");
382
383CFStringRef kSecAssessmentUpdateKeyRow = CFSTR("update:row");
384CFStringRef kSecAssessmentUpdateKeyCount = CFSTR("update:count");
385CFStringRef kSecAssessmentUpdateKeyFound = CFSTR("update:found");
386
387CFStringRef kSecAssessmentRuleKeyID = CFSTR("rule:id");
388CFStringRef kSecAssessmentRuleKeyPriority = CFSTR("rule:priority");
389CFStringRef kSecAssessmentRuleKeyAllow = CFSTR("rule:allow");
390CFStringRef kSecAssessmentRuleKeyLabel = CFSTR("rule:label");
391CFStringRef kSecAssessmentRuleKeyRemarks = CFSTR("rule:remarks");
392CFStringRef kSecAssessmentRuleKeyRequirement = CFSTR("rule:requirement");
393CFStringRef kSecAssessmentRuleKeyType = CFSTR("rule:type");
394CFStringRef kSecAssessmentRuleKeyExpires = CFSTR("rule:expires");
395CFStringRef kSecAssessmentRuleKeyDisabled = CFSTR("rule:disabled");
396CFStringRef kSecAssessmentRuleKeyBookmark = CFSTR("rule:bookmark");
397
398
399Boolean SecAssessmentUpdate(CFTypeRef target,
400 SecAssessmentFlags flags,
401 CFDictionaryRef context,
402 CFErrorRef *errors)
403{
404 if (CFDictionaryRef outcome = SecAssessmentCopyUpdate(target, flags, context, errors)) {
405 CFRelease(outcome);
406 return true;
407 } else {
408 return false;
409 }
410}
411
412CFDictionaryRef SecAssessmentCopyUpdate(CFTypeRef target,
413 SecAssessmentFlags flags,
414 CFDictionaryRef context,
415 CFErrorRef *errors)
416{
417 BEGIN_CSAPI
418
419 CFDictionary ctx(context, errSecCSInvalidAttributeValues);
427c49bc 420 CFRef<CFDictionaryRef> result;
b1ab9ed8
A
421
422 if (flags & kSecAssessmentFlagDirect) {
427c49bc
A
423 if (__esp_enabled()) {
424 CFTemp<CFDictionaryRef> dict("{target=%O, flags=%d, context=%O}", target, flags, context);
425 OSStatus esp_result = __esp_check_ns("cs-assessment-update", (void *)(CFDictionaryRef)dict);
426 if (esp_result != noErr)
427 return NULL;
428 }
429
b1ab9ed8 430 // ask the engine right here to do its thing
427c49bc 431 result = gEngine().update(target, flags, ctx);
b1ab9ed8
A
432 } else {
433 // relay the question to our daemon for consideration
427c49bc 434 result = xpcEngineUpdate(target, flags, ctx);
b1ab9ed8
A
435 }
436
427c49bc
A
437 if (__esp_enabled() && (flags & kSecAssessmentFlagDirect)) {
438 CFTemp<CFDictionaryRef> dict("{target=%O, flags=%d, context=%O, outcome=%O}", target, flags, context, (CFDictionaryRef)result);
439 __esp_notify_ns("cs-assessment-update", (void *)(CFDictionaryRef)dict);
440 }
441
442 traceUpdate(target, context, result);
443 return result.yield();
444
b1ab9ed8
A
445 END_CSAPI_ERRORS1(false)
446}
447
448
449//
450// The fcntl of System Policies.
451// For those very special requests.
452//
453Boolean SecAssessmentControl(CFStringRef control, void *arguments, CFErrorRef *errors)
454{
455 BEGIN_CSAPI
456
427c49bc
A
457 CFTemp<CFDictionaryRef> dict("{control=%O}", control);
458 esp_do_check("cs-assessment-control", dict);
459
b1ab9ed8
A
460 if (CFEqual(control, CFSTR("ui-enable"))) {
461 setAssessment(true);
462 MessageTrace trace("com.apple.security.assessment.state", "enable");
463 trace.send("enable assessment outcomes");
464 return true;
465 } else if (CFEqual(control, CFSTR("ui-disable"))) {
466 setAssessment(false);
467 MessageTrace trace("com.apple.security.assessment.state", "disable");
468 trace.send("disable assessment outcomes");
469 return true;
470 } else if (CFEqual(control, CFSTR("ui-status"))) {
471 CFBooleanRef &result = *(CFBooleanRef*)(arguments);
472 if (overrideAssessment())
473 result = kCFBooleanFalse;
474 else
475 result = kCFBooleanTrue;
476 return true;
477 } else if (CFEqual(control, CFSTR("ui-enable-devid"))) {
478 CFTemp<CFDictionaryRef> ctx("{%O=%s}", kSecAssessmentUpdateKeyLabel, "Developer ID");
479 if (CFDictionaryRef result = gEngine().enable(NULL, kAuthorityInvalid, kSecCSDefaultFlags, ctx))
480 CFRelease(result);
313fa17b
A
481 MessageTrace trace("com.apple.security.assessment.state", "enable-devid");
482 trace.send("enable Developer ID approval");
b1ab9ed8
A
483 return true;
484 } else if (CFEqual(control, CFSTR("ui-disable-devid"))) {
485 CFTemp<CFDictionaryRef> ctx("{%O=%s}", kSecAssessmentUpdateKeyLabel, "Developer ID");
486 if (CFDictionaryRef result = gEngine().disable(NULL, kAuthorityInvalid, kSecCSDefaultFlags, ctx))
487 CFRelease(result);
313fa17b
A
488 MessageTrace trace("com.apple.security.assessment.state", "disable-devid");
489 trace.send("disable Developer ID approval");
b1ab9ed8
A
490 return true;
491 } else if (CFEqual(control, CFSTR("ui-get-devid"))) {
492 CFBooleanRef &result = *(CFBooleanRef*)(arguments);
493 if (gEngine().value<int>("SELECT disabled FROM authority WHERE label = 'Developer ID';", true))
494 result = kCFBooleanFalse;
495 else
496 result = kCFBooleanTrue;
497 return true;
427c49bc
A
498 } else if (CFEqual(control, CFSTR("ui-record-reject"))) {
499 // send this through syspolicyd for update validation
500 xpcEngineRecord(CFDictionaryRef(arguments));
501 return true;
502 } else if (CFEqual(control, CFSTR("ui-record-reject-local"))) {
503 // perform the local operation (requires root)
504 gEngine().recordFailure(CFDictionaryRef(arguments));
505 return true;
506 } else if (CFEqual(control, CFSTR("ui-recall-reject"))) {
507 // no special privileges required for this, so read directly
508 CFDictionaryRef &result = *(CFDictionaryRef*)(arguments);
509 CFRef<CFDataRef> infoData = cfLoadFile(lastRejectFile);
510 if (infoData)
511 result = makeCFDictionaryFrom(infoData);
512 else
513 result = NULL;
514 return true;
b1ab9ed8
A
515 } else
516 MacOSError::throwMe(errSecCSInvalidAttributeValues);
517
518 END_CSAPI_ERRORS1(false)
519}