]> git.saurik.com Git - apple/security.git/blob - SecurityTool/keychain_add.c
Security-57337.50.23.tar.gz
[apple/security.git] / SecurityTool / keychain_add.c
1 /*
2 * Copyright (c) 2003-2009,2011-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_add.c
24 */
25
26 #include "keychain_add.h"
27 #include "keychain_find.h"
28 #include "readline.h"
29 #include "security.h"
30 #include "access_utils.h"
31 #include "keychain_utilities.h"
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36 #include <libkern/OSByteOrder.h>
37 #include <Security/SecAccess.h>
38 #include <Security/SecCertificate.h>
39 #include <Security/SecKeychain.h>
40 #include <Security/SecKeychainItem.h>
41 #include <Security/SecTrustedApplication.h>
42
43 // SecTrustedApplicationCreateApplicationGroup
44 #include <Security/SecTrustedApplicationPriv.h>
45
46
47 static int
48 do_update_generic_password(const char *keychainName,
49 FourCharCode itemCreator,
50 FourCharCode itemType,
51 const char *kind,
52 const char *value,
53 const char *comment,
54 const char *label,
55 const char *serviceName,
56 const char *accountName,
57 const void *passwordData,
58 SecAccessRef access)
59 {
60 OSStatus status;
61 SecKeychainRef keychainRef = NULL;
62 SecKeychainItemRef itemRef = NULL;
63
64 if (keychainName) {
65 keychainRef = keychain_open(keychainName);
66 }
67 itemRef = find_first_generic_password(keychainRef,itemCreator,itemType,kind,value,comment,label,serviceName,accountName);
68 if (keychainRef) {
69 CFRelease(keychainRef);
70 }
71 if (!itemRef) {
72 return errSecItemNotFound;
73 }
74
75 // build list of attributes
76 SecKeychainAttribute attrs[8]; // maximum number of attributes
77 SecKeychainAttributeList attrList = { 0, attrs };
78
79 if ((UInt32)itemCreator != 0) {
80 attrs[attrList.count].tag = kSecCreatorItemAttr;
81 attrs[attrList.count].length = sizeof(FourCharCode);
82 attrs[attrList.count].data = (FourCharCode *)&itemCreator;
83 attrList.count++;
84 }
85 if ((UInt32)itemType != 0) {
86 attrs[attrList.count].tag = kSecTypeItemAttr;
87 attrs[attrList.count].length = sizeof(FourCharCode);
88 attrs[attrList.count].data = (FourCharCode *)&itemType;
89 attrList.count++;
90 }
91 if (kind != NULL) {
92 attrs[attrList.count].tag = kSecDescriptionItemAttr;
93 attrs[attrList.count].length = strlen(kind);
94 attrs[attrList.count].data = (void *)kind;
95 attrList.count++;
96 }
97 if (value != NULL) {
98 attrs[attrList.count].tag = kSecGenericItemAttr;
99 attrs[attrList.count].length = strlen(value);
100 attrs[attrList.count].data = (void *)value;
101 attrList.count++;
102 }
103 if (comment != NULL) {
104 attrs[attrList.count].tag = kSecCommentItemAttr;
105 attrs[attrList.count].length = strlen(comment);
106 attrs[attrList.count].data = (void *)comment;
107 attrList.count++;
108 }
109 if (label != NULL) {
110 attrs[attrList.count].tag = kSecLabelItemAttr;
111 attrs[attrList.count].length = strlen(label);
112 attrs[attrList.count].data = (void *)label;
113 attrList.count++;
114 }
115 if (serviceName != NULL) {
116 attrs[attrList.count].tag = kSecServiceItemAttr;
117 attrs[attrList.count].length = strlen(serviceName);
118 attrs[attrList.count].data = (void *)serviceName;
119 attrList.count++;
120 }
121 if (accountName != NULL) {
122 attrs[attrList.count].tag = kSecAccountItemAttr;
123 attrs[attrList.count].length = strlen(accountName);
124 attrs[attrList.count].data = (void *)accountName;
125 attrList.count++;
126 }
127
128 // modify attributes and password data, if provided
129 status = SecKeychainItemModifyContent(itemRef, &attrList, (passwordData) ? strlen(passwordData) : 0, passwordData);
130 if (status) {
131 sec_error("SecKeychainItemModifyContent: %s", sec_errstr(status));
132 }
133
134 // modify access, if provided
135 if (!status && access) {
136 SecAccessRef curAccess = NULL;
137 // for historical reasons, we have to modify the item's existing access reference
138 // (setting the item's access to a freshly created SecAccessRef instance doesn't behave as expected)
139 status = SecKeychainItemCopyAccess(itemRef, &curAccess);
140 if (status) {
141 sec_error("SecKeychainItemCopyAccess: %s", sec_errstr(status));
142 } else {
143 int result = merge_access(curAccess, access); // make changes to the existing access reference
144 if (result == noErr) {
145 status = SecKeychainItemSetAccess(itemRef, curAccess); // will prompt
146 if (status) {
147 sec_error("SecKeychainItemSetAccess: %s", sec_errstr(status));
148 }
149 }
150 }
151 if (curAccess) {
152 CFRelease(curAccess);
153 }
154 }
155
156 CFRelease(itemRef);
157
158 return status;
159 }
160
161 static int
162 do_add_generic_password(const char *keychainName,
163 FourCharCode itemCreator,
164 FourCharCode itemType,
165 const char *kind,
166 const char *value,
167 const char *comment,
168 const char *label,
169 const char *serviceName,
170 const char *accountName,
171 const void *passwordData,
172 SecAccessRef access,
173 Boolean update)
174 {
175 SecKeychainRef keychain = NULL;
176 OSStatus result = 0;
177 SecKeychainItemRef itemRef = NULL;
178
179 // if update flag is specified, try to find and update an existing item
180 if (update) {
181 result = do_update_generic_password(keychainName,itemCreator,itemType,kind,value,comment,label,serviceName,accountName,passwordData,access);
182 if (result == noErr)
183 return result;
184 }
185
186 if (keychainName)
187 {
188 keychain = keychain_open(keychainName);
189 if (!keychain)
190 {
191 result = 1;
192 goto cleanup;
193 }
194 }
195
196 // set up attribute vector for new item (each attribute consists of {tag, length, pointer})
197 SecKeychainAttribute attrs[] = {
198 { kSecLabelItemAttr, label ? strlen(label) : 0, (char *)label },
199 { kSecDescriptionItemAttr, kind ? strlen(kind) : 0, (char *)kind },
200 { kSecGenericItemAttr, value ? strlen(value) : 0, (char *)value },
201 { kSecCommentItemAttr, comment ? strlen(comment) : 0, (char *)comment },
202 { kSecServiceItemAttr, serviceName ? strlen(serviceName) : 0, (char *)serviceName },
203 { kSecAccountItemAttr, accountName ? strlen(accountName) : 0, (char *)accountName },
204 { (SecKeychainAttrType)0, sizeof(FourCharCode), NULL }, /* placeholder */
205 { (SecKeychainAttrType)0, sizeof(FourCharCode), NULL } /* placeholder */
206 };
207 SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };
208 attributes.count -= 2;
209
210 if (itemCreator != 0)
211 {
212 attrs[attributes.count].tag = kSecCreatorItemAttr;
213 attrs[attributes.count].data = (FourCharCode *)&itemCreator;
214 attributes.count++;
215 }
216 if (itemType != 0)
217 {
218 attrs[attributes.count].tag = kSecTypeItemAttr;
219 attrs[attributes.count].data = (FourCharCode *)&itemType;
220 attributes.count++;
221 }
222
223 result = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass,
224 &attributes,
225 passwordData ? strlen(passwordData) : 0,
226 passwordData,
227 keychain,
228 access,
229 &itemRef);
230
231 if (result)
232 {
233 sec_error("SecKeychainItemCreateFromContent (%s): %s", keychainName ? keychainName : "<default>", sec_errstr(result));
234 }
235
236 cleanup:
237 if (itemRef)
238 CFRelease(itemRef);
239 if (keychain)
240 CFRelease(keychain);
241
242 return result;
243 }
244
245 static int
246 do_update_internet_password(const char *keychainName,
247 FourCharCode itemCreator,
248 FourCharCode itemType,
249 const char *kind,
250 const char *comment,
251 const char *label,
252 const char *serverName,
253 const char *securityDomain,
254 const char *accountName,
255 const char *path,
256 UInt16 port,
257 SecProtocolType protocol,
258 SecAuthenticationType authenticationType,
259 const void *passwordData,
260 SecAccessRef access)
261 {
262 OSStatus status;
263 SecKeychainRef keychainRef = NULL;
264 SecKeychainItemRef itemRef = NULL;
265
266 if (keychainName) {
267 keychainRef = keychain_open(keychainName);
268 }
269 itemRef = find_first_internet_password(keychainRef,itemCreator,itemType,kind,comment,label,serverName,
270 securityDomain,accountName,path,port,protocol,authenticationType);
271 if (keychainRef) {
272 CFRelease(keychainRef);
273 }
274 if (!itemRef) {
275 return errSecItemNotFound;
276 }
277
278 // build list of attributes
279 SecKeychainAttribute attrs[12]; // maximum number of attributes
280 SecKeychainAttributeList attrList = { 0, attrs };
281
282 if ((UInt32)itemCreator != 0) {
283 attrs[attrList.count].tag = kSecCreatorItemAttr;
284 attrs[attrList.count].length = sizeof(FourCharCode);
285 attrs[attrList.count].data = (FourCharCode *)&itemCreator;
286 attrList.count++;
287 }
288 if ((UInt32)itemType != 0) {
289 attrs[attrList.count].tag = kSecTypeItemAttr;
290 attrs[attrList.count].length = sizeof(FourCharCode);
291 attrs[attrList.count].data = (FourCharCode *)&itemType;
292 attrList.count++;
293 }
294 if (kind != NULL) {
295 attrs[attrList.count].tag = kSecDescriptionItemAttr;
296 attrs[attrList.count].length = strlen(kind);
297 attrs[attrList.count].data = (void *)kind;
298 attrList.count++;
299 }
300 if (comment != NULL) {
301 attrs[attrList.count].tag = kSecCommentItemAttr;
302 attrs[attrList.count].length = strlen(comment);
303 attrs[attrList.count].data = (void *)comment;
304 attrList.count++;
305 }
306 if (label != NULL) {
307 attrs[attrList.count].tag = kSecLabelItemAttr;
308 attrs[attrList.count].length = strlen(label);
309 attrs[attrList.count].data = (void *)label;
310 attrList.count++;
311 }
312 if (serverName != NULL) {
313 attrs[attrList.count].tag = kSecServerItemAttr;
314 attrs[attrList.count].length = strlen(serverName);
315 attrs[attrList.count].data = (void *)serverName;
316 attrList.count++;
317 }
318 if (securityDomain != NULL) {
319 attrs[attrList.count].tag = kSecSecurityDomainItemAttr;
320 attrs[attrList.count].length = strlen(securityDomain);
321 attrs[attrList.count].data = (void *)securityDomain;
322 attrList.count++;
323 }
324 if (accountName != NULL) {
325 attrs[attrList.count].tag = kSecAccountItemAttr;
326 attrs[attrList.count].length = strlen(accountName);
327 attrs[attrList.count].data = (void *)accountName;
328 attrList.count++;
329 }
330 if (path != NULL) {
331 attrs[attrList.count].tag = kSecPathItemAttr;
332 attrs[attrList.count].length = strlen(path);
333 attrs[attrList.count].data = (void *)path;
334 attrList.count++;
335 }
336 if (port != 0) {
337 attrs[attrList.count].tag = kSecPortItemAttr;
338 attrs[attrList.count].length = sizeof(UInt16);
339 attrs[attrList.count].data = (UInt16 *)&port;
340 attrList.count++;
341 }
342 if ((UInt32)protocol != 0) {
343 attrs[attrList.count].tag = kSecProtocolItemAttr;
344 attrs[attrList.count].length = sizeof(SecProtocolType);
345 attrs[attrList.count].data = (SecProtocolType *)&protocol;
346 attrList.count++;
347 }
348 if ((UInt32)authenticationType != 0) {
349 attrs[attrList.count].tag = kSecAuthenticationTypeItemAttr;
350 attrs[attrList.count].length = sizeof(SecAuthenticationType);
351 attrs[attrList.count].data = (SecAuthenticationType *)&authenticationType;
352 attrList.count++;
353 }
354
355 // modify attributes and password data, if provided
356 status = SecKeychainItemModifyContent(itemRef, &attrList, (passwordData) ? strlen(passwordData) : 0, passwordData);
357 if (status) {
358 sec_error("SecKeychainItemModifyContent: %s", sec_errstr(status));
359 }
360
361 // modify access, if provided
362 if (!status && access) {
363 status = modify_access(itemRef, access);
364 }
365
366 CFRelease(itemRef);
367
368 return status;
369 }
370
371 static int
372 do_add_internet_password(const char *keychainName,
373 FourCharCode itemCreator,
374 FourCharCode itemType,
375 const char *kind,
376 const char *comment,
377 const char *label,
378 const char *serverName,
379 const char *securityDomain,
380 const char *accountName,
381 const char *path,
382 UInt16 port,
383 SecProtocolType protocol,
384 SecAuthenticationType authenticationType,
385 const void *passwordData,
386 SecAccessRef access,
387 Boolean update)
388 {
389 SecKeychainRef keychain = NULL;
390 SecKeychainItemRef itemRef = NULL;
391 OSStatus result = 0;
392
393 // if update flag is specified, try to find and update an existing item
394 if (update) {
395 result = do_update_internet_password(keychainName,itemCreator,itemType,kind,comment,label,serverName,
396 securityDomain,accountName,path,port,protocol,authenticationType,
397 passwordData,access);
398 if (result == noErr)
399 return result;
400 }
401
402 if (keychainName)
403 {
404 keychain = keychain_open(keychainName);
405 if (!keychain)
406 {
407 result = 1;
408 goto cleanup;
409 }
410 }
411
412 // set up attribute vector for new item (each attribute consists of {tag, length, pointer})
413 SecKeychainAttribute attrs[] = {
414 { kSecLabelItemAttr, label ? strlen(label) : 0, (char *)label },
415 { kSecDescriptionItemAttr, kind ? strlen(kind) : 0, (char *)kind },
416 { kSecCommentItemAttr, comment ? strlen(comment) : 0, (char *)comment },
417 { kSecServerItemAttr, serverName ? strlen(serverName) : 0, (char *)serverName },
418 { kSecSecurityDomainItemAttr, securityDomain ? strlen(securityDomain) : 0, (char *)securityDomain },
419 { kSecAccountItemAttr, accountName ? strlen(accountName) : 0, (char *)accountName },
420 { kSecPathItemAttr, path ? strlen(path) : 0, (char *)path },
421 { kSecPortItemAttr, sizeof(UInt16), (UInt16 *)&port },
422 { kSecProtocolItemAttr, sizeof(SecProtocolType), (SecProtocolType *)&protocol },
423 { kSecAuthenticationTypeItemAttr, sizeof(SecAuthenticationType), (SecAuthenticationType *)&authenticationType },
424 { (SecKeychainAttrType)0, sizeof(FourCharCode), NULL }, /* placeholder */
425 { (SecKeychainAttrType)0, sizeof(FourCharCode), NULL } /* placeholder */
426 };
427 SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };
428 attributes.count -= 2;
429
430 if (itemCreator != 0)
431 {
432 attrs[attributes.count].tag = kSecCreatorItemAttr;
433 attrs[attributes.count].data = (FourCharCode *)&itemCreator;
434 attributes.count++;
435 }
436 if (itemType != 0)
437 {
438 attrs[attributes.count].tag = kSecTypeItemAttr;
439 attrs[attributes.count].data = (FourCharCode *)&itemType;
440 attributes.count++;
441 }
442
443 result = SecKeychainItemCreateFromContent(kSecInternetPasswordItemClass,
444 &attributes,
445 passwordData ? strlen(passwordData) : 0,
446 passwordData,
447 keychain,
448 access,
449 &itemRef);
450
451 if (result)
452 {
453 sec_error("SecKeychainAddInternetPassword %s: %s", keychainName ? keychainName : "<NULL>", sec_errstr(result));
454 }
455
456 cleanup:
457 if (itemRef)
458 CFRelease(itemRef);
459 if (keychain)
460 CFRelease(keychain);
461
462 return result;
463 }
464
465 static int
466 do_add_certificates(const char *keychainName, int argc, char * const *argv)
467 {
468 SecKeychainRef keychain = NULL;
469 int ix, result = 0;
470
471 if (keychainName)
472 {
473 keychain = keychain_open(keychainName);
474 if (!keychain)
475 {
476 result = 1;
477 goto cleanup;
478 }
479 }
480
481 for (ix = 0; ix < argc; ++ix)
482 {
483 CSSM_DATA certData = {};
484 OSStatus status;
485 SecCertificateRef certificate = NULL;
486
487 if (read_file(argv[ix], &certData))
488 {
489 result = 1;
490 continue;
491 }
492
493 status = SecCertificateCreateFromData(&certData, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_UNKNOWN, &certificate);
494 if (status)
495 {
496 sec_perror("SecCertificateCreateFromData", status);
497 result = 1;
498 }
499 else
500 {
501 status = SecCertificateAddToKeychain(certificate, keychain);
502 if (status)
503 {
504 if (status == errSecDuplicateItem)
505 {
506 if (keychainName)
507 sec_error("%s: already in %s", argv[ix], keychainName);
508 else
509 sec_error("%s: already in default keychain", argv[ix]);
510 }
511 else
512 {
513 sec_perror("SecCertificateAddToKeychain", status);
514 }
515 result = 1;
516 }
517 }
518
519 if (certData.Data)
520 free(certData.Data);
521 if (certificate)
522 CFRelease(certificate);
523 }
524
525 cleanup:
526 if (keychain)
527 CFRelease(keychain);
528
529 return result;
530 }
531
532 int
533 keychain_add_generic_password(int argc, char * const *argv)
534 {
535 char *serviceName = NULL, *passwordData = NULL, *accountName = NULL;
536 char *kind = NULL, *label = NULL, *value = NULL, *comment = NULL;
537 FourCharCode itemCreator = 0, itemType = 0;
538 int ch, result = 0;
539 const char *keychainName = NULL;
540 Boolean access_specified = FALSE;
541 Boolean always_allow = FALSE;
542 Boolean update_item = FALSE;
543 SecAccessRef access = NULL;
544 CFMutableArrayRef trusted_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
545 OSStatus status;
546
547 /*
548 * " -a Specify account name (required)\n"
549 * " -c Specify item creator (optional four-character code)\n"
550 * " -C Specify item type (optional four-character code)\n"
551 * " -D Specify kind (default is \"application password\")\n"
552 * " -G Specify generic attribute (optional)\n"
553 * " -j Specify comment string (optional)\n"
554 * " -l Specify label (if omitted, service name is used as default label)\n"
555 * " -s Specify service name (required)\n"
556 * " -p Specify password to be added (legacy option, equivalent to -w)\n"
557 * " -w Specify password to be added\n"
558 * " -A Allow any application to access this item without warning (insecure, not recommended!)\n"
559 * " -T Specify an application which may access this item (multiple -T options are allowed)\n"
560 * " -U Update item if it already exists (if omitted, the item cannot already exist)\n"
561 */
562
563 while ((ch = getopt(argc, argv, "a:c:C:D:G:j:l:s:p:w:UAT:")) != -1)
564 {
565 switch (ch)
566 {
567 case 'a':
568 accountName = optarg;
569 break;
570 case 'c':
571 result = parse_fourcharcode(optarg, &itemCreator);
572 if (result) goto cleanup;
573 break;
574 case 'C':
575 result = parse_fourcharcode(optarg, &itemType);
576 if (result) goto cleanup;
577 break;
578 case 'D':
579 kind = optarg;
580 break;
581 case 'G':
582 value = optarg;
583 break;
584 case 'j':
585 comment = optarg;
586 break;
587 case 'l':
588 label = optarg;
589 break;
590 case 's':
591 serviceName = optarg;
592 break;
593 case 'p':
594 case 'w':
595 passwordData = optarg;
596 break;
597 case 'U':
598 update_item = TRUE;
599 break;
600 case 'A':
601 always_allow = TRUE;
602 access_specified = TRUE;
603 break;
604 case 'T':
605 {
606 if (optarg[0])
607 {
608 SecTrustedApplicationRef app = NULL;
609 /* check whether the argument specifies an application group */
610 const char *groupPrefix = "group://";
611 size_t prefixLen = strlen(groupPrefix);
612 if (strlen(optarg) > prefixLen && !memcmp(optarg, groupPrefix, prefixLen)) {
613 const char *groupName = &optarg[prefixLen];
614 if ((status = SecTrustedApplicationCreateApplicationGroup(groupName, NULL, &app)) != noErr) {
615 sec_error("SecTrustedApplicationCreateApplicationGroup %s: %s", optarg, sec_errstr(status));
616 }
617 } else {
618 if ((status = SecTrustedApplicationCreateFromPath(optarg, &app)) != noErr) {
619 sec_error("SecTrustedApplicationCreateFromPath %s: %s", optarg, sec_errstr(status));
620 }
621 }
622
623 if (status) {
624 result = 1;
625 goto cleanup;
626 }
627
628 CFArrayAppendValue(trusted_list, app);
629 CFRelease(app);
630 }
631 access_specified = TRUE;
632 break;
633 }
634 case '?':
635 default:
636 result = 2;
637 goto cleanup; /* @@@ Return 2 triggers usage message. */
638 }
639 }
640
641 argc -= optind;
642 argv += optind;
643
644 if (!accountName || !serviceName)
645 {
646 result = 2;
647 goto cleanup;
648 }
649 else if (argc > 0)
650 {
651 keychainName = argv[0];
652 if (argc > 1 || *keychainName == '\0')
653 {
654 result = 2;
655 goto cleanup;
656 }
657 }
658
659 if (access_specified)
660 {
661 const char *accessName = (label) ? label : (serviceName) ? serviceName : (accountName) ? accountName : "";
662 if ((result = create_access(accessName, always_allow, trusted_list, &access)) != 0)
663 goto cleanup;
664 }
665
666 result = do_add_generic_password(keychainName,
667 itemCreator,
668 itemType,
669 kind,
670 value,
671 comment,
672 (label) ? label : serviceName,
673 serviceName,
674 accountName,
675 passwordData,
676 access,
677 update_item);
678
679 cleanup:
680 if (trusted_list)
681 CFRelease(trusted_list);
682 if (access)
683 CFRelease(access);
684
685 return result;
686 }
687
688 int
689 keychain_add_internet_password(int argc, char * const *argv)
690 {
691 char *serverName = NULL, *securityDomain = NULL, *accountName = NULL, *path = NULL, *passwordData = NULL;
692 char *kind = NULL, *comment = NULL, *label = NULL;
693 FourCharCode itemCreator = 0, itemType = 0;
694 UInt16 port = 0;
695 SecProtocolType protocol = 0;
696 SecAuthenticationType authenticationType = OSSwapHostToBigInt32('dflt');
697 int ch, result = 0;
698 const char *keychainName = NULL;
699 Boolean access_specified = FALSE;
700 Boolean always_allow = FALSE;
701 Boolean update_item = FALSE;
702 SecAccessRef access = NULL;
703 CFMutableArrayRef trusted_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
704 OSStatus status;
705
706 /*
707 * " -a Specify account name (required)\n"
708 * " -c Specify item creator (optional four-character code)\n"
709 * " -C Specify item type (optional four-character code)\n"
710 * " -d Specify security domain string (optional)\n"
711 * " -D Specify kind (default is \"Internet password\")\n"
712 * " -j Specify comment string (optional)\n"
713 * " -l Specify label (if omitted, server name is used as default label)\n"
714 * " -p Specify path string (optional)\n"
715 * " -P Specify port number (optional)\n"
716 * " -r Specify protocol (optional four-character SecProtocolType, e.g. \"http\", \"ftp \")\n"
717 * " -s Specify server name (required)\n"
718 * " -t Specify authentication type (as a four-character SecAuthenticationType, default is \"dflt\")\n"
719 * " -w Specify password to be added\n"
720 * " -A Allow any application to access this item without warning (insecure, not recommended!)\n"
721 * " -T Specify an application which may access this item (multiple -T options are allowed)\n"
722 * " -U Update item if it already exists (if omitted, the item cannot already exist)\n"
723 */
724
725 while ((ch = getopt(argc, argv, "a:c:C:d:D:j:l:p:P:r:s:t:w:UAT:h")) != -1)
726 {
727 switch (ch)
728 {
729 case 'a':
730 accountName = optarg;
731 break;
732 case 'c':
733 result = parse_fourcharcode(optarg, &itemCreator);
734 if (result) goto cleanup;
735 break;
736 case 'C':
737 result = parse_fourcharcode(optarg, &itemType);
738 if (result) goto cleanup;
739 break;
740 case 'd':
741 securityDomain = optarg;
742 break;
743 case 'D':
744 kind = optarg;
745 break;
746 case 'j':
747 comment = optarg;
748 break;
749 case 'l':
750 label = optarg;
751 break;
752 case 'p':
753 path = optarg;
754 break;
755 case 'P':
756 port = atoi(optarg);
757 break;
758 case 'r':
759 result = parse_fourcharcode(optarg, &protocol);
760 if (result) goto cleanup;
761 break;
762 case 's':
763 serverName = optarg;
764 break;
765 case 't':
766 result = parse_fourcharcode(optarg, &authenticationType);
767 if (result) goto cleanup;
768 /* auth type attribute is special */
769 authenticationType = OSSwapHostToBigInt32(authenticationType);
770 break;
771 case 'w':
772 passwordData = optarg;
773 break;
774 case 'U':
775 update_item = TRUE;
776 break;
777 case 'A':
778 always_allow = TRUE;
779 access_specified = TRUE;
780 break;
781 case 'T':
782 {
783 if (optarg[0])
784 {
785 SecTrustedApplicationRef app = NULL;
786 /* check whether the argument specifies an application group */
787 const char *groupPrefix = "group://";
788 size_t prefixLen = strlen(groupPrefix);
789 if (strlen(optarg) > prefixLen && !memcmp(optarg, groupPrefix, prefixLen)) {
790 const char *groupName = &optarg[prefixLen];
791 if ((status = SecTrustedApplicationCreateApplicationGroup(groupName, NULL, &app)) != noErr) {
792 sec_error("SecTrustedApplicationCreateApplicationGroup %s: %s", optarg, sec_errstr(status));
793 }
794 } else {
795 if ((status = SecTrustedApplicationCreateFromPath(optarg, &app)) != noErr) {
796 sec_error("SecTrustedApplicationCreateFromPath %s: %s", optarg, sec_errstr(status));
797 }
798 }
799
800 if (status) {
801 result = 1;
802 goto cleanup;
803 }
804
805 CFArrayAppendValue(trusted_list, app);
806 CFRelease(app);
807 }
808 access_specified = TRUE;
809 break;
810 }
811 case '?':
812 default:
813 result = 2;
814 goto cleanup; /* @@@ Return 2 triggers usage message. */
815 }
816 }
817
818 argc -= optind;
819 argv += optind;
820
821 if (!accountName || !serverName)
822 {
823 result = 2;
824 goto cleanup;
825 }
826 else if (argc > 0)
827 {
828 keychainName = argv[0];
829 if (argc > 1 || *keychainName == '\0')
830 {
831 result = 2;
832 goto cleanup;
833 }
834 }
835
836 if (access_specified)
837 {
838 const char *accessName = (label) ? label : (serverName) ? serverName : (accountName) ? accountName : "";
839 if ((result = create_access(accessName, always_allow, trusted_list, &access)) != 0)
840 goto cleanup;
841 }
842
843 result = do_add_internet_password(keychainName,
844 itemCreator,
845 itemType,
846 kind,
847 comment,
848 (label) ? label : serverName,
849 serverName,
850 securityDomain,
851 accountName,
852 path,
853 port,
854 protocol,
855 authenticationType,
856 passwordData,
857 access,
858 update_item);
859
860 cleanup:
861 if (trusted_list)
862 CFRelease(trusted_list);
863 if (access)
864 CFRelease(access);
865
866 return result;
867 }
868
869 int
870 keychain_add_certificates(int argc, char * const *argv)
871 {
872 int ch, result = 0;
873 const char *keychainName = NULL;
874 while ((ch = getopt(argc, argv, "hk:")) != -1)
875 {
876 switch (ch)
877 {
878 case 'k':
879 keychainName = optarg;
880 if (*keychainName == '\0')
881 return 2;
882 break;
883 case '?':
884 default:
885 return 2; /* @@@ Return 2 triggers usage message. */
886 }
887 }
888
889 argc -= optind;
890 argv += optind;
891
892 if (argc == 0)
893 return 2;
894
895 result = do_add_certificates(keychainName, argc, argv);
896
897 return result;
898 }