1/* 2 * Copyright (c) 2002-2004 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 24// 25// ACL.cpp 26// 27#include <security_keychain/ACL.h> 28#include <security_keychain/SecCFTypes.h> 29#include <security_utilities/osxcode.h> 30#include <security_utilities/trackingallocator.h> 31#include <security_cdsa_utilities/walkers.h> 32#include <security_keychain/TrustedApplication.h> 33#include <Security/SecTrustedApplication.h> 34#include <security_utilities/devrandom.h> 35#include <security_cdsa_utilities/uniformrandom.h> 36#include <memory> 37 38 39using namespace KeychainCore; 40using namespace DataWalkers; 41 42 43// 44// The default form of a prompt selector 45// 46const CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR ACL::defaultSelector = { 47 CSSM_ACL_KEYCHAIN_PROMPT_CURRENT_VERSION, 0 48}; 49 50 51// 52// ACL static constants 53// 54const CSSM_ACL_HANDLE ACL::ownerHandle; 55 56 57// 58// Create an ACL object from the result of a CSSM ACL query 59// 60ACL::ACL(Access &acc, const AclEntryInfo &info, Allocator &alloc) 61 : allocator(alloc), access(acc), mState(unchanged), mSubjectForm(NULL), mMutex(Mutex::recursive) 62{ 63 // parse the subject 64 parse(info.proto().subject()); 65 66 // fill in AclEntryInfo layer information 67 const AclEntryPrototype &proto = info.proto(); 68 mAuthorizations = proto.authorization(); 69 mDelegate = proto.delegate(); 70 mEntryTag = proto.s_tag(); 71 72 // take CSSM entry handle from info layer 73 mCssmHandle = info.handle(); 74} 75 76ACL::ACL(Access &acc, const AclOwnerPrototype &owner, Allocator &alloc) 77 : allocator(alloc), access(acc), mState(unchanged), mSubjectForm(NULL), mMutex(Mutex::recursive) 78{ 79 // parse subject 80 parse(owner.subject()); 81 82 // for an owner "entry", the next-layer information is fixed (and fake) 83 mAuthorizations.insert(CSSM_ACL_AUTHORIZATION_CHANGE_ACL); 84 mDelegate = owner.delegate(); 85 mEntryTag[0] = '\0'; 86 87 // use fixed (fake) entry handle 88 mCssmHandle = ownerHandle; 89} 90 91 92// 93// Create a new ACL that authorizes anyone to do anything. 94// This constructor produces a "pure" ANY ACL, without descriptor or selector. 95// To generate a "standard" form of ANY, use the appListForm constructor below, 96// then change its form to allowAnyForm. 97// 98ACL::ACL(Access &acc, Allocator &alloc) 99 : allocator(alloc), access(acc), mSubjectForm(NULL), mMutex(Mutex::recursive) 100{ 101 mState = inserted; // new 102 mForm = allowAllForm; // everybody 103 mAuthorizations.insert(CSSM_ACL_AUTHORIZATION_ANY); // anything 104 mDelegate = false; 105 106 //mPromptDescription stays empty 107 mPromptSelector = defaultSelector; 108 109 // randomize the CSSM handle 110 UniformRandomBlobs<DevRandomGenerator>().random(mCssmHandle); 111} 112 113 114// 115// Create a new ACL in standard form. 116// As created, it authorizes all activities. 117// 118ACL::ACL(Access &acc, string description, const CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR &promptSelector, 119 Allocator &alloc) 120 : allocator(alloc), access(acc), mSubjectForm(NULL), mMutex(Mutex::recursive) 121{ 122 mState = inserted; // new 123 mForm = appListForm; 124 mAuthorizations.insert(CSSM_ACL_AUTHORIZATION_ANY); // anything 125 mDelegate = false; 126 127 mPromptDescription = description; 128 mPromptSelector = promptSelector; 129 130 // randomize the CSSM handle 131 UniformRandomBlobs<DevRandomGenerator>().random(mCssmHandle); 132} 133 134 135// 136// Destroy an ACL 137// 138ACL::~ACL() 139{ 140 // release subject form (if any) 141 chunkFree(mSubjectForm, allocator); 142} 143 144 145// 146// Does this ACL authorize a particular right? 147// 148bool ACL::authorizes(AclAuthorization right) 149{ 150 StLock<Mutex>_(mMutex); 151 return mAuthorizations.find(right) != mAuthorizations.end() 152 || mAuthorizations.find(CSSM_ACL_AUTHORIZATION_ANY) != mAuthorizations.end() 153 || mAuthorizations.empty(); 154} 155 156 157// 158// Add an application to the trusted-app list of this ACL. 159// Will fail unless this is a standard "simple" form ACL. 160// 161void ACL::addApplication(TrustedApplication *app) 162{ 163 StLock<Mutex>_(mMutex); 164 switch (mForm) { 165 case appListForm: // simple... 166 mAppList.push_back(app); 167 modify(); 168 break; 169 case allowAllForm: // hmm... 170 if (!mPromptDescription.empty()) { 171 // verbose "any" form (has description, "any" override) 172 mAppList.push_back(app); 173 modify(); 174 break; 175 } 176 // pure "any" form without description. Cannot convert to appListForm 177 default: 178 MacOSError::throwMe(errSecACLNotSimple); 179 } 180} 181 182 183// 184// Mark an ACL as modified. 185// 186void ACL::modify() 187{ 188 StLock<Mutex>_(mMutex); 189 if (mState == unchanged) { 190 secdebug("SecAccess", "ACL %p marked modified", this); 191 mState = modified; 192 } 193} 194 195 196// 197// Mark an ACL as "removed" 198// Removed ACLs have no valid contents (they are invalid on their face). 199// When "updated" to the originating item, they will cause the corresponding 200// ACL entry to be deleted. Otherwise, they are irrelevant. 201// Note: Removing an ACL does not actually remove it from its Access's map. 202// 203void ACL::remove() 204{ 205 StLock<Mutex>_(mMutex); 206 mAppList.clear(); 207 mForm = invalidForm; 208 mState = deleted; 209} 210 211 212// 213// Produce CSSM-layer form (ACL prototype) copies of our content. 214// Note that the result is chunk-allocated, and becomes owned by the caller. 215// 216void ACL::copyAclEntry(AclEntryPrototype &proto, Allocator &alloc) 217{ 218 StLock<Mutex>_(mMutex); 219 proto.clearPod(); // preset 220 221 // carefully copy the subject 222 makeSubject(); 223 assert(mSubjectForm); 224 proto = AclEntryPrototype(*mSubjectForm, mDelegate); // shares subject 225 ChunkCopyWalker w(alloc); 226 walk(w, proto.subject()); // copy subject in-place 227 228 // the rest of a prototype 229 proto.tag(mEntryTag); 230 AuthorizationGroup tags(mAuthorizations, allocator); 231 proto.authorization() = tags; 232} 233 234void ACL::copyAclOwner(AclOwnerPrototype &proto, Allocator &alloc) 235{ 236 StLock<Mutex>_(mMutex); 237 proto.clearPod(); 238 239 makeSubject(); 240 assert(mSubjectForm); 241 proto = AclOwnerPrototype(*mSubjectForm, mDelegate); // shares subject 242 ChunkCopyWalker w(alloc); 243 walk(w, proto.subject()); // copy subject in-place 244} 245 246 247// 248// (Re)place this ACL's setting into the AclBearer specified. 249// If update, assume this is an update operation and the ACL was 250// originally derived from this object; specifically, assume the 251// CSSM handle is valid. If not update, assume this is a different 252// object that has no related ACL entry (yet). 253// 254void ACL::setAccess(AclBearer &target, bool update, 255 const AccessCredentials *cred) 256{ 257 StLock<Mutex>_(mMutex); 258 // determine what action we need to perform 259 State action = state(); 260 if (!update) 261 action = (action == deleted) ? unchanged : inserted; 262 263 // the owner acl (pseudo) "entry" is a special case 264 if (isOwner()) { 265 switch (action) { 266 case unchanged: 267 secdebug("SecAccess", "ACL %p owner unchanged", this); 268 return; 269 case inserted: // means modify the initial owner 270 case modified: 271 { 272 secdebug("SecAccess", "ACL %p owner modified", this); 273 makeSubject(); 274 assert(mSubjectForm); 275 AclOwnerPrototype proto(*mSubjectForm, mDelegate); 276 target.changeOwner(proto, cred); 277 return; 278 } 279 default: 280 assert(false); 281 return; 282 } 283 } 284 285 // simple cases 286 switch (action) { 287 case unchanged: // ignore 288 secdebug("SecAccess", "ACL %p handle 0x%lx unchanged", this, entryHandle()); 289 return; 290 case deleted: // delete 291 secdebug("SecAccess", "ACL %p handle 0x%lx deleted", this, entryHandle()); 292 target.deleteAcl(entryHandle(), cred); 293 return; 294 default: 295 break; 296 } 297 298 // build the byzantine data structures that CSSM loves so much 299 makeSubject(); 300 assert(mSubjectForm); 301 AclEntryPrototype proto(*mSubjectForm, mDelegate); 302 proto.tag(mEntryTag); 303 AutoAuthorizationGroup tags(mAuthorizations, allocator); 304 proto.authorization() = tags; 305 AclEntryInput input(proto); 306 switch (action) { 307 case inserted: // insert 308 secdebug("SecAccess", "ACL %p inserted", this); 309 target.addAcl(input, cred); 310 break; 311 case modified: // update 312 secdebug("SecAccess", "ACL %p handle 0x%lx modified", this, entryHandle()); 313 target.changeAcl(entryHandle(), input, cred); 314 break; 315 default: 316 assert(false); 317 } 318} 319 320 321// 322// Parse an AclEntryPrototype (presumably from a CSSM "Get" ACL operation 323// into internal form. 324// 325void ACL::parse(const TypedList &subject) 326{ 327 StLock<Mutex>_(mMutex); 328 try { 329 switch (subject.type()) { 330 case CSSM_ACL_SUBJECT_TYPE_ANY: 331 // subsume an "any" as a standard form 332 mForm = allowAllForm; 333 return; 334 case CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT: 335 // pure keychain prompt - interpret as applist form with no apps 336 parsePrompt(subject); 337 mForm = appListForm; 338 return; 339 case CSSM_ACL_SUBJECT_TYPE_THRESHOLD: 340 { 341 // app-list format: THRESHOLD(1, n): sign(1), ..., sign(n), PROMPT 342 if (subject[1] != 1) 343 throw ParseError(); 344 uint32 count = subject[2]; 345 346 // parse final (PROMPT) element 347 TypedList &end = subject[count + 2]; // last choice 348 if (end.type() != CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT) 349 throw ParseError(); // not PROMPT at end 350 parsePrompt(end); 351 352 // check for leading ANY 353 TypedList &first = subject[3]; 354 if (first.type() == CSSM_ACL_SUBJECT_TYPE_ANY) { 355 mForm = allowAllForm; 356 return; 357 } 358 359 // parse other (code signing) elements 360 for (uint32 n = 0; n < count - 1; n++) 361 mAppList.push_back(new TrustedApplication(TypedList(subject[n + 3].list()))); 362 } 363 mForm = appListForm; 364 return; 365 default: 366 mForm = customForm; 367 mSubjectForm = chunkCopy(&subject); 368 return; 369 } 370 } catch (const ParseError &) { 371 secdebug("SecAccess", "acl compile failed; marking custom"); 372 mForm = customForm; 373 mSubjectForm = chunkCopy(&subject); 374 mAppList.clear(); 375 } 376} 377 378void ACL::parsePrompt(const TypedList &subject) 379{ 380 StLock<Mutex>_(mMutex); 381 assert(subject.length() == 3); 382 mPromptSelector = 383 *subject[1].data().interpretedAs<CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR>(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE); 384 mPromptDescription = subject[2].toString(); 385} 386 387 388// 389// Take this ACL and produce its meaning as a CSSM ACL subject in mSubjectForm 390// 391void ACL::makeSubject() 392{ 393 StLock<Mutex>_(mMutex); 394 switch (form()) { 395 case allowAllForm: 396 chunkFree(mSubjectForm, allocator); // release previous 397 if (mPromptDescription.empty()) { 398 // no description -> pure ANY 399 mSubjectForm = new(allocator) TypedList(allocator, CSSM_ACL_SUBJECT_TYPE_ANY); 400 } else { 401 // have description -> threshold(1 of 2) of { ANY, PROMPT } 402 mSubjectForm = new(allocator) TypedList(allocator, CSSM_ACL_SUBJECT_TYPE_THRESHOLD, 403 new(allocator) ListElement(1), 404 new(allocator) ListElement(2)); 405 *mSubjectForm += new(allocator) ListElement(TypedList(allocator, CSSM_ACL_SUBJECT_TYPE_ANY)); 406 TypedList prompt(allocator, CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT, 407 new(allocator) ListElement(allocator, CssmData::wrap(mPromptSelector)), 408 new(allocator) ListElement(allocator, mPromptDescription)); 409 *mSubjectForm += new(allocator) ListElement(prompt); 410 } 411 return; 412 case appListForm: { 413 // threshold(1 of n+1) of { app1, ..., appn, PROMPT } 414 chunkFree(mSubjectForm, allocator); // release previous 415 uint32 appCount = (uint32)mAppList.size(); 416 mSubjectForm = new(allocator) TypedList(allocator, CSSM_ACL_SUBJECT_TYPE_THRESHOLD, 417 new(allocator) ListElement(1), 418 new(allocator) ListElement(appCount + 1)); 419 for (uint32 n = 0; n < appCount; n++) 420 *mSubjectForm += 421 new(allocator) ListElement(mAppList[n]->makeSubject(allocator)); 422 TypedList prompt(allocator, CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT, 423 new(allocator) ListElement(allocator, CssmData::wrap(mPromptSelector)), 424 new(allocator) ListElement(allocator, mPromptDescription)); 425 *mSubjectForm += new(allocator) ListElement(prompt); 426 } 427 return; 428 case customForm: 429 assert(mSubjectForm); // already set; keep it 430 return; 431 default: 432 assert(false); // unexpected 433 } 434} 435