/* * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.2 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This 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 OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * security.c * security * * Created by Michael Brouwer on Tue May 06 2003. * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. * */ #include "security.h" #include "leaks.h" #include "readline.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 "key_create.h" #include "keychain_find.h" #include #include #include #include #include /* 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> "; /* The name of this program. */ const char *prog_name; /* 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|alternate] [-s [keychain...]]\n" " -d Use the specified domain.\n" " -s Set the searchlist to the specified keychains.\n" "With no parameters display the searchlist.", "Display or manipulate the keychain search list." }, { "default-keychain", keychain_default, "[-d user|system|common|alternate] [-s [keychain]]\n" " -d Use the specified 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|alternate] [-s [keychain]]\n" " -d Use the specified 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.", "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.", "Unlock the specified keychain."}, { "set-keychain-settings", keychain_set_settings, "[-lu] [-t locktimeout] [keychain]\n" " -l Lock keychain when the system sleeps.\n" " -u Lock keychain after certain period of time.\n" " -t Timeout in seconds before the keychain locks.\n", "Set settings for a keychain."}, { "show-keychain-info", keychain_show_info, "[keychain]", "Show the settings for keychain." }, { "dump-keychain", keychain_dump, "[-dr] [keychain...]\n" " -d Dump data of items.\n" " -r Dump the raw (encrypted) data of items.", "Dump the contents of one or more keychains." }, { "create-keypair", key_create_pair, "[-a alg] [-s size] [-f date] [-t date] [-v days] [-k keychain] [-n name] [-A|-T app1:app2:...]\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" " -v 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 without warning.\n" " -T Allow the applications specified to access without warning.\n" "If no options are provided ask the user interactively", "Create an assymetric keypair." }, { "add-internet-password", keychain_add_internet_password, "[-a accountName] [-d securityDomain] [-p path] [-P port] [-r protocol] [-s serverName] [-t authenticationType] [-w passwordData] [keychain]\n" " -a Use \"accountName\".\n" " -d Use \"securityDomain\".\n" " -p Use \"path\".\n" " -P Use \"port\".\n" " -r Use \"protocol\".\n" " -s Use \"serverName\".\n" " -t Use \"authenticationType\".\n" " -w Use passwordData.\n" "If no keychains is specified the password is added to the default keychain.", "Add an internet password item."}, { "add-generic-password", keychain_add_generic_password, "[-a accountName] [-s serviceName] [-p passwordData] [keychain]\n" " -a Use \"accountName\".\n" " -s Use \"serverName\".\n" " -p Use passwordData.\n" "If no keychains is specified the password is added to the default keychain.", "Add a generic password item."}, { "add-certificates", keychain_add_certificates, "[-k keychain] file...\n" "If no keychains is specified the certificates are added to the default keychain.", "Add certificates to a keychain."}, { "find-internet-password", keychain_find_internet_password, "[-a accountName] [-d securityDomain] [-g] [-p path] [-P port] [-r protocol] [-s serverName] [-t authenticationType] [keychain...]\n" " -a Match on \"accountName\" when searching.\n" " -d Match on \"securityDomain\" when searching.\n" " -g Display the password for the item found.\n" " -p Match on \"path\" when searching.\n" " -P Match on \"port\" when searching.\n" " -r Match on \"protocol\" when searching.\n" " -s Match on \"serverName\" when searching.\n" " -t Match on \"authenticationType\" when searching.\n" "If no keychains are specified the default search list is used.", "Find an internet password item."}, { "find-generic-password", keychain_find_generic_password, "[-a accountName] [-s serviceName] [keychain...]\n" " -a Match on \"accountName\" when searching.\n" " -g Display the password for the item found.\n" " -s Match on \"serviceName\" when searching.\n" "If no keychains are specified the default search list is used.", "Find a generic password item."}, { "find-certificate", keychain_find_certificate, "[-a] [-e emailAddress] [-m] [-p] [keychain...]\n" " -a Find all matching certificates, not just the first one.\n" " -e Match on \"emailAddress\" when searching.\n" " -m Show the \"emailAddresses\" in the certificate.\n" " -p Output certificate in pem form.\n" "If no keychains are specified the default search list is used.", "Find a certificate item."}, { "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 an db using the DL." }, { "leaks", leaks, "[-cycles] [-nocontext] [-nostacks] [-exclude symbol]\n" " -cycles Use a stricter algorithm (Man leaks for details).\n" " -nocontext Withhold the hex dumps of the leaked memory.\n" " -nostacks Don't show stack traces fo 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 proccess." }, {} }; /* 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) fprintf(stderr, "Usage: %s %s\n", c->c_name, c->c_usage); else { fprintf(stderr, "%s: no such command: %s\n", argv[0], *arg); return 1; } } } else { for (c = commands; c->c_name; ++c) fprintf(stderr, " %-17s %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) { const char *p = strrchr(prog_name, '/'); prog_name = p ? p + 1 : prog_name; fprintf(stderr, "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", prog_name, prog_name); help(0, NULL); return 2; } /* 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 { fprintf(stderr, "unknown command \"%s\"\n", argv[0]); return 1; } } int main(int argc, char * const *argv) { int result = 0; int do_help = 0; int do_interactive = 0; int do_leaks = 0; int ch; /* Remember my name. */ prog_name = argv[0]; /* Do getopt stuff for global options. */ optind = 1; optreset = 1; while ((ch = getopt(argc, argv, "hilp:qv")) != -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 '?': 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) result = execute_command(argc, argv); else if (do_interactive) { /* In interactive mode we just read commands and run them until readline returns NULL. */ for (;;) { static char buffer[MAX_LINE_LEN]; char * const *av, *input; int ac; fprintf(stderr, "%s", prompt_string); input = readline(buffer, MAX_LINE_LEN); if (!input) break; split_line(input, &ac, &av); result = execute_command(ac, av); 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; }