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