1/* 2 * Copyright (c) 2004-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 24/* 25 * trustSettings.cpp - routines called by MIG for Trust Settings r/w 26 * 27 * Created 9 May 2006 by dmitch 28 */ 29 30#include <security_ocspd/ocspd.h> /* created by MIG */ 31#include <Security/Authorization.h> 32#include <Security/AuthorizationDB.h> 33#include <security_utilities/threading.h> 34#include <security_utilities/globalizer.h> 35#include <security_cdsa_utils/cuFileIo.h> 36#include <sys/param.h> 37#include <sys/stat.h> 38#include <bsm/libbsm.h> /* for audit_token_to_au32() */ 39#include <security_ocspd/ocspdDebug.h> 40#include <Security/SecBase.h> 41#include <Security/SecTrustSettings.h> 42#include <Security/TrustSettingsSchema.h> 43#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h> 44#include "ocspdServer.h" 45#include <membership.h> /* for mbr_uid_to_uuid() */ 46 47/* One lock for all accesses to TrustSettings files */ 48static ModuleNexus<Mutex> gTrustSettingsLock; 49 50/* 51 * The auth rights we use. 52 * This probably will get tweaked: what I'd like to have, I think, for 53 * the admin settings is "OK if root, else authenticate as admin". For 54 * now just require root. 55 */ 56#define TRUST_SETTINGS_RIGHT_USER "com.apple.trust-settings.user" 57#define TRUST_SETTINGS_RIGHT_ADMIN "com.apple.trust-settings.admin" 58 59#define TRUST_SETTINGS_RULE_USER CFSTR("authenticate-session-owner"); 60#define TRUST_SETTINGS_RULE_ADMIN CFSTR("is-root"); 61 62/* 63 * Everyone can look in the settings directory, and hence stat the admin 64 * file (as a quick optimization to avoid an RPC to us if it's not there), 65 * but the individual files are readable only by root. 66 */ 67#define TRUST_SETTINGS_PATH_MODE 0755 68#define TRUST_SETTINGS_FILES_MODE 0600 69 70/* 71 * Create a stringified UUID. Caller allocs result, length UUID_STR_LEN + 1. 72 * String has two characters for each UUID byte, plus 4 '-' chars, plus NULL. 73 */ 74#define UUID_STR_LEN ((2 * sizeof(uuid_t)) + 5) 75 76static void uuidString( 77 uuid_t uuid, 78 char *cp) 79{ 80 unsigned dex; 81 unsigned char *uuidCp = (unsigned char *)uuid; 82 83 for(dex=0; dex<sizeof(uuid_t); dex++) { 84 sprintf(cp, "%02X", *uuidCp++); 85 cp += 2; 86 switch(dex) { 87 case 3: 88 case 5: 89 case 7: 90 case 9: 91 *cp++ = '-'; 92 break; 93 default: 94 break; 95 } 96 } 97 *cp = '\0'; 98} 99 100/* 101 * Given the audit_token from an incoming message, cook up the path to the 102 * appropriate settings file. 103 * 104 * We use UUID here to form the file name, not uid, since uids can be reused. 105 * (User Joe, uid 600, has some settings, then quits to company. User Mary joins, 106 * is assigned to the currently unused uid 600. User Mary unwittingly inherits 107 * Joe's Trust Settings. Bad.) 108 * 109 * I have no idea under which circumstances the mbr_uid_to_uuid() function could 110 * fail, but we fall back to uid if it does. 111 * 112 * The UUID-based filename is of the same form as UUID are generally 113 * presented: FA36D0A2-D91A-4E36-8156-1A22E8982652.plist, 114 */ 115static void trustSettingsPath( 116 audit_token_t auditToken, 117 SecTrustSettingsDomain domain, 118 char *path) /* caller allocated, (MAXPATHLEN + 1) bytes */ 119{ 120 switch(domain) { 121 case kSecTrustSettingsDomainUser: 122 { 123 uid_t euid; 124 uuid_t uuid; 125 126 /* uid from the incoming message's audit_token */ 127 audit_token_to_au32(auditToken, NULL, &euid, NULL, NULL, NULL, NULL, NULL, NULL); 128 /* convert that to uuid */ 129 if(!mbr_uid_to_uuid(euid, uuid)) { 130 char uuidStr[UUID_STR_LEN]; 131 uuidString(uuid, uuidStr); 132 snprintf(path, MAXPATHLEN + 1, "%s/%s.plist", TRUST_SETTINGS_PATH, uuidStr); 133 } 134 else { 135 /* can't get UUID - use the uid */ 136 snprintf(path, MAXPATHLEN + 1, "%s/%lu.plist", TRUST_SETTINGS_PATH, 137 (unsigned long)euid); 138 } 139 break; 140 } 141 case kSecTrustSettingsDomainAdmin: 142 snprintf(path, MAXPATHLEN + 1, "%s/%s", TRUST_SETTINGS_PATH, ADMIN_TRUST_SETTINGS); 143 break; 144 case kSecTrustSettingsDomainSystem: 145 /* 146 * The client really should just read this file themselves since it's 147 * immutable. But, we'll do it if asked. 148 */ 149 strcpy(path, SYSTEM_TRUST_SETTINGS_PATH); 150 break; 151 default: 152 break; 153 } 154} 155 156static mach_port_t gBootstrapPort=MACH_PORT_NULL; 157static mach_port_t gAuditSessionPort=MACH_PORT_NULL; 158 159void switchToContext(mach_port_t clientBootstrapPort, audit_token_t auditToken) 160{ 161 au_asid_t asid, tmp_asid; 162 mach_port_t clientAuditSessionPort=MACH_PORT_NULL; 163 kern_return_t kr; 164 165 /* get the audit session identifier and port from the audit_token */ 166 audit_token_to_au32(auditToken, NULL, NULL, NULL, NULL, NULL, NULL, &asid, NULL); 167 audit_session_port(asid, &clientAuditSessionPort); 168 169 if (gBootstrapPort == MACH_PORT_NULL) { 170 /* save our own bootstrap port the first time through (to restore later) */ 171 task_get_bootstrap_port(mach_task_self(), &gBootstrapPort); 172 } 173 kr = task_set_bootstrap_port(mach_task_self(), clientBootstrapPort); 174 if (kr != KERN_SUCCESS) { 175 ocspdErrorLog("Unable to set client bootstrap port\n"); 176 } 177 if (gAuditSessionPort == MACH_PORT_NULL) { 178 /* save our own audit session port the first time through (to restore later) */ 179 gAuditSessionPort = audit_session_self(); 180 mach_port_mod_refs(mach_task_self(), gAuditSessionPort, MACH_PORT_RIGHT_SEND, +1); 181 } 182 tmp_asid = audit_session_join(clientAuditSessionPort); 183 if (tmp_asid == AU_DEFAUDITSID) { 184 ocspdErrorLog("Unable to join client security session\n"); 185 } 186} 187 188void restoreContext() 189{ 190 if (gBootstrapPort != MACH_PORT_NULL) 191 { 192 kern_return_t kr = task_set_bootstrap_port(mach_task_self(), gBootstrapPort); 193 if (kr != KERN_SUCCESS) { 194 ocspdErrorLog("Unable to restore server bootstrap port"); 195 } 196 } 197 198 if (gAuditSessionPort != MACH_PORT_NULL) 199 { 200 au_asid_t asid = audit_session_join(gAuditSessionPort); 201 if (asid == AU_DEFAUDITSID) { 202 ocspdErrorLog("Unable to rejoin original security session"); 203 } 204 } 205} 206 207 208kern_return_t ocsp_server_trustSettingsRead( 209 mach_port_t serverport, 210 audit_token_t auditToken, 211 uint32_t domain, 212 Data *trustSettings, 213 mach_msg_type_number_t *trustSettingsCnt, 214 OSStatus *rcode) 215{ 216 ocspdDebug("Processing trustSettingsRead request"); 217 ServerActivity(); 218 219 StLock<Mutex> _(gTrustSettingsLock()); 220 char path[MAXPATHLEN + 1]; 221 222 trustSettingsPath(auditToken, domain, path); 223 unsigned char *fileData = NULL; 224 unsigned fileDataLen; 225 if(readFile(path, &fileData, &fileDataLen)) { 226 ocspdTrustDebug("trustSettingsRead: no file at %s", path); 227 *rcode = errSecNoTrustSettings; 228 *trustSettings = NULL; 229 *trustSettingsCnt = 0; 230 return 0; 231 } 232 233 /* realloc using our server's allocator for later free */ 234 CSSM_DATA cdata; 235 Allocator &alloc = OcspdServer::active().alloc(); 236 cdata.Data = (uint8 *)alloc.malloc(fileDataLen); 237 cdata.Length = fileDataLen; 238 memmove(cdata.Data, fileData, fileDataLen); 239 free(fileData); 240 passDataToCaller(cdata, trustSettings, trustSettingsCnt); 241 ocspdTrustDebug("trustSettingsRead: read %lu bytes from %s", 242 (unsigned long)cdata.Length, path); 243 *rcode = noErr; 244 return 0; 245} 246 247kern_return_t ocsp_server_trustSettingsWrite( 248 mach_port_t serverport, 249 audit_token_t auditToken, 250 mach_port_t clientport, 251 uint32_t domain, 252 Data authBlob, 253 mach_msg_type_number_t authBlobCnt, 254 Data trustSettings, 255 mach_msg_type_number_t trustSettingsCnt, 256 OSStatus *rcode) 257{ 258 ocspdDebug("Processing trustSettingsWrite request"); 259 ServerActivity(); 260 261 const char *authRight = NULL; 262 CFStringRef authRule = NULL; 263 264 switch(domain) { 265 case kSecTrustSettingsDomainUser: 266 authRight = TRUST_SETTINGS_RIGHT_USER; 267 authRule = TRUST_SETTINGS_RULE_USER; 268 break; 269 case kSecTrustSettingsDomainAdmin: 270 authRight = TRUST_SETTINGS_RIGHT_ADMIN; 271 authRule = TRUST_SETTINGS_RULE_ADMIN; 272 break; 273 case kSecTrustSettingsDomainSystem: 274 /* this TrustSetting is immutable */ 275 *rcode = errSecDataNotModifiable; 276 return 0; 277 default: 278 *rcode = errSecInvalidPrefsDomain; 279 return 0; 280 } 281 282 AuthorizationExternalForm extForm; 283 if(authBlobCnt > sizeof(extForm)) { 284 /* not sure how this could legitimately happen.... */ 285 ocspdErrorLog("trustSettingsWrite: authBlob too big\n"); 286 *rcode = paramErr; 287 return 0; 288 } 289 290 /* 291 * Lazily create auth rights we (and we alone) use 292 */ 293 AuthorizationRef authRef; 294 OSStatus ortn; 295 /* 296 * WARNING: Authorization APIs may cause ocspd to be re-entered! 297 */ 298 if(AuthorizationRightGet(authRight, NULL)) { 299 ortn = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, 300 0, &authRef); 301 if(ortn) { 302 /* should never happen */ 303 ocspdErrorLog("trustSettingsWrite: AuthorizationCreate failure\n"); 304 *rcode = internalComponentErr; 305 return 0; 306 } 307 ortn = AuthorizationRightSet(authRef, authRight, authRule, 308 NULL, NULL, NULL); 309 if(ortn) { 310 ocspdErrorLog("trustSettingsWrite: AuthorizationRightSet failure\n"); 311 *rcode = internalComponentErr; 312 return 0; 313 } 314 AuthorizationFree(authRef, 0); 315 } 316 317 /* 318 * Cook up an auth object from the client's blob 319 */ 320 memmove(&extForm, authBlob, authBlobCnt); 321 ortn = AuthorizationCreateFromExternalForm(&extForm, &authRef); 322 if(ortn) { 323 ocspdErrorLog("trustSettingsWrite: AuthorizationCreateFromExternalForm failure\n"); 324 *rcode = paramErr; 325 return 0; 326 } 327 328 /* now, see if we're authorized to do this thing */ 329 AuthorizationItem authItem = {authRight, 0, NULL, 0}; 330 AuthorizationRights authRights = { 1, &authItem }; 331 AuthorizationFlags authFlags = kAuthorizationFlagInteractionAllowed | 332 kAuthorizationFlagExtendRights; 333 /* save and restore context around call which can put up UI */ 334 switchToContext(clientport, auditToken); 335 ortn = AuthorizationCopyRights(authRef, &authRights, NULL, 336 authFlags, NULL); 337 restoreContext(); 338 if(ortn) { 339 ocspdErrorLog("trustSettingsWrite: AuthorizationCopyRights failure\n"); 340 } 341 /* fixme - destroy rights? Really? */ 342 AuthorizationFree(authRef, kAuthorizationFlagDestroyRights); 343 if(ortn) { 344 *rcode = ortn; 345 return 0; 346 } 347 348 /* 349 * Take the trust settings lock only after we've confirmed our authorization 350 */ 351 StLock<Mutex> _(gTrustSettingsLock()); 352 353 char path[MAXPATHLEN + 1]; 354 355 trustSettingsPath(auditToken, domain, path); 356 357 /* 358 * Looks like we're good to go. 359 * First, handle easy case of deleting a Trust Settings file (indicated 360 * by an empty trustSettings). 361 */ 362 if(trustSettingsCnt == 0) { 363 ocspdTrustDebug("trustSettingsWrite: DELETING %s", path); 364 if(unlink(path)) { 365 /* FIXME maybe we should log this to the console */ 366 *rcode = errno; 367 ocspdErrorLog("trustSettingsWrite: unlink error %d\n", errno); 368 } 369 else { 370 *rcode = noErr; 371 } 372 return 0; 373 } 374 375 376 /* 377 * Create TRUST_SETTINGS_PATH if necessary. 378 */ 379 struct stat sb; 380 if(stat(TRUST_SETTINGS_PATH, &sb)) { 381 ocspdTrustDebug("trustSettingsWrite: creating %s", TRUST_SETTINGS_PATH); 382 int errcode = mkpath_np(TRUST_SETTINGS_PATH, TRUST_SETTINGS_PATH_MODE); 383 if(errcode) { 384 ocspdErrorLog("trustSettingsWrite: mkpath_np() returned %d\n", errcode); 385 *rcode = internalComponentErr; 386 return 0; 387 } 388 389 /* override the probable umask that made this directory unreadable by others */ 390 chmod(TRUST_SETTINGS_PATH, TRUST_SETTINGS_PATH_MODE); 391 } 392 393 /* And, finally.... */ 394 if(writeFile(path, (const unsigned char *)trustSettings, trustSettingsCnt)) { 395 ocspdErrorLog("trustSettingsWrite: writeFile() error\n"); 396 *rcode = internalComponentErr; 397 } 398 else { 399 ocspdTrustDebug("trustSettingsWrite: wrote %lu bytes to %s", 400 (unsigned long)trustSettingsCnt, path); 401 chmod(path, TRUST_SETTINGS_FILES_MODE); 402 *rcode = noErr; 403 } 404 return 0; 405} 406