2
0
mirror of https://github.com/sudo-project/sudo.git synced 2025-08-22 01:49:11 +00:00

Simple event subsystem that uses poll() or select(). Basically a

simplied subset of libevent2.  Currently only fd events are supported
(since that's all we need).  The poll() backend is used by default,
except on Mac OS X where poll() is broken for devices (including
/dev/tty and ptys).
This commit is contained in:
Todd C. Miller 2013-10-12 05:53:43 -06:00
parent ff18c65862
commit 79acd5db49
18 changed files with 890 additions and 15 deletions

View File

@ -150,6 +150,13 @@ Compilation options:
option may be needed on some Linux systems where PIE binaries
are not fully supported.
--disable-poll
Use select() instead of poll() in the event loop. By default,
sudo will use poll() on systems that support it. Some systems
have a broken poll() implementation and need to use select instead.
On Mac OS X, select() is always used since its poll() doesn't
support devices.
--disable-rpath
By default, configure will use -Rpath in addition to -Lpath
when passing library paths to the loader. This option will

View File

@ -12,6 +12,9 @@ common/aix.c
common/alloc.c
common/atobool.c
common/atoid.c
common/event.c
common/event_poll.c
common/event_select.c
common/fatal.c
common/fileops.c
common/fmt_string.c
@ -147,6 +150,7 @@ include/missing.h
include/secure_path.h
include/sudo_conf.h
include/sudo_debug.h
include/sudo_event.h
include/sudo_plugin.h
indent.pro
install-sh

View File

@ -66,9 +66,10 @@ DEFS = @OSDEFS@ -D_PATH_SUDO_CONF=\"$(sysconfdir)/sudo.conf\"
SHELL = @SHELL@
LTOBJS = alloc.lo atobool.lo atoid.lo fatal.lo fileops.lo fmt_string.lo \
gidlist.lo lbuf.lo list.lo secure_path.lo setgroups.lo sudo_conf.lo \
sudo_debug.lo sudo_printf.lo term.lo ttysize.lo @COMMON_OBJS@
LTOBJS = alloc.lo atobool.lo atoid.lo event.lo fatal.lo fileops.lo \
fmt_string.lo gidlist.lo lbuf.lo list.lo secure_path.lo \
setgroups.lo sudo_conf.lo sudo_debug.lo sudo_printf.lo term.lo \
ttysize.lo @COMMON_OBJS@
PARSELN_TEST_OBJS = parseln_test.lo
@ -171,6 +172,21 @@ conf_test.lo: $(srcdir)/regress/sudo_conf/conf_test.c $(top_builddir)/config.h \
$(top_srcdir)/compat/stdbool.h $(incdir)/missing.h \
$(incdir)/sudo_conf.h $(incdir)/list.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/regress/sudo_conf/conf_test.c
event.lo: $(srcdir)/event.c $(top_builddir)/config.h \
$(top_srcdir)/compat/stdbool.h $(incdir)/missing.h $(incdir)/alloc.h \
$(incdir)/fatal.h $(incdir)/list.h $(incdir)/sudo_debug.h \
$(incdir)/sudo_event.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/event.c
event_poll.lo: $(srcdir)/event_poll.c $(top_builddir)/config.h \
$(top_srcdir)/compat/stdbool.h $(incdir)/missing.h \
$(incdir)/alloc.h $(incdir)/fatal.h $(incdir)/list.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_event.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/event_poll.c
event_select.lo: $(srcdir)/event_select.c $(top_builddir)/config.h \
$(top_srcdir)/compat/stdbool.h $(incdir)/missing.h \
$(incdir)/alloc.h $(incdir)/fatal.h $(incdir)/list.h \
$(incdir)/sudo_debug.h $(incdir)/sudo_event.h
$(LIBTOOL) --mode=compile $(CC) -c -o $@ $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/event_select.c
fatal.lo: $(srcdir)/fatal.c $(top_builddir)/config.h \
$(top_srcdir)/compat/stdbool.h $(incdir)/missing.h $(incdir)/alloc.h \
$(incdir)/fatal.h $(incdir)/sudo_plugin.h $(incdir)/gettext.h

224
common/event.c Normal file
View File

@ -0,0 +1,224 @@
/*
* Copyright (c) 2013 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <sys/types.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STDBOOL_H
# include <stdbool.h>
#else
# include "compat/stdbool.h"
#endif /* HAVE_STDBOOL_H */
#ifdef HAVE_STRING_H
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <errno.h>
#include "missing.h"
#include "alloc.h"
#include "fatal.h"
#include "list.h"
#include "sudo_debug.h"
#include "sudo_event.h"
/* XXX - use non-exiting allocators? */
struct sudo_event_base *
sudo_ev_base_alloc(void)
{
struct sudo_event_base *base;
debug_decl(sudo_ev_base_alloc, SUDO_DEBUG_EVENT)
base = ecalloc(1, sizeof(*base));
if (sudo_ev_base_alloc_impl(base) != 0) {
efree(base);
base = NULL;
}
debug_return_ptr(base);
}
void
sudo_ev_base_free(struct sudo_event_base *base)
{
debug_decl(sudo_ev_base_free, SUDO_DEBUG_EVENT)
sudo_ev_base_free_impl(base);
efree(base);
debug_return;
}
struct sudo_event *
sudo_ev_alloc(int fd, short events, sudo_ev_callback_t callback, void *closure)
{
struct sudo_event *ev;
debug_decl(sudo_ev_alloc, SUDO_DEBUG_EVENT)
ev = ecalloc(1, sizeof(*ev));
ev->fd = fd;
ev->events = events;
ev->pfd_idx = -1;
ev->callback = callback;
ev->closure = closure;
debug_return_ptr(ev);
}
void
sudo_ev_free(struct sudo_event *ev)
{
debug_decl(sudo_ev_free, SUDO_DEBUG_EVENT)
free(ev);
debug_return;
}
int
sudo_ev_add(struct sudo_event_base *base, struct sudo_event *ev, bool tohead)
{
debug_decl(sudo_ev_add, SUDO_DEBUG_EVENT)
/* Don't add an event twice; revisit if we want to support timeouts. */
if (ev->base == NULL) {
if (sudo_ev_add_impl(base, ev) != 0)
debug_return_int(-1);
ev->base = base;
if (tohead) {
tq_insert_head(base, ev);
} else {
tq_insert_tail(base, ev);
}
}
/* Clear pending delete so adding from callback works properly. */
CLR(ev->events, SUDO_EV_DELETE);
debug_return_int(0);
}
int
sudo_ev_del(struct sudo_event_base *base, struct sudo_event *ev)
{
debug_decl(sudo_ev_del, SUDO_DEBUG_EVENT)
/* Make sure event is really in the queue. */
if (ev->base == NULL) {
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: event %p not in queue",
__func__, ev);
debug_return_int(0);
}
/* Check for event base mismatch, if one is specified. */
if (base == NULL) {
base = ev->base;
} else if (base != ev->base) {
sudo_debug_printf(SUDO_DEBUG_ERROR, "%s: mismatch base %p, ev->base %p",
__func__, base, ev->base);
debug_return_int(-1);
}
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: removing event %p from base %p",
__func__, ev, base);
/* Call backend. */
if (sudo_ev_del_impl(base, ev) != 0)
debug_return_int(-1);
/* Unlink from list. */
tq_remove(base, ev);
/* If we removed the pending event, replace it with the next one. */
if (base->pending == ev)
base->pending = ev->next;
/* Mark event unused. */
ev->pfd_idx = -1;
ev->base = NULL;
ev->prev = NULL;
ev->next = NULL;
debug_return_int(0);
}
int
sudo_ev_loop(struct sudo_event_base *base, int flags)
{
int rc;
debug_decl(sudo_ev_loop, SUDO_DEBUG_EVENT)
/*
* If sudo_ev_loopexit() was called when events were not running
* the next invocation of sudo_ev_loop() only runs once.
* All other base flags are ignored unless we are running events.
*/
if (ISSET(base->flags, SUDO_EVBASE_LOOPEXIT))
flags |= SUDO_EVLOOP_ONCE;
base->flags = 0;
/* Most work is done by the backend. */
rc = sudo_ev_loop_impl(base, flags);
base->flags &= SUDO_EVBASE_GOT_MASK;
debug_return_int(rc);
}
void
sudo_ev_loopexit(struct sudo_event_base *base)
{
debug_decl(sudo_ev_loopexit, SUDO_DEBUG_EVENT)
base->flags |= SUDO_EVBASE_LOOPEXIT;
debug_return;
}
void
sudo_ev_loopbreak(struct sudo_event_base *base)
{
debug_decl(sudo_ev_loopbreak, SUDO_DEBUG_EVENT)
base->flags |= SUDO_EVBASE_LOOPBREAK;
debug_return;
}
void
sudo_ev_loopcontinue(struct sudo_event_base *base)
{
debug_decl(sudo_ev_loopcontinue, SUDO_DEBUG_EVENT)
base->flags |= SUDO_EVBASE_LOOPCONT;
debug_return;
}
bool
sudo_ev_got_exit(struct sudo_event_base *base)
{
debug_decl(sudo_ev_got_exit, SUDO_DEBUG_EVENT)
debug_return_bool(ISSET(base->flags, SUDO_EVBASE_GOT_EXIT));
}
bool
sudo_ev_got_break(struct sudo_event_base *base)
{
debug_decl(sudo_ev_got_break, SUDO_DEBUG_EVENT)
debug_return_bool(ISSET(base->flags, SUDO_EVBASE_GOT_BREAK));
}

