1/* 2 * Copyright (c) 2004-2006 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 * aclUtils.cpp - ACL utility functions, copied from the SecurityTool project. 26 */ 27 28#include "aclUtils.h" 29#include <Security/SecTrustedApplicationPriv.h> 30#include <CoreServices/../Frameworks/CarbonCore.framework/Headers/MacErrors.h> 31 32/* Read a line from stdin into buffer as a null terminated string. If buffer is 33 non NULL use at most buffer_size bytes and return a pointer to buffer. Otherwise 34 return a newly malloced buffer. 35 if EOF is read this function returns NULL. */ 36char * 37readline(char *buffer, int buffer_size) 38{ 39 int ix = 0, bytes_malloced = 0; 40 41 if (!buffer) 42 { 43 bytes_malloced = 64; 44 buffer = (char *)malloc(bytes_malloced); 45 buffer_size = bytes_malloced; 46 } 47 48 for (;;++ix) 49 { 50 int ch; 51 52 if (ix == buffer_size - 1) 53 { 54 if (!bytes_malloced) 55 break; 56 bytes_malloced += bytes_malloced; 57 buffer = (char *)realloc(buffer, bytes_malloced); 58 buffer_size = bytes_malloced; 59 } 60 61 ch = getchar(); 62 if (ch == EOF) 63 { 64 if (bytes_malloced) 65 free(buffer); 66 return NULL; 67 } 68 if (ch == '\n') 69 break; 70 buffer[ix] = ch; 71 } 72 73 /* 0 terminate buffer. */ 74 buffer[ix] = '\0'; 75 76 return buffer; 77} 78 79void 80print_buffer_hex(FILE *stream, UInt32 length, const void *data) 81{ 82 unsigned i; 83 const unsigned char *cp = (const unsigned char *)data; 84 85 printf("\n "); 86 for(i=0; i<length; i++) { 87 fprintf(stream, "%02X ", cp[i]); 88 if((i % 24) == 23) { 89 printf("\n "); 90 } 91 } 92} 93 94void 95print_buffer_ascii(FILE *stream, UInt32 length, const void *data) 96{ 97 uint8 *p = (uint8 *) data; 98 while (length--) 99 { 100 int ch = *p++; 101 if (ch >= ' ' && ch <= '~' && ch != '\\') 102 { 103 fputc(ch, stream); 104 } 105 else 106 { 107 fputc('\\', stream); 108 fputc('0' + ((ch >> 6) & 7), stream); 109 fputc('0' + ((ch >> 3) & 7), stream); 110 fputc('0' + ((ch >> 0) & 7), stream); 111 } 112 } 113} 114 115void 116print_buffer(FILE *stream, UInt32 length, const void *data) 117{ 118 uint8 *p = (uint8 *) data; 119 Boolean ascii = TRUE; // unless we determine otherwise 120 UInt32 ix; 121 for (ix = 0; ix < length; ++ix) { 122 int ch = *p++; 123 if ((ch < ' ') || (ch > '~')) { 124 if((ch == 0) && (ix == (length - 1))) { 125 /* ignore trailing null */ 126 length--; 127 break; 128 } 129 ascii = FALSE; 130 break; 131 } 132 } 133 134 if (ascii) { 135 fputc('"', stream); 136 print_buffer_ascii(stream, length, data); 137 fputc('"', stream); 138 } 139 else { 140 print_buffer_hex(stream, length, data); 141 } 142} 143 144void 145print_cfdata(FILE *stream, CFDataRef data) 146{ 147 if (data) 148 return print_buffer(stream, CFDataGetLength(data), CFDataGetBytePtr(data)); 149 else 150 fprintf(stream, "<NULL>"); 151} 152 153void 154print_cfstring(FILE *stream, CFStringRef string) 155{ 156 if (!string) 157 fprintf(stream, "<NULL>"); 158 else 159 { 160 const char *utf8 = CFStringGetCStringPtr(string, kCFStringEncodingUTF8); 161 if (utf8) 162 fprintf(stream, "%s", utf8); 163 else 164 { 165 CFRange rangeToProcess = CFRangeMake(0, CFStringGetLength(string)); 166 while (rangeToProcess.length > 0) 167 { 168 UInt8 localBuffer[256]; 169 CFIndex usedBufferLength; 170 CFIndex numChars = CFStringGetBytes(string, rangeToProcess, 171 kCFStringEncodingUTF8, '?', FALSE, localBuffer, 172 sizeof(localBuffer), &usedBufferLength); 173 if (numChars == 0) 174 break; // Failed to convert anything... 175 176 fprintf(stream, "%.*s", (int)usedBufferLength, localBuffer); 177 rangeToProcess.location += numChars; 178 rangeToProcess.length -= numChars; 179 } 180 } 181 } 182} 183 184 185int 186print_access(FILE *stream, SecAccessRef access, Boolean interactive) 187{ 188 CFArrayRef aclList = NULL; 189 CFIndex aclix, aclCount; 190 int result = 0; 191 OSStatus status; 192 193 status = SecAccessCopyACLList(access, &aclList); 194 if (status) 195 { 196 cssmPerror("SecAccessCopyACLList", status); 197 result = 1; 198 goto loser; 199 } 200 201 aclCount = CFArrayGetCount(aclList); 202 fprintf(stream, "access: %lu entries\n", aclCount); 203 for (aclix = 0; aclix < aclCount; ++aclix) 204 { 205 CFArrayRef applicationList = NULL; 206 CFStringRef description = NULL; 207 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector = {}; 208 CFIndex appix, appCount; 209 210 SecACLRef acl = (SecACLRef)CFArrayGetValueAtIndex(aclList, aclix); 211 CSSM_ACL_AUTHORIZATION_TAG tags[64]; // Pick some upper limit 212 uint32 tagix, tagCount = sizeof(tags) / sizeof(*tags); 213 status = SecACLGetAuthorizations(acl, tags, &tagCount); 214 if (status) 215 { 216 cssmPerror("SecACLGetAuthorizations", status); 217 result = 1; 218 goto loser; 219 } 220 221 fprintf(stream, " entry %lu:\n authorizations (%lu):", aclix, (unsigned long)tagCount); 222 for (tagix = 0; tagix < tagCount; ++tagix) 223 { 224 CSSM_ACL_AUTHORIZATION_TAG tag = tags[tagix]; 225 switch (tag) 226 { 227 case CSSM_ACL_AUTHORIZATION_ANY: 228 fputs(" any", stream); 229 break; 230 case CSSM_ACL_AUTHORIZATION_LOGIN: 231 fputs(" login", stream); 232 break; 233 case CSSM_ACL_AUTHORIZATION_GENKEY: 234 fputs(" genkey", stream); 235 break; 236 case CSSM_ACL_AUTHORIZATION_DELETE: 237 fputs(" delete", stream); 238 break; 239 case CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED: 240 fputs(" export_wrapped", stream); 241 break; 242 case CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR: 243 fputs(" export_clear", stream); 244 break; 245 case CSSM_ACL_AUTHORIZATION_IMPORT_WRAPPED: 246 fputs(" import_wrapped", stream); 247 break; 248 case CSSM_ACL_AUTHORIZATION_IMPORT_CLEAR: 249 fputs(" import_clear", stream); 250 break; 251 case CSSM_ACL_AUTHORIZATION_SIGN: 252 fputs(" sign", stream); 253 break; 254 case CSSM_ACL_AUTHORIZATION_ENCRYPT: 255 fputs(" encrypt", stream); 256 break; 257 case CSSM_ACL_AUTHORIZATION_DECRYPT: 258 fputs(" decrypt", stream); 259 break; 260 case CSSM_ACL_AUTHORIZATION_MAC: 261 fputs(" mac", stream); 262 break; 263 case CSSM_ACL_AUTHORIZATION_DERIVE: 264 fputs(" derive", stream); 265 break; 266 case CSSM_ACL_AUTHORIZATION_DBS_CREATE: 267 fputs(" dbs_create", stream); 268 break; 269 case CSSM_ACL_AUTHORIZATION_DBS_DELETE: 270 fputs(" dbs_delete", stream); 271 break; 272 case CSSM_ACL_AUTHORIZATION_DB_READ: 273 fputs(" db_read", stream); 274 break; 275 case CSSM_ACL_AUTHORIZATION_DB_INSERT: 276 fputs(" db_insert", stream); 277 break; 278 case CSSM_ACL_AUTHORIZATION_DB_MODIFY: 279 fputs(" db_modify", stream); 280 break; 281 case CSSM_ACL_AUTHORIZATION_DB_DELETE: 282 fputs(" db_delete", stream); 283 break; 284 case CSSM_ACL_AUTHORIZATION_CHANGE_ACL: 285 fputs(" change_acl", stream); 286 break; 287 case CSSM_ACL_AUTHORIZATION_CHANGE_OWNER: 288 fputs(" change_owner", stream); 289 break; 290 default: 291 fprintf(stream, " tag=%lu", (unsigned long)tag); 292 break; 293 } 294 } 295 fputc('\n', stream); 296 297 status = SecACLCopySimpleContents(acl, &applicationList, &description, &promptSelector); 298 if (status) 299 { 300 cssmPerror("SecACLCopySimpleContents", status); 301 continue; 302 } 303 304 if (promptSelector.flags & CSSM_ACL_KEYCHAIN_PROMPT_REQUIRE_PASSPHRASE) 305 fputs(" require-password\n", stream); 306 else 307 fputs(" don't-require-password\n", stream); 308 309 fputs(" description: ", stream); 310 print_cfstring(stream, description); 311 fputc('\n', stream); 312 313 if (applicationList) 314 { 315 appCount = CFArrayGetCount(applicationList); 316 fprintf(stream, " applications (%lu):\n", appCount); 317 } 318 else 319 { 320 appCount = 0; 321 fprintf(stream, " applications: <null>\n"); 322 } 323 324 for (appix = 0; appix < appCount; ++appix) 325 { 326 const UInt8* bytes; 327 SecTrustedApplicationRef app = (SecTrustedApplicationRef)CFArrayGetValueAtIndex(applicationList, appix); 328 CFDataRef data = NULL; 329 fprintf(stream, " %lu: ", appix); 330 status = SecTrustedApplicationCopyData(app, &data); 331 if (status) 332 { 333 cssmPerror("SecTrustedApplicationCopyData", status); 334 continue; 335 } 336 337 bytes = CFDataGetBytePtr(data); 338 if (bytes && bytes[0] == 0x2f) { 339 fprintf(stream, "%s", (const char *)bytes); 340 if ((status = SecTrustedApplicationValidateWithPath(app, (const char *)bytes)) == noErr) { 341 fprintf(stream, " (OK)"); 342 } else { 343 fprintf(stream, " (status %ld)", status); 344 } 345 fprintf(stream, "\n"); 346 } else { 347 print_cfdata(stream, data); 348 fputc('\n', stream); 349 } 350 if (data) 351 CFRelease(data); 352 } 353 354 355 if (interactive) 356 { 357 char buffer[10] = {}; 358 if(applicationList != NULL) { 359 fprintf(stderr, "NULL out this application list? "); 360 if (readline(buffer, sizeof(buffer)) && buffer[0] == 'y') 361 { 362 /* 363 * This makes the ops in this entry wide-open, no dialog or confirmation 364 * other than requiring the keychain be open. 365 */ 366 fprintf(stderr, "setting app list to NULL\n"); 367 status = SecACLSetSimpleContents(acl, NULL, description, &promptSelector); 368 if (status) 369 { 370 cssmPerror("SecACLSetSimpleContents", status); 371 continue; 372 } 373 } 374 else { 375 fprintf(stderr, "Set this application list to empty array? "); 376 if (readline(buffer, sizeof(buffer)) && buffer[0] == 'y') 377 { 378 /* 379 * This means "always get confirmation, from all apps". 380 */ 381 fprintf(stderr, "setting app list to empty array\n"); 382 status = SecACLSetSimpleContents(acl, 383 CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks), 384 description, &promptSelector); 385 if (status) 386 { 387 cssmPerror("SecACLSetSimpleContents", status); 388 continue; 389 } 390 } 391 } 392 } 393 else { 394 fprintf(stderr, "Remove this acl? "); 395 if (readline(buffer, sizeof(buffer)) && buffer[0] == 'y') 396 { 397 /* 398 * This make ths ops in this entry completely inaccessible. 399 */ 400 fprintf(stderr, "removing acl\n"); 401 status = SecACLRemove(acl); 402 if (status) 403 { 404 cssmPerror("SecACLRemove", status); 405 continue; 406 } 407 } 408 } 409 } 410 if (description) 411 CFRelease(description); 412 if (applicationList) 413 CFRelease(applicationList); 414 415 } 416 417loser: 418 if (aclList) 419 CFRelease(aclList); 420 421 return result; 422} 423 424/* Simluate what StickyRecord is trying to do.... */ 425 426/* 427 * Given an Access object: 428 * -- extract the ACL for the specified CSSM_ACL_AUTHORIZATION_TAG. We expect there 429 * to exactly one of these - if the form of a default ACL changes we'll have to 430 * revisit this. 431 * -- set the ACL's app list to the provided CFArray, which may be NULL (meaning 432 * "any app can access this, no problem"), an empty array (meaning "always 433 * prompt"), or an actual app list. 434 * -- set or clear the PROMPT_REQUIRE_PASSPHRASE bit per the requirePassphrase 435 * argument 436 */ 437static OSStatus srUpdateAcl( 438 SecAccessRef accessRef, 439 CSSM_ACL_AUTHORIZATION_TAG whichAcl, // e.g. CSSM_ACL_AUTHORIZATION_DECRYPT 440 CFArrayRef appArray, 441 bool requirePassphrase) 442{ 443 OSStatus ortn; 444 CFArrayRef aclList = NULL; 445 446 ortn = SecAccessCopySelectedACLList(accessRef, whichAcl, &aclList); 447 if(ortn) { 448 cssmPerror("SecAccessCopySelectedACLList", ortn); 449 return ortn; 450 } 451 452 if(CFArrayGetCount(aclList) != 1) { 453 printf("StickyRecord::updateAcl - unexpected ACL list count (%d)", 454 (int)CFArrayGetCount(aclList)); 455 return internalComponentErr; 456 } 457 SecACLRef acl = (SecACLRef)CFArrayGetValueAtIndex(aclList, 0); 458 459 CFArrayRef applicationList = NULL; 460 CFStringRef description = NULL; 461 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector = {}; 462 ortn = SecACLCopySimpleContents(acl, &applicationList, &description, &promptSelector); 463 if(ortn) { 464 cssmPerror("SecACLCopySimpleContents", ortn); 465 return ortn; 466 } 467 if(applicationList != NULL) { 468 CFRelease(applicationList); 469 } 470 if(requirePassphrase) { 471 promptSelector.flags |= CSSM_ACL_KEYCHAIN_PROMPT_REQUIRE_PASSPHRASE; 472 } 473 else { 474 promptSelector.flags &= ~CSSM_ACL_KEYCHAIN_PROMPT_REQUIRE_PASSPHRASE; 475 } 476 /* update */ 477 ortn = SecACLSetSimpleContents(acl, appArray, description, &promptSelector); 478 479 /* we got this from SecACLCopySimpleContents - release it regardless */ 480 if(description != NULL) { 481 CFRelease(description); 482 } 483 if(ortn) { 484 cssmPerror("SecACLSetSimpleContents", ortn); 485 } 486 if(aclList != NULL) { 487 CFRelease(aclList); 488 } 489 return ortn; 490} 491 492OSStatus stickyRecordUpdateAcl( 493 SecAccessRef accessRef) 494{ 495 OSStatus ortn; 496 497 printf("...updating ACL to simulate a StickyRecord\n"); 498 499 /* First: decrypt. Wide open (NULL app list), !REQUIRE_PASSPHRASE. */ 500 ortn = srUpdateAcl(accessRef, CSSM_ACL_AUTHORIZATION_DECRYPT, NULL, false); 501 if(ortn) { 502 return ortn; 503 } 504 505 /* encrypt: always ask (empty app list, require passphrase */ 506 CFArrayRef nullArray = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks); 507 return srUpdateAcl(accessRef, CSSM_ACL_AUTHORIZATION_ENCRYPT, nullArray, true); 508 509} 510