1/* 2 * Copyright (c) 2000-2009,2012-2013 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// 26// acls - securityd ACL implementation 27// 28#include "acls.h" 29#include "connection.h" 30#include "server.h" 31#include "agentquery.h" 32#include "tokendatabase.h" 33#include "acl_keychain.h" 34 35// ACL subjects whose Environments we implement 36#include <security_cdsa_utilities/acl_any.h> 37#include <security_cdsa_utilities/acl_password.h> 38#include <security_cdsa_utilities/acl_threshold.h> 39 40#include <sys/sysctl.h> 41#include <security_utilities/logging.h> 42 43// 44// SecurityServerAcl is virtual 45// 46SecurityServerAcl::~SecurityServerAcl() 47{ } 48 49 50// 51// The default implementation of the ACL interface simply uses the local ObjectAcl 52// data. You can customize this by implementing instantiateAcl() [from ObjectAcl] 53// or by overriding these methods as desired. 54// Note: While you can completely ignore the ObjectAcl personality if you wish, it's 55// usually smarter to adapt it. 56// 57void SecurityServerAcl::getOwner(AclOwnerPrototype &owner) 58{ 59 StLock<Mutex> _(aclSequence); 60 ObjectAcl::cssmGetOwner(owner); 61} 62 63void SecurityServerAcl::getAcl(const char *tag, uint32 &count, AclEntryInfo *&acls) 64{ 65 StLock<Mutex> _(aclSequence); 66 ObjectAcl::cssmGetAcl(tag, count, acls); 67} 68 69void SecurityServerAcl::changeAcl(const AclEdit &edit, const AccessCredentials *cred, 70 Database *db) 71{ 72 StLock<Mutex> _(aclSequence); 73 SecurityServerEnvironment env(*this, db); 74 ObjectAcl::cssmChangeAcl(edit, cred, &env); 75} 76 77void SecurityServerAcl::changeOwner(const AclOwnerPrototype &newOwner, 78 const AccessCredentials *cred, Database *db) 79{ 80 StLock<Mutex> _(aclSequence); 81 SecurityServerEnvironment env(*this, db); 82 ObjectAcl::cssmChangeOwner(newOwner, cred, &env); 83} 84 85 86// 87// Modified validate() methods to connect all the conduits... 88// 89void SecurityServerAcl::validate(AclAuthorization auth, const AccessCredentials *cred, Database *db) 90{ 91 SecurityServerEnvironment env(*this, db); 92 93 StLock<Mutex> objectSequence(aclSequence); 94 StLock<Mutex> processSequence(Server::process().aclSequence); 95 ObjectAcl::validate(auth, cred, &env); 96} 97 98void SecurityServerAcl::validate(AclAuthorization auth, const Context &context, Database *db) 99{ 100 validate(auth, 101 context.get<AccessCredentials>(CSSM_ATTRIBUTE_ACCESS_CREDENTIALS), db); 102} 103 104 105// 106// This helper tries to add the (new) subject given to the ACL 107// whose validation is currently proceeding through context. 108// This will succeed if the ACL is in standard form, which means 109// a ThresholdAclSubject. 110// The new subject will be added at the front (so it is checked first 111// from now on), and as a side effect we'll notify the client side to 112// re-encode the object. 113// Returns true if the edit could be done, or false if the ACL wasn't 114// standard enough. May throw if the ACL is malformed or otherwise messed up. 115// 116// This is a self-contained helper that is here merely because it's "about" 117// ACLs and has no better home. 118// 119bool SecurityServerAcl::addToStandardACL(const AclValidationContext &context, AclSubject *subject) 120{ 121 if (SecurityServerEnvironment *env = context.environment<SecurityServerEnvironment>()) 122 if (ThresholdAclSubject *threshold = env->standardSubject(context)) { 123 unsigned size = threshold->count(); 124 if (dynamic_cast<KeychainPromptAclSubject *>(threshold->subject(size-1))) { 125 // looks standard enough 126 secdebug("acl", "adding new subject %p to from of threshold ACL", subject); 127 threshold->add(subject, 0); 128 129 // tell the ACL it's been modified 130 context.acl()->changedAcl(); 131 132 // trigger a special notification code on (otherwise successful) return 133 Server::connection().overrideReturn(CSSMERR_CSP_APPLE_ADD_APPLICATION_ACL_SUBJECT); 134 return true; 135 } 136 } 137 secdebug("acl", "ACL is not standard form; cannot edit"); 138 return false; 139} 140 141 142// 143// Look at the ACL whose validation is currently proceeding through context. 144// If it LOOKS like a plausible version of a legacy "dot mac item" ACL. 145// We don't have access to the database attributes of the item up here in the 146// securityd sky, so we have to apply a heuristic based on which applications (by path) 147// are given access to the item. 148// So this is strictly a heuristic. The potential downside is that we may inadvertently 149// give access to new .Mac authorized Apple (only) applications when the user only intended 150// a limited set of extremely popular Apple (only) applications that just happen to all be 151// .Mac authorized today. We can live with that. 152// 153bool SecurityServerAcl::looksLikeLegacyDotMac(const AclValidationContext &context) 154{ 155 static const char * const prototypicalDotMacPath[] = { 156 "/Applications/Mail.app", 157 "/Applications/Safari.app", 158 "/Applications/iSync.app", 159 "/Applications/System Preferences.app", 160 "/Applications/iCal.app", 161 "/Applications/iChat.app", 162 "/Applications/iTunes.app", 163 "/Applications/Address Book.app", 164 "/Applications/iSync.app", 165 NULL // sentinel 166 }; 167 168 static const unsigned threshold = 6; 169 170 if (SecurityServerEnvironment *env = context.environment<SecurityServerEnvironment>()) { 171 if (ThresholdAclSubject *list = env->standardSubject(context)) { 172 unsigned count = list->count(); 173 unsigned matches = 0; 174 for (unsigned n = 0; n < count; ++n) { 175 if (CodeSignatureAclSubject *app = dynamic_cast<CodeSignatureAclSubject *>(list->subject(n))) { 176 for (const char * const *p = prototypicalDotMacPath; *p; p++) 177 if (app->path() == *p) 178 matches++; 179 } 180 } 181 secdebug("codesign", "matched %d of %zd candididates (threshold=%d)", 182 matches, sizeof(prototypicalDotMacPath) / sizeof(char *) - 1, threshold); 183 return matches >= threshold; 184 } 185 } 186 return false; 187} 188 189 190// 191// External storage interface 192// 193Adornable &SecurityServerEnvironment::store(const AclSubject *subject) 194{ 195 switch (subject->type()) { 196 case CSSM_ACL_SUBJECT_TYPE_PREAUTH: 197 { 198 if (TokenDatabase *tokenDb = dynamic_cast<TokenDatabase *>(database)) 199 return tokenDb->common().store(); 200 } 201 break; 202 default: 203 break; 204 } 205 CssmError::throwMe(CSSM_ERRCODE_ACL_SUBJECT_TYPE_NOT_SUPPORTED); 206} 207 208 209// 210// ProcessAclSubject personality: uid/gid/pid come from the active Process object 211// 212uid_t SecurityServerEnvironment::getuid() const 213{ 214 return Server::process().uid(); 215} 216 217gid_t SecurityServerEnvironment::getgid() const 218{ 219 return Server::process().gid(); 220} 221 222pid_t SecurityServerEnvironment::getpid() const 223{ 224 return Server::process().pid(); 225} 226 227 228// 229// CodeSignatureAclSubject personality: take code signature from active Process object 230// 231bool SecurityServerEnvironment::verifyCodeSignature(const OSXVerifier &verifier, 232 const AclValidationContext &context) 233{ 234 return Server::codeSignatures().verify(Server::process(), verifier, context); 235} 236 237 238// 239// PromptedAclSubject personality: Get a secret by prompting through SecurityAgent 240// 241bool SecurityServerEnvironment::getSecret(CssmOwnedData &secret, const CssmData &prompt) const 242{ 243 //@@@ ignoring prompt - not used right now 244 if (database) { 245 QueryPIN query(*database); 246 query.inferHints(Server::process()); 247 if (!query()) { // success 248 secret = query.pin(); 249 return true; 250 } 251 } 252 return false; 253} 254 255 256// 257// SecretAclSubject personality: externally validate a secret (passphrase etc.) 258// Right now, this always goes to the (Token)Database object, because that's where 259// the PIN ACL entries are. We could direct this at the ObjectAcl (database or key) 260// instead and rely on tokend to perform the PIN mapping, but the generic tokend 261// wrappers do not (currently) perform any ACL validation, so every tokend would have 262// to re-implement that. Perhaps in the next ACL revamp cycle... 263// 264bool SecurityServerEnvironment::validateSecret(const SecretAclSubject *me, 265 const AccessCredentials *cred) 266{ 267 return database && database->validateSecret(me, cred); 268} 269 270 271// 272// PreAuthenticationAclSubject personality - refer to database (ObjectAcl) 273// 274ObjectAcl *SecurityServerEnvironment::preAuthSource() 275{ 276 return database ? &database->acl() : NULL; 277} 278 279 280// 281// Autonomous ACL editing support 282// 283ThresholdAclSubject *SecurityServerEnvironment::standardSubject(const AclValidationContext &context) 284{ 285 return dynamic_cast<ThresholdAclSubject *>(context.subject()); 286} 287 288 289// 290// The default AclSource denies having an ACL at all 291// 292AclSource::~AclSource() 293{ /* virtual */ } 294 295SecurityServerAcl &AclSource::acl() 296{ 297 CssmError::throwMe(CSSM_ERRCODE_OBJECT_ACL_NOT_SUPPORTED); 298} 299 300Database *AclSource::relatedDatabase() 301{ 302 return NULL; 303} 304