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