197
common/event_poll.c Normal file
View File

@ -0,0 +1,197 @@
/*
* Copyright (c) 2013 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <sys/types.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STDBOOL_H
# include <stdbool.h>
#else
# include "compat/stdbool.h"
#endif /* HAVE_STDBOOL_H */
#ifdef HAVE_STRING_H
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <errno.h>
#include <poll.h>
#include "missing.h"
#include "alloc.h"
#include "fatal.h"
#include "list.h"
#include "sudo_debug.h"
#include "sudo_event.h"
/* XXX - use non-exiting allocators? */
int
sudo_ev_base_alloc_impl(struct sudo_event_base *base)
{
int i;
debug_decl(sudo_ev_base_alloc_impl, SUDO_DEBUG_EVENT)
base->pfd_high = -1;
base->pfd_max = 32;
base->pfds = erealloc3(NULL, base->pfd_max, sizeof(struct pollfd));
for (i = 0; i < base->pfd_max; i++) {
base->pfds[i].fd = -1;
}
debug_return_int(0);
}
void
sudo_ev_base_free_impl(struct sudo_event_base *base)
{
debug_decl(sudo_ev_base_free_impl, SUDO_DEBUG_EVENT)
efree(base->pfds);
debug_return;
}
int
sudo_ev_add_impl(struct sudo_event_base *base, struct sudo_event *ev)
{
debug_decl(sudo_ev_add_impl, SUDO_DEBUG_EVENT)
struct pollfd *pfd;
/* If out of space in pfds array, realloc. */
if (base->pfd_free == base->pfd_max) {
int i;
base->pfd_max <<= 1;
base->pfds =
erealloc3(base->pfds, base->pfd_max, sizeof(struct pollfd));
for (i = base->pfd_free; i < base->pfd_max; i++) {
base->pfds[i].fd = -1;
}
}
/* Fill in pfd entry. */
ev->pfd_idx = base->pfd_free;
pfd = &base->pfds[ev->pfd_idx];
pfd->fd = ev->fd;
pfd->events = 0;
if (ISSET(ev->events, SUDO_EV_READ))
pfd->events |= POLLIN;
if (ISSET(ev->events, SUDO_EV_WRITE))
pfd->events |= POLLOUT;
/* Update pfd_high and pfd_free. */
if (ev->pfd_idx > base->pfd_high)
base->pfd_high = ev->pfd_idx;
for (;;) {
if (++base->pfd_free == base->pfd_max)
break;
if (base->pfds[base->pfd_free].fd == -1)
break;
}
debug_return_int(0);
}
int
sudo_ev_del_impl(struct sudo_event_base *base, struct sudo_event *ev)
{
debug_decl(sudo_ev_del_impl, SUDO_DEBUG_EVENT)
/* Mark pfd entry unused, add to free list and adjust high slot. */
base->pfds[ev->pfd_idx].fd = -1;
if (ev->pfd_idx < base->pfd_free)
base->pfd_free = ev->pfd_idx;
while (base->pfd_high >= 0 && base->pfds[base->pfd_high].fd == -1)
base->pfd_high--;
debug_return_int(0);
}
int
sudo_ev_loop_impl(struct sudo_event_base *base, int flags)
{
const int timeout = (flags & SUDO_EVLOOP_NONBLOCK) ? 0 : -1;
struct sudo_event *ev;
int nready;
debug_decl(sudo_ev_loop_impl, SUDO_DEBUG_EVENT)
rescan:
while (base->pfd_high != -1) {
nready = poll(base->pfds, base->pfd_high + 1, timeout);
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: %d fds ready",
__func__, nready);
switch (nready) {
case -1:
if (errno == EINTR || errno == ENOMEM)
continue;
debug_return_int(-1);
case 0:
/* timeout or no events */
break;
default:
/* Service each event that fired. */
for (ev = tq_first(base); ev != NULL; ev = base->pending) {
base->pending = list_next(ev);
if (base->pfds[ev->pfd_idx].revents) {
int what = 0;
if (base->pfds[ev->pfd_idx].revents & (POLLIN|POLLHUP|POLLNVAL|POLLERR))
what |= (ev->events & SUDO_EV_READ);
if (base->pfds[ev->pfd_idx].revents & (POLLOUT|POLLHUP|POLLNVAL|POLLERR))
what |= (ev->events & SUDO_EV_WRITE);
if (!ISSET(ev->events, SUDO_EV_PERSIST))
SET(ev->events, SUDO_EV_DELETE);
ev->callback(ev->fd, what, ev->closure);
if (ISSET(ev->events, SUDO_EV_DELETE))
sudo_ev_del(base, ev);
if (ISSET(base->flags, SUDO_EVBASE_LOOPBREAK)) {
/* stop processing events immediately */
base->flags |= SUDO_EVBASE_GOT_BREAK;
base->pending = NULL;
goto done;
}
if (ISSET(base->flags, SUDO_EVBASE_LOOPCONT)) {
/* rescan events and start polling again */
CLR(base->flags, SUDO_EVBASE_LOOPCONT);
base->pending = NULL;
goto rescan;
}
}
}
base->pending = NULL;
if (ISSET(base->flags, SUDO_EVBASE_LOOPEXIT)) {
/* exit loop after once through */
base->flags |= SUDO_EVBASE_GOT_EXIT;
goto done;
}
break;
}
if (flags & (SUDO_EVLOOP_ONCE | SUDO_EVLOOP_NONBLOCK))
break;
}
done:
debug_return_int(0);
}

