2 * Copyright (c) 2003-2009,2011-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 #include "keychain_add.h"
27 #include "keychain_find.h"
30 #include "access_utils.h"
31 #include "keychain_utilities.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>
43 // SecTrustedApplicationCreateApplicationGroup
44 #include <Security/SecTrustedApplicationPriv.h>
48 do_update_generic_password(const char *keychainName
,
49 FourCharCode itemCreator
,
50 FourCharCode itemType
,
55 const char *serviceName
,
56 const char *accountName
,
57 const void *passwordData
,
61 SecKeychainRef keychainRef
= NULL
;
62 SecKeychainItemRef itemRef
= NULL
;
65 keychainRef
= keychain_open(keychainName
);
67 itemRef
= find_first_generic_password(keychainRef
,itemCreator
,itemType
,kind
,value
,comment
,label
,serviceName
,accountName
);
69 CFRelease(keychainRef
);
72 return errSecItemNotFound
;
75 // build list of attributes
76 SecKeychainAttribute attrs
[8]; // maximum number of attributes
77 SecKeychainAttributeList attrList
= { 0, attrs
};
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
;
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
;
92 attrs
[attrList
.count
].tag
= kSecDescriptionItemAttr
;
93 attrs
[attrList
.count
].length
= strlen(kind
);
94 attrs
[attrList
.count
].data
= (void *)kind
;
98 attrs
[attrList
.count
].tag
= kSecGenericItemAttr
;
99 attrs
[attrList
.count
].length
= strlen(value
);
100 attrs
[attrList
.count
].data
= (void *)value
;
103 if (comment
!= NULL
) {
104 attrs
[attrList
.count
].tag
= kSecCommentItemAttr
;
105 attrs
[attrList
.count
].length
= strlen(comment
);
106 attrs
[attrList
.count
].data
= (void *)comment
;
110 attrs
[attrList
.count
].tag
= kSecLabelItemAttr
;
111 attrs
[attrList
.count
].length
= strlen(label
);
112 attrs
[attrList
.count
].data
= (void *)label
;
115 if (serviceName
!= NULL
) {
116 attrs
[attrList
.count
].tag
= kSecServiceItemAttr
;
117 attrs
[attrList
.count
].length
= strlen(serviceName
);
118 attrs
[attrList
.count
].data
= (void *)serviceName
;
121 if (accountName
!= NULL
) {
122 attrs
[attrList
.count
].tag
= kSecAccountItemAttr
;
123 attrs
[attrList
.count
].length
= strlen(accountName
);
124 attrs
[attrList
.count
].data
= (void *)accountName
;
128 // modify attributes and password data, if provided
129 status
= SecKeychainItemModifyContent(itemRef
, &attrList
, (passwordData
) ? strlen(passwordData
) : 0, passwordData
);
131 sec_error("SecKeychainItemModifyContent: %s", sec_errstr(status
));
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
);
141 sec_error("SecKeychainItemCopyAccess: %s", sec_errstr(status
));
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
147 sec_error("SecKeychainItemSetAccess: %s", sec_errstr(status
));
152 CFRelease(curAccess
);
162 do_add_generic_password(const char *keychainName
,
163 FourCharCode itemCreator
,
164 FourCharCode itemType
,
169 const char *serviceName
,
170 const char *accountName
,
171 const void *passwordData
,
175 SecKeychainRef keychain
= NULL
;
177 SecKeychainItemRef itemRef
= NULL
;
179 // if update flag is specified, try to find and update an existing item
181 result
= do_update_generic_password(keychainName
,itemCreator
,itemType
,kind
,value
,comment
,label
,serviceName
,accountName
,passwordData
,access
);
188 keychain
= keychain_open(keychainName
);
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 */
207 SecKeychainAttributeList attributes
= { sizeof(attrs
) / sizeof(attrs
[0]), attrs
};
208 attributes
.count
-= 2;
210 if (itemCreator
!= 0)
212 attrs
[attributes
.count
].tag
= kSecCreatorItemAttr
;
213 attrs
[attributes
.count
].data
= (FourCharCode
*)&itemCreator
;
218 attrs
[attributes
.count
].tag
= kSecTypeItemAttr
;
219 attrs
[attributes
.count
].data
= (FourCharCode
*)&itemType
;
223 result
= SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass
,
225 passwordData
? strlen(passwordData
) : 0,
233 sec_error("SecKeychainItemCreateFromContent (%s): %s", keychainName
? keychainName
: "<default>", sec_errstr(result
));
246 do_update_internet_password(const char *keychainName
,
247 FourCharCode itemCreator
,
248 FourCharCode itemType
,
252 const char *serverName
,
253 const char *securityDomain
,
254 const char *accountName
,
257 SecProtocolType protocol
,
258 SecAuthenticationType authenticationType
,
259 const void *passwordData
,
263 SecKeychainRef keychainRef
= NULL
;
264 SecKeychainItemRef itemRef
= NULL
;
267 keychainRef
= keychain_open(keychainName
);
269 itemRef
= find_first_internet_password(keychainRef
,itemCreator
,itemType
,kind
,comment
,label
,serverName
,
270 securityDomain
,accountName
,path
,port
,protocol
,authenticationType
);
272 CFRelease(keychainRef
);
275 return errSecItemNotFound
;
278 // build list of attributes
279 SecKeychainAttribute attrs
[12]; // maximum number of attributes
280 SecKeychainAttributeList attrList
= { 0, attrs
};
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
;
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
;
295 attrs
[attrList
.count
].tag
= kSecDescriptionItemAttr
;
296 attrs
[attrList
.count
].length
= strlen(kind
);
297 attrs
[attrList
.count
].data
= (void *)kind
;
300 if (comment
!= NULL
) {
301 attrs
[attrList
.count
].tag
= kSecCommentItemAttr
;
302 attrs
[attrList
.count
].length
= strlen(comment
);
303 attrs
[attrList
.count
].data
= (void *)comment
;
307 attrs
[attrList
.count
].tag
= kSecLabelItemAttr
;
308 attrs
[attrList
.count
].length
= strlen(label
);
309 attrs
[attrList
.count
].data
= (void *)label
;
312 if (serverName
!= NULL
) {
313 attrs
[attrList
.count
].tag
= kSecServerItemAttr
;
314 attrs
[attrList
.count
].length
= strlen(serverName
);
315 attrs
[attrList
.count
].data
= (void *)serverName
;
318 if (securityDomain
!= NULL
) {
319 attrs
[attrList
.count
].tag
= kSecSecurityDomainItemAttr
;
320 attrs
[attrList
.count
].length
= strlen(securityDomain
);
321 attrs
[attrList
.count
].data
= (void *)securityDomain
;
324 if (accountName
!= NULL
) {
325 attrs
[attrList
.count
].tag
= kSecAccountItemAttr
;
326 attrs
[attrList
.count
].length
= strlen(accountName
);
327 attrs
[attrList
.count
].data
= (void *)accountName
;
331 attrs
[attrList
.count
].tag
= kSecPathItemAttr
;
332 attrs
[attrList
.count
].length
= strlen(path
);
333 attrs
[attrList
.count
].data
= (void *)path
;
337 attrs
[attrList
.count
].tag
= kSecPortItemAttr
;
338 attrs
[attrList
.count
].length
= sizeof(UInt16
);
339 attrs
[attrList
.count
].data
= (UInt16
*)&port
;
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
;
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
;
355 // modify attributes and password data, if provided
356 status
= SecKeychainItemModifyContent(itemRef
, &attrList
, (passwordData
) ? strlen(passwordData
) : 0, passwordData
);
358 sec_error("SecKeychainItemModifyContent: %s", sec_errstr(status
));
361 // modify access, if provided
362 if (!status
&& access
) {
363 status
= modify_access(itemRef
, access
);
372 do_add_internet_password(const char *keychainName
,
373 FourCharCode itemCreator
,
374 FourCharCode itemType
,
378 const char *serverName
,
379 const char *securityDomain
,
380 const char *accountName
,
383 SecProtocolType protocol
,
384 SecAuthenticationType authenticationType
,
385 const void *passwordData
,
389 SecKeychainRef keychain
= NULL
;
390 SecKeychainItemRef itemRef
= NULL
;
393 // if update flag is specified, try to find and update an existing item
395 result
= do_update_internet_password(keychainName
,itemCreator
,itemType
,kind
,comment
,label
,serverName
,
396 securityDomain
,accountName
,path
,port
,protocol
,authenticationType
,
397 passwordData
,access
);
404 keychain
= keychain_open(keychainName
);
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 */
427 SecKeychainAttributeList attributes
= { sizeof(attrs
) / sizeof(attrs
[0]), attrs
};
428 attributes
.count
-= 2;
430 if (itemCreator
!= 0)
432 attrs
[attributes
.count
].tag
= kSecCreatorItemAttr
;
433 attrs
[attributes
.count
].data
= (FourCharCode
*)&itemCreator
;
438 attrs
[attributes
.count
].tag
= kSecTypeItemAttr
;
439 attrs
[attributes
.count
].data
= (FourCharCode
*)&itemType
;
443 result
= SecKeychainItemCreateFromContent(kSecInternetPasswordItemClass
,
445 passwordData
? strlen(passwordData
) : 0,
453 sec_error("SecKeychainAddInternetPassword %s: %s", keychainName
? keychainName
: "<NULL>", sec_errstr(result
));
466 do_add_certificates(const char *keychainName
, int argc
, char * const *argv
)
468 SecKeychainRef keychain
= NULL
;
473 keychain
= keychain_open(keychainName
);
481 for (ix
= 0; ix
< argc
; ++ix
)
483 CSSM_DATA certData
= {};
485 SecCertificateRef certificate
= NULL
;
487 if (read_file(argv
[ix
], &certData
))
493 status
= SecCertificateCreateFromData(&certData
, CSSM_CERT_X_509v3
, CSSM_CERT_ENCODING_UNKNOWN
, &certificate
);
496 sec_perror("SecCertificateCreateFromData", status
);
501 status
= SecCertificateAddToKeychain(certificate
, keychain
);
504 if (status
== errSecDuplicateItem
)
507 sec_error("%s: already in %s", argv
[ix
], keychainName
);
509 sec_error("%s: already in default keychain", argv
[ix
]);
513 sec_perror("SecCertificateAddToKeychain", status
);
522 CFRelease(certificate
);
533 keychain_add_generic_password(int argc
, char * const *argv
)
535 char *serviceName
= NULL
, *passwordData
= NULL
, *accountName
= NULL
;
536 char *kind
= NULL
, *label
= NULL
, *value
= NULL
, *comment
= NULL
;
537 FourCharCode itemCreator
= 0, itemType
= 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
);
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"
563 while ((ch
= getopt(argc
, argv
, "a:c:C:D:G:j:l:s:p:w:UAT:")) != -1)
568 accountName
= optarg
;
571 result
= parse_fourcharcode(optarg
, &itemCreator
);
572 if (result
) goto cleanup
;
575 result
= parse_fourcharcode(optarg
, &itemType
);
576 if (result
) goto cleanup
;
591 serviceName
= optarg
;
595 passwordData
= optarg
;
602 access_specified
= TRUE
;
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
));
618 if ((status
= SecTrustedApplicationCreateFromPath(optarg
, &app
)) != noErr
) {
619 sec_error("SecTrustedApplicationCreateFromPath %s: %s", optarg
, sec_errstr(status
));
628 CFArrayAppendValue(trusted_list
, app
);
631 access_specified
= TRUE
;
637 goto cleanup
; /* @@@ Return 2 triggers usage message. */
644 if (!accountName
|| !serviceName
)
651 keychainName
= argv
[0];
652 if (argc
> 1 || *keychainName
== '\0')
659 if (access_specified
)
661 const char *accessName
= (label
) ? label
: (serviceName
) ? serviceName
: (accountName
) ? accountName
: "";
662 if ((result
= create_access(accessName
, always_allow
, trusted_list
, &access
)) != 0)
666 result
= do_add_generic_password(keychainName
,
672 (label
) ? label
: serviceName
,
681 CFRelease(trusted_list
);
689 keychain_add_internet_password(int argc
, char * const *argv
)
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;
695 SecProtocolType protocol
= 0;
696 SecAuthenticationType authenticationType
= OSSwapHostToBigInt32('dflt');
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
);
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"
725 while ((ch
= getopt(argc
, argv
, "a:c:C:d:D:j:l:p:P:r:s:t:w:UAT:h")) != -1)
730 accountName
= optarg
;
733 result
= parse_fourcharcode(optarg
, &itemCreator
);
734 if (result
) goto cleanup
;
737 result
= parse_fourcharcode(optarg
, &itemType
);
738 if (result
) goto cleanup
;
741 securityDomain
= optarg
;
759 result
= parse_fourcharcode(optarg
, &protocol
);
760 if (result
) goto cleanup
;
766 result
= parse_fourcharcode(optarg
, &authenticationType
);
767 if (result
) goto cleanup
;
768 /* auth type attribute is special */
769 authenticationType
= OSSwapHostToBigInt32(authenticationType
);
772 passwordData
= optarg
;
779 access_specified
= TRUE
;
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
));
795 if ((status
= SecTrustedApplicationCreateFromPath(optarg
, &app
)) != noErr
) {
796 sec_error("SecTrustedApplicationCreateFromPath %s: %s", optarg
, sec_errstr(status
));
805 CFArrayAppendValue(trusted_list
, app
);
808 access_specified
= TRUE
;
814 goto cleanup
; /* @@@ Return 2 triggers usage message. */
821 if (!accountName
|| !serverName
)
828 keychainName
= argv
[0];
829 if (argc
> 1 || *keychainName
== '\0')
836 if (access_specified
)
838 const char *accessName
= (label
) ? label
: (serverName
) ? serverName
: (accountName
) ? accountName
: "";
839 if ((result
= create_access(accessName
, always_allow
, trusted_list
, &access
)) != 0)
843 result
= do_add_internet_password(keychainName
,
848 (label
) ? label
: serverName
,
862 CFRelease(trusted_list
);
870 keychain_add_certificates(int argc
, char * const *argv
)
873 const char *keychainName
= NULL
;
874 while ((ch
= getopt(argc
, argv
, "hk:")) != -1)
879 keychainName
= optarg
;
880 if (*keychainName
== '\0')
885 return 2; /* @@@ Return 2 triggers usage message. */
895 result
= do_add_certificates(keychainName
, argc
, argv
);