summaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to 'include')
-rw-r--r--include/Makefile.in96
-rw-r--r--include/compat/charclass.h39
-rw-r--r--include/compat/endian.h72
-rw-r--r--include/compat/fnmatch.h32
-rw-r--r--include/compat/getaddrinfo.h83
-rw-r--r--include/compat/getopt.h81
-rw-r--r--include/compat/glob.h76
-rw-r--r--include/compat/nss_dbdefs.h106
-rw-r--r--include/compat/sha2.h98
-rw-r--r--include/compat/stdbool.h44
-rw-r--r--include/sudo_compat.h531
-rw-r--r--include/sudo_conf.h85
-rw-r--r--include/sudo_debug.h297
-rw-r--r--include/sudo_digest.h44
-rw-r--r--include/sudo_dso.h55
-rw-r--r--include/sudo_event.h193
-rw-r--r--include/sudo_fatal.h205
-rw-r--r--include/sudo_gettext.h75
-rw-r--r--include/sudo_lbuf.h53
-rw-r--r--include/sudo_plugin.h201
-rw-r--r--include/sudo_queue.h821
-rw-r--r--include/sudo_rand.h50
-rw-r--r--include/sudo_util.h275
23 files changed, 3612 insertions, 0 deletions
diff --git a/include/Makefile.in b/include/Makefile.in
new file mode 100644
index 0000000..cb39b36
--- /dev/null
+++ b/include/Makefile.in
@@ -0,0 +1,96 @@
+#
+# Copyright (c) 2011-2015, 2017-2018 Todd C. Miller <Todd.Miller@sudo.ws>
+#
+# 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.
+#
+# @configure_input@
+#
+
+#### Start of system configuration section. ####
+
+srcdir = @srcdir@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+includedir = @includedir@
+cross_compiling = @CROSS_COMPILING@
+
+# Our install program supports extra flags...
+INSTALL = $(SHELL) $(top_srcdir)/install-sh -c
+INSTALL_OWNER = -o $(install_uid) -g $(install_gid)
+
+# Where to install things...
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+bindir = @bindir@
+sbindir = @sbindir@
+sysconfdir = @sysconfdir@
+libexecdir = @libexecdir@
+datarootdir = @datarootdir@
+localstatedir = @localstatedir@
+
+# User and group ids the installed files should be "owned" by
+install_uid = 0
+install_gid = 0
+
+#### End of system configuration section. ####
+
+SHELL = @SHELL@
+
+all:
+
+Makefile: $(srcdir)/Makefile.in
+ cd $(top_builddir) && ./config.status --file include/Makefile
+
+.SUFFIXES: .h
+
+pre-install:
+
+install: install-includes
+
+install-dirs:
+ $(SHELL) $(top_srcdir)/mkinstalldirs $(DESTDIR)$(includedir)
+
+install-binaries:
+
+install-doc:
+
+install-includes: install-dirs
+ $(INSTALL) $(INSTALL_OWNER) -m 0644 $(srcdir)/sudo_plugin.h $(DESTDIR)$(includedir)
+
+install-plugin:
+
+uninstall:
+ -rm -f $(DESTDIR)$(includedir)/sudo_plugin.h
+
+splint:
+
+cppcheck:
+
+pvs-log-files:
+
+pvs-studio:
+
+check:
+
+clean:
+
+mostlyclean: clean
+
+distclean: clean
+ -rm -rf Makefile
+
+clobber: distclean
+
+realclean: distclean
+
+cleandir: distclean
diff --git a/include/compat/charclass.h b/include/compat/charclass.h
new file mode 100644
index 0000000..645e884
--- /dev/null
+++ b/include/compat/charclass.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2008, 2010 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * 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.
+ */
+
+/*
+ * POSIX character class support for fnmatch() and glob().
+ */
+static struct cclass {
+ const char *name;
+ int (*isctype)(int);
+} cclasses[] = {
+ { "alnum", isalnum },
+ { "alpha", isalpha },
+ { "blank", isblank },
+ { "cntrl", iscntrl },
+ { "digit", isdigit },
+ { "graph", isgraph },
+ { "lower", islower },
+ { "print", isprint },
+ { "punct", ispunct },
+ { "space", isspace },
+ { "upper", isupper },
+ { "xdigit", isxdigit },
+ { NULL, NULL }
+};
+
+#define NCCLASSES (nitems(cclasses) - 1)
diff --git a/include/compat/endian.h b/include/compat/endian.h
new file mode 100644
index 0000000..279549c
--- /dev/null
+++ b/include/compat/endian.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2013 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * 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 COMPAT_ENDIAN_H
+#define COMPAT_ENDIAN_H
+
+#ifndef BYTE_ORDER
+# undef LITTLE_ENDIAN
+# define LITTLE_ENDIAN 1234
+# undef BIG_ENDIAN
+# define BIG_ENDIAN 4321
+# undef UNKNOWN_ENDIAN
+# define UNKNOWN_ENDIAN 0
+
+/*
+ * Attempt to guess endianness.
+ * Solaris may define _LITTLE_ENDIAN and _BIG_ENDIAN to 1
+ * HP-UX may define __LITTLE_ENDIAN__ and __BIG_ENDIAN__ to 1
+ * Otherwise, check for cpu-specific cpp defines.
+ * Note that some CPUs are bi-endian, including: arm, powerpc, alpha,
+ * sparc64, mips, hppa, sh4 and ia64.
+ * We just check for the most common uses.
+ */
+
+# if defined(__BYTE_ORDER)
+# define BYTE_ORDER __BYTE_ORDER
+# elif defined(_BYTE_ORDER)
+# define BYTE_ORDER _BYTE_ORDER
+# elif defined(_LITTLE_ENDIAN) || defined(__LITTLE_ENDIAN__)
+# define BYTE_ORDER LITTLE_ENDIAN
+# elif defined(_BIG_ENDIAN) || defined(__BIG_ENDIAN__)
+# define BYTE_ORDER BIG_ENDIAN
+# elif defined(__alpha__) || defined(__alpha) || defined(__amd64) || \
+ defined(BIT_ZERO_ON_RIGHT) || defined(i386) || defined(__i386) || \
+ defined(MIPSEL) || defined(_MIPSEL) || defined(ns32000) || \
+ defined(__ns3200) || defined(sun386) || defined(vax) || \
+ defined(__vax) || defined(__x86__) || \
+ (defined(sun) && defined(__powerpc)) || \
+ (!defined(__hpux) && defined(__ia64))
+# define BYTE_ORDER LITTLE_ENDIAN
+# elif defined(__68k__) || defined(apollo) || defined(BIT_ZERO_ON_LEFT) || \
+ defined(__convex__) || defined(_CRAY) || defined(DGUX) || \
+ defined(__hppa) || defined(__hp9000) || defined(__hp9000s300) || \
+ defined(__hp9000s700) || defined(__hp3000s900) || \
+ defined(ibm032) || defined(ibm370) || defined(_IBMR2) || \
+ defined(is68k) || defined(mc68000) || defined(m68k) || \
+ defined(__m68k) || defined(m88k) || defined(__m88k) || \
+ defined(MIPSEB) || defined(_MIPSEB) || defined(MPE) || \
+ defined(pyr) || defined(__powerpc) || defined(__powerpc__) || \
+ defined(sel) || defined(__sparc) || defined(__sparc__) || \
+ defined(tahoe) || (defined(__hpux) && defined(__ia64)) || \
+ (defined(sun) && defined(__powerpc))
+# define BYTE_ORDER BIG_ENDIAN
+# else
+# define BYTE_ORDER UNKNOWN_ENDIAN
+# endif
+#endif /* BYTE_ORDER */
+
+#endif /* COMPAT_ENDIAN_H */
diff --git a/include/compat/fnmatch.h b/include/compat/fnmatch.h
new file mode 100644
index 0000000..fb81e38
--- /dev/null
+++ b/include/compat/fnmatch.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2011 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * 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 COMPAT_FNMATCH_H
+#define COMPAT_FNMATCH_H
+
+#define FNM_NOMATCH 1 /* String does not match pattern */
+
+#define FNM_PATHNAME (1 << 0) /* Globbing chars don't match '/' */
+#define FNM_PERIOD (1 << 1) /* Leading '.' in string must exactly */
+#define FNM_NOESCAPE (1 << 2) /* Backslash treated as ordinary char */
+#define FNM_LEADING_DIR (1 << 3) /* Only match the leading directory */
+#define FNM_CASEFOLD (1 << 4) /* Case insensitive matching */
+
+__dso_public int sudo_fnmatch(const char *pattern, const char *string, int flags);
+
+#define fnmatch(_a, _b, _c) sudo_fnmatch((_a), (_b), (_c))
+
+#endif /* COMPAT_FNMATCH_H */
diff --git a/include/compat/getaddrinfo.h b/include/compat/getaddrinfo.h
new file mode 100644
index 0000000..6f2f203
--- /dev/null
+++ b/include/compat/getaddrinfo.h
@@ -0,0 +1,83 @@
+/*
+ * Replacement implementation of getaddrinfo.
+ *
+ * This is an implementation of the getaddrinfo family of functions for
+ * systems that lack it, so that code can use getaddrinfo always. It provides
+ * IPv4 support only; for IPv6 support, a native getaddrinfo implemenation is
+ * required.
+ *
+ * The canonical version of this file is maintained in the rra-c-util package,
+ * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
+ *
+ * Written by Russ Allbery <rra@stanford.edu>
+ *
+ * The authors hereby relinquish any claim to any copyright that they may have
+ * in this work, whether granted under contract or by operation of law or
+ * international treaty, and hereby commit to the public, at large, that they
+ * shall not, at any time in the future, seek to enforce any copyright in this
+ * work against any person or entity, or prevent any person or entity from
+ * copying, publishing, distributing or creating derivative works of this
+ * work.
+ */
+
+#ifndef COMPAT_GETADDRINFO_H
+#define COMPAT_GETADDRINFO_H
+
+#include <config.h>
+
+/* Skip this entire file if a system getaddrinfo was detected. */
+#ifndef HAVE_GETADDRINFO
+
+/* OpenBSD likes to have sys/types.h included before sys/socket.h. */
+#include <sys/types.h>
+#include <sys/socket.h>
+
+/* The struct returned by getaddrinfo, from RFC 3493. */
+struct addrinfo {
+ int ai_flags; /* AI_PASSIVE, AI_CANONNAME, .. */
+ int ai_family; /* AF_xxx */
+ int ai_socktype; /* SOCK_xxx */
+ int ai_protocol; /* 0 or IPPROTO_xxx for IPv4 and IPv6 */
+ socklen_t ai_addrlen; /* Length of ai_addr */
+ char *ai_canonname; /* Canonical name for nodename */
+ struct sockaddr *ai_addr; /* Binary address */
+ struct addrinfo *ai_next; /* Next structure in linked list */
+};
+
+/* Constants for ai_flags from RFC 3493, combined with binary or. */
+#define AI_PASSIVE 0x0001
+#define AI_CANONNAME 0x0002
+#define AI_NUMERICHOST 0x0004
+#define AI_NUMERICSERV 0x0008
+#define AI_V4MAPPED 0x0010
+#define AI_ALL 0x0020
+#define AI_ADDRCONFIG 0x0040
+
+/* Error return codes from RFC 3493. */
+#define EAI_AGAIN 1 /* Temporary name resolution failure */
+#define EAI_BADFLAGS 2 /* Invalid value in ai_flags parameter */
+#define EAI_FAIL 3 /* Permanent name resolution failure */
+#define EAI_FAMILY 4 /* Address family not recognized */
+#define EAI_MEMORY 5 /* Memory allocation failure */
+#define EAI_NONAME 6 /* nodename or servname unknown */
+#define EAI_SERVICE 7 /* Service not recognized for socket type */
+#define EAI_SOCKTYPE 8 /* Socket type not recognized */
+#define EAI_SYSTEM 9 /* System error occurred, see errno */
+#define EAI_OVERFLOW 10 /* An argument buffer overflowed */
+
+/* Function prototypes. */
+__dso_public int sudo_getaddrinfo(const char *nodename, const char *servname,
+ const struct addrinfo *hints, struct addrinfo **res);
+__dso_public void sudo_freeaddrinfo(struct addrinfo *ai);
+__dso_public const char *sudo_gai_strerror(int ecode);
+
+/* Map sudo_* to RFC 3493 names. */
+#undef getaddrinfo
+#define getaddrinfo(_a, _b, _c, _d) sudo_getaddrinfo((_a), (_b), (_c), (_d))
+#undef freeaddrinfo
+#define freeaddrinfo(_a) sudo_freeaddrinfo((_a))
+#undef gai_strerror
+#define gai_strerror(_a) sudo_gai_strerror((_a))
+
+#endif /* !HAVE_GETADDRINFO */
+#endif /* COMPAT_GETADDRINFO_H */
diff --git a/include/compat/getopt.h b/include/compat/getopt.h
new file mode 100644
index 0000000..8f49518
--- /dev/null
+++ b/include/compat/getopt.h
@@ -0,0 +1,81 @@
+/* $OpenBSD: getopt.h,v 1.2 2008/06/26 05:42:04 ray Exp $ */
+/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */
+/* $FreeBSD: head/include/getopt.h 203963 2010-02-16 19:28:10Z imp $ */
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Dieter Baron and Thomas Klausner.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef COMPAT_GETOPT_H
+#define COMPAT_GETOPT_H
+
+/*
+ * GNU-like getopt_long()/getopt_long_only() with 4.4BSD optreset extension.
+ */
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+struct option {
+ /* name of long option */
+ const char *name;
+ /*
+ * one of no_argument, required_argument, and optional_argument:
+ * whether option takes an argument
+ */
+ int has_arg;
+ /* if not NULL, set *flag to val when option found */
+ int *flag;
+ /* if flag not NULL, value to set *flag to; else return value */
+ int val;
+};
+
+__dso_public int sudo_getopt_long(int, char * const *, const char *,
+ const struct option *, int *);
+#undef getopt_long
+#define getopt_long(_a, _b, _c, _d, _e) \
+ sudo_getopt_long((_a), (_b), (_c), (_d), (_e))
+
+__dso_public int sudo_getopt_long_only(int, char * const *, const char *,
+ const struct option *, int *);
+#undef getopt_long_only
+#define getopt_long_only(_a, _b, _c, _d, _e) \
+ sudo_getopt_long_only((_a), (_b), (_c), (_d), (_e))
+#if 0
+__dso_public int sudo_getopt(int, char * const [], const char *);
+#undef getopt
+#define getopt(_a, _b, _c) sudo_getopt((_a), (_b), (_c))
+#endif
+
+extern char *optarg; /* getopt(3) external variables */
+extern int opterr;
+extern int optind;
+extern int optopt;
+extern int optreset;
+
+#endif /* !COMPAT_GETOPT_H */
diff --git a/include/compat/glob.h b/include/compat/glob.h
new file mode 100644
index 0000000..40f5d39
--- /dev/null
+++ b/include/compat/glob.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)glob.h 8.1 (Berkeley) 6/2/93
+ */
+
+#ifndef COMPAT_GLOB_H
+#define COMPAT_GLOB_H
+
+struct stat;
+typedef struct {
+ int gl_pathc; /* Count of total paths so far. */
+ int gl_matchc; /* Count of paths matching pattern. */
+ int gl_offs; /* Reserved at beginning of gl_pathv. */
+ int gl_flags; /* Copy of flags parameter to glob. */
+ char **gl_pathv; /* List of paths matching pattern. */
+ /* Copy of errfunc parameter to glob. */
+ int (*gl_errfunc)(const char *, int);
+} glob_t;
+
+/* Flags */
+#define GLOB_APPEND 0x0001 /* Append to output from previous call. */
+#define GLOB_DOOFFS 0x0002 /* Use gl_offs. */
+#define GLOB_ERR 0x0004 /* Return on error. */
+#define GLOB_MARK 0x0008 /* Append / to matching directories. */
+#define GLOB_NOCHECK 0x0010 /* Return pattern itself if nothing matches. */
+#define GLOB_NOSORT 0x0020 /* Don't sort. */
+#define GLOB_NOESCAPE 0x0040 /* Disable backslash escaping. */
+
+/* Non-POSIX extensions */
+#define GLOB_BRACE 0x0080 /* Expand braces ala csh. */
+#define GLOB_MAGCHAR 0x0100 /* Pattern had globbing characters. */
+#define GLOB_TILDE 0x0200 /* Expand tilde names from the passwd file. */
+#define GLOB_LIMIT 0x0400 /* Limit pattern match output to ARG_MAX */
+
+/* Error values returned by glob(3) */
+#define GLOB_NOSPACE (-1) /* Malloc call failed. */
+#define GLOB_ABORTED (-2) /* Unignored error. */
+#define GLOB_NOMATCH (-3) /* No match and GLOB_NOCHECK not set. */
+#define GLOB_NOSYS (-4) /* Function not supported. */
+
+__dso_public int sudo_glob(const char *, int, int (*)(const char *, int), glob_t *);
+__dso_public void sudo_globfree(glob_t *);
+
+#define glob(_a, _b, _c, _d) sudo_glob((_a), (_b), (_c), (_d))
+#define globfree(_a) sudo_globfree((_a))
+
+#endif /* !COMPAT_GLOB_H */
diff --git a/include/compat/nss_dbdefs.h b/include/compat/nss_dbdefs.h
new file mode 100644
index 0000000..bc3f9b9
--- /dev/null
+++ b/include/compat/nss_dbdefs.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2013 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * 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 COMPAT_NSS_DBDEFS_H
+#define COMPAT_NSS_DBDEFS_H
+
+/*
+ * Bits of nss_dbdefs.h and nss_common.h needed to implement
+ * getgrouplist(3) using nss_search(3).
+ *
+ * HP-UX does not ship those headers so we need this compatibility header.
+ * It may also work on other systems that use a Solaris-derived nsswitch
+ * API.
+ */
+
+#ifdef NEED_HPUX_MUTEX
+# include <synch.h>
+#endif
+
+typedef enum {
+ NSS_SUCCESS,
+ NSS_NOTFOUND,
+ NSS_UNAVAIL
+} nss_status_t;
+
+typedef struct nss_db_params {
+ const char *name;
+ const char *config_name;
+ const char *default_config;
+ unsigned int max_active_per_src;
+ unsigned int max_dormant_per_src;
+ int flags;
+ void *finders;
+ void *private;
+ void (*cleanup)(struct nss_db_params *);
+} nss_db_params_t;
+
+struct nss_groupsbymem {
+ const char *username;
+ gid_t *gid_array;
+ int maxgids;
+ int force_slow_way;
+ int (*str2ent)(const char *, int, void *, char *, int);
+ nss_status_t (*process_cstr)(const char *, int, struct nss_groupsbymem *);
+ int numgids;
+};
+
+typedef struct {
+ void *result; /* group struct to fill in. */
+ char *buffer; /* string buffer for above */
+ size_t buflen; /* string buffer size */
+} nss_XbyY_buf_t;
+
+typedef struct {
+ void *state; /* really struct nss_db_state * */
+#ifdef NEED_HPUX_MUTEX
+ lwp_mutex_t lock;
+#endif
+} nss_db_root_t;
+
+#ifdef NEED_HPUX_MUTEX
+# define NSS_DB_ROOT_INIT { 0, LWP_MUTEX_INITIALIZER }
+#else
+# define NSS_DB_ROOT_INIT { 0 }
+#endif
+# define DEFINE_NSS_DB_ROOT(name) nss_db_root_t name = NSS_DB_ROOT_INIT
+
+/* Backend function to find all groups a user belongs to for initgroups(). */
+#define NSS_DBOP_GROUP_BYMEMBER 6
+
+/* str2ent function return values */
+#define NSS_STR_PARSE_SUCCESS 0
+#define NSS_STR_PARSE_PARSE 1
+#define NSS_STR_PARSE_ERANGE 2
+
+/* Max length for an /etc/group file line. */
+#define NSS_BUFLEN_GROUP 8192
+
+/* HP-UX uses an extra underscore for these functions. */
+#ifdef HAVE___NSS_INITF_GROUP
+# define _nss_initf_group __nss_initf_group
+#endif
+#ifdef HAVE___NSS_XBYY_BUF_ALLOC
+# define _nss_XbyY_buf_alloc __nss_XbyY_buf_alloc
+# define _nss_XbyY_buf_free __nss_XbyY_buf_free
+#endif
+
+typedef void (*nss_db_initf_t)(nss_db_params_t *);
+extern nss_status_t nss_search(nss_db_root_t *, nss_db_initf_t, int, void *);
+extern nss_XbyY_buf_t *_nss_XbyY_buf_alloc(int, int);
+extern void _nss_XbyY_buf_free(nss_XbyY_buf_t *);
+
+#endif /* COMPAT_NSS_DBDEFS_H */
diff --git a/include/compat/sha2.h b/include/compat/sha2.h
new file mode 100644
index 0000000..3638423
--- /dev/null
+++ b/include/compat/sha2.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2013-2015 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * 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.
+ */
+
+/*
+ * Derived from the public domain SHA-1 and SHA-2 implementations
+ * by Steve Reid and Wei Dai respectively.
+ */
+
+#ifndef COMPAT_SHA2_H
+#define COMPAT_SHA2_H
+
+#define SHA224_BLOCK_LENGTH 64
+#define SHA224_DIGEST_LENGTH 28
+#define SHA224_DIGEST_STRING_LENGTH (SHA224_DIGEST_LENGTH * 2 + 1)
+
+#define SHA256_BLOCK_LENGTH 64
+#define SHA256_DIGEST_LENGTH 32
+#define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2 + 1)
+
+#define SHA384_BLOCK_LENGTH 128
+#define SHA384_DIGEST_LENGTH 48
+#define SHA384_DIGEST_STRING_LENGTH (SHA384_DIGEST_LENGTH * 2 + 1)
+
+#define SHA512_BLOCK_LENGTH 128
+#define SHA512_DIGEST_LENGTH 64
+#define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1)
+
+typedef struct {
+ union {
+ uint32_t st32[8]; /* sha224 and sha256 */
+ uint64_t st64[8]; /* sha384 and sha512 */
+ } state;
+ uint64_t count[2];
+ uint8_t buffer[SHA512_BLOCK_LENGTH];
+} SHA2_CTX;
+
+__dso_public void sudo_SHA224Init(SHA2_CTX *ctx);
+__dso_public void sudo_SHA224Pad(SHA2_CTX *ctx);
+__dso_public void sudo_SHA224Transform(uint32_t state[8], const uint8_t buffer[SHA224_BLOCK_LENGTH]);
+__dso_public void sudo_SHA224Update(SHA2_CTX *ctx, const uint8_t *data, size_t len);
+__dso_public void sudo_SHA224Final(uint8_t digest[SHA224_DIGEST_LENGTH], SHA2_CTX *ctx);
+
+#define SHA224Init sudo_SHA224Init
+#define SHA224Pad sudo_SHA224Pad
+#define SHA224Transform sudo_SHA224Transform
+#define SHA224Update sudo_SHA224Update
+#define SHA224Final sudo_SHA224Final
+
+__dso_public void sudo_SHA256Init(SHA2_CTX *ctx);
+__dso_public void sudo_SHA256Pad(SHA2_CTX *ctx);
+__dso_public void sudo_SHA256Transform(uint32_t state[8], const uint8_t buffer[SHA256_BLOCK_LENGTH]);
+__dso_public void sudo_SHA256Update(SHA2_CTX *ctx, const uint8_t *data, size_t len);
+__dso_public void sudo_SHA256Final(uint8_t digest[SHA256_DIGEST_LENGTH], SHA2_CTX *ctx);
+
+#define SHA256Init sudo_SHA256Init
+#define SHA256Pad sudo_SHA256Pad
+#define SHA256Transform sudo_SHA256Transform
+#define SHA256Update sudo_SHA256Update
+#define SHA256Final sudo_SHA256Final
+
+__dso_public void sudo_SHA384Init(SHA2_CTX *ctx);
+__dso_public void sudo_SHA384Pad(SHA2_CTX *ctx);
+__dso_public void sudo_SHA384Transform(uint64_t state[8], const uint8_t buffer[SHA384_BLOCK_LENGTH]);
+__dso_public void sudo_SHA384Update(SHA2_CTX *ctx, const uint8_t *data, size_t len);
+__dso_public void sudo_SHA384Final(uint8_t digest[SHA384_DIGEST_LENGTH], SHA2_CTX *ctx);
+
+#define SHA384Init sudo_SHA384Init
+#define SHA384Pad sudo_SHA384Pad
+#define SHA384Transform sudo_SHA384Transform
+#define SHA384Update sudo_SHA384Update
+#define SHA384Final sudo_SHA384Final
+
+__dso_public void sudo_SHA512Init(SHA2_CTX *ctx);
+__dso_public void sudo_SHA512Pad(SHA2_CTX *ctx);
+__dso_public void sudo_SHA512Transform(uint64_t state[8], const uint8_t buffer[SHA512_BLOCK_LENGTH]);
+__dso_public void sudo_SHA512Update(SHA2_CTX *ctx, const uint8_t *data, size_t len);
+__dso_public void sudo_SHA512Final(uint8_t digest[SHA512_DIGEST_LENGTH], SHA2_CTX *ctx);
+
+#define SHA512Init sudo_SHA512Init
+#define SHA512Pad sudo_SHA512Pad
+#define SHA512Transform sudo_SHA512Transform
+#define SHA512Update sudo_SHA512Update
+#define SHA512Final sudo_SHA512Final
+
+#endif /* COMPAT_SHA2_H */
diff --git a/include/compat/stdbool.h b/include/compat/stdbool.h
new file mode 100644
index 0000000..b865a46
--- /dev/null
+++ b/include/compat/stdbool.h
@@ -0,0 +1,44 @@
+/* $OpenBSD: stdbool.h,v 1.5 2010/07/24 22:17:03 guenther Exp $ */
+
+/*
+ * Written by Marc Espie, September 25, 1999
+ * Public domain.
+ */
+
+#ifndef COMPAT_STDBOOL_H
+#define COMPAT_STDBOOL_H
+
+#ifndef __cplusplus
+
+#if (defined(HAVE__BOOL) && HAVE__BOOL > 0) || defined(lint)
+/* Support for _C99: type _Bool is already built-in. */
+#define false 0
+#define true 1
+
+#else
+/* `_Bool' type must promote to `int' or `unsigned int'. */
+typedef enum {
+ false = 0,
+ true = 1
+} _Bool;
+
+/* And those constants must also be available as macros. */
+#define false false
+#define true true
+
+#endif
+
+/* User visible type `bool' is provided as a macro which may be redefined */
+#define bool _Bool
+
+#else /* __cplusplus */
+#define _Bool bool
+#define bool bool
+#define false false
+#define true true
+#endif /* __cplusplus */
+
+/* Inform that everything is fine */
+#define __bool_true_false_are_defined 1
+
+#endif /* COMPAT_STDBOOL_H */
diff --git a/include/sudo_compat.h b/include/sudo_compat.h
new file mode 100644
index 0000000..5c32840
--- /dev/null
+++ b/include/sudo_compat.h
@@ -0,0 +1,531 @@
+/*
+ * Copyright (c) 1996, 1998-2005, 2008, 2009-2018
+ * Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * 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.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+#ifndef SUDO_COMPAT_H
+#define SUDO_COMPAT_H
+
+#include <stdio.h>
+#if !defined(HAVE_VSNPRINTF) || !defined(HAVE_VASPRINTF) || \
+ !defined(HAVE_VSYSLOG) || defined(PREFER_PORTABLE_SNPRINTF)
+# include <stdarg.h>
+#endif
+#if !defined(HAVE_MEMSET_S) && !defined(rsize_t)
+# include <stddef.h> /* for rsize_t */
+# ifdef HAVE_STRING_H
+# include <string.h> /* for rsize_t on AIX */
+# endif /* HAVE_STRING_H */
+#endif /* HAVE_MEMSET_S && rsize_t */
+
+/*
+ * Macros and functions that may be missing on some operating systems.
+ */
+
+#ifndef __GNUC_PREREQ__
+# ifdef __GNUC__
+# define __GNUC_PREREQ__(ma, mi) \
+ ((__GNUC__ > (ma)) || (__GNUC__ == (ma) && __GNUC_MINOR__ >= (mi)))
+# else
+# define __GNUC_PREREQ__(ma, mi) 0
+# endif
+#endif
+
+/* Define away __attribute__ for non-gcc or old gcc */
+#if !defined(__attribute__) && !__GNUC_PREREQ__(2, 5)
+# define __attribute__(x)
+#endif
+
+/* For catching format string mismatches */
+#ifndef __printflike
+# if __GNUC_PREREQ__(3, 3)
+# define __printflike(f, v) __attribute__((__format__ (__printf__, f, v))) __attribute__((__nonnull__ (f)))
+# elif __GNUC_PREREQ__(2, 7)
+# define __printflike(f, v) __attribute__((__format__ (__printf__, f, v)))
+# else
+# define __printflike(f, v)
+# endif
+#endif
+#ifndef __printf0like
+# if __GNUC_PREREQ__(2, 7)
+# define __printf0like(f, v) __attribute__((__format__ (__printf__, f, v)))
+# else
+# define __printf0like(f, v)
+# endif
+#endif
+#ifndef __format_arg
+# if __GNUC_PREREQ__(2, 7)
+# define __format_arg(f) __attribute__((__format_arg__ (f)))
+# else
+# define __format_arg(f)
+# endif
+#endif
+
+/*
+ * Given the pointer x to the member m of the struct s, return
+ * a pointer to the containing structure.
+ */
+#ifndef __containerof
+# define __containerof(x, s, m) ((s *)((char *)(x) - offsetof(s, m)))
+#endif
+
+#ifndef __dso_public
+# ifdef HAVE_DSO_VISIBILITY
+# if defined(__GNUC__)
+# define __dso_public __attribute__((__visibility__("default")))
+# define __dso_hidden __attribute__((__visibility__("hidden")))
+# elif defined(__SUNPRO_C)
+# define __dso_public __global
+# define __dso_hidden __hidden
+# else
+# define __dso_public __declspec(dllexport)
+# define __dso_hidden
+# endif
+# else
+# define __dso_public
+# define __dso_hidden
+# endif
+#endif
+
+/*
+ * Pre-C99 compilers may lack a va_copy macro.
+ */
+#ifndef va_copy
+# ifdef __va_copy
+# define va_copy(d, s) __va_copy(d, s)
+# else
+# define va_copy(d, s) memcpy(&(d), &(s), sizeof(d));
+# endif
+#endif
+
+/*
+ * Some systems lack full limit definitions.
+ */
+#if defined(HAVE_DECL_LLONG_MAX) && !HAVE_DECL_LLONG_MAX
+# if defined(HAVE_DECL_QUAD_MAX) && HAVE_DECL_QUAD_MAX
+# define LLONG_MAX QUAD_MAX
+# else
+# define LLONG_MAX 0x7fffffffffffffffLL
+# endif
+#endif
+
+#if defined(HAVE_DECL_LLONG_MIN) && !HAVE_DECL_LLONG_MIN
+# if defined(HAVE_DECL_QUAD_MIN) && HAVE_DECL_QUAD_MIN
+# define LLONG_MIN QUAD_MIN
+# else
+# define LLONG_MIN (-0x7fffffffffffffffLL-1)
+# endif
+#endif
+
+#if defined(HAVE_DECL_ULLONG_MAX) && !HAVE_DECL_ULLONG_MAX
+# if defined(HAVE_DECL_UQUAD_MAX) && HAVE_DECL_UQUAD_MAX
+# define ULLONG_MAX UQUAD_MAX
+# else
+# define ULLONG_MAX 0xffffffffffffffffULL
+# endif
+#endif
+
+#if defined(HAVE_DECL_SIZE_MAX) && !HAVE_DECL_SIZE_MAX
+# if defined(HAVE_DECL_SIZE_T_MAX) && HAVE_DECL_SIZE_T_MAX
+# define SIZE_MAX SIZE_T_MAX
+# else
+# define SIZE_MAX ULONG_MAX
+# endif
+#endif
+
+#if defined(HAVE_DECL_PATH_MAX) && !HAVE_DECL_PATH_MAX
+# if defined(HAVE_DECL__POSIX_PATH_MAX) && HAVE_DECL__POSIX_PATH_MAX
+# define PATH_MAX _POSIX_PATH_MAX
+# else
+# define PATH_MAX 256
+# endif
+#endif
+
+/*
+ * POSIX versions for those without...
+ */
+#ifndef _S_IFMT
+# define _S_IFMT S_IFMT
+#endif /* _S_IFMT */
+#ifndef _S_IFREG
+# define _S_IFREG S_IFREG
+#endif /* _S_IFREG */
+#ifndef _S_IFDIR
+# define _S_IFDIR S_IFDIR
+#endif /* _S_IFDIR */
+#ifndef _S_IFLNK
+# define _S_IFLNK S_IFLNK
+#endif /* _S_IFLNK */
+#ifndef _S_IFIFO
+# define _S_IFIFO S_IFIFO
+#endif /* _S_IFIFO */
+#ifndef S_ISREG
+# define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
+#endif /* S_ISREG */
+#ifndef S_ISDIR
+# define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
+#endif /* S_ISDIR */
+#ifndef S_ISLNK
+# define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK)
+#endif /* S_ISLNK */
+#ifndef S_ISFIFO
+# define S_ISFIFO(m) (((m) & _S_IFMT) == _S_IFIFO)
+#endif /* S_ISLNK */
+#ifndef S_ISTXT
+# define S_ISTXT 0001000
+#endif /* S_ISTXT */
+
+/*
+ * ACCESSPERMS (00777) and ALLPERMS (07777) are handy BSDisms
+ */
+#ifndef ACCESSPERMS
+# define ACCESSPERMS (S_IRWXU|S_IRWXG|S_IRWXO)
+#endif /* ACCESSPERMS */
+#ifndef ALLPERMS
+# define ALLPERMS (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO)
+#endif /* ALLPERMS */
+
+/* For futimens() and utimensat() emulation. */
+#if !defined(HAVE_FUTIMENS) && !defined(HAVE_UTIMENSAT)
+# ifndef UTIME_OMIT
+# define UTIME_OMIT -1L
+# endif
+# ifndef UTIME_NOW
+# define UTIME_NOW -2L
+# endif
+#endif
+#if !defined(HAVE_OPENAT) || (!defined(HAVE_FUTIMENS) && !defined(HAVE_UTIMENSAT))
+# ifndef AT_FDCWD
+# define AT_FDCWD -100
+# endif
+#endif
+
+/* For pipe2() emulation. */
+#if !defined(HAVE_PIPE2) && defined(O_NONBLOCK) && !defined(O_CLOEXEC)
+# define O_CLOEXEC 0x80000000
+#endif
+
+/*
+ * BSD defines these in <sys/param.h> but we don't include that anymore.
+ */
+#ifndef MIN
+# define MIN(a,b) (((a)<(b))?(a):(b))
+#endif
+#ifndef MAX
+# define MAX(a,b) (((a)>(b))?(a):(b))
+#endif
+
+/* Macros to set/clear/test flags. */
+#undef SET
+#define SET(t, f) ((t) |= (f))
+#undef CLR
+#define CLR(t, f) ((t) &= ~(f))
+#undef ISSET
+#define ISSET(t, f) ((t) & (f))
+
+/*
+ * Some systems define this in <sys/param.h> but we don't include that anymore.
+ */
+#ifndef howmany
+# define howmany(x, y) (((x) + ((y) - 1)) / (y))
+#endif
+
+/*
+ * Simple isblank() macro and function for systems without it.
+ */
+#ifndef HAVE_ISBLANK
+__dso_public int isblank(int);
+# define isblank(_x) ((_x) == ' ' || (_x) == '\t')
+#endif
+
+/*
+ * NCR's SVr4 has _innetgr(3) instead of innetgr(3) for some reason.
+ */
+#ifdef HAVE__INNETGR
+# define innetgr(n, h, u, d) (_innetgr(n, h, u, d))
+# define HAVE_INNETGR 1
+#endif /* HAVE__INNETGR */
+
+/*
+ * The nitems macro may be defined in sys/param.h
+ */
+#ifndef nitems
+# define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
+#endif
+
+/*
+ * If dirfd() does not exists, hopefully dd_fd does.
+ */
+#if !defined(HAVE_DIRFD) && defined(HAVE_DD_FD)
+# define dirfd(_d) ((_d)->dd_fd)
+# define HAVE_DIRFD
+#endif
+
+#if !defined(HAVE_KILLPG) && !defined(killpg)
+# define killpg(p, s) kill(-(p), (s))
+#endif
+
+/*
+ * Declare errno if errno.h doesn't do it for us.
+ */
+#if defined(HAVE_DECL_ERRNO) && !HAVE_DECL_ERRNO
+extern int errno;
+#endif /* !HAVE_DECL_ERRNO */
+
+/* Not all systems define NSIG in signal.h */
+#if !defined(NSIG)
+# if defined(_NSIG)
+# define NSIG _NSIG
+# elif defined(__NSIG)
+# define NSIG __NSIG
+# else
+# define NSIG 64
+# endif
+#endif
+
+/* For sig2str() */
+#if !defined(HAVE_DECL_SIG2STR_MAX) || !HAVE_DECL_SIG2STR_MAX
+# define SIG2STR_MAX 32
+#endif
+
+/* WCOREDUMP is not POSIX, this usually works (verified on AIX). */
+#ifndef WCOREDUMP
+# define WCOREDUMP(x) ((x) & 0x80)
+#endif
+
+/* W_EXITCODE is not POSIX but the encoding of wait status is. */
+#ifndef W_EXITCODE
+# define W_EXITCODE(ret, sig) ((ret) << 8 | (sig))
+#endif
+
+/* Number of bits in a byte. */
+#ifndef NBBY
+# ifdef __NBBY
+# define NBBY __NBBY
+# else
+# define NBBY 8
+# endif
+#endif
+
+#ifndef HAVE_SETEUID
+# if defined(HAVE_SETRESUID)
+# define seteuid(u) setresuid(-1, (u), -1)
+# define setegid(g) setresgid(-1, (g), -1)
+# define HAVE_SETEUID 1
+# elif defined(HAVE_SETREUID)
+# define seteuid(u) setreuid(-1, (u))
+# define setegid(g) setregid(-1, (g))
+# define HAVE_SETEUID 1
+# endif
+#endif /* HAVE_SETEUID */
+
+/*
+ * Older HP-UX does not declare setresuid() or setresgid().
+ */
+#if defined(HAVE_DECL_SETRESUID) && !HAVE_DECL_SETRESUID
+int setresuid(uid_t, uid_t, uid_t);
+int setresgid(gid_t, gid_t, gid_t);
+#endif
+#if defined(HAVE_DECL_GETRESUID) && !HAVE_DECL_GETRESUID
+int getresuid(uid_t *, uid_t *, uid_t *);
+int getresgid(gid_t *, gid_t *, gid_t *);
+#endif
+
+/*
+ * HP-UX does not declare innetgr() or getdomainname().
+ * Solaris does not declare getdomainname().
+ */
+#if defined(HAVE_DECL_INNETGR) && !HAVE_DECL_INNETGR
+int innetgr(const char *, const char *, const char *, const char *);
+#endif
+#if defined(HAVE_DECL__INNETGR) && !HAVE_DECL__INNETGR
+int _innetgr(const char *, const char *, const char *, const char *);
+#endif
+#if defined(HAVE_DECL_GETDOMAINNAME) && !HAVE_DECL_GETDOMAINNAME
+int getdomainname(char *, size_t);
+#endif
+
+/*
+ * HP-UX 11.00 has broken pread/pwrite that can't handle a 64-bit off_t
+ * on 32-bit machines.
+ */
+#if defined(__hpux) && !defined(__LP64__)
+# ifdef HAVE_PREAD64
+# undef pread
+# define pread(_a, _b, _c, _d) pread64((_a), (_b), (_c), (_d))
+# endif
+# ifdef HAVE_PWRITE64
+# undef pwrite
+# define pwrite(_a, _b, _c, _d) pwrite64((_a), (_b), (_c), (_d))
+# endif
+#endif /* __hpux && !__LP64__ */
+
+/* We wrap OpenBSD's strtonum() to get translatable error strings. */
+__dso_public long long sudo_strtonum(const char *, long long, long long, const char **);
+#undef strtonum
+#define strtonum(_a, _b, _c, _d) sudo_strtonum((_a), (_b), (_c), (_d))
+
+/*
+ * Functions "missing" from libc.
+ * All libc replacements are prefixed with "sudo_" to avoid namespace issues.
+ */
+
+struct passwd;
+struct timespec;
+
+#ifndef HAVE_CLOSEFROM
+__dso_public void sudo_closefrom(int);
+# undef closefrom
+# define closefrom(_a) sudo_closefrom((_a))
+#endif /* HAVE_CLOSEFROM */
+#ifdef PREFER_PORTABLE_GETCWD
+__dso_public char *sudo_getcwd(char *, size_t size);
+# undef getcwd
+# define getcwd(_a, _b) sudo_getcwd((_a), (_b))
+#endif /* PREFER_PORTABLE_GETCWD */
+#ifndef HAVE_GETGROUPLIST
+__dso_public int sudo_getgrouplist(const char *name, GETGROUPS_T basegid, GETGROUPS_T *groups, int *ngroupsp);
+# undef getgrouplist
+# define getgrouplist(_a, _b, _c, _d) sudo_getgrouplist((_a), (_b), (_c), (_d))
+#endif /* GETGROUPLIST */
+#ifndef HAVE_GETLINE
+__dso_public ssize_t sudo_getline(char **bufp, size_t *bufsizep, FILE *fp);
+# undef getline
+# define getline(_a, _b, _c) sudo_getline((_a), (_b), (_c))
+#endif /* HAVE_GETLINE */
+#ifndef HAVE_UTIMENSAT
+__dso_public int sudo_utimensat(int fd, const char *file, const struct timespec *times, int flag);
+# undef utimensat
+# define utimensat(_a, _b, _c, _d) sudo_utimensat((_a), (_b), (_c), (_d))
+#endif /* HAVE_UTIMENSAT */
+#ifndef HAVE_FUTIMENS
+__dso_public int sudo_futimens(int fd, const struct timespec *times);
+# undef futimens
+# define futimens(_a, _b) sudo_futimens((_a), (_b))
+#endif /* HAVE_FUTIMENS */
+#if !defined(HAVE_SNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
+__dso_public int sudo_snprintf(char *str, size_t n, char const *fmt, ...) __printflike(3, 4);
+# undef snprintf
+# define snprintf sudo_snprintf
+#endif /* HAVE_SNPRINTF */
+#if !defined(HAVE_VSNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
+__dso_public int sudo_vsnprintf(char *str, size_t n, const char *fmt, va_list ap) __printflike(3, 0);
+# undef vsnprintf
+# define vsnprintf sudo_vsnprintf
+#endif /* HAVE_VSNPRINTF */
+#if !defined(HAVE_ASPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
+__dso_public int sudo_asprintf(char **str, char const *fmt, ...) __printflike(2, 3);
+# undef asprintf
+# define asprintf sudo_asprintf
+#endif /* HAVE_ASPRINTF */
+#if !defined(HAVE_VASPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
+__dso_public int sudo_vasprintf(char **str, const char *fmt, va_list ap) __printflike(2, 0);
+# undef vasprintf
+# define vasprintf sudo_vasprintf
+#endif /* HAVE_VASPRINTF */
+#ifndef HAVE_STRLCAT
+__dso_public size_t sudo_strlcat(char *dst, const char *src, size_t siz);
+# undef strlcat
+# define strlcat(_a, _b, _c) sudo_strlcat((_a), (_b), (_c))
+#endif /* HAVE_STRLCAT */
+#ifndef HAVE_STRLCPY
+__dso_public size_t sudo_strlcpy(char *dst, const char *src, size_t siz);
+# undef strlcpy
+# define strlcpy(_a, _b, _c) sudo_strlcpy((_a), (_b), (_c))
+#endif /* HAVE_STRLCPY */
+#ifndef HAVE_STRNDUP
+__dso_public char *sudo_strndup(const char *str, size_t maxlen);
+# undef strndup
+# define strndup(_a, _b) sudo_strndup((_a), (_b))
+#endif /* HAVE_STRNDUP */
+#ifndef HAVE_STRNLEN
+__dso_public size_t sudo_strnlen(const char *str, size_t maxlen);
+# undef strnlen
+# define strnlen(_a, _b) sudo_strnlen((_a), (_b))
+#endif /* HAVE_STRNLEN */
+#ifndef HAVE_MEMRCHR
+__dso_public void *sudo_memrchr(const void *s, int c, size_t n);
+# undef memrchr
+# define memrchr(_a, _b, _c) sudo_memrchr((_a), (_b), (_c))
+#endif /* HAVE_MEMRCHR */
+#ifndef HAVE_MEMSET_S
+__dso_public errno_t sudo_memset_s(void *v, rsize_t smax, int c, rsize_t n);
+# undef memset_s
+# define memset_s(_a, _b, _c, _d) sudo_memset_s((_a), (_b), (_c), (_d))
+#endif /* HAVE_MEMSET_S */
+#if !defined(HAVE_MKDTEMP) || !defined(HAVE_MKSTEMPS)
+__dso_public char *sudo_mkdtemp(char *path);
+# undef mkdtemp
+# define mkdtemp(_a) sudo_mkdtemp((_a))
+__dso_public int sudo_mkstemps(char *path, int slen);
+# undef mkstemps
+# define mkstemps(_a, _b) sudo_mkstemps((_a), (_b))
+#endif /* !HAVE_MKDTEMP || !HAVE_MKSTEMPS */
+#ifndef HAVE_NANOSLEEP
+__dso_public int sudo_nanosleep(const struct timespec *timeout, struct timespec *remainder);
+#undef nanosleep
+# define nanosleep(_a, _b) sudo_nanosleep((_a), (_b))
+#endif
+#ifndef HAVE_PW_DUP
+__dso_public struct passwd *sudo_pw_dup(const struct passwd *pw);
+# undef pw_dup
+# define pw_dup(_a) sudo_pw_dup((_a))
+#endif /* HAVE_PW_DUP */
+#ifndef HAVE_STRSIGNAL
+__dso_public char *sudo_strsignal(int signo);
+# undef strsignal
+# define strsignal(_a) sudo_strsignal((_a))
+#endif /* HAVE_STRSIGNAL */
+#ifndef HAVE_SIG2STR
+__dso_public int sudo_sig2str(int signo, char *signame);
+# undef sig2str
+# define sig2str(_a, _b) sudo_sig2str((_a), (_b))
+#endif /* HAVE_SIG2STR */
+#if !defined(HAVE_INET_NTOP) && defined(SUDO_NET_IFS_C)
+__dso_public char *sudo_inet_ntop(int af, const void *src, char *dst, socklen_t size);
+# undef inet_ntop
+# define inet_ntop(_a, _b, _c, _d) sudo_inet_ntop((_a), (_b), (_c), (_d))
+#endif /* HAVE_INET_NTOP */
+#ifndef HAVE_INET_PTON
+__dso_public int sudo_inet_pton(int af, const char *src, void *dst);
+# undef inet_pton
+# define inet_pton(_a, _b, _c) sudo_inet_pton((_a), (_b), (_c))
+#endif /* HAVE_INET_PTON */
+#ifndef HAVE_GETPROGNAME
+__dso_public const char *sudo_getprogname(void);
+# undef getprogname
+# define getprogname() sudo_getprogname()
+#endif /* HAVE_GETPROGNAME */
+#ifndef HAVE_REALLOCARRAY
+__dso_public void *sudo_reallocarray(void *ptr, size_t nmemb, size_t size);
+# undef reallocarray
+# define reallocarray(_a, _b, _c) sudo_reallocarray((_a), (_b), (_c))
+#endif /* HAVE_REALLOCARRAY */
+#ifndef HAVE_VSYSLOG
+__dso_public void sudo_vsyslog(int pri, const char *fmt, va_list ap);
+# undef vsyslog
+# define vsyslog(_a, _b, _c) sudo_vsyslog((_a), (_b), (_c))
+#endif /* HAVE_VSYSLOG */
+#ifndef HAVE_PIPE2
+__dso_public int sudo_pipe2(int fildes[2], int flags);
+# undef pipe2
+# define pipe2(_a, _b) sudo_pipe2((_a), (_b))
+#endif /* HAVE_PIPE2 */
+
+#endif /* SUDO_COMPAT_H */
diff --git a/include/sudo_conf.h b/include/sudo_conf.h
new file mode 100644
index 0000000..6676116
--- /dev/null
+++ b/include/sudo_conf.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2011-2014 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * 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_CONF_H
+#define SUDO_CONF_H
+
+#include "sudo_queue.h"
+
+/* Flags for sudo_conf_read() */
+#define SUDO_CONF_DEBUG 0x01
+#define SUDO_CONF_PATHS 0x02
+#define SUDO_CONF_PLUGINS 0x04
+#define SUDO_CONF_SETTINGS 0x08
+#define SUDO_CONF_ALL 0x0f
+
+/* Values of sudo_conf_group_source() */
+#define GROUP_SOURCE_ADAPTIVE 0
+#define GROUP_SOURCE_STATIC 1
+#define GROUP_SOURCE_DYNAMIC 2
+
+struct sudo_debug_file;
+TAILQ_HEAD(sudo_conf_debug_file_list, sudo_debug_file);
+
+struct plugin_info {
+ TAILQ_ENTRY(plugin_info) entries;
+ char *path;
+ char *symbol_name;
+ char **options;
+ unsigned int lineno;
+};
+TAILQ_HEAD(plugin_info_list, plugin_info);
+
+struct sudo_conf_debug {
+ TAILQ_ENTRY(sudo_conf_debug) entries;
+ struct sudo_conf_debug_file_list debug_files;
+ char *progname;
+};
+TAILQ_HEAD(sudo_conf_debug_list, sudo_conf_debug);
+
+/* Read main sudo.conf file. */
+__dso_public int sudo_conf_read_v1(const char *conf_file, int conf_types);
+#define sudo_conf_read(_a, _b) sudo_conf_read_v1((_a), (_b))
+
+/* Accessor functions. */
+__dso_public const char *sudo_conf_askpass_path_v1(void);
+__dso_public const char *sudo_conf_sesh_path_v1(void);
+__dso_public const char *sudo_conf_noexec_path_v1(void);
+__dso_public const char *sudo_conf_plugin_dir_path_v1(void);
+__dso_public const char *sudo_conf_devsearch_path_v1(void);
+__dso_public struct sudo_conf_debug_list *sudo_conf_debugging_v1(void);
+__dso_public struct sudo_conf_debug_file_list *sudo_conf_debug_files_v1(const char *progname);
+__dso_public struct plugin_info_list *sudo_conf_plugins_v1(void);
+__dso_public bool sudo_conf_disable_coredump_v1(void);
+__dso_public bool sudo_conf_probe_interfaces_v1(void);
+__dso_public int sudo_conf_group_source_v1(void);
+__dso_public int sudo_conf_max_groups_v1(void);
+__dso_public void sudo_conf_clear_paths_v1(void);
+#define sudo_conf_askpass_path() sudo_conf_askpass_path_v1()
+#define sudo_conf_sesh_path() sudo_conf_sesh_path_v1()
+#define sudo_conf_noexec_path() sudo_conf_noexec_path_v1()
+#define sudo_conf_plugin_dir_path() sudo_conf_plugin_dir_path_v1()
+#define sudo_conf_devsearch_path() sudo_conf_devsearch_path_v1()
+#define sudo_conf_debugging() sudo_conf_debugging_v1()
+#define sudo_conf_debug_files(_a) sudo_conf_debug_files_v1((_a))
+#define sudo_conf_plugins() sudo_conf_plugins_v1()
+#define sudo_conf_disable_coredump() sudo_conf_disable_coredump_v1()
+#define sudo_conf_probe_interfaces() sudo_conf_probe_interfaces_v1()
+#define sudo_conf_group_source() sudo_conf_group_source_v1()
+#define sudo_conf_max_groups() sudo_conf_max_groups_v1()
+#define sudo_conf_clear_paths() sudo_conf_clear_paths_v1()
+
+#endif /* SUDO_CONF_H */
diff --git a/include/sudo_debug.h b/include/sudo_debug.h
new file mode 100644
index 0000000..105a7e6
--- /dev/null
+++ b/include/sudo_debug.h
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2011-2017 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * 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_DEBUG_H
+#define SUDO_DEBUG_H
+
+#include <stdarg.h>
+#ifdef HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# include "compat/stdbool.h"
+#endif
+#include "sudo_queue.h"
+
+/*
+ * List of debug files and flags for use in registration.
+ */
+struct sudo_debug_file {
+ TAILQ_ENTRY(sudo_debug_file) entries;
+ char *debug_file;
+ char *debug_flags;
+};
+struct sudo_conf_debug_file_list;
+
+/*
+ * The priority and subsystem are encoded in a single 32-bit value.
+ * The lower 4 bits are the priority and the top 26 bits are the subsystem.
+ * This allows for 16 priorities and a very large number of subsystems.
+ * Bit 5 is used as a flag to specify whether to log the errno value.
+ * Bit 6 specifies whether to log the function, file and line number data.
+ */
+
+/*
+ * Sudo debug priorities, ordered least to most verbose,
+ * in other words, highest to lowest priority. Max pri is 15.
+ * Note: order must match sudo_debug_priorities[]
+ */
+#define SUDO_DEBUG_CRIT 1 /* critical errors */
+#define SUDO_DEBUG_ERROR 2 /* non-critical errors */
+#define SUDO_DEBUG_WARN 3 /* non-fatal warnings */
+#define SUDO_DEBUG_NOTICE 4 /* non-error condition notices */
+#define SUDO_DEBUG_DIAG 5 /* diagnostic messages */
+#define SUDO_DEBUG_INFO 6 /* informational message */
+#define SUDO_DEBUG_TRACE 7 /* log function enter/exit */
+#define SUDO_DEBUG_DEBUG 8 /* very verbose debugging */
+
+/* Flag to include string version of errno in debug info. */
+#define SUDO_DEBUG_ERRNO (1<<4)
+
+/* Flag to include function, file and line number in debug info. */
+#define SUDO_DEBUG_LINENO (1<<5)
+
+/*
+ * Sudo debug subsystems.
+ * This includes subsystems in the sudoers plugin.
+ * Note: order must match sudo_debug_subsystems[]
+ */
+#define SUDO_DEBUG_ARGS ( 1<<6) /* command line argument handling */
+#define SUDO_DEBUG_CONV ( 2<<6) /* user conversation */
+#define SUDO_DEBUG_EDIT ( 3<<6) /* sudoedit */
+#define SUDO_DEBUG_EVENT ( 4<<6) /* event handling */
+#define SUDO_DEBUG_EXEC ( 5<<6) /* command execution */
+#define SUDO_DEBUG_HOOKS ( 6<<6) /* hook functions */
+#define SUDO_DEBUG_MAIN ( 7<<6) /* sudo main() */
+#define SUDO_DEBUG_NETIF ( 8<<6) /* network interface functions */
+#define SUDO_DEBUG_PCOMM ( 9<<6) /* plugin communications */
+#define SUDO_DEBUG_PLUGIN (10<<6) /* main plugin functions */
+#define SUDO_DEBUG_PTY (11<<6) /* pseudo-tty */
+#define SUDO_DEBUG_SELINUX (12<<6) /* selinux */
+#define SUDO_DEBUG_UTIL (13<<6) /* utility functions */
+#define SUDO_DEBUG_UTMP (14<<6) /* utmp file ops */
+#define SUDO_DEBUG_ALL 0xffff0000 /* all subsystems */
+
+/* Error return for sudo_debug_register(). */
+#define SUDO_DEBUG_INSTANCE_ERROR -2
+
+/* Initializer for instance index to indicate that debugging is not setup. */
+#define SUDO_DEBUG_INSTANCE_INITIALIZER -1
+
+/* Extract priority number and convert to an index. */
+#define SUDO_DEBUG_PRI(n) (((n) & 0x0f) - 1)
+
+/* Extract subsystem number and convert to an index. */
+#define SUDO_DEBUG_SUBSYS(n) (((n) >> 6) - 1)
+
+/*
+ * Wrapper for sudo_debug_enter() that declares __func__ as needed
+ * and sets sudo_debug_subsys for sudo_debug_exit().
+ */
+#ifdef HAVE___FUNC__
+# define debug_decl_func(funcname)
+# define debug_decl_vars(funcname, subsys) \
+ const int sudo_debug_subsys = (subsys);
+#else
+# define debug_decl_func(funcname) \
+ const char __func__[] = #funcname;
+# define debug_decl_vars(funcname, subsys) \
+ const int sudo_debug_subsys = (subsys); \
+ debug_decl_func(funcname);
+#endif
+#define debug_decl(funcname, subsys) \
+ debug_decl_vars((funcname), (subsys)) \
+ sudo_debug_enter(__func__, __FILE__, __LINE__, sudo_debug_subsys);
+
+/*
+ * Wrappers for sudo_debug_exit() and friends.
+ */
+#define debug_return \
+ do { \
+ sudo_debug_exit(__func__, __FILE__, __LINE__, sudo_debug_subsys); \
+ return; \
+ } while (0)
+
+#define debug_return_int(ret) \
+ do { \
+ int sudo_debug_ret = (ret); \
+ sudo_debug_exit_int(__func__, __FILE__, __LINE__, sudo_debug_subsys, \
+ sudo_debug_ret); \
+ return sudo_debug_ret; \
+ } while (0)
+
+#define debug_return_id_t(ret) \
+ do { \
+ id_t sudo_debug_ret = (ret); \
+ sudo_debug_exit_id_t(__func__, __FILE__, __LINE__, sudo_debug_subsys,\
+ sudo_debug_ret); \
+ return sudo_debug_ret; \
+ } while (0)
+
+#define debug_return_size_t(ret) \
+ do { \
+ size_t sudo_debug_ret = (ret); \
+ sudo_debug_exit_size_t(__func__, __FILE__, __LINE__, sudo_debug_subsys,\
+ sudo_debug_ret); \
+ return sudo_debug_ret; \
+ } while (0)
+
+#define debug_return_ssize_t(ret) \
+ do { \
+ ssize_t sudo_debug_ret = (ret); \
+ sudo_debug_exit_ssize_t(__func__, __FILE__, __LINE__, sudo_debug_subsys,\
+ sudo_debug_ret); \
+ return sudo_debug_ret; \
+ } while (0)
+
+#define debug_return_time_t(ret) \
+ do { \
+ time_t sudo_debug_ret = (ret); \
+ sudo_debug_exit_time_t(__func__, __FILE__, __LINE__, sudo_debug_subsys,\
+ sudo_debug_ret); \
+ return sudo_debug_ret; \
+ } while (0)
+
+#define debug_return_long(ret) \
+ do { \
+ long sudo_debug_ret = (ret); \
+ sudo_debug_exit_long(__func__, __FILE__, __LINE__, sudo_debug_subsys, \
+ sudo_debug_ret); \
+ return sudo_debug_ret; \
+ } while (0)
+
+#define debug_return_bool(ret) \
+ do { \
+ bool sudo_debug_ret = (ret); \
+ sudo_debug_exit_bool(__func__, __FILE__, __LINE__, sudo_debug_subsys, \
+ sudo_debug_ret); \
+ return sudo_debug_ret; \
+ } while (0)
+
+#define debug_return_str(ret) \
+ do { \
+ char *sudo_debug_ret = (ret); \
+ sudo_debug_exit_str(__func__, __FILE__, __LINE__, sudo_debug_subsys, \
+ sudo_debug_ret); \
+ return sudo_debug_ret; \
+ } while (0)
+
+#define debug_return_const_str(ret) \
+ do { \
+ const char *sudo_debug_ret = (ret); \
+ sudo_debug_exit_str(__func__, __FILE__, __LINE__, sudo_debug_subsys, \
+ sudo_debug_ret); \
+ return sudo_debug_ret; \
+ } while (0)
+
+#define debug_return_str_masked(ret) \
+ do { \
+ char *sudo_debug_ret = (ret); \
+ sudo_debug_exit_str_masked(__func__, __FILE__, __LINE__, \
+ sudo_debug_subsys, sudo_debug_ret); \
+ return sudo_debug_ret; \
+ } while (0)
+
+#define debug_return_ptr(ret) \
+ do { \
+ void *sudo_debug_ret = (ret); \
+ sudo_debug_exit_ptr(__func__, __FILE__, __LINE__, sudo_debug_subsys, \
+ sudo_debug_ret); \
+ return sudo_debug_ret; \
+ } while (0)
+
+#define debug_return_const_ptr(ret) \
+ do { \
+ const void *sudo_debug_ret = (ret); \
+ sudo_debug_exit_ptr(__func__, __FILE__, __LINE__, sudo_debug_subsys, \
+ sudo_debug_ret); \
+ return sudo_debug_ret; \
+ } while (0)
+
+/*
+ * Variadic macros are a C99 feature but GNU cpp has supported
+ * a (different) version of them for a long time.
+ */
+#if defined(NO_VARIADIC_MACROS)
+# define sudo_debug_printf sudo_debug_printf_nvm
+#elif defined(__GNUC__) && __GNUC__ == 2
+# define sudo_debug_printf(pri, fmt...) \
+ sudo_debug_printf2(__func__, __FILE__, __LINE__, (pri)|sudo_debug_subsys, \
+ fmt)
+#else
+# define sudo_debug_printf(pri, ...) \
+ sudo_debug_printf2(__func__, __FILE__, __LINE__, (pri)|sudo_debug_subsys, \
+ __VA_ARGS__)
+#endif
+
+#define sudo_debug_execve(pri, path, argv, envp) \
+ sudo_debug_execve2((pri)|sudo_debug_subsys, (path), (argv), (envp))
+
+#define sudo_debug_write(fd, str, len, errnum) \
+ sudo_debug_write2(fd, NULL, NULL, 0, (str), (len), (errnum))
+
+__dso_public int sudo_debug_deregister_v1(int instance_id);
+__dso_public void sudo_debug_enter_v1(const char *func, const char *file, int line, int subsys);
+__dso_public void sudo_debug_execve2_v1(int level, const char *path, char *const argv[], char *const envp[]);
+__dso_public void sudo_debug_exit_v1(const char *func, const char *file, int line, int subsys);
+__dso_public void sudo_debug_exit_bool_v1(const char *func, const char *file, int line, int subsys, bool ret);
+__dso_public void sudo_debug_exit_int_v1(const char *func, const char *file, int line, int subsys, int ret);
+__dso_public void sudo_debug_exit_long_v1(const char *func, const char *file, int line, int subsys, long ret);
+__dso_public void sudo_debug_exit_ptr_v1(const char *func, const char *file, int line, int subsys, const void *ret);
+__dso_public void sudo_debug_exit_id_t_v1(const char *func, const char *file, int line, int subsys, id_t ret);
+__dso_public void sudo_debug_exit_size_t_v1(const char *func, const char *file, int line, int subsys, size_t ret);
+__dso_public void sudo_debug_exit_ssize_t_v1(const char *func, const char *file, int line, int subsys, ssize_t ret);
+__dso_public void sudo_debug_exit_str_v1(const char *func, const char *file, int line, int subsys, const char *ret);
+__dso_public void sudo_debug_exit_str_masked_v1(const char *func, const char *file, int line, int subsys, const char *ret);
+__dso_public void sudo_debug_exit_time_t_v1(const char *func, const char *file, int line, int subsys, time_t ret);
+__dso_public pid_t sudo_debug_fork_v1(void);
+__dso_public int sudo_debug_get_active_instance_v1(void);
+__dso_public int sudo_debug_get_fds_v1(unsigned char **fds);
+__dso_public int sudo_debug_get_instance_v1(const char *program);
+__dso_public void sudo_debug_printf2_v1(const char *func, const char *file, int line, int level, const char *fmt, ...) __printf0like(5, 6);
+__dso_public void sudo_debug_printf_nvm_v1(int pri, const char *fmt, ...) __printf0like(2, 3);
+__dso_public int sudo_debug_register_v1(const char *program, const char *const subsystems[], unsigned int ids[], struct sudo_conf_debug_file_list *debug_files);
+__dso_public int sudo_debug_set_active_instance_v1(int inst);
+__dso_public void sudo_debug_update_fd_v1(int ofd, int nfd);
+__dso_public void sudo_debug_vprintf2_v1(const char *func, const char *file, int line, int level, const char *fmt, va_list ap) __printf0like(5, 0);
+__dso_public void sudo_debug_write2_v1(int fd, const char *func, const char *file, int line, const char *str, int len, int errnum);
+
+#define sudo_debug_deregister(_a) sudo_debug_deregister_v1((_a))
+#define sudo_debug_enter(_a, _b, _c, _d) sudo_debug_enter_v1((_a), (_b), (_c), (_d))
+#define sudo_debug_execve2(_a, _b, _c, _d) sudo_debug_execve2_v1((_a), (_b), (_c), (_d))
+#define sudo_debug_exit(_a, _b, _c, _d) sudo_debug_exit_v1((_a), (_b), (_c), (_d))
+#define sudo_debug_exit_bool(_a, _b, _c, _d, _e) sudo_debug_exit_bool_v1((_a), (_b), (_c), (_d), (_e))
+#define sudo_debug_exit_int(_a, _b, _c, _d, _e) sudo_debug_exit_int_v1((_a), (_b), (_c), (_d), (_e))
+#define sudo_debug_exit_long(_a, _b, _c, _d, _e) sudo_debug_exit_long_v1((_a), (_b), (_c), (_d), (_e))
+#define sudo_debug_exit_ptr(_a, _b, _c, _d, _e) sudo_debug_exit_ptr_v1((_a), (_b), (_c), (_d), (_e))
+#define sudo_debug_exit_id_t(_a, _b, _c, _d, _e) sudo_debug_exit_id_t_v1((_a), (_b), (_c), (_d), (_e))
+#define sudo_debug_exit_size_t(_a, _b, _c, _d, _e) sudo_debug_exit_size_t_v1((_a), (_b), (_c), (_d), (_e))
+#define sudo_debug_exit_ssize_t(_a, _b, _c, _d, _e) sudo_debug_exit_ssize_t_v1((_a), (_b), (_c), (_d), (_e))
+#define sudo_debug_exit_str(_a, _b, _c, _d, _e) sudo_debug_exit_str_v1((_a), (_b), (_c), (_d), (_e))
+#define sudo_debug_exit_str_masked(_a, _b, _c, _d, _e) sudo_debug_exit_str_masked_v1((_a), (_b), (_c), (_d), (_e))
+#define sudo_debug_exit_time_t(_a, _b, _c, _d, _e) sudo_debug_exit_time_t_v1((_a), (_b), (_c), (_d), (_e))
+#define sudo_debug_fork() sudo_debug_fork_v1()
+#define sudo_debug_get_active_instance() sudo_debug_get_active_instance_v1()
+#define sudo_debug_get_fds(_a) sudo_debug_get_fds_v1((_a))
+#define sudo_debug_get_instance(_a) sudo_debug_get_instance_v1((_a))
+#define sudo_debug_printf2 sudo_debug_printf2_v1
+#define sudo_debug_printf_nvm sudo_debug_printf_nvm_v1
+#define sudo_debug_register(_a, _b, _c, _d) sudo_debug_register_v1((_a), (_b), (_c), (_d))
+#define sudo_debug_set_active_instance(_a) sudo_debug_set_active_instance_v1((_a))
+#define sudo_debug_update_fd(_a, _b) sudo_debug_update_fd_v1((_a), (_b))
+#define sudo_debug_vprintf2(_a, _b, _c, _d, _e, _f) sudo_debug_vprintf2_v1((_a), (_b), (_c), (_d), (_e), (_f))
+#define sudo_debug_write2(_a, _b, _c, _d, _e, _f, _g) sudo_debug_write2_v1((_a), (_b), (_c), (_d), (_e), (_f), (_g))
+
+#endif /* SUDO_DEBUG_H */
diff --git a/include/sudo_digest.h b/include/sudo_digest.h
new file mode 100644
index 0000000..9fffa1b
--- /dev/null
+++ b/include/sudo_digest.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * 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_DIGEST_H
+#define SUDO_DIGEST_H
+
+/* Digest types. */
+#define SUDO_DIGEST_SHA224 0
+#define SUDO_DIGEST_SHA256 1
+#define SUDO_DIGEST_SHA384 2
+#define SUDO_DIGEST_SHA512 3
+#define SUDO_DIGEST_INVALID 4
+
+struct sudo_digest;
+
+/* Public functions. */
+__dso_public struct sudo_digest *sudo_digest_alloc_v1(int digest_type);
+__dso_public void sudo_digest_free_v1(struct sudo_digest *dig);
+__dso_public void sudo_digest_reset_v1(struct sudo_digest *dig);
+__dso_public int sudo_digest_getlen_v1(int digest_type);
+__dso_public void sudo_digest_update_v1(struct sudo_digest *dig, const void *data, size_t len);
+__dso_public void sudo_digest_final_v1(struct sudo_digest *dig, unsigned char *md);
+
+#define sudo_digest_alloc(_a) sudo_digest_alloc_v1((_a))
+#define sudo_digest_free(_a) sudo_digest_free_v1((_a))
+#define sudo_digest_reset(_a) sudo_digest_reset_v1((_a))
+#define sudo_digest_getlen(_a) sudo_digest_getlen_v1((_a))
+#define sudo_digest_update(_a, _b, _c) sudo_digest_update_v1((_a), (_b), (_c))
+#define sudo_digest_final(_a, _b) sudo_digest_final_v1((_a), (_b))
+
+#endif /* SUDO_DIGEST_H */
diff --git a/include/sudo_dso.h b/include/sudo_dso.h
new file mode 100644
index 0000000..08fd8a8
--- /dev/null
+++ b/include/sudo_dso.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2010, 2013, 2014 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * 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_DSO_H
+#define SUDO_DSO_H
+
+/* Values for sudo_dso_load() mode. */
+#define SUDO_DSO_LAZY 0x1
+#define SUDO_DSO_NOW 0x2
+#define SUDO_DSO_GLOBAL 0x4
+#define SUDO_DSO_LOCAL 0x8
+
+/* Special handle arguments for sudo_dso_findsym(). */
+#define SUDO_DSO_NEXT ((void *)-1) /* Search subsequent objects. */
+#define SUDO_DSO_DEFAULT ((void *)-2) /* Use default search algorithm. */
+#define SUDO_DSO_SELF ((void *)-3) /* Search the caller itself. */
+
+/* Internal structs for static linking of plugins. */
+struct sudo_preload_symbol {
+ const char *name;
+ void *addr;
+};
+struct sudo_preload_table {
+ const char *path;
+ void *handle;
+ struct sudo_preload_symbol *symbols;
+};
+
+/* Public functions. */
+__dso_public char *sudo_dso_strerror_v1(void);
+__dso_public int sudo_dso_unload_v1(void *handle);
+__dso_public void *sudo_dso_findsym_v1(void *handle, const char *symbol);
+__dso_public void *sudo_dso_load_v1(const char *path, int mode);
+__dso_public void sudo_dso_preload_table_v1(struct sudo_preload_table *table);
+
+#define sudo_dso_strerror() sudo_dso_strerror_v1()
+#define sudo_dso_unload(_a) sudo_dso_unload_v1((_a))
+#define sudo_dso_findsym(_a, _b) sudo_dso_findsym_v1((_a), (_b))
+#define sudo_dso_load(_a, _b) sudo_dso_load_v1((_a), (_b))
+#define sudo_dso_preload_table(_a) sudo_dso_preload_table_v1((_a))
+
+#endif /* SUDO_DSO_H */
diff --git a/include/sudo_event.h b/include/sudo_event.h
new file mode 100644
index 0000000..9797d58
--- /dev/null
+++ b/include/sudo_event.h
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2013-2015, 2017 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * 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
+
+#include <signal.h> /* for sigatomic_t and NSIG */
+#include "sudo_queue.h"
+
+/* Event types */
+#define SUDO_EV_TIMEOUT 0x01 /* fire after timeout */
+#define SUDO_EV_READ 0x02 /* fire when readable */
+#define SUDO_EV_WRITE 0x04 /* fire when writable */
+#define SUDO_EV_PERSIST 0x08 /* persist until deleted */
+#define SUDO_EV_SIGNAL 0x10 /* fire on signal receipt */
+#define SUDO_EV_SIGINFO 0x20 /* fire on signal receipt (siginfo) */
+
+/* Event flags (internal) */
+#define SUDO_EVQ_INSERTED 0x01 /* event is on the event queue */
+#define SUDO_EVQ_ACTIVE 0x02 /* event is on the active queue */
+#define SUDO_EVQ_TIMEOUTS 0x04 /* event is on the timeouts queue */
+
+/* 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_LOOPONCE SUDO_EVLOOP_ONCE
+#define SUDO_EVBASE_LOOPEXIT 0x02
+#define SUDO_EVBASE_LOOPBREAK 0x04
+#define SUDO_EVBASE_LOOPCONT 0x08
+#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);
+
+/*
+ * Container for SUDO_EV_SIGINFO events that gets passed as the closure
+ * pointer. This allows us to pass a siginfo_t without changing everything.
+ */
+struct sudo_ev_siginfo_container {
+ void *closure;
+ siginfo_t *siginfo;
+ char si_buf[1];
+};
+
+/* Member of struct sudo_event_base. */
+struct sudo_event {
+ TAILQ_ENTRY(sudo_event) entries;
+ TAILQ_ENTRY(sudo_event) active_entries;
+ TAILQ_ENTRY(sudo_event) timeouts_entries;
+ struct sudo_event_base *base; /* base this event belongs to */
+ int fd; /* fd/signal we are interested in */
+ short events; /* SUDO_EV_* flags (in) */
+ short revents; /* SUDO_EV_* flags (out) */
+ short flags; /* internal event flags */
+ short pfd_idx; /* index into pfds array (XXX) */
+ sudo_ev_callback_t callback;/* user-provided callback */
+ struct timespec timeout; /* for SUDO_EV_TIMEOUT */
+ void *closure; /* user-provided data pointer */
+};
+TAILQ_HEAD(sudo_event_list, sudo_event);
+
+struct sudo_event_base {
+ struct sudo_event_list events; /* tail queue of all events */
+ struct sudo_event_list active; /* tail queue of active events */
+ struct sudo_event_list timeouts; /* tail queue of timeout events */
+ struct sudo_event signal_event; /* storage for signal pipe event */
+ struct sudo_event_list signals[NSIG]; /* array of signal event tail queues */
+ struct sigaction *orig_handlers[NSIG]; /* original signal handlers */
+ siginfo_t *siginfo[NSIG]; /* detailed signal info */
+ sig_atomic_t signal_pending[NSIG]; /* pending signals */
+ sig_atomic_t signal_caught; /* at least one signal caught */
+ int num_handlers; /* number of installed handlers */
+ int signal_pipe[2]; /* so we can wake up on singal */
+#if defined(HAVE_POLL) || defined(HAVE_PPOLL)
+ 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_in; /* read I/O descriptor set (in) */
+ fd_set *writefds_in; /* write I/O descriptor set (in) */
+ fd_set *readfds_out; /* read I/O descriptor set (out) */
+ fd_set *writefds_out; /* write I/O descriptor set (out) */
+ int maxfd; /* max fd we can store in readfds/writefds */
+ int highfd; /* highest fd to pass as 1st arg to select */
+#endif /* HAVE_POLL */
+ unsigned int flags; /* SUDO_EVBASE_* */
+};
+
+/* Allocate a new event base. */
+__dso_public struct sudo_event_base *sudo_ev_base_alloc_v1(void);
+#define sudo_ev_base_alloc() sudo_ev_base_alloc_v1()
+
+/* Free an event base. */
+__dso_public void sudo_ev_base_free_v1(struct sudo_event_base *base);
+#define sudo_ev_base_free(_a) sudo_ev_base_free_v1((_a))
+
+/* Set the default event base. */
+__dso_public void sudo_ev_base_setdef_v1(struct sudo_event_base *base);
+#define sudo_ev_base_setdef(_a) sudo_ev_base_setdef_v1((_a))
+
+/* Allocate a new event. */
+__dso_public struct sudo_event *sudo_ev_alloc_v1(int fd, short events, sudo_ev_callback_t callback, void *closure);
+#define sudo_ev_alloc(_a, _b, _c, _d) sudo_ev_alloc_v1((_a), (_b), (_c), (_d))
+
+/* Free an event. */
+__dso_public void sudo_ev_free_v1(struct sudo_event *ev);
+#define sudo_ev_free(_a) sudo_ev_free_v1((_a))
+
+/* Add an event, returns 0 on success, -1 on error */
+__dso_public int sudo_ev_add_v1(struct sudo_event_base *head, struct sudo_event *ev, struct timeval *timo, bool tohead);
+__dso_public int sudo_ev_add_v2(struct sudo_event_base *head, struct sudo_event *ev, struct timespec *timo, bool tohead);
+#define sudo_ev_add(_a, _b, _c, _d) sudo_ev_add_v2((_a), (_b), (_c), (_d))
+
+/* Delete an event, returns 0 on success, -1 on error */
+__dso_public int sudo_ev_del_v1(struct sudo_event_base *head, struct sudo_event *ev);
+#define sudo_ev_del(_a, _b) sudo_ev_del_v1((_a), (_b))
+
+/* Dispatch events, returns SUDO_CB_SUCCESS, SUDO_CB_BREAK or SUDO_CB_ERROR */
+__dso_public int sudo_ev_dispatch_v1(struct sudo_event_base *head);
+#define sudo_ev_dispatch(_a) sudo_ev_dispatch_v1((_a))
+
+/* Main event loop, returns SUDO_CB_SUCCESS, SUDO_CB_BREAK or SUDO_CB_ERROR */
+__dso_public int sudo_ev_loop_v1(struct sudo_event_base *head, int flags);
+#define sudo_ev_loop(_a, _b) sudo_ev_loop_v1((_a), (_b))
+
+/* Return the remaining timeout associated with an event. */
+__dso_public int sudo_ev_get_timeleft_v1(struct sudo_event *ev, struct timeval *tv);
+__dso_public int sudo_ev_get_timeleft_v2(struct sudo_event *ev, struct timespec *tv);
+#define sudo_ev_get_timeleft(_a, _b) sudo_ev_get_timeleft_v2((_a), (_b))
+
+/* Cause the event loop to exit after one run through. */
+__dso_public void sudo_ev_loopexit_v1(struct sudo_event_base *base);
+#define sudo_ev_loopexit(_a) sudo_ev_loopexit_v1((_a))
+
+/* Break out of the event loop right now. */
+__dso_public void sudo_ev_loopbreak_v1(struct sudo_event_base *base);
+#define sudo_ev_loopbreak(_a) sudo_ev_loopbreak_v1((_a))
+
+/* Rescan for events and restart the event loop. */
+__dso_public void sudo_ev_loopcontinue_v1(struct sudo_event_base *base);
+#define sudo_ev_loopcontinue(_a) sudo_ev_loopcontinue_v1((_a))
+
+/* Returns true if event loop stopped due to sudo_ev_loopexit(). */
+__dso_public bool sudo_ev_got_exit_v1(struct sudo_event_base *base);
+#define sudo_ev_got_exit(_a) sudo_ev_got_exit_v1((_a))
+
+/* Returns true if event loop stopped due to sudo_ev_loopbreak(). */
+__dso_public bool sudo_ev_got_break_v1(struct sudo_event_base *base);
+#define sudo_ev_got_break(_a) sudo_ev_got_break_v1((_a))
+
+/* Return the fd associated with an event. */
+#define sudo_ev_get_fd(_ev) ((_ev) ? (_ev)->fd : -1)
+
+/* Return the (absolute) timeout associated with an event or NULL. */
+#define sudo_ev_get_timeout(_ev) \
+ (ISSET((_ev)->flags, SUDO_EVQ_TIMEOUTS) ? &(_ev)->timeout : NULL)
+
+/* Return the base an event is associated with or NULL. */
+#define sudo_ev_get_base(_ev) ((_ev) ? (_ev)->base : NULL)
+
+/* Magic pointer value to use self pointer as callback arg. */
+#define sudo_ev_self_cbarg() ((void *)-1)
+
+/* Add an event to the base's active queue and mark it active (internal). */
+void sudo_ev_activate(struct sudo_event_base *base, struct sudo_event *ev);
+
+/*
+ * 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_scan_impl(struct sudo_event_base *base, int flags);
+
+#endif /* SUDO_EVENT_H */
diff --git a/include/sudo_fatal.h b/include/sudo_fatal.h
new file mode 100644
index 0000000..ea92b3b
--- /dev/null
+++ b/include/sudo_fatal.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 2004, 2010-2015, 2017-2018 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * 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_FATAL_H
+#define SUDO_FATAL_H
+
+#include <stdarg.h>
+#ifdef HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# include "compat/stdbool.h"
+#endif /* HAVE_STDBOOL_H */
+
+/*
+ * We wrap fatal/fatalx and warn/warnx so that the same output can
+ * go to the debug file, if there is one.
+ */
+#if (defined(SUDO_ERROR_WRAP) && SUDO_ERROR_WRAP == 0) || defined(NO_VARIADIC_MACROS)
+# define sudo_fatal sudo_fatal_nodebug_v1
+# define sudo_fatalx sudo_fatalx_nodebug_v1
+# define sudo_gai_fatal sudo_gai_fatal_nodebug_v1
+# define sudo_warn sudo_warn_nodebug_v1
+# define sudo_warnx sudo_warnx_nodebug_v1
+# define sudo_gai_warn sudo_gai_warn_nodebug_v1
+# define sudo_vfatal(fmt, ap) sudo_vfatal_nodebug_v1((fmt), (ap))
+# define sudo_vfatalx(fmt, ap) sudo_vfatalx_nodebug_v1((fmt), (ap))
+# define sudo_gai_vfatal(en, fmt, ap) sudo_vfatal_nodebug_v1((en), (fmt), (ap))
+# define sudo_vwarn(fmt, ap) sudo_vwarn_nodebug_v1((fmt), (ap))
+# define sudo_vwarnx(fmt, ap) sudo_vwarnx_nodebug_v1((fmt), (ap))
+# define sudo_gai_vwarn(en, fmt, ap) sudo_vwarn_nodebug_v1((en), (fmt), (ap))
+#else /* SUDO_ERROR_WRAP */
+# if defined(__GNUC__) && __GNUC__ == 2
+# define sudo_fatal(fmt...) do { \
+ sudo_debug_printf2(__func__, __FILE__, __LINE__, \
+ SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO|sudo_debug_subsys, \
+ fmt); \
+ sudo_fatal_nodebug_v1(fmt); \
+} while (0)
+# define sudo_fatalx(fmt...) do { \
+ sudo_debug_printf2(__func__, __FILE__, __LINE__, \
+ SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|sudo_debug_subsys, fmt); \
+ sudo_fatalx_nodebug_v1(fmt); \
+} while (0)
+# define sudo_gai_fatal(en, fmt...) do { \
+ sudo_debug_printf2(__func__, __FILE__, __LINE__, \
+ SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|sudo_debug_subsys, fmt); \
+ sudo_gai_fatal_nodebug_v1((en), fmt); \
+} while (0)
+# define sudo_warn(fmt...) do { \
+ sudo_debug_printf2(__func__, __FILE__, __LINE__, \
+ SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO|sudo_debug_subsys, \
+ fmt); \
+ sudo_warn_nodebug_v1(fmt); \
+} while (0)
+# define sudo_warnx(fmt...) do { \
+ sudo_debug_printf2(__func__, __FILE__, __LINE__, \
+ SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|sudo_debug_subsys, fmt); \
+ sudo_warnx_nodebug_v1(fmt); \
+} while (0)
+# define sudo_gai_warn(en, fmt...) do { \
+ sudo_debug_printf2(__func__, __FILE__, __LINE__, \
+ SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|sudo_debug_subsys, fmt); \
+ sudo_gai_warn_nodebug_v1((en), fmt); \
+} while (0)
+# else
+# define sudo_fatal(...) do { \
+ sudo_debug_printf2(__func__, __FILE__, __LINE__, \
+ SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO|sudo_debug_subsys, \
+ __VA_ARGS__); \
+ sudo_fatal_nodebug_v1(__VA_ARGS__); \
+} while (0)
+# define sudo_fatalx(...) do { \
+ sudo_debug_printf2(__func__, __FILE__, __LINE__, \
+ SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|sudo_debug_subsys, __VA_ARGS__); \
+ sudo_fatalx_nodebug_v1(__VA_ARGS__); \
+} while (0)
+# define sudo_gai_fatal(en, ...) do { \
+ sudo_debug_printf2(__func__, __FILE__, __LINE__, \
+ SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|sudo_debug_subsys, __VA_ARGS__); \
+ sudo_gai_fatal_nodebug_v1((en), __VA_ARGS__); \
+} while (0)
+# define sudo_warn(...) do { \
+ sudo_debug_printf2(__func__, __FILE__, __LINE__, \
+ SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO|sudo_debug_subsys, \
+ __VA_ARGS__); \
+ sudo_warn_nodebug_v1(__VA_ARGS__); \
+} while (0)
+# define sudo_warnx(...) do { \
+ sudo_debug_printf2(__func__, __FILE__, __LINE__, \
+ SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO|sudo_debug_subsys, __VA_ARGS__); \
+ sudo_warnx_nodebug_v1(__VA_ARGS__); \
+} while (0)
+# define sudo_gai_warn(en, ...) do { \
+ sudo_debug_printf2(__func__, __FILE__, __LINE__, \
+ SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO|sudo_debug_subsys, __VA_ARGS__); \
+ sudo_gai_warn_nodebug_v1((en), __VA_ARGS__); \
+} while (0)
+# endif /* __GNUC__ == 2 */
+# define sudo_vfatal(fmt, ap) do { \
+ va_list ap2; \
+ va_copy(ap2, (ap)); \
+ sudo_debug_vprintf2(__func__, __FILE__, __LINE__, \
+ SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO|sudo_debug_subsys, \
+ (fmt), ap2); \
+ sudo_vfatal_nodebug_v1((fmt), (ap)); \
+} while (0)
+# define sudo_vfatalx(fmt, ap) do { \
+ va_list ap2; \
+ va_copy(ap2, (ap)); \
+ sudo_debug_vprintf2(__func__, __FILE__, __LINE__, \
+ SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|sudo_debug_subsys, (fmt), ap2); \
+ sudo_vfatalx_nodebug_v1((fmt), (ap)); \
+} while (0)
+# define sudo_gai_vfatal(en, fmt, ap) do { \
+ va_list ap2; \
+ va_copy(ap2, (ap)); \
+ sudo_debug_vprintf2(__func__, __FILE__, __LINE__, \
+ SUDO_DEBUG_ERROR|SUDO_DEBUG_LINENO|sudo_debug_subsys, (fmt), ap2); \
+ sudo_gai_vfatal_nodebug_v1((en), (fmt), (ap)); \
+} while (0)
+# define sudo_vwarn(fmt, ap) do { \
+ va_list ap2; \
+ va_copy(ap2, (ap)); \
+ sudo_debug_vprintf2(__func__, __FILE__, __LINE__, \
+ SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO|SUDO_DEBUG_ERRNO|sudo_debug_subsys, \
+ (fmt), ap2); \
+ sudo_vwarn_nodebug_v1((fmt), (ap)); \
+} while (0)
+# define sudo_vwarnx(fmt, ap) do { \
+ va_list ap2; \
+ va_copy(ap2, (ap)); \
+ sudo_debug_vprintf2(__func__, __FILE__, __LINE__, \
+ SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO|sudo_debug_subsys, (fmt), ap2); \
+ sudo_vwarnx_nodebug_v1((fmt), (ap)); \
+} while (0)
+# define sudo_gai_vwarn(en, fmt, ap) do { \
+ va_list ap2; \
+ va_copy(ap2, (ap)); \
+ sudo_debug_vprintf2(__func__, __FILE__, __LINE__, \
+ SUDO_DEBUG_WARN|SUDO_DEBUG_LINENO|sudo_debug_subsys, (fmt), ap2); \
+ sudo_gai_vwarn_nodebug_v1((en), (fmt), (ap)); \
+} while (0)
+#endif /* SUDO_ERROR_WRAP */
+
+typedef void (*sudo_fatal_callback_t)(void);
+
+struct sudo_conv_message;
+struct sudo_conv_reply;
+struct sudo_conv_callback;
+
+__dso_public int sudo_fatal_callback_deregister_v1(sudo_fatal_callback_t func);
+__dso_public int sudo_fatal_callback_register_v1(sudo_fatal_callback_t func);
+__dso_public char *sudo_warn_gettext_v1(const char *domainname, const char *msgid) __format_arg(2);
+__dso_public void sudo_warn_set_locale_func_v1(bool (*func)(bool, int *));
+__dso_public void sudo_fatal_nodebug_v1(const char *fmt, ...) __printf0like(1, 2) __attribute__((__noreturn__));
+__dso_public void sudo_fatalx_nodebug_v1(const char *fmt, ...) __printflike(1, 2) __attribute__((__noreturn__));
+__dso_public void sudo_gai_fatal_nodebug_v1(int errnum, const char *fmt, ...) __printflike(2, 3) __attribute__((__noreturn__));
+__dso_public void sudo_vfatal_nodebug_v1(const char *fmt, va_list ap) __printf0like(1, 0) __attribute__((__noreturn__));
+__dso_public void sudo_vfatalx_nodebug_v1(const char *fmt, va_list ap) __printflike(1, 0) __attribute__((__noreturn__));
+__dso_public void sudo_gai_vfatal_nodebug_v1(int errnum, const char *fmt, va_list ap) __printflike(2, 0) __attribute__((__noreturn__));
+__dso_public void sudo_warn_nodebug_v1(const char *fmt, ...) __printf0like(1, 2);
+__dso_public void sudo_warnx_nodebug_v1(const char *fmt, ...) __printflike(1, 2);
+__dso_public void sudo_gai_warn_nodebug_v1(int errnum, const char *fmt, ...) __printflike(2, 3);
+__dso_public void sudo_vwarn_nodebug_v1(const char *fmt, va_list ap) __printf0like(1, 0);
+__dso_public void sudo_vwarnx_nodebug_v1(const char *fmt, va_list ap) __printflike(1, 0);
+__dso_public void sudo_gai_vwarn_nodebug_v1(int errnum, const char *fmt, va_list ap) __printflike(2, 0);
+__dso_public void sudo_warn_set_conversation_v1(int (*conv)(int num_msgs, const struct sudo_conv_message *msgs, struct sudo_conv_reply *replies, struct sudo_conv_callback *callback));
+
+#define sudo_fatal_callback_deregister(_a) sudo_fatal_callback_deregister_v1((_a))
+#define sudo_fatal_callback_register(_a) sudo_fatal_callback_register_v1((_a))
+#define sudo_warn_set_locale_func(_a) sudo_warn_set_locale_func_v1((_a))
+#define sudo_fatal_nodebug sudo_fatal_nodebug_v1
+#define sudo_fatalx_nodebug sudo_fatalx_nodebug_v1
+#define sudo_gai_fatal_nodebug sudo_gai_fatal_nodebug_v1
+#define sudo_vfatal_nodebug(_a, _b) sudo_vfatal_nodebug_v1((_a), (_b))
+#define sudo_vfatalx_nodebug(_a, _b) sudo_vfatalx_nodebug_v1((_a), (_b))
+#define sudo_gai_vfatal_nodebug(_a, _b, _c) sudo_gai_vfatal_nodebug_v1((_a), (_b), (_c))
+#define sudo_warn_nodebug sudo_warn_nodebug_v1
+#define sudo_warnx_nodebug sudo_warnx_nodebug_v1
+#define sudo_gai_warn_nodebug sudo_gai_warn_nodebug_v1
+#define sudo_vwarn_nodebug(_a, _b) sudo_vwarn_nodebug_v1((_a), (_b))
+#define sudo_vwarnx_nodebug(_a, _b) sudo_vwarnx_nodebug_v1((_a), (_b))
+#define sudo_gai_vwarn_nodebug(_a, _b, _c) sudo_gai_vwarn_nodebug_v1((_a), (_b), (_c))
+#define sudo_warn_set_conversation(_a) sudo_warn_set_conversation_v1(_a)
+
+#ifdef DEFAULT_TEXT_DOMAIN
+# define sudo_warn_gettext(_a) sudo_warn_gettext_v1(DEFAULT_TEXT_DOMAIN, (_a))
+#else
+# define sudo_warn_gettext(_a) sudo_warn_gettext_v1(NULL, (_a))
+#endif
+
+#endif /* SUDO_FATAL_H */
diff --git a/include/sudo_gettext.h b/include/sudo_gettext.h
new file mode 100644
index 0000000..a770b08
--- /dev/null
+++ b/include/sudo_gettext.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2011-2014 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * 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_GETTEXT_H
+#define SUDO_GETTEXT_H
+
+/*
+ * Solaris locale.h includes libintl.h which causes problems when we
+ * redefine the gettext functions. We include it first to avoid this.
+ */
+#include <locale.h>
+
+#ifdef HAVE_LIBINTL_H
+
+# include <libintl.h>
+
+/*
+ * If DEFAULT_TEXT_DOMAIN is defined, use its value as the domain for
+ * gettext() and ngettext() instead of the value set by textdomain().
+ * This is used by the sudoers plugin as well as the convenience libraries.
+ */
+# ifdef DEFAULT_TEXT_DOMAIN
+# undef gettext
+# define gettext(String) \
+ dgettext(DEFAULT_TEXT_DOMAIN, String)
+# undef ngettext
+# define ngettext(String, String_Plural, N) \
+ dngettext(DEFAULT_TEXT_DOMAIN, String, String_Plural, N)
+# endif
+
+/*
+ * Older versions of Solaris lack ngettext() so we have to kludge it.
+ */
+# ifndef HAVE_NGETTEXT
+# undef ngettext
+# define ngettext(String, String_Plural, N) \
+ ((N) == 1 ? gettext(String) : gettext(String_Plural))
+# endif
+
+/* Gettext convenience macros */
+# define _(String) gettext(String)
+# define gettext_noop(String) String
+# define N_(String) gettext_noop(String)
+# define U_(String) sudo_warn_gettext(String)
+
+#else /* !HAVE_LIBINTL_H */
+
+/*
+ * Internationalization is either unavailable or has been disabled.
+ * Define away the gettext functions used by sudo.
+ */
+# define _(String) String
+# define N_(String) String
+# define U_(String) String
+# define textdomain(Domain)
+# define bindtextdomain(Package, Directory)
+# define ngettext(String, String_Plural, N) \
+ ((N) == 1 ? (String) : (String_Plural))
+
+#endif /* HAVE_LIBINTL_H */
+
+#endif /* SUDO_GETTEXT_H */
diff --git a/include/sudo_lbuf.h b/include/sudo_lbuf.h
new file mode 100644
index 0000000..8e07063
--- /dev/null
+++ b/include/sudo_lbuf.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2007, 2010, 2011, 2013-2015
+ * Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * 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_LBUF_H
+#define SUDO_LBUF_H
+
+/*
+ * Line buffer struct.
+ */
+struct sudo_lbuf {
+ int (*output)(const char *);
+ char *buf;
+ const char *continuation;
+ int indent;
+ int len;
+ int size;
+ short cols;
+ short error;
+};
+
+typedef int (*sudo_lbuf_output_t)(const char *);
+
+__dso_public void sudo_lbuf_init_v1(struct sudo_lbuf *lbuf, sudo_lbuf_output_t output, int indent, const char *continuation, int cols);
+__dso_public void sudo_lbuf_destroy_v1(struct sudo_lbuf *lbuf);
+__dso_public bool sudo_lbuf_append_v1(struct sudo_lbuf *lbuf, const char *fmt, ...) __printflike(2, 3);
+__dso_public bool sudo_lbuf_append_quoted_v1(struct sudo_lbuf *lbuf, const char *set, const char *fmt, ...) __printflike(3, 4);
+__dso_public void sudo_lbuf_print_v1(struct sudo_lbuf *lbuf);
+__dso_public bool sudo_lbuf_error_v1(struct sudo_lbuf *lbuf);
+__dso_public void sudo_lbuf_clearerr_v1(struct sudo_lbuf *lbuf);
+
+#define sudo_lbuf_init(_a, _b, _c, _d, _e) sudo_lbuf_init_v1((_a), (_b), (_c), (_d), (_e))
+#define sudo_lbuf_destroy(_a) sudo_lbuf_destroy_v1((_a))
+#define sudo_lbuf_append sudo_lbuf_append_v1
+#define sudo_lbuf_append_quoted sudo_lbuf_append_quoted_v1
+#define sudo_lbuf_print(_a) sudo_lbuf_print_v1((_a))
+#define sudo_lbuf_error(_a) sudo_lbuf_error_v1((_a))
+#define sudo_lbuf_clearerr(_a) sudo_lbuf_clearerr_v1((_a))
+
+#endif /* SUDO_LBUF_H */
diff --git a/include/sudo_plugin.h b/include/sudo_plugin.h
new file mode 100644
index 0000000..31d96cc
--- /dev/null
+++ b/include/sudo_plugin.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2009-2018 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * 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_PLUGIN_H
+#define SUDO_PLUGIN_H
+
+/* API version major/minor */
+#define SUDO_API_VERSION_MAJOR 1
+#define SUDO_API_VERSION_MINOR 13
+#define SUDO_API_MKVERSION(x, y) (((x) << 16) | (y))
+#define SUDO_API_VERSION SUDO_API_MKVERSION(SUDO_API_VERSION_MAJOR, SUDO_API_VERSION_MINOR)
+
+/* Getters and setters for plugin API versions */
+#define SUDO_API_VERSION_GET_MAJOR(v) ((v) >> 16)
+#define SUDO_API_VERSION_GET_MINOR(v) ((v) & 0xffffU)
+#define SUDO_API_VERSION_SET_MAJOR(vp, n) do { \
+ *(vp) = (*(vp) & 0x0000ffffU) | ((n) << 16); \
+} while(0)
+#define SUDO_API_VERSION_SET_MINOR(vp, n) do { \
+ *(vp) = (*(vp) & 0xffff0000U) | (n); \
+} while(0)
+
+/* Conversation function types and defines */
+struct sudo_conv_message {
+#define SUDO_CONV_PROMPT_ECHO_OFF 0x0001 /* do not echo user input */
+#define SUDO_CONV_PROMPT_ECHO_ON 0x0002 /* echo user input */
+#define SUDO_CONV_ERROR_MSG 0x0003 /* error message */
+#define SUDO_CONV_INFO_MSG 0x0004 /* informational message */
+#define SUDO_CONV_PROMPT_MASK 0x0005 /* mask user input */
+#define SUDO_CONV_PROMPT_ECHO_OK 0x1000 /* flag: allow echo if no tty */
+#define SUDO_CONV_PREFER_TTY 0x2000 /* flag: use tty if possible */
+ int msg_type;
+ int timeout;
+ const char *msg;
+};
+
+/*
+ * Maximum length of a reply (not including the trailing NUL) when
+ * conversing with the user. In practical terms, this is the longest
+ * password sudo will support. This means that a buffer of size
+ * SUDO_CONV_REPL_MAX+1 is guaranteed to be able to hold any reply
+ * from the conversation function. It is also useful as a max value
+ * for memset_s() when clearing passwords returned by the conversation
+ * function.
+ */
+#define SUDO_CONV_REPL_MAX 255
+
+struct sudo_conv_reply {
+ char *reply;
+};
+
+/* Conversation callback API version major/minor */
+#define SUDO_CONV_CALLBACK_VERSION_MAJOR 1
+#define SUDO_CONV_CALLBACK_VERSION_MINOR 0
+#define SUDO_CONV_CALLBACK_VERSION SUDO_API_MKVERSION(SUDO_CONV_CALLBACK_VERSION_MAJOR, SUDO_CONV_CALLBACK_VERSION_MINOR)
+
+/*
+ * Callback struct to be passed to the conversation function.
+ * Can be used to perform operations on suspend/resume such
+ * as dropping/acquiring locks.
+ */
+typedef int (*sudo_conv_callback_fn_t)(int signo, void *closure);
+struct sudo_conv_callback {
+ unsigned int version;
+ void *closure;
+ sudo_conv_callback_fn_t on_suspend;
+ sudo_conv_callback_fn_t on_resume;
+};
+
+typedef int (*sudo_conv_t)(int num_msgs, const struct sudo_conv_message msgs[],
+ struct sudo_conv_reply replies[], struct sudo_conv_callback *callback);
+typedef int (*sudo_printf_t)(int msg_type, const char *fmt, ...);
+
+/*
+ * Hooks allow a plugin to hook into specific sudo and/or libc functions.
+ */
+
+/* Hook functions typedefs. */
+typedef int (*sudo_hook_fn_t)();
+typedef int (*sudo_hook_fn_setenv_t)(const char *name, const char *value, int overwrite, void *closure);
+typedef int (*sudo_hook_fn_putenv_t)(char *string, void *closure);
+typedef int (*sudo_hook_fn_getenv_t)(const char *name, char **value, void *closure);
+typedef int (*sudo_hook_fn_unsetenv_t)(const char *name, void *closure);
+
+/* Hook structure definition. */
+struct sudo_hook {
+ unsigned int hook_version;
+ unsigned int hook_type;
+ sudo_hook_fn_t hook_fn;
+ void *closure;
+};
+
+/* Hook API version major/minor */
+#define SUDO_HOOK_VERSION_MAJOR 1
+#define SUDO_HOOK_VERSION_MINOR 0
+#define SUDO_HOOK_VERSION SUDO_API_MKVERSION(SUDO_HOOK_VERSION_MAJOR, SUDO_HOOK_VERSION_MINOR)
+
+/*
+ * Hook function return values.
+ */
+#define SUDO_HOOK_RET_ERROR -1 /* error */
+#define SUDO_HOOK_RET_NEXT 0 /* go to the next hook in the list */
+#define SUDO_HOOK_RET_STOP 1 /* stop hook processing for this type */
+
+/*
+ * Hooks for setenv/unsetenv/putenv/getenv.
+ * This allows the plugin to be notified when a PAM module modifies
+ * the environment so it can update the copy of the environment that
+ * is passed to execve().
+ */
+#define SUDO_HOOK_SETENV 1
+#define SUDO_HOOK_UNSETENV 2
+#define SUDO_HOOK_PUTENV 3
+#define SUDO_HOOK_GETENV 4
+
+/* Policy plugin type and defines */
+struct passwd;
+struct policy_plugin {
+#define SUDO_POLICY_PLUGIN 1
+ unsigned int type; /* always SUDO_POLICY_PLUGIN */
+ unsigned int version; /* always SUDO_API_VERSION */
+ int (*open)(unsigned int version, sudo_conv_t conversation,
+ sudo_printf_t sudo_printf, char * const settings[],
+ char * const user_info[], char * const user_env[],
+ char * const plugin_options[]);
+ void (*close)(int exit_status, int error); /* wait status or error */
+ int (*show_version)(int verbose);
+ int (*check_policy)(int argc, char * const argv[],
+ char *env_add[], char **command_info[],
+ char **argv_out[], char **user_env_out[]);
+ int (*list)(int argc, char * const argv[], int verbose,
+ const char *list_user);
+ int (*validate)(void);
+ void (*invalidate)(int remove);
+ int (*init_session)(struct passwd *pwd, char **user_env_out[]);
+ void (*register_hooks)(int version, int (*register_hook)(struct sudo_hook *hook));
+ void (*deregister_hooks)(int version, int (*deregister_hook)(struct sudo_hook *hook));
+};
+
+/* I/O plugin type and defines */
+struct io_plugin {
+#define SUDO_IO_PLUGIN 2
+ unsigned int type; /* always SUDO_IO_PLUGIN */
+ unsigned int version; /* always SUDO_API_VERSION */
+ int (*open)(unsigned int version, sudo_conv_t conversation,
+ sudo_printf_t sudo_printf, char * const settings[],
+ char * const user_info[], char * const command_info[],
+ int argc, char * const argv[], char * const user_env[],
+ char * const plugin_options[]);
+ void (*close)(int exit_status, int error); /* wait status or error */
+ int (*show_version)(int verbose);
+ int (*log_ttyin)(const char *buf, unsigned int len);
+ int (*log_ttyout)(const char *buf, unsigned int len);
+ int (*log_stdin)(const char *buf, unsigned int len);
+ int (*log_stdout)(const char *buf, unsigned int len);
+ int (*log_stderr)(const char *buf, unsigned int len);
+ void (*register_hooks)(int version, int (*register_hook)(struct sudo_hook *hook));
+ void (*deregister_hooks)(int version, int (*deregister_hook)(struct sudo_hook *hook));
+ int (*change_winsize)(unsigned int rows, unsigned int cols);
+ int (*log_suspend)(int signo);
+};
+
+/* Sudoers group plugin version major/minor */
+#define GROUP_API_VERSION_MAJOR 1
+#define GROUP_API_VERSION_MINOR 0
+#define GROUP_API_VERSION SUDO_API_MKVERSION(GROUP_API_VERSION_MAJOR, GROUP_API_VERSION_MINOR)
+
+/* Getters and setters for group version (for source compat only) */
+#define GROUP_API_VERSION_GET_MAJOR(v) SUDO_API_VERSION_GET_MAJOR(v)
+#define GROUP_API_VERSION_GET_MINOR(v) SUDO_API_VERSION_GET_MINOR(v)
+#define GROUP_API_VERSION_SET_MAJOR(vp, n) SUDO_API_VERSION_SET_MAJOR(vp, n)
+#define GROUP_API_VERSION_SET_MINOR(vp, n) SUDO_API_VERSION_SET_MINOR(vp, n)
+
+/*
+ * version: for compatibility checking
+ * group_init: return 1 on success, 0 if unconfigured, -1 on error.
+ * group_cleanup: called to clean up resources used by provider
+ * user_in_group: returns 1 if user is in group, 0 if not.
+ * note that pwd may be NULL if the user is not in passwd.
+ */
+struct sudoers_group_plugin {
+ unsigned int version;
+ int (*init)(int version, sudo_printf_t sudo_printf, char *const argv[]);
+ void (*cleanup)(void);
+ int (*query)(const char *user, const char *group, const struct passwd *pwd);
+};
+
+#endif /* SUDO_PLUGIN_H */
diff --git a/include/sudo_queue.h b/include/sudo_queue.h
new file mode 100644
index 0000000..f48daf9
--- /dev/null
+++ b/include/sudo_queue.h
@@ -0,0 +1,821 @@
+/*
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ * $FreeBSD: head/sys/sys/queue.h 251887 2013-06-18 02:57:56Z lstewart $
+ */
+
+#ifndef SUDO_QUEUE_H
+#define SUDO_QUEUE_H
+
+/*
+ * This file defines four types of data structures: singly-linked lists,
+ * singly-linked tail queues, lists and tail queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A singly-linked tail queue is headed by a pair of pointers, one to the
+ * head of the list and the other to the tail of the list. The elements are
+ * singly linked for minimum space and pointer manipulation overhead at the
+ * expense of O(n) removal for arbitrary elements. New elements can be added
+ * to the list after an existing element, at the head of the list, or at the
+ * end of the list. Elements being removed from the head of the tail queue
+ * should use the explicit macro for this purpose for optimum efficiency.
+ * A singly-linked tail queue may only be traversed in the forward direction.
+ * Singly-linked tail queues are ideal for applications with large datasets
+ * and few or no removals or for implementing a FIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may be traversed in either direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * A headless tail queue lacks a head structure, The first element acts
+ * as a de facto list head. It uses the same entry struct as a regular
+ * tail queue for easy conversion from headless to headful.
+ * It is capable of concatenating queues as well as individual elements.
+ * Traversing in reverse is more expensive due to lack of a list head.
+ * Note: elements must be initialized before use.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ *
+ *
+ * SLIST LIST STAILQ TAILQ
+ * _HEAD + + + +
+ * _HEAD_INITIALIZER + + + +
+ * _ENTRY + + + +
+ * _INIT + + + +
+ * _EMPTY + + + +
+ * _FIRST + + + +
+ * _NEXT + + + +
+ * _PREV - + - +
+ * _LAST - - + +
+ * _FOREACH + + + +
+ * _FOREACH_FROM + + + +
+ * _FOREACH_SAFE + + + +
+ * _FOREACH_FROM_SAFE + + + +
+ * _FOREACH_REVERSE - - - +
+ * _FOREACH_REVERSE_FROM - - - +
+ * _FOREACH_REVERSE_SAFE - - - +
+ * _FOREACH_REVERSE_FROM_SAFE - - - +
+ * _INSERT_HEAD + + + +
+ * _INSERT_BEFORE - + - +
+ * _INSERT_AFTER + + + +
+ * _INSERT_TAIL - - + +
+ * _CONCAT - - + +
+ * _REMOVE_AFTER + - + -
+ * _REMOVE_HEAD + - + -
+ * _REMOVE + + + +
+ * _SWAP + + + +
+ *
+ */
+#ifdef QUEUE_MACRO_DEBUG
+/* Store the last 2 places the queue element or head was altered */
+struct qm_trace {
+ unsigned long lastline;
+ unsigned long prevline;
+ const char *lastfile;
+ const char *prevfile;
+};
+
+#undef TRACEBUF
+#define TRACEBUF struct qm_trace trace;
+#undef TRACEBUF_INITIALIZER
+#define TRACEBUF_INITIALIZER { __FILE__, __LINE__, NULL, 0 } ,
+#undef TRASHIT
+#define TRASHIT(x) do {(x) = (void *)-1;} while (0)
+#undef QMD_SAVELINK
+#define QMD_SAVELINK(name, link) void **name = (void *)&(link)
+
+#undef QMD_TRACE_HEAD
+#define QMD_TRACE_HEAD(head) do { \
+ (head)->trace.prevline = (head)->trace.lastline; \
+ (head)->trace.prevfile = (head)->trace.lastfile; \
+ (head)->trace.lastline = __LINE__; \
+ (head)->trace.lastfile = __FILE__; \
+} while (0)
+
+#undef QMD_TRACE_ELEM
+#define QMD_TRACE_ELEM(elem) do { \
+ (elem)->trace.prevline = (elem)->trace.lastline; \
+ (elem)->trace.prevfile = (elem)->trace.lastfile; \
+ (elem)->trace.lastline = __LINE__; \
+ (elem)->trace.lastfile = __FILE__; \
+} while (0)
+
+#else
+#undef QMD_TRACE_ELEM
+#define QMD_TRACE_ELEM(elem)
+#undef QMD_TRACE_HEAD
+#define QMD_TRACE_HEAD(head)
+#undef QMD_SAVELINK
+#define QMD_SAVELINK(name, link)
+#undef TRACEBUF
+#define TRACEBUF
+#undef TRACEBUF_INITIALIZER
+#define TRACEBUF_INITIALIZER
+#undef TRASHIT
+#define TRASHIT(x)
+#endif /* QUEUE_MACRO_DEBUG */
+
+/*
+ * XXX - Work around a bug in the llvm static analyzer.
+ * https://bugs.llvm.org//show_bug.cgi?id=18222
+ */
+#ifdef __clang_analyzer__
+# define ANALYZER_ASSERT(x) do { \
+ if (!__builtin_expect(!(x), 0)) \
+ __builtin_trap(); \
+} while (0)
+#else
+# define ANALYZER_ASSERT(x) do {} while (0)
+#endif /* __clang_analyzer__ */
+
+ /*
+ * Singly-linked List declarations.
+ */
+#undef SLIST_HEAD
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#undef SLIST_HEAD_INITIALIZER
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#undef SLIST_ENTRY
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List functions.
+ */
+#undef SLIST_EMPTY
+#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
+
+#undef SLIST_FIRST
+#define SLIST_FIRST(head) ((head)->slh_first)
+
+#undef SLIST_FOREACH
+#define SLIST_FOREACH(var, head, field) \
+ for ((var) = SLIST_FIRST((head)); \
+ (var); \
+ (var) = SLIST_NEXT((var), field))
+
+#undef SLIST_FOREACH_FROM
+#define SLIST_FOREACH_FROM(var, head, field) \
+ for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
+ (var); \
+ (var) = SLIST_NEXT((var), field))
+
+#undef SLIST_FOREACH_SAFE
+#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = SLIST_FIRST((head)); \
+ (var) && ((tvar) = SLIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#undef SLIST_FOREACH_FROM_SAFE
+#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
+ for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
+ (var) && ((tvar) = SLIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#undef SLIST_FOREACH_PREVPTR
+#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
+ for ((varp) = &SLIST_FIRST((head)); \
+ ((var) = *(varp)) != NULL; \
+ (varp) = &SLIST_NEXT((var), field))
+
+#undef SLIST_INIT
+#define SLIST_INIT(head) do { \
+ SLIST_FIRST((head)) = NULL; \
+} while (0)
+
+#undef SLIST_INSERT_AFTER
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
+ SLIST_NEXT((slistelm), field) = (elm); \
+} while (0)
+
+#undef SLIST_INSERT_HEAD
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
+ SLIST_FIRST((head)) = (elm); \
+} while (0)
+
+#undef SLIST_NEXT
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#undef SLIST_REMOVE
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ QMD_SAVELINK(oldnext, (elm)->field.sle_next); \
+ if (SLIST_FIRST((head)) == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = SLIST_FIRST((head)); \
+ while (SLIST_NEXT(curelm, field) != (elm)) \
+ curelm = SLIST_NEXT(curelm, field); \
+ SLIST_REMOVE_AFTER(curelm, field); \
+ } \
+ TRASHIT(*oldnext); \
+} while (0)
+
+#undef SLIST_REMOVE_AFTER
+#define SLIST_REMOVE_AFTER(elm, field) do { \
+ SLIST_NEXT(elm, field) = \
+ SLIST_NEXT(SLIST_NEXT(elm, field), field); \
+} while (0)
+
+#undef SLIST_REMOVE_HEAD
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
+} while (0)
+
+#undef SLIST_SWAP
+#define SLIST_SWAP(head1, head2, type) do { \
+ struct type *swap_first = SLIST_FIRST(head1); \
+ SLIST_FIRST(head1) = SLIST_FIRST(head2); \
+ SLIST_FIRST(head2) = swap_first; \
+} while (0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#undef STAILQ_HEAD
+#define STAILQ_HEAD(name, type) \
+struct name { \
+ struct type *stqh_first;/* first element */ \
+ struct type **stqh_last;/* addr of last next element */ \
+}
+
+#undef STAILQ_HEAD_INITIALIZER
+#define STAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).stqh_first }
+
+#undef STAILQ_ENTRY
+#define STAILQ_ENTRY(type) \
+struct { \
+ struct type *stqe_next; /* next element */ \
+}
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#undef STAILQ_CONCAT
+#define STAILQ_CONCAT(head1, head2) do { \
+ if (!STAILQ_EMPTY((head2))) { \
+ *(head1)->stqh_last = (head2)->stqh_first; \
+ (head1)->stqh_last = (head2)->stqh_last; \
+ STAILQ_INIT((head2)); \
+ } \
+} while (0)
+
+#undef STAILQ_EMPTY
+#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
+
+#undef STAILQ_FIRST
+#define STAILQ_FIRST(head) ((head)->stqh_first)
+
+#undef STAILQ_FOREACH
+#define STAILQ_FOREACH(var, head, field) \
+ for ((var) = STAILQ_FIRST((head)); \
+ (var); \
+ (var) = STAILQ_NEXT((var), field))
+
+#undef STAILQ_FOREACH_FROM
+#define STAILQ_FOREACH_FROM(var, head, field) \
+ for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
+ (var); \
+ (var) = STAILQ_NEXT((var), field))
+
+#undef STAILQ_FOREACH_SAFE
+#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = STAILQ_FIRST((head)); \
+ (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#undef STAILQ_FOREACH_FROM_SAFE
+#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
+ for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
+ (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#undef STAILQ_INIT
+#define STAILQ_INIT(head) do { \
+ STAILQ_FIRST((head)) = NULL; \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+} while (0)
+
+#undef STAILQ_INSERT_AFTER
+#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
+ if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ STAILQ_NEXT((tqelm), field) = (elm); \
+} while (0)
+
+#undef STAILQ_INSERT_HEAD
+#define STAILQ_INSERT_HEAD(head, elm, field) do { \
+ if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ STAILQ_FIRST((head)) = (elm); \
+} while (0)
+
+#undef STAILQ_INSERT_TAIL
+#define STAILQ_INSERT_TAIL(head, elm, field) do { \
+ STAILQ_NEXT((elm), field) = NULL; \
+ *(head)->stqh_last = (elm); \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+} while (0)
+
+#undef STAILQ_LAST
+#define STAILQ_LAST(head, type, field) \
+ (STAILQ_EMPTY((head)) ? NULL : \
+ __containerof((head)->stqh_last, struct type, field.stqe_next))
+
+#undef STAILQ_NEXT
+#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+
+#undef STAILQ_REMOVE
+#define STAILQ_REMOVE(head, elm, type, field) do { \
+ QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \
+ if (STAILQ_FIRST((head)) == (elm)) { \
+ STAILQ_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = STAILQ_FIRST((head)); \
+ while (STAILQ_NEXT(curelm, field) != (elm)) \
+ curelm = STAILQ_NEXT(curelm, field); \
+ STAILQ_REMOVE_AFTER(head, curelm, field); \
+ } \
+ TRASHIT(*oldnext); \
+} while (0)
+
+#undef STAILQ_REMOVE_AFTER
+#define STAILQ_REMOVE_AFTER(head, elm, field) do { \
+ if ((STAILQ_NEXT(elm, field) = \
+ STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+} while (0)
+
+#undef STAILQ_REMOVE_HEAD
+#define STAILQ_REMOVE_HEAD(head, field) do { \
+ if ((STAILQ_FIRST((head)) = \
+ STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+} while (0)
+
+#undef STAILQ_SWAP
+#define STAILQ_SWAP(head1, head2, type) do { \
+ struct type *swap_first = STAILQ_FIRST(head1); \
+ struct type **swap_last = (head1)->stqh_last; \
+ STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \
+ (head1)->stqh_last = (head2)->stqh_last; \
+ STAILQ_FIRST(head2) = swap_first; \
+ (head2)->stqh_last = swap_last; \
+ if (STAILQ_EMPTY(head1)) \
+ (head1)->stqh_last = &STAILQ_FIRST(head1); \
+ if (STAILQ_EMPTY(head2)) \
+ (head2)->stqh_last = &STAILQ_FIRST(head2); \
+} while (0)
+
+
+/*
+ * List declarations.
+ */
+#undef LIST_HEAD
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#undef LIST_HEAD_INITIALIZER
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#undef LIST_ENTRY
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List functions.
+ */
+#undef LIST_EMPTY
+#define LIST_EMPTY(head) ((head)->lh_first == NULL)
+
+#undef LIST_FIRST
+#define LIST_FIRST(head) ((head)->lh_first)
+
+#undef LIST_FOREACH
+#define LIST_FOREACH(var, head, field) \
+ for ((var) = LIST_FIRST((head)); \
+ (var); \
+ (var) = LIST_NEXT((var), field))
+
+#undef LIST_FOREACH_FROM
+#define LIST_FOREACH_FROM(var, head, field) \
+ for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
+ (var); \
+ (var) = LIST_NEXT((var), field))
+
+#undef LIST_FOREACH_SAFE
+#define LIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = LIST_FIRST((head)); \
+ (var) && ((tvar) = LIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#undef LIST_FOREACH_FROM_SAFE
+#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
+ for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
+ (var) && ((tvar) = LIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#undef LIST_INIT
+#define LIST_INIT(head) do { \
+ LIST_FIRST((head)) = NULL; \
+} while (0)
+
+#undef LIST_INSERT_AFTER
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
+ LIST_NEXT((listelm), field)->field.le_prev = \
+ &LIST_NEXT((elm), field); \
+ LIST_NEXT((listelm), field) = (elm); \
+ (elm)->field.le_prev = &LIST_NEXT((listelm), field); \
+} while (0)
+
+#undef LIST_INSERT_BEFORE
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ LIST_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &LIST_NEXT((elm), field); \
+} while (0)
+
+#undef LIST_INSERT_HEAD
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
+ LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
+ LIST_FIRST((head)) = (elm); \
+ (elm)->field.le_prev = &LIST_FIRST((head)); \
+} while (0)
+
+#undef LIST_NEXT
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#undef LIST_PREV
+#define LIST_PREV(elm, head, type, field) \
+ ((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \
+ __containerof((elm)->field.le_prev, struct type, field.le_next))
+
+#undef LIST_REMOVE
+#define LIST_REMOVE(elm, field) do { \
+ ANALYZER_ASSERT(elm != NULL); \
+ QMD_SAVELINK(oldnext, (elm)->field.le_next); \
+ QMD_SAVELINK(oldprev, (elm)->field.le_prev); \
+ if (LIST_NEXT((elm), field) != NULL) \
+ LIST_NEXT((elm), field)->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = LIST_NEXT((elm), field); \
+ TRASHIT(*oldnext); \
+ TRASHIT(*oldprev); \
+} while (0)
+
+#undef LIST_SWAP
+#define LIST_SWAP(head1, head2, type, field) do { \
+ struct type *swap_tmp = LIST_FIRST((head1)); \
+ LIST_FIRST((head1)) = LIST_FIRST((head2)); \
+ LIST_FIRST((head2)) = swap_tmp; \
+ if ((swap_tmp = LIST_FIRST((head1))) != NULL) \
+ swap_tmp->field.le_prev = &LIST_FIRST((head1)); \
+ if ((swap_tmp = LIST_FIRST((head2))) != NULL) \
+ swap_tmp->field.le_prev = &LIST_FIRST((head2)); \
+} while (0)
+
+/*
+ * Tail queue declarations.
+ */
+#undef TAILQ_HEAD
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+ TRACEBUF \
+}
+
+#undef TAILQ_HEAD_INITIALIZER
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first, TRACEBUF_INITIALIZER }
+
+#undef TAILQ_ENTRY
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+ TRACEBUF \
+}
+
+/*
+ * Tail queue functions.
+ */
+#undef TAILQ_CONCAT
+#define TAILQ_CONCAT(head1, head2, field) do { \
+ if (!TAILQ_EMPTY(head2)) { \
+ *(head1)->tqh_last = (head2)->tqh_first; \
+ (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
+ (head1)->tqh_last = (head2)->tqh_last; \
+ TAILQ_INIT((head2)); \
+ QMD_TRACE_HEAD(head1); \
+ QMD_TRACE_HEAD(head2); \
+ } \
+} while (0)
+
+#undef TAILQ_EMPTY
+#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+
+#undef TAILQ_FIRST
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+
+#undef TAILQ_FOREACH
+#define TAILQ_FOREACH(var, head, field) \
+ for ((var) = TAILQ_FIRST((head)); \
+ (var); \
+ (var) = TAILQ_NEXT((var), field))
+
+#undef TAILQ_FOREACH_FROM
+#define TAILQ_FOREACH_FROM(var, head, field) \
+ for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
+ (var); \
+ (var) = TAILQ_NEXT((var), field))
+
+#undef TAILQ_FOREACH_SAFE
+#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = TAILQ_FIRST((head)); \
+ (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#undef TAILQ_FOREACH_FROM_SAFE
+#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
+ for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
+ (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#undef TAILQ_FOREACH_REVERSE
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var); \
+ (var) = TAILQ_PREV((var), headname, field))
+
+#undef TAILQ_FOREACH_REVERSE_FROM
+#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \
+ for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
+ (var); \
+ (var) = TAILQ_PREV((var), headname, field))
+
+#undef TAILQ_FOREACH_REVERSE_SAFE
+#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
+ (var) = (tvar))
+
+#undef TAILQ_FOREACH_REVERSE_FROM_SAFE
+#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \
+ for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
+ (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
+ (var) = (tvar))
+
+#undef TAILQ_INIT
+#define TAILQ_INIT(head) do { \
+ TAILQ_FIRST((head)) = NULL; \
+ (head)->tqh_last = &TAILQ_FIRST((head)); \
+ QMD_TRACE_HEAD(head); \
+} while (0)
+
+#undef TAILQ_INSERT_AFTER
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
+ TAILQ_NEXT((elm), field)->field.tqe_prev = \
+ &TAILQ_NEXT((elm), field); \
+ else { \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ QMD_TRACE_HEAD(head); \
+ } \
+ TAILQ_NEXT((listelm), field) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+ QMD_TRACE_ELEM(&listelm->field); \
+} while (0)
+
+#undef TAILQ_INSERT_BEFORE
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ TAILQ_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+ QMD_TRACE_ELEM(&listelm->field); \
+} while (0)
+
+#undef TAILQ_INSERT_HEAD
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
+ TAILQ_FIRST((head))->field.tqe_prev = \
+ &TAILQ_NEXT((elm), field); \
+ else \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ TAILQ_FIRST((head)) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
+ QMD_TRACE_HEAD(head); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+} while (0)
+
+#undef TAILQ_INSERT_TAIL
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ TAILQ_NEXT((elm), field) = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ QMD_TRACE_HEAD(head); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+} while (0)
+
+#undef TAILQ_LAST
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#undef TAILQ_NEXT
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#undef TAILQ_PREV
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#undef TAILQ_REMOVE
+#define TAILQ_REMOVE(head, elm, field) do { \
+ ANALYZER_ASSERT(elm != NULL); \
+ QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \
+ QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \
+ if ((TAILQ_NEXT((elm), field)) != NULL) \
+ TAILQ_NEXT((elm), field)->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else { \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ QMD_TRACE_HEAD(head); \
+ } \
+ *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
+ TRASHIT(*oldnext); \
+ TRASHIT(*oldprev); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+} while (0)
+
+#undef TAILQ_SWAP
+#define TAILQ_SWAP(head1, head2, type, field) do { \
+ struct type *swap_first = (head1)->tqh_first; \
+ struct type **swap_last = (head1)->tqh_last; \
+ (head1)->tqh_first = (head2)->tqh_first; \
+ (head1)->tqh_last = (head2)->tqh_last; \
+ (head2)->tqh_first = swap_first; \
+ (head2)->tqh_last = swap_last; \
+ if ((swap_first = (head1)->tqh_first) != NULL) \
+ swap_first->field.tqe_prev = &(head1)->tqh_first; \
+ else \
+ (head1)->tqh_last = &(head1)->tqh_first; \
+ if ((swap_first = (head2)->tqh_first) != NULL) \
+ swap_first->field.tqe_prev = &(head2)->tqh_first; \
+ else \
+ (head2)->tqh_last = &(head2)->tqh_first; \
+} while (0)
+
+/*
+ * Headless Tail queue definitions.
+ */
+#undef HLTQ_ENTRY
+#define HLTQ_ENTRY(type) TAILQ_ENTRY(type)
+
+#undef HLTQ_INIT
+#define HLTQ_INIT(entry, field) do { \
+ (entry)->field.tqe_next = NULL; \
+ (entry)->field.tqe_prev = &(entry)->field.tqe_next; \
+} while (0)
+
+#undef HLTQ_INITIALIZER
+#define HLTQ_INITIALIZER(entry, field) \
+ { NULL, &(entry)->field.tqe_next }
+
+#undef HLTQ_FIRST
+#define HLTQ_FIRST(elm) (elm)
+
+#undef HLTQ_END
+#define HLTQ_END(elm) NULL
+
+#undef HLTQ_NEXT
+#define HLTQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#undef HLTQ_LAST
+#define HLTQ_LAST(elm, type, field) \
+ ((elm)->field.tqe_next == NULL ? (elm) : \
+ __containerof((elm)->field.tqe_prev, struct type, field.tqe_next))
+
+#undef HLTQ_PREV
+#define HLTQ_PREV(elm, type, field) \
+ (*(elm)->field.tqe_prev == NULL ? NULL : \
+ __containerof((elm)->field.tqe_prev, struct type, field.tqe_next))
+
+#undef HLTQ_FOREACH
+#define HLTQ_FOREACH(var, head, field) \
+ for ((var) = HLTQ_FIRST(head); \
+ (var) != HLTQ_END(head); \
+ (var) = HLTQ_NEXT(var, field))
+
+#undef HLTQ_FOREACH_SAFE
+#define HLTQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = HLTQ_FIRST(head); \
+ (var) != HLTQ_END(head) && \
+ ((tvar) = HLTQ_NEXT(var, field), 1); \
+ (var) = (tvar))
+
+#undef HLTQ_FOREACH_REVERSE
+#define HLTQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = HLTQ_LAST(head, headname); \
+ (var) != HLTQ_END(head); \
+ (var) = HLTQ_PREV(var, headname, field))
+
+#undef HLTQ_FOREACH_REVERSE_SAFE
+#define HLTQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
+ for ((var) = HLTQ_LAST(head, headname); \
+ (var) != HLTQ_END(head) && \
+ ((tvar) = HLTQ_PREV(var, headname, field), 1); \
+ (var) = (tvar))
+
+/* Concatenate queue2 to the end of queue1. */
+#undef HLTQ_CONCAT
+#define HLTQ_CONCAT(queue1, queue2, field) do { \
+ (queue2)->field.tqe_prev = (queue1)->field.tqe_prev; \
+ *(queue1)->field.tqe_prev = (queue2); \
+ (queue1)->field.tqe_prev = &(queue2)->field.tqe_next; \
+} while (0)
+
+/* Convert a headless tailq to a headful one. */
+#define HLTQ_TO_TAILQ(head, hl, field) do { \
+ (head)->tqh_first = (hl); \
+ (head)->tqh_last = (hl)->field.tqe_prev; \
+ (hl)->field.tqe_prev = &(head)->tqh_first; \
+} while (0)
+
+/* Concatenate a headless tail queue to the end of a regular tail queue. */
+#define TAILQ_CONCAT_HLTQ(head, hl, field) do { \
+ void *last = (hl)->field.tqe_prev; \
+ (hl)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (hl); \
+ (head)->tqh_last = last; \
+} while (0)
+
+#endif /* !SUDO_QUEUE_H */
diff --git a/include/sudo_rand.h b/include/sudo_rand.h
new file mode 100644
index 0000000..2e83085
--- /dev/null
+++ b/include/sudo_rand.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2018 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * 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.
+ *
+ * Sponsored in part by the Defense Advanced Research Projects
+ * Agency (DARPA) and Air Force Research Laboratory, Air Force
+ * Materiel Command, USAF, under agreement number F39502-99-1-0512.
+ */
+
+#ifndef SUDO_RAND_H
+#define SUDO_RAND_H
+
+#include <stdlib.h> /* For arc4random() on systems that have it */
+
+/*
+ * All libc replacements are prefixed with "sudo_" to avoid namespace issues.
+ */
+
+#ifndef HAVE_ARC4RANDOM
+/* Note: not exported by libutil. */
+uint32_t sudo_arc4random(void);
+# undef arc4random
+# define arc4random() sudo_arc4random()
+#endif /* ARC4RANDOM */
+
+#ifndef HAVE_ARC4RANDOM_UNIFORM
+__dso_public uint32_t sudo_arc4random_uniform(uint32_t upper_bound);
+# undef arc4random_uniform
+# define arc4random_uniform(_a) sudo_arc4random_uniform((_a))
+#endif /* ARC4RANDOM_UNIFORM */
+
+#ifndef HAVE_GETENTROPY
+/* Note: not exported by libutil. */
+int sudo_getentropy(void *buf, size_t buflen);
+# undef getentropy
+# define getentropy(_a, _b) sudo_getentropy((_a), (_b))
+#endif /* HAVE_GETENTROPY */
+
+#endif /* SUDO_RAND_H */
diff --git a/include/sudo_util.h b/include/sudo_util.h
new file mode 100644
index 0000000..96cac16
--- /dev/null
+++ b/include/sudo_util.h
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2013-2018 Todd C. Miller <Todd.Miller@sudo.ws>
+ *
+ * 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_UTIL_H
+#define SUDO_UTIL_H
+
+#ifdef HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# include "compat/stdbool.h"
+#endif /* HAVE_STDBOOL_H */
+
+#ifndef TIME_T_MAX
+# if SIZEOF_TIME_T == 8
+# define TIME_T_MAX LLONG_MAX
+# else
+# define TIME_T_MAX INT_MAX
+# endif
+#endif
+
+/*
+ * Macros for operating on struct timeval.
+ */
+#define sudo_timevalclear(tv) ((tv)->tv_sec = (tv)->tv_usec = 0)
+
+#define sudo_timevalisset(tv) ((tv)->tv_sec || (tv)->tv_usec)
+
+#define sudo_timevalcmp(tv1, tv2, op) \
+ (((tv1)->tv_sec == (tv2)->tv_sec) ? \
+ ((tv1)->tv_usec op (tv2)->tv_usec) : \
+ ((tv1)->tv_sec op (tv2)->tv_sec))
+
+#define sudo_timevaladd(tv1, tv2, tv3) \
+ do { \
+ (tv3)->tv_sec = (tv1)->tv_sec + (tv2)->tv_sec; \
+ (tv3)->tv_usec = (tv1)->tv_usec + (tv2)->tv_usec; \
+ if ((tv3)->tv_usec >= 1000000) { \
+ (tv3)->tv_sec++; \
+ (tv3)->tv_usec -= 1000000; \
+ } \
+ } while (0)
+
+#define sudo_timevalsub(tv1, tv2, tv3) \
+ do { \
+ (tv3)->tv_sec = (tv1)->tv_sec - (tv2)->tv_sec; \
+ (tv3)->tv_usec = (tv1)->tv_usec - (tv2)->tv_usec; \
+ if ((tv3)->tv_usec < 0) { \
+ (tv3)->tv_sec--; \
+ (tv3)->tv_usec += 1000000; \
+ } \
+ } while (0)
+
+#ifndef TIMEVAL_TO_TIMESPEC
+# define TIMEVAL_TO_TIMESPEC(tv, ts) \
+ do { \
+ (ts)->tv_sec = (tv)->tv_sec; \
+ (ts)->tv_nsec = (tv)->tv_usec * 1000; \
+ } while (0)
+#endif
+
+/*
+ * Macros for operating on struct timespec.
+ */
+#define sudo_timespecclear(ts) ((ts)->tv_sec = (ts)->tv_nsec = 0)
+
+#define sudo_timespecisset(ts) ((ts)->tv_sec || (ts)->tv_nsec)
+
+#define sudo_timespeccmp(ts1, ts2, op) \
+ (((ts1)->tv_sec == (ts2)->tv_sec) ? \
+ ((ts1)->tv_nsec op (ts2)->tv_nsec) : \
+ ((ts1)->tv_sec op (ts2)->tv_sec))
+
+#define sudo_timespecadd(ts1, ts2, ts3) \
+ do { \
+ (ts3)->tv_sec = (ts1)->tv_sec + (ts2)->tv_sec; \
+ (ts3)->tv_nsec = (ts1)->tv_nsec + (ts2)->tv_nsec; \
+ while ((ts3)->tv_nsec >= 1000000000) { \
+ (ts3)->tv_sec++; \
+ (ts3)->tv_nsec -= 1000000000; \
+ } \
+ } while (0)
+
+#define sudo_timespecsub(ts1, ts2, ts3) \
+ do { \
+ (ts3)->tv_sec = (ts1)->tv_sec - (ts2)->tv_sec; \
+ (ts3)->tv_nsec = (ts1)->tv_nsec - (ts2)->tv_nsec; \
+ while ((ts3)->tv_nsec < 0) { \
+ (ts3)->tv_sec--; \
+ (ts3)->tv_nsec += 1000000000; \
+ } \
+ } while (0)
+
+#ifndef TIMESPEC_TO_TIMEVAL
+# define TIMESPEC_TO_TIMEVAL(tv, ts) \
+ do { \
+ (tv)->tv_sec = (ts)->tv_sec; \
+ (tv)->tv_usec = (ts)->tv_nsec / 1000; \
+ } while (0)
+#endif
+
+/*
+ * The timespec version of st_mtime may vary on different platforms.
+ */
+#if defined(HAVE_ST_MTIM)
+# if defined(HAVE_ST__TIM)
+# define SUDO_ST_MTIM st_mtim.st__tim
+# else
+# define SUDO_ST_MTIM st_mtim
+# endif
+#elif defined(HAVE_ST_MTIMESPEC)
+# define SUDO_ST_MTIM st_mtimespec
+#endif
+
+/*
+ * Macro to extract mtime as timespec.
+ * If there is no way to set the timestamp using nanosecond precision,
+ * we only fetch microsecond precision. Otherwise there is a mismatch
+ * between the timestamp we read and the one we wrote.
+ */
+#if defined(SUDO_ST_MTIM)
+# if defined(HAVE_FUTIMENS) && defined(HAVE_UTIMENSAT)
+# define mtim_get(_x, _y) do { (_y).tv_sec = (_x)->SUDO_ST_MTIM.tv_sec; (_y).tv_nsec = (_x)->SUDO_ST_MTIM.tv_nsec; } while (0)
+# else
+# define mtim_get(_x, _y) do { (_y).tv_sec = (_x)->SUDO_ST_MTIM.tv_sec; (_y).tv_nsec = ((_x)->SUDO_ST_MTIM.tv_nsec / 1000) * 1000; } while (0)
+# endif
+#elif defined(HAVE_ST_NMTIME)
+# define mtim_get(_x, _y) do { (_y).tv_sec = (_x)->st_mtime; (_y).tv_nsec = (_x)->st_nmtime; } while (0)
+#else
+# define mtim_get(_x, _y) do { (_y).tv_sec = (_x)->st_mtime; (_y).tv_nsec = 0; } while (0)
+#endif /* HAVE_ST_MTIM */
+
+/* Bit map macros. */
+#define sudo_setbit(_a, _i) ((_a)[(_i) / NBBY] |= 1 << ((_i) % NBBY))
+#define sudo_clrbit(_a, _i) ((_a)[(_i) / NBBY] &= ~(1<<((_i) % NBBY)))
+#define sudo_isset(_a, _i) ((_a)[(_i) / NBBY] & (1<<((_i) % NBBY)))
+#define sudo_isclr(_a, _i) (((_a)[(_i) / NBBY] & (1<<((_i) % NBBY))) == 0)
+
+/* sudo_parseln() flags */
+#define PARSELN_COMM_BOL 0x01 /* comments only at begining of line */
+#define PARSELN_CONT_IGN 0x02 /* ignore line continuation char */
+
+/*
+ * Macros to quiet gcc's warn_unused_result attribute.
+ */
+#ifdef __GNUC__
+# define ignore_result(x) do { \
+ __typeof__(x) y = (x); \
+ (void)y; \
+} while(0)
+#else
+# define ignore_result(x) (void)(x)
+#endif
+
+/* aix.c */
+__dso_public int aix_getauthregistry_v1(char *user, char *saved_registry);
+#define aix_getauthregistry(_a, _b) aix_getauthregistry_v1((_a), (_b))
+__dso_public int aix_prep_user_v1(char *user, const char *tty);
+#define aix_prep_user(_a, _b) aix_prep_user_v1((_a), (_b))
+__dso_public int aix_restoreauthdb_v1(void);
+#define aix_restoreauthdb() aix_restoreauthdb_v1()
+__dso_public int aix_setauthdb_v1(char *user);
+__dso_public int aix_setauthdb_v2(char *user, char *registry);
+#define aix_setauthdb(_a, _b) aix_setauthdb_v2((_a), (_b))
+
+/* gethostname.c */
+__dso_public char *sudo_gethostname_v1(void);
+#define sudo_gethostname() sudo_gethostname_v1()
+
+/* gettime.c */
+__dso_public int sudo_gettime_awake_v1(struct timespec *ts);
+#define sudo_gettime_awake(_a) sudo_gettime_awake_v1((_a))
+__dso_public int sudo_gettime_mono_v1(struct timespec *ts);
+#define sudo_gettime_mono(_a) sudo_gettime_mono_v1((_a))
+__dso_public int sudo_gettime_real_v1(struct timespec *ts);
+#define sudo_gettime_real(_a) sudo_gettime_real_v1((_a))
+
+/* gidlist.c */
+__dso_public int sudo_parse_gids_v1(const char *gidstr, const gid_t *basegid, GETGROUPS_T **gidsp);
+#define sudo_parse_gids(_a, _b, _c) sudo_parse_gids_v1((_a), (_b), (_c))
+
+/* getgrouplist.c */
+__dso_public int sudo_getgrouplist2_v1(const char *name, gid_t basegid, GETGROUPS_T **groupsp, int *ngroupsp);
+#define sudo_getgrouplist2(_a, _b, _c, _d) sudo_getgrouplist2_v1((_a), (_b), (_c), (_d))
+
+/* key_val.c */
+__dso_public char *sudo_new_key_val_v1(const char *key, const char *value);
+#define sudo_new_key_val(_a, _b) sudo_new_key_val_v1((_a), (_b))
+
+/* locking.c */
+#define SUDO_LOCK 1 /* lock a file */
+#define SUDO_TLOCK 2 /* test & lock a file (non-blocking) */
+#define SUDO_UNLOCK 4 /* unlock a file */
+__dso_public bool sudo_lock_file_v1(int fd, int action);
+#define sudo_lock_file(_a, _b) sudo_lock_file_v1((_a), (_b))
+__dso_public bool sudo_lock_region_v1(int fd, int action, off_t len);
+#define sudo_lock_region(_a, _b, _c) sudo_lock_region_v1((_a), (_b), (_c))
+
+/* parseln.c */
+__dso_public ssize_t sudo_parseln_v1(char **buf, size_t *bufsize, unsigned int *lineno, FILE *fp);
+__dso_public ssize_t sudo_parseln_v2(char **buf, size_t *bufsize, unsigned int *lineno, FILE *fp, int flags);
+#define sudo_parseln(_a, _b, _c, _d, _e) sudo_parseln_v2((_a), (_b), (_c), (_d), (_e))
+
+/* progname.c */
+__dso_public void initprogname(const char *);
+
+/* secure_path.c */
+#define SUDO_PATH_SECURE 0
+#define SUDO_PATH_MISSING -1
+#define SUDO_PATH_BAD_TYPE -2
+#define SUDO_PATH_WRONG_OWNER -3
+#define SUDO_PATH_WORLD_WRITABLE -4
+#define SUDO_PATH_GROUP_WRITABLE -5
+struct stat;
+__dso_public int sudo_secure_dir_v1(const char *path, uid_t uid, gid_t gid, struct stat *sbp);
+#define sudo_secure_dir(_a, _b, _c, _d) sudo_secure_dir_v1((_a), (_b), (_c), (_d))
+__dso_public int sudo_secure_file_v1(const char *path, uid_t uid, gid_t gid, struct stat *sbp);
+#define sudo_secure_file(_a, _b, _c, _d) sudo_secure_file_v1((_a), (_b), (_c), (_d))
+
+/* setgroups.c */
+__dso_public int sudo_setgroups_v1(int ngids, const GETGROUPS_T *gids);
+#define sudo_setgroups(_a, _b) sudo_setgroups_v1((_a), (_b))
+
+/* strsplit.c */
+__dso_public const char *sudo_strsplit_v1(const char *str, const char *endstr, const char *sep, const char **last);
+#define sudo_strsplit(_a, _b, _c, _d) sudo_strsplit_v1(_a, _b, _c, _d)
+
+/* strtobool.c */
+__dso_public int sudo_strtobool_v1(const char *str);
+#define sudo_strtobool(_a) sudo_strtobool_v1((_a))
+
+/* strtoid.c */
+__dso_public id_t sudo_strtoid_v1(const char *str, const char *sep, char **endp, const char **errstr);
+#define sudo_strtoid(_a, _b, _c, _d) sudo_strtoid_v1((_a), (_b), (_c), (_d))
+
+/* strtomode.c */
+__dso_public int sudo_strtomode_v1(const char *cp, const char **errstr);
+#define sudo_strtomode(_a, _b) sudo_strtomode_v1((_a), (_b))
+
+/* sudo_printf.c */
+extern int (*sudo_printf)(int msg_type, const char *fmt, ...);
+
+/* term.c */
+__dso_public bool sudo_term_cbreak_v1(int fd);
+#define sudo_term_cbreak(_a) sudo_term_cbreak_v1((_a))
+__dso_public bool sudo_term_copy_v1(int src, int dst);
+#define sudo_term_copy(_a, _b) sudo_term_copy_v1((_a), (_b))
+__dso_public bool sudo_term_noecho_v1(int fd);
+#define sudo_term_noecho(_a) sudo_term_noecho_v1((_a))
+__dso_public bool sudo_term_raw_v1(int fd, int isig);
+#define sudo_term_raw(_a, _b) sudo_term_raw_v1((_a), (_b))
+__dso_public bool sudo_term_restore_v1(int fd, bool flush);
+#define sudo_term_restore(_a, _b) sudo_term_restore_v1((_a), (_b))
+
+/* ttyname_dev.c */
+__dso_public char *sudo_ttyname_dev_v1(dev_t tdev, char *name, size_t namelen);
+#define sudo_ttyname_dev(_a, _b, _c) sudo_ttyname_dev_v1((_a), (_b), (_c))
+
+/* ttysize.c */
+__dso_public void sudo_get_ttysize_v1(int *rowp, int *colp);
+#define sudo_get_ttysize(_a, _b) sudo_get_ttysize_v1((_a), (_b))
+
+#endif /* SUDO_UTIL_H */