-/*
- * Copyright (c) 2003-2010,2013-2017 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 "SecurityTool.h"
-#include "SecInternalReleasePriv.h"
-
-#include <SecurityTool/readline.h>
-#include "SecurityTool/security_tool_commands.h"
-
-#include "leaks.h"
-
-#include <ctype.h>
-#include <err.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <CoreFoundation/CFRunLoop.h>
-
-#if NO_SERVER
-#include <securityd/spi.h>
-#endif
-
-/* 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
-
-
-/* The default prompt. */
-const char *prompt_string = "security> ";
-
-/* Forward declarations of static functions. */
-
-
-/* Global variables. */
-int do_quiet = 0;
-int do_verbose = 0;
-
-/* Return 1 if name matches command. */
-static int
-match_command(const char *command_name, const char *name)
-{
- return !strncmp(command_name, name, strlen(name));
-}
-
-/* The help command. */
-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
- {
- fprintf(stderr, "%s: no such command: %s", argv[0], *arg);
- return 1;
- }
- }
- }
- else
- {
- for (c = commands; c->c_name; ++c)
- printf(" %-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)
-{
- 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
- {
- warnx("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);
-}
-
-int
-main(int argc, char * const *argv)
-{
- int result = 0;
- int do_help = 0;
- int do_interactive = 0;
- int do_leaks = 0;
- int ch;
-
- if (!SecIsInternalRelease()) {
- errx(1, "command unavailable");
- }
-
- /* 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 NO_SERVER
-# if DEBUG
-// securityd_init();
-# endif
-#endif
-
- 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;
-}