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 }