/ SecurityTool / sharedTool / SecurityTool.c
SecurityTool.c
  1  /*
  2   * Copyright (c) 2003-2010,2013-2017 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   * security.c
 24   */
 25  
 26  #include "SecurityTool.h"
 27  #include "SecInternalReleasePriv.h"
 28  
 29  #include "SecurityTool/sharedTool/readline.h"
 30  #include "SecurityTool/sharedTool/security_tool_commands.h"
 31  
 32  #include "leaks.h"
 33  
 34  #include <ctype.h>
 35  #include <err.h>
 36  #include <stdio.h>
 37  #include <stdlib.h>
 38  #include <string.h>
 39  #include <unistd.h>
 40  
 41  #include <CoreFoundation/CFRunLoop.h>
 42  
 43  #if NO_SERVER
 44  #include "keychain/securityd/spi.h"
 45  #endif
 46  
 47  /* Maximum length of an input line in interactive mode. */
 48  #define MAX_LINE_LEN 4096
 49  /* Maximum number of arguments on an input line in interactive mode. */
 50  #define MAX_ARGS 32
 51  
 52  
 53  /* The default prompt. */
 54  const char *prompt_string = "security> ";
 55  
 56  /* Forward declarations of static functions. */
 57  
 58  
 59  /* Global variables. */
 60  int do_quiet = 0;
 61  int do_verbose = 0;
 62  
 63  /* Return 1 if name matches command. */
 64  static int
 65  match_command(const char *command_name, const char *name)
 66  {
 67  	return !strncmp(command_name, name, strlen(name));
 68  }
 69  
 70  /* The help command. */
 71  int
 72  help(int argc, char * const *argv)
 73  {
 74  	const command *c;
 75  
 76  	if (argc > 1)
 77  	{
 78  		char * const *arg;
 79  		for (arg = argv + 1; *arg; ++arg)
 80  		{
 81  			int found = 0;
 82  
 83  			for (c = commands; c->c_name; ++c)
 84  			{
 85  				if (match_command(c->c_name, *arg))
 86  				{
 87  					found = 1;
 88  					break;
 89  				}
 90  			}
 91  
 92  			if (found)
 93  				printf("Usage: %s %s\n", c->c_name, c->c_usage);
 94  			else
 95  			{
 96  				fprintf(stderr, "%s: no such command: %s", argv[0], *arg);
 97  				return 1;
 98  			}
 99  		}
100  	}
101  	else
102  	{
103  		for (c = commands; c->c_name; ++c)
104  			printf("    %-17s %s\n", c->c_name, c->c_help);
105  	}
106  
107  	return 0;
108  }
109  
110  /* States for split_line parser. */
111  typedef enum
112  {
113  	SKIP_WS,
114  	READ_ARG,
115  	READ_ARG_ESCAPED,
116  	QUOTED_ARG,
117  	QUOTED_ARG_ESCAPED
118  } parse_state;
119  
120  /* Split a line into multiple arguments and return them in *pargc and *pargv. */
121  static void
122  split_line(char *line, int *pargc, char * const **pargv)
123  {
124  	static char *argvec[MAX_ARGS + 1];
125  	int argc = 0;
126  	char *ptr = line;
127  	char *dst = line;
128  	parse_state state = SKIP_WS;
129  	int quote_ch = 0;
130  
131  	for (ptr = line; *ptr; ++ptr)
132  	{
133  		if (state == SKIP_WS)
134  		{
135  			if (isspace(*ptr))
136  				continue;
137  
138  			if (*ptr == '"' || *ptr == '\'')
139  			{
140  				quote_ch = *ptr;
141  				state = QUOTED_ARG;
142  				argvec[argc] = dst;
143  				continue; /* Skip the quote. */
144  			}
145  			else
146  			{
147  				state = READ_ARG;
148  				argvec[argc] = dst;
149  			}
150  		}
151  
152  		if (state == READ_ARG)
153  		{
154  			if (*ptr == '\\')
155  			{
156  				state = READ_ARG_ESCAPED;
157  				continue;
158  			}
159  			else if (isspace(*ptr))
160  			{
161  				/* 0 terminate each arg. */
162  				*dst++ = '\0';
163  				argc++;
164  				state = SKIP_WS;
165  				if (argc >= MAX_ARGS)
166  					break;
167  			}
168  			else
169  				*dst++ = *ptr;
170  		}
171  
172  		if (state == QUOTED_ARG)
173  		{
174  			if (*ptr == '\\')
175  			{
176  				state = QUOTED_ARG_ESCAPED;
177  				continue;
178  			}
179  			if (*ptr == quote_ch)
180  			{
181  				/* 0 terminate each arg. */
182  				*dst++ = '\0';
183  				argc++;
184  				state = SKIP_WS;
185  				if (argc >= MAX_ARGS)
186  					break;
187  			}
188  			else
189  				*dst++ = *ptr;
190  		}
191  
192  		if (state == READ_ARG_ESCAPED)
193  		{
194  			*dst++ = *ptr;
195  			state = READ_ARG;
196  		}
197  
198  		if (state == QUOTED_ARG_ESCAPED)
199  		{
200  			*dst++ = *ptr;
201  			state = QUOTED_ARG;
202  		}
203  	}
204  
205  	if (state != SKIP_WS)
206  	{
207  		/* Terminate last arg. */
208  		*dst++ = '\0';
209  		argc++;
210  	}
211  
212  	/* Teminate arg vector. */
213  	argvec[argc] = NULL;
214  
215  	*pargv = argvec;
216  	*pargc = argc;
217  }
218  
219  /* Print a (hopefully) useful usage message. */
220  static int
221  usage(void)
222  {
223  	printf(
224  		"Usage: %s [-h] [-i] [-l] [-p prompt] [-q] [-v] [command] [opt ...]\n"
225  		"    -i    Run in interactive mode.\n"
226  		"    -l    Run /usr/bin/leaks -nocontext before exiting.\n"
227  		"    -p    Set the prompt to \"prompt\" (implies -i).\n"
228  		"    -q    Be less verbose.\n"
229  		"    -v    Be more verbose about what's going on.\n"
230  		"%s commands are:\n", getprogname(), getprogname());
231  	help(0, NULL);
232  	return SHOW_USAGE_MESSAGE;
233  }
234  
235  /* Execute a single command. */
236  static int
237  execute_command(int argc, char * const *argv)
238  {
239  	const command *c;
240  	int found = 0;
241  
242  	/* Nothing to do. */
243  	if (argc == 0)
244  		return 0;
245  
246  	for (c = commands; c->c_name; ++c)
247  	{
248  		if (match_command(c->c_name, argv[0]))
249  		{
250  			found = 1;
251  			break;
252  		}
253  	}
254  
255  	if (found)
256  	{
257  		int result;
258  
259  		/* Reset getopt for command proc. */
260  		optind = 1;
261  		optreset = 1;
262  
263  		if (do_verbose)
264  		{
265  			int ix;
266  
267  			fprintf(stderr, "%s", c->c_name);
268  			for (ix = 1; ix < argc; ++ix)
269  				fprintf(stderr, " \"%s\"", argv[ix]);
270  			fprintf(stderr, "\n");
271  		}
272  
273  		result = c->c_func(argc, argv);
274  		if (result == 2)
275  			fprintf(stderr, "Usage: %s %s\n        %s\n", c->c_name, c->c_usage, c->c_help);
276  
277  		return result;
278  	}
279  	else
280  	{
281  		warnx("unknown command: %s", argv[0]);
282  		return 1;
283  	}
284  }
285  
286  static void
287  receive_notifications(void)
288  {
289  	/* Run the CFRunloop to get any pending notifications. */
290  	while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, TRUE) == kCFRunLoopRunHandledSource);
291  }
292  
293  int
294  main(int argc, char * const *argv)
295  {
296  	int result = 0;
297  	int do_help = 0;
298  	int do_interactive = 0;
299  	int do_leaks = 0;
300  	int ch;
301  
302  	if (!SecIsInternalRelease()) {
303  		errx(1, "command unavailable");
304  	}
305  
306  	/* Do getopt stuff for global options. */
307  	optind = 1;
308  	optreset = 1;
309  	while ((ch = getopt(argc, argv, "hilp:qv")) != -1)
310  	{
311  		switch  (ch)
312  		{
313  		case 'h':
314  			do_help = 1;
315  			break;
316  		case 'i':
317  			do_interactive = 1;
318  			break;
319  		case 'l':
320  			do_leaks = 1;
321  			break;
322  		case 'p':
323  			do_interactive = 1;
324  			prompt_string = optarg;
325  			break;
326  		case 'q':
327  			do_quiet = 1;
328  			break;
329  		case 'v':
330  			do_verbose = 1;
331  			break;
332  		case '?':
333  		default:
334  			return usage();
335  		}
336  	}
337  
338  	argc -= optind;
339  	argv += optind;
340  
341  #if NO_SERVER
342  # if DEBUG
343  //    securityd_init();
344  # endif
345  #endif
346  
347  	if (do_help)
348  	{
349  		/* Munge argc/argv so that argv[0] is something. */
350  		return help(argc + 1, argv - 1);
351  	}
352  	else if (argc > 0)
353  	{
354  		receive_notifications();
355  		result = execute_command(argc, argv);
356  		receive_notifications();
357  	}
358  	else if (do_interactive)
359  	{
360  		/* In interactive mode we just read commands and run them until readline returns NULL. */
361  
362          /* Only show prompt string if stdin is a tty. */
363          int show_prompt = isatty(0);
364  
365  		for (;;)
366  		{
367  			static char buffer[MAX_LINE_LEN];
368  			char * const *av, *input;
369  			int ac;
370  
371              if (show_prompt)
372                  fprintf(stderr, "%s", prompt_string);
373  
374  			input = readline(buffer, MAX_LINE_LEN);
375  			if (!input)
376  				break;
377  
378  			split_line(input, &ac, &av);
379  			receive_notifications();
380  			result = execute_command(ac, av);
381  			receive_notifications();
382  			if (result == -1)
383  			{
384  				result = 0;
385  				break;
386  			}
387  
388  			if (result && ! do_quiet)
389  			{
390  				fprintf(stderr, "%s: returned %d\n", av[0], result);
391  			}
392  		}
393  	}
394  	else
395  		result = usage();
396  
397  	if (do_leaks)
398  	{
399  		char *const argvec[3] = { "leaks", "-nocontext", NULL };
400  		leaks(2, argvec);
401  	}
402  
403  	return result;
404  }