1 /* 2 * Copyright (C) 2015 John Crispin <blogic@openwrt.org> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU Lesser General Public License version 2.1 6 * as published by the Free Software Foundation 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 */ 13 14 #define _GNU_SOURCE 15 #include <fcntl.h> 16 #include <stddef.h> 17 #include <sys/ptrace.h> 18 #include <sys/stat.h> 19 #include <sys/types.h> 20 #include <sys/user.h> 21 #include <sys/wait.h> 22 #include <unistd.h> 23 #include <stdlib.h> 24 #include <stdio.h> 25 #include <errno.h> 26 #include <libgen.h> 27 #include <syslog.h> 28 #include <limits.h> 29 30 #ifndef PTRACE_EVENT_STOP 31 /* PTRACE_EVENT_STOP is defined in linux/ptrace.h, but this header 32 * collides with musl's sys/ptrace.h */ 33 #define PTRACE_EVENT_STOP 128 34 #endif 35 36 #ifndef PTRACE_EVENT_SECCOMP 37 /* undefined with uClibc-ng */ 38 #define PTRACE_EVENT_SECCOMP 7 39 #endif 40 41 #include <libubox/ulog.h> 42 #include <libubox/uloop.h> 43 #include <libubox/blobmsg.h> 44 #include <libubox/blobmsg_json.h> 45 46 #include "../syscall-names.h" 47 48 #define _offsetof(a, b) __builtin_offsetof(a,b) 49 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) 50 51 #if defined (__aarch64__) 52 #include <linux/ptrace.h> 53 #elif defined(__amd64__) 54 #define reg_syscall_nr _offsetof(struct user, regs.orig_rax) 55 #elif defined(__arm__) 56 #include <asm/ptrace.h> /* for PTRACE_SET_SYSCALL */ 57 #define reg_syscall_nr _offsetof(struct user, regs.uregs[7]) 58 # if defined(__ARM_EABI__) 59 # define reg_retval_nr _offsetof(struct user, regs.uregs[0]) 60 # endif 61 #elif defined(__i386__) 62 #define reg_syscall_nr _offsetof(struct user, regs.orig_eax) 63 #elif defined(__mips) 64 # ifndef EF_REG2 65 # define EF_REG2 8 66 # endif 67 #define reg_syscall_nr (EF_REG2 / 4) 68 #elif defined(__PPC__) 69 #define reg_syscall_nr _offsetof(struct user, regs.gpr[0]) 70 #define reg_retval_nr _offsetof(struct user, regs.gpr[3]) 71 #else 72 #error tracing is not supported on this architecture 73 #endif 74 75 enum mode { 76 UTRACE, 77 SECCOMP_TRACE, 78 } mode = UTRACE; 79 80 struct tracee { 81 struct uloop_process proc; 82 int in_syscall; 83 }; 84 85 static struct tracee tracer; 86 static int syscall_count[SYSCALL_COUNT]; 87 static int violation_count; 88 static struct blob_buf b; 89 static int debug; 90 char *json = NULL; 91 int ptrace_restart; 92 93 static void set_syscall(const char *name, int val) 94 { 95 int i; 96 97 for (i = 0; i < SYSCALL_COUNT; i++) { 98 int sc = syscall_index_to_number(i); 99 if (syscall_name(sc) && !strcmp(syscall_name(sc), name)) { 100 syscall_count[i] = val; 101 return; 102 } 103 } 104 } 105 106 struct syscall { 107 int syscall; 108 int count; 109 }; 110 111 static int cmp_count(const void *a, const void *b) 112 { 113 return ((struct syscall*)b)->count - ((struct syscall*)a)->count; 114 } 115 116 static void print_syscalls(int policy, const char *json) 117 { 118 void *c, *d, *e; 119 int i; 120 char *tmp; 121 122 if (mode == UTRACE) { 123 set_syscall("rt_sigaction", 1); 124 set_syscall("sigreturn", 1); 125 set_syscall("rt_sigreturn", 1); 126 set_syscall("exit_group", 1); 127 set_syscall("exit", 1); 128 } 129 130 struct syscall sorted[SYSCALL_COUNT]; 131 132 for (i = 0; i < SYSCALL_COUNT; i++) { 133 sorted[i].syscall = syscall_index_to_number(i); 134 sorted[i].count = syscall_count[i]; 135 } 136 137 qsort(sorted, SYSCALL_COUNT, sizeof(sorted[0]), cmp_count); 138 139 blob_buf_init(&b, 0); 140 blobmsg_add_string(&b, "defaultAction", "SCMP_ACT_KILL_PROCESS"); 141 c = blobmsg_open_array(&b, "syscalls"); 142 d = blobmsg_open_table(&b, ""); 143 e = blobmsg_open_array(&b, "names"); 144 145 for (i = 0; i < SYSCALL_COUNT; i++) { 146 int sc = sorted[i].syscall; 147 if (!sorted[i].count) 148 break; 149 if (syscall_name(sc)) { 150 if (debug) 151 printf("syscall %d (%s) was called %d times\n", 152 sc, syscall_name(sc), sorted[i].count); 153 blobmsg_add_string(&b, NULL, syscall_name(sc)); 154 } else { 155 ULOG_ERR("no name found for syscall(%d)\n", sc); 156 } 157 } 158 blobmsg_close_array(&b, e); 159 blobmsg_add_string(&b, "action", "SCMP_ACT_ALLOW"); 160 blobmsg_close_table(&b, d); 161 blobmsg_close_array(&b, c); 162 if (json) { 163 FILE *fp = fopen(json, "w"); 164 if (fp) { 165 tmp = blobmsg_format_json_indent(b.head, true, 0); 166 if (!tmp) { 167 fclose(fp); 168 return; 169 } 170 171 fprintf(fp, "%s\n", tmp); 172 free(tmp); 173 fclose(fp); 174 ULOG_INFO("saving syscall trace to %s\n", json); 175 } else { 176 ULOG_ERR("failed to open %s\n", json); 177 } 178 } else { 179 tmp = blobmsg_format_json_indent(b.head, true, 0); 180 if (!tmp) 181 return; 182 183 printf("%s\n", tmp); 184 free(tmp); 185 } 186 } 187 188 static void report_seccomp_vialation(pid_t pid, unsigned syscall) 189 { 190 char buf[200]; 191 snprintf(buf, sizeof(buf), "/proc/%d/cmdline", pid); 192 int f = open(buf, O_RDONLY); 193 if (f < 0) 194 return; 195 196 int r = read(f, buf, sizeof(buf) - 1); 197 buf[sizeof(buf) - 1] = '\0'; 198 199 if (r >= 0) 200 buf[r] = 0; 201 else 202 strcpy(buf, "unknown?"); 203 close(f); 204 205 if (violation_count < INT_MAX) 206 violation_count++; 207 int i = syscall_index(syscall); 208 if (i >= 0) { 209 syscall_count[i]++; 210 ULOG_ERR("%s[%u] tried to call non-whitelisted syscall: %s (see %s)\n", 211 buf, pid, syscall_name(syscall), json); 212 } else { 213 ULOG_ERR("%s[%u] tried to call non-whitelisted syscall: %d (see %s)\n", 214 buf, pid, syscall, json); 215 } 216 } 217 218 static void tracer_cb(struct uloop_process *c, int ret) 219 { 220 struct tracee *tracee = container_of(c, struct tracee, proc); 221 int inject_signal = 0; 222 223 /* We explicitely check for events in upper 16 bits, because 224 * musl (as opposed to glibc) does not report 225 * PTRACE_EVENT_STOP as WIFSTOPPED */ 226 if (WIFSTOPPED(ret) || (ret >> 16)) { 227 if (WSTOPSIG(ret) & 0x80) { 228 if (!tracee->in_syscall) { 229 #ifdef __aarch64__ 230 int syscall = -1; 231 struct ptrace_syscall_info ptsi = {.op=PTRACE_SYSCALL_INFO_ENTRY}; 232 if (ptrace(PTRACE_GET_SYSCALL_INFO, c->pid, sizeof(ptsi), &ptsi) != -1) 233 syscall = ptsi.entry.nr; 234 #else 235 int syscall = ptrace(PTRACE_PEEKUSER, c->pid, reg_syscall_nr); 236 #endif 237 int i = syscall_index(syscall); 238 if (i >= 0) { 239 syscall_count[i]++; 240 if (debug) 241 fprintf(stderr, "%s()\n", syscall_name(syscall)); 242 } else if (debug) { 243 fprintf(stderr, "syscal(%d)\n", syscall); 244 } 245 } 246 tracee->in_syscall = !tracee->in_syscall; 247 } else if ((ret >> 8) == (SIGTRAP | (PTRACE_EVENT_FORK << 8)) || 248 (ret >> 8) == (SIGTRAP | (PTRACE_EVENT_VFORK << 8)) || 249 (ret >> 8) == (SIGTRAP | (PTRACE_EVENT_CLONE << 8))) { 250 struct tracee *child = calloc(1, sizeof(struct tracee)); 251 252 unsigned long msg; 253 ptrace(PTRACE_GETEVENTMSG, c->pid, 0, &msg); 254 child->proc.pid = msg; 255 child->proc.cb = tracer_cb; 256 ptrace(ptrace_restart, child->proc.pid, 0, 0); 257 uloop_process_add(&child->proc); 258 if (debug) 259 fprintf(stderr, "Tracing new child %d\n", child->proc.pid); 260 } else if ((ret >> 16) == PTRACE_EVENT_STOP) { 261 /* Nothing special to do here */ 262 } else if ((ret >> 8) == (SIGTRAP | (PTRACE_EVENT_SECCOMP << 8))) { 263 #ifdef __aarch64__ 264 int syscall = -1; 265 struct ptrace_syscall_info ptsi = {.op=PTRACE_SYSCALL_INFO_SECCOMP}; 266 if (ptrace(PTRACE_GET_SYSCALL_INFO, c->pid, sizeof(ptsi), &ptsi) != -1) 267 syscall = ptsi.entry.nr; 268 #else 269 int syscall = ptrace(PTRACE_PEEKUSER, c->pid, reg_syscall_nr); 270 #if defined(__arm__) 271 ptrace(PTRACE_SET_SYSCALL, c->pid, 0, -1); 272 ptrace(PTRACE_POKEUSER, c->pid, reg_retval_nr, -ENOSYS); 273 #else 274 ptrace(PTRACE_POKEUSER, c->pid, reg_syscall_nr, -1); 275 #endif 276 #endif 277 report_seccomp_vialation(c->pid, syscall); 278 } else { 279 inject_signal = WSTOPSIG(ret); 280 if (debug) 281 fprintf(stderr, "Injecting signal %d into pid %d\n", 282 inject_signal, tracee->proc.pid); 283 } 284 } else if (WIFEXITED(ret) || (WIFSIGNALED(ret) && WTERMSIG(ret))) { 285 if (tracee == &tracer) { 286 uloop_end(); /* Main process exit */ 287 } else { 288 if (debug) 289 fprintf(stderr, "Child %d exited\n", tracee->proc.pid); 290 free(tracee); 291 } 292 return; 293 } 294 295 ptrace(ptrace_restart, c->pid, 0, inject_signal); 296 uloop_process_add(c); 297 } 298 299 static void sigterm_handler(int signum) 300 { 301 /* When we receive SIGTERM, we forward it to the tracee. After 302 * the tracee exits, trace_cb() will be called and make us 303 * exit too. */ 304 kill(tracer.proc.pid, SIGTERM); 305 } 306 307 308 int main(int argc, char **argv, char **envp) 309 { 310 int status, ch, policy = EPERM; 311 pid_t child; 312 313 /* When invoked via seccomp-trace symlink, work as seccomp 314 * violation logger rather than as syscall tracer */ 315 if (strstr(argv[0], "seccomp-trace")) 316 mode = SECCOMP_TRACE; 317 318 while ((ch = getopt(argc, argv, "f:p:")) != -1) { 319 switch (ch) { 320 case 'f': 321 json = optarg; 322 break; 323 case 'p': 324 policy = atoi(optarg); 325 break; 326 } 327 } 328 329 if (!json) 330 json = getenv("SECCOMP_FILE"); 331 332 argc -= optind; 333 argv += optind; 334 335 if (!argc) 336 return -1; 337 338 if (getenv("TRACE_DEBUG")) 339 debug = 1; 340 unsetenv("TRACE_DEBUG"); 341 342 child = fork(); 343 344 if (child == 0) { 345 char **_argv = calloc(argc + 1, sizeof(char *)); 346 char **_envp; 347 char *preload = NULL; 348 const char *old_preload = getenv("LD_PRELOAD"); 349 int newenv = 0; 350 int envc = 0; 351 int ret; 352 353 memcpy(_argv, argv, argc * sizeof(char *)); 354 355 while (envp[envc++]) 356 ; 357 358 _envp = calloc(envc + 2, sizeof(char *)); 359 switch (mode) { 360 case UTRACE: 361 preload = "/lib/libpreload-trace.so"; 362 newenv = 1; 363 break; 364 case SECCOMP_TRACE: 365 preload = "/lib/libpreload-seccomp.so"; 366 newenv = 2; 367 if (asprintf(&_envp[1], "SECCOMP_FILE=%s", json ? json : "") < 0) 368 ULOG_ERR("failed to allocate SECCOMP_FILE env: %m\n"); 369 370 kill(getpid(), SIGSTOP); 371 break; 372 } 373 if (asprintf(&_envp[0], "LD_PRELOAD=%s%s%s", preload, 374 old_preload ? ":" : "", 375 old_preload ? old_preload : "") < 0) 376 ULOG_ERR("failed to allocate LD_PRELOAD env: %m\n"); 377 378 memcpy(&_envp[newenv], envp, envc * sizeof(char *)); 379 380 ret = execve(_argv[0], _argv, _envp); 381 ULOG_ERR("failed to exec %s: %m\n", _argv[0]); 382 383 free(_argv); 384 if (_envp[0]) 385 free(_envp[0]); 386 if (newenv == 2 && _envp[1]) 387 free(_envp[1]); 388 free(_envp); 389 return ret; 390 } 391 392 if (child < 0) 393 return -1; 394 395 waitpid(child, &status, WUNTRACED); 396 if (!WIFSTOPPED(status)) { 397 ULOG_ERR("failed to start %s\n", *argv); 398 return -1; 399 } 400 401 /* Initialize uloop to catch all ptrace stops from now on. */ 402 uloop_init(); 403 404 int ptrace_options = PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE; 405 switch (mode) { 406 case UTRACE: 407 ptrace_options |= PTRACE_O_TRACESYSGOOD; 408 ptrace_restart = PTRACE_SYSCALL; 409 break; 410 case SECCOMP_TRACE: 411 ptrace_options |= PTRACE_O_TRACESECCOMP; 412 ptrace_restart = PTRACE_CONT; 413 break; 414 } 415 if (ptrace(PTRACE_SEIZE, child, 0, ptrace_options) == -1) { 416 ULOG_ERR("PTRACE_SEIZE: %m\n"); 417 return -1; 418 } 419 if (ptrace(ptrace_restart, child, 0, SIGCONT) == -1) { 420 ULOG_ERR("ptrace_restart: %m\n"); 421 return -1; 422 } 423 424 tracer.proc.pid = child; 425 tracer.proc.cb = tracer_cb; 426 uloop_process_add(&tracer.proc); 427 signal(SIGTERM, sigterm_handler); /* Override uloop's SIGTERM handler */ 428 uloop_run(); 429 uloop_done(); 430 431 432 switch (mode) { 433 case UTRACE: 434 if (!json) 435 if (asprintf(&json, "/tmp/%s.%u.json", basename(*argv), child) < 0) 436 ULOG_ERR("failed to allocate output path: %m\n"); 437 break; 438 case SECCOMP_TRACE: 439 if (!violation_count) 440 return 0; 441 if (asprintf(&json, "/tmp/%s.%u.violations.json", basename(*argv), child) < 0) 442 ULOG_ERR("failed to allocate violations output path: %m\n"); 443 break; 444 } 445 print_syscalls(policy, json); 446 return 0; 447 } 448
This page was automatically generated by LXR 0.3.1. • OpenWrt