207
common/event_select.c Normal file
View File

@ -0,0 +1,207 @@
/*
* Copyright (c) 2013 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <config.h>
#include <sys/types.h>
#ifdef HAVE_SYS_SYSMACROS_H
# include <sys/sysmacros.h>
#endif
#ifdef HAVE_SYS_SELECT_H
# include <sys/select.h>
#endif /* HAVE_SYS_SELECT_H */
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STDBOOL_H
# include <stdbool.h>
#else
# include "compat/stdbool.h"
#endif /* HAVE_STDBOOL_H */
#ifdef HAVE_STRING_H
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif /* HAVE_UNISTD_H */
#include <errno.h>
#include "missing.h"
#include "alloc.h"
#include "fatal.h"
#include "list.h"
#include "sudo_debug.h"
#include "sudo_event.h"
/* XXX - use non-exiting allocators? */
int
sudo_ev_base_alloc_impl(struct sudo_event_base *base)
{
debug_decl(sudo_ev_base_alloc_impl, SUDO_DEBUG_EVENT)
base->maxfd = NFDBITS - 1;
base->nevents = 0;
base->readfds = ecalloc(1, sizeof(fd_mask));
base->writefds = ecalloc(1, sizeof(fd_mask));
debug_return_int(0);
}
void
sudo_ev_base_free_impl(struct sudo_event_base *base)
{
debug_decl(sudo_ev_base_free_impl, SUDO_DEBUG_EVENT)
efree(base->readfds);
efree(base->writefds);
debug_return;
}
int
sudo_ev_add_impl(struct sudo_event_base *base, struct sudo_event *ev)
{
debug_decl(sudo_ev_add_impl, SUDO_DEBUG_EVENT)
/* If out of space in fd sets, realloc. */
if (ev->fd > base->maxfd) {
const int n = howmany(ev->fd + 1, NFDBITS);
efree(base->readfds);
efree(base->writefds);
base->readfds = ecalloc(n, sizeof(fd_mask));
base->writefds = ecalloc(n, sizeof(fd_mask));
base->maxfd = (n * NFDBITS) - 1;
}
base->nevents++;
debug_return_int(0);
}
int
sudo_ev_del_impl(struct sudo_event_base *base, struct sudo_event *ev)
{
debug_decl(sudo_ev_del_impl, SUDO_DEBUG_EVENT)
/* Remove from readfds and writefds and adjust event count. */
FD_CLR(ev->fd, base->readfds);
FD_CLR(ev->fd, base->writefds);
base->nevents--;
debug_return_int(0);
}
int
sudo_ev_loop_impl(struct sudo_event_base *base, int flags)
{
struct timeval tv, *timeout;
struct sudo_event *ev;
int nready;
debug_decl(sudo_ev_loop, SUDO_DEBUG_EVENT)
if (ISSET(flags, SUDO_EVLOOP_NONBLOCK)) {
tv.tv_sec = 0;
tv.tv_usec = 0;
timeout = &tv;
} else {
timeout = NULL;
}
rescan:
while (base->nevents != 0) {
int highfd = 0;
/* For select we need to redo readfds and writefds each time. */
memset(base->readfds, 0, howmany(base->maxfd + 1, NFDBITS) * sizeof(fd_mask));
memset(base->writefds, 0, howmany(base->maxfd + 1, NFDBITS) * sizeof(fd_mask));
tq_foreach_fwd(base, ev) {
if (ISSET(ev->events, SUDO_EV_READ)) {
sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: added fd %d to readfs",
__func__, ev->fd);
FD_SET(ev->fd, base->readfds);
if (ev->fd > highfd)
highfd = ev->fd;
}
if (ISSET(ev->events, SUDO_EV_WRITE)) {
sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: added fd %d to writefds",
__func__, ev->fd);
FD_SET(ev->fd, base->writefds);
if (ev->fd > highfd)
highfd = ev->fd;
}
}
sudo_debug_printf(SUDO_DEBUG_DEBUG, "%s: select high fd %d",
__func__, highfd);
nready = select(highfd + 1, base->readfds, base->writefds, NULL, timeout);
sudo_debug_printf(SUDO_DEBUG_INFO, "%s: %d fds ready",
__func__, nready);
switch (nready) {
case -1:
if (errno == EINTR || errno == ENOMEM)
continue;
debug_return_int(-1);
case 0:
/* timeout or no events */
break;
default:
/* Service each event that fired. */
for (ev = tq_first(base); ev != NULL; ev = base->pending) {
int what = 0;
base->pending = list_next(ev);
if (FD_ISSET(ev->fd, base->readfds))
what |= (ev->events & SUDO_EV_READ);
if (FD_ISSET(ev->fd, base->writefds))
what |= (ev->events & SUDO_EV_WRITE);
if (what != 0) {
if (!ISSET(ev->events, SUDO_EV_PERSIST))
SET(ev->events, SUDO_EV_DELETE);
ev->callback(ev->fd, what, ev->closure);
if (ISSET(ev->events, SUDO_EV_DELETE))
sudo_ev_del(base, ev);
if (ISSET(base->flags, SUDO_EVBASE_LOOPBREAK)) {
/* stop processing events immediately */
base->flags |= SUDO_EVBASE_GOT_BREAK;
base->pending = NULL;
goto done;
}
if (ISSET(base->flags, SUDO_EVBASE_LOOPCONT)) {
/* rescan events and start polling again */
CLR(base->flags, SUDO_EVBASE_LOOPCONT);
base->pending = NULL;
goto rescan;
}
}
}
base->pending = NULL;
if (ISSET(base->flags, SUDO_EVBASE_LOOPEXIT)) {
/* exit loop after once through */
base->flags |= SUDO_EVBASE_GOT_EXIT;
goto done;
}
break;
}
if (flags & (SUDO_EVLOOP_ONCE | SUDO_EVLOOP_NONBLOCK))
break;
}
done:
debug_return_int(0);
}

