summaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--include/Makefile.in115
-rw-r--r--include/compat/charclass.h41
-rw-r--r--include/compat/endian.h80
-rw-r--r--include/compat/fnmatch.h34
-rw-r--r--include/compat/getaddrinfo.h83
-rw-r--r--include/compat/getopt.h83
-rw-r--r--include/compat/glob.h78
-rw-r--r--include/compat/nss_dbdefs.h110
-rw-r--r--include/compat/sha2.h100
-rw-r--r--include/compat/stdbool.h44
-rw-r--r--include/hostcheck.h37
-rw-r--r--include/intercept.pb-c.h369
-rw-r--r--include/log_server.pb-c.h790
-rw-r--r--include/protobuf-c/protobuf-c.h1110
-rw-r--r--include/sudo_compat.h566
-rw-r--r--include/sudo_conf.h97
-rw-r--r--include/sudo_debug.h435
-rw-r--r--include/sudo_digest.h47
-rw-r--r--include/sudo_dso.h57
-rw-r--r--include/sudo_event.h216
-rw-r--r--include/sudo_eventlog.h175
-rw-r--r--include/sudo_fatal.h211
-rw-r--r--include/sudo_gettext.h77
-rw-r--r--include/sudo_iolog.h158
-rw-r--r--include/sudo_json.h100
-rw-r--r--include/sudo_lbuf.h62
-rw-r--r--include/sudo_plugin.h288
-rw-r--r--include/sudo_queue.h823
-rw-r--r--include/sudo_rand.h57
-rw-r--r--include/sudo_ssl_compat.h46
-rw-r--r--include/sudo_util.h358
31 files changed, 6847 insertions, 0 deletions
diff --git a/include/Makefile.in b/include/Makefile.in
new file mode 100644
index 0000000..da6d08d
--- /dev/null
+++ b/include/Makefile.in
@@ -0,0 +1,115 @@
+#
+# SPDX-License-Identifier: ISC
+#
+# Copyright (c) 2011-2015, 2017-2023 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@
+abs_srcdir = @abs_srcdir@
+top_srcdir = @top_srcdir@
+abs_top_srcdir = @abs_top_srcdir@
+top_builddir = @top_builddir@
+abs_top_builddir = @abs_top_builddir@
+includedir = @includedir@
+scriptdir = $(top_srcdir)/scripts
+cross_compiling = @CROSS_COMPILING@
+
+# Our install program supports extra flags...
+INSTALL = $(SHELL) $(scriptdir)/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@
+adminconfdir = @adminconfdir@
+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:
+
+depend:
+
+Makefile: $(srcdir)/Makefile.in
+ cd $(top_builddir) && ./config.status --file include/Makefile
+
+.SUFFIXES: .h
+
+pre-install:
+
+install: install-includes
+
+install-dirs:
+ $(SHELL) $(scriptdir)/mkinstalldirs $(DESTDIR)$(includedir)
+
+install-binaries:
+
+install-doc:
+
+install-includes: install-dirs
+ $(INSTALL) $(INSTALL_OWNER) -m 0644 $(srcdir)/sudo_plugin.h $(DESTDIR)$(includedir)
+
+install-plugin:
+
+install-fuzzer:
+
+uninstall:
+ -rm -f $(DESTDIR)$(includedir)/sudo_plugin.h
+
+splint:
+
+cppcheck:
+
+pvs-log-files:
+
+pvs-studio:
+
+fuzz:
+
+check-fuzzer:
+
+check: check-fuzzer
+
+check-verbose: check
+
+clean:
+
+mostlyclean: clean
+
+distclean: clean
+ -rm -rf Makefile
+
+clobber: distclean
+
+realclean: distclean
+
+cleandir: distclean
+
+.PHONY: clean mostlyclean distclean cleandir clobber realclean
diff --git a/include/compat/charclass.h b/include/compat/charclass.h
new file mode 100644
index 0000000..77c8d7f
--- /dev/null
+++ b/include/compat/charclass.h
@@ -0,0 +1,41 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * 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..fe6ee83
--- /dev/null
+++ b/include/compat/endian.h
@@ -0,0 +1,80 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2013, 2022 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__) && defined(__ORDER_LITTLE_ENDIAN__) && \
+ (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+# define BYTE_ORDER LITTLE_ENDIAN
+# elif defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && \
+ (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+# define BYTE_ORDER BIG_ENDIAN
+# elif 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(__riscv) || \
+ (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..75cac1f
--- /dev/null
+++ b/include/compat/fnmatch.h
@@ -0,0 +1,34 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * 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 */
+
+sudo_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..c1f87ef
--- /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 system getaddrinfo implementation 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. */
+sudo_dso_public int sudo_getaddrinfo(const char *nodename, const char *servname,
+ const struct addrinfo *hints, struct addrinfo **res);
+sudo_dso_public void sudo_freeaddrinfo(struct addrinfo *ai);
+sudo_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..da99d34
--- /dev/null
+++ b/include/compat/getopt.h
@@ -0,0 +1,83 @@
+/* $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 $ */
+
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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;
+};
+
+sudo_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))
+
+sudo_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
+sudo_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..7c06a2e
--- /dev/null
+++ b/include/compat/glob.h
@@ -0,0 +1,78 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * 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 {
+ size_t gl_pathc; /* Count of total paths so far. */
+ size_t gl_matchc; /* Count of paths matching pattern. */
+ size_t 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. */
+
+sudo_dso_public int sudo_glob(const char *, int, int (*)(const char *, int), glob_t *);
+sudo_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..726beae
--- /dev/null
+++ b/include/compat/nss_dbdefs.h
@@ -0,0 +1,110 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * 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_TRYAGAIN
+} 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 *instr, int instr_len, void *ent, char *buffer, int buflen);
+ nss_status_t (*process_cstr)(const char *instr, int instr_len, struct nss_groupsbymem *);
+ int numgids;
+};
+
+typedef struct {
+ void *result; /* group struct to fill in. */
+ char *buffer; /* string buffer for above */
+ int buflen; /* string buffer size */
+} nss_XbyY_buf_t;
+
+struct nss_db_state;
+typedef struct {
+ struct nss_db_state *s;
+#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 search_fnum, void *search_args);
+extern nss_XbyY_buf_t *_nss_XbyY_buf_alloc(int struct_size, int buffer_size);
+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..2908696
--- /dev/null
+++ b/include/compat/sha2.h
@@ -0,0 +1,100 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * 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;
+
+sudo_dso_public void sudo_SHA224Init(SHA2_CTX *ctx);
+sudo_dso_public void sudo_SHA224Pad(SHA2_CTX *ctx);
+sudo_dso_public void sudo_SHA224Transform(uint32_t state[8], const uint8_t buffer[SHA224_BLOCK_LENGTH]);
+sudo_dso_public void sudo_SHA224Update(SHA2_CTX *ctx, const uint8_t *data, size_t len);
+sudo_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
+
+sudo_dso_public void sudo_SHA256Init(SHA2_CTX *ctx);
+sudo_dso_public void sudo_SHA256Pad(SHA2_CTX *ctx);
+sudo_dso_public void sudo_SHA256Transform(uint32_t state[8], const uint8_t buffer[SHA256_BLOCK_LENGTH]);
+sudo_dso_public void sudo_SHA256Update(SHA2_CTX *ctx, const uint8_t *data, size_t len);
+sudo_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
+
+sudo_dso_public void sudo_SHA384Init(SHA2_CTX *ctx);
+sudo_dso_public void sudo_SHA384Pad(SHA2_CTX *ctx);
+sudo_dso_public void sudo_SHA384Transform(uint64_t state[8], const uint8_t buffer[SHA384_BLOCK_LENGTH]);
+sudo_dso_public void sudo_SHA384Update(SHA2_CTX *ctx, const uint8_t *data, size_t len);
+sudo_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
+
+sudo_dso_public void sudo_SHA512Init(SHA2_CTX *ctx);
+sudo_dso_public void sudo_SHA512Pad(SHA2_CTX *ctx);
+sudo_dso_public void sudo_SHA512Transform(uint64_t state[8], const uint8_t buffer[SHA512_BLOCK_LENGTH]);
+sudo_dso_public void sudo_SHA512Update(SHA2_CTX *ctx, const uint8_t *data, size_t len);
+sudo_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/hostcheck.h b/include/hostcheck.h
new file mode 100644
index 0000000..5c05d36
--- /dev/null
+++ b/include/hostcheck.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Laszlo Orban <laszlo.orban@oneidentity.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef SUDO_HOSTCHECK_H
+#define SUDO_HOSTCHECK_H
+
+#if defined(HAVE_OPENSSL)
+
+# include <openssl/x509v3.h>
+
+typedef enum {
+ MatchFound,
+ MatchNotFound,
+ NoSANPresent,
+ MalformedCertificate,
+ Error
+} HostnameValidationResult;
+
+HostnameValidationResult validate_hostname(const X509 *cert,
+ const char *hostname, const char *ipaddr, int resolve);
+
+#endif /* HAVE_OPENSSL */
+
+#endif /* SUDO_HOSTCHECK_H */
diff --git a/include/intercept.pb-c.h b/include/intercept.pb-c.h
new file mode 100644
index 0000000..2ea1683
--- /dev/null
+++ b/include/intercept.pb-c.h
@@ -0,0 +1,369 @@
+/* Generated by the protocol buffer compiler. DO NOT EDIT! */
+/* Generated from: intercept.proto */
+
+#ifndef PROTOBUF_C_intercept_2eproto__INCLUDED
+#define PROTOBUF_C_intercept_2eproto__INCLUDED
+
+#include <protobuf-c/protobuf-c.h>
+
+PROTOBUF_C__BEGIN_DECLS
+
+#if PROTOBUF_C_VERSION_NUMBER < 1003000
+# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers.
+#elif 1004000 < PROTOBUF_C_MIN_COMPILER_VERSION
+# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c.
+#endif
+
+
+typedef struct InterceptRequest InterceptRequest;
+typedef struct InterceptHello InterceptHello;
+typedef struct HelloResponse HelloResponse;
+typedef struct PolicyCheckRequest PolicyCheckRequest;
+typedef struct PolicyAcceptMessage PolicyAcceptMessage;
+typedef struct PolicyRejectMessage PolicyRejectMessage;
+typedef struct PolicyErrorMessage PolicyErrorMessage;
+typedef struct InterceptResponse InterceptResponse;
+
+
+/* --- enums --- */
+
+
+/* --- messages --- */
+
+typedef enum {
+ INTERCEPT_REQUEST__TYPE__NOT_SET = 0,
+ INTERCEPT_REQUEST__TYPE_POLICY_CHECK_REQ = 1,
+ INTERCEPT_REQUEST__TYPE_HELLO = 2
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(INTERCEPT_REQUEST__TYPE__CASE)
+} InterceptRequest__TypeCase;
+
+/*
+ * Intercept message from sudo_intercept.so. Messages on the
+ * wire are prefixed with a 32-bit size in network byte order.
+ */
+struct InterceptRequest
+{
+ ProtobufCMessage base;
+ InterceptRequest__TypeCase type_case;
+ union {
+ PolicyCheckRequest *policy_check_req;
+ InterceptHello *hello;
+ } u;
+};
+#define INTERCEPT_REQUEST__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&intercept_request__descriptor) \
+ , INTERCEPT_REQUEST__TYPE__NOT_SET, {0} }
+
+
+/*
+ * Hello message from sudo_intercept.so to main sudo process.
+ * Sudo sends back the token and localhost port number.
+ */
+struct InterceptHello
+{
+ ProtobufCMessage base;
+ int32_t pid;
+};
+#define INTERCEPT_HELLO__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&intercept_hello__descriptor) \
+ , 0 }
+
+
+/*
+ * Sudo response to an InterceptHello from sudo_intercept.so.
+ * The client uses the port number and token to connect back to sudo.
+ * If log_only is set there is no InterceptResponse to a PolicyCheckRequest.
+ */
+struct HelloResponse
+{
+ ProtobufCMessage base;
+ uint64_t token_lo;
+ uint64_t token_hi;
+ int32_t portno;
+ protobuf_c_boolean log_only;
+};
+#define HELLO_RESPONSE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&hello_response__descriptor) \
+ , 0, 0, 0, 0 }
+
+
+/*
+ * Policy check request from sudo_intercept.so.
+ * Note that the plugin API only currently supports passing
+ * the new environment in to the open() function.
+ */
+struct PolicyCheckRequest
+{
+ ProtobufCMessage base;
+ char *command;
+ char *cwd;
+ size_t n_argv;
+ char **argv;
+ size_t n_envp;
+ char **envp;
+ int32_t intercept_fd;
+};
+#define POLICY_CHECK_REQUEST__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&policy_check_request__descriptor) \
+ , (char *)protobuf_c_empty_string, (char *)protobuf_c_empty_string, 0,NULL, 0,NULL, 0 }
+
+
+struct PolicyAcceptMessage
+{
+ ProtobufCMessage base;
+ char *run_command;
+ size_t n_run_argv;
+ char **run_argv;
+ size_t n_run_envp;
+ char **run_envp;
+};
+#define POLICY_ACCEPT_MESSAGE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&policy_accept_message__descriptor) \
+ , (char *)protobuf_c_empty_string, 0,NULL, 0,NULL }
+
+
+struct PolicyRejectMessage
+{
+ ProtobufCMessage base;
+ char *reject_message;
+};
+#define POLICY_REJECT_MESSAGE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&policy_reject_message__descriptor) \
+ , (char *)protobuf_c_empty_string }
+
+
+struct PolicyErrorMessage
+{
+ ProtobufCMessage base;
+ char *error_message;
+};
+#define POLICY_ERROR_MESSAGE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&policy_error_message__descriptor) \
+ , (char *)protobuf_c_empty_string }
+
+
+typedef enum {
+ INTERCEPT_RESPONSE__TYPE__NOT_SET = 0,
+ INTERCEPT_RESPONSE__TYPE_HELLO_RESP = 1,
+ INTERCEPT_RESPONSE__TYPE_ACCEPT_MSG = 2,
+ INTERCEPT_RESPONSE__TYPE_REJECT_MSG = 3,
+ INTERCEPT_RESPONSE__TYPE_ERROR_MSG = 4
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(INTERCEPT_RESPONSE__TYPE__CASE)
+} InterceptResponse__TypeCase;
+
+/*
+ * Response sent back to sudo_intercept.so.
+ */
+struct InterceptResponse
+{
+ ProtobufCMessage base;
+ InterceptResponse__TypeCase type_case;
+ union {
+ HelloResponse *hello_resp;
+ PolicyAcceptMessage *accept_msg;
+ PolicyRejectMessage *reject_msg;
+ PolicyErrorMessage *error_msg;
+ } u;
+};
+#define INTERCEPT_RESPONSE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&intercept_response__descriptor) \
+ , INTERCEPT_RESPONSE__TYPE__NOT_SET, {0} }
+
+
+/* InterceptRequest methods */
+void intercept_request__init
+ (InterceptRequest *message);
+size_t intercept_request__get_packed_size
+ (const InterceptRequest *message);
+size_t intercept_request__pack
+ (const InterceptRequest *message,
+ uint8_t *out);
+size_t intercept_request__pack_to_buffer
+ (const InterceptRequest *message,
+ ProtobufCBuffer *buffer);
+InterceptRequest *
+ intercept_request__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void intercept_request__free_unpacked
+ (InterceptRequest *message,
+ ProtobufCAllocator *allocator);
+/* InterceptHello methods */
+void intercept_hello__init
+ (InterceptHello *message);
+size_t intercept_hello__get_packed_size
+ (const InterceptHello *message);
+size_t intercept_hello__pack
+ (const InterceptHello *message,
+ uint8_t *out);
+size_t intercept_hello__pack_to_buffer
+ (const InterceptHello *message,
+ ProtobufCBuffer *buffer);
+InterceptHello *
+ intercept_hello__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void intercept_hello__free_unpacked
+ (InterceptHello *message,
+ ProtobufCAllocator *allocator);
+/* HelloResponse methods */
+void hello_response__init
+ (HelloResponse *message);
+size_t hello_response__get_packed_size
+ (const HelloResponse *message);
+size_t hello_response__pack
+ (const HelloResponse *message,
+ uint8_t *out);
+size_t hello_response__pack_to_buffer
+ (const HelloResponse *message,
+ ProtobufCBuffer *buffer);
+HelloResponse *
+ hello_response__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void hello_response__free_unpacked
+ (HelloResponse *message,
+ ProtobufCAllocator *allocator);
+/* PolicyCheckRequest methods */
+void policy_check_request__init
+ (PolicyCheckRequest *message);
+size_t policy_check_request__get_packed_size
+ (const PolicyCheckRequest *message);
+size_t policy_check_request__pack
+ (const PolicyCheckRequest *message,
+ uint8_t *out);
+size_t policy_check_request__pack_to_buffer
+ (const PolicyCheckRequest *message,
+ ProtobufCBuffer *buffer);
+PolicyCheckRequest *
+ policy_check_request__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void policy_check_request__free_unpacked
+ (PolicyCheckRequest *message,
+ ProtobufCAllocator *allocator);
+/* PolicyAcceptMessage methods */
+void policy_accept_message__init
+ (PolicyAcceptMessage *message);
+size_t policy_accept_message__get_packed_size
+ (const PolicyAcceptMessage *message);
+size_t policy_accept_message__pack
+ (const PolicyAcceptMessage *message,
+ uint8_t *out);
+size_t policy_accept_message__pack_to_buffer
+ (const PolicyAcceptMessage *message,
+ ProtobufCBuffer *buffer);
+PolicyAcceptMessage *
+ policy_accept_message__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void policy_accept_message__free_unpacked
+ (PolicyAcceptMessage *message,
+ ProtobufCAllocator *allocator);
+/* PolicyRejectMessage methods */
+void policy_reject_message__init
+ (PolicyRejectMessage *message);
+size_t policy_reject_message__get_packed_size
+ (const PolicyRejectMessage *message);
+size_t policy_reject_message__pack
+ (const PolicyRejectMessage *message,
+ uint8_t *out);
+size_t policy_reject_message__pack_to_buffer
+ (const PolicyRejectMessage *message,
+ ProtobufCBuffer *buffer);
+PolicyRejectMessage *
+ policy_reject_message__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void policy_reject_message__free_unpacked
+ (PolicyRejectMessage *message,
+ ProtobufCAllocator *allocator);
+/* PolicyErrorMessage methods */
+void policy_error_message__init
+ (PolicyErrorMessage *message);
+size_t policy_error_message__get_packed_size
+ (const PolicyErrorMessage *message);
+size_t policy_error_message__pack
+ (const PolicyErrorMessage *message,
+ uint8_t *out);
+size_t policy_error_message__pack_to_buffer
+ (const PolicyErrorMessage *message,
+ ProtobufCBuffer *buffer);
+PolicyErrorMessage *
+ policy_error_message__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void policy_error_message__free_unpacked
+ (PolicyErrorMessage *message,
+ ProtobufCAllocator *allocator);
+/* InterceptResponse methods */
+void intercept_response__init
+ (InterceptResponse *message);
+size_t intercept_response__get_packed_size
+ (const InterceptResponse *message);
+size_t intercept_response__pack
+ (const InterceptResponse *message,
+ uint8_t *out);
+size_t intercept_response__pack_to_buffer
+ (const InterceptResponse *message,
+ ProtobufCBuffer *buffer);
+InterceptResponse *
+ intercept_response__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void intercept_response__free_unpacked
+ (InterceptResponse *message,
+ ProtobufCAllocator *allocator);
+/* --- per-message closures --- */
+
+typedef void (*InterceptRequest_Closure)
+ (const InterceptRequest *message,
+ void *closure_data);
+typedef void (*InterceptHello_Closure)
+ (const InterceptHello *message,
+ void *closure_data);
+typedef void (*HelloResponse_Closure)
+ (const HelloResponse *message,
+ void *closure_data);
+typedef void (*PolicyCheckRequest_Closure)
+ (const PolicyCheckRequest *message,
+ void *closure_data);
+typedef void (*PolicyAcceptMessage_Closure)
+ (const PolicyAcceptMessage *message,
+ void *closure_data);
+typedef void (*PolicyRejectMessage_Closure)
+ (const PolicyRejectMessage *message,
+ void *closure_data);
+typedef void (*PolicyErrorMessage_Closure)
+ (const PolicyErrorMessage *message,
+ void *closure_data);
+typedef void (*InterceptResponse_Closure)
+ (const InterceptResponse *message,
+ void *closure_data);
+
+/* --- services --- */
+
+
+/* --- descriptors --- */
+
+extern const ProtobufCMessageDescriptor intercept_request__descriptor;
+extern const ProtobufCMessageDescriptor intercept_hello__descriptor;
+extern const ProtobufCMessageDescriptor hello_response__descriptor;
+extern const ProtobufCMessageDescriptor policy_check_request__descriptor;
+extern const ProtobufCMessageDescriptor policy_accept_message__descriptor;
+extern const ProtobufCMessageDescriptor policy_reject_message__descriptor;
+extern const ProtobufCMessageDescriptor policy_error_message__descriptor;
+extern const ProtobufCMessageDescriptor intercept_response__descriptor;
+
+PROTOBUF_C__END_DECLS
+
+
+#endif /* PROTOBUF_C_intercept_2eproto__INCLUDED */
diff --git a/include/log_server.pb-c.h b/include/log_server.pb-c.h
new file mode 100644
index 0000000..a7238c3
--- /dev/null
+++ b/include/log_server.pb-c.h
@@ -0,0 +1,790 @@
+/* Generated by the protocol buffer compiler. DO NOT EDIT! */
+/* Generated from: log_server.proto */
+
+#ifndef PROTOBUF_C_log_5fserver_2eproto__INCLUDED
+#define PROTOBUF_C_log_5fserver_2eproto__INCLUDED
+
+#include <protobuf-c/protobuf-c.h>
+
+PROTOBUF_C__BEGIN_DECLS
+
+#if PROTOBUF_C_VERSION_NUMBER < 1003000
+# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers.
+#elif 1004000 < PROTOBUF_C_MIN_COMPILER_VERSION
+# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c.
+#endif
+
+
+typedef struct ClientMessage ClientMessage;
+typedef struct TimeSpec TimeSpec;
+typedef struct IoBuffer IoBuffer;
+typedef struct InfoMessage InfoMessage;
+typedef struct InfoMessage__StringList InfoMessage__StringList;
+typedef struct InfoMessage__NumberList InfoMessage__NumberList;
+typedef struct AcceptMessage AcceptMessage;
+typedef struct RejectMessage RejectMessage;
+typedef struct ExitMessage ExitMessage;
+typedef struct AlertMessage AlertMessage;
+typedef struct RestartMessage RestartMessage;
+typedef struct ChangeWindowSize ChangeWindowSize;
+typedef struct CommandSuspend CommandSuspend;
+typedef struct ClientHello ClientHello;
+typedef struct ServerMessage ServerMessage;
+typedef struct ServerHello ServerHello;
+
+
+/* --- enums --- */
+
+
+/* --- messages --- */
+
+typedef enum {
+ CLIENT_MESSAGE__TYPE__NOT_SET = 0,
+ CLIENT_MESSAGE__TYPE_ACCEPT_MSG = 1,
+ CLIENT_MESSAGE__TYPE_REJECT_MSG = 2,
+ CLIENT_MESSAGE__TYPE_EXIT_MSG = 3,
+ CLIENT_MESSAGE__TYPE_RESTART_MSG = 4,
+ CLIENT_MESSAGE__TYPE_ALERT_MSG = 5,
+ CLIENT_MESSAGE__TYPE_TTYIN_BUF = 6,
+ CLIENT_MESSAGE__TYPE_TTYOUT_BUF = 7,
+ CLIENT_MESSAGE__TYPE_STDIN_BUF = 8,
+ CLIENT_MESSAGE__TYPE_STDOUT_BUF = 9,
+ CLIENT_MESSAGE__TYPE_STDERR_BUF = 10,
+ CLIENT_MESSAGE__TYPE_WINSIZE_EVENT = 11,
+ CLIENT_MESSAGE__TYPE_SUSPEND_EVENT = 12,
+ CLIENT_MESSAGE__TYPE_HELLO_MSG = 13
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(CLIENT_MESSAGE__TYPE__CASE)
+} ClientMessage__TypeCase;
+
+/*
+ * Client message to the server. Messages on the wire are
+ * prefixed with a 32-bit size in network byte order.
+ */
+struct ClientMessage
+{
+ ProtobufCMessage base;
+ ClientMessage__TypeCase type_case;
+ union {
+ AcceptMessage *accept_msg;
+ RejectMessage *reject_msg;
+ ExitMessage *exit_msg;
+ RestartMessage *restart_msg;
+ AlertMessage *alert_msg;
+ IoBuffer *ttyin_buf;
+ IoBuffer *ttyout_buf;
+ IoBuffer *stdin_buf;
+ IoBuffer *stdout_buf;
+ IoBuffer *stderr_buf;
+ ChangeWindowSize *winsize_event;
+ CommandSuspend *suspend_event;
+ ClientHello *hello_msg;
+ } u;
+};
+#define CLIENT_MESSAGE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&client_message__descriptor) \
+ , CLIENT_MESSAGE__TYPE__NOT_SET, {0} }
+
+
+/*
+ * Equivalent of POSIX struct timespec
+ */
+struct TimeSpec
+{
+ ProtobufCMessage base;
+ /*
+ * seconds
+ */
+ int64_t tv_sec;
+ /*
+ * nanoseconds
+ */
+ int32_t tv_nsec;
+};
+#define TIME_SPEC__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&time_spec__descriptor) \
+ , 0, 0 }
+
+
+/*
+ * I/O buffer with keystroke data
+ */
+struct IoBuffer
+{
+ ProtobufCMessage base;
+ /*
+ * elapsed time since last record
+ */
+ TimeSpec *delay;
+ /*
+ * keystroke data
+ */
+ ProtobufCBinaryData data;
+};
+#define IO_BUFFER__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&io_buffer__descriptor) \
+ , NULL, {0,NULL} }
+
+
+struct InfoMessage__StringList
+{
+ ProtobufCMessage base;
+ size_t n_strings;
+ char **strings;
+};
+#define INFO_MESSAGE__STRING_LIST__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&info_message__string_list__descriptor) \
+ , 0,NULL }
+
+
+struct InfoMessage__NumberList
+{
+ ProtobufCMessage base;
+ size_t n_numbers;
+ int64_t *numbers;
+};
+#define INFO_MESSAGE__NUMBER_LIST__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&info_message__number_list__descriptor) \
+ , 0,NULL }
+
+
+typedef enum {
+ INFO_MESSAGE__VALUE__NOT_SET = 0,
+ INFO_MESSAGE__VALUE_NUMVAL = 2,
+ INFO_MESSAGE__VALUE_STRVAL = 3,
+ INFO_MESSAGE__VALUE_STRLISTVAL = 4,
+ INFO_MESSAGE__VALUE_NUMLISTVAL = 5
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(INFO_MESSAGE__VALUE__CASE)
+} InfoMessage__ValueCase;
+
+/*
+ * Key/value pairs, like Privilege Manager struct info.
+ * The value may be a number, a string, or a list of strings.
+ */
+struct InfoMessage
+{
+ ProtobufCMessage base;
+ char *key;
+ InfoMessage__ValueCase value_case;
+ union {
+ int64_t numval;
+ char *strval;
+ InfoMessage__StringList *strlistval;
+ InfoMessage__NumberList *numlistval;
+ } u;
+};
+#define INFO_MESSAGE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&info_message__descriptor) \
+ , (char *)protobuf_c_empty_string, INFO_MESSAGE__VALUE__NOT_SET, {0} }
+
+
+/*
+ * Event log data for command accepted by the policy.
+ */
+struct AcceptMessage
+{
+ ProtobufCMessage base;
+ /*
+ * when command was submitted
+ */
+ TimeSpec *submit_time;
+ /*
+ * key,value event log data
+ */
+ size_t n_info_msgs;
+ InfoMessage **info_msgs;
+ /*
+ * true if I/O logging enabled
+ */
+ protobuf_c_boolean expect_iobufs;
+};
+#define ACCEPT_MESSAGE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&accept_message__descriptor) \
+ , NULL, 0,NULL, 0 }
+
+
+/*
+ * Event log data for command rejected by the policy.
+ */
+struct RejectMessage
+{
+ ProtobufCMessage base;
+ /*
+ * when command was submitted
+ */
+ TimeSpec *submit_time;
+ /*
+ * reason command was rejected
+ */
+ char *reason;
+ /*
+ * key,value event log data
+ */
+ size_t n_info_msgs;
+ InfoMessage **info_msgs;
+};
+#define REJECT_MESSAGE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&reject_message__descriptor) \
+ , NULL, (char *)protobuf_c_empty_string, 0,NULL }
+
+
+/*
+ * Might revisit runtime and use end_time instead
+ */
+struct ExitMessage
+{
+ ProtobufCMessage base;
+ /*
+ * total elapsed run time
+ */
+ TimeSpec *run_time;
+ /*
+ * 0-255
+ */
+ int32_t exit_value;
+ /*
+ * true if command dumped core
+ */
+ protobuf_c_boolean dumped_core;
+ /*
+ * signal name if killed by signal
+ */
+ char *signal;
+ /*
+ * if killed due to other error
+ */
+ char *error;
+};
+#define EXIT_MESSAGE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&exit_message__descriptor) \
+ , NULL, 0, 0, (char *)protobuf_c_empty_string, (char *)protobuf_c_empty_string }
+
+
+/*
+ * Alert message, policy module-specific.
+ */
+struct AlertMessage
+{
+ ProtobufCMessage base;
+ /*
+ * time alert message occurred
+ */
+ TimeSpec *alert_time;
+ /*
+ * policy alert error string
+ */
+ char *reason;
+ /*
+ * optional key,value event log data
+ */
+ size_t n_info_msgs;
+ InfoMessage **info_msgs;
+};
+#define ALERT_MESSAGE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&alert_message__descriptor) \
+ , NULL, (char *)protobuf_c_empty_string, 0,NULL }
+
+
+/*
+ * Used to restart an existing I/O log on the server.
+ */
+struct RestartMessage
+{
+ ProtobufCMessage base;
+ /*
+ * ID of log being restarted
+ */
+ char *log_id;
+ /*
+ * resume point (elapsed time)
+ */
+ TimeSpec *resume_point;
+};
+#define RESTART_MESSAGE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&restart_message__descriptor) \
+ , (char *)protobuf_c_empty_string, NULL }
+
+
+/*
+ * Window size change event.
+ */
+struct ChangeWindowSize
+{
+ ProtobufCMessage base;
+ /*
+ * elapsed time since last record
+ */
+ TimeSpec *delay;
+ /*
+ * new number of rows
+ */
+ int32_t rows;
+ /*
+ * new number of columns
+ */
+ int32_t cols;
+};
+#define CHANGE_WINDOW_SIZE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&change_window_size__descriptor) \
+ , NULL, 0, 0 }
+
+
+/*
+ * Command suspend/resume event.
+ */
+struct CommandSuspend
+{
+ ProtobufCMessage base;
+ /*
+ * elapsed time since last record
+ */
+ TimeSpec *delay;
+ /*
+ * signal that caused suspend/resume
+ */
+ char *signal;
+};
+#define COMMAND_SUSPEND__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&command_suspend__descriptor) \
+ , NULL, (char *)protobuf_c_empty_string }
+
+
+/*
+ * Hello message from client when connecting to server.
+ */
+struct ClientHello
+{
+ ProtobufCMessage base;
+ /*
+ * free-form client description
+ */
+ char *client_id;
+};
+#define CLIENT_HELLO__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&client_hello__descriptor) \
+ , (char *)protobuf_c_empty_string }
+
+
+typedef enum {
+ SERVER_MESSAGE__TYPE__NOT_SET = 0,
+ SERVER_MESSAGE__TYPE_HELLO = 1,
+ SERVER_MESSAGE__TYPE_COMMIT_POINT = 2,
+ SERVER_MESSAGE__TYPE_LOG_ID = 3,
+ SERVER_MESSAGE__TYPE_ERROR = 4,
+ SERVER_MESSAGE__TYPE_ABORT = 5
+ PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(SERVER_MESSAGE__TYPE__CASE)
+} ServerMessage__TypeCase;
+
+/*
+ * Server messages to the client. Messages on the wire are
+ * prefixed with a 32-bit size in network byte order.
+ */
+struct ServerMessage
+{
+ ProtobufCMessage base;
+ ServerMessage__TypeCase type_case;
+ union {
+ /*
+ * server hello message
+ */
+ ServerHello *hello;
+ /*
+ * cumulative time of records stored
+ */
+ TimeSpec *commit_point;
+ /*
+ * ID of server-side I/O log
+ */
+ char *log_id;
+ /*
+ * error message from server
+ */
+ char *error;
+ /*
+ * abort message, kill command
+ */
+ char *abort;
+ } u;
+};
+#define SERVER_MESSAGE__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&server_message__descriptor) \
+ , SERVER_MESSAGE__TYPE__NOT_SET, {0} }
+
+
+/*
+ * Hello message from server when client connects.
+ */
+struct ServerHello
+{
+ ProtobufCMessage base;
+ /*
+ * free-form server description
+ */
+ char *server_id;
+ /*
+ * optional redirect if busy
+ */
+ char *redirect;
+ /*
+ * optional list of known servers
+ */
+ size_t n_servers;
+ char **servers;
+ /*
+ * flag: server supports sub-commands
+ */
+ protobuf_c_boolean subcommands;
+};
+#define SERVER_HELLO__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&server_hello__descriptor) \
+ , (char *)protobuf_c_empty_string, (char *)protobuf_c_empty_string, 0,NULL, 0 }
+
+
+/* ClientMessage methods */
+void client_message__init
+ (ClientMessage *message);
+size_t client_message__get_packed_size
+ (const ClientMessage *message);
+size_t client_message__pack
+ (const ClientMessage *message,
+ uint8_t *out);
+size_t client_message__pack_to_buffer
+ (const ClientMessage *message,
+ ProtobufCBuffer *buffer);
+ClientMessage *
+ client_message__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void client_message__free_unpacked
+ (ClientMessage *message,
+ ProtobufCAllocator *allocator);
+/* TimeSpec methods */
+void time_spec__init
+ (TimeSpec *message);
+size_t time_spec__get_packed_size
+ (const TimeSpec *message);
+size_t time_spec__pack
+ (const TimeSpec *message,
+ uint8_t *out);
+size_t time_spec__pack_to_buffer
+ (const TimeSpec *message,
+ ProtobufCBuffer *buffer);
+TimeSpec *
+ time_spec__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void time_spec__free_unpacked
+ (TimeSpec *message,
+ ProtobufCAllocator *allocator);
+/* IoBuffer methods */
+void io_buffer__init
+ (IoBuffer *message);
+size_t io_buffer__get_packed_size
+ (const IoBuffer *message);
+size_t io_buffer__pack
+ (const IoBuffer *message,
+ uint8_t *out);
+size_t io_buffer__pack_to_buffer
+ (const IoBuffer *message,
+ ProtobufCBuffer *buffer);
+IoBuffer *
+ io_buffer__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void io_buffer__free_unpacked
+ (IoBuffer *message,
+ ProtobufCAllocator *allocator);
+/* InfoMessage__StringList methods */
+void info_message__string_list__init
+ (InfoMessage__StringList *message);
+/* InfoMessage__NumberList methods */
+void info_message__number_list__init
+ (InfoMessage__NumberList *message);
+/* InfoMessage methods */
+void info_message__init
+ (InfoMessage *message);
+size_t info_message__get_packed_size
+ (const InfoMessage *message);
+size_t info_message__pack
+ (const InfoMessage *message,
+ uint8_t *out);
+size_t info_message__pack_to_buffer
+ (const InfoMessage *message,
+ ProtobufCBuffer *buffer);
+InfoMessage *
+ info_message__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void info_message__free_unpacked
+ (InfoMessage *message,
+ ProtobufCAllocator *allocator);
+/* AcceptMessage methods */
+void accept_message__init
+ (AcceptMessage *message);
+size_t accept_message__get_packed_size
+ (const AcceptMessage *message);
+size_t accept_message__pack
+ (const AcceptMessage *message,
+ uint8_t *out);
+size_t accept_message__pack_to_buffer
+ (const AcceptMessage *message,
+ ProtobufCBuffer *buffer);
+AcceptMessage *
+ accept_message__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void accept_message__free_unpacked
+ (AcceptMessage *message,
+ ProtobufCAllocator *allocator);
+/* RejectMessage methods */
+void reject_message__init
+ (RejectMessage *message);
+size_t reject_message__get_packed_size
+ (const RejectMessage *message);
+size_t reject_message__pack
+ (const RejectMessage *message,
+ uint8_t *out);
+size_t reject_message__pack_to_buffer
+ (const RejectMessage *message,
+ ProtobufCBuffer *buffer);
+RejectMessage *
+ reject_message__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void reject_message__free_unpacked
+ (RejectMessage *message,
+ ProtobufCAllocator *allocator);
+/* ExitMessage methods */
+void exit_message__init
+ (ExitMessage *message);
+size_t exit_message__get_packed_size
+ (const ExitMessage *message);
+size_t exit_message__pack
+ (const ExitMessage *message,
+ uint8_t *out);
+size_t exit_message__pack_to_buffer
+ (const ExitMessage *message,
+ ProtobufCBuffer *buffer);
+ExitMessage *
+ exit_message__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void exit_message__free_unpacked
+ (ExitMessage *message,
+ ProtobufCAllocator *allocator);
+/* AlertMessage methods */
+void alert_message__init
+ (AlertMessage *message);
+size_t alert_message__get_packed_size
+ (const AlertMessage *message);
+size_t alert_message__pack
+ (const AlertMessage *message,
+ uint8_t *out);
+size_t alert_message__pack_to_buffer
+ (const AlertMessage *message,
+ ProtobufCBuffer *buffer);
+AlertMessage *
+ alert_message__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void alert_message__free_unpacked
+ (AlertMessage *message,
+ ProtobufCAllocator *allocator);
+/* RestartMessage methods */
+void restart_message__init
+ (RestartMessage *message);
+size_t restart_message__get_packed_size
+ (const RestartMessage *message);
+size_t restart_message__pack
+ (const RestartMessage *message,
+ uint8_t *out);
+size_t restart_message__pack_to_buffer
+ (const RestartMessage *message,
+ ProtobufCBuffer *buffer);
+RestartMessage *
+ restart_message__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void restart_message__free_unpacked
+ (RestartMessage *message,
+ ProtobufCAllocator *allocator);
+/* ChangeWindowSize methods */
+void change_window_size__init
+ (ChangeWindowSize *message);
+size_t change_window_size__get_packed_size
+ (const ChangeWindowSize *message);
+size_t change_window_size__pack
+ (const ChangeWindowSize *message,
+ uint8_t *out);
+size_t change_window_size__pack_to_buffer
+ (const ChangeWindowSize *message,
+ ProtobufCBuffer *buffer);
+ChangeWindowSize *
+ change_window_size__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void change_window_size__free_unpacked
+ (ChangeWindowSize *message,
+ ProtobufCAllocator *allocator);
+/* CommandSuspend methods */
+void command_suspend__init
+ (CommandSuspend *message);
+size_t command_suspend__get_packed_size
+ (const CommandSuspend *message);
+size_t command_suspend__pack
+ (const CommandSuspend *message,
+ uint8_t *out);
+size_t command_suspend__pack_to_buffer
+ (const CommandSuspend *message,
+ ProtobufCBuffer *buffer);
+CommandSuspend *
+ command_suspend__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void command_suspend__free_unpacked
+ (CommandSuspend *message,
+ ProtobufCAllocator *allocator);
+/* ClientHello methods */
+void client_hello__init
+ (ClientHello *message);
+size_t client_hello__get_packed_size
+ (const ClientHello *message);
+size_t client_hello__pack
+ (const ClientHello *message,
+ uint8_t *out);
+size_t client_hello__pack_to_buffer
+ (const ClientHello *message,
+ ProtobufCBuffer *buffer);
+ClientHello *
+ client_hello__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void client_hello__free_unpacked
+ (ClientHello *message,
+ ProtobufCAllocator *allocator);
+/* ServerMessage methods */
+void server_message__init
+ (ServerMessage *message);
+size_t server_message__get_packed_size
+ (const ServerMessage *message);
+size_t server_message__pack
+ (const ServerMessage *message,
+ uint8_t *out);
+size_t server_message__pack_to_buffer
+ (const ServerMessage *message,
+ ProtobufCBuffer *buffer);
+ServerMessage *
+ server_message__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void server_message__free_unpacked
+ (ServerMessage *message,
+ ProtobufCAllocator *allocator);
+/* ServerHello methods */
+void server_hello__init
+ (ServerHello *message);
+size_t server_hello__get_packed_size
+ (const ServerHello *message);
+size_t server_hello__pack
+ (const ServerHello *message,
+ uint8_t *out);
+size_t server_hello__pack_to_buffer
+ (const ServerHello *message,
+ ProtobufCBuffer *buffer);
+ServerHello *
+ server_hello__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+void server_hello__free_unpacked
+ (ServerHello *message,
+ ProtobufCAllocator *allocator);
+/* --- per-message closures --- */
+
+typedef void (*ClientMessage_Closure)
+ (const ClientMessage *message,
+ void *closure_data);
+typedef void (*TimeSpec_Closure)
+ (const TimeSpec *message,
+ void *closure_data);
+typedef void (*IoBuffer_Closure)
+ (const IoBuffer *message,
+ void *closure_data);
+typedef void (*InfoMessage__StringList_Closure)
+ (const InfoMessage__StringList *message,
+ void *closure_data);
+typedef void (*InfoMessage__NumberList_Closure)
+ (const InfoMessage__NumberList *message,
+ void *closure_data);
+typedef void (*InfoMessage_Closure)
+ (const InfoMessage *message,
+ void *closure_data);
+typedef void (*AcceptMessage_Closure)
+ (const AcceptMessage *message,
+ void *closure_data);
+typedef void (*RejectMessage_Closure)
+ (const RejectMessage *message,
+ void *closure_data);
+typedef void (*ExitMessage_Closure)
+ (const ExitMessage *message,
+ void *closure_data);
+typedef void (*AlertMessage_Closure)
+ (const AlertMessage *message,
+ void *closure_data);
+typedef void (*RestartMessage_Closure)
+ (const RestartMessage *message,
+ void *closure_data);
+typedef void (*ChangeWindowSize_Closure)
+ (const ChangeWindowSize *message,
+ void *closure_data);
+typedef void (*CommandSuspend_Closure)
+ (const CommandSuspend *message,
+ void *closure_data);
+typedef void (*ClientHello_Closure)
+ (const ClientHello *message,
+ void *closure_data);
+typedef void (*ServerMessage_Closure)
+ (const ServerMessage *message,
+ void *closure_data);
+typedef void (*ServerHello_Closure)
+ (const ServerHello *message,
+ void *closure_data);
+
+/* --- services --- */
+
+
+/* --- descriptors --- */
+
+extern const ProtobufCMessageDescriptor client_message__descriptor;
+extern const ProtobufCMessageDescriptor time_spec__descriptor;
+extern const ProtobufCMessageDescriptor io_buffer__descriptor;
+extern const ProtobufCMessageDescriptor info_message__descriptor;
+extern const ProtobufCMessageDescriptor info_message__string_list__descriptor;
+extern const ProtobufCMessageDescriptor info_message__number_list__descriptor;
+extern const ProtobufCMessageDescriptor accept_message__descriptor;
+extern const ProtobufCMessageDescriptor reject_message__descriptor;
+extern const ProtobufCMessageDescriptor exit_message__descriptor;
+extern const ProtobufCMessageDescriptor alert_message__descriptor;
+extern const ProtobufCMessageDescriptor restart_message__descriptor;
+extern const ProtobufCMessageDescriptor change_window_size__descriptor;
+extern const ProtobufCMessageDescriptor command_suspend__descriptor;
+extern const ProtobufCMessageDescriptor client_hello__descriptor;
+extern const ProtobufCMessageDescriptor server_message__descriptor;
+extern const ProtobufCMessageDescriptor server_hello__descriptor;
+
+PROTOBUF_C__END_DECLS
+
+
+#endif /* PROTOBUF_C_log_5fserver_2eproto__INCLUDED */
diff --git a/include/protobuf-c/protobuf-c.h b/include/protobuf-c/protobuf-c.h
new file mode 100644
index 0000000..442abf6
--- /dev/null
+++ b/include/protobuf-c/protobuf-c.h
@@ -0,0 +1,1110 @@
+/*
+ * Copyright (c) 2008-2022, Dave Benson and the protobuf-c authors.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * 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 COPYRIGHT HOLDERS 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 COPYRIGHT
+ * OWNER 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.
+ */
+
+/*! \file
+ * \mainpage Introduction
+ *
+ * This is [protobuf-c], a C implementation of [Protocol Buffers].
+ *
+ * This file defines the public API for the `libprotobuf-c` support library.
+ * This API includes interfaces that can be used directly by client code as well
+ * as the interfaces used by the code generated by the `protoc-c` compiler.
+ *
+ * The `libprotobuf-c` support library performs the actual serialization and
+ * deserialization of Protocol Buffers messages. It interacts with structures,
+ * definitions, and metadata generated by the `protoc-c` compiler from .proto
+ * files.
+ *
+ * \authors Dave Benson and the `protobuf-c` authors.
+ *
+ * \copyright 2008-2014. Licensed under the terms of the [BSD-2-Clause] license.
+ *
+ * [protobuf-c]: https://github.com/protobuf-c/protobuf-c
+ * [Protocol Buffers]: https://developers.google.com/protocol-buffers/
+ * [BSD-2-Clause]: http://opensource.org/licenses/BSD-2-Clause
+ *
+ * \page gencode Generated Code
+ *
+ * For each enum, we generate a C enum. For each message, we generate a C
+ * structure which can be cast to a `ProtobufCMessage`.
+ *
+ * For each enum and message, we generate a descriptor object that allows us to
+ * implement a kind of reflection on the structures.
+ *
+ * First, some naming conventions:
+ *
+ * - The name of the type for enums and messages and services is camel case
+ * (meaning WordsAreCrammedTogether) except that double underscores are used
+ * to delimit scopes. For example, the following `.proto` file:
+ *
+~~~{.proto}
+ package foo.bar;
+ message BazBah {
+ optional int32 val = 1;
+ }
+~~~
+ *
+ * would generate a C type `Foo__Bar__BazBah`.
+ *
+ * - Identifiers for functions and globals are all lowercase, with camel case
+ * words separated by single underscores. For example, one of the function
+ * prototypes generated by `protoc-c` for the above example:
+ *
+~~~{.c}
+Foo__Bar__BazBah *
+ foo__bar__baz_bah__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+~~~
+ *
+ * - Identifiers for enum values contain an uppercase prefix which embeds the
+ * package name and the enum type name.
+ *
+ * - A double underscore is used to separate further components of identifier
+ * names.
+ *
+ * For example, in the name of the unpack function above, the package name
+ * `foo.bar` has become `foo__bar`, the message name BazBah has become
+ * `baz_bah`, and the method name is `unpack`. These are all joined with double
+ * underscores to form the C identifier `foo__bar__baz_bah__unpack`.
+ *
+ * We also generate descriptor objects for messages and enums. These are
+ * declared in the `.pb-c.h` files:
+ *
+~~~{.c}
+extern const ProtobufCMessageDescriptor foo__bar__baz_bah__descriptor;
+~~~
+ *
+ * The message structures all begin with `ProtobufCMessageDescriptor *` which is
+ * sufficient to allow them to be cast to `ProtobufCMessage`.
+ *
+ * For each message defined in a `.proto` file, we generate a number of
+ * functions and macros. Each function name contains a prefix based on the
+ * package name and message name in order to make it a unique C identifier.
+ *
+ * - `INIT`. Statically initializes a message object, initializing its
+ * descriptor and setting its fields to default values. Uninitialized
+ * messages cannot be processed by the protobuf-c library.
+ *
+~~~{.c}
+#define FOO__BAR__BAZ_BAH__INIT \
+ { PROTOBUF_C_MESSAGE_INIT (&foo__bar__baz_bah__descriptor), 0 }
+~~~
+ * - `init()`. Initializes a message object, initializing its descriptor and
+ * setting its fields to default values. Uninitialized messages cannot be
+ * processed by the protobuf-c library.
+ *
+~~~{.c}
+void foo__bar__baz_bah__init
+ (Foo__Bar__BazBah *message);
+~~~
+ * - `unpack()`. Unpacks data for a particular message format. Note that the
+ * `allocator` parameter is usually `NULL` to indicate that the system's
+ * `malloc()` and `free()` functions should be used for dynamically allocating
+ * memory.
+ *
+~~~{.c}
+Foo__Bar__BazBah *
+ foo__bar__baz_bah__unpack
+ (ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+~~~
+ *
+ * - `free_unpacked()`. Frees a message object obtained with the `unpack()`
+ * method. Freeing `NULL` is allowed (the same as with `free()`).
+ *
+~~~{.c}
+void foo__bar__baz_bah__free_unpacked
+ (Foo__Bar__BazBah *message,
+ ProtobufCAllocator *allocator);
+~~~
+ *
+ * - `get_packed_size()`. Calculates the length in bytes of the serialized
+ * representation of the message object.
+ *
+~~~{.c}
+size_t foo__bar__baz_bah__get_packed_size
+ (const Foo__Bar__BazBah *message);
+~~~
+ *
+ * - `pack()`. Pack a message object into a preallocated buffer. Assumes that
+ * the buffer is large enough. (Use `get_packed_size()` first.)
+ *
+~~~{.c}
+size_t foo__bar__baz_bah__pack
+ (const Foo__Bar__BazBah *message,
+ uint8_t *out);
+~~~
+ *
+ * - `pack_to_buffer()`. Packs a message into a "virtual buffer". This is an
+ * object which defines an "append bytes" callback to consume data as it is
+ * serialized.
+ *
+~~~{.c}
+size_t foo__bar__baz_bah__pack_to_buffer
+ (const Foo__Bar__BazBah *message,
+ ProtobufCBuffer *buffer);
+~~~
+ *
+ * \page pack Packing and unpacking messages
+ *
+ * To pack a message, first compute the packed size of the message with
+ * protobuf_c_message_get_packed_size(), then allocate a buffer of at least
+ * that size, then call protobuf_c_message_pack().
+ *
+ * Alternatively, a message can be serialized without calculating the final size
+ * first. Use the protobuf_c_message_pack_to_buffer() function and provide a
+ * ProtobufCBuffer object which implements an "append" method that consumes
+ * data.
+ *
+ * To unpack a message, call the protobuf_c_message_unpack() function. The
+ * result can be cast to an object of the type that matches the descriptor for
+ * the message.
+ *
+ * The result of unpacking a message should be freed with
+ * protobuf_c_message_free_unpacked().
+ */
+
+#ifndef PROTOBUF_C_H
+#define PROTOBUF_C_H
+
+#include <assert.h>
+#include <limits.h>
+#include <stddef.h>
+#include <inttypes.h> /* stdint.h not present on older systems */
+
+#ifdef __cplusplus
+# define PROTOBUF_C__BEGIN_DECLS extern "C" {
+# define PROTOBUF_C__END_DECLS }
+#else
+# define PROTOBUF_C__BEGIN_DECLS
+# define PROTOBUF_C__END_DECLS
+#endif
+
+PROTOBUF_C__BEGIN_DECLS
+
+#if defined(_WIN32) && defined(PROTOBUF_C_USE_SHARED_LIB)
+# ifdef PROTOBUF_C_EXPORT
+# define PROTOBUF_C__API __declspec(dllexport)
+# else
+# define PROTOBUF_C__API __declspec(dllimport)
+# endif
+#else
+# define PROTOBUF_C__API
+#endif
+
+#if !defined(PROTOBUF_C__NO_DEPRECATED) && \
+ ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
+# define PROTOBUF_C__DEPRECATED __attribute__((__deprecated__))
+#else
+# define PROTOBUF_C__DEPRECATED
+#endif
+
+#ifndef PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE
+ #define PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(enum_name) \
+ , _##enum_name##_IS_INT_SIZE = INT_MAX
+#endif
+
+#define PROTOBUF_C__SERVICE_DESCRIPTOR_MAGIC 0x14159bc3
+#define PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC 0x28aaeef9
+#define PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC 0x114315af
+
+/* Empty string used for initializers */
+#if defined(_WIN32) && defined(PROTOBUF_C_USE_SHARED_LIB)
+static const char protobuf_c_empty_string[] = "";
+#else
+extern const char protobuf_c_empty_string[];
+#endif
+
+/**
+ * \defgroup api Public API
+ *
+ * This is the public API for `libprotobuf-c`. These interfaces are stable and
+ * subject to Semantic Versioning guarantees.
+ *
+ * @{
+ */
+
+/**
+ * Values for the `flags` word in `ProtobufCFieldDescriptor`.
+ */
+typedef enum {
+ /** Set if the field is repeated and marked with the `packed` option. */
+ PROTOBUF_C_FIELD_FLAG_PACKED = (1 << 0),
+
+ /** Set if the field is marked with the `deprecated` option. */
+ PROTOBUF_C_FIELD_FLAG_DEPRECATED = (1 << 1),
+
+ /** Set if the field is a member of a oneof (union). */
+ PROTOBUF_C_FIELD_FLAG_ONEOF = (1 << 2),
+} ProtobufCFieldFlag;
+
+/**
+ * Message field rules.
+ *
+ * \see [Defining A Message Type] in the Protocol Buffers documentation.
+ *
+ * [Defining A Message Type]:
+ * https://developers.google.com/protocol-buffers/docs/proto#simple
+ */
+typedef enum {
+ /** A well-formed message must have exactly one of this field. */
+ PROTOBUF_C_LABEL_REQUIRED,
+
+ /**
+ * A well-formed message can have zero or one of this field (but not
+ * more than one).
+ */
+ PROTOBUF_C_LABEL_OPTIONAL,
+
+ /**
+ * This field can be repeated any number of times (including zero) in a
+ * well-formed message. The order of the repeated values will be
+ * preserved.
+ */
+ PROTOBUF_C_LABEL_REPEATED,
+
+ /**
+ * This field has no label. This is valid only in proto3 and is
+ * equivalent to OPTIONAL but no "has" quantifier will be consulted.
+ */
+ PROTOBUF_C_LABEL_NONE,
+} ProtobufCLabel;
+
+/**
+ * Field value types.
+ *
+ * \see [Scalar Value Types] in the Protocol Buffers documentation.
+ *
+ * [Scalar Value Types]:
+ * https://developers.google.com/protocol-buffers/docs/proto#scalar
+ */
+typedef enum {
+ PROTOBUF_C_TYPE_INT32, /**< int32 */
+ PROTOBUF_C_TYPE_SINT32, /**< signed int32 */
+ PROTOBUF_C_TYPE_SFIXED32, /**< signed int32 (4 bytes) */
+ PROTOBUF_C_TYPE_INT64, /**< int64 */
+ PROTOBUF_C_TYPE_SINT64, /**< signed int64 */
+ PROTOBUF_C_TYPE_SFIXED64, /**< signed int64 (8 bytes) */
+ PROTOBUF_C_TYPE_UINT32, /**< unsigned int32 */
+ PROTOBUF_C_TYPE_FIXED32, /**< unsigned int32 (4 bytes) */
+ PROTOBUF_C_TYPE_UINT64, /**< unsigned int64 */
+ PROTOBUF_C_TYPE_FIXED64, /**< unsigned int64 (8 bytes) */
+ PROTOBUF_C_TYPE_FLOAT, /**< float */
+ PROTOBUF_C_TYPE_DOUBLE, /**< double */
+ PROTOBUF_C_TYPE_BOOL, /**< boolean */
+ PROTOBUF_C_TYPE_ENUM, /**< enumerated type */
+ PROTOBUF_C_TYPE_STRING, /**< UTF-8 or ASCII string */
+ PROTOBUF_C_TYPE_BYTES, /**< arbitrary byte sequence */
+ PROTOBUF_C_TYPE_MESSAGE, /**< nested message */
+} ProtobufCType;
+
+/**
+ * Field wire types.
+ *
+ * \see [Message Structure] in the Protocol Buffers documentation.
+ *
+ * [Message Structure]:
+ * https://developers.google.com/protocol-buffers/docs/encoding#structure
+ */
+typedef enum {
+ PROTOBUF_C_WIRE_TYPE_VARINT = 0,
+ PROTOBUF_C_WIRE_TYPE_64BIT = 1,
+ PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED = 2,
+ /* "Start group" and "end group" wire types are unsupported. */
+ PROTOBUF_C_WIRE_TYPE_32BIT = 5,
+} ProtobufCWireType;
+
+struct ProtobufCAllocator;
+struct ProtobufCBinaryData;
+struct ProtobufCBuffer;
+struct ProtobufCBufferSimple;
+struct ProtobufCEnumDescriptor;
+struct ProtobufCEnumValue;
+struct ProtobufCEnumValueIndex;
+struct ProtobufCFieldDescriptor;
+struct ProtobufCIntRange;
+struct ProtobufCMessage;
+struct ProtobufCMessageDescriptor;
+struct ProtobufCMessageUnknownField;
+struct ProtobufCMethodDescriptor;
+struct ProtobufCService;
+struct ProtobufCServiceDescriptor;
+
+typedef struct ProtobufCAllocator ProtobufCAllocator;
+typedef struct ProtobufCBinaryData ProtobufCBinaryData;
+typedef struct ProtobufCBuffer ProtobufCBuffer;
+typedef struct ProtobufCBufferSimple ProtobufCBufferSimple;
+typedef struct ProtobufCEnumDescriptor ProtobufCEnumDescriptor;
+typedef struct ProtobufCEnumValue ProtobufCEnumValue;
+typedef struct ProtobufCEnumValueIndex ProtobufCEnumValueIndex;
+typedef struct ProtobufCFieldDescriptor ProtobufCFieldDescriptor;
+typedef struct ProtobufCIntRange ProtobufCIntRange;
+typedef struct ProtobufCMessage ProtobufCMessage;
+typedef struct ProtobufCMessageDescriptor ProtobufCMessageDescriptor;
+typedef struct ProtobufCMessageUnknownField ProtobufCMessageUnknownField;
+typedef struct ProtobufCMethodDescriptor ProtobufCMethodDescriptor;
+typedef struct ProtobufCService ProtobufCService;
+typedef struct ProtobufCServiceDescriptor ProtobufCServiceDescriptor;
+
+/** Boolean type. */
+typedef int protobuf_c_boolean;
+
+typedef void (*ProtobufCClosure)(const ProtobufCMessage *, void *closure_data);
+typedef void (*ProtobufCMessageInit)(ProtobufCMessage *);
+typedef void (*ProtobufCServiceDestroy)(ProtobufCService *);
+
+/**
+ * Structure for defining a custom memory allocator.
+ */
+struct ProtobufCAllocator {
+ /** Function to allocate memory. */
+ void *(*alloc)(void *allocator_data, size_t size);
+
+ /** Function to free memory. */
+ void (*free)(void *allocator_data, void *pointer);
+
+ /** Opaque pointer passed to `alloc` and `free` functions. */
+ void *allocator_data;
+};
+
+/**
+ * Structure for the protobuf `bytes` scalar type.
+ *
+ * The data contained in a `ProtobufCBinaryData` is an arbitrary sequence of
+ * bytes. It may contain embedded `NUL` characters and is not required to be
+ * `NUL`-terminated.
+ */
+struct ProtobufCBinaryData {
+ size_t len; /**< Number of bytes in the `data` field. */
+ uint8_t *data; /**< Data bytes. */
+};
+
+/**
+ * Structure for defining a virtual append-only buffer. Used by
+ * protobuf_c_message_pack_to_buffer() to abstract the consumption of serialized
+ * bytes.
+ *
+ * `ProtobufCBuffer` "subclasses" may be defined on the stack. For example, to
+ * write to a `FILE` object:
+ *
+~~~{.c}
+typedef struct {
+ ProtobufCBuffer base;
+ FILE *fp;
+} BufferAppendToFile;
+
+static void
+my_buffer_file_append(ProtobufCBuffer *buffer,
+ size_t len,
+ const uint8_t *data)
+{
+ BufferAppendToFile *file_buf = (BufferAppendToFile *) buffer;
+ fwrite(data, len, 1, file_buf->fp); // XXX: No error handling!
+}
+~~~
+ *
+ * To use this new type of ProtobufCBuffer, it could be called as follows:
+ *
+~~~{.c}
+...
+BufferAppendToFile tmp = {0};
+tmp.base.append = my_buffer_file_append;
+tmp.fp = fp;
+protobuf_c_message_pack_to_buffer(&message, &tmp);
+...
+~~~
+ */
+struct ProtobufCBuffer {
+ /** Append function. Consumes the `len` bytes stored at `data`. */
+ void (*append)(ProtobufCBuffer *buffer,
+ size_t len,
+ const uint8_t *data);
+};
+
+/**
+ * Simple buffer "subclass" of `ProtobufCBuffer`.
+ *
+ * A `ProtobufCBufferSimple` object is declared on the stack and uses a
+ * scratch buffer provided by the user for the initial allocation. It performs
+ * exponential resizing, using dynamically allocated memory. A
+ * `ProtobufCBufferSimple` object can be created and used as follows:
+ *
+~~~{.c}
+uint8_t pad[128];
+ProtobufCBufferSimple simple = PROTOBUF_C_BUFFER_SIMPLE_INIT(pad);
+ProtobufCBuffer *buffer = (ProtobufCBuffer *) &simple;
+~~~
+ *
+ * `buffer` can now be used with `protobuf_c_message_pack_to_buffer()`. Once a
+ * message has been serialized to a `ProtobufCBufferSimple` object, the
+ * serialized data bytes can be accessed from the `.data` field.
+ *
+ * To free the memory allocated by a `ProtobufCBufferSimple` object, if any,
+ * call PROTOBUF_C_BUFFER_SIMPLE_CLEAR() on the object, for example:
+ *
+~~~{.c}
+PROTOBUF_C_BUFFER_SIMPLE_CLEAR(&simple);
+~~~
+ *
+ * \see PROTOBUF_C_BUFFER_SIMPLE_INIT
+ * \see PROTOBUF_C_BUFFER_SIMPLE_CLEAR
+ */
+struct ProtobufCBufferSimple {
+ /** "Base class". */
+ ProtobufCBuffer base;
+ /** Number of bytes allocated in `data`. */
+ size_t alloced;
+ /** Number of bytes currently stored in `data`. */
+ size_t len;
+ /** Data bytes. */
+ uint8_t *data;
+ /** Whether `data` must be freed. */
+ protobuf_c_boolean must_free_data;
+ /** Allocator to use. May be NULL to indicate the system allocator. */
+ ProtobufCAllocator *allocator;
+};
+
+/**
+ * Describes an enumeration as a whole, with all of its values.
+ */
+struct ProtobufCEnumDescriptor {
+ /** Magic value checked to ensure that the API is used correctly. */
+ uint32_t magic;
+
+ /** The qualified name (e.g., "namespace.Type"). */
+ const char *name;
+ /** The unqualified name as given in the .proto file (e.g., "Type"). */
+ const char *short_name;
+ /** Identifier used in generated C code. */
+ const char *c_name;
+ /** The dot-separated namespace. */
+ const char *package_name;
+
+ /** Number elements in `values`. */
+ unsigned n_values;
+ /** Array of distinct values, sorted by numeric value. */
+ const ProtobufCEnumValue *values;
+
+ /** Number of elements in `values_by_name`. */
+ unsigned n_value_names;
+ /** Array of named values, including aliases, sorted by name. */
+ const ProtobufCEnumValueIndex *values_by_name;
+
+ /** Number of elements in `value_ranges`. */
+ unsigned n_value_ranges;
+ /** Value ranges, for faster lookups by numeric value. */
+ const ProtobufCIntRange *value_ranges;
+
+ /** Reserved for future use. */
+ void *reserved1;
+ /** Reserved for future use. */
+ void *reserved2;
+ /** Reserved for future use. */
+ void *reserved3;
+ /** Reserved for future use. */
+ void *reserved4;
+};
+
+/**
+ * Represents a single value of an enumeration.
+ */
+struct ProtobufCEnumValue {
+ /** The string identifying this value in the .proto file. */
+ const char *name;
+
+ /** The string identifying this value in generated C code. */
+ const char *c_name;
+
+ /** The numeric value assigned in the .proto file. */
+ int value;
+};
+
+/**
+ * Used by `ProtobufCEnumDescriptor` to look up enum values.
+ */
+struct ProtobufCEnumValueIndex {
+ /** Name of the enum value. */
+ const char *name;
+ /** Index into values[] array. */
+ unsigned index;
+};
+
+/**
+ * Describes a single field in a message.
+ */
+struct ProtobufCFieldDescriptor {
+ /** Name of the field as given in the .proto file. */
+ const char *name;
+
+ /** Tag value of the field as given in the .proto file. */
+ uint32_t id;
+
+ /** Whether the field is `REQUIRED`, `OPTIONAL`, or `REPEATED`. */
+ ProtobufCLabel label;
+
+ /** The type of the field. */
+ ProtobufCType type;
+
+ /**
+ * The offset in bytes of the message's C structure's quantifier field
+ * (the `has_MEMBER` field for optional members or the `n_MEMBER` field
+ * for repeated members or the case enum for oneofs).
+ */
+ unsigned quantifier_offset;
+
+ /**
+ * The offset in bytes into the message's C structure for the member
+ * itself.
+ */
+ unsigned offset;
+
+ /**
+ * A type-specific descriptor.
+ *
+ * If `type` is `PROTOBUF_C_TYPE_ENUM`, then `descriptor` points to the
+ * corresponding `ProtobufCEnumDescriptor`.
+ *
+ * If `type` is `PROTOBUF_C_TYPE_MESSAGE`, then `descriptor` points to
+ * the corresponding `ProtobufCMessageDescriptor`.
+ *
+ * Otherwise this field is NULL.
+ */
+ const void *descriptor; /* for MESSAGE and ENUM types */
+
+ /** The default value for this field, if defined. May be NULL. */
+ const void *default_value;
+
+ /**
+ * A flag word. Zero or more of the bits defined in the
+ * `ProtobufCFieldFlag` enum may be set.
+ */
+ uint32_t flags;
+
+ /** Reserved for future use. */
+ unsigned reserved_flags;
+ /** Reserved for future use. */
+ void *reserved2;
+ /** Reserved for future use. */
+ void *reserved3;
+};
+
+/**
+ * Helper structure for optimizing int => index lookups in the case
+ * where the keys are mostly consecutive values, as they presumably are for
+ * enums and fields.
+ *
+ * The data structures requires that the values in the original array are
+ * sorted.
+ */
+struct ProtobufCIntRange {
+ int start_value;
+ unsigned orig_index;
+ /*
+ * NOTE: the number of values in the range can be inferred by looking
+ * at the next element's orig_index. A dummy element is added to make
+ * this simple.
+ */
+};
+
+/**
+ * An instance of a message.
+ *
+ * `ProtobufCMessage` is a light-weight "base class" for all messages.
+ *
+ * In particular, `ProtobufCMessage` doesn't have any allocation policy
+ * associated with it. That's because it's common to create `ProtobufCMessage`
+ * objects on the stack. In fact, that's what we recommend for sending messages.
+ * If the object is allocated from the stack, you can't really have a memory
+ * leak.
+ *
+ * This means that calls to functions like protobuf_c_message_unpack() which
+ * return a `ProtobufCMessage` must be paired with a call to a free function,
+ * like protobuf_c_message_free_unpacked().
+ */
+struct ProtobufCMessage {
+ /** The descriptor for this message type. */
+ const ProtobufCMessageDescriptor *descriptor;
+ /** The number of elements in `unknown_fields`. */
+ unsigned n_unknown_fields;
+ /** The fields that weren't recognized by the parser. */
+ ProtobufCMessageUnknownField *unknown_fields;
+};
+
+/**
+ * Describes a message.
+ */
+struct ProtobufCMessageDescriptor {
+ /** Magic value checked to ensure that the API is used correctly. */
+ uint32_t magic;
+
+ /** The qualified name (e.g., "namespace.Type"). */
+ const char *name;
+ /** The unqualified name as given in the .proto file (e.g., "Type"). */
+ const char *short_name;
+ /** Identifier used in generated C code. */
+ const char *c_name;
+ /** The dot-separated namespace. */
+ const char *package_name;
+
+ /**
+ * Size in bytes of the C structure representing an instance of this
+ * type of message.
+ */
+ size_t sizeof_message;
+
+ /** Number of elements in `fields`. */
+ unsigned n_fields;
+ /** Field descriptors, sorted by tag number. */
+ const ProtobufCFieldDescriptor *fields;
+ /** Used for looking up fields by name. */
+ const unsigned *fields_sorted_by_name;
+
+ /** Number of elements in `field_ranges`. */
+ unsigned n_field_ranges;
+ /** Used for looking up fields by id. */
+ const ProtobufCIntRange *field_ranges;
+
+ /** Message initialisation function. */
+ ProtobufCMessageInit message_init;
+
+ /** Reserved for future use. */
+ void *reserved1;
+ /** Reserved for future use. */
+ void *reserved2;
+ /** Reserved for future use. */
+ void *reserved3;
+};
+
+/**
+ * An unknown message field.
+ */
+struct ProtobufCMessageUnknownField {
+ /** The tag number. */
+ uint32_t tag;
+ /** The wire type of the field. */
+ ProtobufCWireType wire_type;
+ /** Number of bytes in `data`. */
+ size_t len;
+ /** Field data. */
+ uint8_t *data;
+};
+
+/**
+ * Method descriptor.
+ */
+struct ProtobufCMethodDescriptor {
+ /** Method name. */
+ const char *name;
+ /** Input message descriptor. */
+ const ProtobufCMessageDescriptor *input;
+ /** Output message descriptor. */
+ const ProtobufCMessageDescriptor *output;
+};
+
+/**
+ * Service.
+ */
+struct ProtobufCService {
+ /** Service descriptor. */
+ const ProtobufCServiceDescriptor *descriptor;
+ /** Function to invoke the service. */
+ void (*invoke)(ProtobufCService *service,
+ unsigned method_index,
+ const ProtobufCMessage *input,
+ ProtobufCClosure closure,
+ void *closure_data);
+ /** Function to destroy the service. */
+ void (*destroy)(ProtobufCService *service);
+};
+
+/**
+ * Service descriptor.
+ */
+struct ProtobufCServiceDescriptor {
+ /** Magic value checked to ensure that the API is used correctly. */
+ uint32_t magic;
+
+ /** Service name. */
+ const char *name;
+ /** Short version of service name. */
+ const char *short_name;
+ /** C identifier for the service name. */
+ const char *c_name;
+ /** Package name. */
+ const char *package;
+ /** Number of elements in `methods`. */
+ unsigned n_methods;
+ /** Method descriptors, in the order defined in the .proto file. */
+ const ProtobufCMethodDescriptor *methods;
+ /** Sort index of methods. */
+ const unsigned *method_indices_by_name;
+};
+
+/**
+ * Get the version of the protobuf-c library. Note that this is the version of
+ * the library linked against, not the version of the headers compiled against.
+ *
+ * \return A string containing the version number of protobuf-c.
+ */
+PROTOBUF_C__API
+const char *
+protobuf_c_version(void);
+
+/**
+ * Get the version of the protobuf-c library. Note that this is the version of
+ * the library linked against, not the version of the headers compiled against.
+ *
+ * \return A 32 bit unsigned integer containing the version number of
+ * protobuf-c, represented in base-10 as (MAJOR*1E6) + (MINOR*1E3) + PATCH.
+ */
+PROTOBUF_C__API
+uint32_t
+protobuf_c_version_number(void);
+
+/**
+ * The version of the protobuf-c headers, represented as a string using the same
+ * format as protobuf_c_version().
+ */
+#define PROTOBUF_C_VERSION "1.4.1"
+
+/**
+ * The version of the protobuf-c headers, represented as an integer using the
+ * same format as protobuf_c_version_number().
+ */
+#define PROTOBUF_C_VERSION_NUMBER 1004001
+
+/**
+ * The minimum protoc-c version which works with the current version of the
+ * protobuf-c headers.
+ */
+#define PROTOBUF_C_MIN_COMPILER_VERSION 1000000
+
+/**
+ * Look up a `ProtobufCEnumValue` from a `ProtobufCEnumDescriptor` by name.
+ *
+ * \param desc
+ * The `ProtobufCEnumDescriptor` object.
+ * \param name
+ * The `name` field from the corresponding `ProtobufCEnumValue` object to
+ * match.
+ * \return
+ * A `ProtobufCEnumValue` object.
+ * \retval NULL
+ * If not found or if the optimize_for = CODE_SIZE option was set.
+ */
+PROTOBUF_C__API
+const ProtobufCEnumValue *
+protobuf_c_enum_descriptor_get_value_by_name(
+ const ProtobufCEnumDescriptor *desc,
+ const char *name);
+
+/**
+ * Look up a `ProtobufCEnumValue` from a `ProtobufCEnumDescriptor` by numeric
+ * value.
+ *
+ * \param desc
+ * The `ProtobufCEnumDescriptor` object.
+ * \param value
+ * The `value` field from the corresponding `ProtobufCEnumValue` object to
+ * match.
+ *
+ * \return
+ * A `ProtobufCEnumValue` object.
+ * \retval NULL
+ * If not found.
+ */
+PROTOBUF_C__API
+const ProtobufCEnumValue *
+protobuf_c_enum_descriptor_get_value(
+ const ProtobufCEnumDescriptor *desc,
+ int value);
+
+/**
+ * Look up a `ProtobufCFieldDescriptor` from a `ProtobufCMessageDescriptor` by
+ * the name of the field.
+ *
+ * \param desc
+ * The `ProtobufCMessageDescriptor` object.
+ * \param name
+ * The name of the field.
+ * \return
+ * A `ProtobufCFieldDescriptor` object.
+ * \retval NULL
+ * If not found or if the optimize_for = CODE_SIZE option was set.
+ */
+PROTOBUF_C__API
+const ProtobufCFieldDescriptor *
+protobuf_c_message_descriptor_get_field_by_name(
+ const ProtobufCMessageDescriptor *desc,
+ const char *name);
+
+/**
+ * Look up a `ProtobufCFieldDescriptor` from a `ProtobufCMessageDescriptor` by
+ * the tag value of the field.
+ *
+ * \param desc
+ * The `ProtobufCMessageDescriptor` object.
+ * \param value
+ * The tag value of the field.
+ * \return
+ * A `ProtobufCFieldDescriptor` object.
+ * \retval NULL
+ * If not found.
+ */
+PROTOBUF_C__API
+const ProtobufCFieldDescriptor *
+protobuf_c_message_descriptor_get_field(
+ const ProtobufCMessageDescriptor *desc,
+ unsigned value);
+
+/**
+ * Determine the number of bytes required to store the serialised message.
+ *
+ * \param message
+ * The message object to serialise.
+ * \return
+ * Number of bytes.
+ */
+PROTOBUF_C__API
+size_t
+protobuf_c_message_get_packed_size(const ProtobufCMessage *message);
+
+/**
+ * Serialise a message from its in-memory representation.
+ *
+ * This function stores the serialised bytes of the message in a pre-allocated
+ * buffer.
+ *
+ * \param message
+ * The message object to serialise.
+ * \param[out] out
+ * Buffer to store the bytes of the serialised message. This buffer must
+ * have enough space to store the packed message. Use
+ * protobuf_c_message_get_packed_size() to determine the number of bytes
+ * required.
+ * \return
+ * Number of bytes stored in `out`.
+ */
+PROTOBUF_C__API
+size_t
+protobuf_c_message_pack(const ProtobufCMessage *message, uint8_t *out);
+
+/**
+ * Serialise a message from its in-memory representation to a virtual buffer.
+ *
+ * This function calls the `append` method of a `ProtobufCBuffer` object to
+ * consume the bytes generated by the serialiser.
+ *
+ * \param message
+ * The message object to serialise.
+ * \param buffer
+ * The virtual buffer object.
+ * \return
+ * Number of bytes passed to the virtual buffer.
+ */
+PROTOBUF_C__API
+size_t
+protobuf_c_message_pack_to_buffer(
+ const ProtobufCMessage *message,
+ ProtobufCBuffer *buffer);
+
+/**
+ * Unpack a serialised message into an in-memory representation.
+ *
+ * \param descriptor
+ * The message descriptor.
+ * \param allocator
+ * `ProtobufCAllocator` to use for memory allocation. May be NULL to
+ * specify the default allocator.
+ * \param len
+ * Length in bytes of the serialised message.
+ * \param data
+ * Pointer to the serialised message.
+ * \return
+ * An unpacked message object.
+ * \retval NULL
+ * If an error occurred during unpacking.
+ */
+PROTOBUF_C__API
+ProtobufCMessage *
+protobuf_c_message_unpack(
+ const ProtobufCMessageDescriptor *descriptor,
+ ProtobufCAllocator *allocator,
+ size_t len,
+ const uint8_t *data);
+
+/**
+ * Free an unpacked message object.
+ *
+ * This function should be used to deallocate the memory used by a call to
+ * protobuf_c_message_unpack().
+ *
+ * \param message
+ * The message object to free. May be NULL.
+ * \param allocator
+ * `ProtobufCAllocator` to use for memory deallocation. May be NULL to
+ * specify the default allocator.
+ */
+PROTOBUF_C__API
+void
+protobuf_c_message_free_unpacked(
+ ProtobufCMessage *message,
+ ProtobufCAllocator *allocator);
+
+/**
+ * Check the validity of a message object.
+ *
+ * Makes sure all required fields (`PROTOBUF_C_LABEL_REQUIRED`) are present.
+ * Recursively checks nested messages.
+ *
+ * \retval TRUE
+ * Message is valid.
+ * \retval FALSE
+ * Message is invalid.
+ */
+PROTOBUF_C__API
+protobuf_c_boolean
+protobuf_c_message_check(const ProtobufCMessage *);
+
+/** Message initialiser. */
+#define PROTOBUF_C_MESSAGE_INIT(descriptor) { descriptor, 0, NULL }
+
+/**
+ * Initialise a message object from a message descriptor.
+ *
+ * \param descriptor
+ * Message descriptor.
+ * \param message
+ * Allocated block of memory of size `descriptor->sizeof_message`.
+ */
+PROTOBUF_C__API
+void
+protobuf_c_message_init(
+ const ProtobufCMessageDescriptor *descriptor,
+ void *message);
+
+/**
+ * Free a service.
+ *
+ * \param service
+ * The service object to free.
+ */
+PROTOBUF_C__API
+void
+protobuf_c_service_destroy(ProtobufCService *service);
+
+/**
+ * Look up a `ProtobufCMethodDescriptor` by name.
+ *
+ * \param desc
+ * Service descriptor.
+ * \param name
+ * Name of the method.
+ *
+ * \return
+ * A `ProtobufCMethodDescriptor` object.
+ * \retval NULL
+ * If not found or if the optimize_for = CODE_SIZE option was set.
+ */
+PROTOBUF_C__API
+const ProtobufCMethodDescriptor *
+protobuf_c_service_descriptor_get_method_by_name(
+ const ProtobufCServiceDescriptor *desc,
+ const char *name);
+
+/**
+ * Initialise a `ProtobufCBufferSimple` object.
+ */
+#define PROTOBUF_C_BUFFER_SIMPLE_INIT(array_of_bytes) \
+{ \
+ { protobuf_c_buffer_simple_append }, \
+ sizeof(array_of_bytes), \
+ 0, \
+ (array_of_bytes), \
+ 0, \
+ NULL \
+}
+
+/**
+ * Clear a `ProtobufCBufferSimple` object, freeing any allocated memory.
+ */
+#define PROTOBUF_C_BUFFER_SIMPLE_CLEAR(simp_buf) \
+do { \
+ if ((simp_buf)->must_free_data) { \
+ if ((simp_buf)->allocator != NULL) \
+ (simp_buf)->allocator->free( \
+ (simp_buf)->allocator, \
+ (simp_buf)->data); \
+ else \
+ free((simp_buf)->data); \
+ } \
+} while (0)
+
+/**
+ * The `append` method for `ProtobufCBufferSimple`.
+ *
+ * \param buffer
+ * The buffer object to append to. Must actually be a
+ * `ProtobufCBufferSimple` object.
+ * \param len
+ * Number of bytes in `data`.
+ * \param data
+ * Data to append.
+ */
+PROTOBUF_C__API
+void
+protobuf_c_buffer_simple_append(
+ ProtobufCBuffer *buffer,
+ size_t len,
+ const unsigned char *data);
+
+PROTOBUF_C__API
+void
+protobuf_c_service_generated_init(
+ ProtobufCService *service,
+ const ProtobufCServiceDescriptor *descriptor,
+ ProtobufCServiceDestroy destroy);
+
+PROTOBUF_C__API
+void
+protobuf_c_service_invoke_internal(
+ ProtobufCService *service,
+ unsigned method_index,
+ const ProtobufCMessage *input,
+ ProtobufCClosure closure,
+ void *closure_data);
+
+/**@}*/
+
+PROTOBUF_C__END_DECLS
+
+#endif /* PROTOBUF_C_H */
diff --git a/include/sudo_compat.h b/include/sudo_compat.h
new file mode 100644
index 0000000..c718182
--- /dev/null
+++ b/include/sudo_compat.h
@@ -0,0 +1,566 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 1996, 1998-2005, 2008, 2009-2023
+ * 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 <sys/types.h> /* for gid_t, mode_t, size_t, ssize_t, time_t, uid_t */
+#include <sys/stat.h> /* to avoid problems with mismatched headers and libc */
+#include <unistd.h> /* to avoid problems with mismatched headers and libc */
+#include <stdio.h>
+#if !defined(HAVE_VSNPRINTF) || !defined(HAVE_VASPRINTF) || \
+ !defined(HAVE_VSYSLOG) || defined(PREFER_PORTABLE_SNPRINTF)
+# include <stdarg.h>
+#endif
+
+/*
+ * Macros and functions that may be missing on some operating systems.
+ */
+
+/*
+ * 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
+
+/*
+ * Pre-C99 compilers may lack a va_copy macro.
+ */
+#ifndef HAVE_VA_COPY
+# ifdef HAVE___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_SSIZE_MAX) && !HAVE_DECL_SSIZE_MAX
+# define SSIZE_MAX LONG_MAX
+#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
+
+/* ACCESSPERMS and ALLPERMS are handy BSDisms. */
+#ifndef ACCESSPERMS
+# define ACCESSPERMS 00777
+#endif /* ACCESSPERMS */
+#ifndef ALLPERMS
+# define ALLPERMS 07777
+#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)) || !defined(HAVE_FCHMODAT) || !defined(HAVE_FSTATAT) || !defined(HAVE_UNLINKAT)
+# ifndef AT_FDCWD
+# define AT_FDCWD -100
+# endif
+# ifndef AT_SYMLINK_NOFOLLOW
+# define AT_SYMLINK_NOFOLLOW 0x02
+# endif
+#endif
+
+/* For dup3() and pipe2() emulation. */
+#if (!defined(HAVE_PIPE2) || !defined(HAVE_DUP3)) && defined(O_NONBLOCK)
+# if !defined(O_CLOEXEC) || O_CLOEXEC > 0xffffffff
+# undef O_CLOEXEC
+# define O_CLOEXEC 0x80000000
+# endif
+#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
+sudo_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(HAVE_DECL_NSIG) && !HAVE_DECL_NSIG
+# if defined(HAVE_DECL__NSIG) && HAVE_DECL__NSIG
+# define NSIG _NSIG
+# elif defined(HAVE_DECL___NSIG) && HAVE_DECL___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 on 32-bit machines when
+ * _FILE_OFFSET_BITS == 64. Use pread64/pwrite64 instead.
+ */
+#if defined(__hpux) && !defined(__LP64__)
+# ifdef HAVE_PREAD64
+# undef pread
+# define pread(_a, _b, _c, _d) pread64((_a), (_b), (_c), (_d))
+# if defined(HAVE_DECL_PREAD64) && !HAVE_DECL_PREAD64
+ ssize_t pread64(int fd, void *buf, size_t nbytes, off64_t offset);
+# endif
+# endif
+# ifdef HAVE_PWRITE64
+# undef pwrite
+# define pwrite(_a, _b, _c, _d) pwrite64((_a), (_b), (_c), (_d))
+# if defined(HAVE_DECL_PWRITE64) && !HAVE_DECL_PWRITE64
+ ssize_t pwrite64(int fd, const void *buf, size_t nbytes, off64_t offset);
+# endif
+# endif
+#endif /* __hpux && !__LP64__ */
+
+/*
+ * Older systems may lack fseeko(3), just use fseek(3) instead.
+ */
+#ifndef HAVE_FSEEKO
+# define fseeko(f, o, w) fseek((f), (long)(o), (w))
+#endif
+
+/*
+ * Functions "missing" from libc.
+ * All libc replacements are prefixed with "sudo_" to avoid namespace issues.
+ */
+
+struct passwd;
+struct stat;
+struct timespec;
+struct termios;
+struct tm;
+
+#ifndef HAVE_CFMAKERAW
+sudo_dso_public void sudo_cfmakeraw(struct termios *term);
+# undef cfmakeraw
+# define cfmakeraw(_a) sudo_cfmakeraw((_a))
+#endif /* HAVE_CFMAKERAW */
+#ifndef HAVE_CLOSEFROM
+sudo_dso_public void sudo_closefrom(int);
+# undef closefrom
+# define closefrom(_a) sudo_closefrom((_a))
+#endif /* HAVE_CLOSEFROM */
+#ifndef HAVE_EXPLICIT_BZERO
+sudo_dso_public void sudo_explicit_bzero(void *s, size_t n);
+# undef explicit_bzero
+# define explicit_bzero(_a, _b) sudo_explicit_bzero((_a), (_b))
+#endif /* HAVE_EXPLICIT_BZERO */
+#ifndef HAVE_FREEZERO
+sudo_dso_public void sudo_freezero(void *p, size_t n);
+# undef freezero
+# define freezero(_a, _b) sudo_freezero((_a), (_b))
+#endif /* HAVE_FREEZERO */
+#ifndef HAVE_GETGROUPLIST
+sudo_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 */
+#if !defined(HAVE_GETDELIM)
+sudo_dso_public ssize_t sudo_getdelim(char ** restrict bufp, size_t * restrict bufsizep, int delim, FILE * restrict fp);
+# undef getdelim
+# define getdelim(_a, _b, _c, _d) sudo_getdelim((_a), (_b), (_c), (_d))
+#elif defined(HAVE_DECL_GETDELIM) && !HAVE_DECL_GETDELIM
+/* getdelim present in libc but missing prototype (old gcc fixed includes?) */
+ssize_t getdelim(char **bufp, size_t *bufsizep, int delim, FILE *fp);
+#endif /* HAVE_GETDELIM */
+#ifndef HAVE_GETUSERSHELL
+sudo_dso_public char *sudo_getusershell(void);
+# undef getusershell
+# define getusershell() sudo_getusershell()
+sudo_dso_public void sudo_setusershell(void);
+# undef setusershell
+# define setusershell() sudo_setusershell()
+sudo_dso_public void sudo_endusershell(void);
+# undef endusershell
+# define endusershell() sudo_endusershell()
+#elif HAVE_DECL_GETUSERSHELL == 0
+/* Older Solaris has getusershell() et al but does not declare it. */
+char *getusershell(void);
+void setusershell(void);
+void endusershell(void);
+#endif /* HAVE_GETUSERSHELL */
+#ifndef HAVE_GMTIME_R
+sudo_dso_public struct tm *sudo_gmtime_r(const time_t *, struct tm *);
+# undef gmtime_r
+# define gmtime_r(_a, _b) sudo_gmtime_r((_a), (_b))
+#endif /* HAVE_GMTIME_R */
+#ifndef HAVE_LOCALTIME_R
+sudo_dso_public struct tm *sudo_localtime_r(const time_t *, struct tm *);
+# undef localtime_r
+# define localtime_r(_a, _b) sudo_localtime_r((_a), (_b))
+#endif /* HAVE_LOCALTIME_R */
+#ifndef HAVE_TIMEGM
+sudo_dso_public time_t sudo_timegm(struct tm *);
+#endif /* HAVE_TIMEGM */
+#ifndef HAVE_UTIMENSAT
+sudo_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_FCHMODAT
+sudo_dso_public int sudo_fchmodat(int dfd, const char *path, mode_t mode, int flag);
+# undef fchmodat
+# define fchmodat(_a, _b, _c, _d) sudo_fchmodat((_a), (_b), (_c), (_d))
+#endif /* HAVE_FCHMODAT */
+#ifndef HAVE_FSTATAT
+sudo_dso_public int sudo_fstatat(int dfd, const char *path, struct stat *sb, int flag);
+# undef fstatat
+# define fstatat(_a, _b, _c, _d) sudo_fstatat((_a), (_b), (_c), (_d))
+#endif /* HAVE_FSTATAT */
+#ifndef HAVE_FUTIMENS
+sudo_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)
+sudo_dso_public int sudo_snprintf(char * restrict str, size_t n, char const * restrict fmt, ...) sudo_printflike(3, 4);
+# undef snprintf
+# define snprintf sudo_snprintf
+#endif /* HAVE_SNPRINTF */
+#if !defined(HAVE_VSNPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
+sudo_dso_public int sudo_vsnprintf(char * restrict str, size_t n, const char * restrict fmt, va_list ap) sudo_printflike(3, 0);
+# undef vsnprintf
+# define vsnprintf sudo_vsnprintf
+#endif /* HAVE_VSNPRINTF */
+#if !defined(HAVE_ASPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
+sudo_dso_public int sudo_asprintf(char ** restrict str, char const * restrict fmt, ...) sudo_printflike(2, 3);
+# undef asprintf
+# define asprintf sudo_asprintf
+#endif /* HAVE_ASPRINTF */
+#if !defined(HAVE_VASPRINTF) || defined(PREFER_PORTABLE_SNPRINTF)
+sudo_dso_public int sudo_vasprintf(char ** restrict str, const char * restrict fmt, va_list ap) sudo_printflike(2, 0);
+# undef vasprintf
+# define vasprintf sudo_vasprintf
+#endif /* HAVE_VASPRINTF */
+#ifndef HAVE_STRLCAT
+sudo_dso_public size_t sudo_strlcat(char * restrict dst, const char * restrict src, size_t siz);
+# undef strlcat
+# define strlcat(_a, _b, _c) sudo_strlcat((_a), (_b), (_c))
+#endif /* HAVE_STRLCAT */
+#ifndef HAVE_STRLCPY
+sudo_dso_public size_t sudo_strlcpy(char * restrict dst, const char * restrict src, size_t siz);
+# undef strlcpy
+# define strlcpy(_a, _b, _c) sudo_strlcpy((_a), (_b), (_c))
+#endif /* HAVE_STRLCPY */
+#ifndef HAVE_STRNDUP
+sudo_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
+sudo_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_FCHOWNAT
+sudo_dso_public int sudo_fchownat(int dfd, const char *path, uid_t uid, gid_t gid, int flag);
+# undef fchownat
+# define fchownat(_a, _b, _c, _d, _e) sudo_fchownat((_a), (_b), (_c), (_d), (_e))
+#endif /* HAVE_FCHOWNAT */
+#ifndef HAVE_MEMRCHR
+sudo_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_MKDIRAT
+sudo_dso_public int sudo_mkdirat(int dfd, const char *path, mode_t mode);
+# undef mkdirat
+# define mkdirat(_a, _b, _c) sudo_mkdirat((_a), (_b), (_c))
+#endif /* HAVE_MKDIRAT */
+#if !defined(HAVE_MKDTEMPAT) || !defined(HAVE_MKOSTEMPSAT)
+# if defined(HAVE_MKDTEMPAT_NP) && defined(HAVE_MKOSTEMPSAT_NP)
+# undef mkdtempat
+# define mkdtempat mkdtempat_np
+# undef mkostempsat
+# define mkostempsat mkostempsat_np
+# else
+sudo_dso_public char *sudo_mkdtemp(char *path);
+# undef mkdtemp
+# define mkdtemp(_a) sudo_mkdtemp((_a))
+sudo_dso_public char *sudo_mkdtempat(int dfd, char *path);
+# undef mkdtempat
+# define mkdtempat(_a, _b) sudo_mkdtempat((_a), (_b))
+sudo_dso_public int sudo_mkostempsat(int dfd, char *path, int slen, int flags);
+# undef mkostempsat
+# define mkostempsat(_a, _b, _c, _d) sudo_mkostempsat((_a), (_b), (_c), (_d))
+sudo_dso_public int sudo_mkstemp(char *path);
+# undef mkstemp
+# define mkstemp(_a) sudo_mkstemp((_a))
+sudo_dso_public int sudo_mkstemps(char *path, int slen);
+# undef mkstemps
+# define mkstemps(_a, _b) sudo_mkstemps((_a), (_b))
+# endif /* HAVE_MKDTEMPAT_NP || HAVE_MKOSTEMPSAT_NP */
+#endif /* !HAVE_MKDTEMPAT || !HAVE_MKOSTEMPSAT */
+#ifndef HAVE_NANOSLEEP
+sudo_dso_public int sudo_nanosleep(const struct timespec *timeout, struct timespec *remainder);
+#undef nanosleep
+# define nanosleep(_a, _b) sudo_nanosleep((_a), (_b))
+#endif /* HAVE_NANOSLEEP */
+#ifndef HAVE_OPENAT
+sudo_dso_public int sudo_openat(int dfd, const char *path, int flags, mode_t mode);
+# undef openat
+# define openat(_a, _b, _c, _d) sudo_openat((_a), (_b), (_c), (_d))
+#endif /* HAVE_OPENAT */
+#ifndef HAVE_PW_DUP
+sudo_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
+sudo_dso_public char *sudo_strsignal(int signo);
+# undef strsignal
+# define strsignal(_a) sudo_strsignal((_a))
+#endif /* HAVE_STRSIGNAL */
+#ifndef HAVE_SIG2STR
+sudo_dso_public int sudo_sig2str(int signo, char *signame);
+# undef sig2str
+# define sig2str(_a, _b) sudo_sig2str((_a), (_b))
+#endif /* HAVE_SIG2STR */
+#ifndef HAVE_STR2SIG
+sudo_dso_public int sudo_str2sig(const char *signame, int *signum);
+# undef str2sig
+# define str2sig(_a, _b) sudo_str2sig((_a), (_b))
+#endif /* HAVE_STR2SIG */
+#if !defined(HAVE_INET_NTOP) && defined(NEED_INET_NTOP)
+sudo_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
+sudo_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
+sudo_dso_public const char *sudo_getprogname(void);
+# undef getprogname
+# define getprogname() sudo_getprogname()
+#endif /* HAVE_GETPROGNAME */
+#ifndef HAVE_SETPROGNAME
+sudo_dso_public void sudo_setprogname(const char *name);
+# undef setprogname
+# define setprogname(_a) sudo_setprogname(_a)
+#endif /* HAVE_SETPROGNAME */
+#ifndef HAVE_REALLOCARRAY
+sudo_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_REALPATH
+sudo_dso_public char *sudo_realpath(const char * restrict path, char * restrict resolved);
+# undef realpath
+# define realpath(_a, _b) sudo_realpath((_a), (_b))
+#endif /* HAVE_REALPATH */
+#ifndef HAVE_DUP3
+sudo_dso_public int sudo_dup3(int oldd, int newd, int flags);
+# undef dup3
+# define dup3(_a, _b, _c) sudo_dup3((_a), (_b), (_c))
+#endif /* HAVE_DUP3 */
+#ifndef HAVE_PIPE2
+sudo_dso_public int sudo_pipe2(int fildes[2], int flags);
+# undef pipe2
+# define pipe2(_a, _b) sudo_pipe2((_a), (_b))
+#endif /* HAVE_PIPE2 */
+#ifndef HAVE_PREAD
+sudo_dso_public ssize_t sudo_pread(int fd, void *buf, size_t nbytes, off_t offset);
+# undef pread
+# define pread(_a, _b, _c, _d) sudo_pread((_a), (_b), (_c), (_d))
+#endif /* HAVE_PREAD */
+#ifndef HAVE_PWRITE
+sudo_dso_public ssize_t sudo_pwrite(int fd, const void *buf, size_t nbytes, off_t offset);
+# undef pwrite
+# define pwrite(_a, _b, _c, _d) sudo_pwrite((_a), (_b), (_c), (_d))
+#endif /* HAVE_PWRITE */
+#ifndef HAVE_UNLINKAT
+sudo_dso_public int sudo_unlinkat(int dfd, const char *path, int flag);
+# undef unlinkat
+# define unlinkat(_a, _b, _c) sudo_unlinkat((_a), (_b), (_c))
+#endif /* HAVE_UNLINKAT */
+
+#endif /* SUDO_COMPAT_H */
diff --git a/include/sudo_conf.h b/include/sudo_conf.h
new file mode 100644
index 0000000..866e47d
--- /dev/null
+++ b/include/sudo_conf.h
@@ -0,0 +1,97 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2011-2017, 2019-2021 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
+
+#ifdef HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# include <compat/stdbool.h>
+#endif
+
+#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. */
+sudo_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. */
+sudo_dso_public const char *sudo_conf_askpass_path_v1(void);
+sudo_dso_public const char *sudo_conf_sesh_path_v1(void);
+sudo_dso_public const char *sudo_conf_intercept_path_v1(void);
+sudo_dso_public const char *sudo_conf_noexec_path_v1(void);
+sudo_dso_public const char *sudo_conf_plugin_dir_path_v1(void);
+sudo_dso_public const char *sudo_conf_devsearch_path_v1(void);
+sudo_dso_public struct sudo_conf_debug_list *sudo_conf_debugging_v1(void);
+sudo_dso_public struct sudo_conf_debug_file_list *sudo_conf_debug_files_v1(const char *progname);
+sudo_dso_public struct plugin_info_list *sudo_conf_plugins_v1(void);
+sudo_dso_public bool sudo_conf_disable_coredump_v1(void);
+sudo_dso_public bool sudo_conf_developer_mode_v1(void);
+sudo_dso_public bool sudo_conf_probe_interfaces_v1(void);
+sudo_dso_public int sudo_conf_group_source_v1(void);
+sudo_dso_public int sudo_conf_max_groups_v1(void);
+sudo_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_intercept_path() sudo_conf_intercept_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_developer_mode() sudo_conf_developer_mode_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..b8b2d6c
--- /dev/null
+++ b/include/sudo_debug.h
@@ -0,0 +1,435 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * 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 <sys/types.h> /* for id_t, mode_t, size_t, ssize_t, time_t */
+#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_APPARMOR (15<<6) /* AppArmor */
+#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 unsigned int sudo_debug_subsys = (subsys)
+#else
+# define debug_decl_func(funcname) \
+ const char __func__[] = #funcname;
+# define debug_decl_vars(funcname, subsys) \
+ debug_decl_func(funcname) \
+ const unsigned int sudo_debug_subsys = (subsys)
+#endif
+#define debug_decl(funcname, subsys) \
+ debug_decl_vars((funcname), (subsys)); \
+ sudo_debug_enter(__func__, __FILE__, __LINE__, sudo_debug_subsys)
+
+/*
+ * Different flavors of sudo_debug_exit() macros.
+ */
+#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+# define sudo_debug_enter(_func, _file, _line, _sys) \
+ do { \
+ sudo_debug_printf2(NULL, NULL, 0, (_sys) | SUDO_DEBUG_TRACE, \
+ "-> %s @ %s:%d", (_func), (_file), (_line)); \
+ } while (0)
+
+# define sudo_debug_exit(_func, _file, _line, _sys) \
+ do { \
+ sudo_debug_printf2(NULL, NULL, 0, (_sys) | SUDO_DEBUG_TRACE, \
+ "<- %s @ %s:%d", (_func), (_file), (_line)); \
+ } while (0)
+
+# define sudo_debug_exit_int(_func, _file, _line, _sys, _ret) \
+ do { \
+ sudo_debug_printf2(NULL, NULL, 0, (_sys) | SUDO_DEBUG_TRACE, \
+ "<- %s @ %s:%d := %d", (_func), (_file), (_line), (_ret)); \
+ } while (0)
+
+# define sudo_debug_exit_uint(_func, _file, _line, _sys, _ret) \
+ do { \
+ sudo_debug_printf2(NULL, NULL, 0, (_sys) | SUDO_DEBUG_TRACE, \
+ "<- %s @ %s:%d := %u", (_func), (_file), (_line), (_ret)); \
+ } while (0)
+
+# define sudo_debug_exit_long(_func, _file, _line, _sys, _ret) \
+ do { \
+ sudo_debug_printf2(NULL, NULL, 0, (_sys) | SUDO_DEBUG_TRACE, \
+ "<- %s @ %s:%d := %ld", (_func), (_file), (_line), (_ret)); \
+ } while (0)
+
+# if SIZEOF_ID_T == 8
+# define sudo_debug_exit_id_t(_func, _file, _line, _sys, _ret) \
+ do { \
+ sudo_debug_printf2(NULL, NULL, 0, (_sys) | SUDO_DEBUG_TRACE, \
+ "<- %s @ %s:%d := %lld", (_func), (_file), (_line), (long long)(_ret));\
+ } while (0)
+# else
+# define sudo_debug_exit_id_t(_func, _file, _line, _sys, _ret) \
+ do { \
+ sudo_debug_printf2(NULL, NULL, 0, (_sys) | SUDO_DEBUG_TRACE, \
+ "<- %s @ %s:%d := %d", (_func), (_file), (_line), (int)(_ret)); \
+ } while (0)
+# endif
+
+# define sudo_debug_exit_size_t(_func, _file, _line, _sys, _ret) \
+ do { \
+ sudo_debug_printf2(NULL, NULL, 0, (_sys) | SUDO_DEBUG_TRACE, \
+ "<- %s @ %s:%d := %zu", (_func), (_file), (_line), (_ret)); \
+ } while (0)
+
+# define sudo_debug_exit_ssize_t(_func, _file, _line, _sys, _ret) \
+ do { \
+ sudo_debug_printf2(NULL, NULL, 0, (_sys) | SUDO_DEBUG_TRACE, \
+ "<- %s @ %s:%d := %zd", (_func), (_file), (_line), (_ret)); \
+ } while (0)
+
+# if SIZEOF_TIME_T == 8
+# define sudo_debug_exit_time_t(_func, _file, _line, _sys, _ret) \
+ do { \
+ sudo_debug_printf2(NULL, NULL, 0, (_sys) | SUDO_DEBUG_TRACE, \
+ "<- %s @ %s:%d := %lld", (_func), (_file), (_line), (long long)(_ret));\
+ } while (0)
+# else
+# define sudo_debug_exit_time_t(_func, _file, _line, _sys, _ret) \
+ do { \
+ sudo_debug_printf2(NULL, NULL, 0, (_sys) | SUDO_DEBUG_TRACE, \
+ "<- %s @ %s:%d := %d", (_func), (_file), (_line), (int)(_ret)); \
+ } while (0)
+# endif
+
+# define sudo_debug_exit_mode_t(_func, _file, _line, _sys, _ret) \
+ do { \
+ sudo_debug_printf2(NULL, NULL, 0, (_sys) | SUDO_DEBUG_TRACE, \
+ "<- %s @ %s:%d := %d", (_func), (_file), (_line), (int)(_ret)); \
+ } while (0)
+
+# define sudo_debug_exit_bool(_func, _file, _line, _sys, _ret) \
+ do { \
+ sudo_debug_printf2(NULL, NULL, 0, (_sys) | SUDO_DEBUG_TRACE, \
+ "<- %s @ %s:%d := %s", (_func), (_file), (_line), (_ret) ? "true": "false");\
+ } while (0)
+
+# define sudo_debug_exit_str(_func, _file, _line, _sys, _ret) \
+ do { \
+ sudo_debug_printf2(NULL, NULL, 0, (_sys) | SUDO_DEBUG_TRACE, \
+ "<- %s @ %s:%d := %s", (_func), (_file), (_line), (_ret) ? (_ret) : "(null)");\
+ } while (0)
+
+# define sudo_debug_exit_str_masked(_func, _file, _line, _sys, _ret) \
+ do { \
+ const char _stars[] = "********************************************************************************"; \
+ const size_t _len = (_ret) ? strlen(_ret) : sizeof("(null)") - 1; \
+ const char *_s = (_ret) ? _stars : "(null)"; \
+ sudo_debug_printf2(NULL, NULL, 0, (_sys) | SUDO_DEBUG_TRACE, \
+ "<- %s @ %s:%d := %.*s", (_func), (_file), (_line), (int)_len, _s);\
+ } while (0)
+
+# define sudo_debug_exit_ptr(_func, _file, _line, _sys, _ret) \
+ do { \
+ sudo_debug_printf2(NULL, NULL, 0, (_sys) | SUDO_DEBUG_TRACE, \
+ "<- %s @ %s:%d := %p", (_func), (_file), (_line), (_ret)); \
+ } while (0)
+#else /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
+# define sudo_debug_enter(_a, _b, _c, _d) ((void)&(_d))
+# define sudo_debug_exit(_a, _b, _c, _d) ((void)&(_d))
+# define sudo_debug_exit_int(_a, _b, _c, _d, _e) ((void)&(_d))
+# define sudo_debug_exit_uint(_a, _b, _c, _d, _e) ((void)&(_d))
+# define sudo_debug_exit_long(_a, _b, _c, _d, _e) ((void)&(_d))
+# define sudo_debug_exit_id_t(_a, _b, _c, _d, _e) ((void)&(_d))
+# define sudo_debug_exit_size_t(_a, _b, _c, _d, _e) ((void)&(_d))
+# define sudo_debug_exit_ssize_t(_a, _b, _c, _d, _e) ((void)&(_d))
+# define sudo_debug_exit_time_t(_a, _b, _c, _d, _e) ((void)&(_d))
+# define sudo_debug_exit_mode_t(_a, _b, _c, _d, _e) ((void)&(_d))
+# define sudo_debug_exit_bool(_a, _b, _c, _d, _e) ((void)&(_d))
+# define sudo_debug_exit_str(_a, _b, _c, _d, _e) ((void)&(_d))
+# define sudo_debug_exit_str_masked(_a, _b, _c, _d, _e) ((void)&(_d))
+# define sudo_debug_exit_ptr(_a, _b, _c, _d, _e) ((void)&(_d))
+#endif /* FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION */
+
+/*
+ * 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_uint(ret) \
+ do { \
+ unsigned int sudo_debug_ret = (ret); \
+ sudo_debug_exit_uint(__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_mode_t(ret) \
+ do { \
+ mode_t sudo_debug_ret = (ret); \
+ sudo_debug_exit_mode_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))
+
+sudo_dso_public int sudo_debug_deregister_v1(int instance_id);
+sudo_dso_public void sudo_debug_enter_v1(const char *func, const char *file, int line, unsigned int subsys);
+sudo_dso_public void sudo_debug_execve2_v1(unsigned int level, const char *path, char *const argv[], char *const envp[]);
+sudo_dso_public void sudo_debug_exit_v1(const char *func, const char *file, int line, unsigned int subsys);
+sudo_dso_public void sudo_debug_exit_bool_v1(const char *func, const char *file, int line, unsigned int subsys, bool ret);
+sudo_dso_public void sudo_debug_exit_int_v1(const char *func, const char *file, int line, unsigned int subsys, int ret);
+sudo_dso_public void sudo_debug_exit_uint_v1(const char *func, const char *file, int line, unsigned int subsys, unsigned int ret);
+sudo_dso_public void sudo_debug_exit_long_v1(const char *func, const char *file, int line, unsigned int subsys, long ret);
+sudo_dso_public void sudo_debug_exit_ptr_v1(const char *func, const char *file, int line, unsigned int subsys, const void *ret);
+sudo_dso_public void sudo_debug_exit_id_t_v1(const char *func, const char *file, int line, unsigned int subsys, id_t ret);
+sudo_dso_public void sudo_debug_exit_size_t_v1(const char *func, const char *file, int line, unsigned int subsys, size_t ret);
+sudo_dso_public void sudo_debug_exit_ssize_t_v1(const char *func, const char *file, int line, unsigned int subsys, ssize_t ret);
+sudo_dso_public void sudo_debug_exit_str_v1(const char *func, const char *file, int line, unsigned int subsys, const char *ret);
+sudo_dso_public void sudo_debug_exit_str_masked_v1(const char *func, const char *file, int line, unsigned int subsys, const char *ret);
+sudo_dso_public void sudo_debug_exit_time_t_v1(const char *func, const char *file, int line, unsigned int subsys, time_t ret);
+sudo_dso_public void sudo_debug_exit_mode_t_v1(const char *func, const char *file, int line, unsigned int subsys, mode_t ret);
+sudo_dso_public pid_t sudo_debug_fork_v1(void);
+sudo_dso_public int sudo_debug_get_active_instance_v1(void);
+sudo_dso_public int sudo_debug_get_fds_v1(unsigned char **fds);
+sudo_dso_public int sudo_debug_get_instance_v1(const char *program);
+sudo_dso_public int sudo_debug_parse_flags_v1(struct sudo_conf_debug_file_list *debug_files, const char *entry);
+sudo_dso_public void sudo_debug_printf2_v1(const char *func, const char *file, int line, unsigned int level, const char * restrict fmt, ...) sudo_printf0like(5, 6);
+sudo_dso_public void sudo_debug_printf_nvm_v1(int pri, const char * restrict fmt, ...) sudo_printf0like(2, 3);
+sudo_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);
+sudo_dso_public int sudo_debug_register_v2(const char *program, const char *const subsystems[], unsigned int ids[], struct sudo_conf_debug_file_list *debug_files, int minfd);
+sudo_dso_public int sudo_debug_set_active_instance_v1(int inst);
+sudo_dso_public void sudo_debug_update_fd_v1(int ofd, int nfd);
+sudo_dso_public void sudo_debug_vprintf2_v1(const char *func, const char *file, int line, unsigned int level, const char * restrict fmt, va_list ap) sudo_printf0like(5, 0);
+sudo_dso_public void sudo_debug_write2_v1(int fd, const char *func, const char *file, int line, const char *str, unsigned int len, int errnum);
+sudo_dso_public bool sudo_debug_needed_v1(unsigned int level);
+
+#define sudo_debug_needed(level) sudo_debug_needed_v1((level)|sudo_debug_subsys)
+#define sudo_debug_deregister(_a) sudo_debug_deregister_v1((_a))
+#define sudo_debug_execve2(_a, _b, _c, _d) sudo_debug_execve2_v1((_a), (_b), (_c), (_d))
+#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_parse_flags(_a, _b) sudo_debug_parse_flags_v1((_a), (_b))
+#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, _e) sudo_debug_register_v2((_a), (_b), (_c), (_d), (_e))
+#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..0e7f8ef
--- /dev/null
+++ b/include/sudo_digest.h
@@ -0,0 +1,47 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * 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. */
+sudo_dso_public struct sudo_digest *sudo_digest_alloc_v1(unsigned int digest_type);
+sudo_dso_public void sudo_digest_free_v1(struct sudo_digest *dig);
+sudo_dso_public void sudo_digest_reset_v1(struct sudo_digest *dig);
+sudo_dso_public int sudo_digest_getlen_v1(unsigned int digest_type);
+sudo_dso_public size_t sudo_digest_getlen_v2(unsigned int digest_type);
+sudo_dso_public void sudo_digest_update_v1(struct sudo_digest *dig, const void *data, size_t len);
+sudo_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_v2((_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..c2e1aaf
--- /dev/null
+++ b/include/sudo_dso.h
@@ -0,0 +1,57 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * 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. */
+sudo_dso_public char *sudo_dso_strerror_v1(void);
+sudo_dso_public int sudo_dso_unload_v1(void *handle);
+sudo_dso_public void *sudo_dso_findsym_v1(void *handle, const char *symbol);
+sudo_dso_public void *sudo_dso_load_v1(const char *path, int mode);
+sudo_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..27985c4
--- /dev/null
+++ b/include/sudo_event.h
@@ -0,0 +1,216 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * 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 <time.h> /* for struct timespec */
+#include <signal.h> /* for sigatomic_t and NSIG */
+#include <sudo_queue.h>
+
+struct timeval; /* for deprecated APIs */
+
+/* Event types (keep in sync with sudo_plugin.h) */
+#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) */
+
+/* User-settable events for sudo_ev_init() (SUDO_EV_TIMEOUT not valid here) */
+#define SUDO_EV_MASK (SUDO_EV_READ|SUDO_EV_WRITE|SUDO_EV_PERSIST|SUDO_EV_SIGNAL|SUDO_EV_SIGINFO)
+
+/* Event flags (internal) */
+#define SUDO_EVQ_INSERTED 0x01U /* event is on the event queue */
+#define SUDO_EVQ_ACTIVE 0x02U /* event is on the active queue */
+#define SUDO_EVQ_TIMEOUTS 0x04U /* event is on the timeouts queue */
+
+/* Event loop flags */
+#define SUDO_EVLOOP_ONCE 0x01U /* Only run once through the loop */
+#define SUDO_EVLOOP_NONBLOCK 0x02U /* Do not block in event loop */
+
+/* Event base flags (internal) */
+#define SUDO_EVBASE_LOOPONCE SUDO_EVLOOP_ONCE
+#define SUDO_EVBASE_LOOPEXIT 0x02U
+#define SUDO_EVBASE_LOOPBREAK 0x04U
+#define SUDO_EVBASE_LOOPCONT 0x08U
+#define SUDO_EVBASE_GOT_EXIT 0x10U
+#define SUDO_EVBASE_GOT_BREAK 0x20U
+#define SUDO_EVBASE_GOT_MASK 0xf0U
+
+/* Must match sudo_plugin_ev_callback_t in sudo_plugin.h */
+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[];
+};
+
+/* 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) */
+ unsigned 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 signal */
+#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
+ void *readfds_in; /* read I/O descriptor set (in) */
+ void *writefds_in; /* write I/O descriptor set (in) */
+ void *readfds_out; /* read I/O descriptor set (out) */
+ void *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. */
+sudo_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. */
+sudo_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. */
+sudo_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. */
+sudo_dso_public struct sudo_event *sudo_ev_alloc_v1(int fd, short events, sudo_ev_callback_t callback, void *closure);
+sudo_dso_public struct sudo_event *sudo_ev_alloc_v2(int fd, int events, sudo_ev_callback_t callback, void *closure);
+#define sudo_ev_alloc(_a, _b, _c, _d) sudo_ev_alloc_v2((_a), (_b), (_c), (_d))
+
+/* Free an event. */
+sudo_dso_public void sudo_ev_free_v1(struct sudo_event *ev);
+#define sudo_ev_free(_a) sudo_ev_free_v1((_a))
+
+/* Set an event struct that was pre-allocated. */
+sudo_dso_public int sudo_ev_set_v1(struct sudo_event *ev, int fd, short events, sudo_ev_callback_t callback, void *closure);
+sudo_dso_public int sudo_ev_set_v2(struct sudo_event *ev, int fd, int events, sudo_ev_callback_t callback, void *closure);
+#define sudo_ev_set(_a, _b, _c, _d, _e) sudo_ev_set_v2((_a), (_b), (_c), (_d), (_e))
+
+/* Add an event, returns 0 on success, -1 on error */
+sudo_dso_public int sudo_ev_add_v1(struct sudo_event_base *head, struct sudo_event *ev, const struct timeval *timo, bool tohead);
+sudo_dso_public int sudo_ev_add_v2(struct sudo_event_base *head, struct sudo_event *ev, const 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 */
+sudo_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 */
+sudo_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 */
+sudo_dso_public int sudo_ev_loop_v1(struct sudo_event_base *head, unsigned int flags);
+#define sudo_ev_loop(_a, _b) sudo_ev_loop_v1((_a), (_b))
+
+/* Return pending event types, fills in ts if non-NULL and there is a timeout */
+sudo_dso_public int sudo_ev_pending_v1(struct sudo_event *ev, short events, struct timespec *ts);
+sudo_dso_public int sudo_ev_pending_v2(struct sudo_event *ev, int events, struct timespec *ts);
+#define sudo_ev_pending(_a, _b, _c) sudo_ev_pending_v2((_a), (_b), (_c))
+
+/* Return the remaining timeout associated with an event (deprecated). */
+sudo_dso_public int sudo_ev_get_timeleft_v1(struct sudo_event *ev, struct timeval *tv);
+sudo_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. */
+sudo_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. */
+sudo_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. */
+sudo_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(). */
+sudo_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(). */
+sudo_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)
+
+/* Set the base an event is associated with. */
+#define sudo_ev_set_base(_ev, _b) ((_ev)->base = (_b))
+
+/* 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, unsigned int flags);
+
+#endif /* SUDO_EVENT_H */
diff --git a/include/sudo_eventlog.h b/include/sudo_eventlog.h
new file mode 100644
index 0000000..d19a095
--- /dev/null
+++ b/include/sudo_eventlog.h
@@ -0,0 +1,175 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2020-2021 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_EVENTLOG_H
+#define SUDO_EVENTLOG_H
+
+#include <sys/types.h> /* for gid_t, uid_t */
+#include <time.h> /* for struct timespec */
+#ifdef HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# include <compat/stdbool.h>
+#endif /* HAVE_STDBOOL_H */
+
+/* Supported event types. */
+enum event_type {
+ EVLOG_ACCEPT,
+ EVLOG_REJECT,
+ EVLOG_EXIT,
+ EVLOG_ALERT
+};
+
+/* Supported eventlog types (bitmask). */
+#define EVLOG_NONE 0x00
+#define EVLOG_SYSLOG 0x01
+#define EVLOG_FILE 0x02
+
+/* Supported eventlog formats. */
+enum eventlog_format {
+ EVLOG_SUDO,
+ EVLOG_JSON
+};
+
+/* Eventlog flag values. */
+#define EVLOG_RAW 0x01 /* only include message and errstr */
+#define EVLOG_MAIL 0x02 /* mail the log message too */
+#define EVLOG_MAIL_ONLY 0x04 /* only mail the message, no other logging */
+#define EVLOG_CWD 0x08 /* log cwd if no runcwd and use CWD, not PWD */
+
+/*
+ * Maximum number of characters to log per entry. The syslogger
+ * will log this much, after that, it truncates the log line.
+ * We need this here to make sure that we continue with another
+ * syslog(3) call if the internal buffer is more than 1023 characters.
+ */
+#ifndef MAXSYSLOGLEN
+# define MAXSYSLOGLEN 960
+#endif
+
+/*
+ * Indentation level for file-based logs when word wrap is enabled.
+ */
+#define EVENTLOG_INDENT " "
+
+/*
+ * Event log config, used with eventlog_getconf()
+ */
+struct eventlog_config {
+ int type;
+ enum eventlog_format format;
+ size_t file_maxlen;
+ size_t syslog_maxlen;
+ int syslog_acceptpri;
+ int syslog_rejectpri;
+ int syslog_alertpri;
+ uid_t mailuid;
+ bool omit_hostname;
+ const char *logpath;
+ const char *time_fmt;
+ const char *mailerpath;
+ const char *mailerflags;
+ const char *mailfrom;
+ const char *mailto;
+ const char *mailsub;
+ FILE *(*open_log)(int type, const char *);
+ void (*close_log)(int type, FILE *);
+};
+
+/*
+ * Info present in the eventlog file, regardless of format.
+ */
+struct eventlog {
+ char *iolog_path;
+ const char *iolog_file; /* substring of iolog_path, do not free */
+ char *command;
+ char *cwd;
+ char *runchroot;
+ char *runcwd;
+ char *rungroup;
+ char *runuser;
+ char *peeraddr;
+ char *signal_name;
+ char *source;
+ char *submithost;
+ char *submituser;
+ char *submitgroup;
+ char **submitenv;
+ char *ttyname;
+ char **runargv;
+ char **runenv;
+ char **env_add;
+ struct timespec submit_time;
+ struct timespec iolog_offset;
+ struct timespec run_time;
+ int exit_value;
+ int lines;
+ int columns;
+ uid_t runuid;
+ gid_t rungid;
+ bool dumped_core;
+ char sessid[7];
+ char uuid_str[37];
+};
+
+/* Callback from eventlog code to write log info */
+struct json_container;
+struct sudo_lbuf;
+typedef bool (*eventlog_json_callback_t)(struct json_container *, void *);
+
+/* eventlog.c */
+bool eventlog_accept(const struct eventlog *evlog, int flags, eventlog_json_callback_t info_cb, void *info);
+bool eventlog_exit(const struct eventlog *evlog, int flags);
+bool eventlog_alert(const struct eventlog *evlog, int flags, struct timespec *alert_time, const char *reason, const char *errstr);
+bool eventlog_mail(const struct eventlog *evlog, int flags, struct timespec *event_time, const char *reason, const char *errstr, char * const extra[]);
+bool eventlog_reject(const struct eventlog *evlog, int flags, const char *reason, eventlog_json_callback_t info_cb, void *info);
+bool eventlog_store_json(struct json_container *jsonc, const struct eventlog *evlog);
+bool eventlog_store_sudo(int event_type, const struct eventlog *evlog, struct sudo_lbuf *lbuf);
+void eventlog_free(struct eventlog *evlog);
+
+/* eventlog_conf.c */
+void eventlog_set_type(int type);
+void eventlog_set_format(enum eventlog_format format);
+void eventlog_set_syslog_acceptpri(int pri);
+void eventlog_set_syslog_rejectpri(int pri);
+void eventlog_set_syslog_alertpri(int pri);
+void eventlog_set_syslog_maxlen(size_t len);
+void eventlog_set_file_maxlen(size_t len);
+void eventlog_set_mailuid(uid_t uid);
+void eventlog_set_omit_hostname(bool omit_hostname);
+void eventlog_set_logpath(const char *path);
+void eventlog_set_time_fmt(const char *fmt);
+void eventlog_set_mailerpath(const char *path);
+void eventlog_set_mailerflags(const char *mflags);
+void eventlog_set_mailfrom(const char *from_addr);
+void eventlog_set_mailto(const char *to_addr);
+void eventlog_set_mailsub(const char *subject);
+void eventlog_set_open_log(FILE *(*fn)(int type, const char *));
+void eventlog_set_close_log(void (*fn)(int type, FILE *));
+const struct eventlog_config *eventlog_getconf(void);
+
+/* logwrap.c */
+size_t eventlog_writeln(FILE *fp, char *line, size_t len, size_t maxlen);
+
+/* parse_json.c */
+struct eventlog_json_object;
+struct eventlog_json_object *eventlog_json_read(FILE *fp, const char *filename);
+bool eventlog_json_parse(struct eventlog_json_object *object, struct eventlog *evlog);
+void eventlog_json_free(struct eventlog_json_object *root);
+
+#endif /* SUDO_EVENTLOG_H */
diff --git a/include/sudo_fatal.h b/include/sudo_fatal.h
new file mode 100644
index 0000000..194da08
--- /dev/null
+++ b/include/sudo_fatal.h
@@ -0,0 +1,211 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * 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 */
+
+#include <sudo_plugin.h> /* for conversation function */
+
+/* No output to debug files when fuzzing. */
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+# define SUDO_ERROR_WRAP 0
+#endif
+
+/*
+ * 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);
+typedef bool (*sudo_warn_setlocale_t)(bool, int *);
+
+sudo_dso_public int sudo_fatal_callback_deregister_v1(sudo_fatal_callback_t func);
+sudo_dso_public int sudo_fatal_callback_register_v1(sudo_fatal_callback_t func);
+sudo_dso_public char *sudo_warn_gettext_v1(const char *domainname, const char *msgid) sudo_attr_fmt_arg(2);
+sudo_dso_public void sudo_warn_set_locale_func_v1(sudo_warn_setlocale_t func);
+sudo_noreturn sudo_dso_public void sudo_fatal_nodebug_v1(const char * restrict fmt, ...) sudo_printf0like(1, 2);
+sudo_noreturn sudo_dso_public void sudo_fatalx_nodebug_v1(const char * restrict fmt, ...) sudo_printflike(1, 2);
+sudo_noreturn sudo_dso_public void sudo_gai_fatal_nodebug_v1(int errnum, const char * restrict fmt, ...) sudo_printflike(2, 3);
+sudo_noreturn sudo_dso_public void sudo_vfatal_nodebug_v1(const char * restrict fmt, va_list ap) sudo_printf0like(1, 0);
+sudo_noreturn sudo_dso_public void sudo_vfatalx_nodebug_v1(const char * restrict fmt, va_list ap) sudo_printflike(1, 0);
+sudo_noreturn sudo_dso_public void sudo_gai_vfatal_nodebug_v1(int errnum, const char * restrict fmt, va_list ap) sudo_printflike(2, 0);
+sudo_dso_public void sudo_warn_nodebug_v1(const char * restrict fmt, ...) sudo_printf0like(1, 2);
+sudo_dso_public void sudo_warnx_nodebug_v1(const char * restrict fmt, ...) sudo_printflike(1, 2);
+sudo_dso_public void sudo_gai_warn_nodebug_v1(int errnum, const char * restrict fmt, ...) sudo_printflike(2, 3);
+sudo_dso_public void sudo_vwarn_nodebug_v1(const char * restrict fmt, va_list ap) sudo_printf0like(1, 0);
+sudo_dso_public void sudo_vwarnx_nodebug_v1(const char * restrict fmt, va_list ap) sudo_printflike(1, 0);
+sudo_dso_public void sudo_gai_vwarn_nodebug_v1(int errnum, const char * restrict fmt, va_list ap) sudo_printflike(2, 0);
+sudo_dso_public void sudo_warn_set_conversation_v1(sudo_conv_t conv);
+
+#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..16275ab
--- /dev/null
+++ b/include/sudo_gettext.h
@@ -0,0 +1,77 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * 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_iolog.h b/include/sudo_iolog.h
new file mode 100644
index 0000000..052fcf3
--- /dev/null
+++ b/include/sudo_iolog.h
@@ -0,0 +1,158 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2009-2022 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_IOLOG_H
+#define SUDO_IOLOG_H
+
+#include <sys/types.h> /* for gid_t, mode_t, size_t, ssize_t, uid_t */
+
+#ifdef HAVE_ZLIB_H
+# include <zlib.h> /* for gzFile */
+#endif
+
+/* Default maximum session ID */
+#define SESSID_MAX 2176782336U
+
+/* Default value for "iolog_file" */
+#define IOLOG_FILE "%{seq}"
+
+/*
+ * I/O log event types as stored as the first field in the timing file.
+ * Changing existing values will result in incompatible I/O log files.
+ */
+#define IO_EVENT_STDIN 0
+#define IO_EVENT_STDOUT 1
+#define IO_EVENT_STDERR 2
+#define IO_EVENT_TTYIN 3
+#define IO_EVENT_TTYOUT 4
+#define IO_EVENT_WINSIZE 5
+#define IO_EVENT_TTYOUT_1_8_7 6
+#define IO_EVENT_SUSPEND 7
+#define IO_EVENT_COUNT 8
+
+/*
+ * Indexes into iolog_files[] array.
+ * These must match the IO_EVENT_ defines above.
+ * TODO: eliminate use of IOFD_* and IO_EVENT_* as indexes in favor of
+ * a struct containing iolog_file *s for each (and names too?).
+ */
+#define IOFD_STDIN 0
+#define IOFD_STDOUT 1
+#define IOFD_STDERR 2
+#define IOFD_TTYIN 3
+#define IOFD_TTYOUT 4
+#define IOFD_TIMING 5
+#define IOFD_MAX 6
+
+/*
+ * Default password prompt regex.
+ */
+#define PASSPROMPT_REGEX "[Pp]assword[: ]*"
+
+struct timing_closure {
+ struct timespec delay;
+ const char *decimal;
+ struct iolog_file *iol;
+ int event;
+ union {
+ struct {
+ int lines;
+ int cols;
+ } winsize;
+ size_t nbytes;
+ int signo;
+ } u;
+};
+
+struct iolog_file {
+ bool enabled;
+ bool compressed;
+ bool writable;
+ union {
+ FILE *f;
+#ifdef HAVE_ZLIB_H
+ gzFile g;
+#endif
+ void *v;
+ } fd;
+};
+
+struct iolog_path_escape {
+ const char *name;
+ size_t (*copy_fn)(char *, size_t, void *);
+};
+
+/* host_port.c */
+bool iolog_parse_host_port(char *str, char **hostp, char **portp, bool *tlsp, const char *defport, const char *defport_tls);
+
+/* iolog_path.c */
+bool expand_iolog_path(const char *inpath, char *path, size_t pathlen, const struct iolog_path_escape *escapes, void *closure);
+
+/* iolog_util.c */
+bool iolog_parse_timing(const char *line, struct timing_closure *timing);
+char *iolog_parse_delay(const char *cp, struct timespec *delay, const char *decimal_point);
+int iolog_read_timing_record(struct iolog_file *iol, struct timing_closure *timing);
+struct eventlog *iolog_parse_loginfo(int dfd, const char *iolog_dir);
+bool iolog_parse_loginfo_json(FILE *fp, const char *iolog_dir, struct eventlog *evlog);
+bool iolog_parse_loginfo_legacy(FILE *fp, const char *iolog_dir, struct eventlog *evlog);
+void iolog_adjust_delay(struct timespec *delay, struct timespec *max_delay, double scale_factor);
+
+/* iolog_fileio.c */
+struct passwd;
+struct group;
+bool iolog_close(struct iolog_file *iol, const char **errstr);
+bool iolog_eof(struct iolog_file *iol);
+bool iolog_mkdtemp(char *path);
+bool iolog_mkpath(char *path);
+bool iolog_nextid(const char *iolog_dir, char sessid[7]);
+bool iolog_open(struct iolog_file *iol, int dfd, int iofd, const char *mode);
+bool iolog_write_info_file(int dfd, struct eventlog *evlog);
+char *iolog_gets(struct iolog_file *iol, char *buf, int bufsize, const char **errsttr);
+const char *iolog_fd_to_name(int iofd);
+int iolog_openat(int fdf, const char *path, int flags);
+off_t iolog_seek(struct iolog_file *iol, off_t offset, int whence);
+ssize_t iolog_read(struct iolog_file *iol, void *buf, size_t nbytes, const char **errstr);
+ssize_t iolog_write(struct iolog_file *iol, const void *buf, size_t len, const char **errstr);
+void iolog_clearerr(struct iolog_file *iol);
+bool iolog_flush(struct iolog_file *iol, const char **errstr);
+void iolog_rewind(struct iolog_file *iol);
+unsigned int iolog_get_maxseq(void);
+uid_t iolog_get_uid(void);
+gid_t iolog_get_gid(void);
+mode_t iolog_get_file_mode(void);
+mode_t iolog_get_dir_mode(void);
+bool iolog_get_compress(void);
+bool iolog_get_flush(void);
+void iolog_set_compress(bool);
+void iolog_set_defaults(void);
+void iolog_set_flush(bool);
+void iolog_set_gid(gid_t gid);
+void iolog_set_maxseq(unsigned int maxval);
+void iolog_set_mode(mode_t mode);
+void iolog_set_owner(uid_t uid, uid_t gid);
+bool iolog_swapids(bool restore);
+bool iolog_mkdirs(const char *path);
+
+/* iolog_filter.c */
+void *iolog_pwfilt_alloc(void);
+bool iolog_pwfilt_add(void *handle, const char *pattern);
+void iolog_pwfilt_free(void *handle);
+bool iolog_pwfilt_remove(void *handle, const char *pattern);
+bool iolog_pwfilt_run(void *handle, int event, const char *buf, size_t len, char **newbuf);
+
+#endif /* SUDO_IOLOG_H */
diff --git a/include/sudo_json.h b/include/sudo_json.h
new file mode 100644
index 0000000..be4bab5
--- /dev/null
+++ b/include/sudo_json.h
@@ -0,0 +1,100 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2013-2020 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_JSON_H
+#define SUDO_JSON_H
+
+#include <sys/types.h> /* for id_t */
+
+#ifdef HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# include <compat/stdbool.h>
+#endif
+
+/*
+ * JSON values may be of the following types.
+ */
+enum json_value_type {
+ JSON_STRING,
+ JSON_ID,
+ JSON_NUMBER,
+ JSON_OBJECT,
+ JSON_ARRAY,
+ JSON_BOOL,
+ JSON_NULL
+};
+
+/*
+ * JSON value suitable for printing.
+ * Note: this does not support object values.
+ */
+struct json_value {
+ enum json_value_type type;
+ union {
+ const char *string;
+ long long number;
+ id_t id;
+ bool boolean;
+ } u;
+};
+
+struct json_container {
+ char *buf;
+ unsigned int buflen;
+ unsigned int bufsize;
+ unsigned int indent_level;
+ unsigned int indent_increment;
+ bool minimal;
+ bool memfatal;
+ bool need_comma;
+ bool quiet;
+};
+
+sudo_dso_public bool sudo_json_init_v1(struct json_container *jsonc, unsigned int indent, bool minimal, bool memfatal);
+sudo_dso_public bool sudo_json_init_v2(struct json_container *jsonc, unsigned int indent, bool minimal, bool memfatal, bool quiet);
+#define sudo_json_init(_a, _b, _c, _d, _e) sudo_json_init_v2((_a), (_b), (_c), (_d), (_e))
+
+sudo_dso_public void sudo_json_free_v1(struct json_container *jsonc);
+#define sudo_json_free(_a) sudo_json_free_v1((_a))
+
+sudo_dso_public bool sudo_json_open_object_v1(struct json_container *jsonc, const char *name);
+#define sudo_json_open_object(_a, _b) sudo_json_open_object_v1((_a), (_b))
+
+sudo_dso_public bool sudo_json_close_object_v1(struct json_container *jsonc);
+#define sudo_json_close_object(_a) sudo_json_close_object_v1((_a))
+
+sudo_dso_public bool sudo_json_open_array_v1(struct json_container *jsonc, const char *name);
+#define sudo_json_open_array(_a, _b) sudo_json_open_array_v1((_a), (_b))
+
+sudo_dso_public bool sudo_json_close_array_v1(struct json_container *jsonc);
+#define sudo_json_close_array(_a) sudo_json_close_array_v1((_a))
+
+sudo_dso_public bool sudo_json_add_value_v1(struct json_container *jsonc, const char *name, struct json_value *value);
+#define sudo_json_add_value(_a, _b, _c) sudo_json_add_value_v1((_a), (_b), (_c))
+
+sudo_dso_public bool sudo_json_add_value_as_object_v1(struct json_container *jsonc, const char *name, struct json_value *value);
+#define sudo_json_add_value_as_object(_a, _b, _c) sudo_json_add_value_as_object_v1((_a), (_b), (_c))
+
+sudo_dso_public char *sudo_json_get_buf_v1(struct json_container *jsonc);
+#define sudo_json_get_buf(_a) sudo_json_get_buf_v1((_a))
+
+sudo_dso_public unsigned int sudo_json_get_len_v1(struct json_container *jsonc);
+#define sudo_json_get_len(_a) sudo_json_get_len_v1((_a))
+
+#endif /* SUDO_JSON_H */
diff --git a/include/sudo_lbuf.h b/include/sudo_lbuf.h
new file mode 100644
index 0000000..c1b9fb4
--- /dev/null
+++ b/include/sudo_lbuf.h
@@ -0,0 +1,62 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2007, 2010, 2011, 2013-2015, 2023
+ * 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;
+ unsigned int indent;
+ unsigned int len;
+ unsigned int size;
+ unsigned short cols;
+ unsigned short error;
+};
+
+typedef int (*sudo_lbuf_output_t)(const char *);
+
+/* Flags for sudo_lbuf_append_esc() */
+#define LBUF_ESC_CNTRL 0x01
+#define LBUF_ESC_BLANK 0x02
+#define LBUF_ESC_QUOTE 0x04
+
+sudo_dso_public void sudo_lbuf_init_v1(struct sudo_lbuf *lbuf, sudo_lbuf_output_t output, unsigned int indent, const char *continuation, int cols);
+sudo_dso_public void sudo_lbuf_destroy_v1(struct sudo_lbuf *lbuf);
+sudo_dso_public bool sudo_lbuf_append_v1(struct sudo_lbuf *lbuf, const char * restrict fmt, ...) sudo_printflike(2, 3);
+sudo_dso_public bool sudo_lbuf_append_esc_v1(struct sudo_lbuf *lbuf, int flags, const char * restrict fmt, ...) sudo_printflike(3, 4);
+sudo_dso_public bool sudo_lbuf_append_quoted_v1(struct sudo_lbuf *lbuf, const char *set, const char * restrict fmt, ...) sudo_printflike(3, 4);
+sudo_dso_public void sudo_lbuf_print_v1(struct sudo_lbuf *lbuf);
+sudo_dso_public bool sudo_lbuf_error_v1(struct sudo_lbuf *lbuf);
+sudo_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_esc sudo_lbuf_append_esc_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..81a68aa
--- /dev/null
+++ b/include/sudo_plugin.h
@@ -0,0 +1,288 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2009-2023 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 21
+#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)
+
+/* "plugin type" for the sudo front end, as passed to an audit plugin */
+#define SUDO_FRONT_END 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.
+ */
+#define SUDO_CONV_REPL_MAX 1023
+
+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 * restrict fmt, ...);
+
+/*
+ * Hooks allow a plugin to hook into specific sudo and/or libc functions.
+ */
+
+#if defined(__GNUC__) && ((__GNUC__ == 4 && __GNUC_MINOR__ >= 4) || __GNUC__ > 4)
+# pragma GCC diagnostic ignored "-Wstrict-prototypes"
+#endif
+
+/* 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
+
+/*
+ * Plugin interface to sudo's main event loop.
+ */
+typedef void (*sudo_plugin_ev_callback_t)(int fd, int what, void *closure);
+
+struct timespec;
+struct sudo_plugin_event {
+ int (*set)(struct sudo_plugin_event *pev, int fd, int events, sudo_plugin_ev_callback_t callback, void *closure);
+ int (*add)(struct sudo_plugin_event *pev, struct timespec *timeout);
+ int (*del)(struct sudo_plugin_event *pev);
+ int (*pending)(struct sudo_plugin_event *pev, int events, struct timespec *ts);
+ int (*fd)(struct sudo_plugin_event *pev);
+ void (*setbase)(struct sudo_plugin_event *pev, void *base);
+ void (*loopbreak)(struct sudo_plugin_event *pev);
+ void (*free)(struct sudo_plugin_event *pev);
+ /* actually larger... */
+};
+
+/* Sudo plugin Event types */
+#define SUDO_PLUGIN_EV_TIMEOUT 0x01 /* fire after timeout */
+#define SUDO_PLUGIN_EV_READ 0x02 /* fire when readable */
+#define SUDO_PLUGIN_EV_WRITE 0x04 /* fire when writable */
+#define SUDO_PLUGIN_EV_PERSIST 0x08 /* persist until deleted */
+#define SUDO_PLUGIN_EV_SIGNAL 0x10 /* fire on signal receipt */
+
+/* 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_plugin_printf, char * const settings[],
+ char * const user_info[], char * const user_env[],
+ char * const plugin_options[], const char **errstr);
+ 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[], const char **errstr);
+ int (*list)(int argc, char * const argv[], int verbose,
+ const char *user, const char **errstr);
+ int (*validate)(const char **errstr);
+ void (*invalidate)(int rmcred);
+ int (*init_session)(struct passwd *pwd, char **user_env_out[],
+ const char **errstr);
+ void (*register_hooks)(int version, int (*register_hook)(struct sudo_hook *hook));
+ void (*deregister_hooks)(int version, int (*deregister_hook)(struct sudo_hook *hook));
+ struct sudo_plugin_event * (*event_alloc)(void);
+};
+
+/* 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_plugin_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[], const char **errstr);
+ 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, const char **errstr);
+ int (*log_ttyout)(const char *buf, unsigned int len, const char **errstr);
+ int (*log_stdin)(const char *buf, unsigned int len, const char **errstr);
+ int (*log_stdout)(const char *buf, unsigned int len, const char **errstr);
+ int (*log_stderr)(const char *buf, unsigned int len, const char **errstr);
+ 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 line, unsigned int cols,
+ const char **errstr);
+ int (*log_suspend)(int signo, const char **errstr);
+ struct sudo_plugin_event * (*event_alloc)(void);
+};
+
+/* Differ audit plugin close status types. */
+#define SUDO_PLUGIN_NO_STATUS 0
+#define SUDO_PLUGIN_WAIT_STATUS 1
+#define SUDO_PLUGIN_EXEC_ERROR 2
+#define SUDO_PLUGIN_SUDO_ERROR 3
+
+/* Audit plugin type and defines */
+struct audit_plugin {
+#define SUDO_AUDIT_PLUGIN 3
+ unsigned int type; /* always SUDO_AUDIT_PLUGIN */
+ unsigned int version; /* always SUDO_API_VERSION */
+ int (*open)(unsigned int version, sudo_conv_t conversation,
+ sudo_printf_t sudo_plugin_printf, char * const settings[],
+ char * const user_info[], int submit_optind,
+ char * const submit_argv[], char * const submit_envp[],
+ char * const plugin_options[], const char **errstr);
+ void (*close)(int status_type, int status);
+ int (*accept)(const char *plugin_name, unsigned int plugin_type,
+ char * const command_info[], char * const run_argv[],
+ char * const run_envp[], const char **errstr);
+ int (*reject)(const char *plugin_name, unsigned int plugin_type,
+ const char *audit_msg, char * const command_info[],
+ const char **errstr);
+ int (*error)(const char *plugin_name, unsigned int plugin_type,
+ const char *audit_msg, char * const command_info[],
+ const char **errstr);
+ int (*show_version)(int verbose);
+ void (*register_hooks)(int version, int (*register_hook)(struct sudo_hook *hook));
+ void (*deregister_hooks)(int version, int (*deregister_hook)(struct sudo_hook *hook));
+ struct sudo_plugin_event * (*event_alloc)(void);
+};
+
+/* Approval plugin type and defines */
+struct approval_plugin {
+#define SUDO_APPROVAL_PLUGIN 4
+ unsigned int type; /* always SUDO_APPROVAL_PLUGIN */
+ unsigned int version; /* always SUDO_API_VERSION */
+ int (*open)(unsigned int version, sudo_conv_t conversation,
+ sudo_printf_t sudo_plugin_printf, char * const settings[],
+ char * const user_info[], int submit_optind,
+ char * const submit_argv[], char * const submit_envp[],
+ char * const plugin_options[], const char **errstr);
+ void (*close)(void);
+ int (*check)(char * const command_info[], char * const run_argv[],
+ char * const run_envp[], const char **errstr);
+ int (*show_version)(int verbose);
+};
+
+/* 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_plugin_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..f055957
--- /dev/null
+++ b/include/sudo_queue.h
@@ -0,0 +1,823 @@
+/*
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * 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..d030ed0
--- /dev/null
+++ b/include/sudo_rand.h
@@ -0,0 +1,57 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * 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
+sudo_dso_public uint32_t sudo_arc4random(void);
+# undef arc4random
+# define arc4random() sudo_arc4random()
+#endif /* ARC4RANDOM */
+
+#ifndef HAVE_ARC4RANDOM_BUF
+sudo_dso_public void sudo_arc4random_buf(void *buf, size_t n);
+# undef arc4random_buf
+# define arc4random_buf(a, b) sudo_arc4random_buf((a), (b))
+#endif /* ARC4RANDOM_BUF */
+
+#ifndef HAVE_ARC4RANDOM_UNIFORM
+sudo_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_ssl_compat.h b/include/sudo_ssl_compat.h
new file mode 100644
index 0000000..1f09472
--- /dev/null
+++ b/include/sudo_ssl_compat.h
@@ -0,0 +1,46 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2023 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_SSL_COMPAT_H
+#define SUDO_SSL_COMPAT_H
+
+# ifdef HAVE_OPENSSL
+
+/*
+ * Compatibility defines for OpenSSL 1.0.2 (not needed for 1.1.x)
+ */
+# ifndef HAVE_WOLFSSL
+# ifndef HAVE_X509_STORE_CTX_GET0_CERT
+# define X509_STORE_CTX_get0_cert(x) ((x)->cert)
+# endif
+# ifndef HAVE_TLS_METHOD
+# define TLS_method() SSLv23_method()
+# endif
+# endif /* !HAVE_WOLFSSL */
+
+/*
+ * SSL_read_ex() and SSL_write_ex() were added in OpenSSL 1.1.1.
+ */
+# ifndef HAVE_SSL_READ_EX
+int SSL_read_ex(SSL *, void *, size_t, size_t *);
+int SSL_write_ex(SSL *, const void *, size_t, size_t *);
+# endif /* HAVE_SSL_READ_EX */
+
+# endif /* HAVE_OPENSSL */
+
+#endif /* SUDO_SSL_COMPAT_H */
diff --git a/include/sudo_util.h b/include/sudo_util.h
new file mode 100644
index 0000000..d534d40
--- /dev/null
+++ b/include/sudo_util.h
@@ -0,0 +1,358 @@
+/*
+ * SPDX-License-Identifier: ISC
+ *
+ * Copyright (c) 2013-2023 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
+
+#include <sys/types.h> /* for dev_t, mode_t, gid_t, size_t, ssize_t, uid_t */
+#ifdef HAVE_STDBOOL_H
+# include <stdbool.h>
+#else
+# include <compat/stdbool.h>
+#endif /* HAVE_STDBOOL_H */
+
+#ifdef __TANDEM
+# define ROOT_UID 65535
+#else
+# define ROOT_UID 0
+#endif
+#define ROOT_GID 0
+
+#ifndef TIME_T_MIN
+# if SIZEOF_TIME_T == 8
+# define TIME_T_MIN LLONG_MIN
+# else
+# define TIME_T_MIN INT_MIN
+# endif
+#endif
+#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 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 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
+
+#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 */
+
+/* sizeof() that returns a signed value */
+#define ssizeof(_x) ((ssize_t)sizeof(_x))
+
+/* Bit map macros. */
+#define sudo_setbit(_a, _i) ((_a)[(_i) / NBBY] |= 1U << ((_i) % NBBY))
+#define sudo_clrbit(_a, _i) ((_a)[(_i) / NBBY] &= ~(1U << ((_i) % NBBY)))
+#define sudo_isset(_a, _i) ((_a)[(_i) / NBBY] & (1U << ((_i) % NBBY)))
+#define sudo_isclr(_a, _i) (((_a)[(_i) / NBBY] & (1U << ((_i) % NBBY))) == 0)
+
+/* Macros to determine the length of a type in string form. */
+#define STRLEN_MAX_UNSIGNED(t) (((sizeof(t) * 8 * 1233) >> 12) + 1)
+#define STRLEN_MAX_SIGNED(t) (STRLEN_MAX_UNSIGNED(t) + ((sizeof(t) == 8) ? 0 : 1))
+
+/* sudo_parseln() flags */
+#define PARSELN_COMM_BOL 0x01 /* comments only at beginning 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
+
+/* Forward struct declarations. */
+struct stat;
+
+/* aix.c */
+sudo_dso_public int aix_getauthregistry_v1(char *user, char *saved_registry);
+#define aix_getauthregistry(_a, _b) aix_getauthregistry_v1((_a), (_b))
+sudo_dso_public int aix_prep_user_v1(char *user, const char *tty);
+#define aix_prep_user(_a, _b) aix_prep_user_v1((_a), (_b))
+sudo_dso_public int aix_restoreauthdb_v1(void);
+#define aix_restoreauthdb() aix_restoreauthdb_v1()
+sudo_dso_public int aix_setauthdb_v1(char *user);
+sudo_dso_public int aix_setauthdb_v2(char *user, char *registry);
+#define aix_setauthdb(_a, _b) aix_setauthdb_v2((_a), (_b))
+
+/* basename.c */
+sudo_dso_public char *sudo_basename_v1(const char *filename);
+#define sudo_basename(_a) sudo_basename_v1(_a)
+
+/* gethostname.c */
+sudo_dso_public char *sudo_gethostname_v1(void);
+#define sudo_gethostname() sudo_gethostname_v1()
+
+/* gettime.c */
+sudo_dso_public int sudo_gettime_awake_v1(struct timespec *ts);
+#define sudo_gettime_awake(_a) sudo_gettime_awake_v1((_a))
+sudo_dso_public int sudo_gettime_mono_v1(struct timespec *ts);
+#define sudo_gettime_mono(_a) sudo_gettime_mono_v1((_a))
+sudo_dso_public int sudo_gettime_real_v1(struct timespec *ts);
+#define sudo_gettime_real(_a) sudo_gettime_real_v1((_a))
+
+/* gidlist.c */
+sudo_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 */
+sudo_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))
+
+/* hexchar.c */
+sudo_dso_public int sudo_hexchar_v1(const char *s);
+#define sudo_hexchar(_a) sudo_hexchar_v1(_a)
+
+/* key_val.c */
+sudo_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 */
+sudo_dso_public bool sudo_lock_file_v1(int fd, int action);
+#define sudo_lock_file(_a, _b) sudo_lock_file_v1((_a), (_b))
+sudo_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))
+
+/* logfac.c */
+sudo_dso_public bool sudo_str2logfac_v1(const char *str, int *logfac);
+#define sudo_str2logfac(_a, _b) sudo_str2logfac_v1((_a), (_b))
+sudo_dso_public const char *sudo_logfac2str_v1(int num);
+#define sudo_logfac2str(_a) sudo_logfac2str_v1((_a))
+
+/* logpri.c */
+sudo_dso_public bool sudo_str2logpri_v1(const char *str, int *logpri);
+#define sudo_str2logpri(_a, _b) sudo_str2logpri_v1((_a), (_b))
+sudo_dso_public const char *sudo_logpri2str_v1(int num);
+#define sudo_logpri2str(_a) sudo_logpri2str_v1((_a))
+
+/* mkdir_parents.c */
+sudo_dso_public bool sudo_mkdir_parents_v1(const char *path, uid_t uid, gid_t gid, mode_t mode, bool quiet);
+#define sudo_mkdir_parents(_a, _b, _c, _d, _e) sudo_mkdir_parents_v1((_a), (_b), (_c), (_d), (_e))
+sudo_dso_public int sudo_open_parent_dir_v1(const char *path, uid_t uid, gid_t gid, mode_t mode, bool quiet);
+#define sudo_open_parent_dir(_a, _b, _c, _d, _e) sudo_open_parent_dir_v1((_a), (_b), (_c), (_d), (_e))
+
+/* mmap_alloc.c */
+sudo_dso_public void *sudo_mmap_alloc_v1(size_t size) sudo_malloclike;
+#define sudo_mmap_alloc(_a) sudo_mmap_alloc_v1(_a)
+sudo_dso_public void *sudo_mmap_allocarray_v1(size_t count, size_t size) sudo_malloclike;
+#define sudo_mmap_allocarray(_a, _b) sudo_mmap_allocarray_v1((_a), (_b))
+sudo_dso_public char *sudo_mmap_strdup_v1(const char *str);
+#define sudo_mmap_strdup(_a) sudo_mmap_strdup_v1(_a)
+sudo_dso_public void sudo_mmap_free_v1(void *ptr);
+#define sudo_mmap_free(_a) sudo_mmap_free_v1(_a)
+sudo_dso_public int sudo_mmap_protect_v1(void *ptr);
+#define sudo_mmap_protect(_a) sudo_mmap_protect_v1(_a)
+
+/* multiarch.c */
+sudo_dso_public char *sudo_stat_multiarch_v1(const char *path, struct stat *sb);
+#define sudo_stat_multiarch(_a, _b) sudo_stat_multiarch_v1((_a), (_b))
+
+/* parseln.c */
+sudo_dso_public ssize_t sudo_parseln_v1(char **buf, size_t *bufsize, unsigned int *lineno, FILE *fp);
+sudo_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 */
+sudo_dso_public void initprogname(const char *);
+sudo_dso_public void initprogname2(const char *, const char * const *);
+
+/* rcstr.c */
+sudo_dso_public char *sudo_rcstr_dup(const char *src);
+sudo_dso_public char *sudo_rcstr_alloc(size_t len) sudo_malloclike;
+sudo_dso_public char *sudo_rcstr_addref(const char *s);
+sudo_dso_public void sudo_rcstr_delref(const char *s);
+
+/* regex.c */
+sudo_dso_public bool sudo_regex_compile_v1(void *v, const char *pattern, const char **errstr);
+#define sudo_regex_compile(_a, _b, _c) sudo_regex_compile_v1((_a), (_b), (_c))
+
+/* roundup.c */
+sudo_dso_public unsigned int sudo_pow2_roundup_v1(unsigned int len);
+sudo_dso_public size_t sudo_pow2_roundup_v2(size_t len);
+#define sudo_pow2_roundup(_a) sudo_pow2_roundup_v2((_a))
+
+/* 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
+sudo_dso_public int sudo_secure_dir_v1(const char *path, uid_t uid, gid_t gid, struct stat *sb);
+#define sudo_secure_dir(_a, _b, _c, _d) sudo_secure_dir_v1((_a), (_b), (_c), (_d))
+sudo_dso_public int sudo_secure_fd_v1(int fd, unsigned int type, uid_t uid, gid_t gid, struct stat *sb);
+#define sudo_secure_fd(_a, _b, _c, _d, _e) sudo_secure_fd_v1((_a), (_b), (_c), (_d), (_e))
+sudo_dso_public int sudo_secure_file_v1(const char *path, uid_t uid, gid_t gid, struct stat *sb);
+#define sudo_secure_file(_a, _b, _c, _d) sudo_secure_file_v1((_a), (_b), (_c), (_d))
+sudo_dso_public int sudo_secure_open_file_v1(const char *path, uid_t uid, gid_t gid, struct stat *sb, int *error);
+#define sudo_secure_open_file(_a, _b, _c, _d, _e) sudo_secure_open_file_v1((_a), (_b), (_c), (_d), (_e))
+sudo_dso_public int sudo_secure_open_dir_v1(const char *path, uid_t uid, gid_t gid, struct stat *sb, int *error);
+#define sudo_secure_open_dir(_a, _b, _c, _d, _e) sudo_secure_open_dir_v1((_a), (_b), (_c), (_d), (_e))
+sudo_dso_public int sudo_open_conf_path_v1(const char *path, char *name, size_t namesize, int (*fn)(const char *, int));
+#define sudo_open_conf_path(_a, _b, _c, _d) sudo_open_conf_path_v1((_a), (_b), (_c), (_d))
+
+/* setgroups.c */
+sudo_dso_public int sudo_setgroups_v1(int ngids, const GETGROUPS_T *gids);
+#define sudo_setgroups(_a, _b) sudo_setgroups_v1((_a), (_b))
+
+/* strsplit.c */
+sudo_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 */
+sudo_dso_public int sudo_strtobool_v1(const char *str);
+#define sudo_strtobool(_a) sudo_strtobool_v1((_a))
+
+/* strtonum.c */
+/* Not versioned for historical reasons. */
+sudo_dso_public long long sudo_strtonum(const char *, long long, long long, const char **);
+/* Not currently exported. */
+long long sudo_strtonumx(const char *str, long long minval, long long maxval, char **endp, const char **errstrp);
+
+/* strtoid.c */
+sudo_dso_public id_t sudo_strtoid_v1(const char *str, const char *sep, char **endp, const char **errstr);
+sudo_dso_public id_t sudo_strtoid_v2(const char *str, const char **errstr);
+#define sudo_strtoid(_a, _b) sudo_strtoid_v2((_a), (_b))
+sudo_dso_public id_t sudo_strtoidx_v1(const char *str, const char *sep, char **endp, const char **errstr);
+#define sudo_strtoidx(_a, _b, _c, _d) sudo_strtoidx_v1((_a), (_b), (_c), (_d))
+
+/* strtomode.c */
+sudo_dso_public int sudo_strtomode_v1(const char *cp, const char **errstr);
+sudo_dso_public mode_t sudo_strtomode_v2(const char *cp, const char **errstr);
+#define sudo_strtomode(_a, _b) sudo_strtomode_v2((_a), (_b))
+
+/* sudo_printf.c */
+extern int (*sudo_printf)(int msg_type, const char * restrict fmt, ...);
+
+/* term.c */
+#define SUDO_TERM_ISIG 0x01U
+#define SUDO_TERM_OFLAG 0x02U
+sudo_dso_public bool sudo_isatty_v1(int fd, struct stat *sbp);
+#define sudo_isatty(_a, _b) sudo_isatty_v1((_a), (_b))
+sudo_dso_public bool sudo_term_cbreak_v1(int fd);
+#define sudo_term_cbreak(_a) sudo_term_cbreak_v1((_a))
+sudo_dso_public bool sudo_term_copy_v1(int src, int dst);
+#define sudo_term_copy(_a, _b) sudo_term_copy_v1((_a), (_b))
+sudo_dso_public bool sudo_term_noecho_v1(int fd);
+#define sudo_term_noecho(_a) sudo_term_noecho_v1((_a))
+sudo_dso_public bool sudo_term_raw_v1(int fd, unsigned int flags);
+#define sudo_term_raw(_a, _b) sudo_term_raw_v1((_a), (_b))
+sudo_dso_public bool sudo_term_restore_v1(int fd, bool flush);
+#define sudo_term_restore(_a, _b) sudo_term_restore_v1((_a), (_b))
+sudo_dso_public bool sudo_term_is_raw_v1(int fd);
+#define sudo_term_is_raw(_a) sudo_term_is_raw_v1((_a))
+
+/* ttyname_dev.c */
+sudo_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 */
+sudo_dso_public void sudo_get_ttysize_v1(int *rowp, int *colp);
+sudo_dso_public void sudo_get_ttysize_v2(int fd, int *rowp, int *colp);
+#define sudo_get_ttysize(_a, _b, _c) sudo_get_ttysize_v2((_a), (_b), (_c))
+
+/* uuid.c */
+sudo_dso_public void sudo_uuid_create_v1(unsigned char uuid_out[16]);
+#define sudo_uuid_create(_a) sudo_uuid_create_v1((_a))
+sudo_dso_public char *sudo_uuid_to_string_v1(unsigned char uuid[16], char *dst, size_t dstsiz);
+#define sudo_uuid_to_string(_a, _b, _c) sudo_uuid_to_string_v1((_a), (_b), (_c))
+
+#endif /* SUDO_UTIL_H */