diff --git a/main.c b/main.c index b795427..f233642 100644 --- a/main.c +++ b/main.c @@ -367,204 +367,230 @@ watchForNotifications(int notifyFd, struct cmdLineOpts *opts) /* Loop handling notifications */ for (;;) { - /* Wait for next notification, returning info in '*req' */ + /* Wait for next notification, returning info in '*req' */ - bzero(req, sizes.seccomp_notif); - bzero(resp, sizes.seccomp_notif_resp); + bzero(req, sizes.seccomp_notif); + bzero(resp, sizes.seccomp_notif_resp); - if (ioctl(notifyFd, SECCOMP_IOCTL_NOTIF_RECV, req) == -1) - errExit("Tracer: ioctlSECCOMP_IOCTL_NOTIF_RECV"); + if (ioctl(notifyFd, SECCOMP_IOCTL_NOTIF_RECV, req) == -1) + errExit("Tracer: ioctlSECCOMP_IOCTL_NOTIF_RECV"); - if(opts->debug) printf("Tracer: got notification for PID %d; ID is %llx\n", - req->pid, req->id); + switch(req->data.nr) { + default: + fprintf(stderr, "seccomp: got unexpected syscall %d", req->data.nr); + exit(EXIT_FAILURE); - /* Access the memory of the target process in order to discover - the syscall arguments */ + case __NR_bind: { - snprintf(path, sizeof(path), "/proc/%d/mem", req->pid); + if(opts->debug) printf("Tracer: got notification for PID %d; ID is %llx\n", + req->pid, req->id); - procMem = open(path, O_RDWR); - if (procMem == -1) - errExit("Tracer: open"); + /* Access the memory of the target process in order to discover + the syscall arguments */ - /* Check that the process whose info we are accessing is still alive */ + snprintf(path, sizeof(path), "/proc/%d/mem", req->pid); - checkNotificationIdIsValid(notifyFd, req->id, "post-open", opts); + procMem = open(path, O_RDWR); + if (procMem == -1) + errExit("Tracer: open"); - /* Since, the SECCOMP_IOCTL_NOTIF_ID_VALID operation (performed in - checkNotificationIdIsValid()) succeeded, we know that the - /proc/PID/mem file descriptor that we opened corresponded to the - process for which we received a notification. If that process - subsequently terminates, then read() on that file descriptor will - return 0 (EOF). This can be tested by (1) uncommenting the sleep() - call below (and rebuilding the program); (2) running the program - with flags to ensure that the tracer is not killed if the target - dies; and (3) killing the target process during the sleep(). */ + /* Check that the process whose info we are accessing is still alive */ - // if(opts->debug) printf("About to sleep in target\n"); - // sleep(15); + checkNotificationIdIsValid(notifyFd, req->id, "post-open", opts); - /* Seek to the location containing the pathname argument (i.e., the - first argument) of the mkdir(2) call and read that pathname */ + /* Since, the SECCOMP_IOCTL_NOTIF_ID_VALID operation (performed in + checkNotificationIdIsValid()) succeeded, we know that the + /proc/PID/mem file descriptor that we opened corresponded to the + process for which we received a notification. If that process + subsequently terminates, then read() on that file descriptor will + return 0 (EOF). This can be tested by (1) uncommenting the sleep() + call below (and rebuilding the program); (2) running the program + with flags to ensure that the tracer is not killed if the target + dies; and (3) killing the target process during the sleep(). */ - int socketfd = req->data.args[0]; - intptr_t addrptr = req->data.args[1]; - size_t addrlen = req->data.args[2]; - if(opts->debug) printf("Tracer: bind(%d, 0x%llx, %lld, %lld, %lld, %llx)\n", socketfd, addrptr, addrlen, req->data.args[3], req->data.args[4], req->data.args[5]); + // if(opts->debug) printf("About to sleep in target\n"); + // sleep(15); - if (lseek(procMem, addrptr, SEEK_SET) == -1) - errExit("Tracer: lseek"); + /* Seek to the location containing the pathname argument (i.e., the + first argument) of the mkdir(2) call and read that pathname */ - addr = malloc(addrlen); - if (resp == NULL) - errExit("Tracer: malloc"); + int socketfd = req->data.args[0]; + intptr_t addrptr = req->data.args[1]; + size_t addrlen = req->data.args[2]; + if(opts->debug) printf("Tracer: bind(%d, 0x%llx, %lld, %lld, %lld, %llx)\n", socketfd, addrptr, addrlen, req->data.args[3], req->data.args[4], req->data.args[5]); - ssize_t s = read(procMem, addr, addrlen); - if (s == -1) - errExit("read"); - else if (s == 0) { - if(opts->debug) fprintf(stderr, "Tracer: read returned EOF\n"); - exit(EXIT_FAILURE); - } + if (lseek(procMem, addrptr, SEEK_SET) == -1) + errExit("Tracer: lseek"); - char addrstring[PATH_MAX]; - if(opts->debug) { - printf("Tracer: %p = %s\n", (void*) addrptr, + addr = malloc(addrlen); + if (resp == NULL) + errExit("Tracer: malloc"); + + ssize_t s = read(procMem, addr, addrlen); + if (s == -1) + errExit("read"); + else if (s == 0) { + if(opts->debug) fprintf(stderr, "Tracer: read returned EOF\n"); + exit(EXIT_FAILURE); + } + + char addrstring[PATH_MAX]; + if(opts->debug) { + printf("Tracer: %p = %s\n", (void*) addrptr, get_ip_str(addr, addrstring, sizeof(addrstring))); - for(int i = 0; i < addrlen; ++i) { - if(i == 0) printf("Tracer bind addr:"); - printf(" %02x", ((char*) addr)[i]); - if(i == addrlen - 1) printf("\n"); - } - } + for(int i = 0; i < addrlen; ++i) { + if(i == 0) printf("Tracer bind addr:"); + printf(" %02x", ((char*) addr)[i]); + if(i == addrlen - 1) printf("\n"); + } + } - if(opts->debug) printf("Tracer: bind(%d, %s)\n", socketfd, get_ip_str(addr, addrstring, sizeof(addrstring))); + if(opts->debug) printf("Tracer: bind(%d, %s)\n", socketfd, get_ip_str(addr, addrstring, sizeof(addrstring))); - /* The response to the notification includes the notification ID */ + /* The response to the notification includes the notification ID */ - resp->id = req->id; - resp->flags = 0; /* Must be zero as at Linux 5.0 */ - resp->val = 0; /* Success return value is 0 */ - resp->error = 0; + resp->id = req->id; + resp->flags = 0; /* Must be zero as at Linux 5.0 */ + resp->val = 0; /* Success return value is 0 */ + resp->error = 0; - if (addr->sa_family != AF_INET && addr->sa_family != AF_INET6) { + if (addr->sa_family != AF_INET && addr->sa_family != AF_INET6) { - /* Continue the syscall. This is not secure at all, but we don't - * care for now */ + /* In this branch, the bind() syscall was performed on addresses + * that are neither INET nor INET6, don't alter */ - resp->flags = SECCOMP_USER_NOTIF_FLAG_CONTINUE; + /* Continue the syscall. This is not secure at all, but we don't + * care for now */ - } else { + resp->flags = SECCOMP_USER_NOTIF_FLAG_CONTINUE; - /* Continue the syscall. ideally we should filter the IP address and - * make sure it is allowed, but this is not yet implemented */ + } else { - // use ptrace (most secure) https://github.com/briceburg/fdclose/blob/master/src/ptrace_do/libptrace_do.c - // (but will that call seccomp recursively ???) - // or modify process memory and return with SECCOMP_USER_NOTIF_FLAG_CONTINUE + /* In this branch, the bind() syscall was performed on INET or + * INET6 addresses */ - /* - snprintf(path, sizeof(path), "/proc/%d/fd/%d", req->pid, socketfd); + /* Continue the syscall. ideally we should filter the IP address and + * make sure it is allowed, but this is not yet implemented */ - int sock = open(path, 0); - if (sock == -1) - errExit("Tracer: open(sock)"); + // use ptrace (most secure) https://github.com/briceburg/fdclose/blob/master/src/ptrace_do/libptrace_do.c + // (but will that call seccomp recursively ???) + // or modify process memory and return with SECCOMP_USER_NOTIF_FLAG_CONTINUE - resp->error = bind(sock, addr, addrlen); + /* + snprintf(path, sizeof(path), "/proc/%d/fd/%d", req->pid, socketfd); - if(opts->debug) printf("Tracer: bind() = %d", resp->error); - */ + int sock = open(path, 0); + if (sock == -1) + errExit("Tracer: open(sock)"); - resp->flags = SECCOMP_USER_NOTIF_FLAG_CONTINUE; + resp->error = bind(sock, addr, addrlen); - struct sockaddr *replacement = malloc(addrlen); - if (replacement == NULL) - errExit("Tracer: malloc"); - memcpy(replacement, addr, addrlen); - - int newfd; - // FIXME: handle multiple replacement - int matchres = matchAllAddr(opts->map, replacement, &newfd, opts); - if(matchres < 0) { - resp->flags = 0; - resp->error = -matchres; - } else if(matchres == 2) { - resp->flags = 0; - resp->error = -EINVAL; - } else if(matchres == 1) { - char repladdrstring[PATH_MAX]; - if(!opts->quiet) printf("force-bind: replace %s with %s\n", - get_ip_str(addr, addrstring, sizeof(addrstring)), - get_ip_str(replacement, repladdrstring, sizeof(repladdrstring))); - - if (lseek(procMem, addrptr, SEEK_SET) == -1) + if(opts->debug) printf("Tracer: bind() = %d", resp->error); + */ + + resp->flags = SECCOMP_USER_NOTIF_FLAG_CONTINUE; + + struct sockaddr *replacement = malloc(addrlen); + if (replacement == NULL) + errExit("Tracer: malloc"); + memcpy(replacement, addr, addrlen); + + int newfd; + // FIXME: handle multiple replacement + int matchres = matchAllAddr(opts->map, replacement, &newfd, opts); + if(matchres < 0) { + resp->flags = 0; + resp->error = -matchres; + } else if(matchres == 2) { + struct seccomp_notif_addfd addfd; + addfd.id = req->id; /* Cookie from SECCOMP_IOCTL_NOTIF_RECV */ + addfd.srcfd = newfd; + addfd.newfd = 0; + addfd.flags = 0; + addfd.newfd_flags = 0; + int targetFd = ioctl(notifyFd, SECCOMP_IOCTL_NOTIF_ADDFD, &addfd); + + + + resp->flags = 0; + resp->error = (targetFd < 0) ? targetFd : 0; + resp->val = targetFd; + } else if(matchres == 1) { + char repladdrstring[PATH_MAX]; + if(!opts->quiet) printf("force-bind: replace %s with %s\n", + get_ip_str(addr, addrstring, sizeof(addrstring)), + get_ip_str(replacement, repladdrstring, sizeof(repladdrstring))); + + if (lseek(procMem, addrptr, SEEK_SET) == -1) errExit("force-bind: lseek"); - if(opts->debug) { + if(opts->debug) { for(int i = 0; i < addrlen; ++i) { - if(i == 0) printf("Tracer write addr:"); - printf(" %02x", ((char*) replacement)[i]); - if(i == addrlen - 1) printf("\n"); + if(i == 0) printf("Tracer write addr:"); + printf(" %02x", ((char*) replacement)[i]); + if(i == addrlen - 1) printf("\n"); } - } + } - ssize_t s = write(procMem, replacement, addrlen); - if (s == -1) + ssize_t s = write(procMem, replacement, addrlen); + if (s == -1) errExit("read"); - else if (s != addrlen) { + else if (s != addrlen) { fprintf(stderr, "force-bind: short write\n"); exit(EXIT_FAILURE); - } + } - if(opts->debug) { + if(opts->debug) { if (lseek(procMem, addrptr, SEEK_SET) == -1) - errExit("Tracer: lseek"); + errExit("Tracer: lseek"); free(addr); addr = malloc(addrlen); if (resp == NULL) - errExit("Tracer: malloc"); + errExit("Tracer: malloc"); ssize_t s = read(procMem, addr, addrlen); if (s == -1) - errExit("read"); + errExit("read"); else if (s == 0) { - if(opts->debug) fprintf(stderr, "Tracer: read returned EOF\n"); - exit(EXIT_FAILURE); + if(opts->debug) fprintf(stderr, "Tracer: read returned EOF\n"); + exit(EXIT_FAILURE); } printf("Tracer: %p = %s\n", (void*) addrptr, - get_ip_str(addr, addrstring, sizeof(addrstring))); + get_ip_str(addr, addrstring, sizeof(addrstring))); for(int i = 0; i < addrlen; ++i) { - if(i == 0) printf("Tracer bind addr:"); - printf(" %02x", ((char*) addr)[i]); - if(i == addrlen - 1) printf("\n"); + if(i == 0) printf("Tracer bind addr:"); + printf(" %02x", ((char*) addr)[i]); + if(i == addrlen - 1) printf("\n"); } - } + } - free(replacement); - } + free(replacement); + } - } + } - free(addr); + free(addr); - if (close(procMem) == -1) - errExit("close-/proc/PID/mem"); + if (close(procMem) == -1) + errExit("close-/proc/PID/mem"); - /* Provide a response to the target process */ + /* Provide a response to the target process */ - if (ioctl(notifyFd, SECCOMP_IOCTL_NOTIF_SEND, resp) == -1) { - if (errno == ENOENT) { - if(opts->debug) printf("Tracer: response failed with ENOENT; perhaps target " - "process's syscall was interrupted by signal?\n"); - } else { - perror("ioctl-SECCOMP_IOCTL_NOTIF_SEND"); + if (ioctl(notifyFd, SECCOMP_IOCTL_NOTIF_SEND, resp) == -1) { + if (errno == ENOENT) { + if(opts->debug) printf("Tracer: response failed with ENOENT; perhaps target " + "process's syscall was interrupted by signal?\n"); + } else { + perror("ioctl-SECCOMP_IOCTL_NOTIF_SEND"); + } + } + if(opts->debug) printf("Tracer: notification sent.\n"); } } - if(opts->debug) printf("Tracer: notification sent.\n"); } } @@ -897,14 +923,14 @@ parseMap(const char *map0, struct mapping *next, struct cmdLineOpts *opts, bool int fd = atoi(&replace[3]); cur->replacement_fd = fd; cur->replacement = NULL; - opts->require_ptrace = true; + //opts->require_ptrace = true; if(opts->debug) printf("parsed replacement fd %d\n", cur->replacement_fd); } else if (len > 3 && replace[0] == 's' && replace[1] == 'd' && (replace[2] == '=' || replace[2] == '-')) { int sd = atoi(&replace[3]); int fd = sd + 3; cur->replacement_fd = fd; cur->replacement = NULL; - opts->require_ptrace = true; + //opts->require_ptrace = true; if(opts->debug) printf("parsed replacement fd %d (systemd)\n", cur->replacement_fd); } else { err = getaddrinfo2(replace, &hints, &res);