View File

@ -112,7 +112,7 @@ list_append(void *vl1, void *vl2)
/*
* Append the list of entries to the head node and convert
* e from a semi-circle queue to normal doubly-linked list.
* from a semi-circle queue to normal doubly-linked list.
*/
void
tq_append(void *vh, void *vl)
@ -129,6 +129,42 @@ tq_append(void *vh, void *vl)
h->last = tail;
}
/*
* Insert a single item at the head of the queue.
*/
void
tq_insert_head(void *vh, void *vl)
{
struct list_head_proto *h = (struct list_head_proto *)vh;
struct list_proto *l = (struct list_proto *)vl;
l->prev = NULL;
l->next = h->first;
if (tq_empty(h))
h->last = l;
else
h->first->prev = l;
h->first = l;
}
/*
* Insert a single item at the tail of the queue.
*/
void
tq_insert_tail(void *vh, void *vl)
{
struct list_head_proto *h = (struct list_head_proto *)vh;
struct list_proto *l = (struct list_proto *)vl;
l->prev = h->last;
l->next = NULL;
if (tq_empty(h))
h->first = l;
else
h->last->next = l;
h->last = l;
}
/*
* Remove element from the tail_queue
*/
@ -146,7 +182,7 @@ tq_remove(void *vh, void *vl)
/* At least two elements in the list. */
if (h->first == l) {
h->first = l->next;
h->first->prev = h->first;
h->first->prev = h->first; /* XXX - do we rely on this behavior? */
} else if (h->last == l) {
h->last = l->prev;
h->last->next = NULL;

View File

@ -103,6 +103,7 @@ const char *const sudo_debug_subsystems[] = {
"plugin",
"hooks",
"sssd",
"event",
NULL
};

View File

@ -442,6 +442,9 @@
/* Define to 1 if you have the <paths.h> header file. */
#undef HAVE_PATHS_H
/* Define to 1 if you have the `poll' function. */
#undef HAVE_POLL
/* Define to 1 if you have the `posix_openpt' function. */
#undef HAVE_POSIX_OPENPT

36
configure vendored
View File

@ -903,6 +903,7 @@ enable_warnings
enable_werror
enable_hardening
enable_pie
enable_poll
enable_admin_flag
enable_nls
enable_rpath
@ -1570,6 +1571,7 @@ Optional Features:
--disable-hardening Do not use compiler/linker exploit mitigation
options
--enable-pie Build sudo as a position independent executable.
--disable-poll Use select() instead of poll().
--enable-admin-flag Whether to create a Ubuntu-style admin flag file
--disable-nls Disable natural language support using gettext
--disable-rpath Disable passing of -Rpath to the linker
@ -5662,6 +5664,12 @@ if test "${enable_pie+set}" = set; then :
fi
# Check whether --enable-poll was given.
if test "${enable_poll+set}" = set; then :
enableval=$enable_poll;
fi
# Check whether --enable-admin-flag was given.
if test "${enable_admin_flag+set}" = set; then :
enableval=$enable_admin_flag; case "$enableval" in
@ -14062,7 +14070,7 @@ _ACEOF
fi
done
COMMON_OBJS="$COMMON_OBJS aix.lo"
COMMON_OBJS="${COMMON_OBJS} aix.lo"
;;
*-*-hiuxmpp*)
: ${mansectsu='1m'}
@ -14582,6 +14590,8 @@ done
CHECKSHADOW="false"
test -z "$with_pam" && AUTH_EXCL_DEF="PAM"
: ${with_logincap='yes'}
# Darwin has a broken poll()
: ${enable_poll='no'}
# Darwin 8 and above can interpose library symbols cleanly
if test $OSMAJOR -ge 8; then
$as_echo "#define HAVE___INTERPOSE 1" >>confdefs.h
@ -20330,6 +20340,30 @@ done
fi
fi
if test X"$enable_poll" = X""; then
for ac_func in poll
do :
ac_fn_c_check_func "$LINENO" "poll" "ac_cv_func_poll"
if test "x$ac_cv_func_poll" = xyes; then :
cat >>confdefs.h <<_ACEOF
#define HAVE_POLL 1
_ACEOF
enable_poll=yes
else
enable_poll=no
fi
done
elif test X"$enable_poll" = X"yes"; then
$as_echo "#define HAVE_POLL 1" >>confdefs.h
fi
if test "$enable_poll" = "yes"; then
COMMON_OBJS="${COMMON_OBJS} event_poll.lo"
else
COMMON_OBJS="${COMMON_OBJS} event_select.lo"
fi
if test ${with_ldap-'no'} != "no"; then
O_LDFLAGS="$LDFLAGS"
if test "$with_ldap" != "yes"; then

