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