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. */ 51const char *prompt_string = "security> "; 52 53/* The name of this program. */ 54const char *prog_name; 55 56 57/* Forward declarations of static functions. */ 58 59 60/* Global variables. */ 61int do_quiet = 0; 62int do_verbose = 0; 63 64/* Return 1 if name matches command. */ 65static int 66match_command(const char *command_name, const char *name) 67{ 68 return !strncmp(command_name, name, strlen(name)); 69} 70 71/* The help command. */ 72int 73help(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. */ 112typedef 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. */ 122static void 123split_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. */ 221static int 222usage(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. */ 237static int 238execute_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 287static void 288receive_notifications(void) 289{ 290 /* Run the CFRunloop to get any pending notifications. */ 291 while (CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, TRUE) == kCFRunLoopRunHandledSource); 292} 293 294int 295main(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} 406