]> git.saurik.com Git - apple/security.git/blob - SecurityTool/macOS/keychain_add.c
Security-59306.11.20.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 if (trusted_list)
770 CFRelease(trusted_list);
771 if (access)
772 CFRelease(access);
773
774 return result;
775 }
776
777 int
778 keychain_add_internet_password(int argc, char * const *argv)
779 {
780 char *serverName = NULL, *securityDomain = NULL, *accountName = NULL, *path = NULL, *passwordData = NULL;
781 char *kind = NULL, *comment = NULL, *label = NULL;
782 FourCharCode itemCreator = 0, itemType = 0;
783 UInt16 port = 0;
784 SecProtocolType protocol = 0;
785 SecAuthenticationType authenticationType = OSSwapHostToBigInt32('dflt');
786 int ch, result = 0;
787 uint32_t passwordLength = 0;
788 const char *keychainName = NULL;
789 Boolean access_specified = FALSE;
790 Boolean always_allow = FALSE;
791 Boolean update_item = FALSE;
792 SecAccessRef access = NULL;
793 CFMutableArrayRef trusted_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
794 OSStatus status;
795 bool mustFreePasswordData = false; // if we got it via user prompting
796
797 /*
798 * " -a Specify account name (required)\n"
799 * " -c Specify item creator (optional four-character code)\n"
800 * " -C Specify item type (optional four-character code)\n"
801 * " -d Specify security domain string (optional)\n"
802 * " -D Specify kind (default is \"Internet password\")\n"
803 * " -j Specify comment string (optional)\n"
804 * " -l Specify label (if omitted, server name is used as default label)\n"
805 * " -p Specify path string (optional)\n"
806 * " -P Specify port number (optional)\n"
807 * " -r Specify protocol (optional four-character SecProtocolType, e.g. \"http\", \"ftp \")\n"
808 * " -s Specify server name (required)\n"
809 * " -t Specify authentication type (as a four-character SecAuthenticationType, default is \"dflt\")\n"
810 * " -w Specify password to be added\n"
811 * " -X Specify password data to be added as a hexadecimal string\n"
812 * " -A Allow any application to access this item without warning (insecure, not recommended!)\n"
813 * " -T Specify an application which may access this item (multiple -T options are allowed)\n"
814 * " -U Update item if it already exists (if omitted, the item cannot already exist)\n"
815 */
816
817 while ((ch = getopt(argc, argv, ":a:c:C:d:D:j:l:p:P:r:s:t:w:X:UAT:h")) != -1)
818 {
819 switch (ch)
820 {
821 case 'a':
822 accountName = optarg;
823 break;
824 case 'c':
825 result = parse_fourcharcode(optarg, &itemCreator);
826 if (result) goto cleanup;
827 break;
828 case 'C':
829 result = parse_fourcharcode(optarg, &itemType);
830 if (result) goto cleanup;
831 break;
832 case 'd':
833 securityDomain = optarg;
834 break;
835 case 'D':
836 kind = optarg;
837 break;
838 case 'j':
839 comment = optarg;
840 break;
841 case 'l':
842 label = optarg;
843 break;
844 case 'p':
845 path = optarg;
846 break;
847 case 'P':
848 port = atoi(optarg);
849 break;
850 case 'r':
851 result = parse_fourcharcode(optarg, &protocol);
852 if (result) goto cleanup;
853 break;
854 case 's':
855 serverName = optarg;
856 break;
857 case 't':
858 result = parse_fourcharcode(optarg, &authenticationType);
859 if (result) goto cleanup;
860 /* auth type attribute is special */
861 authenticationType = OSSwapHostToBigInt32(authenticationType);
862 break;
863 case 'w':
864 passwordData = optarg;
865 passwordLength = (passwordData) ? (uint32_t)strlen(passwordData) : 0;
866 break;
867 case 'U':
868 update_item = TRUE;
869 break;
870 case 'A':
871 always_allow = TRUE;
872 access_specified = TRUE;
873 break;
874 case 'T':
875 {
876 if (optarg[0])
877 {
878 SecTrustedApplicationRef app = NULL;
879 /* check whether the argument specifies an application group */
880 const char *groupPrefix = "group://";
881 size_t prefixLen = strlen(groupPrefix);
882 if (strlen(optarg) > prefixLen && !memcmp(optarg, groupPrefix, prefixLen)) {
883 const char *groupName = &optarg[prefixLen];
884 if ((status = SecTrustedApplicationCreateApplicationGroup(groupName, NULL, &app)) != noErr) {
885 sec_error("SecTrustedApplicationCreateApplicationGroup %s: %s", optarg, sec_errstr(status));
886 }
887 } else {
888 if ((status = SecTrustedApplicationCreateFromPath(optarg, &app)) != noErr) {
889 sec_error("SecTrustedApplicationCreateFromPath %s: %s", optarg, sec_errstr(status));
890 }
891 }
892
893 if (status) {
894 result = 1;
895 goto cleanup;
896 }
897
898 CFArrayAppendValue(trusted_list, app);
899 CFRelease(app);
900 }
901 access_specified = TRUE;
902 break;
903 }
904 case 'X':
905 if (convertPasswordData(optarg, &passwordData, &passwordLength)) {
906 mustFreePasswordData = true;
907 break;
908 }
909 sec_error("Unable to convert password data (-X must specify valid hex digits)");
910 result = 2;
911 goto cleanup; /* @@@ Return 2 triggers usage message. */
912 case '?':
913 case ':':
914 // They supplied the -p or -w but with no data, so prompt
915 // This differs from the case where no -p or -w was given, where we set the data to empty
916 if (optopt == 'p' || optopt == 'w') {
917 if (promptForPasswordData(&passwordData)) {
918 passwordLength = (passwordData) ? (uint32_t)strlen(passwordData) : 0;
919 mustFreePasswordData = true;
920 break;
921 } else {
922 result = 1;
923 goto cleanup; /* @@@ Do not trigger usage message, but indicate failure. */
924 }
925 }
926 result = 2;
927 goto cleanup; /* @@@ Return 2 triggers usage message. */
928
929 default:
930 result = 2;
931 goto cleanup; /* @@@ Return 2 triggers usage message. */
932 }
933 }
934
935 argc -= optind;
936 argv += optind;
937
938 if (!accountName || !serverName)
939 {
940 result = 2;
941 goto cleanup;
942 }
943 else if (argc > 0)
944 {
945 keychainName = argv[0];
946 if (argc > 1 || *keychainName == '\0')
947 {
948 result = 2;
949 goto cleanup;
950 }
951 }
952
953 if (access_specified)
954 {
955 const char *accessName = (label) ? label : (serverName) ? serverName : (accountName) ? accountName : "";
956 if ((result = create_access(accessName, always_allow, trusted_list, &access)) != 0)
957 goto cleanup;
958 }
959
960 result = do_add_internet_password(keychainName,
961 itemCreator,
962 itemType,
963 kind,
964 comment,
965 (label) ? label : serverName,
966 serverName,
967 securityDomain,
968 accountName,
969 path,
970 port,
971 protocol,
972 authenticationType,
973 passwordData,
974 passwordLength,
975 access,
976 update_item);
977
978 cleanup:
979 if (mustFreePasswordData)
980 free(passwordData);
981 if (trusted_list)
982 CFRelease(trusted_list);
983 if (access)
984 CFRelease(access);
985
986 return result;
987 }
988
989 int
990 keychain_add_certificates(int argc, char * const *argv)
991 {
992 int ch, result = 0;
993 const char *keychainName = NULL;
994 while ((ch = getopt(argc, argv, "hk:")) != -1)
995 {
996 switch (ch)
997 {
998 case 'k':
999 keychainName = optarg;
1000 if (*keychainName == '\0')
1001 return SHOW_USAGE_MESSAGE;
1002 break;
1003 case '?':
1004 default:
1005 return SHOW_USAGE_MESSAGE;
1006 }
1007 }
1008
1009 argc -= optind;
1010 argv += optind;
1011
1012 if (argc == 0)
1013 return SHOW_USAGE_MESSAGE;
1014
1015 result = do_add_certificates(keychainName, argc, argv);
1016
1017 return result;
1018 }