1/* 2 * Copyright (c) 2003-2010,2012,2014 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 * keychain_list.c 24 */ 25 26#include "keychain_list.h" 27 28#include "keychain_utilities.h" 29#include "security.h" 30 31#include <stdio.h> 32#include <string.h> 33#include <sys/param.h> 34#include <unistd.h> 35#include <CoreFoundation/CFArray.h> 36#include <Security/SecKeychain.h> 37 38// SecKeychainCopyLogin 39#include <Security/SecKeychainPriv.h> 40 41 42typedef enum 43{ 44 kList, 45 kAdd, 46 kRemove, 47 kSet, 48} list_operation; 49 50static void 51display_name(const void *value, void *context) 52{ 53 SecKeychainRef keychain = (SecKeychainRef)value; 54 UInt32 pathLength = MAXPATHLEN; 55 char pathName[MAXPATHLEN + 1]; 56 OSStatus result = SecKeychainGetPath(keychain, &pathLength, pathName); 57 if (result) 58 sec_error("SecKeychainGetPath %p: %s", keychain, sec_errstr(result)); 59 else 60 fprintf(stdout, " \"%*s\"\n", (int)pathLength, pathName); 61} 62 63 64static void 65display_list(const char *desc, CFTypeRef keychainOrArray) 66{ 67 if (desc && strlen(desc)) 68 fprintf(stdout, "%s\n", desc); 69 70 if (!keychainOrArray) 71 { 72 fprintf(stdout, " <NULL>\n"); 73 } 74 else if (CFGetTypeID(keychainOrArray) == SecKeychainGetTypeID()) 75 { 76 display_name(keychainOrArray, NULL); 77 } 78 else 79 { 80 CFArrayRef array = (CFArrayRef)keychainOrArray; 81 CFRange range = { 0, CFArrayGetCount(array) }; 82 CFArrayApplyFunction(array, range, display_name, NULL); 83 } 84} 85 86static int 87parse_domain(const char *name, SecPreferencesDomain *domain) 88{ 89 size_t len = strlen(name); 90 91 if (!strncmp("user", name, len)) 92 *domain = kSecPreferencesDomainUser; 93 else if (!strncmp("system", name, len)) 94 *domain = kSecPreferencesDomainSystem; 95 else if (!strncmp("common", name, len)) 96 *domain = kSecPreferencesDomainCommon; 97 else if (!strncmp("dynamic", name, len)) 98 *domain = kSecPreferencesDomainDynamic; 99 else 100 { 101 sec_error("Invalid domain: %s", name); 102 return 2; 103 } 104 105 return 0; 106} 107 108const char * 109domain2str(SecPreferencesDomain domain) 110{ 111 switch (domain) 112 { 113 case kSecPreferencesDomainUser: 114 return "user"; 115 case kSecPreferencesDomainSystem: 116 return "system"; 117 case kSecPreferencesDomainCommon: 118 return "common"; 119 case kSecPreferencesDomainDynamic: 120 return "dynamic"; 121 default: 122 return "unknown"; 123 } 124} 125 126int 127keychain_list(int argc, char * const *argv) 128{ 129 CFTypeRef keychainOrArray = NULL; 130 CFArrayRef searchList = NULL; 131 list_operation operation = kList; 132 SecPreferencesDomain domain = kSecPreferencesDomainUser; 133 Boolean use_domain = false; 134 int ch, result = 0; 135 OSStatus status; 136 137 while ((ch = getopt(argc, argv, "d:hs")) != -1) 138 { 139 switch (ch) 140 { 141 case 'd': 142 result = parse_domain(optarg, &domain); 143 if (result) 144 return result; 145 use_domain = true; 146 break; 147 case 's': 148 operation = kSet; 149 break; 150 case '?': 151 default: 152 return 2; /* @@@ Return 2 triggers usage message. */ 153 } 154 } 155 156 argc -= optind; 157 argv += optind; 158 159 switch (operation) 160 { 161 case kAdd: 162 result = 1; 163 break; 164 case kRemove: 165 result = 1; 166 break; 167 case kList: 168 if (argc > 0) 169 result = 2; // Show usage 170 else 171 { 172 if (use_domain) 173 { 174 status = SecKeychainCopyDomainSearchList(domain, &searchList); 175 if (status) 176 { 177 sec_error("SecKeychainCopyDomainSearchList %s: %s", domain2str(domain), sec_errstr(status)); 178 result = 1; 179 } 180 else 181 { 182#if 0 183 fprintf(stdout, "%s search list: ", domain2str(domain)); 184#endif 185 display_list("", searchList); 186 } 187 } 188 else 189 { 190 status = SecKeychainCopySearchList(&searchList); 191 if (status) 192 { 193 sec_perror("SecKeychainCopySearchList", status); 194 result = 1; 195 } 196 else 197 { 198#if 0 199 display_list("search list:", searchList); 200#else 201 display_list("", searchList); 202#endif 203 } 204 } 205 } 206 break; 207 case kSet: 208 keychainOrArray = keychain_create_array(argc, argv); 209 if (argc == 0) 210 searchList = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks); 211 else if (argc == 1) 212 searchList = CFArrayCreate(NULL, &keychainOrArray, 1, &kCFTypeArrayCallBacks); 213 else 214 searchList = (CFArrayRef)CFRetain(keychainOrArray); 215 216 if (use_domain) 217 { 218 status = SecKeychainSetDomainSearchList(domain, searchList); 219 if (status) 220 { 221 sec_error("SecKeychainSetDomainSearchList %s: %s", domain2str(domain), sec_errstr(status)); 222 result = 1; 223 } 224 } 225 else 226 { 227 status = SecKeychainSetSearchList(searchList); 228 if (status) 229 { 230 sec_perror("SecKeychainSetSearchList", status); 231 result = 1; 232 } 233 } 234 break; 235 } 236 237 if (keychainOrArray) 238 CFRelease(keychainOrArray); 239 if (searchList) 240 CFRelease(searchList); 241 242 return result; 243} 244 245int 246keychain_default(int argc, char * const *argv) 247{ 248 SecPreferencesDomain domain = kSecPreferencesDomainUser; 249 SecKeychainRef keychain = NULL; 250 Boolean use_domain = false; 251 Boolean do_set = false; 252 int ch, result = 0; 253 OSStatus status; 254 255 while ((ch = getopt(argc, argv, "d:hs")) != -1) 256 { 257 switch (ch) 258 { 259 case 'd': 260 result = parse_domain(optarg, &domain); 261 if (result) 262 return result; 263 use_domain = true; 264 break; 265 case 's': 266 do_set = true; 267 break; 268 case '?': 269 default: 270 return 2; /* @@@ Return 2 triggers usage message. */ 271 } 272 } 273 274 argc -= optind; 275 argv += optind; 276 277 if (do_set) 278 { 279 if (argc == 1) 280 keychain = (SecKeychainRef)keychain_create_array(argc, argv); 281 else if (argc > 0) 282 return 2; 283 284 if (use_domain) 285 { 286 status = SecKeychainSetDomainDefault(domain, keychain); 287 if (status) 288 { 289 sec_error("SecKeychainSetDomainDefault %s: %s", domain2str(domain), sec_errstr(status)); 290 result = 1; 291 } 292 } 293 else 294 { 295 status = SecKeychainSetDefault(keychain); 296 if (status) 297 { 298 sec_perror("SecKeychainSetDefault", status); 299 result = 1; 300 } 301 } 302 } 303 else 304 { 305 if (argc > 0) 306 return 2; 307 308 if (use_domain) 309 { 310 status = SecKeychainCopyDomainDefault(domain, &keychain); 311 if (status) 312 { 313 sec_error("SecKeychainCopyDomainDefault %s: %s", domain2str(domain), sec_errstr(status)); 314 result = 1; 315 } 316 else 317 { 318#if 0 319 fprintf(stdout, "default %s keychain: ", domain2str(domain)); 320#endif 321 display_list("", keychain); 322 } 323 } 324 else 325 { 326 status = SecKeychainCopyDefault(&keychain); 327 if (status) 328 { 329 sec_perror("SecKeychainCopyDefault", status); 330 result = 1; 331 } 332 else 333 { 334#if 0 335 display_list("default keychain: ", keychain); 336#else 337 display_list("", keychain); 338#endif 339 } 340 } 341 } 342 343 if (keychain) 344 CFRelease(keychain); 345 346 return result; 347} 348 349int 350keychain_login(int argc, char * const *argv) 351{ 352 SecPreferencesDomain domain = kSecPreferencesDomainUser; 353 SecKeychainRef keychain = NULL; 354 Boolean use_domain = false; 355 Boolean do_set = false; 356 int ch, result = 0; 357 OSStatus status; 358 359 while ((ch = getopt(argc, argv, "d:hs")) != -1) 360 { 361 switch (ch) 362 { 363 case 'd': 364 result = parse_domain(optarg, &domain); 365 if (result) 366 return result; 367 use_domain = true; 368 break; 369 case 's': 370 do_set = true; 371 break; 372 case '?': 373 default: 374 return 2; /* @@@ Return 2 triggers usage message. */ 375 } 376 } 377 378 argc -= optind; 379 argv += optind; 380 381 if (do_set) 382 { 383 if (argc == 1) 384 keychain = (SecKeychainRef)keychain_create_array(argc, argv); 385 else if (argc > 0) 386 return 2; 387 388#if 0 389 if (use_domain) 390 { 391 status = SecKeychainSetDomainLogin(domain, keychain); 392 if (status) 393 { 394 sec_error("SecKeychainSetDomainLogin %s: %s", domain2str(domain), sec_errstr(status)); 395 result = 1; 396 } 397 } 398 else 399 { 400 status = SecKeychainSetLogin(keychain); 401 if (status) 402 { 403 sec_perror("SecKeychainSetLogin", status); 404 result = 1; 405 } 406 } 407#else 408 result = 1; 409#endif 410 } 411 else 412 { 413 if (argc > 0) 414 return 2; 415 416 if (use_domain) 417 { 418#if 0 419 status = SecKeychainCopyDomainLogin(domain, &keychain); 420 if (status) 421 { 422 sec_error("SecKeychainCopyDomainLogin %s: %s", domain2str(domain), sec_errstr(status)); 423 result = 1; 424 } 425 else 426 { 427#if 0 428 fprintf(stdout, "login %s keychain: ", domain2str(domain)); 429#endif 430 display_list("", keychain); 431 } 432#else 433 result = 1; 434#endif 435 } 436 else 437 { 438 status = SecKeychainCopyLogin(&keychain); 439 if (status) 440 { 441 sec_perror("SecKeychainCopyLogin", status); 442 result = 1; 443 } 444 else 445 { 446#if 0 447 display_list("login keychain: ", keychain); 448#else 449 display_list("", keychain); 450#endif 451 } 452 } 453 } 454 455 if (keychain) 456 CFRelease(keychain); 457 458 return result; 459} 460