--- /dev/null
+/*
+ * Copyright (c) 2003-2019 Apple Inc. All Rights Reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ *
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ *
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ *
+ * @APPLE_LICENSE_HEADER_END@
+ *
+ * security.c
+ */
+
+#include "security_tool.h"
+
+#include "leaks.h"
+#include "readline_cssm.h"
+
+#include "cmsutil.h"
+#include "db_commands.h"
+#include "keychain_add.h"
+#include "keychain_create.h"
+#include "keychain_delete.h"
+#include "keychain_list.h"
+#include "keychain_lock.h"
+#include "keychain_set_settings.h"
+#include "keychain_show_info.h"
+#include "keychain_unlock.h"
+#include "keychain_recode.h"
+#include "key_create.h"
+#include "keychain_find.h"
+#include "keychain_import.h"
+#include "keychain_export.h"
+#include "identity_find.h"
+#include "identity_prefs.h"
+#include "mds_install.h"
+#include "trusted_cert_add.h"
+#include "trusted_cert_dump.h"
+#include "user_trust_enable.h"
+#include "trust_settings_impexp.h"
+#include "verify_cert.h"
+#include "authz.h"
+#include "smartcards.h"
+#include "display_error_code.h"
+#include "createFVMaster.h"
+#include "smartcards.h"
+#include "translocate.h"
+#include "requirement.h"
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <dispatch/dispatch.h>
+
+#include <CoreFoundation/CFRunLoop.h>
+#include <Security/SecBasePriv.h>
+#include <Security/SecKeychainPriv.h>
+#include <security_asn1/secerr.h>
+
+/* Maximum length of an input line in interactive mode. */
+#define MAX_LINE_LEN 4096
+/* Maximum number of arguments on an input line in interactive mode. */
+#define MAX_ARGS 32
+
+/* Entry in commands array for a command. */
+typedef struct command
+{
+ const char *c_name; /* name of the command. */
+ command_func c_func; /* function to execute the command. */
+ const char *c_usage; /* usage sting for command. */
+ const char *c_help; /* help string for (or description of) command. */
+} command;
+
+/* The default prompt. */
+const char *prompt_string = "security> ";
+
+/* Forward declarations of static functions. */
+static int help(int argc, char * const *argv);
+
+/*
+ * The command array itself.
+ * Add commands here at will.
+ * Matching is done on a prefix basis. The first command in the array
+ * gets matched first.
+ */
+const command commands[] =
+{
+ { "help", help,
+ "[command ...]",
+ "Show all commands, or show usage for a command." },
+
+ { "list-keychains", keychain_list,
+ "[-d user|system|common|dynamic] [-s [keychain...]]\n"
+ " -d Use the specified preference domain\n"
+ " -s Set the search list to the specified keychains\n"
+ "With no parameters, display the search list.",
+ "Display or manipulate the keychain search list." },
+
+ { "list-smartcards", ctk_list,
+ "Display IDs of available smartcards.",
+ "Display available smartcards." },
+
+ { "default-keychain", keychain_default,
+ "[-d user|system|common|dynamic] [-s [keychain]]\n"
+ " -d Use the specified preference domain\n"
+ " -s Set the default keychain to the specified keychain\n"
+ "With no parameters, display the default keychain.",
+ "Display or set the default keychain." },
+
+ { "login-keychain", keychain_login,
+ "[-d user|system|common|dynamic] [-s [keychain]]\n"
+ " -d Use the specified preference domain\n"
+ " -s Set the login keychain to the specified keychain\n"
+ "With no parameters, display the login keychain.",
+ "Display or set the login keychain." },
+
+ { "create-keychain", keychain_create,
+ "[-P] [-p password] [keychains...]\n"
+ " -p Use \"password\" as the password for the keychains being created\n"
+ " -P Prompt the user for a password using the SecurityAgent\n"
+ "Use of the -p option is insecure",
+ "Create keychains and add them to the search list.",
+ },
+
+ { "delete-keychain", keychain_delete,
+ "[keychains...]",
+ "Delete keychains and remove them from the search list." },
+
+ { "lock-keychain", keychain_lock,
+ "[-a | keychain]\n"
+ " -a Lock all keychains",
+ "Lock the specified keychain."},
+
+ { "unlock-keychain", keychain_unlock,
+ "[-u] [-p password] [keychain]\n"
+ " -p Use \"password\" as the password to unlock the keychain\n"
+ " -u Do not use the password\n"
+ "Use of the -p option is insecure",
+ "Unlock the specified keychain."},
+
+ { "set-keychain-settings", keychain_set_settings,
+ "[-lu] [-t timeout] [keychain]\n"
+ " -l Lock keychain when the system sleeps\n"
+ " -u Lock keychain after timeout interval\n"
+ " -t Timeout in seconds (omitting this option specifies \"no timeout\")\n",
+ "Set settings for a keychain."},
+
+ { "set-keychain-password", keychain_set_password,
+ "[-o oldPassword] [-p newPassword] [keychain]\n"
+ " -o Old keychain password (if not provided, will prompt)\n"
+ " -p New keychain password (if not provided, will prompt)\n"
+ "Use of either the -o or -p options is insecure\n",
+ "Set password for a keychain."},
+
+ { "show-keychain-info", keychain_show_info,
+ "[keychain]",
+ "Show the settings for keychain." },
+
+ { "dump-keychain", keychain_dump,
+ "[-adir] [keychain...]\n"
+ " -a Dump access control list of items\n"
+ " -d Dump (decrypted) data of items\n"
+ " -i Interactive access control list editing mode\n"
+ " -r Dump the raw (encrypted) data of items",
+ "Dump the contents of one or more keychains." },
+
+#ifndef NDEBUG
+ { "recode-keychain", keychain_recode,
+ "keychain_to_recode keychain_to_get_secrets_from",
+ "Recode a keychain to use the secrets from another one."},
+#endif
+
+ { "create-keypair", key_create_pair,
+ "[-a alg] [-s size] [-f date] [-t date] [-d days] [-k keychain] [-A|-T appPath] description\n"
+ " -a Use alg as the algorithm, can be rsa, dh, dsa or fee (default rsa)\n"
+ " -s Specify the keysize in bits (default 512)\n"
+ " -f Make a key valid from the specified date\n"
+ " -t Make a key valid to the specified date\n"
+ " -d Make a key valid for the number of days specified from today\n"
+ " -k Use the specified keychain rather than the default\n"
+ " -A Allow any application to access this key without warning (insecure, not recommended!)\n"
+ " -T Specify an application which may access this key (multiple -T options are allowed)\n"
+ "If no options are provided, ask the user interactively.",
+ "Create an asymmetric key pair." },
+
+ #if 0
+ /* this was added in mb's integration of PR-3420772, but this is an unimplemented command */
+ { "create-csr", csr_create,
+ "[-a alg] [-s size] [-f date] [-t date] [-d days] [-k keychain] [-A|-T appPath] description\n"
+ " -a Use alg as the algorithm, can be rsa, dh, dsa or fee (default rsa)\n"
+ " -s Specify the keysize in bits (default 512)\n"
+ " -f Make a key valid from the specified date\n"
+ " -t Make a key valid to the specified date\n"
+ " -d Make a key valid for the number of days specified from today\n"
+ " -k Use the specified keychain rather than the default\n"
+ " -A Allow any application to access this key without warning (insecure, not recommended!)\n"
+ " -T Specify an application which may access this key (multiple -T options are allowed)\n"
+ "If no options are provided, ask the user interactively.",
+ "Create a certificate signing request." },
+ #endif
+
+ { "add-generic-password", keychain_add_generic_password,
+ "[-a account] [-s service] [-w password] [options...] [-A|-T appPath] [keychain]\n"
+ " -a Specify account name (required)\n"
+ " -c Specify item creator (optional four-character code)\n"
+ " -C Specify item type (optional four-character code)\n"
+ " -D Specify kind (default is \"application password\")\n"
+ " -G Specify generic attribute (optional)\n"
+ " -j Specify comment string (optional)\n"
+ " -l Specify label (if omitted, service name is used as default label)\n"
+ " -s Specify service name (required)\n"
+ " -p Specify password to be added (legacy option, equivalent to -w)\n"
+ " -w Specify password to be added\n"
+ " -X Specify password data to be added as a hexadecimal string\n"
+ " -A Allow any application to access this item without warning (insecure, not recommended!)\n"
+ " -T Specify an application which may access this item (multiple -T options are allowed)\n"
+ " -U Update item if it already exists (if omitted, the item cannot already exist)\n"
+ "\n"
+ "By default, the application which creates an item is trusted to access its data without warning.\n"
+ "You can remove this default access by explicitly specifying an empty app pathname: -T \"\"\n"
+ "If no keychain is specified, the password is added to the default keychain.\n"
+ "Use of the -p or -w options is insecure. Specify -w as the last option to be prompted.\n",
+ "Add a generic password item."},
+
+ { "add-internet-password", keychain_add_internet_password,
+ "[-a account] [-s server] [-w password] [options...] [-A|-T appPath] [keychain]\n"
+ " -a Specify account name (required)\n"
+ " -c Specify item creator (optional four-character code)\n"
+ " -C Specify item type (optional four-character code)\n"
+ " -d Specify security domain string (optional)\n"
+ " -D Specify kind (default is \"Internet password\")\n"
+ " -j Specify comment string (optional)\n"
+ " -l Specify label (if omitted, server name is used as default label)\n"
+ " -p Specify path string (optional)\n"
+ " -P Specify port number (optional)\n"
+ " -r Specify protocol (optional four-character SecProtocolType, e.g. \"http\", \"ftp \")\n"
+ " -s Specify server name (required)\n"
+ " -t Specify authentication type (as a four-character SecAuthenticationType, default is \"dflt\")\n"
+ " -w Specify password to be added\n"
+ " -X Specify password data to be added as a hexadecimal string\n"
+ " -A Allow any application to access this item without warning (insecure, not recommended!)\n"
+ " -T Specify an application which may access this item (multiple -T options are allowed)\n"
+ " -U Update item if it already exists (if omitted, the item cannot already exist)\n"
+ "\n"
+ "By default, the application which creates an item is trusted to access its data without warning.\n"
+ "You can remove this default access by explicitly specifying an empty app pathname: -T \"\"\n"
+ "If no keychain is specified, the password is added to the default keychain.\n"
+ "Use of the -p or -w options is insecure. Specify -w as the last option to be prompted.\n",
+ "Add an internet password item."},
+
+ { "add-certificates", keychain_add_certificates,
+ "[-k keychain] file...\n"
+ "If no keychain is specified, the certificates are added to the default keychain.",
+ "Add certificates to a keychain."},
+
+ { "find-generic-password", keychain_find_generic_password,
+ "[-a account] [-s service] [options...] [-g] [keychain...]\n"
+ " -a Match \"account\" string\n"
+ " -c Match \"creator\" (four-character code)\n"
+ " -C Match \"type\" (four-character code)\n"
+ " -D Match \"kind\" string\n"
+ " -G Match \"value\" string (generic attribute)\n"
+ " -j Match \"comment\" string\n"
+ " -l Match \"label\" string\n"
+ " -s Match \"service\" string\n"
+ " -g Display the password for the item found\n"
+ " -w Display only the password on stdout\n"
+ "If no keychains are specified to search, the default search list is used.",
+ "Find a generic password item."},
+
+ { "delete-generic-password", keychain_delete_generic_password,
+ "[-a account] [-s service] [options...] [keychain...]\n"
+ " -a Match \"account\" string\n"
+ " -c Match \"creator\" (four-character code)\n"
+ " -C Match \"type\" (four-character code)\n"
+ " -D Match \"kind\" string\n"
+ " -G Match \"value\" string (generic attribute)\n"
+ " -j Match \"comment\" string\n"
+ " -l Match \"label\" string\n"
+ " -s Match \"service\" string\n"
+ "If no keychains are specified to search, the default search list is used.",
+ "Delete a generic password item."},
+
+ { "set-generic-password-partition-list", keychain_set_generic_password_partition_list,
+ "[-a account] [-s service] [-S partition-list] [-k keychain password] [options...] [keychain]\n"
+ " -a Match \"account\" string\n"
+ " -c Match \"creator\" (four-character code)\n"
+ " -C Match \"type\" (four-character code)\n"
+ " -D Match \"kind\" string\n"
+ " -G Match \"value\" string (generic attribute)\n"
+ " -j Match \"comment\" string\n"
+ " -l Match \"label\" string\n"
+ " -s Match \"service\" string\n"
+ " -S Comma-separated list of allowed partition IDs\n"
+ " -k The password for the keychain (required)\n"
+ "If no keychains are specified to search, the default search list is used.\n"
+ "Use of the -k option is insecure. Omit it to be prompted.\n",
+ "Set the partition list of a generic password item."},
+
+ { "find-internet-password", keychain_find_internet_password,
+ "[-a account] [-s server] [options...] [-g] [keychain...]\n"
+ " -a Match \"account\" string\n"
+ " -c Match \"creator\" (four-character code)\n"
+ " -C Match \"type\" (four-character code)\n"
+ " -d Match \"securityDomain\" string\n"
+ " -D Match \"kind\" string\n"
+ " -j Match \"comment\" string\n"
+ " -l Match \"label\" string\n"
+ " -p Match \"path\" string\n"
+ " -P Match port number\n"
+ " -r Match \"protocol\" (four-character code)\n"
+ " -s Match \"server\" string\n"
+ " -t Match \"authenticationType\" (four-character code)\n"
+ " -g Display the password for the item found\n"
+ " -w Display only the password on stdout\n"
+ "If no keychains are specified to search, the default search list is used.",
+ "Find an internet password item."},
+
+ { "delete-internet-password", keychain_delete_internet_password,
+ "[-a account] [-s server] [options...] [keychain...]\n"
+ " -a Match \"account\" string\n"
+ " -c Match \"creator\" (four-character code)\n"
+ " -C Match \"type\" (four-character code)\n"
+ " -d Match \"securityDomain\" string\n"
+ " -D Match \"kind\" string\n"
+ " -j Match \"comment\" string\n"
+ " -l Match \"label\" string\n"
+ " -p Match \"path\" string\n"
+ " -P Match port number\n"
+ " -r Match \"protocol\" (four-character code)\n"
+ " -s Match \"server\" string\n"
+ " -t Match \"authenticationType\" (four-character code)\n"
+ "If no keychains are specified to search, the default search list is used.",
+ "Delete an internet password item."},
+
+ { "set-internet-password-partition-list", keychain_set_internet_password_partition_list,
+ "[-a account] [-s service] [-S partition-list] [-k keychain password] [options...] [keychain]\n"
+ " -a Match \"account\" string\n"
+ " -c Match \"creator\" (four-character code)\n"
+ " -C Match \"type\" (four-character code)\n"
+ " -d Match \"securityDomain\" string\n"
+ " -D Match \"kind\" string\n"
+ " -j Match \"comment\" string\n"
+ " -l Match \"label\" string\n"
+ " -p Match \"path\" string\n"
+ " -P Match port number\n"
+ " -r Match \"protocol\" (four-character code)\n"
+ " -s Match \"server\" string\n"
+ " -t Match \"authenticationType\" (four-character code)\n"
+ " -S Comma-separated list of allowed partition IDs\n"
+ " -k password for keychain (required)\n"
+
+ "If no keychains are specified to search, the default search list is used.\n"
+ "Use of the -k option is insecure. Omit it to be prompted.\n",
+ "Set the partition list of a internet password item."},
+
+ { "find-key", keychain_find_key,
+ "[options...] [keychain...]\n"
+ " -a Match \"application label\" string\n"
+ " -c Match \"creator\" (four-character code)\n"
+ " -d Match keys that can decrypt\n"
+ " -D Match \"description\" string\n"
+ " -e Match keys that can encrypt\n"
+ " -j Match \"comment\" string\n"
+ " -l Match \"label\" string\n"
+ " -r Match keys that can derive\n"
+ " -s Match keys that can sign\n"
+ " -t Type of key to find: one of \"symmetric\", \"public\", or \"private\"\n"
+ " -u Match keys that can unwrap\n"
+ " -v Match keys that can verify\n"
+ " -w Match keys that can wrap\n"
+
+ "If no keychains are specified to search, the default search list is used.",
+ "Find keys in the keychain"},
+
+ { "set-key-partition-list", keychain_set_key_partition_list,
+ "[options...] [keychain]\n"
+ " -a Match \"application label\" string\n"
+ " -c Match \"creator\" (four-character code)\n"
+ " -d Match keys that can decrypt\n"
+ " -D Match \"description\" string\n"
+ " -e Match keys that can encrypt\n"
+ " -j Match \"comment\" string\n"
+ " -l Match \"label\" string\n"
+ " -r Match keys that can derive\n"
+ " -s Match keys that can sign\n"
+ " -t Type of key to find: one of \"symmetric\", \"public\", or \"private\"\n"
+ " -u Match keys that can unwrap\n"
+ " -v Match keys that can verify\n"
+ " -w Match keys that can wrap\n"
+ " -S Comma-separated list of allowed partition IDs\n"
+ " -k password for keychain (required)\n"
+
+ "If no keychains are specified to search, the default search list is used.",
+ "Set the partition list of a key."},
+
+ { "find-certificate", keychain_find_certificate,
+ "[-a] [-c name] [-e emailAddress] [-m] [-p] [-Z] [keychain...]\n"
+ " -a Find all matching certificates, not just the first one\n"
+ " -c Match on \"name\" when searching (optional)\n"
+ " -e Match on \"emailAddress\" when searching (optional)\n"
+ " -m Show the email addresses in the certificate\n"
+ " -p Output certificate in pem format\n"
+ " -Z Print SHA-256 (and SHA-1) hash of the certificate\n"
+ "If no keychains are specified to search, the default search list is used.",
+ "Find a certificate item."},
+
+ { "find-identity", keychain_find_identity,
+ "[-p policy] [-s string] [-v] [keychain...]\n"
+ " -p Specify policy to evaluate (multiple -p options are allowed)\n"
+ " Supported policies: basic, ssl-client, ssl-server, smime, eap,\n"
+ " ipsec, ichat, codesigning, sys-default, sys-kerberos-kdc, macappstore, appleID\n"
+ " -s Specify optional policy-specific string (e.g. DNS hostname for SSL,\n"
+ " or RFC822 email address for S/MIME)\n"
+ " -v Show valid identities only (default is to show all identities)\n"
+ "If no keychains are specified to search, the default search list is used.",
+ "Find an identity (certificate + private key)."},
+
+ { "delete-certificate", keychain_delete_certificate,
+ "[-c name] [-Z hash] [-t] [keychain...]\n"
+ " -c Specify certificate to delete by its common name\n"
+ " -Z Specify certificate to delete by its SHA-256 (or SHA-1) hash value\n"
+ " -t Also delete user trust settings for this certificate\n"
+ "The certificate to be deleted must be uniquely specified either by a\n"
+ "string found in its common name, or by its SHA-256 (or SHA-1) hash.\n"
+ "If no keychains are specified to search, the default search list is used.",
+ "Delete a certificate from a keychain."},
+
+ { "delete-identity", keychain_delete_identity,
+ "[-c name] [-Z hash] [-t] [keychain...]\n"
+ " -c Specify certificate to delete by its common name\n"
+ " -Z Specify certificate to delete by its SHA-256 (or SHA-1) hash value\n"
+ " -t Also delete user trust settings for this identity certificate\n"
+ "The identity to be deleted must be uniquely specified either by a\n"
+ "string found in its common name, or by its SHA-256 (or SHA-1) hash.\n"
+ "If no keychains are specified to search, the default search list is used.",
+ "Delete an identity (certificate + private key) from a keychain."},
+
+ { "set-identity-preference", set_identity_preference,
+ "[-n] [-c identity] [-s service] [-u keyUsage] [-Z hash] [keychain...]\n"
+ " -n Specify no identity (clears existing preference for service)\n"
+ " -c Specify identity by common name of the certificate\n"
+ " -s Specify service (may be a URL, RFC822 email address, DNS host, or\n"
+ " other name) for which this identity is to be preferred\n"
+ " -u Specify key usage (optional) - see man page for values\n"
+ " -Z Specify identity by SHA-256 (or SHA-1) hash of certificate (optional)\n",
+ "Set the preferred identity to use for a service."},
+
+ { "get-identity-preference", get_identity_preference,
+ "[-s service] [-u keyUsage] [-p] [-c] [-Z] [keychain...]\n"
+ " -s Specify service (may be a URL, RFC822 email address, DNS host, or\n"
+ " other name)\n"
+ " -u Specify key usage (optional) - see man page for values\n"
+ " -p Output identity certificate in pem format\n"
+ " -c Print common name of the preferred identity certificate\n"
+ " -Z Print SHA-256 (and SHA-1) hash of the preferred identity certificate\n",
+ "Get the preferred identity to use for a service."},
+
+ { "create-db", db_create,
+ "[-ao0] [-g dl|cspdl] [-m mode] [name]\n"
+ " -a Turn off autocommit\n"
+ " -g Attach to \"guid\" rather than the AppleFileDL\n"
+ " -m Set the inital mode of the created db to \"mode\"\n"
+ " -o Force using openparams argument\n"
+ " -0 Force using version 0 openparams\n"
+ "If no name is provided, ask the user interactively.",
+ "Create a db using the DL." },
+
+ { "export" , keychain_export,
+ "[-k keychain] [-t type] [-f format] [-w] [-p] [-P passphrase] [-o outfile]\n"
+ " -k keychain to export items from\n"
+ " -t Type = certs|allKeys|pubKeys|privKeys|identities|all (Default: all)\n"
+ " -f Format = openssl|openssh1|openssh2|bsafe|pkcs7|pkcs8|pkcs12|pemseq|x509\n"
+ " ...default format is pemseq for aggregate, openssl for single\n"
+ " -w Private keys are wrapped\n"
+ " -p PEM encode the output\n"
+ " -P Specify wrapping passphrase immediately (default is secure passphrase via GUI)\n"
+ " -o Specify output file (default is stdout)\n"
+ "Use of the -P option is insecure\n",
+ "Export items from a keychain." },
+
+ { "import", keychain_import,
+ "inputfile [-k keychain] [-t type] [-f format] [-w] [-P passphrase] [options...]\n"
+ " -k Target keychain to import into\n"
+ " -t Type = pub|priv|session|cert|agg\n"
+ " -f Format = openssl|openssh1|openssh2|bsafe|raw|pkcs7|pkcs8|pkcs12|netscape|pemseq\n"
+ " -w Specify that private keys are wrapped and must be unwrapped on import\n"
+ " -x Specify that private keys are non-extractable after being imported\n"
+ " -P Specify wrapping passphrase immediately (default is secure passphrase via GUI)\n"
+ " -a Specify name and value of extended attribute (can be used multiple times)\n"
+ " -A Allow any application to access the imported key without warning (insecure, not recommended!)\n"
+ " -T Specify an application which may access the imported key (multiple -T options are allowed)\n"
+ "Use of the -P option is insecure\n",
+ "Import items into a keychain." },
+
+ { "export-smartcard" , ctk_export,
+ "[-i id] [-t type] [-e exportPath] \n"
+ " -i id of the smartcard to export (available IDs can be listed by list-smartcards\n"
+ " command, default: export/display all smartcards)\n"
+ " -t Type = certs|privKeys|identities|all (Default: all)\n"
+ " -e Specify path to export certificates and public keys. This option cannot be combined with -t option.\n",
+ "Export items from a smartcard." },
+
+ { "cms", cms_util,
+ "[-C|-D|-E|-S] [<options>]\n"
+ " -C create a CMS encrypted message\n"
+ " -D decode a CMS message\n"
+ " -E create a CMS enveloped message\n"
+ " -S create a CMS signed message\n"
+ "\n"
+ "Decoding options:\n"
+ " -c content use this detached content file\n"
+ " -h level generate email headers with info about CMS message\n"
+ " (output level >= 0)\n"
+ " -n suppress output of content\n"
+ "\n"
+ "Encoding options:\n"
+ " -r id,... create envelope for these recipients,\n"
+ " where id can be a certificate nickname or email address\n"
+ " -G include a signing time attribute\n"
+ " -H hash hash = MD2|MD4|MD5|SHA1|SHA256|SHA384|SHA512 (default: SHA1)\n"
+ " -N nick use certificate named \"nick\" for signing\n"
+ " -P include a SMIMECapabilities attribute\n"
+ " -T do not include content in CMS message\n"
+ " -Y nick include an EncryptionKeyPreference attribute with certificate\n"
+ " (use \"NONE\" to omit)\n"
+ " -Z hash find a certificate by subject key ID\n"
+ "\n"
+ "Common options:\n"
+ " -e envelope specify envelope file (valid with -D or -E)\n"
+ " -k keychain specify keychain to use\n"
+ " -i infile use infile as source of data (default: stdin)\n"
+ " -o outfile use outfile as destination of data (default: stdout)\n"
+ " -p password use password as key db password (default: prompt). Using -p is insecure\n"
+ " -s pass data a single byte at a time to CMS\n"
+ " -u certusage set type of certificate usage (default: certUsageEmailSigner)\n"
+ " -v print debugging information\n"
+ "\n"
+ "Cert usage codes:\n"
+ " 0 - certUsageSSLClient\n"
+ " 1 - certUsageSSLServer\n"
+ " 2 - certUsageSSLServerWithStepUp\n"
+ " 3 - certUsageSSLCA\n"
+ " 4 - certUsageEmailSigner\n"
+ " 5 - certUsageEmailRecipient\n"
+ " 6 - certUsageObjectSigner\n"
+ " 7 - certUsageUserCertImport\n"
+ " 8 - certUsageVerifyCA\n"
+ " 9 - certUsageProtectedObjectSigner\n"
+ " 10 - certUsageStatusResponder\n"
+ " 11 - certUsageAnyCA",
+ "Encode or decode CMS messages." },
+
+ { "install-mds" , mds_install,
+ "", /* no options */
+ "Install (or re-install) the MDS database." },
+
+ { "add-trusted-cert" , trusted_cert_add,
+ " [<options>] [certFile]\n"
+ " -d Add to admin cert store; default is user\n"
+ " -r resultType resultType = trustRoot|trustAsRoot|deny|unspecified;\n"
+ " default is trustRoot\n"
+ " -p policy Specify policy constraint (ssl, smime, codeSign, IPSec, iChat,\n"
+ " basic, swUpdate, pkgSign, pkinitClient, pkinitServer, eap)\n"
+ " -a appPath Specify application constraint\n"
+ " -s policyString Specify policy-specific string\n"
+ " -e allowedError Specify allowed error (certExpired, hostnameMismatch) or integer\n"
+ " -u keyUsage Specify key usage, an integer\n"
+ " -k keychain Specify keychain to which cert is added\n"
+ " -i settingsFileIn Input trust settings file; default is user domain\n"
+ " -o settingsFileOut Output trust settings file; default is user domain\n"
+ " certFile Certificate(s)",
+ "Add trusted certificate(s)." },
+
+ { "remove-trusted-cert" , trusted_cert_remove,
+ " [-d] [-D] [certFile]\n"
+ " -d Remove from admin cert store (default is user)\n"
+ " -D Remove default setting instead of per-cert setting\n"
+ " certFile Certificate(s)",
+ "Remove trusted certificate(s)." },
+
+ { "dump-trust-settings" , trusted_cert_dump,
+ " [-s] [-d]\n"
+ " -s Display trusted system certs (default is user)\n"
+ " -d Display trusted admin certs (default is user)\n",
+ "Display contents of trust settings." },
+
+ { "user-trust-settings-enable", user_trust_enable,
+ "[-d] [-e]\n"
+ " -d Disable user-level trust Settings\n"
+ " -e Enable user-level trust Settings\n"
+ "With no parameters, show current enable state of user-level trust settings.",
+ "Display or manipulate user-level trust settings." },
+
+ { "trust-settings-export", trust_settings_export,
+ " [-s] [-d] settings_file\n"
+ " -s Export system trust settings (default is user)\n"
+ " -d Export admin trust settings (default is user)\n",
+ "Export trust settings." },
+
+ { "trust-settings-import", trust_settings_import,
+ " [-d] settings_file\n"
+ " -d Import admin trust settings (default is user)\n",
+ "Import trust settings." },
+
+ { "verify-cert" , verify_cert,
+ "[<options>] [<url>]\n"
+ " -c certFile Certificate to verify. Can be specified multiple times, leaf first.\n"
+ " -r rootCertFile Root Certificate. Can be specified multiple times.\n"
+ " -p policy Verify Policy (basic, ssl, smime, codeSign, IPSec, swUpdate, pkgSign,\n"
+ " eap, appleID, macappstore, timestamping); default is basic.\n"
+ " -C Set client policy to true. Default is server policy. (ssl, IPSec, eap)\n"
+ " -d date Set date and time to use when verifying certificate,\n"
+ " provided in the form of YYYY-MM-DD-hh:mm:ss (time optional) in GMT.\n"
+ " e.g: 2016-04-25-15:59:59 for April 25, 2016 at 3:59:59 pm in GMT\n"
+ " -k keychain Keychain. Can be specified multiple times. Default is default search list.\n"
+ " -n name Name to be verified. (ssl, IPSec, smime)\n"
+ " -N No keychain search list. (For backward compatibility, -n without a\n"
+ " subsequent name argument is interpreted as equivalent to -N.)\n"
+ " -L Local certificates only (do not try to fetch missing CA certs from net).\n"
+ " -l Leaf cert is a CA (normally an error, unless this option is given).\n"
+ " -e emailAddress Email address for smime policy. (Deprecated; use -n instead.)\n"
+ " -s sslHost SSL host name for ssl policy. (Deprecated; use -n instead.)\n"
+ " -q Quiet.\n"
+ " -R revCheckOption Perform revocation checking with one of the following options:\n"
+ " ocsp Check revocation status using OCSP method.\n"
+ " crl Check revocation status using CRL method.\n"
+ " require Require a positive response for successful verification.\n"
+ " offline Consult cached responses only (no network requests).\n"
+ " Can be specified multiple times; e.g. to enable revocation checking\n"
+ " via either OCSP or CRL methods and require a positive response, use\n"
+ " \"-R ocsp -R crl -R require\".\n"
+ " -P Output the constructed certificate chain in PEM format.\n"
+ " -t Output certificate contents as text.\n"
+ " -v Specify verbose output, including per-certificate trust results.\n"
+ "Note: if a direct URL argument is provided, standard SSL server evaluation policy is used\n"
+ "and other certificates or policy options will be ignored.\n",
+ "Verify certificate(s)." },
+
+ { "authorize" , authorize,
+ "[<options>] <right(s)...>\n"
+ " -u Allow user interaction.\n"
+ " -c Use login name and prompt for password.\n"
+ " -C login Use given login name and prompt for password.\n"
+ " -x Do NOT share -c/-C explicit credentials\n"
+#ifndef NDEBUG
+ " -E Don't extend rights.\n"
+#endif
+ " -p Allow returning partial rights.\n"
+ " -d Destroy acquired rights.\n"
+ " -P Pre-authorize rights only.\n"
+ " -l Operate authorizations in least privileged mode.\n"
+ " -i Internalize authref passed on stdin.\n"
+ " -e Externalize authref to stdout.\n"
+ " -w Wait until stdout is closed (to allow reading authref from pipe).\n"
+ "Extend rights flag is passed per default.",
+ "Perform authorization operations." },
+
+ { "authorizationdb" , authorizationdb,
+ "read <right-name>\n"
+ " authorizationdb remove <right-name>\n"
+ " authorizationdb write <right-name> [allow|deny|<rulename>]\n"
+ "If no rulename is specified, write will read a plist from stdin.\n"
+ " authorizationdb merge source [destination]\n"
+ "If no destination path is specified, merge will merge to /etc/authorization.\n"
+ " authorizationdb smartcard <enable|disable|status>\n"
+ "Enables/disables smartcard login support or report current status.",
+ "Make changes to the authorization policy database." },
+
+ { "execute-with-privileges" , execute_with_privileges,
+ "<program> [args...]\n"
+ "On success, stdin will be read and forwarded to the tool.",
+ "Execute tool with privileges." },
+
+ { "leaks", leaks,
+ "[-cycles] [-nocontext] [-nostacks] [-exclude symbol]\n"
+ " -cycles Use a stricter algorithm (\"man leaks\" for details)\n"
+ " -nocontext Withhold hex dumps of the leaked memory\n"
+ " -nostacks Don't show stack traces of leaked memory\n"
+ " -exclude Ignore leaks called from \"symbol\"\n"
+ "(Set the environment variable MallocStackLogging to get symbolic traces.)",
+ "Run /usr/bin/leaks on this process." },
+
+ { "error", display_error_code,
+ "<error code(s)...>\n"
+ "Display an error string for the given security-related error code.\n"
+ "The error can be in decimal or hex, e.g. 1234 or 0x1234. Multiple "
+ "errors can be separated by spaces.",
+ "Display a descriptive message for the given error code(s)." },
+
+ { "create-filevaultmaster-keychain", keychain_createMFV,
+ "[-p password] [keychain name]\n"
+ " -p Use \"password\" as the password for the keychain being created\n"
+ " -s Specify the keysize in bits (default 2048; 1024 & 4096 are allowed)\n"
+ "By default the keychain will be created in ~/Library/Keychains/.\n"
+ "Use of the -p option is insecure. Omit it to be prompted.\n",
+ "Create a keychain containing a key pair for FileVault recovery use."
+ },
+
+ { "smartcards" , smartcards,
+ "token [-l] [-e token] [-d token]\n"
+ " -l List disabled smartcard tokens]\n"
+ " -e token Enable specified token\n"
+ " -d token Disable specified token\n",
+ "Enable, disable or list disabled smartcard tokens." },
+
+ { "translocate-create", translocate_create,
+ "<path to translocate>\n"
+ "Displays the created path or the error returned.",
+ "Create a translocation point for the provided path" },
+
+ { "translocate-policy-check", translocate_policy,
+ "<path to check>\n"
+ "Displays \"Would translocate\" or \"Would not translocate\"\n"
+ "based on the current state of the path and system policy.",
+ "Check whether a path would be translocated." },
+
+ { "translocate-status-check", translocate_check,
+ "<path to check>\n"
+ "Displays \"TRANSLOCATED\" or \"NOT TRANSLOCATED\"\n"
+ "for the given path.",
+ "Check whether a path is translocated." },
+
+ { "translocate-original-path", translocate_original_path,
+ "<path to check>\n"
+ "If the provided path is translocated, display the original path\n"
+ "If the provided path is not translocated, display the passed in path",
+ "Find the original path for a translocated path." },
+
+ { "requirement-evaluate", requirement_evaluate,
+ "<requirements> [<DER certificate file> ...]\n"
+ "Evaluates the given requirement string against the given cert chain.",
+ "Evaluate a requirement against a cert chain." },
+
+ {}
+};
+
+/* Global variables. */
+int do_quiet = 0;
+int do_verbose = 0;
+
+/* Return 1 if name matches command. */
+static int
+match_command(const char *command, const char *name)
+{
+ return !strncmp(command, name, strlen(name));
+}
+
+/* The help command. */
+static int
+help(int argc, char * const *argv)
+{
+ const command *c;
+
+ if (argc > 1)
+ {
+ char * const *arg;
+ for (arg = argv + 1; *arg; ++arg)
+ {
+ int found = 0;
+
+ for (c = commands; c->c_name; ++c)
+ {
+ if (match_command(c->c_name, *arg))
+ {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found)
+ printf("Usage: %s %s\n", c->c_name, c->c_usage);
+ else
+ {
+ sec_error("%s: no such command: %s", argv[0], *arg);
+ return 1;
+ }
+ }
+ }
+ else
+ {
+ for (c = commands; c->c_name; ++c)
+ printf(" %-36s %s\n", c->c_name, c->c_help);
+ }
+
+ return 0;
+}
+
+/* States for split_line parser. */
+typedef enum
+{
+ SKIP_WS,
+ READ_ARG,
+ READ_ARG_ESCAPED,
+ QUOTED_ARG,
+ QUOTED_ARG_ESCAPED
+} parse_state;
+
+/* Split a line into multiple arguments and return them in *pargc and *pargv. */
+static void
+split_line(char *line, int *pargc, char * const **pargv)
+{
+ static char *argvec[MAX_ARGS + 1];
+ int argc = 0;
+ char *ptr = line;
+ char *dst = line;
+ parse_state state = SKIP_WS;
+ int quote_ch = 0;
+
+ for (ptr = line; *ptr; ++ptr)
+ {
+ if (state == SKIP_WS)
+ {
+ if (isspace(*ptr))
+ continue;
+
+ if (*ptr == '"' || *ptr == '\'')
+ {
+ quote_ch = *ptr;
+ state = QUOTED_ARG;
+ argvec[argc] = dst;
+ continue; /* Skip the quote. */
+ }
+ else
+ {
+ state = READ_ARG;
+ argvec[argc] = dst;
+ }
+ }
+
+ if (state == READ_ARG)
+ {
+ if (*ptr == '\\')
+ {
+ state = READ_ARG_ESCAPED;
+ continue;
+ }
+ else if (isspace(*ptr))
+ {
+ /* 0 terminate each arg. */
+ *dst++ = '\0';
+ argc++;
+ state = SKIP_WS;
+ if (argc >= MAX_ARGS)
+ break;
+ }
+ else
+ *dst++ = *ptr;
+ }
+
+ if (state == QUOTED_ARG)
+ {
+ if (*ptr == '\\')
+ {
+ state = QUOTED_ARG_ESCAPED;
+ continue;
+ }
+ if (*ptr == quote_ch)
+ {
+ /* 0 terminate each arg. */
+ *dst++ = '\0';
+ argc++;
+ state = SKIP_WS;
+ if (argc >= MAX_ARGS)
+ break;
+ }
+ else
+ *dst++ = *ptr;
+ }
+
+ if (state == READ_ARG_ESCAPED)
+ {
+ *dst++ = *ptr;
+ state = READ_ARG;
+ }
+
+ if (state == QUOTED_ARG_ESCAPED)
+ {
+ *dst++ = *ptr;
+ state = QUOTED_ARG;
+ }
+ }
+
+ if (state != SKIP_WS)
+ {
+ /* Terminate last arg. */
+ *dst++ = '\0';
+ argc++;
+ }
+
+ /* Teminate arg vector. */
+ argvec[argc] = NULL;
+
+ *pargv = argvec;
+ *pargc = argc;
+}
+
+/* Print a (hopefully) useful usage message. */
+static int
+usage(void)
+{
+ printf(
+ "Usage: %s [-h] [-i] [-l] [-p prompt] [-q] [-v] [command] [opt ...]\n"
+ " -i Run in interactive mode.\n"
+ " -l Run /usr/bin/leaks -nocontext before exiting.\n"
+ " -p Set the prompt to \"prompt\" (implies -i).\n"
+ " -q Be less verbose.\n"
+ " -v Be more verbose about what's going on.\n"
+ "%s commands are:\n", getprogname(), getprogname());
+ help(0, NULL);
+ return SHOW_USAGE_MESSAGE;
+}
+
+/* Execute a single command. */
+static int
+execute_command(int argc, char * const *argv)
+{
+ const command *c;
+ int found = 0;
+
+ /* Nothing to do. */
+ if (argc == 0)
+ return 0;
+
+ for (c = commands; c->c_name; ++c)
+ {
+ if (match_command(c->c_name, argv[0]))
+ {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found)
+ {
+ int result;
+
+ /* Reset getopt for command proc. */
+ optind = 1;
+ optreset = 1;
+
+ if (do_verbose)
+ {
+ int ix;
+
+ fprintf(stderr, "%s", c->c_name);
+ for (ix = 1; ix < argc; ++ix)
+ fprintf(stderr, " \"%s\"", argv[ix]);
+ fprintf(stderr, "\n");
+ }
+
+ result = c->c_func(argc, argv);
+ if (result == 2)
+ fprintf(stderr, "Usage: %s %s\n %s\n", c->c_name, c->c_usage, c->c_help);
+
+ return result;
+ }
+ else
+ {
+ sec_error("unknown command \"%s\"", argv[0]);
+ return 1;
+ }
+}
+
+static void
+receive_notifications(void)
+{
+ /* Run the CFRunloop to get any pending notifications. */
+ while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, TRUE) == kCFRunLoopRunHandledSource);
+}
+
+
+const char *
+sec_errstr(int err)
+{
+ const char *errString;
+ if (IS_SEC_ERROR(err))
+ errString = SECErrorString(err);
+ else
+ errString = cssmErrorString(err);
+ return errString;
+}
+
+void
+sec_error(const char *msg, ...)
+{
+ va_list args;
+
+ fprintf(stderr, "%s: ", getprogname());
+
+ va_start(args, msg);
+ vfprintf(stderr, msg, args);
+ va_end(args);
+
+ fprintf(stderr, "\n");
+}
+
+void
+sec_perror(const char *msg, int err)
+{
+ sec_error("%s: %s", msg, sec_errstr(err));
+}
+
+int
+main(int argc, char * const *argv)
+{
+ int result = 0;
+ int do_help = 0;
+ int do_interactive = 0;
+ int do_leaks = 0;
+ int ch;
+
+
+ /* Do getopt stuff for global options. */
+ optind = 1;
+ optreset = 1;
+ while ((ch = getopt(argc, argv, "hilp:qvR")) != -1)
+ {
+ switch (ch)
+ {
+ case 'h':
+ do_help = 1;
+ break;
+ case 'i':
+ do_interactive = 1;
+ break;
+ case 'l':
+ do_leaks = 1;
+ break;
+ case 'p':
+ do_interactive = 1;
+ prompt_string = optarg;
+ break;
+ case 'q':
+ do_quiet = 1;
+ break;
+ case 'v':
+ do_verbose = 1;
+ break;
+ case 'R':
+ // "Recovery mode", do NOT ask security-checksystem to run when using keychain APIs
+ // NOTE: this is a hidden option (not in the usage message)
+ SecKeychainSystemKeychainCheckWouldDeadlock();
+ break;
+ case '?':
+ default:
+ return usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (do_help)
+ {
+ /* Munge argc/argv so that argv[0] is something. */
+ return help(argc + 1, argv - 1);
+ }
+ else if (argc > 0)
+ {
+ receive_notifications();
+ result = execute_command(argc, argv);
+ receive_notifications();
+ }
+ else if (do_interactive)
+ {
+ /* In interactive mode we just read commands and run them until readline returns NULL. */
+
+ /* Only show prompt string if stdin is a tty. */
+ int show_prompt = isatty(0);
+
+ for (;;)
+ {
+ static char buffer[MAX_LINE_LEN];
+ char * const *av, *input;
+ int ac;
+
+ if (show_prompt)
+ fprintf(stderr, "%s", prompt_string);
+
+ input = readline(buffer, MAX_LINE_LEN);
+ if (!input)
+ break;
+
+ split_line(input, &ac, &av);
+ receive_notifications();
+ result = execute_command(ac, av);
+ receive_notifications();
+ if (result == -1)
+ {
+ result = 0;
+ break;
+ }
+
+ if (result && ! do_quiet)
+ {
+ fprintf(stderr, "%s: returned %d\n", av[0], result);
+ }
+ }
+ }
+ else
+ result = usage();
+
+ if (do_leaks)
+ {
+ char *const argvec[3] = { "leaks", "-nocontext", NULL };
+ leaks(2, argvec);
+ }
+
+ return result;
+}