/ SecurityTool / macOS / keychain_add.c
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  }