]> git.saurik.com Git - apple/security.git/blobdiff - SecurityTool/sharedTool/SecurityTool.c
Security-59306.11.20.tar.gz
[apple/security.git] / SecurityTool / sharedTool / SecurityTool.c
diff --git a/SecurityTool/sharedTool/SecurityTool.c b/SecurityTool/sharedTool/SecurityTool.c
new file mode 100644 (file)
index 0000000..847c4e1
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+ * 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/sharedTool/readline.h"
+#include "SecurityTool/sharedTool/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;
+}