]> git.saurik.com Git - apple/security.git/blob - OSX/sec/SecurityTool/SecurityTool.c
Security-58286.270.3.0.1.tar.gz
[apple/security.git] / OSX / sec / SecurityTool / 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/readline.h>
30 #include "SecurityTool/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 <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 }