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