View File

@ -1388,6 +1388,9 @@ AC_ARG_ENABLE(hardening,
AC_ARG_ENABLE(pie,
[AS_HELP_STRING([--enable-pie], [Build sudo as a position independent executable.])])
AC_ARG_ENABLE(poll,
[AS_HELP_STRING([--disable-poll], [Use select() instead of poll().])])
AC_ARG_ENABLE(admin-flag,
[AS_HELP_STRING([--enable-admin-flag], [Whether to create a Ubuntu-style admin flag file])],
[ case "$enableval" in
@ -1646,7 +1649,7 @@ case "$host" in
# AIX-specific functions
AC_CHECK_FUNCS(getuserattr setauthdb setrlimit64)
COMMON_OBJS="$COMMON_OBJS aix.lo"
COMMON_OBJS="${COMMON_OBJS} aix.lo"
;;
*-*-hiuxmpp*)
: ${mansectsu='1m'}
@ -1963,6 +1966,8 @@ case "$host" in
CHECKSHADOW="false"
test -z "$with_pam" && AUTH_EXCL_DEF="PAM"
: ${with_logincap='yes'}
# Darwin has a broken poll()
: ${enable_poll='no'}
# Darwin 8 and above can interpose library symbols cleanly
if test $OSMAJOR -ge 8; then
AC_DEFINE(HAVE___INTERPOSE)
@ -3217,6 +3222,20 @@ if test ${with_passwd-'no'} != "no"; then
fi
fi
dnl
dnl Choose event subsystem backend: poll or select
dnl
if test X"$enable_poll" = X""; then
AC_CHECK_FUNCS(poll, [enable_poll=yes], [enable_poll=no])
elif test X"$enable_poll" = X"yes"; then
AC_DEFINE(HAVE_POLL)
fi
if test "$enable_poll" = "yes"; then
COMMON_OBJS="${COMMON_OBJS} event_poll.lo"
else
COMMON_OBJS="${COMMON_OBJS} event_select.lo"
fi
dnl
dnl extra lib and .o file for LDAP support
dnl

View File

@ -69,6 +69,8 @@ struct n##_list { \
*/
void *tq_pop(void *);
void tq_append(void *, void *);
void tq_insert_head(void *, void *);
void tq_insert_tail(void *, void *);
void tq_remove(void *, void *);
void list_append(void *, void *);
void list2tq(void *, void *);

View File

@ -72,6 +72,7 @@
#define SUDO_DEBUG_PLUGIN (24<<6) /* main plugin functions */
#define SUDO_DEBUG_HOOKS (25<<6) /* hook functions */
#define SUDO_DEBUG_SSSD (26<<6) /* sudoers SSSD */
#define SUDO_DEBUG_EVENT (27<<6) /* event handling */
#define SUDO_DEBUG_ALL 0xfff0 /* all subsystems */
/* Flag to include string version of errno in debug info. */

122
include/sudo_event.h Normal file
View File

@ -0,0 +1,122 @@
/*
* Copyright (c) 2013 Todd C. Miller <Todd.Miller@courtesan.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _SUDO_EVENT_H
#define _SUDO_EVENT_H
/* Event types */
#define SUDO_EV_READ 0x01 /* fire when readable */
#define SUDO_EV_WRITE 0x02 /* fire when writable */
#define SUDO_EV_PERSIST 0x04 /* persist until deleted */
#define SUDO_EV_DELETE 0x08 /* deletion pending */
/* Event loop flags */
#define SUDO_EVLOOP_ONCE 0x01 /* Only run once through the loop */
#define SUDO_EVLOOP_NONBLOCK 0x02 /* Do not block in event loop */
/* Event base flags (internal) */
#define SUDO_EVBASE_LOOPEXIT 0x01
#define SUDO_EVBASE_LOOPBREAK 0x02
#define SUDO_EVBASE_LOOPCONT 0x04
#define SUDO_EVBASE_GOT_EXIT 0x10
#define SUDO_EVBASE_GOT_BREAK 0x20
#define SUDO_EVBASE_GOT_MASK 0xf0
typedef void (*sudo_ev_callback_t)(int fd, int what, void *closure);
/* Member of struct sudo_event_base. */
struct sudo_event {
struct sudo_event *prev; /* must be first element in struct */
struct sudo_event *next; /* must be second element in struct */
struct sudo_event_base *base; /* base this event belongs to */
int fd; /* fd we are interested in */
short events; /* SUDO_EV_* flags */
short pfd_idx; /* index into pfds array */
sudo_ev_callback_t callback;/* user-provided callback */
void *closure; /* user-provided data pointer */
};
/* Tail queue of events. */
struct sudo_event_base {
struct sudo_event *first; /* must be first element in struct */
struct sudo_event *last; /* must be second element in struct */
struct sudo_event *pending; /* next event to be run in the event loop */
#ifdef HAVE_POLL
struct pollfd *pfds; /* array of struct pollfd */
int pfd_max; /* size of the pfds array */
int pfd_high; /* highest slot used */
int pfd_free; /* idx of next free entry or pfd_max if full */
#else
fd_set *readfds; /* read I/O descriptor set */
fd_set *writefds; /* write I/O descriptor set */
int maxfd; /* max fd we can store in readfds/writefds */
int nevents; /* number of events in the list */
#endif /* HAVE_POLL */
unsigned int flags; /* SUDO_EVBASE_* */
};
/* Allocate a new event base. */
struct sudo_event_base *sudo_ev_base_alloc(void);
/* Free an event base. */
void sudo_ev_base_free(struct sudo_event_base *base);
/* Allocate a new event. */
struct sudo_event *sudo_ev_alloc(int fd, short events, sudo_ev_callback_t callback, void *closure);
/* Free an event. */
void sudo_ev_free(struct sudo_event *ev);
/* Add an event, returns 0 on success, -1 on error */
int sudo_ev_add(struct sudo_event_base *head, struct sudo_event *ev, bool tohead);
/* Delete an event, returns 0 on success, -1 on error */
int sudo_ev_del(struct sudo_event_base *head, struct sudo_event *ev);
/* Main event loop, returns SUDO_CB_SUCCESS, SUDO_CB_BREAK or SUDO_CB_ERROR */
int sudo_ev_loop(struct sudo_event_base *head, int flags);
/* Cause the event loop to exit after one run through. */
void sudo_ev_loopexit(struct sudo_event_base *base);
/* Break out of the event loop right now. */
void sudo_ev_loopbreak(struct sudo_event_base *base);
/* Rescan for events and restart the event loop. */
void sudo_ev_loopcontinue(struct sudo_event_base *base);
/* Returns true if event loop stopped due to sudo_ev_loopexit(). */
bool sudo_ev_got_exit(struct sudo_event_base *base);
/* Returns true if event loop stopped due to sudo_ev_loopbreak(). */
bool sudo_ev_got_break(struct sudo_event_base *base);
/* Return the fd associated with an event. */
#define sudo_ev_get_fd(_ev) ((_ev) ? (_ev)->fd : -1)
/* Return the base an event is associated with or NULL. */
#define sudo_ev_get_base(_ev) ((_ev) ? (_ev)->base : NULL)
/*
* Backend implementation.
*/
int sudo_ev_base_alloc_impl(struct sudo_event_base *base);
void sudo_ev_base_free_impl(struct sudo_event_base *base);
int sudo_ev_add_impl(struct sudo_event_base *base, struct sudo_event *ev);
int sudo_ev_del_impl(struct sudo_event_base *base, struct sudo_event *ev);
int sudo_ev_loop_impl(struct sudo_event_base *base, int flags);
#endif /* _SUDO_EVENT_H */

View File

@ -65,7 +65,7 @@ sub mkdep {
# Expand some configure bits
$makefile =~ s:\@DEV\@::g;
$makefile =~ s:\@COMMON_OBJS\@:aix.lo:;
$makefile =~ s:\@COMMON_OBJS\@:aix.lo event_poll.lo event_select.lo:;
$makefile =~ s:\@SUDO_OBJS\@:openbsd.o preload.o selinux.o sesh.o solaris.o sudo_noexec.lo:;
$makefile =~ s:\@SUDOERS_OBJS\@:bsm_audit.lo linux_audit.lo ldap.lo sssd.lo:;
# XXX - fill in AUTH_OBJS from contents of the auth dir instead

View File

@ -615,7 +615,8 @@ iolog.lo: $(srcdir)/iolog.c $(top_builddir)/config.h $(srcdir)/sudoers.h \
$(incdir)/missing.h $(incdir)/fatal.h $(incdir)/alloc.h \
$(incdir)/list.h $(incdir)/fileops.h $(srcdir)/defaults.h \
$(devdir)/def_data.h $(srcdir)/logging.h $(srcdir)/sudo_nss.h \
$(incdir)/sudo_plugin.h $(incdir)/sudo_debug.h $(incdir)/gettext.h
$(incdir)/sudo_plugin.h $(incdir)/sudo_debug.h $(incdir)/gettext.h \
$(srcdir)/iolog.h
$(LIBTOOL) --mode=compile $(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/iolog.c
iolog_path.lo: $(srcdir)/iolog_path.c $(top_builddir)/config.h \
$(srcdir)/sudoers.h $(top_srcdir)/compat/stdbool.h \
@ -837,8 +838,9 @@ sudoreplay.o: $(srcdir)/sudoreplay.c $(top_builddir)/config.h \
$(top_srcdir)/compat/timespec.h $(top_srcdir)/compat/stdbool.h \
$(top_srcdir)/compat/getopt.h $(top_builddir)/pathnames.h \
$(incdir)/missing.h $(incdir)/alloc.h $(incdir)/fatal.h \
$(incdir)/gettext.h $(srcdir)/logging.h $(incdir)/sudo_plugin.h \
$(incdir)/sudo_conf.h $(incdir)/list.h $(incdir)/sudo_debug.h
$(incdir)/gettext.h $(srcdir)/logging.h $(srcdir)/iolog.h \
$(incdir)/sudo_plugin.h $(incdir)/sudo_conf.h $(incdir)/list.h \
$(incdir)/sudo_debug.h
$(CC) -c $(CPPFLAGS) $(CFLAGS) $(PIE_CFLAGS) $(SSP_CFLAGS) $(DEFS) $(srcdir)/sudoreplay.c
testsudoers.o: $(srcdir)/testsudoers.c $(top_builddir)/config.h \
$(top_srcdir)/compat/fnmatch.h $(srcdir)/tsgetgrpw.h \

View File

@ -89,9 +89,9 @@ SHELL = @SHELL@
PROGS = @PROGS@
OBJS = conversation.o env_hooks.o exec.o exec_common.o exec_pty.o get_pty.o \
hooks.o net_ifs.o load_plugins.o parse_args.o signal.o sudo.o \
sudo_edit.o tgetpass.o ttyname.o utmp.o @SUDO_OBJS@
OBJS = conversation.o env_hooks.o exec.o exec_common.o exec_pty.o \
get_pty.o hooks.o net_ifs.o load_plugins.o parse_args.o signal.o \
sudo.o sudo_edit.o tgetpass.o ttyname.o utmp.o @SUDO_OBJS@
SESH_OBJS = sesh.o locale_stub.o exec_common.o

View File

@ -1177,7 +1177,7 @@ exec_monitor(struct command_details *details, int backchannel)
warning(_("error reading from socketpair"));
goto done;
} else {
/* /* short read or EOF, parent process died? */
/* short read or EOF, parent process died? */
goto done;
}
}