diff options
310 files changed, 27458 insertions, 2473 deletions
diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml new file mode 100644 index 00000000..51993106 --- /dev/null +++ b/.builds/freebsd.yml @@ -0,0 +1,23 @@ +image: freebsd/latest +packages: +- autoconf +- automake +- bison +- libevent +- libtool +- libressl +- libasr +- py37-ansible +- db6 +- python3 +- python37 +- python +sources: +- https://github.com/OpenSMTPD/OpenSMTPD +- https://github.com/OpenSMTPD/ci +tasks: +- ansible: | + ls -lah + BUILD_DIR=$(pwd)/OpenSMTPD + cd ci/ansible + ansible-playbook test.yml --inventory inventory/freebsd --skip-tags checkout --extra-vars "build_dir=$BUILD_DIR" diff --git a/.builds/openbsd.yml b/.builds/openbsd.yml new file mode 100644 index 00000000..97b9634c --- /dev/null +++ b/.builds/openbsd.yml @@ -0,0 +1,16 @@ +image: openbsd/6.8 +packages: +- autoconf-2.69p3 +- automake-1.14.1p1 +- libtool +sources: +- https://github.com/OpenSMTPD/OpenSMTPD +tasks: +- configure: | + cd OpenSMTPD + export AUTOCONF_VERSION=2.69 + ./bootstrap + ./configure +- build: | + cd OpenSMTPD + make diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index a1f700fd..36cb5950 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,3 +1,3 @@ -#github: [poolpOrg] +github: [poolpOrg] patreon: gilles diff --git a/.github/workflows/alpine.yml b/.github/workflows/alpine.yml new file mode 100644 index 00000000..23816eab --- /dev/null +++ b/.github/workflows/alpine.yml @@ -0,0 +1,15 @@ +name: Alpine Linux (amd64 musl openssl) +on: + push: + branches: + - master + pull_request: + branches: + - master +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Alpine Linux (amd64 musl openssl) + run: docker build . --file ci/docker/Dockerfile.alpine --tag opensmtpd:alpine diff --git a/.github/workflows/arch.yml b/.github/workflows/arch.yml new file mode 100644 index 00000000..2ee226a2 --- /dev/null +++ b/.github/workflows/arch.yml @@ -0,0 +1,15 @@ +name: Archlinux (amd64 glibc libressl) +on: + push: + branches: + - master + pull_request: + branches: + - master +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Archlinux (amd64 glibc libressl) + run: docker build . --file ci/docker/Dockerfile.archlinux --tag opensmtpd:archlinux diff --git a/.github/workflows/clang.yml b/.github/workflows/clang.yml new file mode 100644 index 00000000..15ccd31d --- /dev/null +++ b/.github/workflows/clang.yml @@ -0,0 +1,35 @@ +name: Clang Scan + +on: + schedule: + - cron: '0 23 * * *' # Daily at 23:00 UTC +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: install dependencies + run: | + wget https://apt.llvm.org/llvm.sh + chmod +x llvm.sh + sudo ./llvm.sh 9 + sudo apt install \ + clang-tools-9 \ + libasr-dev \ + libevent-dev \ + libssl-dev \ + bison + - uses: actions/checkout@v1 + - name: run scan-build + run: | + export PATH=$PATH:/usr/lib/llvm-9/bin/ + CLANG_SCAN_BADGE_REQUIRED=1 ci/scripts/clang_scan.sh + - name: publish results + run: | + aws s3 rm --recursive s3://opensmtpd/reports/clang/ + cd clang-report + cd "$( find . -type d | sort | tail -n1 )" + aws s3 sync . s3://opensmtpd/reports/clang + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION }} diff --git a/.github/workflows/coverity.yml b/.github/workflows/coverity.yml new file mode 100644 index 00000000..d25bb4f7 --- /dev/null +++ b/.github/workflows/coverity.yml @@ -0,0 +1,15 @@ +name: Coverity Scan +on: + schedule: + - cron: '0 23 * * *' # Daily at 23:00 UTC +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: dependencies + run: sudo apt install libasr-dev libevent-dev libssl-dev bison + - name: scan + run: sh ci/scripts/coverity_scan.sh + env: + token: ${{ secrets.COVERITY_SCAN_TOKEN }} diff --git a/.github/workflows/fedora-gcc10.yml b/.github/workflows/fedora-gcc10.yml new file mode 100644 index 00000000..698d9d5b --- /dev/null +++ b/.github/workflows/fedora-gcc10.yml @@ -0,0 +1,15 @@ +name: Fedora (amd64 glibc openssl gcc10) +on: + push: + branches: + - master + pull_request: + branches: + - master +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Fedora (amd64 glibc openssl gcc10) + run: docker build . --file ci/docker/Dockerfile.fedora-gcc10 --tag opensmtpd:fedora-gcc10 diff --git a/.github/workflows/fedora.yml b/.github/workflows/fedora.yml new file mode 100644 index 00000000..448a6eda --- /dev/null +++ b/.github/workflows/fedora.yml @@ -0,0 +1,15 @@ +name: Fedora (amd64 glibc openssl) +on: + push: + branches: + - master + pull_request: + branches: + - master +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Fedora (amd64 glibc openssl) + run: docker build . --file ci/docker/Dockerfile.fedora --tag opensmtpd:fedora diff --git a/.github/workflows/macos-latest.yml b/.github/workflows/macos-latest.yml new file mode 100644 index 00000000..1960a39a --- /dev/null +++ b/.github/workflows/macos-latest.yml @@ -0,0 +1,23 @@ +name: macOS Catalina 10.15 +on: + push: + branches: + - master + pull_request: + branches: + - master +jobs: + build: + runs-on: macos-latest + steps: + - uses: actions/checkout@v1 + - name: macOS Catalina 10.15 + run: | + brew install automake libevent openssl@1.1 bison + export PATH="/usr/local/opt/bison/bin:$PATH" + export LDFLAGS="-L/usr/local/opt/openssl@1.1/lib -L/usr/local/opt/bison/lib" + export CFLAGS=-I/usr/local/opt/openssl@1.1/include + ./bootstrap + ./configure --with-gnu-ld --sysconfdir=/etc/mail --with-auth-pam + make + sudo make install diff --git a/.github/workflows/ubuntu-gcc10.yml b/.github/workflows/ubuntu-gcc10.yml new file mode 100644 index 00000000..e593df39 --- /dev/null +++ b/.github/workflows/ubuntu-gcc10.yml @@ -0,0 +1,15 @@ +name: Ubuntu (amd64 glibc openssl gcc10) +on: + push: + branches: + - master + pull_request: + branches: + - master +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Ubuntu (amd64 glibc openssl gcc10) + run: docker build . --file ci/docker/Dockerfile.ubuntu-gcc10 --tag opensmtpd:ubuntu-gcc10 diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml new file mode 100644 index 00000000..c4bfd437 --- /dev/null +++ b/.github/workflows/ubuntu.yml @@ -0,0 +1,15 @@ +name: Ubuntu (amd64 glibc openssl) +on: + push: + branches: + - master + pull_request: + branches: + - master +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - name: Ubuntu (amd64 glibc openssl) + run: docker build . --file ci/docker/Dockerfile.ubuntu --tag opensmtpd:ubuntu @@ -4,7 +4,6 @@ *.in *.out *.log -*~ .#* .deps m4 @@ -26,6 +25,8 @@ stamp-h1 ylwrap tags obj + +#Other VCS files CVS/ smtpd/CVS smtpd/smtpctl/CVS @@ -34,3 +35,22 @@ smtpd/mail/CVS smtpd/mail/CVS smtpd/mail/*/CVS smtpd/smtpctl/CVS/* + +#Editor temporary files +*~ +.idea + +# Coverity +cov-int/ +cov-analysis-linux64/ +cov-analysis-linux64.tgz +opensmtpd.tgz + +# Clang scan-build +clang-report/ + + +# TLS certs +open.smtpd.cert +open.smtpd.csr +open.smtpd.key @@ -1,6 +1,20 @@ -# Release 6.6.1 (2019-11-06) +# Release 6.6.3p1 (2020-02-10) -## Changes in this release (since 6.6.0) +Following the 6.6.2p1 release, various improvements were done in OpenBSD -current to mitigate the risk of similar bugs. + +This release back-ports them to the portable version of OpenSMTPD. + +# Release 6.6.2p1 (2020-01-28) + +This is CRITICAL security bugfix for +[CVE-2020-7247](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-7247) + +Read more details in +[this blog post](https://poolp.org/posts/2020-01-30/opensmtpd-advisory-dissected/) + +# Release 6.6.1p1 (2019-11-06) + +## Changes in this release (since 6.6.0p1) This is a bugfix release. No new features were added. @@ -9,6 +23,7 @@ This is a bugfix release. No new features were added. - Fixed crash in `arc4random` caused by differences in OpenSSL vs LibreSSL compatibility layer plumbing [#958](https://github.com/OpenSMTPD/OpenSMTPD/issues/958) - Fixed issue where `from any` rules never matched by IPv6 sources [#969](https://github.com/OpenSMTPD/OpenSMTPD/issues/969) - Fixed crash that happened during mail relay on musl distros [#929](https://github.com/OpenSMTPD/OpenSMTPD/issues/929) +- Added reference aliases file in `etc/aliases` - Fixed multiple compilation warnings [#965](https://github.com/OpenSMTPD/OpenSMTPD/issues/965) [#966](https://github.com/OpenSMTPD/OpenSMTPD/issues/966) @@ -19,7 +34,7 @@ This is a bugfix release. No new features were added. -# Release 6.6.0 (2019-10-26) +# Release 6.6.0p1 (2019-10-26) ## Dependencies note: diff --git a/INSTALL b/INSTALL new file mode 100644 index 00000000..d3c5b40a --- /dev/null +++ b/INSTALL @@ -0,0 +1,237 @@ +Installation Instructions +************************* + +Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, +2006, 2007 Free Software Foundation, Inc. + +This file is free documentation; the Free Software Foundation gives +unlimited permission to copy, distribute and modify it. + +Basic Installation +================== + +Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + +The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package. + + 4. Type `make install' to install the programs and any data files and + documentation. + + 5. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 6. Often, you can also type `make uninstall' to remove the installed + files again. + +Compilers and Options +===================== + +Some systems require unusual options for compilation or linking that the +`configure' script does not know about. Run `./configure --help' for +details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + +You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + +Installation Names +================== + +By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX'. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + +Optional Features +================= + +Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + +Specifying the System Type +========================== + +There may be some features `configure' cannot figure out automatically, +but needs to determine by the type of machine the package will run on. +Usually, assuming the package is built to be run on the _same_ +architectures, `configure' can figure that out, but if it prints a +message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + +If you want to set default values for `configure' scripts to share, you +can create a site shell script called `config.site' that gives default +values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + +Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + +`configure' recognizes the following options to control how it operates. + +`--help' +`-h' + Print a summary of the options to `configure', and exit. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..f1cea087 --- /dev/null +++ b/LICENSE @@ -0,0 +1,342 @@ +This file is part of the OpenSMTPD software. + +The licences which components of this software fall under are as +follows. First, we will summarize and say that all components +are under a BSD licence, or a licence more free than that. + +OpenSMTPD contains no GPL code. + +Portable OpenSMTPD is divided in 4 parts: +- Original OpenSMTPD +- mail.local +- openbsd-compat +- smtpctl encrypt sub command + + + +OpenSMTPD +========= + + +1) Almost all code is licensed under an ISC-style license, to the following + copyright holders: + + Gilles Chehade + Eric Faurot + Jacek Masiulaniec + Pierre-Yves Ritschard + Henning Brauer + Esben Norby + Markus Friedl + Daniel Hartmeier + Theo de Raadt + Claudio Jeker + Reyk Floeter + Janne Johansson + Alexander Schrijver + Marc Balmer + Ashish Shukla + Ryan Kavanagh + Charles Longeau + + +2) ssl_privsep.c + + /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + + /* + * SSL operations needed when running in a privilege separated environment. + * Adapted from openssl's ssl_rsa.c by Pierre-Yves Ritschard . + */ + + + +mail.local +========== + + +1) mail.local is covered by a 3-clause BSD license, to the following + copyright holders: + + The Regents of the University of California. + David Mazieres + Theo de Raadt + + * 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. + + + +openbsd-compat +============== + + +Most of the OpenBSD compatibility layer is based on the work by Damien Miller for +Portable OpenSSH. + +1) Almost all code is licensed under an ISC-style license, to the following + copyright holders: + + Internet Software Consortium. + David Mazieres + Damien Miller + Markus Friedl + Todd C. Miller + Henning Brauer + Pierre-Yves Ritschard + Reyk Floeter + Theo de Raadt + Ted Unangst + Charles Longeau + + * 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. + + +2) base64.{c,h} in addition to beeing covered by an ISC-style licence, is also + covered by this one: + + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + + +3) Portable OpenSMTPD includes code under the 2-clause BSD license, from the + following copyright holders: + + Ben Lindstrom + Damien Miller + Marc Espie + Tim Rice + The NetBSD Foundation, Inc. + Jason R. Thorpe? + Niels Provos + + * 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 AUTHOR ``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 AUTHOR 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. + + +4) Some code is under a 3-clause BSD license, from the + following copyright holders: + + The Regents of the University of California. + Ian F. Darwin + Damien Miller + Eric P. Allman + + * 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. + + +5) Some code is under a 4-clause BSD license, from the + following copyright holder: + + Christos Zoulas + + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christos Zoulas. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + + +6) includes.h, log.h, setresguid.c, xmalloc.c, xmalloc.c + + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + + +7) chacha_private.h + +D. J. Bernstein +Public domain. + + +8) bootstrap (only there in the git repository) + + # Copyright (c) 2002-2011 Sam Hocevar <sam@hocevar.net> + # + # This program is free software. It comes without any warranty, to + # the extent permitted by applicable law. You can redistribute it + # and/or modify it under the terms of the Do What The Fuck You Want + # To Public License, Version 2, as published by Sam Hocevar. See + # http://sam.zoy.org/wtfpl/COPYING for more details. + + + +smtpctl encrypt sub command +=========================== + + +smtpctl encrypt sub command is licensed under an ISC-style license, to the +following copyright holders: + + Sunil Nimmagadda + Gilles Chehade diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..7d95a5b8 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,3 @@ +SUBDIRS = openbsd-compat mk contrib + +ACLOCAL_AMFLAGS = -I m4 diff --git a/README b/README deleted file mode 100644 index 0e223143..00000000 --- a/README +++ /dev/null @@ -1,162 +0,0 @@ -Preliminary note -================ - -OpenSMTPD is a FREE implementation of the server-side SMTP protocol as -defined by RFC 5321, with some additional standard extensions. - -It allows ordinary machines to exchange e-mails with other systems -speaking the SMTP protocol. - -OpenSMTPD runs on top of the OpenBSD operating system but also has a -portable version that can build and run on several systems, including: - -* Linux -* FreeBSD -* NetBSD -* DragonFly - -For more information: http://www.opensmtpd.org/portable.html - -People interested about OpenSMTPD are encouraged to subscribe to our -mailing list: http://www.opensmtpd.org/list.html - -and to join the IRC channel: #OpenSMTPD @ irc.freenode.net - -Also note that we have a wiki at -https://github.com/OpenSMTPD/OpenSMTPD/wiki that you are encouraged to -contribute to. - -Cheers! - - -How to build, configure and use Portable OpenSMTPD -================================================== - -Dependencies ------------- - -Portable OpenSMTPD relies on: - * autoconf (http://www.gnu.org/software/autoconf/) - * automake (http://www.gnu.org/software/automake/) - * bison (http://www.gnu.org/software/bison/) - or byacc (http://invisible-island.net/byacc/byacc.html) - * libevent (http://libevent.org/) - * libtool (http://www.gnu.org/software/libtool/) - * openssl (http://www.openssl.org/) - * libasr (https://opensmtpd.org/archives/libasr-1.0.2.tar.gz) - - -Get the source --------------- - - git clone -b portable git://github.com/OpenSMTPD/OpenSMTPD.git opensmtpd - - -Build ------ - - cd opensmtpd* - ./bootstrap # Only if you build from git sources - ./configure - make - sudo make install - -# Special notes for FreeBSD/DragonFlyBSD/Mac OS X: - -Please launch configure with special directive about libevent and -libasr directory: - -# FreeBSD / DragonFlyBSD: - - ./configure --with-libasr=/usr/local - -# Mac OS X: - - ./configure --with-libevent=/opt/local --with-libasr=/opt/local - - -Install -------- - - sudo make install - - -Configure /etc/smtpd.conf -------------------------- - -Please have a look at the complete format description of smtpd.conf -configuration file (http://opensmtpd.org/smtpd.conf.5.html) - - -Add OpenSMTPD users -------------------- - -To operate, OpenSMTPD requires at least one user, by default _smtpd; and -preferably two users, by default _smtpd and _smtpq. - -Using two users instead of one will increase security by a large factor -so... if you want to voluntarily reduce security or you have absolute -more faith in our code than we do, by all means use one. - - -The instructions below assume the default users however, the configure -script allows overriding these using the options: ---with-user-smtpd, --with-user-queue, and --with-group-queue. - - -# NetBSD, Linux (Debian, Arch Linux, ...) - - mkdir /var/empty - useradd -c "SMTP Daemon" -d /var/empty -s /sbin/nologin _smtpd - useradd -c "SMTPD Queue" -d /var/empty -s /sbin/nologin _smtpq - -# DragonFlyBSD, FreeBSD - - pw useradd _smtpd -c "SMTP Daemon" -d /var/empty -s /sbin/nologin - pw useradd _smtpq -c "SMTPD Queue" -d /var/empty -s /sbin/nologin - -# Mac OS X - -First we need a group with an unused GID below 500, list the current -ones used: - - /usr/bin/dscl . -list /Groups PrimaryGroupID | sort -n -k2,2 - -Add a group - here we have picked 444: - - /usr/bin/sudo /usr/bin/dscl . -create /Groups/_smtpd - PrimaryGroupID 444 - -Then the user. Again we need an unused UID below 500, list the current -ones used: - - /usr/bin/dscl . -list /Users UniqueID | sort -n -k2,2 - -Add a user - here we have picked 444: - - /usr/bin/sudo /usr/bin/dscl . -create /Users/_smtpd UniqueID 444 - /usr/bin/sudo /usr/bin/dscl . -delete /Users/_smtpd AuthenticationAuthority - /usr/bin/sudo /usr/bin/dscl . -delete /Users/_smtpd PasswordPolicyOptions - /usr/bin/sudo /usr/bin/dscl . -delete /Users/_smtpd dsAttrTypeNative:KerberosKeys - /usr/bin/sudo /usr/bin/dscl . -delete /Users/_smtpd dsAttrTypeNative:ShadowHashData - /usr/bin/sudo /usr/bin/dscl . -create /Users/_smtpd RealName "SMTP Daemon" - /usr/bin/sudo /usr/bin/dscl . -create /Users/_smtpd Password "*" - /usr/bin/sudo /usr/bin/dscl . -create /Users/_smtpd PrimaryGroupID 444 - /usr/bin/sudo /usr/bin/dscl . -create /Users/_smtpd NFSHomeDirectory /var/empty - /usr/bin/sudo /usr/bin/dscl . -create /Users/_smtpd UserShell /usr/bin/false - -repeat for the _smtpq user. - - -Launch smtpd ------------- - -First, kill any running sendmail/exim/qmail/postfix or other. - -Then: - - smtpd - -or in debug and verbose mode - - smtpd -dv diff --git a/README.md b/README.md new file mode 100644 index 00000000..bc35c16b --- /dev/null +++ b/README.md @@ -0,0 +1,202 @@ +# OpenSMTPD + +[![Version](https://img.shields.io/badge/Version-6.7.1p1-brihtgreen.svg)](https://github.com/OpenSMTPD/OpenSMTPD/releases/tag/6.7.1p1) +[![Coverity Scan analysis](https://scan.coverity.com/projects/278/badge.svg)](https://scan.coverity.com/projects/opensmtpd-opensmtpd) +[![Packaging status](https://repology.org/badge/tiny-repos/opensmtpd.svg)](https://repology.org/project/opensmtpd/versions) +[![License: ISC](https://img.shields.io/badge/License-ISC-blue.svg)](https://www.isc.org/licenses/) + + + +OpenSMTPD is a FREE implementation of the server-side SMTP protocol as +defined by [RFC 5321](https://tools.ietf.org/html/rfc5321), with some +additional standard extensions. + +It allows ordinary machines to exchange e-mails with other systems +speaking the SMTP protocol. + +OpenSMTPD runs on top of the OpenBSD operating system but also has a +portable version that can build and run on several systems, including: + +* Linux +* FreeBSD +* NetBSD +* DragonFly + +For more information: http://www.opensmtpd.org/portable.html + +People interested about OpenSMTPD are encouraged to subscribe to our +mailing list: http://www.opensmtpd.org/list.html + +and to join the IRC channel: #OpenSMTPD @ irc.freenode.net + +The manual pages are available online at https://www.opensmtpd.org/manual.html, +which you are encouraged to contribute to. + +Cheers! + + +# How to build, configure and use Portable OpenSMTPD + +## Dependencies + +Portable OpenSMTPD relies on: + * autoconf (http://www.gnu.org/software/autoconf/) + * automake (http://www.gnu.org/software/automake/) + * bison (http://www.gnu.org/software/bison/) + or byacc (http://invisible-island.net/byacc/byacc.html) + * libevent (http://libevent.org/) + * libtool (http://www.gnu.org/software/libtool/) + * libressl (https://www.libressl.org/) + or OpenSSL (https://www.openssl.org/) + + +By default OpenSMTPD expects latest versions of all dependencies unless noted otherwise. + +Note that some distributions have different packages for a same library, you should always use the `-dev` or `-devel` package (for example, `libevent-dev` or `libevent-devel`) if you're going to build OpenSMTPD yourself. + + +## Get the source + + git clone git://github.com/OpenSMTPD/OpenSMTPD.git opensmtpd + + +## Build + + cd opensmtpd* + ./bootstrap # Only if you build from git sources + ./configure + make + sudo make install + +### Special notes for FreeBSD/DragonFlyBSD/Mac OS X: + +Please launch configure with special directive about libevent and +libasr directory: + +### FreeBSD / DragonFlyBSD: + + ./configure --with-libasr=/usr/local + +### MacOS: + + ./configure --with-libevent=/opt/local + +Though MacOS includes a copy of bison in the bases system, you will +need to install a more recent version from, e.g., MacPorts. + +## Install + + sudo make install + + +## Setup historical interface + +OpenSMTPD provides a single utility `smtpctl` to control the daemon and +the local submission subsystem. + +To accomodate systems that require historical interfaces such as `sendmail`, +`newaliases` or `makemap`, the `smtpctl` utility can operate in compatibility +mode if called with the historical name. + +On mailwrapper-enabled systems, this is achieved by editing `/etc/mailer.conf` +and adding the following lines: + + sendmail /usr/sbin/smtpctl + send-mail /usr/sbin/smtpctl + mailq /usr/sbin/smtpctl + makemap /usr/sbin/smtpctl + newaliases /usr/sbin/smtpctl + + +Whereas on systems that don't provide mailwrapper, it can be achieved by +setting the appropriate symbolic links: + + ln -s /usr/sbin/smtpctl sendmail + ln -s /usr/sbin/smtpctl send-mail + ln -s /usr/sbin/smtpctl mailq + ln -s /usr/sbin/smtpctl makemap + ln -s /usr/sbin/smtpctl newaliases + + +The OpenSMTPD project leaves it up to the package maintainers to setup the +links in their packages as it is very hard for us to accomodate all systems +with the prefered method in a clean way. + + +## Configure /etc/smtpd.conf + +Please have a look at the complete format description of smtpd.conf +configuration file (https://man.openbsd.org/smtpd.conf) + + +## Add OpenSMTPD users + +To operate, OpenSMTPD requires at least one user, by default `_smtpd`; and +preferably two users, by default `_smtpd` and `_smtpq`. + +Using two users instead of one will increase security by a large factor +so... if you want to voluntarily reduce security or you have absolute +more faith in our code than we do, by all means use one. + + +The instructions below assume the default users however, the configure +script allows overriding these using the options: +`--with-user-smtpd`, `--with-user-queue`, and `--with-group-queue`. + + +### NetBSD, Linux (Debian, Arch Linux, ...) + + mkdir /var/empty + useradd -c "SMTP Daemon" -d /var/empty -s /sbin/nologin _smtpd + useradd -c "SMTPD Queue" -d /var/empty -s /sbin/nologin _smtpq + +### DragonFlyBSD, FreeBSD + + pw useradd _smtpd -c "SMTP Daemon" -d /var/empty -s /sbin/nologin + pw useradd _smtpq -c "SMTPD Queue" -d /var/empty -s /sbin/nologin + +### Mac OS X + +First we need a group with an unused GID below `500`, list the current +ones used: + + /usr/bin/dscl . -list /Groups PrimaryGroupID | sort -n -k2,2 + +Add a group - here we have picked `444`: + + /usr/bin/sudo /usr/bin/dscl . -create /Groups/_smtpd + PrimaryGroupID 444 + +Then the user. Again we need an unused UID below `500`, list the current +ones used: + + /usr/bin/dscl . -list /Users UniqueID | sort -n -k2,2 + +Add a user - here we have picked `444`: + + /usr/bin/sudo /usr/bin/dscl . -create /Users/_smtpd UniqueID 444 + /usr/bin/sudo /usr/bin/dscl . -delete /Users/_smtpd AuthenticationAuthority + /usr/bin/sudo /usr/bin/dscl . -delete /Users/_smtpd PasswordPolicyOptions + /usr/bin/sudo /usr/bin/dscl . -delete /Users/_smtpd dsAttrTypeNative:KerberosKeys + /usr/bin/sudo /usr/bin/dscl . -delete /Users/_smtpd dsAttrTypeNative:ShadowHashData + /usr/bin/sudo /usr/bin/dscl . -create /Users/_smtpd RealName "SMTP Daemon" + /usr/bin/sudo /usr/bin/dscl . -create /Users/_smtpd Password "*" + /usr/bin/sudo /usr/bin/dscl . -create /Users/_smtpd PrimaryGroupID 444 + /usr/bin/sudo /usr/bin/dscl . -create /Users/_smtpd NFSHomeDirectory /var/empty + /usr/bin/sudo /usr/bin/dscl . -create /Users/_smtpd UserShell /usr/bin/false + +repeat for the `_smtpq` user. + + +## Launch smtpd + +First, kill any running sendmail/exim/qmail/postfix or other. + +Then: + + smtpd + +or in debug and verbose mode + + smtpd -dv + diff --git a/THANKS b/THANKS deleted file mode 100644 index 20f8fe99..00000000 --- a/THANKS +++ /dev/null @@ -1,19 +0,0 @@ -Jason McIntyre -Freddy Dissaux -Alexandre Lissy -Brad Arrington -Todd T. Fries -Francois Tigeot -Ashish Shukla -Jean-Loup Colautti -Rune Lynge -Jason A. Donenfeld -Gleb Kozyrev -Colin Didier -Ryan Kavanagh -Sunil Nimmagadda - - -Miod Vallat - -And of course Gilles Chehade and Eric Faurot for their work and continuous help. diff --git a/bootstrap b/bootstrap new file mode 100755 index 00000000..24e29440 --- /dev/null +++ b/bootstrap @@ -0,0 +1,151 @@ +#! /bin/sh + +# bootstrap: generic bootstrap/autogen.sh script for autotools projects +# +# Copyright (c) 2002-2011 Sam Hocevar <sam@hocevar.net> +# +# This program is free software. It comes without any warranty, to +# the extent permitted by applicable law. You can redistribute it +# and/or modify it under the terms of the Do What The Fuck You Want +# To Public License, Version 2, as published by Sam Hocevar. See +# http://sam.zoy.org/wtfpl/COPYING for more details. +# +# The latest version of this script can be found at the following place: +# http://caca.zoy.org/wiki/build + +# Die if an error occurs +set -e + +# Guess whether we are using configure.ac or configure.in +if test -f configure.ac; then + conffile="configure.ac" +elif test -f configure.in; then + conffile="configure.in" +else + echo "$0: could not find configure.ac or configure.in" + exit 1 +fi + +# Check for needed features +auxdir="`sed -ne 's/^[ \t]*A._CONFIG_AUX_DIR *([[ ]*\([^] )]*\).*/\1/p' $conffile`" +pkgconfig="`grep '^[ \t]*PKG_PROG_PKG_CONFIG' $conffile >/dev/null 2>&1 && echo yes || echo no`" +libtool="`grep '^[ \t]*A._PROG_LIBTOOL' $conffile >/dev/null 2>&1 && echo yes || echo no`" +header="`grep '^[ \t]*A._CONFIG_HEADER' $conffile >/dev/null 2>&1 && echo yes || echo no`" +makefile="`[ -f Makefile.am ] && echo yes || echo no`" +aclocalflags="`sed -ne 's/^[ \t]*ACLOCAL_AMFLAGS[ \t]*=//p' Makefile.am 2>/dev/null || :`" + +# Check for automake +amvers="no" +for v in 16.1 16 15 14 13; do + if automake-1.${v} --version >/dev/null 2>&1; then + amvers="-1.${v}" + break + elif automake1.${v} --version >/dev/null 2>&1; then + amvers="1.${v}" + break + fi +done + +if test "${amvers}" = "no" && automake --version > /dev/null 2>&1; then + amvers="`automake --version | sed -e '1s/[^0-9]*//' -e q`" + if `echo "$amvers\n1.14" | sort -V | head -n 1 | grep -q "$amvers"`; then + amvers="no" + else + amvers="" + fi +fi + +if test "$amvers" = "no"; then + echo "$0: you need automake version 1.14 or later" + exit 1 +fi + +# Check for autoconf +acvers="no" +for v in "" "269" "-2.69"; do + if autoconf${v} --version >/dev/null 2>&1; then + acvers="${v}" + break + fi +done + +if test "$acvers" = "no"; then + echo "$0: you need autoconf" + exit 1 +fi + +# Check for libtool +if test "$libtool" = "yes"; then + libtoolize="no" + if glibtoolize --version >/dev/null 2>&1; then + libtoolize="glibtoolize" + else + for v in "16" "15" "" "14"; do + if libtoolize${v} --version >/dev/null 2>&1; then + libtoolize="libtoolize${v}" + break + fi + done + fi + + if test "$libtoolize" = "no"; then + echo "$0: you need libtool" + exit 1 + fi +fi + +# Check for pkg-config +if test "$pkgconfig" = "yes"; then + if ! pkg-config --version >/dev/null 2>&1; then + echo "$0: you need pkg-config" + exit 1 + fi +fi + +# Remove old cruft +for x in aclocal.m4 configure config.guess config.log config.sub config.cache config.h.in config.h compile libtool.m4 ltoptions.m4 ltsugar.m4 ltversion.m4 ltmain.sh libtool ltconfig missing mkinstalldirs depcomp install-sh; do rm -f $x autotools/$x; if test -n "$auxdir"; then rm -f "$auxdir/$x"; fi; done +rm -Rf autom4te.cache +if test -n "$auxdir"; then + if test ! -d "$auxdir"; then + mkdir "$auxdir" + fi + aclocalflags="${aclocalflags} -I $auxdir -I ." +fi + +# Honour M4PATH because sometimes M4 doesn't +save_IFS=$IFS +IFS=: +tmp="$M4PATH" +for x in $tmp; do + if test -n "$x"; then + aclocalflags="${aclocalflags} -I $x" + fi +done +IFS=$save_IFS + +# Explain what we are doing from now +set -x + +# Bootstrap package +if test "$libtool" = "yes"; then + ${libtoolize} --copy --force + if test -n "$auxdir" -a ! "$auxdir" = "." -a -f "ltmain.sh"; then + echo "$0: working around a minor libtool issue" + mv ltmain.sh "$auxdir/" + fi +fi + +aclocal${amvers} ${aclocalflags} +autoconf${acvers} +if test "$header" = "yes"; then + autoheader${acvers} +fi +if test "$makefile" = "yes"; then + #add --include-deps if you want to bootstrap with any other compiler than gcc + #automake${amvers} --add-missing --copy --include-deps + automake${amvers} --foreign --add-missing --copy +fi + +# Remove cruft that we no longer want +rm -Rf autom4te.cache + diff --git a/ci/COVERITY.MD5SUM b/ci/COVERITY.MD5SUM new file mode 100644 index 00000000..67b8a2de --- /dev/null +++ b/ci/COVERITY.MD5SUM @@ -0,0 +1 @@ +d0d7d7df9d6609e578f85096a755fb8f ./cov-analysis-linux64.tgz diff --git a/ci/README.md b/ci/README.md new file mode 100644 index 00000000..2eee08f1 --- /dev/null +++ b/ci/README.md @@ -0,0 +1,83 @@ +# Continuous Integration + +This directory contains CI/CD related scripts and resources + +CI/CD process leverages GitHub Actions as a primary automation platform since +up to 20 parallel workflows are available for opensource projects. + +On high level we have multiple dimensions to test: + + - OS Distribution + - TLS library implementation: libress, openssl + - libc implementations: glibc, musl + - compiler: gcc, clang (not yet tested) + +Within this matrix build tests, static code analysis, functional and +integration tests are planned. Currently only build tests and static code +analysis exist. Help is much needed with developing functional and integration +tests. + +## Directory strucuture + +- [docker](#dockerfiles) dockerfiles for various distributions +- [scripts](#scripts) useful scripts for ci/cd automation + + +## Design Considerations + +- Keep workflow yaml files and execution logic as separate as possible. + Reference ci scripts from workflow files to allow running same tests + locally, without depending on github. + + + +# Dockerfiles + +Dockerfiles in [docker](docker/) directory can be used for developing and +testing OpenSMTPD. These dockerfiles are intended to be used for dev/test +cycle ONLY and ARE NOT intended to be a delivery mechanism for end users and +should not be published on external resouces like DockerHub. Dockerfiles in +this folder can be used as a reference for package maintainers of various +distributions. + + +## Usage + +OpenSMTPD provides a set of dockerfiles for getting started with development +quickly locally or with GitHub's Actions. + +For each distribution there is a separate dockerfile with a distro name +suffixed. E.g. `Dockerfile.alpine` is a dockerfile that builds OpenSMTPD in +Alpine Linux environment. + +To build: + + docker build -f docker/Dockerfile.alpine -t opensmtpd-alpine + + +All configuration files that are in `/etc/mail` are taken from `etc/` directory. + + +To run the container that you've just built run: + + docker run --name smtpd_server -p 25:25 opensmtpd-alpine + + + +# Scripts + +Scripts to automate ci/cd actions: + +- [coverity_scan](scripts/coverity_scan.sh) - runs coverity scan and submits + the rusult to Coverity. See script contents for usage instructions. + +- [generate_certs](scripts/generate_certs.sh) - convenient script to quickly + generate some TLS certificates. Useful for testing. + +# Historical reference + +[Initial design discusstion](https://github.com/OpenSMTPD/OpenSMTPD/issues/947) + + + + diff --git a/ci/docker/Dockerfile.alpine b/ci/docker/Dockerfile.alpine new file mode 100644 index 00000000..2c7c66fc --- /dev/null +++ b/ci/docker/Dockerfile.alpine @@ -0,0 +1,50 @@ +FROM alpine:3.11 as build + +# creates /opensmtpd dir and makes all following commands to run in it +# https://docs.docker.com/engine/reference/builder/#workdir +WORKDIR /opensmtpd + +# install necessary packages +RUN apk add --no-cache \ + autoconf \ + automake \ + bison \ + ca-certificates \ + fts-dev \ + gcc \ + fts \ + libevent-dev \ + libtool \ + libtool \ + linux-pam-dev \ + make \ + musl-dev \ + libressl \ + libressl-dev \ + zlib-dev + +# create users and directories +# note: alpine uses busybox and useradd is not available there +# also long flags are not available too, so sorry for the +RUN mkdir -p /var/lib/opensmtpd/empty \ + && adduser _smtpd -h /var/lib/opensmtpd/empty/ -D -H -s /bin/false \ + && adduser _smtpq -h /var/lib/opensmtpd/empty/ -D -H -s /bin/false \ + && mkdir -p /var/spool/smtpd \ + && mkdir -p /var/mail \ + && mkdir -p /etc/mail \ + && chmod 711 /var/spool/smtpd + +# Copy contentes of the repo inside the container +# https://docs.docker.com/engine/reference/builder/#copy +COPY . /opensmtpd + +# build opensmtpd +RUN ./bootstrap \ + && ./configure \ + --with-gnu-ld \ + --sysconfdir=/etc/mail \ + --with-auth-pam \ + && make \ + && make install \ + && cp etc/aliases /etc/mail/aliases + diff --git a/ci/docker/Dockerfile.archlinux b/ci/docker/Dockerfile.archlinux new file mode 100644 index 00000000..dcd46684 --- /dev/null +++ b/ci/docker/Dockerfile.archlinux @@ -0,0 +1,62 @@ +FROM archlinux + +# Allow container to expose ports at runtime, if necessary +# https://docs.docker.com/engine/reference/#expose +EXPOSE 25 +EXPOSE 465 +EXPOSE 587 + +# creates /opensmtpd dir and makes all following commands to run in it +# https://docs.docker.com/engine/reference/builder/#workdir +WORKDIR /opensmtpd + +# install necessary packages +RUN pacman -Suy --noconfirm \ + base \ + make \ + m4 \ + grep \ + gcc \ + automake \ + autoconf \ + libtool \ + bison \ + gettext \ + libevent \ + libressl \ + pam \ + zlib + + +# create users and directories +RUN mkdir -p /var/lib/opensmtpd/empty \ + && useradd _smtpd \ + --home-dir /var/lib/opensmtpd/empty \ + --no-create-home \ + --shell /bin/false \ + && useradd _smtpq \ + --home-dir /var/lib/opensmtpd/empty \ + --no-create-home \ + --shell /bin/false \ + && mkdir -p /var/spool/smtpd \ + && mkdir -p /var/mail \ + && mkdir -p /etc/mail \ + && chmod 711 /var/spool/smtpd + + +# Copy contentes of the repo inside the container +# https://docs.docker.com/engine/reference/builder/#copy +COPY . /opensmtpd + + +# build opensmtpd +RUN ./bootstrap \ + && ./configure --with-gnu-ld \ + --sysconfdir=/etc/mail \ + --with-cflags='-I/usr/include/libressl -L/usr/lib/libressl -Wl,-rpath=/usr/lib/libressl' \ + --with-path-empty=/var/lib/opensmtpd/empty \ + --with-auth-pam \ + && make \ + && make install \ + && cp etc/aliases /etc/mail/aliases + diff --git a/ci/docker/Dockerfile.fedora b/ci/docker/Dockerfile.fedora new file mode 100644 index 00000000..d6c01bdf --- /dev/null +++ b/ci/docker/Dockerfile.fedora @@ -0,0 +1,51 @@ +FROM fedora:latest + +# Allow container to expose ports at runtime, if necessary +# https://docs.docker.com/engine/reference/#expose +EXPOSE 25 +EXPOSE 465 +EXPOSE 587 + +# creates /opensmtpd dir and makes all following commands to run in it +# https://docs.docker.com/engine/reference/builder/#workdir +WORKDIR /opensmtpd + +# install necessary packages +RUN dnf -y install \ + autoconf \ + automake \ + bison \ + @development-tools \ + libevent-devel \ + openssl-devel \ + libtool \ + pam-devel \ + zlib-devel + +# create users and directories +RUN mkdir -p /var/lib/opensmtpd/empty \ + && useradd _smtpd \ + --home-dir /var/lib/opensmtpd/empty \ + --no-create-home \ + --shell /bin/false \ + && useradd _smtpq \ + --home-dir /var/lib/opensmtpd/empty \ + --no-create-home \ + --shell /bin/false \ + && mkdir -p /var/spool/smtpd \ + && mkdir -p /var/mail \ + && mkdir -p /etc/mail \ + && chmod 711 /var/spool/smtpd + +# Copy contentes of the repo inside the container +# https://docs.docker.com/engine/reference/builder/#copy +COPY . /opensmtpd + +RUN ./bootstrap \ + && ./configure \ + --with-gnu-ld \ + --sysconfdir=/etc/mail \ + --with-auth-pam \ + && make \ + && make install \ + && cp etc/aliases /etc/mail/aliases diff --git a/ci/docker/Dockerfile.fedora-gcc10 b/ci/docker/Dockerfile.fedora-gcc10 new file mode 100644 index 00000000..1ab3bcbd --- /dev/null +++ b/ci/docker/Dockerfile.fedora-gcc10 @@ -0,0 +1,54 @@ +FROM fedora:32 + +# Allow container to expose ports at runtime, if necessary +# https://docs.docker.com/engine/reference/#expose +EXPOSE 25 +EXPOSE 465 +EXPOSE 587 + +# creates /opensmtpd dir and makes all following commands to run in it +# https://docs.docker.com/engine/reference/builder/#workdir +WORKDIR /opensmtpd + +# install necessary packages +RUN dnf -y install \ + autoconf \ + automake \ + bison \ + @development-tools \ + libevent-devel \ + openssl-devel \ + libtool \ + pam-devel \ + zlib-devel \ + libdb-devel + +# create users and directories +RUN mkdir -p /var/lib/opensmtpd/empty \ + && useradd _smtpd \ + --home-dir /var/lib/opensmtpd/empty \ + --no-create-home \ + --shell /bin/false \ + && useradd _smtpq \ + --home-dir /var/lib/opensmtpd/empty \ + --no-create-home \ + --shell /bin/false \ + && mkdir -p /var/spool/smtpd \ + && mkdir -p /var/mail \ + && mkdir -p /etc/mail \ + && chmod 711 /var/spool/smtpd + +# Copy contentes of the repo inside the container +# https://docs.docker.com/engine/reference/builder/#copy +COPY . /opensmtpd + +RUN export CC=gcc-10 CXX=g++-10 +RUN ./bootstrap \ + && ./configure \ + --with-gnu-ld \ + --sysconfdir=/etc/mail \ + --with-auth-pam \ + --with-table-db \ + && make \ + && make install \ + && cp etc/aliases /etc/mail/aliases diff --git a/ci/docker/Dockerfile.ubuntu b/ci/docker/Dockerfile.ubuntu new file mode 100644 index 00000000..6626033d --- /dev/null +++ b/ci/docker/Dockerfile.ubuntu @@ -0,0 +1,52 @@ +FROM ubuntu:latest + +# Allow container to expose ports at runtime, if necessary +# https://docs.docker.com/engine/reference/#expose +EXPOSE 25 +EXPOSE 465 +EXPOSE 587 + +# creates /opensmtpd dir and makes all following commands to run in it +# https://docs.docker.com/engine/reference/builder/#workdir +WORKDIR /opensmtpd + +# install necessary packages +RUN apt update \ + && apt install -y --no-install-recommends \ + autoconf \ + automake \ + bison \ + build-essential \ + libevent-dev \ + libssl-dev \ + libtool \ + libpam0g-dev \ + zlib1g-dev + +# create users and directories +RUN mkdir -p /var/lib/opensmtpd/empty \ + && useradd _smtpd \ + --home-dir /var/lib/opensmtpd/empty \ + --no-create-home \ + --shell /bin/false \ + && useradd _smtpq \ + --home-dir /var/lib/opensmtpd/empty \ + --no-create-home \ + --shell /bin/false \ + && mkdir -p /var/spool/smtpd \ + && mkdir -p /var/mail \ + && mkdir -p /etc/mail \ + && chmod 711 /var/spool/smtpd + +# Copy contentes of the repo inside the container +# https://docs.docker.com/engine/reference/builder/#copy +COPY . /opensmtpd + +RUN ./bootstrap \ + && ./configure \ + --with-gnu-ld \ + --sysconfdir=/etc/mail \ + --with-auth-pam \ + && make \ + && make install \ + && cp etc/aliases /etc/mail/aliases diff --git a/ci/docker/Dockerfile.ubuntu-gcc10 b/ci/docker/Dockerfile.ubuntu-gcc10 new file mode 100644 index 00000000..2ebbdf58 --- /dev/null +++ b/ci/docker/Dockerfile.ubuntu-gcc10 @@ -0,0 +1,54 @@ +FROM ubuntu:latest + +# Allow container to expose ports at runtime, if necessary +# https://docs.docker.com/engine/reference/#expose +EXPOSE 25 +EXPOSE 465 +EXPOSE 587 + +# creates /opensmtpd dir and makes all following commands to run in it +# https://docs.docker.com/engine/reference/builder/#workdir +WORKDIR /opensmtpd + +# install necessary packages +RUN apt update \ + && apt install -y --no-install-recommends \ + autoconf \ + automake \ + bison \ + build-essential \ + libevent-dev \ + libssl-dev \ + libtool \ + libpam0g-dev \ + zlib1g-dev \ + gcc-10 + +# create users and directories +RUN mkdir -p /var/lib/opensmtpd/empty \ + && useradd _smtpd \ + --home-dir /var/lib/opensmtpd/empty \ + --no-create-home \ + --shell /bin/false \ + && useradd _smtpq \ + --home-dir /var/lib/opensmtpd/empty \ + --no-create-home \ + --shell /bin/false \ + && mkdir -p /var/spool/smtpd \ + && mkdir -p /var/mail \ + && mkdir -p /etc/mail \ + && chmod 711 /var/spool/smtpd + +# Copy contentes of the repo inside the container +# https://docs.docker.com/engine/reference/builder/#copy +COPY . /opensmtpd + +RUN export CC=gcc-10 CXX=g++-10 +RUN ./bootstrap \ + && ./configure \ + --with-gnu-ld \ + --sysconfdir=/etc/mail \ + --with-auth-pam \ + && make \ + && make install \ + && cp etc/aliases /etc/mail/aliases diff --git a/ci/scripts/clang_scan.sh b/ci/scripts/clang_scan.sh new file mode 100755 index 00000000..714d55a7 --- /dev/null +++ b/ci/scripts/clang_scan.sh @@ -0,0 +1,60 @@ +#!/bin/sh +set -eu + +# Unconditionally go to the root level of the git repo. +# If you invoke it from outside of the repo go to +# the script location first +cd "$(dirname "$0")" +cd "$(git rev-parse --show-toplevel)" + +# Clang Scan script +# +# USAGE: +# - clang must be installed +# - make sure you have clean repository, +# e.g. git clean -ffdx +# - if you want to download github badge set CLANG_SCAN_BADGE_REQUIRED variable +# - Run script from anywhere inside the repository +# ./ci/scripts/clang_scan.sh +# or +# CLANG_SCAN_BADGE_REQUIRED=1 ./ci/scripts/clang_scan.sh +# + +if ! type scan-build > /dev/null; then + echo "clang scan-build is missing" + exit 1 +fi + +# Unconditionally go to the root level of the git repo. +# If you invoke it from outside of the repo go to +# the script location first +cd "$(dirname "$0")" +# This moves us to the root of the repo +cd "$(git rev-parse --show-toplevel)" + +# Get short SHA of the HEAD +sha=$(git rev-parse --short HEAD) + +results_dir=${CLANG_SCAN_RESULTS_DIR:-clang-report} +mkdir -p "$results_dir" + +# Build with scan-build +./bootstrap +./configure +scan-build -o "$results_dir" \ + --keep-empty \ + --html-title="OpenSMTPD $sha" make + + +set -x +# conditionally generate badge +if [ -z "${CLANG_SCAN_BADGE_REQUIRED:-}" ]; then + echo "Skipping badge generation" +else + echo "Generating badge" + . ci/scripts/imports/badge.sh + cd "$results_dir" + cd "$( find . -type d | sort | tail -n1 )" + issues_nr="$( find . -name "report-*" | wc -l)" + download_badge "$issues_nr" "clang analysis" "$(pwd)" 30 +fi diff --git a/ci/scripts/coverity_scan.sh b/ci/scripts/coverity_scan.sh new file mode 100755 index 00000000..ab302767 --- /dev/null +++ b/ci/scripts/coverity_scan.sh @@ -0,0 +1,77 @@ +#!/bin/sh +set -eu + +# Coverity Scan script +# Steps closely follow official documentation https://scan.coverity.com/download +# +# USAGE: provide coverity project token as 'token' environment variable and run +# token=abcdedf ./ci/scripts/coverity_scan.sh +# +# Or uncomment this line and put token here. But do not commit this to git. +# token="" +project_name="OpenSMTPD%2FOpenSMTPD" +cov_analysis_url="https://scan.coverity.com/download/cxx/linux64" +maintainer="ihor@antonovs.family" + +# Unconditionally go to the root level of the git repo. +# If you invoke it from outside of the repo go to +# the script location first +cd "$(dirname "$0")" +# This moves us to the root of the repo +cd "$(git rev-parse --show-toplevel)" + +# Get short SHA of the HEAD +sha=$(git rev-parse --short HEAD) + +# Download Coverity Build Tool if absent +set +x +# shellcheck disable=SC2154 +md5sum -c ./ci/COVERITY.MD5SUM || wget $cov_analysis_url \ + --post-data "token=$token&project=$project_name" \ + -O cov-analysis-linux64.tgz +set -x + +#Check MD5 +md5sum -c ./ci/COVERITY.MD5SUM + +# Extract Coverty Scan Tool +rm -rf ./cov-analysis-linux64 +mkdir -p cov-analysis-linux64 +tar xzf cov-analysis-linux64.tgz --strip 1 -C cov-analysis-linux64 + +# export PATH=$(pwd)/cov-analysis-linux64/bin:$PATH + +# Build with cov-build +./bootstrap +./configure +cov-analysis-linux64/bin/cov-build --dir cov-int make + +# Compress the rusults +tar czvf opensmtpd.tgz cov-int + + +# Submit the result to Coverity Scan +# Some parts are shamelessly taken from: +# https://scan.coverity.com/scripts/travisci_build_coverity_scan.sh +set +x +response=$(curl \ + --silent \ + --write-out "\n%{http_code}\n" \ + --form token="$token" \ + --form email="$maintainer" \ + --form file=@opensmtpd.tgz \ + --form version="portable-$sha" \ + --form description="daily scan" \ + "https://scan.coverity.com/builds?project=$project_name") +set -x + +status_code=$(echo "$response" | sed -n '$p') + +if [ "$status_code" != "200" ]; then + text=$(echo "$response" | sed '$d') + echo -e "Coverity Scan upload failed: $text" + exit 1 +fi + + + diff --git a/ci/scripts/generate_certs.sh b/ci/scripts/generate_certs.sh new file mode 100755 index 00000000..a9249c1e --- /dev/null +++ b/ci/scripts/generate_certs.sh @@ -0,0 +1,33 @@ +#!/bin/sh + +# Generate self-signed SSL certs +# Usage: ./generate_certs.sh + +days=3560 # 10 years +config="$(dirname "$0")/ssl.conf" +cert="open.smtpd.cert" +key="open.smtpd.key" +csr="open.smtpd.csr" + +# Key + CSR generation: +openssl req \ + -new \ + -x509 \ + -newkey rsa:2048 \ + -sha256 \ + -nodes \ + -keyout $key \ + -out $csr \ + -days $days \ + -config "$config" + +# Certificate generation: +openssl req \ + -new \ + -x509 \ + -newkey rsa:2048 \ + -days $days \ + -nodes \ + -config "$config" \ + -keyout $key \ + -out $cert diff --git a/ci/scripts/imports/badge.sh b/ci/scripts/imports/badge.sh new file mode 100644 index 00000000..d6cf0b3a --- /dev/null +++ b/ci/scripts/imports/badge.sh @@ -0,0 +1,72 @@ +#!/bin/sh +# Copyright 2019 Neovim Project Contributors (https://neovim.io/) +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Helper functions for getting badges. + +# Get code quality color. +# ${1}: Amount of bugs actually found. +# ${2}: Maximum number of bugs above which color will be red. Defaults to 20. +# ${3}: Maximum number of bugs above which color will be yellow. Defaults to +# $1 / 2. +# Output: 24-bit hexadecimal representation of the color (xxxxxx). +get_code_quality_color() { + bugs=$1 ; shift # shift will fail if there is no argument + max_bugs=${1:-20} + yellow_threshold=${2:-$(( max_bugs / 2 ))} + + red=255 + green=255 + blue=0 + + bugs=$(( bugs < max_bugs ? bugs : max_bugs)) + if test $bugs -ge "$yellow_threshold" ; then + green=$(( 255 - 255 * (bugs - yellow_threshold) / yellow_threshold )) + else + red=$(( 255 * bugs / yellow_threshold )) + fi + + printf "%02x%02x%02x" $red $green $blue +} + +# Get code quality badge. +# ${1}: Amount of bugs actually found. +# ${2}: Badge text. +# ${3}: Directory where to save badge to. +# ${3}: Maximum number of bugs above which color will be red. Defaults to 20. +# ${4}: Maximum number of bugs above which color will be yellow. Defaults to +# $1 / 2. +# Output: 24-bit hexadecimal representation of the color (xxxxxx). +download_badge() { + bugs=$1 ; shift + badge_text="$1" ; shift + reports_dir="$1" ; shift + max_bugs=${1:-20} + yellow_threshold=${2:-$(( max_bugs / 2 ))} + + code_quality_color="$( + get_code_quality_color $bugs $max_bugs $yellow_threshold)" + badge="${badge_text}-${bugs}-${code_quality_color}" + + rm -f "$reports_dir/badge.svg" + + response="$( + curl --tlsv1 "https://img.shields.io/badge/${badge}.svg" \ + -o"$reports_dir/badge.svg" 2>&1)" + + if ! grep -F 'xmlns="http://www.w3.org/2000/svg"' "$reports_dir/badge.svg" ; then + echo "Failed to download badge to $reports_dir: $response" + rm -f "$reports_dir/badge.svg" + fi +} diff --git a/ci/scripts/ssl.conf b/ci/scripts/ssl.conf new file mode 100644 index 00000000..eddfb7f8 --- /dev/null +++ b/ci/scripts/ssl.conf @@ -0,0 +1,23 @@ +[req] +default_bits = 2048 +prompt = no +default_md = sha256 +x509_extensions = v3_req +distinguished_name = dn + + +# Puffy the pufferfish +# https://en.wikipedia.org/wiki/Tetraodontidae +[dn] +C = AZ +ST = Chordata +L = Actinopterygii_Tetraodontiformes +O = Tetraodontoidei_Tetraodontidae +CN = Puffy + +[v3_req] +subjectAltName = @alt_names + +[alt_names] +DNS.1 = puffy.bsd +DNS.2 = puffy.mail diff --git a/configure.ac b/configure.ac new file mode 100644 index 00000000..d46aeae7 --- /dev/null +++ b/configure.ac @@ -0,0 +1,2072 @@ +# $Id: configure.ac,v 1.519 2013/03/22 01:49:15 dtucker Exp $ +# +# Copyright (c) 2016 Gilles Chehade <gilles@poolp.org> +# Copyright (c) 1999-2004 Damien Miller +# +# 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. + + +# +# WE NEED TO CLEANUP CONFIGURE.AC AND MAKE IT FOLLOW THE +# STANDARD LAYOUT ... +# +# 3.1.3 Standard configure.ac Layout +# +# https://www.gnu.org/software/autoconf/manual/autoconf-2.69/html_node/Autoconf-Input-Layout.html +# + + +# +# AUTOCONF REQUIREMENTS +# +AC_PREREQ(2.69) + + +# +# AC_INIT +# +AC_INIT([OpenSMTPD], + [portable], + [bugs@opensmtpd.org], + [opensmtpd], + [https://www.OpenSMTPD.org]) + +AM_INIT_AUTOMAKE([subdir-objects no-dependencies]) +LT_INIT + +# here we should test for variables set by libtool detection +if test "x$with_pic" != "xno"; then + CFLAGS="$CFLAGS ${pic_flag}" +fi + + +# +# PACKAGE INFORMATION +# +AC_LANG([C]) +AC_CONFIG_MACRO_DIR([m4]) +AC_CONFIG_HEADER([config.h]) +AC_PROG_CC +AC_CANONICAL_HOST +AC_C_BIGENDIAN + + +# +# CHECKS FOR PROGRAMS +# +AC_PROG_CPP +AC_PROG_INSTALL +AC_PROG_LIBTOOL +AC_PATH_PROG([AR], [ar]) +AC_PATH_PROG([CAT], [cat]) +AC_PATH_PROG([CHMOD], [chmod]) +AC_PATH_PROG([CHOWN], [chown]) +AC_PATH_PROG([ZCAT], [zcat]) +AC_PATH_PROG([SED], [sed]) +AC_PATH_PROG([TEST_MINUS_S_SH], [bash]) +AC_PATH_PROG([TEST_MINUS_S_SH], [ksh]) +AC_PATH_PROG([TEST_MINUS_S_SH], [sh]) +AC_PATH_PROG([SH], [sh]) +AC_PATH_PROG([GROFF], [groff]) +AC_PATH_PROG([NROFF], [nroff]) +AC_PATH_PROG([MANDOC], [mandoc]) +AC_PROG_YACC + +AC_SUBST([ZCAT]) + + +if test -z "$AR"; then + AC_MSG_ERROR([*** 'ar' missing, please install or fix your \$PATH ***]) +fi + +if test -z "$LD"; then + LD=$CC +fi +AC_SUBST([LD]) + +dnl select manpage formatter +if test -n "$MANDOC"; then + MANFMT="$MANDOC" +elif test -n "$NROFF"; then + MANFMT="$NROFF -mandoc" +elif test -n "$GROFF"; then + MANFMT="$GROFF -mandoc -Tascii" +else + AC_MSG_WARN([no manpage formatted found]) + MANFMT="false" +fi +AC_SUBST([MANFMT]) + + +# +# CHECKS FOR LIBRARIES +# + + + +# +# CHECKS FOR HEADERS +# +AC_CHECK_HEADERS([ \ + arpa/nameser_compat.h \ + crypt.h \ + dirent.h \ + err.h \ + fcntl.h \ + getopt.h \ + grp.h \ + libgen.h \ + limits.h \ + maillock.h \ + mach/mach_time.h \ + ndir.h \ + netdb.h \ + pam/pam_appl.h \ + paths.h \ + security/pam_appl.h \ + shadow.h \ + sys/cdefs.h \ + sys/dir.h \ + sys/file.h \ + sys/mount.h \ + sys/ndir.h \ + sys/pstat.h \ + sys/statfs.h \ + sys/time.h \ + sys/un.h \ + time.h \ + ucred.h \ + util.h \ + vis.h +]) + +AM_CONDITIONAL([NEED_ERR_H], [test x$HAVE_ERR_H = x1]) +AM_CONDITIONAL([SUPPORT_PATHS_H], [test x$HAVE_PATHS_H = x1]) + +# NetBSD requires sys/types.h before login_cap.h +AC_CHECK_HEADERS([login_cap.h], [], [], [ +#include <sys/types.h> +]) + +# older BSDs need sys/param.h before sys/mount.h +AC_CHECK_HEADERS([sys/mount.h], [], [], [ +#include <sys/param.h> +]) + +AC_CHECK_HEADERS([bsd/libutil.h libutil.h]) + +AC_CHECK_HEADER([fts.h], + [], + [AC_MSG_ERROR([*** fts.h missing - please install libfts ***])], + [ +#include <sys/types.h> +#include <sys/stat.h> +]) + +need_libasr=no +AC_CHECK_HEADER([asr.h], + [], + [need_libasr=yes], + [ +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +]) +AM_CONDITIONAL([NEED_LIBASR], [test x"$need_libasr" = x"yes"]) + +# +# CHECKS FOR TYPES +# +AC_CHECK_TYPES([long long, unsigned long long, long double, u_int, u_char]) +AC_CHECK_SIZEOF([short int], [2]) +AC_CHECK_SIZEOF([int], [4]) +AC_CHECK_SIZEOF([long int], [4]) +AC_CHECK_SIZEOF([long long int], [8]) + +AC_TYPE_INT8_T +AC_TYPE_INT16_T +AC_TYPE_INT32_T +AC_TYPE_INT64_T +AC_TYPE_UINT8_T +AC_TYPE_UINT16_T +AC_TYPE_UINT32_T +AC_TYPE_UINT64_T +AC_TYPE_INTPTR_T +AC_TYPE_INTMAX_T +AC_TYPE_UINTPTR_T +AC_TYPE_UINTMAX_T +AC_TYPE_SIZE_T +AC_TYPE_SSIZE_T +AC_TYPE_OFF_T +AC_TYPE_MODE_T +AC_TYPE_PID_T +AC_TYPE_UID_T + +TYPE_SOCKLEN_T + +AC_CHECK_TYPES([sig_atomic_t], [], [], [ +#include <signal.h> +]) +AC_CHECK_TYPES([fsblkcnt_t, fsfilcnt_t], [], [], [ +#include <sys/types.h> +#ifdef HAVE_SYS_BITYPES_H +#include <sys/bitypes.h> +#endif +#ifdef HAVE_SYS_STATFS_H +#include <sys/statfs.h> +#endif +#ifdef HAVE_SYS_STATVFS_H +#include <sys/statvfs.h> +#endif +]) + +AC_CHECK_TYPES([in_addr_t, in_port_t], [], [], [ +#include <sys/types.h> +#include <netinet/in.h> +]) + +AC_CHECK_TYPES([sa_family_t], [], [], [ +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +]) + +AC_CHECK_TYPES([struct timespec]) +AC_CHECK_TYPES([struct ifgroupreq]) +AC_CHECK_TYPES([struct sockaddr_storage], [], [], [ +#include <sys/types.h> +#include <sys/socket.h> +]) +AC_CHECK_TYPES([struct sockaddr_in6], [], [], [ +#include <sys/types.h> +#include <netinet/in.h> +]) +AC_CHECK_TYPES([struct in6_addr], [], [], [ +#include <sys/types.h> +#include <netinet/in.h> +]) +AC_CHECK_TYPES([struct addrinfo], [], [], [ +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> +]) +AC_CHECK_TYPES([struct timeval], [], [], [ +#include <sys/time.h> +]) + +AC_CHECK_DECL([LLONG_MAX], [have_llong_max=1], , [#include <limits.h>]) + + +# +# CHECKS FOR STRUCTURES +# +AC_CHECK_MEMBERS([struct sockaddr_in6.sin6_scope_id], [], [], [ +#ifdef HAVE_SYS_TYPES_H +#include <sys/types.h> +#endif +#include <netinet/in.h> +]) + +AC_CHECK_MEMBERS([struct passwd.pw_gecos, struct passwd.pw_class, +struct passwd.pw_change, struct passwd.pw_expire], +[], [], [ +#include <sys/types.h> +#include <pwd.h> +]) + +AC_CHECK_MEMBERS([struct stat.st_flags], , , + [ #include <sys/types.h> + #include <sys/stat.h> ] +) + +AC_CHECK_MEMBERS([struct stat.st_mtim], , , + [ #include <sys/types.h> + #include <sys/stat.h> ] +) + +AC_CHECK_MEMBERS([struct stat.st_mtimespec], , , + [ #include <sys/types.h> + #include <sys/stat.h> ] +) + +AC_CHECK_MEMBERS([struct sockaddr.sa_len], , , + [ #include <netdb.h> + #include <netinet/in.h> + #include <sys/socket.h> ] +) + +AC_CHECK_MEMBERS([struct sockaddr_storage.ss_len], , , + [ #include <netdb.h> + #include <netinet/in.h> + #include <sys/socket.h> ] +) + +AC_CHECK_MEMBERS([struct sockaddr_in.sin_len], , , + [ #include <netdb.h> + #include <netinet/in.h> + #include <sys/socket.h> ] +) + +AC_CHECK_MEMBERS([struct sockaddr_in6.sin6_len], , , + [ #include <netdb.h> + #include <netinet/in.h> + #include <sys/socket.h> ] +) + +AC_CHECK_MEMBERS([struct statfs.f_favail], , , + [ #include <sys/vfs.h> + #include <sys/statfs.h> ] +) + +AC_CHECK_MEMBERS([struct sockaddr_storage.ss_family], [], [], [ +#include <sys/types.h> +#include <sys/socket.h> +]) + +AC_CHECK_MEMBERS([struct sockaddr_storage.__ss_family], [], [], [ +#include <sys/types.h> +#include <sys/socket.h> +]) + +AC_CHECK_MEMBERS([struct tm.tm_gmtoff, struct tm.tm_zone], [], + [ + AC_CHECK_DECLS([timezone, altzone, tzname], + [], + [ AC_MSG_ERROR([cannot find timezone])], + [ #include <time.h> ] + ) + ], + [ #include <time.h> ] +) + +AC_CHECK_MEMBERS([struct DIR.d_type], [], [], [ +#include <sys/types.h> +#include <dirent.h> +]) + +# +# CHECKS FOR DECLARATIONS +# +AC_CHECK_DECLS([O_NONBLOCK], [], [], [ +#include <sys/types.h> +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif +#ifdef HAVE_FCNTL_H +# include <fcntl.h> +#endif +]) + +AC_CHECK_DECLS([AF_LOCAL, PF_LOCAL], [], [], [ +#include <sys/socket.h> +]) + +AC_CHECK_DECLS([IPPORT_HILASTAUTO], [], [], [ +#include <netinet/in.h> +]) + +AC_CHECK_DECLS([WAIT_MYPGRP], [], [], [ +#include <sys/wait.h> +]) + +AC_CHECK_DECLS([writev], [], [], [ +#include <sys/types.h> +#include <sys/uio.h> +#include <unistd.h> +]) + +AC_CHECK_DECLS([LOG_PERROR], [], [], [ +#include <syslog.h> +]) + + +# +# CHECKS FOR COMPILER CHARACTERISTICS +# +AC_C_INLINE + + +AC_ARG_WITH([libs], + [ --with-libs Specify additional libraries to link with], + [ + if test -n "$withval" -a "$withval" != "no" -a "${withval}" != "yes"; then + LIBS="$LIBS $withval" + fi + ] +) +# +# CHECKS FOR LIBRARY FUNCTIONS +# +AC_SEARCH_LIBS([basename], + [gen], + [ + AC_DEFINE([HAVE_BASENAME], [1], + [Define if you have the basename() function.]) + ]) + +AC_SEARCH_LIBS([closefrom], + [gen], + [ + AC_DEFINE([HAVE_CLOSEFROM], [1], + [Define if you have the closefrom() function.]) + AC_COMPILE_IFELSE( + [ + AC_LANG_PROGRAM([AC_INCLUDES_DEFAULT], [[int res = closefrom(0);]]) + ], + AC_DEFINE(HAVE_CLOSEFROM_INT, 1, [closefrom return int]) + ) + ]) + +AC_SEARCH_LIBS([fmt_scaled], + [util bsd], + [ + AC_DEFINE([HAVE_FMT_SCALED], [1], + [Define if you have the fmt_scaled() function.]) + ]) + +AC_SEARCH_LIBS([dirname], + [gen], + [ + AC_DEFINE([HAVE_DIRNAME], [1], + [Define if you have the dirname() function.]) + ]) + +AC_SEARCH_LIBS([inet_net_pton], + [resolv bsd], + [ + AC_DEFINE([HAVE_INET_NET_PTON], [1], + [Define if you have the inet_net_pton() function.]) + ]) + +AC_SEARCH_LIBS([clock_gettime], + [rt], + [ + AC_DEFINE([HAVE_CLOCK_GETTIME], [1], + [Define if you have the clock_gettime() function.]) + ]) + +AC_SEARCH_LIBS([fts_open], + [fts], + [ + AC_DEFINE([HAVE_FTS_OPEN], [1], + [Define if you have the fts_open() function.]) + ]) + +AC_SEARCH_LIBS([daemon], + [bsd], + [ + AC_DEFINE([HAVE_DAEMON], [1], + [Define if you have the daemon() function.]) + ]) + +AC_SEARCH_LIBS([fparseln], + [util], + [ + AC_DEFINE([HAVE_FPARSELN], [1], + [Define if you have the fparseln() function.]) + ]) + +AC_SEARCH_LIBS([res_hnok], + [resolv], + [ + AC_DEFINE([HAVE_RES_HNOK], [1], + [Define if you have the res_hnok() function.]) + ]) + +AC_SEARCH_LIBS([res_randomid], + [resolv], + [ + AC_DEFINE([HAVE_RES_RANDOMID], [1], + [Define if you have the res_randomid() function.]) + ]) + +AC_SEARCH_LIBS([res_9_b64_ntop], + [resolv], + [ + AC_DEFINE([HAVE_RES_9_B64_NTOP], [1], + [Define if you have the res_9_b64_ntop() function.]) + ]) + +AC_SEARCH_LIBS([__b64_pton], + [resolv], + [ + AC_DEFINE([HAVE___B64_PTON], [1], + [Define if you have the __b64_pton() function.]) + ]) + +AC_SEARCH_LIBS([b64_pton], + [resolv], + [ + AC_DEFINE([HAVE_B64_PTON], [1], + [Define if you have the b64_pton() function.]) + ]) + +AC_SEARCH_LIBS([__b64_ntop], + [resolv], + [ + AC_DEFINE([HAVE___B64_NTOP], [1], + [Define if you have the b64_ntop() function.]) + ]) + +AC_SEARCH_LIBS([b64_ntop], + [resolv], + [ + AC_DEFINE([HAVE_B64_NTOP], [1], + [Define if you have the b64_ntop() function.]) + ]) + +AC_SEARCH_LIBS([setsockopt], + [socket], + [ + AC_DEFINE([HAVE_SETSOCKOPT], [1], + [Define if you have the setsockopt() function.]) + ]) + +AC_SEARCH_LIBS([crypt], + [crypt], + [ + AC_DEFINE([HAVE_CRYPT], [1], + [Define if you have the crypt() function.]) + ]) + +AC_SEARCH_LIBS([imsg_init], + [util], + [ + AC_DEFINE([HAVE_IMSG], [1], + [Define if you have the imsg framework.]) + ]) + +AC_SEARCH_LIBS([event_asr_run], + [event], + [ + AC_DEFINE([HAVE_EVENT_ASR_RUN], [1], + [Define if you have the event_asr_run() function.]) + ]) + +AC_CHECK_FUNCS([ \ + asprintf \ + arc4random \ + bcopy \ + calloc_conceal \ + chflags \ + crypt_checkpass \ + dirfd \ + err \ + errc \ + errx \ + explicit_bzero \ + fchflags \ + fgetln \ + flock \ + freeaddrinfo \ + freezero \ + getaddrinfo \ + getdtablesize \ + getdtablecount \ + getline \ + getnameinfo \ + gettimeofday \ + getopt \ + getpeereid \ + getpeerucred \ + getspnam \ + inet_aton \ + inet_ntoa \ + inet_ntop \ + malloc_conceal \ + memmove \ + nanosleep \ + nsleep \ + pipe2 \ + pidfile \ + pledge \ + reallocarray \ + recallocarray \ + res_hnok \ + res_randomid \ + setenv \ + seteuid \ + setegid \ + setproctitle \ + setregid \ + setreuid \ + setresuid \ + setresgid \ + setsid \ + signal \ + sigaction \ + snprintf \ + socketpair \ + strdup \ + strerror \ + strlcat \ + strlcpy \ + strmode \ + strndup \ + strnlen \ + strnvis \ + strtonum \ + sysconf \ + tcgetpgrp \ + time \ + usleep \ + vasprintf \ + vsnprintf \ + waitpid \ + warn \ + warnx \ +]) + +AC_CHECK_DECL([strsep], + [AC_CHECK_FUNCS([strsep])], + [], + [ +#ifdef HAVE_STRING_H +# include <string.h> +#endif + ]) + + +# These functions might be found in libressl +AC_CHECK_DECLS([strlcat, strlcpy], + [], + [], + []) + +# +# CHECKS FOR SYSTEM SERVICES +# +AC_MSG_CHECKING([for /proc/pid/fd directory]) +if test -d "/proc/$$/fd"; then + AC_DEFINE([HAVE_PROC_PID], [1], [Define if you have /proc/$pid/fd]) + AC_MSG_RESULT([yes]) +else + AC_MSG_RESULT([no]) +fi + + +# +# AC_CONFIG_FILES +# + +# +# AC_OUTPUT +# + + +### +### EVERYTHING BELOW MUST BE CLEANED AND MOVED ABOVE +### + +#l150 (without _FORTIFY_SOURCE=2) +if test "$GCC" = "yes" -o "$GCC" = "egcs"; then + OSSH_CHECK_CFLAG_COMPILE([-Qunused-arguments]) + OSSH_CHECK_CFLAG_COMPILE([-Wunknown-warning-option]) + OSSH_CHECK_CFLAG_COMPILE([-Wall]) + OSSH_CHECK_CFLAG_COMPILE([-Wpointer-arith]) + OSSH_CHECK_CFLAG_COMPILE([-Wuninitialized]) + OSSH_CHECK_CFLAG_COMPILE([-Wsign-compare]) + OSSH_CHECK_CFLAG_COMPILE([-Wformat-security]) + OSSH_CHECK_CFLAG_COMPILE([-Wsizeof-pointer-memaccess]) + OSSH_CHECK_CFLAG_COMPILE([-Wpointer-sign], [-Wno-pointer-sign]) + OSSH_CHECK_CFLAG_COMPILE([-Wunused-result], [-Wno-unused-result]) + OSSH_CHECK_CFLAG_COMPILE([-fno-strict-aliasing]) +# OSSH_CHECK_CFLAG_COMPILE([-D_FORTIFY_SOURCE=2]) + if test "x$use_toolchain_hardening" = "x1"; then + OSSH_CHECK_LDFLAG_LINK([-Wl,-z,relro]) + OSSH_CHECK_LDFLAG_LINK([-Wl,-z,now]) + OSSH_CHECK_LDFLAG_LINK([-Wl,-z,noexecstack]) + # NB. -ftrapv expects certain support functions to be present in + # the compiler library (libgcc or similar) to detect integer operations + # that can overflow. We must check that the result of enabling it + # actually links. The test program compiled/linked includes a number + # of integer operations that should exercise this. + OSSH_CHECK_CFLAG_LINK([-ftrapv]) + fi + AC_MSG_CHECKING([gcc version]) + GCC_VER=`$CC -v 2>&1 | $AWK '/gcc version /{print $3}'` + case $GCC_VER in + 1.*) no_attrib_nonnull=1 ;; + 2.8* | 2.9*) + no_attrib_nonnull=1 + ;; + 2.*) no_attrib_nonnull=1 ;; + *) ;; + esac + AC_MSG_RESULT([$GCC_VER]) + + AC_MSG_CHECKING([if $CC accepts -fno-builtin-memset]) + saved_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -fno-builtin-memset" + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <string.h> ]], + [[ char b[10]; memset(b, 0, sizeof(b)); ]])], + [ AC_MSG_RESULT([yes]) ], + [ AC_MSG_RESULT([no]) + CFLAGS="$saved_CFLAGS" ] + ) + + # -fstack-protector-all doesn't always work for some GCC versions + # and/or platforms, so we test if we can. If it's not supported + # on a given platform gcc will emit a warning so we use -Werror. + if test "x$use_stack_protector" = "x1"; then + for t in -fstack-protector-strong -fstack-protector-all \ + -fstack-protector; do + AC_MSG_CHECKING([if $CC supports $t]) + saved_CFLAGS="$CFLAGS" + saved_LDFLAGS="$LDFLAGS" + CFLAGS="$CFLAGS $t -Werror" + LDFLAGS="$LDFLAGS $t -Werror" + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[ #include <stdio.h> ]], + [[ + char x[256]; + snprintf(x, sizeof(x), "XXX"); + ]])], + [ AC_MSG_RESULT([yes]) + CFLAGS="$saved_CFLAGS $t" + LDFLAGS="$saved_LDFLAGS $t" + AC_MSG_CHECKING([if $t works]) + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ #include <stdio.h> ]], + [[ + char x[256]; + snprintf(x, sizeof(x), "XXX"); + ]])], + [ AC_MSG_RESULT([yes]) + break ], + [ AC_MSG_RESULT([no]) ], + [ AC_MSG_WARN([cross compiling: cannot test]) + break ] + ) + ], + [ AC_MSG_RESULT([no]) ] + ) + CFLAGS="$saved_CFLAGS" + LDFLAGS="$saved_LDFLAGS" + done + fi + + if test -z "$have_llong_max"; then + # retry LLONG_MAX with -std=gnu99, needed on some Linuxes + unset ac_cv_have_decl_LLONG_MAX + saved_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -std=gnu99" + AC_CHECK_DECL([LLONG_MAX], + [have_llong_max=1], + [CFLAGS="$saved_CFLAGS"], + [#include <limits.h>] + ) + fi +fi + +AC_MSG_CHECKING([if compiler allows __attribute__ on return types]) +AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[ +#include <stdlib.h> +__attribute__((__unused__)) static void foo(void){return;}]], + [[ exit(0); ]])], + [ AC_MSG_RESULT([yes]) ], + [ AC_MSG_RESULT([no]) + AC_DEFINE(NO_ATTRIBUTE_ON_RETURN_TYPE, 1, + [compiler does not accept __attribute__ on return types]) ] +) + +if test "x$no_attrib_nonnull" != "x1"; then + AC_DEFINE([HAVE_ATTRIBUTE__NONNULL__], [1], [Have attribute nonnull]) +fi + +AC_ARG_WITH([rpath], + [ --without-rpath Disable auto-added -R linker paths], + [ + if test "x$withval" = "xno"; then + need_dash_r="" + fi + if test "x$withval" = "xyes"; then + need_dash_r=1 + fi + ] +) + + +AC_ARG_WITH([cflags], + [ --with-cflags Specify additional flags to pass to compiler], + [ + if test -n "$withval" -a "$withval" != "no" -a "${withval}" != "yes"; then + CFLAGS="$CFLAGS $withval" + fi + ] +) +AC_ARG_WITH([cppflags], + [ --with-cppflags Specify additional flags to pass to preprocessor] , + [ + if test -n "$withval" -a "$withval" != "no" -a "${withval}" != "yes"; then + CPPFLAGS="$CPPFLAGS $withval" + fi + ] +) +AC_ARG_WITH([ldflags], + [ --with-ldflags Specify additional flags to pass to linker], + [ + if test -n "$withval" -a "$withval" != "xno" -a "${withval}" != "yes"; then + LDFLAGS="$LDFLAGS $withval" + fi + ] +) +AC_ARG_WITH([Werror], + [ --with-Werror Build main code with -Werror], + [ + if test -n "$withval" -a "$withval" != "no"; then + werror_flags="-Werror" + if test "${withval}" != "yes"; then + werror_flags="$withval" + fi + fi + ] +) + + + +AC_ARG_WITH([pie], + [ --with-pie Build Position Independent Executables if possible], [ + if test "x$withval" = "xno"; then + use_pie=no + fi + if test "x$withval" = "xyes"; then + use_pie=yes + fi + ] +) +if test -z "$use_pie"; then + use_pie=no +fi +if test "x$use_toolchain_hardening" != "x1" -a "x$use_pie" = "xauto"; then + # Turn off automatic PIE when toolchain hardening is off. + use_pie=no +fi +if test "x$use_pie" = "xauto"; then + # Automatic PIE requires gcc >= 4.x + AC_MSG_CHECKING([for gcc >= 4.x]) + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ +#if !defined(__GNUC__) || __GNUC__ < 4 +#error gcc is too old +#endif +]])], + [ AC_MSG_RESULT([yes]) ], + [ AC_MSG_RESULT([no]) + use_pie=no ] +) +fi +if test "x$use_pie" != "xno"; then + SAVED_CFLAGS="$CFLAGS" + SAVED_LDFLAGS="$LDFLAGS" + OSSH_CHECK_CFLAG_COMPILE([-fPIE]) + OSSH_CHECK_LDFLAG_LINK([-pie]) + # We use both -fPIE and -pie or neither. + AC_MSG_CHECKING([whether both -fPIE and -pie are supported]) + if echo "x $CFLAGS" | grep ' -fPIE' >/dev/null 2>&1 && \ + echo "x $LDFLAGS" | grep ' -pie' >/dev/null 2>&1 ; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + CFLAGS="$SAVED_CFLAGS" + LDFLAGS="$SAVED_LDFLAGS" + fi +fi + + + + + + +#l432 (customized) +# Check for some target-specific stuff + +case "$host" in +*-*-darwin*) + use_pie=auto + AC_MSG_CHECKING([if we have working getaddrinfo]) + AC_RUN_IFELSE([AC_LANG_SOURCE([[ +#include <stdlib.h> +#include <mach-o/dyld.h> +main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16)) + exit(0); + else + exit(1); +} + ]])], + [AC_MSG_RESULT([working])], + [AC_MSG_RESULT([buggy]) + AC_DEFINE([BROKEN_GETADDRINFO], [1], + [getaddrinfo is broken (if present)]) + ], + [AC_MSG_RESULT([assume it is working])]) + AC_DEFINE([SETEUID_BREAKS_SETUID], [1], [define if seteuid breaks setuid]) + AC_DEFINE([BROKEN_SETREUID], [1], [define if setreuid is broken]) + AC_DEFINE([BROKEN_SETREGID], [1], [define if setregid is broken]) + AC_DEFINE([BROKEN_GLOB], [1], [OS X glob does not do what we expect]) + AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV], + [Define to a Set Process Title type if your system is + supported by bsd-setproctitle.c]) + AC_DEFINE([BROKEN_STRNVIS], [1], + [OSX strnvis argument order is swapped compared to OpenBSD]) + BROKEN_STRNVIS=1 + ;; +*-*-dragonfly*) + ;; +*-*-linux* | *-gnu* | *-k*bsd*-gnu* ) + use_pie=auto + CFLAGS="$CFLAGS -D_BSD_SOURCE -D_DEFAULT_SOURCE" + AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV]) + ;; +*-*-netbsd*) + if test "x$withval" != "xno"; then + need_dash_r=1 + fi + AC_DEFINE([BROKEN_STRNVIS], [1], + [NetBSD strnvis argument order is swapped compared to OpenBSD]) + BROKEN_STRNVIS=1 + ;; +*-*-freebsd*) + AC_DEFINE([BROKEN_GLOB], [1], [FreeBSD glob does not do what we need]) + AC_DEFINE([BROKEN_STRNVIS], [1], + [FreeBSD strnvis argument order is swapped compared to OpenBSD]) + BROKEN_STRNVIS=1 + ;; +*-*-openbsd*) + use_pie=auto + AC_DEFINE([HAVE_ATTRIBUTE__SENTINEL__], [1], [OpenBSD's gcc has sentinel]) + AC_DEFINE([HAVE_ATTRIBUTE__BOUNDED__], [1], [OpenBSD's gcc has bounded]) + + AC_DEFINE([BROKEN_STRNVIS], [0], + [FreeBSD strnvis argument order is swapped compared to OpenBSD]) + BROKEN_STRNVIS=0 + YACC='yacc' + ASR_LIB= + AC_DEFINE([NOOP_ASR_FREEADDRINFO], [0], [OpenBSD doesn't need ASR_FREEADDRINFO]) + ;; +*-sun-solaris*) + AC_DEFINE([HAVE_M_DATA], [1], [M_DATA is defined in sys/stream.h included by netinet/in.h]) + ;; +esac +AC_SUBST([ASR_LIB]) + + +AC_MSG_CHECKING([compiler and flags for sanity]) +AC_RUN_IFELSE([AC_LANG_PROGRAM([[ +#include <stdio.h> +#include <stdlib.h> ]], [[ exit(0); ]])], + [ AC_MSG_RESULT([yes]) ], + [ + AC_MSG_RESULT([no]) + AC_MSG_ERROR([*** compiler cannot create working executables, check config.log ***]) + ], + [ AC_MSG_WARN([cross compiling: not checking compiler sanity]) ] +) + + + +#l1747 + + + + +# Check for missing getpeereid (or equiv) support +NO_PEERCHECK="" +if test "x$ac_cv_func_getpeereid" != "xyes" -a "x$ac_cv_func_getpeerucred" != "xyes"; then + AC_MSG_CHECKING([whether system supports SO_PEERCRED getsockopt]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include <sys/types.h> +#include <sys/socket.h>]], [[int i = SO_PEERCRED;]])], + [ AC_MSG_RESULT([yes]) + AC_DEFINE([HAVE_SO_PEERCRED], [1], [Have PEERCRED socket option]) + ], [AC_MSG_RESULT([no]) + NO_PEERCHECK=1 + ]) +fi + +#l4176 (customized s/ssh.1/smtpd/smtpd.8/) +# Options from here on. Some of these are preset by platform above +AC_ARG_WITH([mantype], + [ --with-mantype=man|cat|doc Set man page type], + [ + case "$withval" in + man|cat|doc) + MANTYPE=$withval + ;; + *) + AC_MSG_ERROR([invalid man type: $withval]) + ;; + esac + ] +) +if test -z "$MANTYPE"; then + TestPath="/usr/bin${PATH_SEPARATOR}/usr/ucb" + AC_PATH_PROGS([NROFF], [nroff awf], [/bin/false], [$TestPath]) + if ${NROFF} -mdoc ${srcdir}/smtpd/smtpd.8 >/dev/null 2>&1; then + MANTYPE=doc + elif ${NROFF} -man ${srcdir}/smtpd/smtpd.8 >/dev/null 2>&1; then + MANTYPE=man + else + MANTYPE=cat + fi +fi +AC_SUBST([MANTYPE]) +if test "$MANTYPE" = "doc"; then + mansubdir=man; +else + mansubdir=$MANTYPE; +fi +AC_SUBST([mansubdir]) +#l4207 + + +#l4432 (customized s/pid/sock/) +# Whether to enable BSD auth support +BSD_AUTH_MSG=no +AC_ARG_WITH([auth-bsdauth], + [ --with-auth-bsdauth Enable bsd_auth(3) authentication support], + [ + if test "x$withval" != "xno"; then + AC_DEFINE([BSD_AUTH], [1], + [Define if you have BSD auth support]) + BSD_AUTH_MSG=yes + fi + ] +) + + +#l2757 +# Check for PAM libs +PAM_MSG="no" +USE_PAM_SERVICE=smtpd +AC_ARG_WITH([auth-pam], + [ --with-auth-pam=SERVICE Enable PAM authentication support (default=smtpd)], + [ + if test "x$withval" != "xno"; then + if test -n "$withval" -a "x${withval}" != "xyes"; then + USE_PAM_SERVICE=$withval + fi + + if test "x$ac_cv_header_security_pam_appl_h" != "xyes" -a \ + test "x$ac_cv_header_pam_pam_appl_h" != "xyes"; then + AC_MSG_ERROR([PAM headers not found]) + fi + + saved_LIBS="$LIBS" + AC_CHECK_LIB([dl], [dlopen], , ) + AC_CHECK_LIB([pam], [pam_set_item], , [AC_MSG_ERROR([*** libpam missing])]) + AC_CHECK_FUNCS([pam_getenvlist]) + AC_CHECK_FUNCS([pam_putenv]) + LIBS="$saved_LIBS" + + PAM_MSG="yes" + + SMTPDLIBS="$SMTPDLIBS -lpam" + AC_DEFINE([USE_PAM], [1], + [Define if you want to enable PAM support]) + + if test "x$ac_cv_lib_dl_dlopen" = "xyes"; then + case "$LIBS" in + *-ldl*) + # libdl already in LIBS + ;; + *) + SMTPDLIBS="$SMTPDLIBS -ldl" + ;; + esac + fi + fi + ] +) +AC_DEFINE_UNQUOTED([USE_PAM_SERVICE], ["$USE_PAM_SERVICE"], [pam service]) +AC_SUBST([USE_PAM_SERVICE]) + + +# Check for older PAM +if test "x$PAM_MSG" = "xyes"; then + # Check PAM strerror arguments (old PAM) + AC_MSG_CHECKING([whether pam_strerror takes only one argument]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include <stdlib.h> +#if defined(HAVE_SECURITY_PAM_APPL_H) +#include <security/pam_appl.h> +#elif defined (HAVE_PAM_PAM_APPL_H) +#include <pam/pam_appl.h> +#endif + ]], [[ +(void)pam_strerror((pam_handle_t *)NULL, -1); + ]])], [AC_MSG_RESULT([no])], [ + AC_DEFINE([HAVE_OLD_PAM], [1], + [Define if you have an old version of PAM + which takes only one argument to pam_strerror]) + AC_MSG_RESULT([yes]) + PAM_MSG="yes (old library)" + + ]) +fi +#l2816 + + +##gilles + +SMTPD_USER=_smtpd +AC_ARG_WITH([user-smtpd], + [ --with-user-smtpd=user Specify non-privileged user for smtpd (default=_smtpd)], + [ + if test -n "$withval" -a "$withval" != "no" -a "${withval}" != "yes"; then + SMTPD_USER=$withval + fi + ] +) +AC_DEFINE_UNQUOTED([SMTPD_USER], ["$SMTPD_USER"], + [non-privileged user for privilege separation]) +AC_SUBST([SMTPD_USER]) + +SMTPD_QUEUE_USER=_smtpq +AC_ARG_WITH([user-queue], + [ --with-user-queue=user Specify non-privileged user for queue process (default=_smtpq)], + [ + if test -n "$withval" -a "$withval" != "no" -a "${withval}" != "yes"; then + SMTPD_QUEUE_USER=$withval + fi + ] +) +AC_DEFINE_UNQUOTED([SMTPD_QUEUE_USER], ["$SMTPD_QUEUE_USER"], + [non-privileged user for queue process]) +AC_SUBST([SMTPD_QUEUE_USER]) + +SMTPD_QUEUE_GROUP=_smtpq +AC_ARG_WITH([group-queue], + [ --with-group-queue=group Specify non-privileged group for offline queue (default=_smtpq)], + [ + if test -n "$withval" -a "$withval" != "no" -a "${withval}" != "yes"; then + SMTPD_QUEUE_GROUP=$withval + fi + ] +) +AC_DEFINE_UNQUOTED([SMTPD_QUEUE_GROUP], ["$SMTPD_QUEUE_GROUP"], + [non-privileged group for queue process]) +AC_SUBST([SMTPD_QUEUE_GROUP]) + +# Where to place spooler +spooldir=/var/spool/smtpd +AC_ARG_WITH([path-queue], + [ --with-path-queue=PATH Specify path to queue directory (default=/var/spool/smtpd)], + [ + if test -n "$withval" -a "$withval" != "no" -a "${withval}" != "yes"; then + spooldir=$withval + if test ! -d $spooldir; then + AC_MSG_WARN([** no $spooldir directory on this system **]) + fi + fi + ] +) + +AC_DEFINE_UNQUOTED([PATH_SPOOL], ["$spooldir"], + [Specify location of spooler]) +AC_SUBST([spooldir]) + + +PRIVSEP_PATH=/var/empty +AC_ARG_WITH([path-empty], + [ --with-path-empty=PATH Specify path to empty directory (default=/var/empty)], + [ + if test -n "$withval" -a "$withval" != "no" -a "${withval}" != "yes"; then + PRIVSEP_PATH=$withval + fi + ] +) +AC_SUBST([PRIVSEP_PATH]) +#l4022 + +#l4066 +dnl # --with-maildir=/path/to/mail gets top priority. +dnl # if maildir is set in the platform case statement above we use that. +dnl # Otherwise we run a program to get the dir from system headers. +dnl # We first look for _PATH_MAILDIR then MAILDIR then _PATH_MAIL +dnl # If we find _PATH_MAILDIR we do nothing because that is what +dnl # session.c expects anyway. Otherwise we set to the value found +dnl # stripping any trailing slash. If for some strage reason our program +dnl # does not find what it needs, we default to /var/spool/mail. +# Check for mail directory +AC_ARG_WITH([path-mbox], + [ --with-path-mbox=PATH Specify path to mbox directory (default=/var/spool/mail)], + [ + if test -n "$withval" -a "$withval" != "no" -a "${withval}" != "yes"; then + AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["$withval"], + [Set this to your mail directory if you do not have _PATH_MAILDIR]) + fi + ],[ + if test -n "$maildir"; then + AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["$maildir"]) + else + AC_MSG_CHECKING([system mail directory]) + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#ifdef HAVE_PATHS_H +#include <paths.h> +#endif +#ifdef HAVE_MAILLOCK_H +#include <maillock.h> +#endif +#define DATA "conftest.maildir" + ]], [[ + FILE *fd; + int rc; + + fd = fopen(DATA,"w"); + if(fd == NULL) + exit(1); + +#if defined (_PATH_MAILDIR) + if ((rc = fprintf(fd ,"_PATH_MAILDIR:%s\n", _PATH_MAILDIR)) <0) + exit(1); +#elif defined (MAILDIR) + if ((rc = fprintf(fd ,"MAILDIR:%s\n", MAILDIR)) <0) + exit(1); +#elif defined (_PATH_MAIL) + if ((rc = fprintf(fd ,"_PATH_MAIL:%s\n", _PATH_MAIL)) <0) + exit(1); +#else + exit (2); +#endif + + exit(0); + ]])], + [ + maildir_what=`awk -F: '{print $1}' conftest.maildir` + maildir=`awk -F: '{print $2}' conftest.maildir \ + | sed 's|/$||'` + AC_MSG_RESULT([$maildir from $maildir_what]) + if test "x$maildir_what" != "x_PATH_MAILDIR"; then + AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["$maildir"]) + fi + ], + [ + if test "X$ac_status" = "X2"; then +# our test program didn't find it. Default to /var/spool/mail + AC_MSG_RESULT([/var/spool/mail]) + AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["/var/spool/mail"]) + else + AC_MSG_RESULT([*** not found ***]) + fi + ], + [ + AC_MSG_WARN([cross compiling: use --with-maildir=/path/to/mail]) + ] + ) + fi + ] +) # maildir +#l4146 + +# Where to place smtpd.sock +sockdir=/var/run +# make sure the directory exists +if test ! -d $sockdir; then + sockdir=`eval echo ${sysconfdir}` + case $sockdir in + NONE/*) sockdir=`echo $sockdir | sed "s~NONE~$ac_default_prefix~"` ;; + esac +fi + +AC_ARG_WITH([path-socket], + [ --with-path-socket=PATH Specify path to smtpd.sock directory (default=/var/run)], + [ + if test -n "$withval" -a "$withval" != "no" -a "${withval}" != "yes"; then + sockdir=$withval + if test ! -d $sockdir; then + AC_MSG_WARN([** no $sockdir directory on this system **]) + fi + fi + ] +) + +AC_DEFINE_UNQUOTED([SMTPD_SOCKDIR], ["$sockdir"], + [Specify location of smtpd.sock]) +AC_SUBST([sockdir]) +#l4470 + +# Where to place smtpd.pid +piddir=/var/run +AC_MSG_CHECKING([system pid directory]) +AC_RUN_IFELSE( + [ + AC_LANG_PROGRAM([[ +#include <stdio.h> +#include <stdlib.h> +#ifdef HAVE_PATHS_H +#include <paths.h> +#endif +#define DATA "conftest.piddir" + ]], + [[ +#ifdef _PATH_VARRUN +FILE *fd; +int rc; + +if ((fd = fopen(DATA,"w")) == NULL) { exit(1); } +if ((rc = fprintf(fd ,"%s\n", _PATH_VARRUN)) < 0) { exit(2); } +exit(0); +#else +exit(-1); +#endif + ]]) + ], [ + piddir=`cat conftest.piddir` + AC_MSG_RESULT([$piddir from paths.h]) + ], + [ + AC_MSG_RESULT([$piddir from default value]) + ], + [ + AC_MSG_RESULT([$piddir from default value]) + ] +) + +AC_ARG_WITH([path-pidfile], + [ --with-path-pidfile=PATH Specify path to smtpd.pid directory (default=/var/run)], + [ + if test -n "$withval" -a "$withval" != "no" -a "${withval}" != "yes"; then + piddir=$withval + fi + ] +) + +AC_DEFINE_UNQUOTED([SMTPD_PIDDIR], ["$piddir"], [Specify location of smtpd.pid]) +AC_SUBST([piddir]) + +CA_FILE=/etc/ssl/cert.pem +AC_ARG_WITH([path-CAfile], + [ --with-path-CAfile=FILE Specify path to CA certificate (default=/etc/ssl/cert.pem)], + [ + if test -n "$withval" -a "$withval" != "no" -a "${withval}" != "yes"; then + CA_FILE=$withval + fi + ] +) +AC_SUBST([CA_FILE]) + + + + + + +# compute LLONG_MIN and LLONG_MAX if we don't know them. +if test -z "$have_llong_max"; then + AC_MSG_CHECKING([for max value of long long]) + AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ +#include <stdio.h> +#include <stdlib.h> +/* Why is this so damn hard? */ +#ifdef __GNUC__ +# undef __GNUC__ +#endif +#define __USE_ISOC99 +#include <limits.h> +#define DATA "conftest.llminmax" +#define my_abs(a) ((a) < 0 ? ((a) * -1) : (a)) + +/* + * printf in libc on some platforms (eg old Tru64) does not understand %lld so + * we do this the hard way. + */ +static int +fprint_ll(FILE *f, long long n) +{ + unsigned int i; + int l[sizeof(long long) * 8]; + + if (n < 0) + if (fprintf(f, "-") < 0) + return -1; + for (i = 0; n != 0; i++) { + l[i] = my_abs(n % 10); + n /= 10; + } + do { + if (fprintf(f, "%d", l[--i]) < 0) + return -1; + } while (i != 0); + if (fprintf(f, " ") < 0) + return -1; + return 0; +} + ]], [[ + FILE *f; + long long i, llmin, llmax = 0; + + if((f = fopen(DATA,"w")) == NULL) + exit(1); + +#if defined(LLONG_MIN) && defined(LLONG_MAX) + fprintf(stderr, "Using system header for LLONG_MIN and LLONG_MAX\n"); + llmin = LLONG_MIN; + llmax = LLONG_MAX; +#else + fprintf(stderr, "Calculating LLONG_MIN and LLONG_MAX\n"); + /* This will work on one's complement and two's complement */ + for (i = 1; i > llmax; i <<= 1, i++) + llmax = i; + llmin = llmax + 1LL; /* wrap */ +#endif + + /* Sanity check */ + if (llmin + 1 < llmin || llmin - 1 < llmin || llmax + 1 > llmax + || llmax - 1 > llmax || llmin == llmax || llmin == 0 + || llmax == 0 || llmax < LONG_MAX || llmin > LONG_MIN) { + fprintf(f, "unknown unknown\n"); + exit(2); + } + + if (fprint_ll(f, llmin) < 0) + exit(3); + if (fprint_ll(f, llmax) < 0) + exit(4); + if (fclose(f) < 0) + exit(5); + exit(0); + ]])], + [ + llong_min=`$AWK '{print $1}' conftest.llminmax` + llong_max=`$AWK '{print $2}' conftest.llminmax` + + AC_MSG_RESULT([$llong_max]) + AC_DEFINE_UNQUOTED([LLONG_MAX], [${llong_max}LL], + [max value of long long calculated by configure]) + AC_MSG_CHECKING([for min value of long long]) + AC_MSG_RESULT([$llong_min]) + AC_DEFINE_UNQUOTED([LLONG_MIN], [${llong_min}LL], + [min value of long long calculated by configure]) + ], + [ + AC_MSG_RESULT([not found]) + ], + [ + AC_MSG_WARN([cross compiling: not checking]) + ] + ) +fi + + + + + +#l3561 + + +dnl make sure we're using the real structure members and not defines +AC_CACHE_CHECK([for msg_accrights field in struct msghdr], + ac_cv_have_accrights_in_msghdr, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <stdlib.h> + ]], [[ +#ifdef msg_accrights +#error "msg_accrights is a macro" +exit(1); +#endif +struct msghdr m; +m.msg_accrights = 0; +exit(0); + ]])], + [ ac_cv_have_accrights_in_msghdr="yes" ], + [ ac_cv_have_accrights_in_msghdr="no" ] + ) +]) +if test "x$ac_cv_have_accrights_in_msghdr" = "xyes"; then + AC_DEFINE([HAVE_ACCRIGHTS_IN_MSGHDR], [1], + [Define if your system uses access rights style + file descriptor passing]) +fi + + +AC_CACHE_CHECK([for msg_control field in struct msghdr], + ac_cv_have_control_in_msghdr, [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <stdlib.h> + ]], [[ +#ifdef msg_control +#error "msg_control is a macro" +exit(1); +#endif +struct msghdr m; +m.msg_control = 0; +exit(0); + ]])], + [ ac_cv_have_control_in_msghdr="yes" ], + [ ac_cv_have_control_in_msghdr="no" ] + ) +]) +if test "x$ac_cv_have_control_in_msghdr" = "xyes"; then + AC_DEFINE([HAVE_CONTROL_IN_MSGHDR], [1], + [Define if your system uses ancillary data style + file descriptor passing]) +fi + +AC_CACHE_CHECK([if libc defines __progname], ac_cv_libc_defines___progname, [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <stdio.h> ]], + [[ extern char *__progname; printf("%s", __progname); ]])], + [ ac_cv_libc_defines___progname="yes" ], + [ ac_cv_libc_defines___progname="no" + ]) +]) +if test "x$ac_cv_libc_defines___progname" = "xyes"; then + AC_DEFINE([HAVE___PROGNAME], [1], [Define if libc defines __progname]) +fi + +AC_CACHE_CHECK([whether $CC implements __FUNCTION__], ac_cv_cc_implements___FUNCTION__, [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <stdio.h> ]], + [[ printf("%s", __FUNCTION__); ]])], + [ ac_cv_cc_implements___FUNCTION__="yes" ], + [ ac_cv_cc_implements___FUNCTION__="no" + ]) +]) +if test "x$ac_cv_cc_implements___FUNCTION__" = "xyes"; then + AC_DEFINE([HAVE___FUNCTION__], [1], + [Define if compiler implements __FUNCTION__]) +fi + +AC_CACHE_CHECK([whether $CC implements __func__], ac_cv_cc_implements___func__, [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <stdio.h> ]], + [[ printf("%s", __func__); ]])], + [ ac_cv_cc_implements___func__="yes" ], + [ ac_cv_cc_implements___func__="no" + ]) +]) +if test "x$ac_cv_cc_implements___func__" = "xyes"; then + AC_DEFINE([HAVE___func__], [1], [Define if compiler implements __func__]) +fi + +AC_CACHE_CHECK([whether va_copy exists], ac_cv_have_va_copy, [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +#include <stdarg.h> +va_list x,y; + ]], [[ va_copy(x,y); ]])], + [ ac_cv_have_va_copy="yes" ], + [ ac_cv_have_va_copy="no" + ]) +]) +if test "x$ac_cv_have_va_copy" = "xyes"; then + AC_DEFINE([HAVE_VA_COPY], [1], [Define if va_copy exists]) +fi + +AC_CACHE_CHECK([whether __va_copy exists], ac_cv_have___va_copy, [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ +#include <stdarg.h> +va_list x,y; + ]], [[ __va_copy(x,y); ]])], + [ ac_cv_have___va_copy="yes" ], [ ac_cv_have___va_copy="no" + ]) +]) +if test "x$ac_cv_have___va_copy" = "xyes"; then + AC_DEFINE([HAVE___VA_COPY], [1], [Define if __va_copy exists]) +fi + +AC_CACHE_CHECK([whether getopt has optreset support], + ac_cv_have_getopt_optreset, [ + AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include <getopt.h> ]], + [[ extern int optreset; optreset = 0; ]])], + [ ac_cv_have_getopt_optreset="yes" ], + [ ac_cv_have_getopt_optreset="no" + ]) +]) +if test "x$ac_cv_have_getopt_optreset" = "xyes"; then + AC_DEFINE([HAVE_GETOPT_OPTRESET], [1], + [Define if your getopt(3) defines and uses optreset]) +fi +#l3765 + + + + +#l4045 +STRIP_OPT=-s +AC_ARG_ENABLE([strip], + [ --disable-strip Disable calling strip(1) on install], + [ + if test "x$enableval" = "xno"; then + STRIP_OPT= + fi + ] +) +AC_SUBST([STRIP_OPT]) +#l4054 + + + +case "$host" in +*-*-openbsd*) + pkglibexecdir="$libexecdir/smtpd" + ;; +*) + pkglibexecdir="$libexecdir/opensmtpd" + ;; +esac +AC_SUBST([pkglibexecdir]) + + + + + + + + + +#l4742 +dnl Adding -Werror to CFLAGS early prevents configure tests from running. +dnl Add now. +CFLAGS="$CFLAGS $werror_flags" + +AC_SUBST([TEST_MALLOC_OPTIONS], [$TEST_MALLOC_OPTIONS]) + + +AC_EXEEXT +#l4757 + + +# Search for fts +AC_ARG_WITH([libfts], + [ --with-libfts=PATH Specify path to libfts installation (default: none, part of libc)], + [ if test "x$withval" = "xno"; then + AC_MSG_ERROR([*** fts is required ***]) + elif test "x$withval" != "xyes"; then + if test -d "$withval/lib"; then + if test -n "${need_dash_r}"; then + LDFLAGS="-L${withval}/lib -R${withval}/lib ${LDFLAGS}" + else + LDFLAGS="-L${withval}/lib ${LDFLAGS}" + fi + else + if test -n "${need_dash_r}"; then + LDFLAGS="-L${withval} -R${withval} ${LDFLAGS}" + else + LDFLAGS="-L${withval} ${LDFLAGS}" + fi + fi + if test -d "$withval/include"; then + CPPFLAGS="-I${withval}/include ${CPPFLAGS}" + else + CPPFLAGS="-I${withval} ${CPPFLAGS}" + fi + LIBS="-lfts $LIBS" + fi + ] +) + + + +##chl (based on OpenSSL checks, see above) +# Search for libevent +saved_CPPFLAGS="$CPPFLAGS" +saved_LDFLAGS="$LDFLAGS" +AC_ARG_WITH([libevent], + [ --with-libevent=PATH Specify path to libevent installation ], + [ + if test "x$withval" != "xno"; then + case "$withval" in + # Relative paths + ./*|../*) withval="`pwd`/$withval" + esac + if test -d "$withval/lib"; then + if test -n "${need_dash_r}"; then + LDFLAGS="-L${withval}/lib -R${withval}/lib ${LDFLAGS}" + else + LDFLAGS="-L${withval}/lib ${LDFLAGS}" + fi + elif test -d "$withval/lib64"; then + if test -n "${need_dash_r}"; then + LDFLAGS="-L${withval}/lib64 -R${withval}/lib64 ${LDFLAGS}" + else + LDFLAGS="-L${withval}/lib64 ${LDFLAGS}" + fi + else + if test -n "${need_dash_r}"; then + LDFLAGS="-L${withval} -R${withval} ${LDFLAGS}" + else + LDFLAGS="-L${withval} ${LDFLAGS}" + fi + fi + if test -d "$withval/include"; then + CPPFLAGS="-I${withval}/include ${CPPFLAGS}" + else + CPPFLAGS="-I${withval} ${CPPFLAGS}" + fi + need_libevent_autodetect=no + fi + ] +) + +if test "x${need_libevent_autodetect}" != "xno"; then + for path in /usr/local /usr; do + if test -f "${path}/include/event.h"; then + CPPFLAGS="-I${path}/include ${CPPFLAGS}" + LDFLAGS="-L${path}/lib ${LDFLAGS}" + fi + done +fi + +AC_CHECK_HEADER([event.h], ,[AC_MSG_ERROR([*** event.h missing - please install libevent ***])], +[#include <sys/types.h>]) +LIBS="-levent $LIBS" +AC_MSG_CHECKING([if programs using libevent functions will link]) +AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[ + #include <event.h> + ]], [[ + event_base_new(); + ]])], + [ + AC_MSG_RESULT([yes]) + ], + [ + AC_MSG_RESULT([no]) + ] +) + + +#l2174 (customized, bu adding -lssl to LIBS) +# Search for OpenSSL +saved_CPPFLAGS="$CPPFLAGS" +saved_LDFLAGS="$LDFLAGS" +AC_ARG_WITH([libssl], + [ --with-libssl=PATH Specify path to libssl installation ], + [ + if test "x$withval" != "xno"; then + case "$withval" in + # Relative paths + ./*|../*) withval="`pwd`/$withval" + esac + if test -d "$withval/lib"; then + if test -n "${need_dash_r}"; then + LDFLAGS="-L${withval}/lib -R${withval}/lib ${LDFLAGS}" + else + LDFLAGS="-L${withval}/lib ${LDFLAGS}" + fi + elif test -d "$withval/lib64"; then + if test -n "${need_dash_r}"; then + LDFLAGS="-L${withval}/lib64 -R${withval}/lib64 ${LDFLAGS}" + else + LDFLAGS="-L${withval}/lib64 ${LDFLAGS}" + fi + else + if test -n "${need_dash_r}"; then + LDFLAGS="-L${withval} -R${withval} ${LDFLAGS}" + else + LDFLAGS="-L${withval} ${LDFLAGS}" + fi + fi + if test -d "$withval/include"; then + CPPFLAGS="-I${withval}/include ${CPPFLAGS}" + else + CPPFLAGS="-I${withval} ${CPPFLAGS}" + fi + fi + ] +) +## XXX chl -lssl manually added +LIBS="-lcrypto -lssl $LIBS" +AC_TRY_LINK_FUNC([RAND_add], [AC_DEFINE([HAVE_OPENSSL], [1], + [Define if your ssl headers are included + with #include <openssl/header.h>])], + [ + dnl Check default openssl install dir + if test -n "${need_dash_r}"; then + LDFLAGS="-L/usr/local/ssl/lib -R/usr/local/ssl/lib ${saved_LDFLAGS}" + else + LDFLAGS="-L/usr/local/ssl/lib ${saved_LDFLAGS}" + fi + CPPFLAGS="-I/usr/local/ssl/include ${saved_CPPFLAGS}" + AC_CHECK_HEADER([openssl/opensslv.h], , + [AC_MSG_ERROR([*** LibreSSL headers missing - please install first or check config.log ***])]) + AC_TRY_LINK_FUNC([RAND_add], [AC_DEFINE([HAVE_OPENSSL])], + [ + AC_MSG_ERROR([*** Can't find recent LibreSSL libcrypto (see config.log for details) ***]) + ] + ) + ] +) + + +LIBS="-lcrypto -lssl $LIBS" +AC_MSG_CHECKING([whether SSL_CTX_use_certificate_chain_mem is available]) +AC_TRY_LINK_FUNC([SSL_CTX_use_certificate_chain_mem], + [ + AC_DEFINE([HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM], [1], + [Define if SSL_CTX_use_certificate_chain_mem exists in libssl]) + AC_MSG_RESULT([yes]) + ], + [ AC_MSG_RESULT([no])] +) + +LIBS="-lcrypto -lssl $LIBS" +AC_MSG_CHECKING([whether ECDSA is available]) +AC_TRY_LINK_FUNC([ENGINE_get_ECDSA], + [ + AC_DEFINE([SUPPORT_ECDSA], [1], + [Define if ECDSA is supported]) + AC_MSG_RESULT([yes]) + ], + [ AC_MSG_RESULT([no])] +) + +# Sanity check OpenSSL headers +AC_MSG_CHECKING([whether LibreSSL's headers match the library]) +AC_RUN_IFELSE( + [AC_LANG_PROGRAM([[ +#include <stdlib.h> +#include <string.h> +#include <openssl/opensslv.h> +#include <openssl/crypto.h> + ]], [[ + exit(SSLeay() == OPENSSL_VERSION_NUMBER ? 0 : 1); + ]])], + [ + AC_MSG_RESULT([yes]) + ], + [ + AC_MSG_RESULT([no]) + AC_MSG_ERROR([Your LibreSSL headers do not match your library.]) + ], + [ + AC_MSG_WARN([cross compiling: not checking]) + ] +) + +AC_MSG_CHECKING([if programs using LibreSSL functions will link]) +AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[ #include <openssl/evp.h> ]], + [[ SSLeay_add_all_algorithms(); ]])], + [ + AC_MSG_RESULT([yes]) + ], + [ + AC_MSG_RESULT([no]) + saved_LIBS="$LIBS" + LIBS="$LIBS -ldl" + AC_MSG_CHECKING([if programs using LibreSSL need -ldl]) + AC_LINK_IFELSE( + [AC_LANG_PROGRAM([[ #include <openssl/evp.h> ]], + [[ SSLeay_add_all_algorithms(); ]])], + [ + AC_MSG_RESULT([yes]) + ], + [ + AC_MSG_RESULT([no]) + LIBS="$saved_LIBS" + ] + ) + ] +) + +AC_CHECK_DECL([LIBRESSL_VERSION_NUMBER], , , [#include <openssl/ssl.h>]) + +#l2371 + + +dnl zlib is required +AC_ARG_WITH([libz], + [ --with-libz=PATH Specify path to libz installation], + [ if test "x$withval" = "xno"; then + AC_MSG_ERROR([*** zlib is required ***]) + elif test "x$withval" != "xyes"; then + if test -d "$withval/lib"; then + if test -n "${need_dash_r}"; then + LDFLAGS="-L${withval}/lib -R${withval}/lib ${LDFLAGS}" + else + LDFLAGS="-L${withval}/lib ${LDFLAGS}" + fi + else + if test -n "${need_dash_r}"; then + LDFLAGS="-L${withval} -R${withval} ${LDFLAGS}" + else + LDFLAGS="-L${withval} ${LDFLAGS}" + fi + fi + if test -d "$withval/include"; then + CPPFLAGS="-I${withval}/include ${CPPFLAGS}" + else + CPPFLAGS="-I${withval} ${CPPFLAGS}" + fi + fi ] +) + +AC_CHECK_HEADER([zlib.h], ,[AC_MSG_ERROR([*** zlib.h missing - please install first or check config.log ***])]) +AC_CHECK_LIB([z], [deflate], , + [ + saved_CPPFLAGS="$CPPFLAGS" + saved_LDFLAGS="$LDFLAGS" + save_LIBS="$LIBS" + dnl Check default zlib install dir + if test -n "${need_dash_r}"; then + LDFLAGS="-L/usr/local/lib -R/usr/local/lib ${saved_LDFLAGS}" + else + LDFLAGS="-L/usr/local/lib ${saved_LDFLAGS}" + fi + CPPFLAGS="-I/usr/local/include ${saved_CPPFLAGS}" + LIBS="$LIBS -lz" + AC_TRY_LINK_FUNC([deflate], [AC_DEFINE([HAVE_LIBZ])], + [ + AC_MSG_ERROR([*** zlib missing - please install first or check config.log ***]) + ] + ) + ] +) + + + + + +AC_ARG_WITH([table-db], + [ --with-table-db Enable building of table-db backend (default=no)], + [ + if test "x$withval" = "xyes"; then + use_db_api=1 + else + use_db_api=0 + fi + ] +) + +if test "x$use_db_api" = "x1"; then +# Search for libdb +AC_CHECK_HEADER(db_185.h, [AC_DEFINE([HAVE_DB_185_H], [], [if you have the <db_185.h> header file]) ] , [ +AC_CHECK_HEADER(db.h, [AC_DEFINE([HAVE_DB_H], [], [if you have the <db.h> header file]) ] , [ +AC_CHECK_HEADER(db1/db.h, [AC_DEFINE([HAVE_DB1_DB_H], [], [if you have the <db1/db.h> header file]) ] , [ + AC_MSG_ERROR([*** Can't find Berkeley DB headers (see config.log for details) ***]) +])])]) +fi + +save_LIBS="$LIBS" + +if test "x$use_db_api" = "x1"; then +DB_LIB= + +for libdb in db db1 c; do + AC_CHECK_LIB($libdb, dbopen, [ DB_LIB="$libdb"; break; ], + AC_CHECK_LIB($libdb, __db185_open, [ DB_LIB="$libdb"; break; ])) +done + +if test -z "$DB_LIB"; then + AC_MSG_ERROR([Berkeley DB not found or not built with --enable-185]) +fi + +DB_LIB="-l$DB_LIB" +AC_SUBST([DB_LIB]) +fi + +LIBS="$save_LIBS" + + +AM_CONDITIONAL([HAVE_DB_API], [test "x$use_db_api" = "x1"]) +AM_COND_IF([HAVE_DB_API], [AC_DEFINE([HAVE_DB_API], [1], [Define to 1 if HAVE_DB_API])]) + + + +if test "$need_libasr" = "no" -a "x$ac_cv_search_event_asr_run" = "xno"; then + LIBS="$LIBS -lasr" +fi + + +LIBS="$LIBS ${SMTPDLIBS}" +##end of chl + + +## +#AM_CONDITIONAL([NEED_BASENAME], [test "x$ac_cv_have_basename" != "xyes"]) +AM_CONDITIONAL([NEED_BASE64], [test "x$ac_cv_search_b64_ntop" = "xno" -a "x$ac_cv_search___b64_ntop" = "xno" ]) +AM_CONDITIONAL([NEED_BASENAME], [test "x$ac_cv_search_basename" = "xno"]) +AM_CONDITIONAL([NEED_CLOCK_GETTIME], [test "x$ac_cv_search_clock_gettime" = "xno"]) +AM_CONDITIONAL([NEED_CLOSEFROM], [test "x$ac_cv_search_closefrom" = "xno"]) +AM_CONDITIONAL([NEED_DAEMON], [test "x$ac_cv_search_daemon" = "xno"]) +AM_CONDITIONAL([NEED_DIRNAME], [test "x$ac_cv_search_dirname" = "xno"]) +AM_CONDITIONAL([NEED_EVENT_ASR_RUN], [test "x$ac_cv_search_event_asr_run" = "xno"]) +AM_CONDITIONAL([NEED_FMT_SCALED], [test "x$ac_cv_search_fmt_scaled" = "xno"]) +AM_CONDITIONAL([NEED_FPARSELN], [test "x$ac_cv_search_fparseln" = "xno"]) +AM_CONDITIONAL([NEED_IMSG], [test "x$ac_cv_search_imsg_init" = "xno"]) +AM_CONDITIONAL([NEED_INET_NET_PTON], [test "x$ac_cv_search_inet_net_pton" = "xno"]) + +AM_CONDITIONAL([NEED_ERR], [test "x$ac_cv_func_err" != "xyes"]) +AM_CONDITIONAL([NEED_ERRC], [test "x$ac_cv_func_errc" != "xyes"]) +AM_CONDITIONAL([NEED_CRYPT_CHECKPASS], [test "x$ac_cv_func_crypt_checkpass" != "xyes"]) +AM_CONDITIONAL([NEED_EXPLICIT_BZERO], [test "x$ac_cv_func_explicit_bzero" != "xyes"]) +AM_CONDITIONAL([NEED_FGETLN], [test "x$ac_cv_func_fgetln" != "xyes"]) +AM_CONDITIONAL([NEED_FREEZERO], [test "x$ac_cv_func_freezero" != "xyes"]) +AM_CONDITIONAL([NEED_GETOPT], [test "x$ac_cv_func_getopt" != "xyes"]) +AM_CONDITIONAL([NEED_GETPEEREID], [test "x$ac_cv_func_getpeereid" != "xyes"]) +AM_CONDITIONAL([NEED_NANOSLEEP], [test "x$ac_cv_func_nanosleep" != "xyes"]) +AM_CONDITIONAL([NEED_PIDFILE], [test "x$ac_cv_func_pidfile" != "xyes"]) +AM_CONDITIONAL([NEED_PIPE2], [test "x$ac_cv_func_pipe2" != "xyes"]) +AM_CONDITIONAL([NEED_REALLOCARRAY], [test "x$ac_cv_func_reallocarray" != "xyes"]) +AM_CONDITIONAL([NEED_RECALLOCARRAY], [test "x$ac_cv_func_recallocarray" != "xyes"]) +AM_CONDITIONAL([NEED_SETPROCTITLE], [test "x$ac_cv_func_setproctitle" != "xyes"]) +AM_CONDITIONAL([NEED_SETEGID], [test "x$ac_cv_func_setegid" != "xyes"]) +AM_CONDITIONAL([NEED_SETEUID], [test "x$ac_cv_func_seteuid" != "xyes"]) +AM_CONDITIONAL([NEED_SETRESGID], [test "x$ac_cv_func_setresgid" != "xyes"]) +AM_CONDITIONAL([NEED_SETRESUID], [test "x$ac_cv_func_setresuid" != "xyes"]) +AM_CONDITIONAL([NEED_SIGNAL], [test "x$ac_cv_func_signal" != "xyes"]) +AM_CONDITIONAL([NEED_STRERROR], [test "x$ac_cv_func_strerror" != "xyes"]) +AM_CONDITIONAL([NEED_STRLCAT], [test "x$ac_cv_func_strlcat" != "xyes"]) +AM_CONDITIONAL([NEED_STRLCPY], [test "x$ac_cv_func_strlcpy" != "xyes"]) +AM_CONDITIONAL([NEED_STRMODE], [test "x$ac_cv_func_strmode" != "xyes"]) +AM_CONDITIONAL([NEED_STRSEP], [test "x$ac_cv_func_strsep" != "xyes"]) +AM_CONDITIONAL([NEED_STRTONUM], [test "x$ac_cv_func_strtonum" != "xyes"]) +AM_CONDITIONAL([NEED_STRNDUP], [test "x$ac_cv_func_strndup" != "xyes"]) +AM_CONDITIONAL([NEED_STRNLEN], [test "x$ac_cv_func_strnlen" != "xyes"]) +AM_CONDITIONAL([NEED_WAITPID], [test "x$ac_cv_func_waitpid" != "xyes"]) +AM_CONDITIONAL([NEED_VIS], [test "x$ac_cv_func_strnvis" != "xyes" -o "x$BROKEN_STRNVIS" = "x1"]) +AM_CONDITIONAL([NEED_USLEEP], [test "x$ac_cv_func_usleep" != "xyes"]) + +AM_CONDITIONAL([NEED_RES_HNOK], [test "x$ac_cv_search_res_hnok" = "xno" -a x"$ac_cv_func_res_hnok" != "xyes" -a x"$need_libasr" = x"yes"]) +AM_CONDITIONAL([NEED_RES_RANDOMID], [test "x$ac_cv_search_res_randomid" = "xno" -a x"$ac_cv_func_res_randomid" != "xyes" -a x"$need_libasr" = x"yes"]) + +AM_CONDITIONAL([NEED_ARC4RANDOM], [test "x$ac_cv_func_arc4random" != "xyes" -a "x$ac_cv_have_decl_LIBRESSL_VERSION_NUMBER" != "xyes"]) +AM_CONDITIONAL([NEED_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM], [test "x$ac_cv_have_decl_LIBRESSL_VERSION_NUMBER" != "xyes"]) + +AM_CONDITIONAL([NEED_PROGNAME], [test "x$ac_cv_libc_defines___progname" != "xyes"]) +## + + +AM_COND_IF([NEED_PROGNAME], [AC_DEFINE([NEED_PROGNAME], [1], [Define to 1 if NEED_PROGNAME])]) +AM_COND_IF([NEED_SETPROCTITLE], [AC_DEFINE([NEED_SETPROCTITLE], [1], [Define to 1 if NEED_SETPROCTITLE])]) + + +AC_CONFIG_FILES([Makefile + openbsd-compat/Makefile + mk/Makefile + mk/mail/Makefile + mk/mail/mail.lmtp/Makefile + mk/mail/mail.maildir/Makefile + mk/mail/mail.mboxfile/Makefile + mk/mail/mail.mda/Makefile + mk/smtpd/Makefile + mk/smtpctl/Makefile + mk/smtp/Makefile + contrib/Makefile + contrib/libexec/Makefile + contrib/libexec/mail.local/Makefile + contrib/libexec/lockspool/Makefile + contrib/libexec/encrypt/Makefile + ]) + +#l4761 +AC_OUTPUT diff --git a/contrib/Makefile.am b/contrib/Makefile.am new file mode 100644 index 00000000..37a8e73a --- /dev/null +++ b/contrib/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = libexec diff --git a/contrib/libexec/Makefile.am b/contrib/libexec/Makefile.am new file mode 100644 index 00000000..0e3a271f --- /dev/null +++ b/contrib/libexec/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = mail.local lockspool encrypt diff --git a/contrib/libexec/encrypt/Makefile.am b/contrib/libexec/encrypt/Makefile.am new file mode 100644 index 00000000..2f96e60d --- /dev/null +++ b/contrib/libexec/encrypt/Makefile.am @@ -0,0 +1,13 @@ +pkglibexec_PROGRAMS = encrypt + +encrypt_SOURCES = encrypt.c +encrypt_SOURCES += $(top_srcdir)/usr.sbin/smtpd/log.c + +AM_CPPFLAGS = -I$(top_srcdir)/openbsd-compat + +LIBCOMPAT = $(top_builddir)/openbsd-compat/libopenbsd.a + +LDADD = $(LIBCOMPAT) + +uninstall-hook: + rmdir $(DESTDIR)$(pkglibexecdir) 2> /dev/null || /bin/true diff --git a/contrib/libexec/encrypt/encrypt.c b/contrib/libexec/encrypt/encrypt.c new file mode 100644 index 00000000..80275921 --- /dev/null +++ b/contrib/libexec/encrypt/encrypt.c @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2013 Sunil Nimmagadda <sunil@sunilnimmagadda.com> + * Copyright (c) 2013 Gilles Chehade <gilles@poolp.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifdef HAVE_CRYPT_H +#include <crypt.h> /* needed for crypt() */ +#endif +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define PASSWORD_LEN 128 +#define SALT_LEN 16 + +static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + +static void to64(char *, long int, int); +static void print_passwd(const char *); + +int +main(int argc, char *argv[]) +{ + char *line; + size_t linesz; + ssize_t linelen; + + if (argc > 2) { + fprintf(stderr, "usage: encrypt <string>\n"); + return (1); + } + + if (argc == 2) { + print_passwd(argv[1]); + return (0); + } + + line = NULL; + linesz = 0; + while ((linelen = getline(&line, &linesz, stdin)) != -1) { + if (line[linelen - 1] == '\n') + line[linelen - 1] = '\0'; + print_passwd(line); + } + free(line); + + return (0); +} + +void +print_passwd(const char *string) +{ + const char *ids[] = { "2a", "6", "5", "3", "2", "1", NULL }; + const char *id; + char salt[SALT_LEN+1]; + char buffer[PASSWORD_LEN]; + int n; + const char *p; + + for (n = 0; n < SALT_LEN; ++n) + to64(&salt[n], arc4random_uniform(0xff), 1); + salt[SALT_LEN] = '\0'; + + for (n = 0; ids[n]; n++) { + id = ids[n]; + (void)snprintf(buffer, sizeof buffer, "$%s$%s$", id, salt); + if ((p = crypt(string, buffer)) == NULL) + continue; + if (strncmp(p, buffer, strlen(buffer)) != 0) + continue; + printf("%s\n", p); + return; + } + + salt[2] = 0; + printf("%s\n", crypt(string, salt)); +} + +void +to64(char *s, long int v, int n) +{ + while (--n >= 0) { + *s++ = itoa64[v & 0x3f]; + v >>= 6; + } +} diff --git a/contrib/libexec/lockspool/Makefile.am b/contrib/libexec/lockspool/Makefile.am new file mode 100644 index 00000000..2801c101 --- /dev/null +++ b/contrib/libexec/lockspool/Makefile.am @@ -0,0 +1,20 @@ +pkglibexec_PROGRAMS = lockspool + +lockspool_SOURCES = lockspool.c +lockspool_SOURCES += locking.c +lockspool_SOURCES += $(top_srcdir)/usr.sbin/smtpd/log.c + +EXTRA_DIST = mail.local.h pathnames.h + +AM_CPPFLAGS = -I$(top_srcdir)/openbsd-compat -I$(top_srcdir)/mail.local + +LIBCOMPAT = $(top_builddir)/openbsd-compat/libopenbsd.a + +LDADD = $(LIBCOMPAT) + +install-exec-hook: $(CONFIGFILES) $(MANPAGES) + chown root $(DESTDIR)$(pkglibexecdir)/lockspool || true + chmod 4555 $(DESTDIR)$(pkglibexecdir)/lockspool || true + +uninstall-hook: + rmdir $(DESTDIR)$(pkglibexecdir) 2> /dev/null || /bin/true diff --git a/contrib/libexec/lockspool/locking.c b/contrib/libexec/lockspool/locking.c new file mode 100644 index 00000000..e4922dd6 --- /dev/null +++ b/contrib/libexec/lockspool/locking.c @@ -0,0 +1,181 @@ +/* $OpenBSD: locking.c,v 1.14 2020/02/09 14:59:20 millert Exp $ */ + +/* + * Copyright (c) 1996-1998 Theo de Raadt <deraadt@theos.com> + * Copyright (c) 1996-1998 David Mazieres <dm@lcs.mit.edu> + * 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. The name of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``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 AUTHORS 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. + */ + +#include "includes.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <pwd.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> +#include <limits.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include "pathnames.h" +#include "mail.local.h" + +static char lpath[PATH_MAX]; + +void +rellock(void) +{ + + if (lpath[0]) + unlink(lpath); +} + +int +getlock(const char *name, struct passwd *pw) +{ + struct stat sb, fsb; + int lfd=-1; + char buf[8*1024]; + int tries = 0; + + (void)snprintf(lpath, sizeof lpath, "%s/%s.lock", + _PATH_MAILDIR, name); + + if (stat(_PATH_MAILDIR, &sb) != -1 && + (sb.st_mode & S_IWOTH) == S_IWOTH) { + /* + * We have a writeable spool, deal with it as + * securely as possible. + */ + time_t ctim = -1; + + seteuid(pw->pw_uid); + if (lstat(lpath, &sb) != -1) + ctim = sb.st_ctime; + while (1) { + /* + * Deal with existing user.lock files + * or directories or symbolic links that + * should not be here. + */ + if (readlink(lpath, buf, sizeof buf-1) != -1) { + if (lstat(lpath, &sb) != -1 && + S_ISLNK(sb.st_mode)) { + seteuid(sb.st_uid); + unlink(lpath); + seteuid(pw->pw_uid); + } + goto again; + } + if ((lfd = open(lpath, O_CREAT|O_WRONLY|O_EXCL|O_EXLOCK, + S_IRUSR|S_IWUSR)) != -1) + break; +again: + if (tries > 10) { + mwarn("%s: %s", lpath, strerror(errno)); + seteuid(0); + return(-1); + } + if (tries > 9 && + (lfd = open(lpath, O_WRONLY|O_EXLOCK, 0)) != -1) { + if (fstat(lfd, &fsb) != -1 && + lstat(lpath, &sb) != -1) { + if (fsb.st_dev == sb.st_dev && + fsb.st_ino == sb.st_ino && + ctim == fsb.st_ctime ) { + seteuid(fsb.st_uid); + baditem(lpath); + seteuid(pw->pw_uid); + } + } + close(lfd); + } + sleep(1U << tries); + tries++; + continue; + } + seteuid(0); + } else { + /* + * Only root can write the spool directory. + */ + while (1) { + if ((lfd = open(lpath, O_CREAT|O_WRONLY|O_EXCL, + S_IRUSR|S_IWUSR)) != -1) + break; + if (tries > 9) { + mwarn("%s: %s", lpath, strerror(errno)); + return(-1); + } + sleep(1U << tries); + tries++; + } + } + return(lfd); +} + +void +baditem(char *path) +{ + char npath[PATH_MAX]; + int fd; + + if (unlink(path) == 0) + return; + snprintf(npath, sizeof npath, "%s/mailXXXXXXXXXX", _PATH_MAILDIR); + if ((fd = mkstemp(npath)) == -1) + return; + close(fd); + if (rename(path, npath) == -1) + unlink(npath); + else + mwarn("nasty spool item %s renamed to %s", path, npath); + /* XXX if we fail to rename, another attempt will happen later */ +} + +void +mwarn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsyslog(LOG_ERR, fmt, ap); + va_end(ap); +} + +void +merr(int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsyslog(LOG_ERR, fmt, ap); + va_end(ap); + exit(eval); +} diff --git a/contrib/libexec/lockspool/lockspool.1 b/contrib/libexec/lockspool/lockspool.1 new file mode 100644 index 00000000..ea5524bf --- /dev/null +++ b/contrib/libexec/lockspool/lockspool.1 @@ -0,0 +1,77 @@ +.\" $OpenBSD: lockspool.1,v 1.14 2019/01/25 00:19:26 millert Exp $ +.\" +.\" Copyright (c) 1998 Todd C. Miller <millert@openbsd.org> +.\" +.\" 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. +.\" +.Dd $Mdocdate: January 25 2019 $ +.Dt LOCKSPOOL 1 +.Os +.Sh NAME +.Nm lockspool +.Nd lock user's system mailbox +.Sh SYNOPSIS +.Nm lockspool +.Op Ar username +.Sh DESCRIPTION +.Nm +is useful for a client mail program to attain proper locking. +.Nm +obtains a +.Pa username.lock +for the calling user and retains it until stdin is closed or a signal like +.Dv SIGINT , +.Dv SIGTERM , +or +.Dv SIGHUP +is received. +Additionally, the superuser may specify the name of a user in order +to lock a different mailbox. +.Pp +If +.Nm +is able to create the lock file, +.Dq 1 +is written to stdout, otherwise +.Dq 0 +is written and an error message is written to stderr. +.Nm +will try up to 10 times to get the lock (sleeping +for a short period in between tries). +.Pp +Typical usage is for a user mail agent (such as +.Xr mail 1 ) +to open a pipe to +.Nm +when it needs to lock the user's mail spool. +Closing the pipe will cause +.Nm +to release the lock. +.Sh FILES +.Bl -tag -width /var/mail/username.lock -compact +.It Pa /var/mail/username.lock +user's mail lock file +.El +.Sh EXIT STATUS +The +.Nm +utility exits 0 on success, and 1 if an error occurs. +.Sh SEE ALSO +.Xr mail 1 , +.Xr mail.local 8 , +.Xr smtpd 8 +.Sh HISTORY +The +.Nm +program appeared in +.Ox 2.4 . diff --git a/contrib/libexec/lockspool/lockspool.c b/contrib/libexec/lockspool/lockspool.c new file mode 100644 index 00000000..9277241b --- /dev/null +++ b/contrib/libexec/lockspool/lockspool.c @@ -0,0 +1,124 @@ +/* $OpenBSD: lockspool.c,v 1.21 2020/02/09 14:59:20 millert Exp $ */ + +/* + * Copyright (c) 1998 Theo de Raadt <deraadt@theos.com> + * Copyright (c) 1998 Todd C. Miller <millert@openbsd.org> + * 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. + * + * THIS SOFTWARE IS PROVIDED ``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 AUTHORS 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. + */ + +#include "includes.h" + +#include <signal.h> +#include <pwd.h> +#include <syslog.h> +#include <unistd.h> +#include <errno.h> +#include <stdio.h> +#include <paths.h> +#include <stdlib.h> +#include <poll.h> +#include <err.h> + +#include "mail.local.h" + +void unhold(int); +void usage(void); + +extern char *__progname; + +int +main(int argc, char *argv[]) +{ + struct passwd *pw; + struct pollfd pfd; + ssize_t nread; + char *from, c; + int holdfd; + +#if HAVE_UNVEIL + if (unveil(_PATH_MAILDIR, "rwc") == -1) + err(1, "unveil"); +#endif +#if HAVE_PLEDGE + if (pledge("stdio rpath wpath getpw cpath fattr", NULL) == -1) + err(1, "pledge"); +#endif + + openlog(__progname, LOG_PERROR, LOG_MAIL); + + if (argc != 1 && argc != 2) + usage(); + if (argc == 2 && getuid() != 0) + merr(1, "you must be root to lock someone else's spool"); + + signal(SIGTERM, unhold); + signal(SIGINT, unhold); + signal(SIGHUP, unhold); + signal(SIGPIPE, unhold); + + if (argc == 2) + pw = getpwnam(argv[1]); + else + pw = getpwuid(getuid()); + if (pw == NULL) + exit (1); + from = pw->pw_name; + + holdfd = getlock(from, pw); + if (holdfd == -1) { + write(STDOUT_FILENO, "0\n", 2); + exit (1); + } + write(STDOUT_FILENO, "1\n", 2); + + /* wait for the other end of the pipe to close, then release the lock */ + pfd.fd = STDIN_FILENO; + pfd.events = POLLIN; + do { + if (poll(&pfd, 1, INFTIM) == -1) { + if (errno != EINTR) + break; + } + do { + nread = read(STDIN_FILENO, &c, 1); + } while (nread == 1 || (nread == -1 && errno == EINTR)); + } while (nread == -1 && errno == EAGAIN); + rellock(); + exit (0); +} + +/*ARGSUSED*/ +void +unhold(int signo) +{ + + rellock(); + _exit(0); +} + +void +usage(void) +{ + + merr(1, "usage: %s [username]", __progname); +} diff --git a/contrib/libexec/lockspool/mail.local.h b/contrib/libexec/lockspool/mail.local.h new file mode 100644 index 00000000..bc3137cb --- /dev/null +++ b/contrib/libexec/lockspool/mail.local.h @@ -0,0 +1,42 @@ +/* $OpenBSD: mail.local.h,v 1.7 2020/02/09 14:59:21 millert Exp $ */ + +/*- + * Copyright (c) 1990 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. + */ + +void baditem(char *); +int deliver(int, char *, int); +void merr(int, const char *, ...); +void mwarn(const char *, ...); +int getlock(const char *, struct passwd *); +void notifybiff(char *); +void rellock(void); +int storemail(char *); +int lockspool(const char *, struct passwd *); +void unlockspool(void); +void usage(void); diff --git a/contrib/libexec/lockspool/pathnames.h b/contrib/libexec/lockspool/pathnames.h new file mode 100644 index 00000000..0a2c2731 --- /dev/null +++ b/contrib/libexec/lockspool/pathnames.h @@ -0,0 +1,38 @@ +/* $OpenBSD: pathnames.h,v 1.5 2003/06/02 19:38:24 millert Exp $*/ + +/*- + * Copyright (c) 1990 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. + * + * from: @(#)pathnames.h 5.3 (Berkeley) 1/17/91 + */ +#ifdef HAVE_PATHS_H +#include <paths.h> +#endif + +#define _PATH_LOCTMP "/tmp/local.XXXXXXXXXX" +#define _PATH_LOCKSPOOL PATH_LIBEXEC"/lockspool" diff --git a/contrib/libexec/mail.local/Makefile.am b/contrib/libexec/mail.local/Makefile.am new file mode 100644 index 00000000..217659c1 --- /dev/null +++ b/contrib/libexec/mail.local/Makefile.am @@ -0,0 +1,22 @@ +pkglibexec_PROGRAMS = mail.local + +mail_local_SOURCES = mail.local.c +mail_local_SOURCES += locking.c +mail_local_SOURCES += $(top_srcdir)/usr.sbin/smtpd/log.c + +EXTRA_DIST = mail.local.h pathnames.h + +AM_CPPFLAGS = -I$(top_srcdir)/openbsd-compat -DPATH_LIBEXEC=\"$(pkglibexecdir)\" + +LIBCOMPAT = $(top_builddir)/openbsd-compat/libopenbsd.a + +LDADD = $(LIBCOMPAT) + +# need to define _GNU_SOURCE to get: +# EAI_NODATA defined +# {v,}asprintf +# setres{g,u}id +#CFLAGS += -D_GNU_SOURCE + +uninstall-hook: + rmdir $(DESTDIR)$(pkglibexecdir) 2> /dev/null || /bin/true diff --git a/contrib/libexec/mail.local/locking.c b/contrib/libexec/mail.local/locking.c new file mode 100644 index 00000000..85a48d5e --- /dev/null +++ b/contrib/libexec/mail.local/locking.c @@ -0,0 +1,182 @@ +/* $OpenBSD: locking.c,v 1.14 2020/02/09 14:59:20 millert Exp $ */ + +/* + * Copyright (c) 1996-1998 Theo de Raadt <deraadt@theos.com> + * Copyright (c) 1996-1998 David Mazieres <dm@lcs.mit.edu> + * 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. The name of the authors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``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 AUTHORS 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. + */ + +#include "includes.h" + +#include <sys/types.h> + +#include <sys/stat.h> +#include <fcntl.h> +#include <pwd.h> +#include <syslog.h> +#include <time.h> +#include <unistd.h> +#include <limits.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include "pathnames.h" +#include "mail.local.h" + +static char lpath[PATH_MAX]; + +void +rellock(void) +{ + + if (lpath[0]) + unlink(lpath); +} + +int +getlock(const char *name, struct passwd *pw) +{ + struct stat sb, fsb; + int lfd=-1; + char buf[8*1024]; + int tries = 0; + + (void)snprintf(lpath, sizeof lpath, "%s/%s.lock", + _PATH_MAILDIR, name); + + if (stat(_PATH_MAILDIR, &sb) != -1 && + (sb.st_mode & S_IWOTH) == S_IWOTH) { + /* + * We have a writeable spool, deal with it as + * securely as possible. + */ + time_t ctim = -1; + + seteuid(pw->pw_uid); + if (lstat(lpath, &sb) != -1) + ctim = sb.st_ctime; + while (1) { + /* + * Deal with existing user.lock files + * or directories or symbolic links that + * should not be here. + */ + if (readlink(lpath, buf, sizeof buf-1) != -1) { + if (lstat(lpath, &sb) != -1 && + S_ISLNK(sb.st_mode)) { + seteuid(sb.st_uid); + unlink(lpath); + seteuid(pw->pw_uid); + } + goto again; + } + if ((lfd = open(lpath, O_CREAT|O_WRONLY|O_EXCL|O_EXLOCK, + S_IRUSR|S_IWUSR)) != -1) + break; +again: + if (tries > 10) { + mwarn("%s: %s", lpath, strerror(errno)); + seteuid(0); + return(-1); + } + if (tries > 9 && + (lfd = open(lpath, O_WRONLY|O_EXLOCK, 0)) != -1) { + if (fstat(lfd, &fsb) != -1 && + lstat(lpath, &sb) != -1) { + if (fsb.st_dev == sb.st_dev && + fsb.st_ino == sb.st_ino && + ctim == fsb.st_ctime ) { + seteuid(fsb.st_uid); + baditem(lpath); + seteuid(pw->pw_uid); + } + } + close(lfd); + } + sleep(1U << tries); + tries++; + continue; + } + seteuid(0); + } else { + /* + * Only root can write the spool directory. + */ + while (1) { + if ((lfd = open(lpath, O_CREAT|O_WRONLY|O_EXCL, + S_IRUSR|S_IWUSR)) != -1) + break; + if (tries > 9) { + mwarn("%s: %s", lpath, strerror(errno)); + return(-1); + } + sleep(1U << tries); + tries++; + } + } + return(lfd); +} + +void +baditem(char *path) +{ + char npath[PATH_MAX]; + int fd; + + if (unlink(path) == 0) + return; + snprintf(npath, sizeof npath, "%s/mailXXXXXXXXXX", _PATH_MAILDIR); + if ((fd = mkstemp(npath)) == -1) + return; + close(fd); + if (rename(path, npath) == -1) + unlink(npath); + else + mwarn("nasty spool item %s renamed to %s", path, npath); + /* XXX if we fail to rename, another attempt will happen later */ +} + +void +mwarn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsyslog(LOG_ERR, fmt, ap); + va_end(ap); +} + +void +merr(int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vsyslog(LOG_ERR, fmt, ap); + va_end(ap); + exit(eval); +} diff --git a/contrib/libexec/mail.local/mail.local.8 b/contrib/libexec/mail.local/mail.local.8 new file mode 100644 index 00000000..330a4473 --- /dev/null +++ b/contrib/libexec/mail.local/mail.local.8 @@ -0,0 +1,183 @@ +.\" $OpenBSD: mail.local.8,v 1.31 2014/09/16 21:28:51 jmc Exp $ +.\" Copyright (c) 1990 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. +.\" +.\" from: @(#)mail.local.8 6.8 (Berkeley) 4/27/91 +.\" +.Dd $Mdocdate: September 16 2014 $ +.Dt MAIL.LOCAL 8 +.Os +.Sh NAME +.Nm mail.local +.Nd store mail in a mailbox +.Sh SYNOPSIS +.Nm mail.local +.Op Fl Ll +.Op Fl f Ar from +.Ar user ... +.Sh DESCRIPTION +.Nm +reads the standard input up to an end-of-file and appends it to each +.Ar user Ns 's +.Pa mail +file. +The +.Ar user +must be a valid user name. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl f Ar from +Specify the sender's name. +.It Fl L +Don't create a +.Pa username.lock +file while locking the spool. +.It Fl l +For compatibility, request that files named +.Pa username.lock +be used for locking. +(This is the default behavior.) +.El +.Pp +Individual mail messages in the mailbox are delimited by an empty +line followed by a line beginning with the string +.Dq "From\&\ " . +A line containing the string +.Dq "From\&\ " , +the sender's name and a timestamp is prepended to each delivered mail message. +A blank line is appended to each message. +A greater-than character +.Pq Ql > +is prepended to any line in the message which could be mistaken for a +.Dq "From\&\ " +delimiter line. +.Pp +Significant efforts have been made to ensure that +.Nm +acts as securely as possible if the spool directory is mode 1777 or 755. +The default of mode 755 is more secure, but it prevents mail clients from using +.Pa username.lock +style locking. +The use of 1777 is more flexible in an NFS shared-spool environment, +so many sites use it. +However, it does carry some risks, such as attackers filling the spool disk. +Some of these problems may be alleviated +by making the spool a separate filesystem, and placing quotas on it. +The use of any mode other than 1777 and 755 for the spool directory is +recommended against but may work properly. +.Pp +The mailbox is always locked using +.Xr flock 2 +while mail is appended. +Unless the +.Fl L +flag is specified, a +.Pa username.lock +file is also used. +.Pp +If the +.Xr biff 1 +service is returned by +.Xr getservbyname 3 , +the biff server is notified of delivered mail. +.Sh ENVIRONMENT +.Bl -tag -width indent +.It Ev TZ +Used to set the appropriate time zone on the timestamp. +.El +.Sh FILES +.Bl -tag -width /tmp/local.XXXXXXXXXX -compact +.It Pa /tmp/local.XXXXXXXXXX +temporary files +.It Pa /var/mail/user +user's mailbox directory +.El +.Sh EXIT STATUS +.Ex -std mail.local +.Sh SEE ALSO +.Xr biff 1 , +.Xr mail 1 , +.Xr flock 2 , +.Xr getservbyname 3 , +.Xr comsat 8 , +.Xr smtpd 8 +.Sh HISTORY +A superset of +.Nm +(handling mailbox reading as well as mail delivery) appeared in +.At v7 +as the program +.Xr mail 1 . +.Sh BUGS +Using quotas in +.Pa /var/mail +can be problematic if using +.Xr sendmail 8 +as an MTA, +since it asks +.Nm +to deliver a message to multiple recipients if possible. +This causes problems in a quota environment since a message may be +delivered to some users but not others due to disk quotas. +Even though the message was delivered to some of the recipients, +.Nm +will exit with an exit code > 0, causing +.Xr sendmail 8 +to attempt redelivery later. +That means that some users will keep getting the same message every time +.Xr sendmail 8 +runs its queue. +This problem does not exist for +.Xr smtpd 8 +users. +.Pp +If you are running +.Xr sendmail 8 +and have disk quotas on +.Pa /var/mail +it is imperative that you unset the +.Dq m +mailer flag for the +.Sq local +mailer. +To do this, locate the line beginning with +.Dq Mlocal +in +.Pa /etc/mail/sendmail.cf +and remove the +.Dq m +from the flags section, denoted by +.Dq F= . +Alternately, you can override the default mailer flags by adding the line: +.Pp +.Dl define(`LOCAL_MAILER_FLAGS', `rn9S')dnl +.Pp +to your +.Dq \.mc +file (this is the source file that is used to generate +.Pa /etc/mail/sendmail.cf ) . diff --git a/contrib/libexec/mail.local/mail.local.c b/contrib/libexec/mail.local/mail.local.c new file mode 100644 index 00000000..a574b3fe --- /dev/null +++ b/contrib/libexec/mail.local/mail.local.c @@ -0,0 +1,392 @@ +/* $OpenBSD: mail.local.c,v 1.39 2020/02/09 14:59:20 millert Exp $ */ + +/*- + * Copyright (c) 1996-1998 Theo de Raadt <deraadt@theos.com> + * Copyright (c) 1996-1998 David Mazieres <dm@lcs.mit.edu> + * Copyright (c) 1990 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. + */ + +#include "includes.h" + +#include <sys/types.h> + +#include <sys/stat.h> +#include <sys/socket.h> +#include <sys/wait.h> +#include <netinet/in.h> +#include <sysexits.h> +#include <syslog.h> +#include <fcntl.h> +#include <netdb.h> +#include <pwd.h> +#include <time.h> +#include <unistd.h> +#include <limits.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include "pathnames.h" +#include "mail.local.h" + +int +main(int argc, char *argv[]) +{ + struct passwd *pw; + int ch, fd, eval, lockfile=1; + uid_t uid; + char *from; + + openlog("mail.local", LOG_PERROR, LOG_MAIL); + + from = NULL; + while ((ch = getopt(argc, argv, "lLdf:r:")) != -1) + switch (ch) { + case 'd': /* backward compatible */ + break; + case 'f': + case 'r': /* backward compatible */ + if (from) + merr(EX_USAGE, "multiple -f options"); + from = optarg; + break; + case 'l': + lockfile=1; + break; + case 'L': + lockfile=0; + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + if (!*argv) + usage(); + + /* + * If from not specified, use the name from getlogin() if the + * uid matches, otherwise, use the name from the password file + * corresponding to the uid. + */ + uid = getuid(); + if (!from && (!(from = getlogin()) || + !(pw = getpwnam(from)) || pw->pw_uid != uid)) + from = (pw = getpwuid(uid)) ? pw->pw_name : "???"; + + fd = storemail(from); + for (eval = 0; *argv; ++argv) { + if ((ch = deliver(fd, *argv, lockfile)) != 0) + eval = ch; + } + exit(eval); +} + +int +storemail(char *from) +{ + FILE *fp = NULL; + time_t tval; + int fd, eline; + size_t len; + char *line, *tbuf; + + if ((tbuf = strdup(_PATH_LOCTMP)) == NULL) + merr(EX_OSERR, "unable to allocate memory"); + if ((fd = mkstemp(tbuf)) == -1 || !(fp = fdopen(fd, "w+"))) + merr(EX_OSERR, "unable to open temporary file"); + (void)unlink(tbuf); + free(tbuf); + + (void)time(&tval); + (void)fprintf(fp, "From %s %s", from, ctime(&tval)); + + for (eline = 1, tbuf = NULL; (line = fgetln(stdin, &len));) { + /* We have to NUL-terminate the line since fgetln does not */ + if (line[len - 1] == '\n') + line[len - 1] = '\0'; + else { + /* No trailing newline, so alloc space and copy */ + if ((tbuf = malloc(len + 1)) == NULL) + merr(EX_OSERR, "unable to allocate memory"); + memcpy(tbuf, line, len); + tbuf[len] = '\0'; + line = tbuf; + } + if (line[0] == '\0') + eline = 1; + else { + if (eline && line[0] == 'F' && len > 5 && + !memcmp(line, "From ", 5)) + (void)putc('>', fp); + eline = 0; + } + (void)fprintf(fp, "%s\n", line); + if (ferror(fp)) + break; + } + free(tbuf); + + /* Output a newline; note, empty messages are allowed. */ + (void)putc('\n', fp); + (void)fflush(fp); + if (ferror(fp)) + merr(EX_OSERR, "temporary file write error"); + return(fd); +} + +int +deliver(int fd, char *name, int lockfile) +{ + struct stat sb, fsb; + struct passwd *pw; + int mbfd=-1, lfd=-1, rval=EX_OSERR; + char biffmsg[100], buf[8*1024], path[PATH_MAX]; + off_t curoff; + size_t off; + ssize_t nr, nw; + + /* + * Disallow delivery to unknown names -- special mailboxes can be + * handled in the sendmail aliases file. + */ + if (!(pw = getpwnam(name))) { + mwarn("unknown name: %s", name); + return(EX_NOUSER); + } + + (void)snprintf(path, sizeof path, "%s/%s", _PATH_MAILDIR, name); + + if (lockfile) { + lfd = lockspool(name, pw); + if (lfd == -1) + return(EX_OSERR); + } + + /* after this point, always exit via bad to remove lockfile */ +retry: + if (lstat(path, &sb)) { + if (errno != ENOENT) { + mwarn("%s: %s", path, strerror(errno)); + goto bad; + } + if ((mbfd = open(path, O_APPEND|O_CREAT|O_EXCL|O_WRONLY|O_EXLOCK, + S_IRUSR|S_IWUSR)) == -1) { +#ifndef HAVE_O_EXLOCK + /* XXX : do something! */ +#endif + if (errno == EEXIST) { + /* file appeared since lstat */ + goto retry; + } else { + mwarn("%s: %s", path, strerror(errno)); + rval = EX_CANTCREAT; + goto bad; + } + } + /* + * Set the owner and group. Historically, binmail repeated + * this at each mail delivery. We no longer do this, assuming + * that if the ownership or permissions were changed there + * was a reason for doing so. + */ + if (fchown(mbfd, pw->pw_uid, pw->pw_gid) == -1) { + mwarn("chown %u:%u: %s", pw->pw_uid, pw->pw_gid, name); + goto bad; + } + } else { + if (sb.st_nlink != 1 || !S_ISREG(sb.st_mode)) { + mwarn("%s: linked or special file", path); + goto bad; + } + if ((mbfd = open(path, O_APPEND|O_WRONLY|O_EXLOCK, + S_IRUSR|S_IWUSR)) == -1) { + mwarn("%s: %s", path, strerror(errno)); + goto bad; + } + if (fstat(mbfd, &fsb) == -1) { + /* relating error to path may be bad style */ + mwarn("%s: %s", path, strerror(errno)); + goto bad; + } + if (sb.st_dev != fsb.st_dev || sb.st_ino != fsb.st_ino) { + mwarn("%s: changed after open", path); + goto bad; + } + /* paranoia? */ + if (fsb.st_nlink != 1 || !S_ISREG(fsb.st_mode)) { + mwarn("%s: linked or special file", path); + rval = EX_CANTCREAT; + goto bad; + } + } + + curoff = lseek(mbfd, 0, SEEK_END); + (void)snprintf(biffmsg, sizeof biffmsg, "%s@%lld\n", name, curoff); + if (lseek(fd, 0, SEEK_SET) == (off_t)-1) { + mwarn("temporary file: %s", strerror(errno)); + goto bad; + } + + while ((nr = read(fd, buf, sizeof(buf))) > 0) + for (off = 0; off < nr; off += nw) + if ((nw = write(mbfd, buf + off, nr - off)) == -1) { + mwarn("%s: %s", path, strerror(errno)); + (void)ftruncate(mbfd, curoff); + goto bad; + } + + if (nr == 0) { + rval = 0; + } else { + (void)ftruncate(mbfd, curoff); + mwarn("temporary file: %s", strerror(errno)); + } + +bad: + if (lfd != -1) + unlockspool(); + + if (mbfd != -1) { + (void)fsync(mbfd); /* Don't wait for update. */ + (void)close(mbfd); /* Implicit unlock. */ + } + + if (!rval) + notifybiff(biffmsg); + return(rval); +} + +void +notifybiff(char *msg) +{ + static struct addrinfo *res0; + struct addrinfo hints, *res; + static int f = -1; + size_t len; + int error; + + if (res0 == NULL) { + memset(&hints, 0, sizeof(hints)); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_DGRAM; + + error = getaddrinfo("localhost", "biff", &hints, &res0); + if (error) { + /* Be silent if biff service not available. */ + if (error != EAI_SERVICE) { + mwarn("localhost: %s", gai_strerror(error)); + } + return; + } + } + + if (f == -1) { + for (res = res0; res != NULL; res = res->ai_next) { + f = socket(res->ai_family, res->ai_socktype, + res->ai_protocol); + if (f != -1) + break; + } + } + if (f == -1) { + mwarn("socket: %s", strerror(errno)); + return; + } + + len = strlen(msg) + 1; /* XXX */ + if (sendto(f, msg, len, 0, res->ai_addr, res->ai_addrlen) != len) + mwarn("sendto biff: %s", strerror(errno)); +} + +static int lockfd = -1; +static pid_t lockpid = -1; + +int +lockspool(const char *name, struct passwd *pw) +{ + int pfd[2]; + char ch; + + if (geteuid() == 0) + return getlock(name, pw); + + /* If not privileged, open pipe to lockspool(1) instead */ + if (pipe2(pfd, O_CLOEXEC) == -1) { + merr(EX_OSERR, "pipe: %s", strerror(errno)); + return -1; + } + + signal(SIGPIPE, SIG_IGN); + switch ((lockpid = fork())) { + case -1: + merr(EX_OSERR, "fork: %s", strerror(errno)); + return -1; + case 0: + /* child */ + close(pfd[0]); + dup2(pfd[1], STDOUT_FILENO); + execl(_PATH_LOCKSPOOL, "lockspool", (char *)NULL); + merr(EX_OSERR, "execl: lockspool: %s", strerror(errno)); + /* NOTREACHED */ + break; + default: + /* parent */ + close(pfd[1]); + lockfd = pfd[0]; + break; + } + + if (read(lockfd, &ch, 1) != 1 || ch != '1') { + unlockspool(); + merr(EX_OSERR, "lockspool: unable to get lock"); + } + + return lockfd; +} + +void +unlockspool(void) +{ + if (lockpid != -1) { + waitpid(lockpid, NULL, 0); + lockpid = -1; + } else { + rellock(); + } + close(lockfd); + lockfd = -1; +} + +void +usage(void) +{ + merr(EX_USAGE, "usage: mail.local [-Ll] [-f from] user ..."); +} diff --git a/contrib/libexec/mail.local/mail.local.h b/contrib/libexec/mail.local/mail.local.h new file mode 100644 index 00000000..bc3137cb --- /dev/null +++ b/contrib/libexec/mail.local/mail.local.h @@ -0,0 +1,42 @@ +/* $OpenBSD: mail.local.h,v 1.7 2020/02/09 14:59:21 millert Exp $ */ + +/*- + * Copyright (c) 1990 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. + */ + +void baditem(char *); +int deliver(int, char *, int); +void merr(int, const char *, ...); +void mwarn(const char *, ...); +int getlock(const char *, struct passwd *); +void notifybiff(char *); +void rellock(void); +int storemail(char *); +int lockspool(const char *, struct passwd *); +void unlockspool(void); +void usage(void); diff --git a/contrib/libexec/mail.local/pathnames.h b/contrib/libexec/mail.local/pathnames.h new file mode 100644 index 00000000..0a2c2731 --- /dev/null +++ b/contrib/libexec/mail.local/pathnames.h @@ -0,0 +1,38 @@ +/* $OpenBSD: pathnames.h,v 1.5 2003/06/02 19:38:24 millert Exp $*/ + +/*- + * Copyright (c) 1990 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. + * + * from: @(#)pathnames.h 5.3 (Berkeley) 1/17/91 + */ +#ifdef HAVE_PATHS_H +#include <paths.h> +#endif + +#define _PATH_LOCTMP "/tmp/local.XXXXXXXXXX" +#define _PATH_LOCKSPOOL PATH_LIBEXEC"/lockspool" diff --git a/etc/README.md b/etc/README.md new file mode 100644 index 00000000..0c53cc6b --- /dev/null +++ b/etc/README.md @@ -0,0 +1,6 @@ +This directory will contain example OpenSMTPD config files that can be used as +a reference or for testing specific usecases. Tests that are run as part of +CI/CD process in docker containers will utilize these files. + + +* `aliases` file - default aliases map that is referenced by default OpenSMTPD config. diff --git a/etc/aliases b/etc/aliases new file mode 100644 index 00000000..09fb6cf5 --- /dev/null +++ b/etc/aliases @@ -0,0 +1,100 @@ +# +# $OpenBSD: aliases,v 1.67 2019/01/26 10:58:05 florian Exp $ +# +# Aliases in this file will NOT be expanded in the header from +# Mail, but WILL be visible over networks or from /usr/libexec/mail.local. +# +# >>>>>>>>>> The program "newaliases" must be run after +# >> NOTE >> this file is updated for any changes to +# >>>>>>>>>> show through to smtpd. +# + +# Basic system aliases -- these MUST be present +MAILER-DAEMON: postmaster +postmaster: root + +# General redirections for important pseudo accounts +daemon: root +ftp-bugs: root +operator: root +www: root + +# Redirections for pseudo accounts that should not receive mail +_bgpd: /dev/null +_dhcp: /dev/null +_dpb: /dev/null +_dvmrpd: /dev/null +_eigrpd: /dev/null +_file: /dev/null +_fingerd: /dev/null +_ftp: /dev/null +_hostapd: /dev/null +_identd: /dev/null +_iked: /dev/null +_isakmpd: /dev/null +_iscsid: /dev/null +_ldapd: /dev/null +_ldpd: /dev/null +_mopd: /dev/null +_nsd: /dev/null +_ntp: /dev/null +_ospfd: /dev/null +_ospf6d: /dev/null +_pbuild: /dev/null +_pfetch: /dev/null +_pflogd: /dev/null +_ping: /dev/null +_pkgfetch: /dev/null +_pkguntar: /dev/null +_portmap: /dev/null +_ppp: /dev/null +_rad: /dev/null +_radiusd: /dev/null +_rbootd: /dev/null +_relayd: /dev/null +_rebound: /dev/null +_ripd: /dev/null +_rstatd: /dev/null +_rusersd: /dev/null +_rwalld: /dev/null +_smtpd: /dev/null +_smtpq: /dev/null +_sndio: /dev/null +_snmpd: /dev/null +_spamd: /dev/null +_switchd: /dev/null +_syslogd: /dev/null +_tcpdump: /dev/null +_traceroute: /dev/null +_tftpd: /dev/null +_unbound: /dev/null +_unwind: /dev/null +_vmd: /dev/null +_x11: /dev/null +_ypldap: /dev/null +bin: /dev/null +build: /dev/null +nobody: /dev/null +_tftp_proxy: /dev/null +_ftp_proxy: /dev/null +_sndiop: /dev/null +_syspatch: /dev/null +_slaacd: /dev/null +sshd: /dev/null + +# Well-known aliases -- these should be filled in! +# root: +# manager: +# dumper: + +# RFC 2142: NETWORK OPERATIONS MAILBOX NAMES +abuse: root +# noc: root +security: root + +# RFC 2142: SUPPORT MAILBOX NAMES FOR SPECIFIC INTERNET SERVICES +# hostmaster: root +# usenet: root +# news: usenet +# webmaster: root +# ftp: root diff --git a/m4/aclocal-openssh.m4 b/m4/aclocal-openssh.m4 new file mode 100644 index 00000000..2944bb99 --- /dev/null +++ b/m4/aclocal-openssh.m4 @@ -0,0 +1,179 @@ +dnl $Id: aclocal.m4,v 1.13 2014/01/22 10:30:12 djm Exp $ +dnl +dnl OpenSSH-specific autoconf macros +dnl + +dnl OSSH_CHECK_CFLAG_COMPILE(check_flag[, define_flag]) +dnl Check that $CC accepts a flag 'check_flag'. If it is supported append +dnl 'define_flag' to $CFLAGS. If 'define_flag' is not specified, then append +dnl 'check_flag'. +AC_DEFUN([OSSH_CHECK_CFLAG_COMPILE], [{ + AC_MSG_CHECKING([if $CC supports compile flag $1]) + saved_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $WERROR $1" + _define_flag="$2" + test "x$_define_flag" = "x" && _define_flag="$1" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ +#include <stdlib.h> +#include <stdio.h> +int main(int argc, char **argv) { + /* Some math to catch -ftrapv problems in the toolchain */ + int i = 123 * argc, j = 456 + argc, k = 789 - argc; + float l = i * 2.1; + double m = l / 0.5; + long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; + printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); + exit(0); +} + ]])], + [ +if `grep -i "unrecognized option" conftest.err >/dev/null` +then + AC_MSG_RESULT([no]) + CFLAGS="$saved_CFLAGS" +else + AC_MSG_RESULT([yes]) + CFLAGS="$saved_CFLAGS $_define_flag" +fi], + [ AC_MSG_RESULT([no]) + CFLAGS="$saved_CFLAGS" ] + ) +}]) + +dnl OSSH_CHECK_CFLAG_LINK(check_flag[, define_flag]) +dnl Check that $CC accepts a flag 'check_flag'. If it is supported append +dnl 'define_flag' to $CFLAGS. If 'define_flag' is not specified, then append +dnl 'check_flag'. +AC_DEFUN([OSSH_CHECK_CFLAG_LINK], [{ + AC_MSG_CHECKING([if $CC supports compile flag $1 and linking succeeds]) + saved_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $WERROR $1" + _define_flag="$2" + test "x$_define_flag" = "x" && _define_flag="$1" + AC_LINK_IFELSE([AC_LANG_SOURCE([[ +#include <stdlib.h> +#include <stdio.h> +int main(int argc, char **argv) { + /* Some math to catch -ftrapv problems in the toolchain */ + int i = 123 * argc, j = 456 + argc, k = 789 - argc; + float l = i * 2.1; + double m = l / 0.5; + long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; + printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); + exit(0); +} + ]])], + [ +if `grep -i "unrecognized option" conftest.err >/dev/null` +then + AC_MSG_RESULT([no]) + CFLAGS="$saved_CFLAGS" +else + AC_MSG_RESULT([yes]) + CFLAGS="$saved_CFLAGS $_define_flag" +fi], + [ AC_MSG_RESULT([no]) + CFLAGS="$saved_CFLAGS" ] + ) +}]) + +dnl OSSH_CHECK_LDFLAG_LINK(check_flag[, define_flag]) +dnl Check that $LD accepts a flag 'check_flag'. If it is supported append +dnl 'define_flag' to $LDFLAGS. If 'define_flag' is not specified, then append +dnl 'check_flag'. +AC_DEFUN([OSSH_CHECK_LDFLAG_LINK], [{ + AC_MSG_CHECKING([if $LD supports link flag $1]) + saved_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $WERROR $1" + _define_flag="$2" + test "x$_define_flag" = "x" && _define_flag="$1" + AC_LINK_IFELSE([AC_LANG_SOURCE([[ +#include <stdlib.h> +#include <stdio.h> +int main(int argc, char **argv) { + /* Some math to catch -ftrapv problems in the toolchain */ + int i = 123 * argc, j = 456 + argc, k = 789 - argc; + float l = i * 2.1; + double m = l / 0.5; + long long int n = argc * 12345LL, o = 12345LL * (long long int)argc; + printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); + exit(0); +} + ]])], + [ AC_MSG_RESULT([yes]) + LDFLAGS="$saved_LDFLAGS $_define_flag"], + [ AC_MSG_RESULT([no]) + LDFLAGS="$saved_LDFLAGS" ] + ) +}]) + +dnl OSSH_CHECK_HEADER_FOR_FIELD(field, header, symbol) +dnl Does AC_EGREP_HEADER on 'header' for the string 'field' +dnl If found, set 'symbol' to be defined. Cache the result. +dnl TODO: This is not foolproof, better to compile and read from there +AC_DEFUN([OSSH_CHECK_HEADER_FOR_FIELD], [ +# look for field '$1' in header '$2' + dnl This strips characters illegal to m4 from the header filename + ossh_safe=`echo "$2" | sed 'y%./+-%__p_%'` + dnl + ossh_varname="ossh_cv_$ossh_safe""_has_"$1 + AC_MSG_CHECKING(for $1 field in $2) + AC_CACHE_VAL($ossh_varname, [ + AC_EGREP_HEADER($1, $2, [ dnl + eval "$ossh_varname=yes" dnl + ], [ dnl + eval "$ossh_varname=no" dnl + ]) dnl + ]) + ossh_result=`eval 'echo $'"$ossh_varname"` + if test -n "`echo $ossh_varname`"; then + AC_MSG_RESULT($ossh_result) + if test "x$ossh_result" = "xyes"; then + AC_DEFINE($3, 1, [Define if you have $1 in $2]) + fi + else + AC_MSG_RESULT(no) + fi +]) + +dnl Check for socklen_t: historically on BSD it is an int, and in +dnl POSIX 1g it is a type of its own, but some platforms use different +dnl types for the argument to getsockopt, getpeername, etc. So we +dnl have to test to find something that will work. +AC_DEFUN([TYPE_SOCKLEN_T], +[ + AC_CHECK_TYPE([socklen_t], ,[ + AC_MSG_CHECKING([for socklen_t equivalent]) + AC_CACHE_VAL([curl_cv_socklen_t_equiv], + [ + # Systems have either "struct sockaddr *" or + # "void *" as the second argument to getpeername + curl_cv_socklen_t_equiv= + for arg2 in "struct sockaddr" void; do + for t in int size_t unsigned long "unsigned long"; do + AC_TRY_COMPILE([ + #include <sys/types.h> + #include <sys/socket.h> + + int getpeername (int, $arg2 *, $t *); + ],[ + $t len; + getpeername(0,0,&len); + ],[ + curl_cv_socklen_t_equiv="$t" + break + ]) + done + done + + if test "x$curl_cv_socklen_t_equiv" = x; then + AC_MSG_ERROR([Cannot find a type to use in place of socklen_t]) + fi + ]) + AC_MSG_RESULT($curl_cv_socklen_t_equiv) + AC_DEFINE_UNQUOTED(socklen_t, $curl_cv_socklen_t_equiv, + [type to use in place of socklen_t if not defined])], + [#include <sys/types.h> +#include <sys/socket.h>]) +]) + diff --git a/mk/Makefile.am b/mk/Makefile.am new file mode 100644 index 00000000..f49c5289 --- /dev/null +++ b/mk/Makefile.am @@ -0,0 +1,4 @@ +SUBDIRS = smtpd +SUBDIRS += smtpctl +SUBDIRS += mail +SUBDIRS += smtp diff --git a/mk/mail/Makefile.am b/mk/mail/Makefile.am new file mode 100644 index 00000000..cc6d96cb --- /dev/null +++ b/mk/mail/Makefile.am @@ -0,0 +1,5 @@ +SUBDIRS = mail.lmtp +SUBDIRS += mail.maildir +SUBDIRS += mail.mboxfile +SUBDIRS += mail.mda + diff --git a/mk/mail/mail.lmtp/Makefile.am b/mk/mail/mail.lmtp/Makefile.am new file mode 100644 index 00000000..29c33dd1 --- /dev/null +++ b/mk/mail/mail.lmtp/Makefile.am @@ -0,0 +1,22 @@ +include $(top_srcdir)/mk/pathnames + +pkglibexec_PROGRAMS = mail.lmtp + +mail_lmtp_SOURCES = $(top_srcdir)/usr.sbin/smtpd/mail.lmtp.c +mail_lmtp_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/log.c + +AM_CPPFLAGS= -I$(top_srcdir)/smtpd \ + -I$(top_srcdir)/openbsd-compat +if !NEED_ERR_H +AM_CPPFLAGS += -I$(top_srcdir)/openbsd-compat/err_h +endif + +LIBCOMPAT = $(top_builddir)/openbsd-compat/libopenbsd.a + +LDADD = $(LIBCOMPAT) + + + + +uninstall-hook: + rmdir $(DESTDIR)$(pkglibexecdir) 2> /dev/null || /bin/true diff --git a/mk/mail/mail.maildir/Makefile.am b/mk/mail/mail.maildir/Makefile.am new file mode 100644 index 00000000..c2afe4d0 --- /dev/null +++ b/mk/mail/mail.maildir/Makefile.am @@ -0,0 +1,22 @@ +include $(top_srcdir)/mk/pathnames + +pkglibexec_PROGRAMS = mail.maildir + +mail_maildir_SOURCES = $(top_srcdir)/usr.sbin/smtpd/mail.maildir.c +mail_maildir_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/log.c + +AM_CPPFLAGS= -I$(top_srcdir)/smtpd \ + -I$(top_srcdir)/openbsd-compat +if !NEED_ERR_H +AM_CPPFLAGS += -I$(top_srcdir)/openbsd-compat/err_h +endif + +LIBCOMPAT = $(top_builddir)/openbsd-compat/libopenbsd.a + +LDADD = $(LIBCOMPAT) + + + + +uninstall-hook: + rmdir $(DESTDIR)$(pkglibexecdir) 2> /dev/null || /bin/true diff --git a/mk/mail/mail.mboxfile/Makefile.am b/mk/mail/mail.mboxfile/Makefile.am new file mode 100644 index 00000000..de5f4ea5 --- /dev/null +++ b/mk/mail/mail.mboxfile/Makefile.am @@ -0,0 +1,22 @@ +include $(top_srcdir)/mk/pathnames + +pkglibexec_PROGRAMS = mail.mboxfile + +mail_mboxfile_SOURCES = $(top_srcdir)/usr.sbin/smtpd/mail.mboxfile.c +mail_mboxfile_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/log.c + +AM_CPPFLAGS= -I$(top_srcdir)/smtpd \ + -I$(top_srcdir)/openbsd-compat +if !NEED_ERR_H +AM_CPPFLAGS += -I$(top_srcdir)/openbsd-compat/err_h +endif + +LIBCOMPAT = $(top_builddir)/openbsd-compat/libopenbsd.a + +LDADD = $(LIBCOMPAT) + + + + +uninstall-hook: + rmdir $(DESTDIR)$(pkglibexecdir) 2> /dev/null || /bin/true diff --git a/mk/mail/mail.mda/Makefile.am b/mk/mail/mail.mda/Makefile.am new file mode 100644 index 00000000..84bbfdb3 --- /dev/null +++ b/mk/mail/mail.mda/Makefile.am @@ -0,0 +1,22 @@ +include $(top_srcdir)/mk/pathnames + +pkglibexec_PROGRAMS = mail.mda + +mail_mda_SOURCES = $(top_srcdir)/usr.sbin/smtpd/mail.mda.c +mail_mda_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/log.c + +AM_CPPFLAGS= -I$(top_srcdir)/smtpd \ + -I$(top_srcdir)/openbsd-compat +if !NEED_ERR_H +AM_CPPFLAGS += -I$(top_srcdir)/openbsd-compat/err_h +endif + +LIBCOMPAT = $(top_builddir)/openbsd-compat/libopenbsd.a + +LDADD = $(LIBCOMPAT) + + + + +uninstall-hook: + rmdir $(DESTDIR)$(pkglibexecdir) 2> /dev/null || /bin/true diff --git a/mk/mdoc2man.awk b/mk/mdoc2man.awk new file mode 100644 index 00000000..726f628c --- /dev/null +++ b/mk/mdoc2man.awk @@ -0,0 +1,391 @@ +#!/usr/bin/awk +# +# Copyright (c) 2003 Peter Stuge <stuge-mdoc2man@cdy.org> +# +# 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. + +# Dramatically overhauled by Tim Kientzle. This version almost +# handles library-style pages with Fn, Ft, etc commands. Still +# a lot of problems... + +BEGIN { + displaylines = 0 + trailer = "" + out = "" + sep = "" + nextsep = " " +} + +# Add a word with appropriate preceding whitespace +# Maintain a short queue of the expected upcoming word separators. +function add(str) { + out=out sep str + sep = nextsep + nextsep = " " +} + +# Add a word with no following whitespace +# Use for opening punctuation such as '(' +function addopen(str) { + add(str) + sep = "" +} + +# Add a word with no preceding whitespace +# Use for closing punctuation such as ')' or '.' +function addclose(str) { + sep = "" + add(str) +} + +# Add a word with no space before or after +# Use for separating punctuation such as '=' +function addpunct(str) { + sep = "" + add(str) + sep = "" +} + +# Emit the current line so far +function endline() { + addclose(trailer) + trailer = "" + if(length(out) > 0) { + print out + out="" + } + if(displaylines > 0) { + displaylines = displaylines - 1 + if (displaylines == 0) + dispend() + } + # First word on next line has no preceding whitespace + sep = "" +} + +function linecmd(cmd) { + endline() + add(cmd) + endline() +} + +function breakline() { + linecmd(".br") +} + +# Start an indented display +function dispstart() { + linecmd(".RS 4") +} + +# End an indented display +function dispend() { + linecmd(".RE") +} + +# Collect rest of input line +function wtail() { + retval="" + while(w<nwords) { + if(length(retval)) + retval=retval " " + retval=retval words[++w] + } + return retval +} + +function splitwords(l, dest, n, o, w) { + n = 1 + delete dest + while (length(l) > 0) { + sub("^[ \t]*", "", l) + if (match(l, "^\"")) { + l = substr(l, 2) + o = index(l, "\"") + if (o > 0) { + w = substr(l, 1, o-1) + l = substr(l, o+1) + dest[n++] = w + } else { + dest[n++] = l + l = "" + } + } else { + o = match(l, "[ \t]") + if (o > 0) { + w = substr(l, 1, o-1) + l = substr(l, o+1) + dest[n++] = w + } else { + dest[n++] = l + l = "" + } + } + } + return n-1 +} + +! /^\./ { + out = $0 + endline() + next +} + +/^\.\\"/ { next } + +{ + sub("^\\.","") + nwords=splitwords($0, words) + # TODO: Instead of iterating 'w' over the array, have a separate + # function that returns 'next word' and use that. This will allow + # proper handling of double-quoted arguments as well. + for(w=1;w<=nwords;w++) { + if(match(words[w],"^Li$")) { # Literal; rest of line is unformatted + dispstart() + displaylines = 1 + } else if(match(words[w],"^Dl$")) { # Display literal + dispstart() + displaylines = 1 + } else if(match(words[w],"^Bd$")) { # Begin display + if(match(words[w+1],"-literal")) { + dispstart() + linecmd(".nf") + displaylines=10000 + w=nwords + } + } else if(match(words[w],"^Ed$")) { # End display + displaylines = 0 + dispend() + } else if(match(words[w],"^Ns$")) { # Suppress space after next word + nextsep = "" + } else if(match(words[w],"^No$")) { # Normal text + add(words[++w]) + } else if(match(words[w],"^Dq$")) { # Quote + addopen("``") + add(words[++w]) + while(w<nwords&&!match(words[w+1],"^[\\.,]")) + add(words[++w]) + addclose("''") + } else if(match(words[w],"^Do$")) { + addopen("``") + } else if(match(words[w],"^Dc$")) { + addclose("''") + } else if(match(words[w],"^Oo$")) { + addopen("[") + } else if(match(words[w],"^Oc$")) { + addclose("]") + } else if(match(words[w],"^Ao$")) { + addopen("<") + } else if(match(words[w],"^Ac$")) { + addclose(">") + } else if(match(words[w],"^Dd$")) { + date=wtail() + next + } else if(match(words[w],"^Dt$")) { + id=wtail() + next + } else if(match(words[w],"^Ox$")) { + add("OpenBSD") + } else if(match(words[w],"^Fx$")) { + add("FreeBSD") + } else if(match(words[w],"^Nx$")) { + add("NetBSD") + } else if(match(words[w],"^St$")) { + if (match(words[w+1], "^-p1003.1$")) { + w++ + add("IEEE Std 1003.1 (``POSIX.1'')") + } else if(match(words[w+1], "^-p1003.1-96$")) { + w++ + add("ISO/IEC 9945-1:1996 (``POSIX.1'')") + } else if(match(words[w+1], "^-p1003.1-88$")) { + w++ + add("IEEE Std 1003.1-1988 (``POSIX.1'')") + } else if(match(words[w+1], "^-p1003.1-2001$")) { + w++ + add("IEEE Std 1003.1-2001 (``POSIX.1'')") + } else if(match(words[w+1], "^-susv2$")) { + w++ + add("Version 2 of the Single UNIX Specification (``SUSv2'')") + } + } else if(match(words[w],"^Ex$")) { + if (match(words[w+1], "^-std$")) { + w++ + add("The \\fB" name "\\fP utility exits 0 on success, and >0 if an error occurs.") + } + } else if(match(words[w],"^Os$")) { + add(".TH " id " \"" date "\" \"" wtail() "\"") + } else if(match(words[w],"^Sh$")) { + section=wtail() + add(".SH " section) + linecmd(".ad l") + } else if(match(words[w],"^Xr$")) { + add("\\fB" words[++w] "\\fP(" words[++w] ")" words[++w]) + } else if(match(words[w],"^Nm$")) { + if(match(section,"SYNOPSIS")) + breakline() + if(w >= nwords) + n=name + else if (match(words[w+1], "^[A-Z][a-z]$")) + n=name + else if (match(words[w+1], "^[.,;:]$")) + n=name + else { + n=words[++w] + if(!length(name)) + name=n + } + if(!length(n)) + n=name + add("\\fB\\%" n "\\fP") + } else if(match(words[w],"^Nd$")) { + add("\\- " wtail()) + } else if(match(words[w],"^Fl$")) { + add("\\fB\\-" words[++w] "\\fP") + } else if(match(words[w],"^Ar$")) { + addopen("\\fI") + if(w==nwords) + add("file ...\\fP") + else + add(words[++w] "\\fP") + } else if(match(words[w],"^Cm$")) { + add("\\fB" words[++w] "\\fP") + } else if(match(words[w],"^Op$")) { + addopen("[") + option=1 + trailer="]" trailer + } else if(match(words[w],"^Pp$")) { + linecmd(".PP") + } else if(match(words[w],"^An$")) { + endline() + } else if(match(words[w],"^Ss$")) { + add(".SS") + } else if(match(words[w],"^Ft$")) { + if (match(section, "SYNOPSIS")) { + breakline() + } + add("\\fI" wtail() "\\fP") + if (match(section, "SYNOPSIS")) { + breakline() + } + } else if(match(words[w],"^Fn$")) { + ++w + F = "\\fB\\%" words[w] "\\fP(" + Fsep = "" + while(w<nwords) { + ++w + if (match(words[w], "^[.,:]$")) { + --w + break + } + gsub(" ", "\\ ", words[w]) + F = F Fsep "\\fI\\%" words[w] "\\fP" + Fsep = ", " + } + add(F ")") + if (match(section, "SYNOPSIS")) { + addclose(";") + } + } else if(match(words[w],"^Fo$")) { + w++ + F = "\\fB\\%" words[w] "\\fP(" + Fsep = "" + } else if(match(words[w],"^Fa$")) { + w++ + gsub(" ", "\\ ", words[w]) + F = F Fsep "\\fI\\%" words[w] "\\fP" + Fsep = ", " + } else if(match(words[w],"^Fc$")) { + add(F ")") + if (match(section, "SYNOPSIS")) { + addclose(";") + } + } else if(match(words[w],"^Va$")) { + w++ + add("\\fI" words[w] "\\fP") + } else if(match(words[w],"^In$")) { + w++ + add("\\fB#include <" words[w] ">\\fP") + } else if(match(words[w],"^Pa$")) { + addopen("\\fI") + w++ + if(match(words[w],"^\\.")) + add("\\&") + add(words[w] "\\fP") + } else if(match(words[w],"^Dv$")) { + add(".BR") + } else if(match(words[w],"^Em|Ev$")) { + add(".IR") + } else if(match(words[w],"^Pq$")) { + addopen("(") + trailer=")" trailer + } else if(match(words[w],"^Aq$")) { + addopen("\\%<") + trailer=">" trailer + } else if(match(words[w],"^Brq$")) { + addopen("{") + trailer="}" trailer + } else if(match(words[w],"^S[xy]$")) { + add(".B " wtail()) + } else if(match(words[w],"^Ic$")) { + add("\\fB") + trailer="\\fP" trailer + } else if(match(words[w],"^Bl$")) { + oldoptlist=optlist + linecmd(".RS 5") + if(match(words[w+1],"-bullet")) + optlist=1 + else if(match(words[w+1],"-enum")) { + optlist=2 + enum=0 + } else if(match(words[w+1],"-tag")) + optlist=3 + else if(match(words[w+1],"-item")) + optlist=4 + else if(match(words[w+1],"-bullet")) + optlist=1 + w=nwords + } else if(match(words[w],"^El$")) { + linecmd(".RE") + optlist=oldoptlist + } else if(match(words[w],"^It$")&&optlist) { + if(optlist==1) + add(".IP \\(bu") + else if(optlist==2) + add(".IP " ++enum ".") + else if(optlist==3) { + add(".TP") + endline() + if(match(words[w+1],"^Pa$|^Ev$")) { + add(".B") + w++ + } + } else if(optlist==4) + add(".IP") + } else if(match(words[w],"^Xo$")) { + # TODO: Figure out how to handle this + } else if(match(words[w],"^Xc$")) { + # TODO: Figure out how to handle this + } else if(match(words[w],"^[=]$")) { + addpunct(words[w]) + } else if(match(words[w],"^[[{(]$")) { + addopen(words[w]) + } else if(match(words[w],"^[\\])}.,;:]$")) { + addclose(words[w]) + } else { + add(words[w]) + } + } + if(match(out,"^\\.[^a-zA-Z]")) + sub("^\\.","",out) + endline() +} diff --git a/mk/pathnames b/mk/pathnames new file mode 100644 index 00000000..5cff32da --- /dev/null +++ b/mk/pathnames @@ -0,0 +1,10 @@ +smtpd_srcdir = $(top_srcdir)/usr.sbin/smtpd +compat_srcdir = $(top_srcdir)/openbsd-compat +regress_srcdir = $(top_srcdir)/regress/bin + +PATHS= -DSMTPD_CONFDIR=\"$(sysconfdir)\" \ + -DPATH_CHROOT=\"$(PRIVSEP_PATH)\" \ + -DPATH_SMTPCTL=\"$(sbindir)/smtpctl\" \ + -DPATH_MAILLOCAL=\"$(pkglibexecdir)/mail.local\" \ + -DPATH_MAKEMAP=\"$(sbindir)/makemap\" \ + -DPATH_LIBEXEC=\"$(pkglibexecdir)\" diff --git a/mk/smtp/Makefile.am b/mk/smtp/Makefile.am new file mode 100644 index 00000000..875cacdc --- /dev/null +++ b/mk/smtp/Makefile.am @@ -0,0 +1,59 @@ +include $(top_srcdir)/mk/pathnames + +bin_PROGRAMS= smtp + +smtp_SOURCES= $(top_srcdir)/usr.sbin/smtpd/iobuf.c +smtp_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/ioev.c +smtp_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/log.c +smtp_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/smtp_client.c +smtp_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/smtpc.c +smtp_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/ssl.c +smtp_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/ssl_verify.c + +smtp_CFLAGS= -DIO_TLS + +AM_CPPFLAGS= -I$(top_srcdir)/smtpd \ + -I$(top_srcdir)/openbsd-compat +if !NEED_ERR_H +AM_CPPFLAGS += -I$(top_srcdir)/openbsd-compat/err_h +endif + +LIBCOMPAT= $(top_builddir)/openbsd-compat/libopenbsd.a + +LDADD= $(LIBCOMPAT) + +# need to define _GNU_SOURCE to get: +# EAI_NODATA defined +# {v,}asprintf +# setres{g,u}id +CFLAGS+= -D_GNU_SOURCE +CPPFLAGS= -I$(srcdir) @CPPFLAGS@ $(PATHS) @DEFS@ + +MANPAGES= smtp.1.out +MANPAGES_IN= $(top_srcdir)/usr.sbin/smtpd/smtp.1 + +EXTRA_DIST= $(MANPAGES_IN) + +PATHSUBS= -e 's|/var/run/smtpd.sock|$(sockdir)/smtpd.sock|g' \ + -e 's|/usr/libexec|$(libexecdir)|g' \ + -e 's|/etc/mail/|$(sysconfdir)/|g' + +FIXPATHSCMD= $(SED) $(PATHSUBS) + + +$(MANPAGES): $(MANPAGES_IN) + manpage=$(top_srcdir)/usr.sbin/smtpd/`echo $@ | sed 's/\.out$$//'`; \ + if test "$(MANTYPE)" = "man"; then \ + $(FIXPATHSCMD) $${manpage} | $(AWK) -f $(srcdir)/../mdoc2man.awk > $@; \ + else \ + $(FIXPATHSCMD) $${manpage} > $@; \ + fi + +install-exec-hook: $(CONFIGFILES) $(MANPAGES) + $(MKDIR_P) $(DESTDIR)$(mandir)/$(mansubdir)1 + $(INSTALL) -m 644 smtp.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/smtp.1 + rm smtp.1.out + +uninstall-hook: + rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/smtp.1 + rmdir $(DESTDIR)$(mandir)/$(mansubdir)1 2> /dev/null || /bin/true diff --git a/mk/smtpctl/Makefile.am b/mk/smtpctl/Makefile.am new file mode 100644 index 00000000..f7d1b418 --- /dev/null +++ b/mk/smtpctl/Makefile.am @@ -0,0 +1,99 @@ +include $(top_srcdir)/mk/pathnames + +sbin_PROGRAMS= smtpctl + +smtpctl_SOURCES= $(top_srcdir)/usr.sbin/smtpd/enqueue.c +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/parser.c +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/log.c +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/envelope.c +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/queue_backend.c +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/queue_fs.c +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/smtpctl.c +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/spfwalk.c +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/util.c +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/unpack_dns.c +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/compress_backend.c +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/compress_gzip.c +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/to.c +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/expand.c +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/tree.c +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/dict.c + +if HAVE_DB_API +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/config.c +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/parse.y +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/limit.c +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/table.c +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/table_static.c +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/table_db.c +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/table_getpwnam.c +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/table_proc.c +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/mailaddr.c +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/makemap.c +endif + +smtpctl_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/crypto.c + +smtpctl_CFLAGS= -DNO_IO -DCONFIG_MINIMUM +smtpctl_CFLAGS+= -DPATH_GZCAT=\"$(ZCAT)\" \ + -DPATH_ENCRYPT=\"$(pkglibexecdir)/encrypt\" + +AM_CPPFLAGS= -I$(top_srcdir)/smtpd \ + -I$(top_srcdir)/openbsd-compat +if !NEED_ERR_H +AM_CPPFLAGS += -I$(top_srcdir)/openbsd-compat/err_h +endif + +LIBCOMPAT= $(top_builddir)/openbsd-compat/libopenbsd.a + +LDADD= $(LIBCOMPAT) +if HAVE_DB_API +LDADD+= $(DB_LIB) +endif + +# need to define _GNU_SOURCE to get: +# EAI_NODATA defined +# {v,}asprintf +# setres{g,u}id +CFLAGS+= -D_GNU_SOURCE +CPPFLAGS= -I$(srcdir) @CPPFLAGS@ $(PATHS) @DEFS@ + +MANPAGES= smtpctl.8.out sendmail.8.out makemap.8.out newaliases.8.out +MANPAGES_IN= $(top_srcdir)/usr.sbin/smtpd/smtpctl.8 $(top_srcdir)/usr.sbin/smtpd/sendmail.8 $(top_srcdir)/usr.sbin/smtpd/makemap.8 $(top_srcdir)/usr.sbin/smtpd/newaliases.8 + +EXTRA_DIST= $(MANPAGES_IN) + +PATHSUBS= -e 's|/var/run/smtpd.sock|$(sockdir)/smtpd.sock|g' \ + -e 's|/usr/libexec|$(libexecdir)|g' \ + -e 's|/etc/mail/|$(sysconfdir)/|g' + +FIXPATHSCMD= $(SED) $(PATHSUBS) + +if NEED_LIBASR +AM_CPPFLAGS+= -I$(top_srcdir)/openbsd-compat/libasr +endif + +$(MANPAGES): $(MANPAGES_IN) + manpage=$(top_srcdir)/usr.sbin/smtpd/`echo $@ | sed 's/\.out$$//'`; \ + if test "$(MANTYPE)" = "man"; then \ + $(FIXPATHSCMD) $${manpage} | $(AWK) -f $(srcdir)/../mdoc2man.awk > $@; \ + else \ + $(FIXPATHSCMD) $${manpage} > $@; \ + fi + +install-exec-hook: $(CONFIGFILES) $(MANPAGES) + $(MKDIR_P) $(DESTDIR)$(mandir)/$(mansubdir)8 + chgrp $(SMTPD_QUEUE_USER) $(DESTDIR)$(sbindir)/smtpctl || true + chmod 2555 $(DESTDIR)$(sbindir)/smtpctl || true + $(INSTALL) -m 644 smtpctl.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/smtpctl.8 + $(INSTALL) -m 644 sendmail.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/sendmail.8 + $(INSTALL) -m 644 makemap.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/makemap.8 + $(INSTALL) -m 644 newaliases.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/newaliases.8 + rm smtpctl.8.out sendmail.8.out makemap.8.out newaliases.8.out + +uninstall-hook: + rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/smtpctl.8 + rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/sendmail.8 + rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/makemap.8 + rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/newaliases.8 + rmdir $(DESTDIR)$(mandir)/$(mansubdir)8 2> /dev/null || /bin/true diff --git a/mk/smtpd/Makefile.am b/mk/smtpd/Makefile.am new file mode 100644 index 00000000..6d652d48 --- /dev/null +++ b/mk/smtpd/Makefile.am @@ -0,0 +1,196 @@ +# In OpenBSD, smtpd's files are installed this way: +# +# /etc/mail/smtpd.conf +# /usr/sbin/smtpd +# +# +# For OpenSMTPD portable, here's where files are installed: +# (assuming PREFIX=/usr/local) +# +# /usr/local/etc/smtpd.conf +# /usr/local/sbin/smtpd + +include $(top_srcdir)/mk/pathnames + +sbin_PROGRAMS= smtpd + +smtpd_SOURCES= $(top_srcdir)/usr.sbin/smtpd/aliases.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/bounce.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/ca.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/cert.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/compress_backend.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/config.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/control.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/dict.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/dns.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/esc.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/envelope.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/expand.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/forward.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/iobuf.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/limit.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/lka.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/lka_filter.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/lka_session.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/log.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/mda.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/mda_mbox.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/mda_unpriv.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/mda_variables.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/mproc.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/mailaddr.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/mta.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/mta_session.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/parse.y +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/pony.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/proxy.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/queue.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/queue_backend.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/report_smtp.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/resolver.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/rfc5322.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/ruleset.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/runq.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/scheduler.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/scheduler_backend.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/smtp.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/smtp_session.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/smtpd.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/srs.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/ssl.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/ssl_smtpd.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/ssl_verify.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/stat_backend.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/table.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/to.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/tree.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/unpack_dns.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/util.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/waitq.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/ioev.c + +# backends +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/crypto.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/compress_gzip.c +if HAVE_DB_API +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/table_db.c +endif +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/table_getpwnam.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/table_proc.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/table_static.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/queue_fs.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/queue_null.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/queue_proc.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/queue_ram.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/scheduler_null.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/scheduler_proc.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/scheduler_ramqueue.c +smtpd_SOURCES+= $(top_srcdir)/usr.sbin/smtpd/stat_ramstat.c + + +smtpd_CFLAGS= -DIO_TLS +smtpd_CFLAGS+= -DCA_FILE=\"$(CA_FILE)\" + +AM_CPPFLAGS= -I$(top_srcdir)/usr.sbin/smtpd \ + -I$(compat_srcdir) +if !NEED_ERR_H +AM_CPPFLAGS += -I$(top_srcdir)/openbsd-compat/err_h +endif +if !SUPPORT_PATHS_H +AM_CPPFLAGS += -I$(top_srcdir)/openbsd-compat/paths_h +endif + +LIBCOMPAT= $(top_builddir)/openbsd-compat/libopenbsd.a +if NEED_LIBASR +AM_CPPFLAGS+= -I$(top_srcdir)/openbsd-compat/libasr +endif + +LDADD= $(LIBCOMPAT) $(DB_LIB) $(ASR_LIB) + +# need to define _GNU_SOURCE to get: +# EAI_NODATA defined +# {v,}asprintf +# setres{g,u}id +AM_CFLAGS= -D_GNU_SOURCE -DNEED_EVENT_ASR_RUN +AM_CPPFLAGS+= -I$(srcdir) $(PATHS) @DEFS@ + +MANPAGES= aliases.5.out forward.5.out smtpd.8.out \ + smtpd.conf.5.out table.5.out + +MANPAGES_IN= $(top_srcdir)/usr.sbin/smtpd/aliases.5 +MANPAGES_IN+= $(top_srcdir)/usr.sbin/smtpd/forward.5 +MANPAGES_IN+= $(top_srcdir)/usr.sbin/smtpd/smtpd.8 +MANPAGES_IN+= $(top_srcdir)/usr.sbin/smtpd/smtpd.conf.5 +MANPAGES_IN+= $(top_srcdir)/usr.sbin/smtpd/table.5 + +CONFIGFILES= smtpd.conf.out +CONFIGFILES_IN= $(top_srcdir)/usr.sbin/smtpd/smtpd.conf + +EXTRA_DIST= $(CONFIGFILES_IN) $(MANPAGES_IN) + + +EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/rfc5322.h +EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/unpack_dns.h +EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/tree.h +EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/smtp.h +EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/smtpd.h +EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/smtpd-api.h +EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/smtpd-defines.h +EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/ioev.h +EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/iobuf.h +EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/log.h +EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/ssl.h +EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/parser.h +EXTRA_DIST+= $(top_srcdir)/usr.sbin/smtpd/dict.h + +PATHSUBS= -e 's|/etc/mail/|$(sysconfdir)/|g' \ + -e 's|/var/run/smtpd.sock|$(sockdir)/smtpd.sock|g' \ + -e 's|/usr/local/libexec/smtpd/|$(pkglibexecdir)|g' + +FIXPATHSCMD= $(SED) $(PATHSUBS) + +$(MANPAGES): $(MANPAGES_IN) + manpage=$(top_srcdir)/usr.sbin/smtpd/`echo $@ | sed 's/\.out$$//'`; \ + if test "$(MANTYPE)" = "man"; then \ + $(FIXPATHSCMD) $${manpage} | $(AWK) -f $(srcdir)/../mdoc2man.awk > $@; \ + else \ + $(FIXPATHSCMD) $${manpage} > $@; \ + fi + +$(CONFIGFILES): $(CONFIGFILES_IN) + conffile=$(top_srcdir)/usr.sbin/smtpd/`echo $@ | sed 's/.out$$//'`; \ + $(CAT) $${conffile} > $@ + + +# smtpd.conf +# newaliases makemap +install-exec-hook: $(CONFIGFILES) $(MANPAGES) + $(MKDIR_P) $(DESTDIR)$(sysconfdir) + $(MKDIR_P) $(DESTDIR)$(bindir) + $(MKDIR_P) $(DESTDIR)$(mandir)/$(mansubdir)5 + $(MKDIR_P) $(DESTDIR)$(mandir)/$(mansubdir)8 + + @if [ ! -f $(DESTDIR)$(sysconfdir)/smtpd.conf ]; then \ + $(INSTALL) -m 644 smtpd.conf.out $(DESTDIR)$(sysconfdir)/smtpd.conf; \ + else \ + echo "$(DESTDIR)$(sysconfdir)/smtpd.conf already exists, install will not overwrite"; \ + fi + + $(INSTALL) -m 644 aliases.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/aliases.5 + $(INSTALL) -m 644 forward.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/forward.5 + $(INSTALL) -m 644 table.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/table.5 + $(INSTALL) -m 644 smtpd.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/smtpd.8 + $(INSTALL) -m 644 smtpd.conf.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/smtpd.conf.5 + rm aliases.5.out forward.5.out table.5.out \ + smtpd.8.out smtpd.conf.5.out smtpd.conf.out + +uninstall-hook: +# XXX to make "make distcheck" happy we need to rm smtpd.conf + rm -f $(DESTDIR)$(sysconfdir)/smtpd.conf \ + $(DESTDIR)$(mandir)/$(mansubdir)5/aliases.5 \ + $(DESTDIR)$(mandir)/$(mansubdir)5/forward.5 \ + $(DESTDIR)$(mandir)/$(mansubdir)5/table.5 \ + $(DESTDIR)$(mandir)/$(mansubdir)5/smtpd.conf.5 \ + $(DESTDIR)$(mandir)/$(mansubdir)8/smtpd.8 + rmdir $(DESTDIR)$(mandir)/$(mansubdir)5 \ + $(DESTDIR)$(mandir)/$(mansubdir)8 2> /dev/null || /bin/true diff --git a/openbsd-compat/Makefile.am b/openbsd-compat/Makefile.am new file mode 100644 index 00000000..170c2b6c --- /dev/null +++ b/openbsd-compat/Makefile.am @@ -0,0 +1,235 @@ +noinst_LIBRARIES = libopenbsd.a + +AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/usr.sbin/smtpd -I$(top_srcdir)/openbsd-compat + +libopenbsd_a_SOURCES = empty.c + +if NEED_LIBASR +AM_CPPFLAGS += -I$(top_srcdir)/openbsd-compat/libasr + +libopenbsd_a_SOURCES += libasr/asr.c +libopenbsd_a_SOURCES += libasr/asr_debug.c +libopenbsd_a_SOURCES += libasr/asr_compat.c +libopenbsd_a_SOURCES += libasr/asr_utils.c +libopenbsd_a_SOURCES += libasr/getaddrinfo_async.c +libopenbsd_a_SOURCES += libasr/gethostnamadr_async.c +libopenbsd_a_SOURCES += libasr/getnameinfo_async.c +libopenbsd_a_SOURCES += libasr/getnetnamadr_async.c +libopenbsd_a_SOURCES += libasr/res_search_async.c +libopenbsd_a_SOURCES += libasr/res_send_async.c +endif + + + +if NEED_PROGNAME +libopenbsd_a_SOURCES += progname.c +endif + +if NEED_ARC4RANDOM +libopenbsd_a_SOURCES += arc4random.c +endif + +if NEED_BASE64 +libopenbsd_a_SOURCES += base64.c +endif + +if NEED_BASENAME +libopenbsd_a_SOURCES += basename.c +endif + +if NEED_CLOCK_GETTIME +libopenbsd_a_SOURCES += clock_gettime.c +endif + +if NEED_CLOSEFROM +libopenbsd_a_SOURCES += closefrom.c +endif + +if NEED_CRYPT_CHECKPASS +libopenbsd_a_SOURCES += crypt_checkpass.c +endif + +if NEED_DAEMON +libopenbsd_a_SOURCES += daemon.c +endif + +if NEED_DIRNAME +libopenbsd_a_SOURCES += dirname.c +endif + +if NEED_ERR +libopenbsd_a_SOURCES += bsd-err.c +endif + +if NEED_ERRC +libopenbsd_a_SOURCES += errc.c +endif + +if NEED_EVENT_ASR_RUN +libopenbsd_a_SOURCES += event_asr_run.c +endif + +if NEED_EXPLICIT_BZERO +libopenbsd_a_SOURCES += explicit_bzero.c +endif + +if NEED_FGETLN +libopenbsd_a_SOURCES += fgetln.c +endif + +if NEED_FMT_SCALED +libopenbsd_a_SOURCES += fmt_scaled.c +endif + +if NEED_FPARSELN +libopenbsd_a_SOURCES += fparseln.c +endif + +if NEED_FREEZERO +libopenbsd_a_SOURCES += freezero.c +endif + +if NEED_GETOPT +libopenbsd_a_SOURCES += getopt.c +endif + +if NEED_GETPEEREID +libopenbsd_a_SOURCES += getpeereid.c +endif + +if NEED_IMSG +libopenbsd_a_SOURCES += imsg.c +libopenbsd_a_SOURCES += imsg-buffer.c +endif + +if NEED_INET_NET_PTON +libopenbsd_a_SOURCES += inet_net_pton.c +endif + +if NEED_NANOSLEEP +libopenbsd_a_SOURCES += nanosleep.c +endif + +if NEED_PIDFILE +libopenbsd_a_SOURCES += pidfile.c +endif + +if NEED_PIPE2 +libopenbsd_a_SOURCES += pipe2.c +endif + +if NEED_REALLOCARRAY +libopenbsd_a_SOURCES += reallocarray.c +endif + +if NEED_RECALLOCARRAY +libopenbsd_a_SOURCES += recallocarray.c +endif + +if NEED_RES_HNOK +libopenbsd_a_SOURCES += res_hnok.c +endif + +if NEED_RES_RANDOMID +libopenbsd_a_SOURCES += res_randomid.c +endif + +if NEED_SETPROCTITLE +libopenbsd_a_SOURCES += setproctitle.c +endif + +if NEED_SETRESGID +libopenbsd_a_SOURCES += setresgid.c +endif + +if NEED_SETRESUID +libopenbsd_a_SOURCES += setresuid.c +endif + +if NEED_SETEGID +libopenbsd_a_SOURCES += setegid.c +endif + +if NEED_SETEUID +libopenbsd_a_SOURCES += seteuid.c +endif + +if NEED_SIGNAL +libopenbsd_a_SOURCES += signal.c +endif + +if NEED_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM +libopenbsd_a_SOURCES += SSL_CTX_use_certificate_chain_mem.c +endif + +if NEED_STRERROR +libopenbsd_a_SOURCES += strerror.c +endif + +if NEED_STRLCAT +libopenbsd_a_SOURCES += strlcat.c +endif + +if NEED_STRLCPY +libopenbsd_a_SOURCES += strlcpy.c +endif + +if NEED_STRMODE +libopenbsd_a_SOURCES += strmode.c +endif + +if NEED_STRSEP +libopenbsd_a_SOURCES += strsep.c +endif + +if NEED_STRTONUM +libopenbsd_a_SOURCES += strtonum.c +endif + +if NEED_STRNDUP +libopenbsd_a_SOURCES += strndup.c +endif + +if NEED_STRNLEN +libopenbsd_a_SOURCES += strnlen.c +endif + +if NEED_USLEEP +libopenbsd_a_SOURCES += usleep.c +endif + +if NEED_VIS +libopenbsd_a_SOURCES += vis.c +endif + +if NEED_WAITPID +libopenbsd_a_SOURCES += bsd-waitpid.c +endif + + + +EXTRA_DIST = base64.h +EXTRA_DIST += bsd-misc.h +EXTRA_DIST += bsd-waitpid.h +EXTRA_DIST += chacha_private.h +EXTRA_DIST += defines.h +EXTRA_DIST += entropy.h +EXTRA_DIST += imsg.h +EXTRA_DIST += includes.h +EXTRA_DIST += $(top_srcdir)/usr.sbin/smtpd/log.h +EXTRA_DIST += openbsd-compat.h +EXTRA_DIST += sys/queue.h +EXTRA_DIST += sys/tree.h +EXTRA_DIST += bsd-vis.h + +if NEED_LIBASR +EXTRA_DIST += libasr/asr_compat.h +EXTRA_DIST += libasr/asr_private.h +EXTRA_DIST += libasr/asr.h +EXTRA_DIST += libasr/thread_private.h +endif + + +if !NEED_ERR_H +AM_CPPFLAGS += -I$(top_srcdir)/openbsd-compat/err_h +endif diff --git a/openbsd-compat/NOTES b/openbsd-compat/NOTES new file mode 100644 index 00000000..42aefc7d --- /dev/null +++ b/openbsd-compat/NOTES @@ -0,0 +1,37 @@ +List of files and where they come from + +arc4random.c portable openssh +base64.{c,h} portable openssh +basename.c portable openssh +bsd-closefrom.c portable openssh +bsd-getpeereid.c portable openssh +bsd-waitpid.{c,h} portable openssh +clock_gettime.c handmade +daemon.c portable openssh +defines.h portable openssh +dirname.c portable openssh +entropy.{c,h} portable openssh +event_asr_run.c end of /usr/src/lib/libevent/event.c +fgetln.c part of /usr/src/usr.bin/make/util.c +fmt_scaled.c portable openssh +fparseln.c part of /usr/src/lib/libutil/fparseln.c +getopt.c portable openssh +imsg-buffer.c part of /usr/src/libutil/imsg-buffer.c +imsg.{c,h} part of /usr/src/libutil/imsg.c +includes.h portable openssh +log.h portable openssh +mktemp.c portable openssh +openbsd-compat.h portable openssh +pidfile.c /usr/src/lib/libutil/pidfile.c +pw_dup.c /usr/src/lib/libc/gen/pw_dup.c +reallocarray.c /usr/src/lib/libc/stdlib/reallocarray.c +setproctitle.c portable openssh +strlcat.c portable openssh +strlcpy.c portable openssh +strmode.c portable openssh +strsep.c portable openssh +strtonum.c portable openssh +sys/queue.h portable openssh +sys/tree.h portable openssh +vis.{c,h} portable openssh +xmalloc.{c,h} portable openssh diff --git a/openbsd-compat/SSL_CTX_use_certificate_chain_mem.c b/openbsd-compat/SSL_CTX_use_certificate_chain_mem.c new file mode 100644 index 00000000..3a47ff0e --- /dev/null +++ b/openbsd-compat/SSL_CTX_use_certificate_chain_mem.c @@ -0,0 +1,174 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +/* + * SSL operations needed when running in a privilege separated environment. + * Adapted from openssl's ssl_rsa.c by Pierre-Yves Ritschard . + */ + +#include "includes.h" + +#include <sys/types.h> + +#include <limits.h> +#include <unistd.h> +#include <stdio.h> + +#include <openssl/err.h> +#include <openssl/bio.h> +#include <openssl/objects.h> +#include <openssl/evp.h> +#include <openssl/x509.h> +#include <openssl/pem.h> +#include <openssl/ssl.h> + +#include "log.h" +#include "ssl.h" + +#define SSL_ECDH_CURVE "prime256v1" + +/* + * Read a bio that contains our certificate in "PEM" format, + * possibly followed by a sequence of CA certificates that should be + * sent to the peer in the Certificate message. + */ +static int +ssl_ctx_use_certificate_chain_bio(SSL_CTX *ctx, BIO *in) +{ + int ret = 0; + X509 *x = NULL; + + ERR_clear_error(); /* clear error stack for SSL_CTX_use_certificate() */ + + x = PEM_read_bio_X509_AUX(in, NULL, SSL_CTX_get_default_passwd_cb(ctx), + SSL_CTX_get_default_passwd_cb_userdata(ctx)); + if (x == NULL) { + SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_PEM_LIB); + goto end; + } + + ret = SSL_CTX_use_certificate(ctx, x); + + if (ERR_peek_error() != 0) + ret = 0; + /* Key/certificate mismatch doesn't imply ret==0 ... */ + if (ret) { + /* + * If we could set up our certificate, now proceed to + * the CA certificates. + */ + X509 *ca; + STACK_OF(X509) *chain; + int r; + unsigned long err; + + SSL_CTX_get_extra_chain_certs_only(ctx, &chain); + if (chain != NULL) { + sk_X509_pop_free(chain, X509_free); + SSL_CTX_clear_extra_chain_certs(ctx); + } + + while ((ca = PEM_read_bio_X509(in, NULL, + SSL_CTX_get_default_passwd_cb(ctx), + SSL_CTX_get_default_passwd_cb_userdata(ctx))) != NULL) { + r = SSL_CTX_add_extra_chain_cert(ctx, ca); + if (!r) { + X509_free(ca); + ret = 0; + goto end; + } + /* + * Note that we must not free r if it was successfully + * added to the chain (while we must free the main + * certificate, since its reference count is increased + * by SSL_CTX_use_certificate). + */ + } + + /* When the while loop ends, it's usually just EOF. */ + err = ERR_peek_last_error(); + if (ERR_GET_LIB(err) == ERR_LIB_PEM && + ERR_GET_REASON(err) == PEM_R_NO_START_LINE) + ERR_clear_error(); + else + ret = 0; /* some real error */ + } + +end: + if (x != NULL) + X509_free(x); + return (ret); +} + +int +SSL_CTX_use_certificate_chain_mem(SSL_CTX *ctx, void *buf, int len) +{ + BIO *in; + int ret = 0; + + in = BIO_new_mem_buf(buf, len); + if (in == NULL) { + SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, ERR_R_BUF_LIB); + goto end; + } + + ret = ssl_ctx_use_certificate_chain_bio(ctx, in); + +end: + BIO_free(in); + return (ret); +} diff --git a/openbsd-compat/arc4random.c b/openbsd-compat/arc4random.c new file mode 100644 index 00000000..f5cda877 --- /dev/null +++ b/openbsd-compat/arc4random.c @@ -0,0 +1,246 @@ +/* OPENBSD ORIGINAL: lib/libc/crypto/arc4random.c */ + +/* $OpenBSD: arc4random.c,v 1.25 2013/10/01 18:34:57 markus Exp $ */ + +/* + * Copyright (c) 1996, David Mazieres <dm@uun.org> + * Copyright (c) 2008, Damien Miller <djm@openbsd.org> + * Copyright (c) 2013, Markus Friedl <markus@openbsd.org> + * + * 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. + */ + +/* + * ChaCha based random number generator for OpenBSD. + */ + +#include "includes.h" + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> + +#include <openssl/rand.h> +#include <openssl/err.h> + +#include "log.h" + +#define KEYSTREAM_ONLY +#include "chacha_private.h" + +#ifdef __GNUC__ +#define inline __inline +#else /* !__GNUC__ */ +#define inline +#endif /* !__GNUC__ */ + +/* OpenSSH isn't multithreaded */ +#define _ARC4_LOCK() +#define _ARC4_UNLOCK() + +#define KEYSZ 32 +#define IVSZ 8 +#define BLOCKSZ 64 +#define RSBUFSZ (16*BLOCKSZ) +static int rs_initialized; +static pid_t rs_stir_pid; +static chacha_ctx rs; /* chacha context for random keystream */ +static u_char rs_buf[RSBUFSZ]; /* keystream blocks */ +static size_t rs_have; /* valid bytes at end of rs_buf */ +static size_t rs_count; /* bytes till reseed */ + +static inline void _rs_rekey(u_char *dat, size_t datlen); + +static inline void +_rs_init(u_char *buf, size_t n) +{ + if (n < KEYSZ + IVSZ) + return; + chacha_keysetup(&rs, buf, KEYSZ * 8, 0); + chacha_ivsetup(&rs, buf + KEYSZ); +} + +static void +_rs_stir(void) +{ + u_char rnd[KEYSZ + IVSZ]; + + if (RAND_bytes(rnd, sizeof(rnd)) <= 0) + fatal("Couldn't obtain random bytes (error %ld)", + ERR_get_error()); + + if (!rs_initialized) { + rs_initialized = 1; + _rs_init(rnd, sizeof(rnd)); + } else + _rs_rekey(rnd, sizeof(rnd)); + memset(rnd, 0, sizeof(rnd)); + + /* invalidate rs_buf */ + rs_have = 0; + memset(rs_buf, 0, RSBUFSZ); + + rs_count = 1600000; +} + +static inline void +_rs_stir_if_needed(size_t len) +{ + pid_t pid = getpid(); + + if (rs_count <= len || !rs_initialized || rs_stir_pid != pid) { + rs_stir_pid = pid; + _rs_stir(); + } else + rs_count -= len; +} + +static inline void +_rs_rekey(u_char *dat, size_t datlen) +{ +#ifndef KEYSTREAM_ONLY + memset(rs_buf, 0,RSBUFSZ); +#endif + /* fill rs_buf with the keystream */ + chacha_encrypt_bytes(&rs, rs_buf, rs_buf, RSBUFSZ); + /* mix in optional user provided data */ + if (dat) { + size_t i, m; + + m = MIN(datlen, KEYSZ + IVSZ); + for (i = 0; i < m; i++) + rs_buf[i] ^= dat[i]; + } + /* immediately reinit for backtracking resistance */ + _rs_init(rs_buf, KEYSZ + IVSZ); + memset(rs_buf, 0, KEYSZ + IVSZ); + rs_have = RSBUFSZ - KEYSZ - IVSZ; +} + +static inline void +_rs_random_buf(void *_buf, size_t n) +{ + u_char *buf = (u_char *)_buf; + size_t m; + + _rs_stir_if_needed(n); + while (n > 0) { + if (rs_have > 0) { + m = MIN(n, rs_have); + memcpy(buf, rs_buf + RSBUFSZ - rs_have, m); + memset(rs_buf + RSBUFSZ - rs_have, 0, m); + buf += m; + n -= m; + rs_have -= m; + } + if (rs_have == 0) + _rs_rekey(NULL, 0); + } +} + +static inline void +_rs_random_u32(uint32_t *val) +{ + _rs_stir_if_needed(sizeof(*val)); + if (rs_have < sizeof(*val)) + _rs_rekey(NULL, 0); + memcpy(val, rs_buf + RSBUFSZ - rs_have, sizeof(*val)); + memset(rs_buf + RSBUFSZ - rs_have, 0, sizeof(*val)); + rs_have -= sizeof(*val); + return; +} + +void +arc4random_stir(void) +{ + _ARC4_LOCK(); + _rs_stir(); + _ARC4_UNLOCK(); +} + +void +arc4random_addrandom(u_char *dat, int datlen) +{ + int m; + + _ARC4_LOCK(); + if (!rs_initialized) + _rs_stir(); + while (datlen > 0) { + m = MIN(datlen, KEYSZ + IVSZ); + _rs_rekey(dat, m); + dat += m; + datlen -= m; + } + _ARC4_UNLOCK(); +} + +uint32_t +arc4random(void) +{ + uint32_t val; + + _ARC4_LOCK(); + _rs_random_u32(&val); + _ARC4_UNLOCK(); + return val; +} + +/* + * If we are providing arc4random, then we can provide a more efficient + * arc4random_buf(). + */ +void +arc4random_buf(void *buf, size_t n) +{ + _ARC4_LOCK(); + _rs_random_buf(buf, n); + _ARC4_UNLOCK(); +} + +/* + * Calculate a uniformly distributed random number less than upper_bound + * avoiding "modulo bias". + * + * Uniformity is achieved by generating new random numbers until the one + * returned is outside the range [0, 2**32 % upper_bound). This + * guarantees the selected random number will be inside + * [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound) + * after reduction modulo upper_bound. + */ +uint32_t +arc4random_uniform(uint32_t upper_bound) +{ + uint32_t r, min; + + if (upper_bound < 2) + return 0; + + /* 2**32 % x == (2**32 - x) % x */ + min = -upper_bound % upper_bound; + + /* + * This could theoretically loop forever but each retry has + * p > 0.5 (worst case, usually far better) of selecting a + * number inside the range we need, so it should rarely need + * to re-roll. + */ + for (;;) { + r = arc4random(); + if (r >= min) + break; + } + + return r % upper_bound; +} diff --git a/openbsd-compat/base64.c b/openbsd-compat/base64.c new file mode 100644 index 00000000..a3c5782b --- /dev/null +++ b/openbsd-compat/base64.c @@ -0,0 +1,306 @@ +/* $OpenBSD: base64.c,v 1.5 2006/10/21 09:55:03 otto Exp $ */ + +/* + * Copyright (c) 1996 by Internet Software Consortium. + * + * 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 INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM 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. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +/* OPENBSD ORIGINAL: lib/libc/net/base64.c */ + +#include "includes.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <ctype.h> +#include <stdio.h> + +#include <stdlib.h> +#include <string.h> + +#include "base64.h" + +static const char Base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + +/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) + The following encoding technique is taken from RFC 1521 by Borenstein + and Freed. It is reproduced here in a slightly edited form for + convenience. + + A 65-character subset of US-ASCII is used, enabling 6 bits to be + represented per printable character. (The extra 65th character, "=", + is used to signify a special processing function.) + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8-bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + + Each 6-bit group is used as an index into an array of 64 printable + characters. The character referenced by the index is placed in the + output string. + + Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a quantity. When fewer than 24 input + bits are available in an input group, zero bits are added (on the + right) to form an integral number of 6-bit groups. Padding at the + end of the data is performed using the '=' character. + + Since all base64 input is an integral number of octets, only the + ------------------------------------------------- + following cases can arise: + + (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded + output will be an integral multiple of 4 characters + with no "=" padding, + (2) the final quantum of encoding input is exactly 8 bits; + here, the final unit of encoded output will be two + characters followed by two "=" padding characters, or + (3) the final quantum of encoding input is exactly 16 bits; + here, the final unit of encoded output will be three + characters followed by one "=" padding character. + */ + +int +b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize) +{ + size_t datalength = 0; + u_char input[3]; + u_char output[4]; + u_int i; + + while (2 < srclength) { + input[0] = *src++; + input[1] = *src++; + input[2] = *src++; + srclength -= 3; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + output[3] = input[2] & 0x3f; + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + target[datalength++] = Base64[output[2]]; + target[datalength++] = Base64[output[3]]; + } + + /* Now we worry about padding. */ + if (0 != srclength) { + /* Get what's left. */ + input[0] = input[1] = input[2] = '\0'; + for (i = 0; i < srclength; i++) + input[i] = *src++; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + if (srclength == 1) + target[datalength++] = Pad64; + else + target[datalength++] = Base64[output[2]]; + target[datalength++] = Pad64; + } + if (datalength >= targsize) + return (-1); + target[datalength] = '\0'; /* Returned value doesn't count \0. */ + return (datalength); +} + +/* skips all whitespace anywhere. + converts characters, four at a time, starting at (or after) + src from base - 64 numbers into three 8 bit bytes in the target area. + it returns the number of data bytes stored at the target, or -1 on error. + */ + +int +b64_pton(char const *src, u_char *target, size_t targsize) +{ + u_int tarindex, state; + int ch; + char *pos; + + state = 0; + tarindex = 0; + + while ((ch = *src++) != '\0') { + if (isspace(ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = strchr(Base64, ch); + if (pos == 0) /* A non-base64 character. */ + return (-1); + + switch (state) { + case 0: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] = (pos - Base64) << 2; + } + state = 1; + break; + case 1: + if (target) { + if (tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + target[tarindex+1] = ((pos - Base64) & 0x0f) + << 4 ; + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if (tarindex + 1 >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + target[tarindex+1] = ((pos - Base64) & 0x03) + << 6; + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = *src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for (; ch != '\0'; ch = *src++) + if (!isspace(ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = *src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for (; ch != '\0'; ch = *src++) + if (!isspace(ch)) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + return (tarindex); +} diff --git a/openbsd-compat/base64.h b/openbsd-compat/base64.h new file mode 100644 index 00000000..732c6b3f --- /dev/null +++ b/openbsd-compat/base64.h @@ -0,0 +1,65 @@ +/* $Id: base64.h,v 1.6 2003/08/29 16:59:52 mouring Exp $ */ + +/* + * Copyright (c) 1996 by Internet Software Consortium. + * + * 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 INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM 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. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +#ifndef _BSD_BASE64_H +#define _BSD_BASE64_H + +#include "includes.h" + +#ifndef HAVE___B64_NTOP +# ifndef HAVE_B64_NTOP +int b64_ntop(u_char const *src, size_t srclength, char *target, + size_t targsize); +# endif /* !HAVE_B64_NTOP */ +# define __b64_ntop(a,b,c,d) b64_ntop(a,b,c,d) +#endif /* HAVE___B64_NTOP */ + +#ifndef HAVE___B64_PTON +# ifndef HAVE_B64_PTON +int b64_pton(char const *src, u_char *target, size_t targsize); +# endif /* !HAVE_B64_PTON */ +# define __b64_pton(a,b,c) b64_pton(a,b,c) +#endif /* HAVE___B64_PTON */ + +#endif /* _BSD_BASE64_H */ diff --git a/openbsd-compat/basename.c b/openbsd-compat/basename.c new file mode 100644 index 00000000..ffa5c898 --- /dev/null +++ b/openbsd-compat/basename.c @@ -0,0 +1,67 @@ +/* $OpenBSD: basename.c,v 1.14 2005/08/08 08:05:33 espie Exp $ */ + +/* + * Copyright (c) 1997, 2004 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/gen/basename.c */ + +#include "includes.h" +#ifndef HAVE_BASENAME +#include <errno.h> +#include <string.h> + +char * +basename(const char *path) +{ + static char bname[MAXPATHLEN]; + size_t len; + const char *endp, *startp; + + /* Empty or NULL string gets treated as "." */ + if (path == NULL || *path == '\0') { + bname[0] = '.'; + bname[1] = '\0'; + return (bname); + } + + /* Strip any trailing slashes */ + endp = path + strlen(path) - 1; + while (endp > path && *endp == '/') + endp--; + + /* All slashes becomes "/" */ + if (endp == path && *endp == '/') { + bname[0] = '/'; + bname[1] = '\0'; + return (bname); + } + + /* Find the start of the base */ + startp = endp; + while (startp > path && *(startp - 1) != '/') + startp--; + + len = endp - startp + 1; + if (len >= sizeof(bname)) { + errno = ENAMETOOLONG; + return (NULL); + } + memcpy(bname, startp, len); + bname[len] = '\0'; + return (bname); +} + +#endif /* !defined(HAVE_BASENAME) */ diff --git a/openbsd-compat/bsd-err.c b/openbsd-compat/bsd-err.c new file mode 100644 index 00000000..a7823fad --- /dev/null +++ b/openbsd-compat/bsd-err.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2015 Tim Rice <tim@multitalents.net> + * + * 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "includes.h" + +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +extern char *__progname; + +void +err(int r, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + fprintf(stderr, "%s: ", __progname); + if (fmt != NULL) { + vfprintf(stderr, fmt, args); + fprintf(stderr, ": "); + } + fprintf(stderr, "%s\n", strerror(errno)); + va_end(args); + exit(r); +} + +void +errx(int r, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + fprintf(stderr, "%s: ", __progname); + if (fmt != NULL) + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); + exit(r); +} + +void +warn(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + fprintf(stderr, "%s: ", __progname); + if (fmt != NULL) { + vfprintf(stderr, fmt, args); + fprintf(stderr, ": "); + } + fprintf(stderr, "%s\n", strerror(errno)); + va_end(args); +} + +void +warnx(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + fprintf(stderr, "%s: ", __progname); + if (fmt != NULL) + vfprintf(stderr, fmt, args); + fprintf(stderr, "\n"); + va_end(args); +} diff --git a/openbsd-compat/bsd-err.h b/openbsd-compat/bsd-err.h new file mode 100644 index 00000000..f75d0eb4 --- /dev/null +++ b/openbsd-compat/bsd-err.h @@ -0,0 +1,29 @@ +/* + * Public domain + * err.h compatibility shim + */ + +#ifndef HAVE_ERR_H + +#ifndef LIBCRYPTOCOMPAT_ERR_H +#define LIBCRYPTOCOMPAT_ERR_H + +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#define err(exitcode, format, args...) \ + errx(exitcode, format ": %s", ## args, strerror(errno)) + +#define errx(exitcode, format, args...) \ + do { warnx(format, ## args); exit(exitcode); } while (0) + +#define warn(format, args...) \ + warnx(format ": %s", ## args, strerror(errno)) + +#define warnx(format, args...) \ + fprintf(stderr, format "\n", ## args) + +#endif + +#endif diff --git a/openbsd-compat/bsd-misc.h b/openbsd-compat/bsd-misc.h new file mode 100644 index 00000000..c638462d --- /dev/null +++ b/openbsd-compat/bsd-misc.h @@ -0,0 +1,24 @@ +/* $Id: bsd-misc.h,v 1.25 2013/08/04 11:48:41 dtucker Exp $ */ + +/* + * Copyright (c) 1999-2004 Damien Miller <djm@mindrot.org> + * + * 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 _BSD_MISC_H +#define _BSD_MISC_H + +#include "includes.h" + +#endif /* _BSD_MISC_H */ diff --git a/openbsd-compat/bsd-vis.h b/openbsd-compat/bsd-vis.h new file mode 100644 index 00000000..d1286c99 --- /dev/null +++ b/openbsd-compat/bsd-vis.h @@ -0,0 +1,95 @@ +/* $OpenBSD: vis.h,v 1.11 2005/08/09 19:38:31 millert Exp $ */ +/* $NetBSD: vis.h,v 1.4 1994/10/26 00:56:41 cgd Exp $ */ + +/*- + * Copyright (c) 1990 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. + * + * @(#)vis.h 5.9 (Berkeley) 4/3/91 + */ + +/* OPENBSD ORIGINAL: include/vis.h */ + +#include "includes.h" +#if !defined(HAVE_STRNVIS) || defined(BROKEN_STRNVIS) + +#ifndef _VIS_H_ +#define _VIS_H_ + +#include <sys/types.h> +#include <limits.h> + +/* + * to select alternate encoding format + */ +#define VIS_OCTAL 0x01 /* use octal \ddd format */ +#define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropriate */ + +/* + * to alter set of characters encoded (default is to encode all + * non-graphic except space, tab, and newline). + */ +#define VIS_SP 0x04 /* also encode space */ +#define VIS_TAB 0x08 /* also encode tab */ +#define VIS_NL 0x10 /* also encode newline */ +#define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL) +#define VIS_SAFE 0x20 /* only encode "unsafe" characters */ + +/* + * other + */ +#define VIS_NOSLASH 0x40 /* inhibit printing '\' */ +#define VIS_GLOB 0x100 /* encode glob(3) magics and '#' */ + +/* + * unvis return codes + */ +#define UNVIS_VALID 1 /* character valid */ +#define UNVIS_VALIDPUSH 2 /* character valid, push back passed char */ +#define UNVIS_NOCHAR 3 /* valid sequence, no character produced */ +#define UNVIS_SYNBAD -1 /* unrecognized escape sequence */ +#define UNVIS_ERROR -2 /* decoder in unknown state (unrecoverable) */ + +/* + * unvis flags + */ +#define UNVIS_END 1 /* no more characters */ + +char *vis(char *, int, int, int); +int strvis(char *, const char *, int); +int strnvis(char *, const char *, size_t, int) + __attribute__ ((__bounded__(__string__,1,3))); +int strvisx(char *, const char *, size_t, int) + __attribute__ ((__bounded__(__string__,1,3))); +int strunvis(char *, const char *); +int unvis(char *, char, int *, int); +ssize_t strnunvis(char *, const char *, size_t) + __attribute__ ((__bounded__(__string__,1,3))); + +#endif /* !_VIS_H_ */ + +#endif /* !HAVE_STRNVIS || BROKEN_STRNVIS */ diff --git a/openbsd-compat/bsd-waitpid.c b/openbsd-compat/bsd-waitpid.c new file mode 100644 index 00000000..3ef68a53 --- /dev/null +++ b/openbsd-compat/bsd-waitpid.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2000 Ben Lindstrom. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "includes.h" + +#include <errno.h> +#include <sys/wait.h> +#include "bsd-waitpid.h" + +pid_t +waitpid(int pid, int *stat_loc, int options) +{ + union wait statusp; + pid_t wait_pid; + + if (pid <= 0) { + if (pid != -1) { + errno = EINVAL; + return (-1); + } + /* wait4() wants pid=0 for indiscriminate wait. */ + pid = 0; + } + wait_pid = wait4(pid, &statusp, options, NULL); + if (stat_loc) + *stat_loc = (int) statusp.w_status; + + return (wait_pid); +} diff --git a/openbsd-compat/bsd-waitpid.h b/openbsd-compat/bsd-waitpid.h new file mode 100644 index 00000000..2d853db6 --- /dev/null +++ b/openbsd-compat/bsd-waitpid.h @@ -0,0 +1,51 @@ +/* $Id: bsd-waitpid.h,v 1.5 2003/08/29 16:59:52 mouring Exp $ */ + +/* + * Copyright (c) 2000 Ben Lindstrom. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _BSD_WAITPID_H +#define _BSD_WAITPID_H + +#ifndef HAVE_WAITPID +/* Clean out any potental issues */ +#undef WIFEXITED +#undef WIFSTOPPED +#undef WIFSIGNALED + +/* Define required functions to mimic a POSIX look and feel */ +#define _W_INT(w) (*(int*)&(w)) /* convert union wait to int */ +#define WIFEXITED(w) (!((_W_INT(w)) & 0377)) +#define WIFSTOPPED(w) ((_W_INT(w)) & 0100) +#define WIFSIGNALED(w) (!WIFEXITED(w) && !WIFSTOPPED(w)) +#define WEXITSTATUS(w) (int)(WIFEXITED(w) ? ((_W_INT(w) >> 8) & 0377) : -1) +#define WTERMSIG(w) (int)(WIFSIGNALED(w) ? (_W_INT(w) & 0177) : -1) +#define WCOREFLAG 0x80 +#define WCOREDUMP(w) ((_W_INT(w)) & WCOREFLAG) + +/* Prototype */ +pid_t waitpid(int, int *, int); + +#endif /* !HAVE_WAITPID */ +#endif /* _BSD_WAITPID_H */ diff --git a/openbsd-compat/chacha_private.h b/openbsd-compat/chacha_private.h new file mode 100644 index 00000000..46613646 --- /dev/null +++ b/openbsd-compat/chacha_private.h @@ -0,0 +1,224 @@ +/* +chacha-merged.c version 20080118 +D. J. Bernstein +Public domain. +*/ + +/* $OpenBSD: chacha_private.h,v 1.2 2013/10/04 07:02:27 djm Exp $ */ + +#include <sys/types.h> + +typedef unsigned char u8; +typedef unsigned int u32; + +typedef struct +{ + u32 input[16]; /* could be compressed */ +} chacha_ctx; + +#define U8C(v) (v##U) +#define U32C(v) (v##U) + +#define U8V(v) ((u8)(v) & U8C(0xFF)) +#define U32V(v) ((u32)(v) & U32C(0xFFFFFFFF)) + +#define ROTL32(v, n) \ + (U32V((v) << (n)) | ((v) >> (32 - (n)))) + +#define U8TO32_LITTLE(p) \ + (((u32)((p)[0]) ) | \ + ((u32)((p)[1]) << 8) | \ + ((u32)((p)[2]) << 16) | \ + ((u32)((p)[3]) << 24)) + +#define U32TO8_LITTLE(p, v) \ + do { \ + (p)[0] = U8V((v) ); \ + (p)[1] = U8V((v) >> 8); \ + (p)[2] = U8V((v) >> 16); \ + (p)[3] = U8V((v) >> 24); \ + } while (0) + +#define ROTATE(v,c) (ROTL32(v,c)) +#define XOR(v,w) ((v) ^ (w)) +#define PLUS(v,w) (U32V((v) + (w))) +#define PLUSONE(v) (PLUS((v),1)) + +#define QUARTERROUND(a,b,c,d) \ + a = PLUS(a,b); d = ROTATE(XOR(d,a),16); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c),12); \ + a = PLUS(a,b); d = ROTATE(XOR(d,a), 8); \ + c = PLUS(c,d); b = ROTATE(XOR(b,c), 7); + +static const char sigma[16] = "expand 32-byte k"; +static const char tau[16] = "expand 16-byte k"; + +static void +chacha_keysetup(chacha_ctx *x,const u8 *k,u32 kbits,u32 ivbits) +{ + const char *constants; + + x->input[4] = U8TO32_LITTLE(k + 0); + x->input[5] = U8TO32_LITTLE(k + 4); + x->input[6] = U8TO32_LITTLE(k + 8); + x->input[7] = U8TO32_LITTLE(k + 12); + if (kbits == 256) { /* recommended */ + k += 16; + constants = sigma; + } else { /* kbits == 128 */ + constants = tau; + } + x->input[8] = U8TO32_LITTLE(k + 0); + x->input[9] = U8TO32_LITTLE(k + 4); + x->input[10] = U8TO32_LITTLE(k + 8); + x->input[11] = U8TO32_LITTLE(k + 12); + x->input[0] = U8TO32_LITTLE(constants + 0); + x->input[1] = U8TO32_LITTLE(constants + 4); + x->input[2] = U8TO32_LITTLE(constants + 8); + x->input[3] = U8TO32_LITTLE(constants + 12); +} + +static void +chacha_ivsetup(chacha_ctx *x,const u8 *iv) +{ + x->input[12] = 0; + x->input[13] = 0; + x->input[14] = U8TO32_LITTLE(iv + 0); + x->input[15] = U8TO32_LITTLE(iv + 4); +} + +static void +chacha_encrypt_bytes(chacha_ctx *x,const u8 *m,u8 *c,u32 bytes) +{ + u32 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + u32 j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15; + u8 *ctarget = NULL; + u8 tmp[64]; + u_int i; + + if (!bytes) return; + + j0 = x->input[0]; + j1 = x->input[1]; + j2 = x->input[2]; + j3 = x->input[3]; + j4 = x->input[4]; + j5 = x->input[5]; + j6 = x->input[6]; + j7 = x->input[7]; + j8 = x->input[8]; + j9 = x->input[9]; + j10 = x->input[10]; + j11 = x->input[11]; + j12 = x->input[12]; + j13 = x->input[13]; + j14 = x->input[14]; + j15 = x->input[15]; + + for (;;) { + if (bytes < 64) { + for (i = 0;i < bytes;++i) tmp[i] = m[i]; + m = tmp; + ctarget = c; + c = tmp; + } + x0 = j0; + x1 = j1; + x2 = j2; + x3 = j3; + x4 = j4; + x5 = j5; + x6 = j6; + x7 = j7; + x8 = j8; + x9 = j9; + x10 = j10; + x11 = j11; + x12 = j12; + x13 = j13; + x14 = j14; + x15 = j15; + for (i = 20;i > 0;i -= 2) { + QUARTERROUND( x0, x4, x8,x12) + QUARTERROUND( x1, x5, x9,x13) + QUARTERROUND( x2, x6,x10,x14) + QUARTERROUND( x3, x7,x11,x15) + QUARTERROUND( x0, x5,x10,x15) + QUARTERROUND( x1, x6,x11,x12) + QUARTERROUND( x2, x7, x8,x13) + QUARTERROUND( x3, x4, x9,x14) + } + x0 = PLUS(x0,j0); + x1 = PLUS(x1,j1); + x2 = PLUS(x2,j2); + x3 = PLUS(x3,j3); + x4 = PLUS(x4,j4); + x5 = PLUS(x5,j5); + x6 = PLUS(x6,j6); + x7 = PLUS(x7,j7); + x8 = PLUS(x8,j8); + x9 = PLUS(x9,j9); + x10 = PLUS(x10,j10); + x11 = PLUS(x11,j11); + x12 = PLUS(x12,j12); + x13 = PLUS(x13,j13); + x14 = PLUS(x14,j14); + x15 = PLUS(x15,j15); + +#ifndef KEYSTREAM_ONLY + x0 = XOR(x0,U8TO32_LITTLE(m + 0)); + x1 = XOR(x1,U8TO32_LITTLE(m + 4)); + x2 = XOR(x2,U8TO32_LITTLE(m + 8)); + x3 = XOR(x3,U8TO32_LITTLE(m + 12)); + x4 = XOR(x4,U8TO32_LITTLE(m + 16)); + x5 = XOR(x5,U8TO32_LITTLE(m + 20)); + x6 = XOR(x6,U8TO32_LITTLE(m + 24)); + x7 = XOR(x7,U8TO32_LITTLE(m + 28)); + x8 = XOR(x8,U8TO32_LITTLE(m + 32)); + x9 = XOR(x9,U8TO32_LITTLE(m + 36)); + x10 = XOR(x10,U8TO32_LITTLE(m + 40)); + x11 = XOR(x11,U8TO32_LITTLE(m + 44)); + x12 = XOR(x12,U8TO32_LITTLE(m + 48)); + x13 = XOR(x13,U8TO32_LITTLE(m + 52)); + x14 = XOR(x14,U8TO32_LITTLE(m + 56)); + x15 = XOR(x15,U8TO32_LITTLE(m + 60)); +#endif + + j12 = PLUSONE(j12); + if (!j12) { + j13 = PLUSONE(j13); + /* stopping at 2^70 bytes per nonce is user's responsibility */ + } + + U32TO8_LITTLE(c + 0,x0); + U32TO8_LITTLE(c + 4,x1); + U32TO8_LITTLE(c + 8,x2); + U32TO8_LITTLE(c + 12,x3); + U32TO8_LITTLE(c + 16,x4); + U32TO8_LITTLE(c + 20,x5); + U32TO8_LITTLE(c + 24,x6); + U32TO8_LITTLE(c + 28,x7); + U32TO8_LITTLE(c + 32,x8); + U32TO8_LITTLE(c + 36,x9); + U32TO8_LITTLE(c + 40,x10); + U32TO8_LITTLE(c + 44,x11); + U32TO8_LITTLE(c + 48,x12); + U32TO8_LITTLE(c + 52,x13); + U32TO8_LITTLE(c + 56,x14); + U32TO8_LITTLE(c + 60,x15); + + if (bytes <= 64) { + if (bytes < 64) { + for (i = 0;i < bytes;++i) ctarget[i] = c[i]; + } + x->input[12] = j12; + x->input[13] = j13; + return; + } + bytes -= 64; + c += 64; +#ifndef KEYSTREAM_ONLY + m += 64; +#endif + } +} diff --git a/openbsd-compat/clock_gettime.c b/openbsd-compat/clock_gettime.c new file mode 100644 index 00000000..6c1ef0d4 --- /dev/null +++ b/openbsd-compat/clock_gettime.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2012 Charles Longeau <chl@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#ifdef HAVE_MACH_MACH_TIME_H +#include <mach/mach_time.h> +#endif +#include <sys/time.h> +#include <time.h> + +#if !defined(HAVE_CLOCK_GETTIME) +int +clock_gettime(int clock_id, struct timespec *tp) +{ + int ret = 0; + uint64_t time; + mach_timebase_info_data_t info; + static double scaling_factor = 0; + +#if 0 + struct timeval tv; + + ret = gettimeofday(&tv, NULL); + TIMEVAL_TO_TIMESPEC(&tv, tp); +#endif + +/* based on http://code-factor.blogspot.fr/2009/11/monotonic-timers.html */ + + time = mach_absolute_time(); + + if (scaling_factor == 0) { + ret = (int) mach_timebase_info(&info); + if (ret != 0) + fatal("mach_timebase_info failed"); + scaling_factor = info.numer/info.denom; + } + + time *= scaling_factor; + + tp->tv_sec = time / 1000000000; + tp->tv_nsec = time % 1000000000; + + return (ret); +} +#endif diff --git a/openbsd-compat/closefrom.c b/openbsd-compat/closefrom.c new file mode 100644 index 00000000..528949a6 --- /dev/null +++ b/openbsd-compat/closefrom.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2004-2005 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/types.h> +#include <sys/param.h> +#include <unistd.h> +#include <stdio.h> +#ifdef HAVE_FCNTL_H +# include <fcntl.h> +#endif +#include <limits.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <unistd.h> +#ifdef HAVE_DIRENT_H +# include <dirent.h> +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# ifdef HAVE_SYS_NDIR_H +# include <sys/ndir.h> +# endif +# ifdef HAVE_SYS_DIR_H +# include <sys/dir.h> +# endif +# ifdef HAVE_NDIR_H +# include <ndir.h> +# endif +#endif + +#ifndef OPEN_MAX +# define OPEN_MAX 256 +#endif + +#if 0 +__unused static const char rcsid[] = "$Sudo: closefrom.c,v 1.11 2006/08/17 15:26:54 millert Exp $"; +#endif /* lint */ + +/* + * Close all file descriptors greater than or equal to lowfd. + */ +#ifdef HAVE_FCNTL_CLOSEM +void +closefrom(int lowfd) +{ + (void) fcntl(lowfd, F_CLOSEM, 0); +} +#else +void +closefrom(int lowfd) +{ + long fd, maxfd; +#if defined(HAVE_DIRFD) && defined(HAVE_PROC_PID) + char fdpath[PATH_MAX], *endp; + struct dirent *dent; + DIR *dirp; + int len; + + /* Check for a /proc/$$/fd directory. */ + len = snprintf(fdpath, sizeof(fdpath), "/proc/%ld/fd", (long)getpid()); + if (len > 0 && (size_t)len <= sizeof(fdpath) && (dirp = opendir(fdpath))) { + while ((dent = readdir(dirp)) != NULL) { + fd = strtol(dent->d_name, &endp, 10); + if (dent->d_name != endp && *endp == '\0' && + fd >= 0 && fd < INT_MAX && fd >= lowfd && fd != dirfd(dirp)) + (void) close((int) fd); + } + (void) closedir(dirp); + } else +#endif + { + /* + * Fall back on sysconf() or getdtablesize(). We avoid checking + * resource limits since it is possible to open a file descriptor + * and then drop the rlimit such that it is below the open fd. + */ +#ifdef HAVE_SYSCONF + maxfd = sysconf(_SC_OPEN_MAX); +#else + maxfd = getdtablesize(); +#endif /* HAVE_SYSCONF */ + if (maxfd < 0) + maxfd = OPEN_MAX; + + for (fd = lowfd; fd < maxfd; fd++) + (void) close((int) fd); + } +} +#endif /* !HAVE_FCNTL_CLOSEM */ + diff --git a/openbsd-compat/crypt_checkpass.c b/openbsd-compat/crypt_checkpass.c new file mode 100644 index 00000000..d10b3a57 --- /dev/null +++ b/openbsd-compat/crypt_checkpass.c @@ -0,0 +1,33 @@ +/* OPENBSD ORIGINAL: lib/libc/crypt/cryptutil.c */ + +#include "includes.h" +#include <errno.h> +#ifdef HAVE_CRYPT_H +#include <crypt.h> +#endif +#include <string.h> +#include <unistd.h> + +int +crypt_checkpass(const char *pass, const char *goodhash) +{ + char *c; + + if (goodhash == NULL) + goto fail; + + /* empty password */ + if (strlen(goodhash) == 0 && strlen(pass) == 0) + return 0; + + c = crypt(pass, goodhash); + if (c == NULL) + goto fail; + + if (strcmp(c, goodhash) == 0) + return 0; + +fail: + errno = EACCES; + return -1; +} diff --git a/openbsd-compat/daemon.c b/openbsd-compat/daemon.c new file mode 100644 index 00000000..3efe14c6 --- /dev/null +++ b/openbsd-compat/daemon.c @@ -0,0 +1,82 @@ +/* $OpenBSD: daemon.c,v 1.6 2005/08/08 08:05:33 espie Exp $ */ +/*- + * Copyright (c) 1990, 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. + */ + +/* OPENBSD ORIGINAL: lib/libc/gen/daemon.c */ + +#include "includes.h" + +#ifndef HAVE_DAEMON + +#include <sys/types.h> + +#ifdef HAVE_SYS_STAT_H +# include <sys/stat.h> +#endif + +#ifdef HAVE_FCNTL_H +# include <fcntl.h> +#endif + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +int +daemon(int nochdir, int noclose) +{ + int fd; + + switch (fork()) { + case -1: + return (-1); + case 0: + break; + default: + _exit(0); + } + + if (setsid() == -1) + return (-1); + + if (!nochdir) + (void)chdir("/"); + + if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { + (void)dup2(fd, STDIN_FILENO); + (void)dup2(fd, STDOUT_FILENO); + (void)dup2(fd, STDERR_FILENO); + if (fd > 2) + (void)close (fd); + } + return (0); +} + +#endif /* !HAVE_DAEMON */ + diff --git a/openbsd-compat/defines.h b/openbsd-compat/defines.h new file mode 100644 index 00000000..a6e528eb --- /dev/null +++ b/openbsd-compat/defines.h @@ -0,0 +1,510 @@ +/* + * Copyright (c) 2016 Gilles Chehade <gilles@poolp.org>. All rights reserved. + * Copyright (c) 1999-2003 Damien Miller. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _DEFINES_H +#define _DEFINES_H + +/* $Id: defines.h,v 1.181 2014/06/11 19:22:50 dtucker Exp $ */ + + +/* Constants */ +#ifndef EAUTH +# define EAUTH 80 +#endif + +#ifndef INFTIM +#define INFTIM (-1) +#endif + +#ifndef HOST_NAME_MAX +# ifdef _POSIX_HOST_NAME_MAX +# define HOST_NAME_MAX _POSIX_HOST_NAME_MAX +# endif +#endif + +#ifndef PATH_MAX +# ifdef _POSIX_PATH_MAX +# define PATH_MAX _POSIX_PATH_MAX +# endif +#endif + +#ifndef MAXPATHLEN +# ifdef PATH_MAX +# define MAXPATHLEN PATH_MAX +# else /* PATH_MAX */ +# define MAXPATHLEN 64 +# define PATH_MAX 64 +/* realpath uses a fixed buffer of size MAXPATHLEN, so force use of ours */ +# ifndef BROKEN_REALPATH +# define BROKEN_REALPATH 1 +# endif /* BROKEN_REALPATH */ +# endif /* PATH_MAX */ +#endif /* MAXPATHLEN */ + +#ifndef MAXHOSTNAMELEN +# define MAXHOSTNAMELEN 64 +#endif + +#ifndef LOGIN_NAME_MAX +# define LOGIN_NAME_MAX 32 +#endif + +#ifndef MAXLOGNAME +#define MAXLOGNAME LOGIN_NAME_MAX +#endif + +#ifndef UID_MAX +#define UID_MAX UINT_MAX +#endif +#ifndef GID_MAX +#define GID_MAX UINT_MAX +#endif + +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +#endif +#ifndef STDOUT_FILENO +# define STDOUT_FILENO 1 +#endif +#ifndef STDERR_FILENO +# define STDERR_FILENO 2 +#endif + +#if !HAVE_DECL_O_NONBLOCK +# define O_NONBLOCK 00004 /* Non Blocking Open */ +#endif + +#ifndef O_EXLOCK +#define O_EXLOCK 0 +#endif + +#ifndef S_ISDIR +# define S_ISDIR(mode) (((mode) & (_S_IFMT)) == (_S_IFDIR)) +#endif /* S_ISDIR */ + +#ifndef S_ISREG +# define S_ISREG(mode) (((mode) & (_S_IFMT)) == (_S_IFREG)) +#endif /* S_ISREG */ + +#ifndef S_ISLNK +# define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#endif /* S_ISLNK */ + +#ifndef S_IXUSR +# define S_ISUID 0004000 /* set-uid */ +# define S_ISGID 0002000 /* set-gid */ +# define S_ISVTX 0001000 /* sticky */ +# define S_IXUSR 0000100 /* execute/search permission, */ +# define S_IXGRP 0000010 /* execute/search permission, */ +# define S_IXOTH 0000001 /* execute/search permission, */ +# define _S_IWUSR 0000200 /* write permission, */ +# define S_IWUSR _S_IWUSR /* write permission, owner */ +# define S_IWGRP 0000020 /* write permission, group */ +# define S_IWOTH 0000002 /* write permission, other */ +# define S_IRUSR 0000400 /* read permission, owner */ +# define S_IRGRP 0000040 /* read permission, group */ +# define S_IROTH 0000004 /* read permission, other */ +# define S_IRWXU 0000700 /* read, write, execute */ +# define S_IRWXG 0000070 /* read, write, execute */ +# define S_IRWXO 0000007 /* read, write, execute */ +#endif /* S_IXUSR */ + +#if !defined(MAP_ANON) && defined(MAP_ANONYMOUS) +#define MAP_ANON MAP_ANONYMOUS +#endif + +#ifndef MAP_FAILED +# define MAP_FAILED ((void *)-1) +#endif + +/* +SCO Open Server 3 has INADDR_LOOPBACK defined in rpc/rpc.h but +including rpc/rpc.h breaks Solaris 6 +*/ +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK ((u_long)0x7f000001) +#endif + + +/* Types */ +#ifndef HAVE_U_CHAR +typedef unsigned char u_char; +# define HAVE_U_CHAR +#endif /* HAVE_U_CHAR */ + +#ifndef HAVE_U_INT +typedef unsigned int u_int; +# define HAVE_U_INT +#endif + +#ifndef HAVE_INTMAX_T +typedef long long intmax_t; +# define HAVE_INTMAX_T +#endif + +#ifndef HAVE_UINTMAX_T +typedef unsigned long long uintmax_t; +# define HAVE_UINTMAX_T +#endif + +#ifndef HAVE_SA_FAMILY_T +typedef int sa_family_t; +# define HAVE_SA_FAMILY_T +#endif /* HAVE_SA_FAMILY_T */ + +#ifndef HAVE_SIG_ATOMIC_T +typedef int sig_atomic_t; +# define HAVE_SIG_ATOMIC_T +#endif /* HAVE_SIG_ATOMIC_T */ + + +#ifndef ULLONG_MAX +# define ULLONG_MAX ((unsigned long long)-1) +#endif + +#ifndef SIZE_T_MAX +#define SIZE_T_MAX ULONG_MAX +#endif /* SIZE_T_MAX */ + +#ifndef SIZE_MAX +#define SIZE_MAX SIZE_T_MAX +#endif + + + +#if !defined(HAVE_SS_FAMILY_IN_SS) && defined(HAVE___SS_FAMILY_IN_SS) +# define ss_family __ss_family +#endif /* !defined(HAVE_SS_FAMILY_IN_SS) && defined(HAVE_SA_FAMILY_IN_SS) */ + +#ifndef HAVE_SYS_UN_H +struct sockaddr_un { + short sun_family; /* AF_UNIX */ + char sun_path[108]; /* path name (gag) */ +}; +#endif /* HAVE_SYS_UN_H */ + +#ifndef HAVE_IN_ADDR_T +typedef uint32_t in_addr_t; +#endif + +#ifndef HAVE_IN_PORT_T +typedef uint16_t in_port_t; +#endif + + +/* Paths */ + +/* needed by compat/daemon.c */ +#ifndef _PATH_DEVNULL +# define _PATH_DEVNULL "/dev/null" +#endif + +/* user may have set a different path */ +#if !defined(_PATH_MAILDIR) +# define _PATH_MAILDIR "/var/spool/mail" +#endif + +#if defined(_PATH_MAILDIR) && defined(MAIL_DIRECTORY) +# undef _PATH_MAILDIR +#endif /* defined(_PATH_MAILDIR) && defined(MAIL_DIRECTORY) */ + +#ifdef MAIL_DIRECTORY +# define _PATH_MAILDIR MAIL_DIRECTORY +#endif + +#ifdef MAILDIR +# undef MAILDIR +#endif + + + +/* Macros */ + +/* needed by compat */ +#ifndef MAX +# define MAX(a,b) (((a)>(b))?(a):(b)) +#endif +#ifndef MIN +# define MIN(a,b) (((a)<(b))?(a):(b)) +#endif + +/* needed by smtpd */ +#ifndef timespeccmp +#define timespeccmp(a, b, cmp) \ + (((a)->tv_sec == (b)->tv_sec) ? \ + ((a)->tv_nsec cmp (b)->tv_nsec) : \ + ((a)->tv_sec cmp (b)->tv_sec)) +#endif + +/* needed by smtpd */ +#ifndef timespecsub +#define timespecsub(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_nsec = (a)->tv_nsec - (b)->tv_nsec; \ + if ((result)->tv_nsec < 0) { \ + --(result)->tv_sec; \ + (result)->tv_nsec += 1000000000L; \ + } \ + } while (0) +#endif + +/* needed by smtpd */ +#ifndef TIMEVAL_TO_TIMESPEC +#define TIMEVAL_TO_TIMESPEC(tv, ts) { \ + (ts)->tv_sec = (tv)->tv_sec; \ + (ts)->tv_nsec = (tv)->tv_usec * 1000; \ +} +#endif + +/* needed by compat */ +#ifndef TIMESPEC_TO_TIMEVAL +#define TIMESPEC_TO_TIMEVAL(tv, ts) { \ + (tv)->tv_sec = (ts)->tv_sec; \ + (tv)->tv_usec = (ts)->tv_nsec / 1000; \ +} +#endif + +#ifndef __P +# define __P(x) x +#endif + +#if !defined(IN6_IS_ADDR_V4MAPPED) +# define IN6_IS_ADDR_V4MAPPED(a) \ + ((((uint32_t *) (a))[0] == 0) && (((uint32_t *) (a))[1] == 0) && \ + (((uint32_t *) (a))[2] == htonl (0xffff))) +#endif /* !defined(IN6_IS_ADDR_V4MAPPED) */ + +#if !defined(__GNUC__) || (__GNUC__ < 2) +# define __attribute__(x) +#endif /* !defined(__GNUC__) || (__GNUC__ < 2) */ + +#ifndef __dead +# define __dead __attribute__((noreturn)) +#endif + +#if !defined(HAVE_ATTRIBUTE__SENTINEL__) && !defined(__sentinel__) +# define __sentinel__ +#endif + +#if !defined(HAVE_ATTRIBUTE__BOUNDED__) && !defined(__bounded__) +# define __bounded__(x, y, z) +#endif + +#if !defined(HAVE_ATTRIBUTE__NONNULL__) && !defined(__nonnull__) +# define __nonnull__(x) +#endif + +#ifndef OSSH_ALIGNBYTES +#define OSSH_ALIGNBYTES (sizeof(int) - 1) +#endif +#ifndef __CMSG_ALIGN +#define __CMSG_ALIGN(p) (((u_int)(p) + OSSH_ALIGNBYTES) &~ OSSH_ALIGNBYTES) +#endif + +/* Length of the contents of a control message of length len */ +#ifndef CMSG_LEN +#define CMSG_LEN(len) (__CMSG_ALIGN(sizeof(struct cmsghdr)) + (len)) +#endif + +/* Length of the space taken up by a padded control message of length len */ +#ifndef CMSG_SPACE +#define CMSG_SPACE(len) (__CMSG_ALIGN(sizeof(struct cmsghdr)) + __CMSG_ALIGN(len)) +#endif + +/* given pointer to struct cmsghdr, return pointer to data */ +#ifndef CMSG_DATA +#define CMSG_DATA(cmsg) ((u_char *)(cmsg) + __CMSG_ALIGN(sizeof(struct cmsghdr))) +#endif /* CMSG_DATA */ + +/* + * RFC 2292 requires to check msg_controllen, in case that the kernel returns + * an empty list for some reasons. + */ +#ifndef CMSG_FIRSTHDR +#define CMSG_FIRSTHDR(mhdr) \ + ((mhdr)->msg_controllen >= sizeof(struct cmsghdr) ? \ + (struct cmsghdr *)(mhdr)->msg_control : \ + (struct cmsghdr *)NULL) +#endif /* CMSG_FIRSTHDR */ + + +/* Set up BSD-style BYTE_ORDER definition if it isn't there already */ +/* XXX: doesn't try to cope with strange byte orders (PDP_ENDIAN) */ +#ifndef BYTE_ORDER +# ifndef LITTLE_ENDIAN +# define LITTLE_ENDIAN 1234 +# endif /* LITTLE_ENDIAN */ +# ifndef BIG_ENDIAN +# define BIG_ENDIAN 4321 +# endif /* BIG_ENDIAN */ +# ifdef WORDS_BIGENDIAN +# define BYTE_ORDER BIG_ENDIAN +# else /* WORDS_BIGENDIAN */ +# define BYTE_ORDER LITTLE_ENDIAN +# endif /* WORDS_BIGENDIAN */ +#endif /* BYTE_ORDER */ + +/* Function replacement / compatibility hacks */ + +#if defined(BROKEN_GETADDRINFO) && defined(HAVE_GETADDRINFO) +# undef HAVE_GETADDRINFO +#endif +#if defined(BROKEN_GETADDRINFO) && defined(HAVE_FREEADDRINFO) +# undef HAVE_FREEADDRINFO +#endif +#if defined(BROKEN_GETADDRINFO) && defined(HAVE_GAI_STRERROR) +# undef HAVE_GAI_STRERROR +#endif + +#if !defined(HAVE_MEMMOVE) && defined(HAVE_BCOPY) +# define memmove(s1, s2, n) bcopy((s2), (s1), (n)) +#endif /* !defined(HAVE_MEMMOVE) && defined(HAVE_BCOPY) */ + +#if !defined(HAVE___func__) && defined(HAVE___FUNCTION__) +# define __func__ __FUNCTION__ +#elif !defined(HAVE___func__) +# define __func__ "" +#endif + + +/* Maximum number of file descriptors available */ +/* needed by compat/bsd-closefrom.c */ +#ifndef OPEN_MAX +# ifdef HAVE_SYSCONF +# define OPEN_MAX sysconf(_SC_OPEN_MAX) +# else +# define OPEN_MAX 256 +# endif +#endif + + + +/** end of login recorder definitions */ + +#ifndef IOV_MAX +# if defined(_XOPEN_IOV_MAX) +# define IOV_MAX _XOPEN_IOV_MAX +# elif defined(DEF_IOV_MAX) +# define IOV_MAX DEF_IOV_MAX +# else +# define IOV_MAX 16 +# endif +#endif + +#ifndef EWOULDBLOCK +# define EWOULDBLOCK EAGAIN +#endif + +#ifndef INET6_ADDRSTRLEN /* for non IPv6 machines */ +#define INET6_ADDRSTRLEN 46 +#endif + +#ifndef HAVE_VA_COPY +# ifdef HAVE___VA_COPY +# define va_copy(dest, src) __va_copy(dest, src) +# else +# define va_copy(dest, src) (dest) = (src) +# endif +#endif + +/* OpenSMTPD-portable specific entries */ + +/* From OpenNTPD portable */ +#if !defined(SA_LEN) +# if defined(HAVE_STRUCT_SOCKADDR_SA_LEN) +# define SA_LEN(x) ((x)->sa_len) +# else +# define SA_LEN(x) ((x)->sa_family == AF_INET6 ? \ + sizeof(struct sockaddr_in6) : \ + sizeof(struct sockaddr_in)) +# endif +#endif + +/* From OpenBGPD portable */ +#if !defined(SS_LEN) +# if defined(HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN) +# define SS_LEN(x) ((x)->ss_len) +# else +# define SS_LEN(x) SA_LEN((struct sockaddr *)(x)) +# endif +#endif + +#ifdef HAVE_SS_LEN +# define STORAGE_LEN(X) ((X).ss_len) +# define SET_STORAGE_LEN(X, Y) do { STORAGE_LEN(X) = (Y); } while(0) +#elif defined(HAVE___SS_LEN) +# define STORAGE_LEN(X) ((X).__ss_len) +# define SET_STORAGE_LEN(X, Y) do { STORAGE_LEN(X) = (Y); } while(0) +#else +# define STORAGE_LEN(X) (STORAGE_FAMILY(X) == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)) +# define SET_STORAGE_LEN(X, Y) (void) 0 +#endif + +/* chl parts */ +#ifndef EAI_NODATA +# ifdef EAI_NONAME +# define EAI_NODATA EAI_NONAME +# else +# error "Neither EAI_NODATA and EAI_NONAME are defined! :(" +# endif +#endif +/* end of chl */ + +#ifndef HAVE_FPARSELN +/* + * fparseln() specific operation flags. + */ +#define FPARSELN_UNESCESC 0x01 +#define FPARSELN_UNESCCONT 0x02 +#define FPARSELN_UNESCCOMM 0x04 +#define FPARSELN_UNESCREST 0x08 +#define FPARSELN_UNESCALL 0x0f +#endif + +#ifdef HAVE_M_DATA +#undef M_DATA +#endif + +#ifndef SCOPE_DELIMITER +#define SCOPE_DELIMITER '%' +#endif + +#ifndef HAVE_FLOCK +#define LOCK_SH 0x01 /* shared file lock */ +#define LOCK_EX 0x02 /* exclusive file lock */ +#define LOCK_NB 0x04 /* don't block when locking */ +#define LOCK_UN 0x08 /* unlock file */ +#endif + +#if !HAVE_DECL_LOG_PERROR +#define LOG_PERROR 0 +#endif + +#ifndef MAXDNAME +#define MAXDNAME 1025 +#endif + +#endif /* _DEFINES_H */ diff --git a/openbsd-compat/dirname.c b/openbsd-compat/dirname.c new file mode 100644 index 00000000..30fcb496 --- /dev/null +++ b/openbsd-compat/dirname.c @@ -0,0 +1,72 @@ +/* $OpenBSD: dirname.c,v 1.13 2005/08/08 08:05:33 espie Exp $ */ + +/* + * Copyright (c) 1997, 2004 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/gen/dirname.c */ + +#include "includes.h" +#ifndef HAVE_DIRNAME + +#include <errno.h> +#include <string.h> +#include <sys/param.h> + +char * +dirname(const char *path) +{ + static char dname[MAXPATHLEN]; + size_t len; + const char *endp; + + /* Empty or NULL string gets treated as "." */ + if (path == NULL || *path == '\0') { + dname[0] = '.'; + dname[1] = '\0'; + return (dname); + } + + /* Strip any trailing slashes */ + endp = path + strlen(path) - 1; + while (endp > path && *endp == '/') + endp--; + + /* Find the start of the dir */ + while (endp > path && *endp != '/') + endp--; + + /* Either the dir is "/" or there are no slashes */ + if (endp == path) { + dname[0] = *endp == '/' ? '/' : '.'; + dname[1] = '\0'; + return (dname); + } else { + /* Move forward past the separating slashes */ + do { + endp--; + } while (endp > path && *endp == '/'); + } + + len = endp - path + 1; + if (len >= sizeof(dname)) { + errno = ENAMETOOLONG; + return (NULL); + } + memcpy(dname, path, len); + dname[len] = '\0'; + return (dname); +} +#endif diff --git a/openbsd-compat/empty.c b/openbsd-compat/empty.c new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/openbsd-compat/empty.c diff --git a/openbsd-compat/entropy.c b/openbsd-compat/entropy.c new file mode 100644 index 00000000..367d7135 --- /dev/null +++ b/openbsd-compat/entropy.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2001 Damien Miller. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +#include "includes.h" + +#include <sys/types.h> +#include <sys/socket.h> +#ifdef HAVE_SYS_UN_H +# include <sys/un.h> +#endif + +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <errno.h> +#include <signal.h> +#include <string.h> +#include <unistd.h> + +#include <openssl/rand.h> +#include <openssl/crypto.h> +#include <openssl/err.h> + +#include "smtpd/log.h" + +void +seed_rng(void) +{ +#ifndef LIBRESSL_VERSION + u_long mask; + + /* + * OpenSSL version numbers: MNNFFPPS: major minor fix patch status + * We match major, minor, fix and status (not patch) for <1.0.0. + * After that, we acceptable compatible fix versions (so we + * allow 1.0.1 to work with 1.0.0). Going backwards is only allowed + * within a patch series. + */ + mask = SSLeay() >= 0x1000000f ? 0xfff00000L : 0xfffff00fL; + if ((SSLeay() & mask) < (OPENSSL_VERSION_NUMBER & mask)) { + fatalx("OpenSSL version mismatch. Built against %lx, you have %lx\n", + (u_long)OPENSSL_VERSION_NUMBER, SSLeay()); + } +#endif + + if (RAND_status() != 1) + fatal("PRNG is not seeded"); +} diff --git a/openbsd-compat/entropy.h b/openbsd-compat/entropy.h new file mode 100644 index 00000000..496bed66 --- /dev/null +++ b/openbsd-compat/entropy.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 1999-2000 Damien Miller. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* $Id: entropy.h,v 1.6 2011/09/09 01:29:41 dtucker Exp $ */ + +#ifndef _RANDOMS_H +#define _RANDOMS_H + +/* #include "buffer.h" */ + +void seed_rng(void); + +/* void rexec_send_rng_seed(Buffer *); */ +/* void rexec_recv_rng_seed(Buffer *); */ + +#endif /* _RANDOMS_H */ diff --git a/openbsd-compat/err_h/err.h b/openbsd-compat/err_h/err.h new file mode 100644 index 00000000..92aa69f6 --- /dev/null +++ b/openbsd-compat/err_h/err.h @@ -0,0 +1,21 @@ +#ifndef ERR_H +#define ERR_H + +#ifndef LIBCRYPTOCOMPAT_ERR_H +#define LIBCRYPTOCOMPAT_ERR_H + +__attribute__ ((noreturn)) +void err(int, const char *, ...); + +__attribute__ ((noreturn)) +void errx(int, const char *, ...); + +__attribute__ ((noreturn)) +void errc(int, int, const char *, ...); + +void warn(const char *, ...); +void warnx(const char *, ...); + +#endif + +#endif diff --git a/openbsd-compat/errc.c b/openbsd-compat/errc.c new file mode 100644 index 00000000..658a55b4 --- /dev/null +++ b/openbsd-compat/errc.c @@ -0,0 +1,56 @@ +/* $OpenBSD: basename.c,v 1.14 2005/08/08 08:05:33 espie Exp $ */ + +/* + * Copyright (c) 1997, 2004 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/gen/errc.c */ + +#include "includes.h" + +#ifndef HAVE_ERRC + +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +extern char *__progname; + +__attribute__((noreturn)) +static void +_verrc(int eval, int code, const char *fmt, va_list ap) +{ + (void)fprintf(stderr, "%s: ", __progname); + if (fmt != NULL) { + (void)vfprintf(stderr, fmt, ap); + (void)fprintf(stderr, ": "); + } + (void)fprintf(stderr, "%s\n", strerror(code)); + exit(eval); +} + +void +errc(int eval, int code, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + _verrc(eval, code, fmt, ap); + va_end(ap); +} + +#endif diff --git a/openbsd-compat/event_asr_run.c b/openbsd-compat/event_asr_run.c new file mode 100644 index 00000000..aef86154 --- /dev/null +++ b/openbsd-compat/event_asr_run.c @@ -0,0 +1,88 @@ +/* $OpenBSD$ */ +/* + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <netdb.h> + +#include <asr.h> +#include <event.h> +#include <stdlib.h> + +struct event_asr { + struct event ev; + struct asr_query *async; + void (*cb)(struct asr_result *, void *); + void *arg; +}; + +struct event_asr * event_asr_run(struct asr_query *, + void (*)(struct asr_result *, void *), void *); +void event_asr_abort(struct event_asr *); + +static void +event_asr_dispatch(int fd __attribute__((__unused__)), + short ev __attribute__((__unused__)), void *arg) +{ + struct event_asr *eva = arg; + struct asr_result ar; + struct timeval tv; + + event_del(&eva->ev); + + if (asr_run(eva->async, &ar)) { + eva->cb(&ar, eva->arg); + free(eva); + } else { + event_set(&eva->ev, ar.ar_fd, + ar.ar_cond == ASR_WANT_READ ? EV_READ : EV_WRITE, + event_asr_dispatch, eva); + tv.tv_sec = ar.ar_timeout / 1000; + tv.tv_usec = (ar.ar_timeout % 1000) * 1000; + event_add(&eva->ev, &tv); + } +} + +struct event_asr * +event_asr_run(struct asr_query *async, void (*cb)(struct asr_result *, void *), + void *arg) +{ + struct event_asr *eva; + struct timeval tv; + + eva = calloc(1, sizeof *eva); + if (eva == NULL) + return (NULL); + eva->async = async; + eva->cb = cb; + eva->arg = arg; + tv.tv_sec = 0; + tv.tv_usec = 0; + evtimer_set(&eva->ev, event_asr_dispatch, eva); + evtimer_add(&eva->ev, &tv); + return (eva); +} + +void +event_asr_abort(struct event_asr *eva) +{ + asr_abort(eva->async); + event_del(&eva->ev); + free(eva); +} diff --git a/openbsd-compat/explicit_bzero.c b/openbsd-compat/explicit_bzero.c new file mode 100644 index 00000000..d9f4abf5 --- /dev/null +++ b/openbsd-compat/explicit_bzero.c @@ -0,0 +1,15 @@ +/* $OpenBSD: explicit_bzero.c,v 1.4 2015/08/31 02:53:57 guenther Exp $ */ +/* + * Public domain. + * Written by Matthew Dempsky. + */ + +#include "includes.h" + +#include <string.h> + +void +explicit_bzero(void *buf, size_t len) +{ + memset(buf, 0, len); +} diff --git a/openbsd-compat/fgetln.c b/openbsd-compat/fgetln.c new file mode 100644 index 00000000..1c51ff78 --- /dev/null +++ b/openbsd-compat/fgetln.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2015 Joerg Jung <jung@openbsd.org> + * + * 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. + */ + +/* + * portable fgetln() version, NOT reentrant + */ + +#include "includes.h" + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> + +void *reallocarray(void *, size_t, size_t); + +char * +fgetln(FILE *fp, size_t *len) +{ + static char *buf = NULL; + static size_t bufsz = 0; + size_t r = 0; + char *p; + int c, e; + + if (buf == NULL) { + if ((buf = calloc(1, BUFSIZ)) == NULL) + return NULL; + bufsz = BUFSIZ; + } + + while ((c = getc(fp)) != EOF) { + buf[r++] = c; + if (r == bufsz) { + if (!(p = reallocarray(buf, 2, bufsz))) { + e = errno; + free(buf); + errno = e; + buf = NULL, bufsz = 0; + return NULL; + } + buf = p, bufsz = 2 * bufsz; + } + if (c == '\n') + break; + } + return (*len = r) ? buf : NULL; +} + diff --git a/openbsd-compat/fmt_scaled.c b/openbsd-compat/fmt_scaled.c new file mode 100644 index 00000000..edd682a4 --- /dev/null +++ b/openbsd-compat/fmt_scaled.c @@ -0,0 +1,274 @@ +/* $OpenBSD: fmt_scaled.c,v 1.9 2007/03/20 03:42:52 tedu Exp $ */ + +/* + * Copyright (c) 2001, 2002, 2003 Ian F. Darwin. 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. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* OPENBSD ORIGINAL: lib/libutil/fmt_scaled.c */ + +/* + * fmt_scaled: Format numbers scaled for human comprehension + * scan_scaled: Scan numbers in this format. + * + * "Human-readable" output uses 4 digits max, and puts a unit suffix at + * the end. Makes output compact and easy-to-read esp. on huge disks. + * Formatting code was originally in OpenBSD "df", converted to library routine. + * Scanning code written for OpenBSD libutil. + */ + +#include "includes.h" + +#ifndef HAVE_FMT_SCALED + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <ctype.h> +#include <limits.h> + +typedef enum { + NONE = 0, KILO = 1, MEGA = 2, GIGA = 3, TERA = 4, PETA = 5, EXA = 6 +} unit_type; + +/* These three arrays MUST be in sync! XXX make a struct */ +static unit_type units[] = { NONE, KILO, MEGA, GIGA, TERA, PETA, EXA }; +static char scale_chars[] = "BKMGTPE"; +static long long scale_factors[] = { + 1LL, + 1024LL, + 1024LL*1024, + 1024LL*1024*1024, + 1024LL*1024*1024*1024, + 1024LL*1024*1024*1024*1024, + 1024LL*1024*1024*1024*1024*1024, +}; +#define SCALE_LENGTH (sizeof(units)/sizeof(units[0])) + +#define MAX_DIGITS (SCALE_LENGTH * 3) /* XXX strlen(sprintf("%lld", -1)? */ + +/** Convert the given input string "scaled" into numeric in "result". + * Return 0 on success, -1 and errno set on error. + */ +int +scan_scaled(char *scaled, long long *result) +{ + char *p = scaled; + int sign = 0; + unsigned int i, ndigits = 0, fract_digits = 0; + long long scale_fact = 1, whole = 0, fpart = 0; + + /* Skip leading whitespace */ + while (isascii(*p) && isspace(*p)) + ++p; + + /* Then at most one leading + or - */ + while (*p == '-' || *p == '+') { + if (*p == '-') { + if (sign) { + errno = EINVAL; + return -1; + } + sign = -1; + ++p; + } else if (*p == '+') { + if (sign) { + errno = EINVAL; + return -1; + } + sign = +1; + ++p; + } + } + + /* Main loop: Scan digits, find decimal point, if present. + * We don't allow exponentials, so no scientific notation + * (but note that E for Exa might look like e to some!). + * Advance 'p' to end, to get scale factor. + */ + for (; isascii(*p) && (isdigit(*p) || *p=='.'); ++p) { + if (*p == '.') { + if (fract_digits > 0) { /* oops, more than one '.' */ + errno = EINVAL; + return -1; + } + fract_digits = 1; + continue; + } + + i = (*p) - '0'; /* whew! finally a digit we can use */ + if (fract_digits > 0) { + if (fract_digits >= MAX_DIGITS-1) + /* ignore extra fractional digits */ + continue; + fract_digits++; /* for later scaling */ + fpart *= 10; + fpart += i; + } else { /* normal digit */ + if (++ndigits >= MAX_DIGITS) { + errno = ERANGE; + return -1; + } + whole *= 10; + whole += i; + } + } + + if (sign) { + whole *= sign; + fpart *= sign; + } + + /* If no scale factor given, we're done. fraction is discarded. */ + if (!*p) { + *result = whole; + return 0; + } + + /* Validate scale factor, and scale whole and fraction by it. */ + for (i = 0; i < SCALE_LENGTH; i++) { + + /** Are we there yet? */ + if (*p == scale_chars[i] || + *p == tolower(scale_chars[i])) { + + /* If it ends with alphanumerics after the scale char, bad. */ + if (isalnum(*(p+1))) { + errno = EINVAL; + return -1; + } + scale_fact = scale_factors[i]; + + /* scale whole part */ + whole *= scale_fact; + + /* truncate fpart so it does't overflow. + * then scale fractional part. + */ + while (fpart >= LLONG_MAX / scale_fact) { + fpart /= 10; + fract_digits--; + } + fpart *= scale_fact; + if (fract_digits > 0) { + for (i = 0; i < fract_digits -1; i++) + fpart /= 10; + } + whole += fpart; + *result = whole; + return 0; + } + } + errno = ERANGE; + return -1; +} + +/* Format the given "number" into human-readable form in "result". + * Result must point to an allocated buffer of length FMT_SCALED_STRSIZE. + * Return 0 on success, -1 and errno set if error. + */ +int +fmt_scaled(long long number, char *result) +{ + long long abval, fract = 0; + unsigned int i; + unit_type unit = NONE; + + abval = (number < 0LL) ? -number : number; /* no long long_abs yet */ + + /* Not every negative long long has a positive representation. + * Also check for numbers that are just too darned big to format + */ + if (abval < 0 || abval / 1024 >= scale_factors[SCALE_LENGTH-1]) { + errno = ERANGE; + return -1; + } + + /* scale whole part; get unscaled fraction */ + for (i = 0; i < SCALE_LENGTH; i++) { + if (abval/1024 < scale_factors[i]) { + unit = units[i]; + fract = (i == 0) ? 0 : abval % scale_factors[i]; + number /= scale_factors[i]; + if (i > 0) + fract /= scale_factors[i - 1]; + break; + } + } + + fract = (10 * fract + 512) / 1024; + /* if the result would be >= 10, round main number */ + if (fract == 10) { + if (number >= 0) + number++; + else + number--; + fract = 0; + } + + if (number == 0) + strlcpy(result, "0B", FMT_SCALED_STRSIZE); + else if (unit == NONE || number >= 100 || number <= -100) { + if (fract >= 5) { + if (number >= 0) + number++; + else + number--; + } + (void)snprintf(result, FMT_SCALED_STRSIZE, "%lld%c", + number, scale_chars[unit]); + } else + (void)snprintf(result, FMT_SCALED_STRSIZE, "%lld.%1lld%c", + number, fract, scale_chars[unit]); + + return 0; +} + +#ifdef MAIN +/* + * This is the original version of the program in the man page. + * Copy-and-paste whatever you need from it. + */ +int +main(int argc, char **argv) +{ + char *cinput = "1.5K", buf[FMT_SCALED_STRSIZE]; + long long ninput = 10483892, result; + + if (scan_scaled(cinput, &result) == 0) + printf("\"%s\" -> %lld\n", cinput, result); + else + perror(cinput); + + if (fmt_scaled(ninput, buf) == 0) + printf("%lld -> \"%s\"\n", ninput, buf); + else + fprintf(stderr, "%lld invalid (%s)\n", ninput, strerror(errno)); + + return 0; +} +#endif + +#endif /* HAVE_FMT_SCALED */ diff --git a/openbsd-compat/fparseln.c b/openbsd-compat/fparseln.c new file mode 100644 index 00000000..dfa9093c --- /dev/null +++ b/openbsd-compat/fparseln.c @@ -0,0 +1,179 @@ +/* $OpenBSD: fparseln.c,v 1.6 2005/08/02 21:46:23 espie Exp $ */ +/* $NetBSD: fparseln.c,v 1.7 1999/07/02 15:49:12 simonb Exp $ */ + +/* + * Copyright (c) 1997 Christos Zoulas. 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christos Zoulas. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* OPENBSD ORIGINAL: lib/libutil/fparseln.c */ + +#include "includes.h" + +#ifdef HAVE_SYS_CDEFS +#include <sys/cdefs.h> +#endif + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +static int isescaped(const char *, const char *, int); + +/* isescaped(): + * Return true if the character in *p that belongs to a string + * that starts in *sp, is escaped by the escape character esc. + */ +static int +isescaped(const char *sp, const char *p, int esc) +{ + const char *cp; + size_t ne; + + /* No escape character */ + if (esc == '\0') + return 1; + + /* Count the number of escape characters that precede ours */ + for (ne = 0, cp = p; --cp >= sp && *cp == esc; ne++) + continue; + + /* Return true if odd number of escape characters */ + return (ne & 1) != 0; +} + + +/* fparseln(): + * Read a line from a file parsing continuations ending in \ + * and eliminating trailing newlines, or comments starting with + * the comment char. + */ +char * +fparseln(FILE *fp, size_t *size, size_t *lineno, const char str[3], + int flags) +{ + static const char dstr[3] = { '\\', '\\', '#' }; + char *buf = NULL, *ptr, *cp, esc, con, nl, com; + size_t s, len = 0; + int cnt = 1; + + if (str == NULL) + str = dstr; + + esc = str[0]; + con = str[1]; + com = str[2]; + + /* + * XXX: it would be cool to be able to specify the newline character, + * but unfortunately, fgetln does not let us + */ + nl = '\n'; + + while (cnt) { + cnt = 0; + + if (lineno) + (*lineno)++; + + if ((ptr = fgetln(fp, &s)) == NULL) + break; + + if (s && com) { /* Check and eliminate comments */ + for (cp = ptr; cp < ptr + s; cp++) + if (*cp == com && !isescaped(ptr, cp, esc)) { + s = cp - ptr; + cnt = s == 0 && buf == NULL; + break; + } + } + + if (s && nl) { /* Check and eliminate newlines */ + cp = &ptr[s - 1]; + + if (*cp == nl) + s--; /* forget newline */ + } + + if (s && con) { /* Check and eliminate continuations */ + cp = &ptr[s - 1]; + + if (*cp == con && !isescaped(ptr, cp, esc)) { + s--; /* forget escape */ + cnt = 1; + } + } + + if (s == 0 && buf != NULL) + continue; + + if ((cp = realloc(buf, len + s + 1)) == NULL) { + free(buf); + return NULL; + } + buf = cp; + + (void) memcpy(buf + len, ptr, s); + len += s; + buf[len] = '\0'; + } + + if ((flags & FPARSELN_UNESCALL) != 0 && esc && buf != NULL && + strchr(buf, esc) != NULL) { + ptr = cp = buf; + while (cp[0] != '\0') { + int skipesc; + + while (cp[0] != '\0' && cp[0] != esc) + *ptr++ = *cp++; + if (cp[0] == '\0' || cp[1] == '\0') + break; + + skipesc = 0; + if (cp[1] == com) + skipesc += (flags & FPARSELN_UNESCCOMM); + if (cp[1] == con) + skipesc += (flags & FPARSELN_UNESCCONT); + if (cp[1] == esc) + skipesc += (flags & FPARSELN_UNESCESC); + if (cp[1] != com && cp[1] != con && cp[1] != esc) + skipesc = (flags & FPARSELN_UNESCREST); + + if (skipesc) + cp++; + else + *ptr++ = *cp++; + *ptr++ = *cp++; + } + *ptr = '\0'; + len = strlen(buf); + } + + if (size) + *size = len; + return buf; +} diff --git a/openbsd-compat/freezero.c b/openbsd-compat/freezero.c new file mode 100644 index 00000000..da20d132 --- /dev/null +++ b/openbsd-compat/freezero.c @@ -0,0 +1,34 @@ +/* $OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert Exp $ */ + +/* + * Copyright (c) 2004 Ted Unangst and Todd Miller + * All rights reserved. + * + * 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. + */ + +/* OPENBSD ORIGINAL: lib/libc/stdlib/malloc.c */ + +#include "includes.h" + +#include <stdlib.h> +#include <strings.h> + +void +freezero(void *ptr, size_t sz) +{ + if (ptr == NULL) + return; + explicit_bzero(ptr, sz); + free(ptr); +} diff --git a/openbsd-compat/getopt.c b/openbsd-compat/getopt.c new file mode 100644 index 00000000..5450e43d --- /dev/null +++ b/openbsd-compat/getopt.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) 1987, 1993, 1994 + * 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. + */ + +/* OPENBSD ORIGINAL: lib/libc/stdlib/getopt.c */ + +#include "includes.h" +#if !defined(HAVE_GETOPT) || !defined(HAVE_GETOPT_OPTRESET) + +#if defined(LIBC_SCCS) && !defined(lint) +static char *rcsid = "$OpenBSD: getopt.c,v 1.5 2003/06/02 20:18:37 millert Exp $"; +#endif /* LIBC_SCCS and not lint */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +int BSDopterr = 1, /* if error message should be printed */ + BSDoptind = 1, /* index into parent argv vector */ + BSDoptopt, /* character checked for validity */ + BSDoptreset; /* reset getopt */ +char *BSDoptarg; /* argument associated with option */ + +#define BADCH (int)'?' +#define BADARG (int)':' +#define EMSG "" + +/* + * getopt -- + * Parse argc/argv argument vector. + */ +int +BSDgetopt(nargc, nargv, ostr) + int nargc; + char * const *nargv; + const char *ostr; +{ + extern char *__progname; + static char *place = EMSG; /* option letter processing */ + char *oli; /* option letter list index */ + + if (ostr == NULL) + return (-1); + + if (BSDoptreset || !*place) { /* update scanning pointer */ + BSDoptreset = 0; + if (BSDoptind >= nargc || *(place = nargv[BSDoptind]) != '-') { + place = EMSG; + return (-1); + } + if (place[1] && *++place == '-') { /* found "--" */ + ++BSDoptind; + place = EMSG; + return (-1); + } + } /* option letter okay? */ + if ((BSDoptopt = (int)*place++) == (int)':' || + !(oli = strchr(ostr, BSDoptopt))) { + /* + * if the user didn't specify '-' as an option, + * assume it means -1. + */ + if (BSDoptopt == (int)'-') + return (-1); + if (!*place) + ++BSDoptind; + if (BSDopterr && *ostr != ':') + (void)fprintf(stderr, + "%s: illegal option -- %c\n", __progname, BSDoptopt); + return (BADCH); + } + if (*++oli != ':') { /* don't need argument */ + BSDoptarg = NULL; + if (!*place) + ++BSDoptind; + } + else { /* need an argument */ + if (*place) /* no white space */ + BSDoptarg = place; + else if (nargc <= ++BSDoptind) { /* no arg */ + place = EMSG; + if (*ostr == ':') + return (BADARG); + if (BSDopterr) + (void)fprintf(stderr, + "%s: option requires an argument -- %c\n", + __progname, BSDoptopt); + return (BADCH); + } + else /* white space */ + BSDoptarg = nargv[BSDoptind]; + place = EMSG; + ++BSDoptind; + } + return (BSDoptopt); /* dump back option letter */ +} + +#endif /* !defined(HAVE_GETOPT) || !defined(HAVE_OPTRESET) */ diff --git a/openbsd-compat/getpeereid.c b/openbsd-compat/getpeereid.c new file mode 100644 index 00000000..c8ce808f --- /dev/null +++ b/openbsd-compat/getpeereid.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2002,2004 Damien Miller <djm@mindrot.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/types.h> +#include <sys/socket.h> + +#include <unistd.h> + +#if defined(SO_PEERCRED) +int +getpeereid(int s, uid_t *euid, gid_t *gid) +{ + struct ucred cred; + socklen_t len = sizeof(cred); + + if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cred, &len) < 0) + return (-1); + *euid = cred.uid; + *gid = cred.gid; + + return (0); +} +#elif defined(HAVE_GETPEERUCRED) + +#ifdef HAVE_UCRED_H +# include <ucred.h> +#endif + +int +getpeereid(int s, uid_t *euid, gid_t *gid) +{ + ucred_t *ucred = NULL; + + if (getpeerucred(s, &ucred) == -1) + return (-1); + if ((*euid = ucred_geteuid(ucred)) == -1) + return (-1); + if ((*gid = ucred_getrgid(ucred)) == -1) + return (-1); + + ucred_free(ucred); + + return (0); +} +#else +int +getpeereid(int s, uid_t *euid, gid_t *gid) +{ + *euid = geteuid(); + *gid = getgid(); + + return (0); +} +#endif /* defined(SO_PEERCRED) */ diff --git a/openbsd-compat/imsg-buffer.c b/openbsd-compat/imsg-buffer.c new file mode 100644 index 00000000..e3762092 --- /dev/null +++ b/openbsd-compat/imsg-buffer.c @@ -0,0 +1,310 @@ +/* $OpenBSD: imsg-buffer.c,v 1.3 2013/11/13 20:40:24 benno Exp $ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/uio.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#ifndef HAVE_EXPLICIT_BZERO +#include <strings.h> +#endif +#include <unistd.h> + +#include "imsg.h" + +int ibuf_realloc(struct ibuf *, size_t); +void ibuf_enqueue(struct msgbuf *, struct ibuf *); +void ibuf_dequeue(struct msgbuf *, struct ibuf *); + +struct ibuf * +ibuf_open(size_t len) +{ + struct ibuf *buf; + + if ((buf = calloc(1, sizeof(struct ibuf))) == NULL) + return (NULL); + if ((buf->buf = malloc(len)) == NULL) { + free(buf); + return (NULL); + } + buf->size = buf->max = len; + buf->fd = -1; + + return (buf); +} + +struct ibuf * +ibuf_dynamic(size_t len, size_t max) +{ + struct ibuf *buf; + + if (max < len) + return (NULL); + + if ((buf = ibuf_open(len)) == NULL) + return (NULL); + + if (max > 0) + buf->max = max; + + return (buf); +} + +int +ibuf_realloc(struct ibuf *buf, size_t len) +{ + u_char *b; + + /* on static buffers max is eq size and so the following fails */ + if (buf->wpos + len > buf->max) { + errno = ENOMEM; + return (-1); + } + + b = realloc(buf->buf, buf->wpos + len); + if (b == NULL) + return (-1); + buf->buf = b; + buf->size = buf->wpos + len; + + return (0); +} + +int +ibuf_add(struct ibuf *buf, const void *data, size_t len) +{ + if (buf->wpos + len > buf->size) + if (ibuf_realloc(buf, len) == -1) + return (-1); + + memcpy(buf->buf + buf->wpos, data, len); + buf->wpos += len; + return (0); +} + +void * +ibuf_reserve(struct ibuf *buf, size_t len) +{ + void *b; + + if (buf->wpos + len > buf->size) + if (ibuf_realloc(buf, len) == -1) + return (NULL); + + b = buf->buf + buf->wpos; + buf->wpos += len; + return (b); +} + +void * +ibuf_seek(struct ibuf *buf, size_t pos, size_t len) +{ + /* only allowed to seek in already written parts */ + if (pos + len > buf->wpos) + return (NULL); + + return (buf->buf + pos); +} + +size_t +ibuf_size(struct ibuf *buf) +{ + return (buf->wpos); +} + +size_t +ibuf_left(struct ibuf *buf) +{ + return (buf->max - buf->wpos); +} + +void +ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf) +{ + ibuf_enqueue(msgbuf, buf); +} + +int +ibuf_write(struct msgbuf *msgbuf) +{ + struct iovec iov[IOV_MAX]; + struct ibuf *buf; + unsigned int i = 0; + ssize_t n; + + bzero(&iov, sizeof(iov)); + TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { + if (i >= IOV_MAX) + break; + iov[i].iov_base = buf->buf + buf->rpos; + iov[i].iov_len = buf->wpos - buf->rpos; + i++; + } + +again: + if ((n = writev(msgbuf->fd, iov, i)) == -1) { + if (errno == EINTR) + goto again; + if (errno == ENOBUFS) + errno = EAGAIN; + return (-1); + } + + if (n == 0) { /* connection closed */ + errno = 0; + return (0); + } + + msgbuf_drain(msgbuf, n); + + return (1); +} + +void +ibuf_free(struct ibuf *buf) +{ + free(buf->buf); + free(buf); +} + +void +msgbuf_init(struct msgbuf *msgbuf) +{ + msgbuf->queued = 0; + msgbuf->fd = -1; + TAILQ_INIT(&msgbuf->bufs); +} + +void +msgbuf_drain(struct msgbuf *msgbuf, size_t n) +{ + struct ibuf *buf, *next; + + for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0; + buf = next) { + next = TAILQ_NEXT(buf, entry); + if (buf->rpos + n >= buf->wpos) { + n -= buf->wpos - buf->rpos; + ibuf_dequeue(msgbuf, buf); + } else { + buf->rpos += n; + n = 0; + } + } +} + +void +msgbuf_clear(struct msgbuf *msgbuf) +{ + struct ibuf *buf; + + while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL) + ibuf_dequeue(msgbuf, buf); +} + +int +msgbuf_write(struct msgbuf *msgbuf) +{ + struct iovec iov[IOV_MAX]; + struct ibuf *buf; + unsigned int i = 0; + ssize_t n; + struct msghdr msg; + struct cmsghdr *cmsg; + union { + struct cmsghdr hdr; + char buf[CMSG_SPACE(sizeof(int))]; + } cmsgbuf; + + bzero(&iov, sizeof(iov)); + bzero(&msg, sizeof(msg)); + TAILQ_FOREACH(buf, &msgbuf->bufs, entry) { + if (i >= IOV_MAX) + break; + iov[i].iov_base = buf->buf + buf->rpos; + iov[i].iov_len = buf->wpos - buf->rpos; + i++; + if (buf->fd != -1) + break; + } + + msg.msg_iov = iov; + msg.msg_iovlen = i; + + if (buf != NULL && buf->fd != -1) { + msg.msg_control = (caddr_t)&cmsgbuf.buf; + msg.msg_controllen = sizeof(cmsgbuf.buf); + cmsg = CMSG_FIRSTHDR(&msg); + cmsg->cmsg_len = CMSG_LEN(sizeof(int)); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + *(int *)CMSG_DATA(cmsg) = buf->fd; + } + +again: + if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) { + if (errno == EINTR) + goto again; + if (errno == ENOBUFS) + errno = EAGAIN; + return (-1); + } + + if (n == 0) { /* connection closed */ + errno = 0; + return (0); + } + + /* + * assumption: fd got sent if sendmsg sent anything + * this works because fds are passed one at a time + */ + if (buf != NULL && buf->fd != -1) { + close(buf->fd); + buf->fd = -1; + } + + msgbuf_drain(msgbuf, n); + + return (1); +} + +void +ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf) +{ + TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry); + msgbuf->queued++; +} + +void +ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf) +{ + TAILQ_REMOVE(&msgbuf->bufs, buf, entry); + + if (buf->fd != -1) + close(buf->fd); + + msgbuf->queued--; + ibuf_free(buf); +} diff --git a/openbsd-compat/imsg.c b/openbsd-compat/imsg.c new file mode 100644 index 00000000..a5900a05 --- /dev/null +++ b/openbsd-compat/imsg.c @@ -0,0 +1,330 @@ +/* $OpenBSD: imsg.c,v 1.5 2013/12/26 17:32:33 eric Exp $ */ + +/* + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/param.h> +#include <sys/queue.h> +#include <sys/socket.h> +#include <sys/uio.h> + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#ifndef HAVE_EXPLICIT_BZERO +#include <strings.h> +#endif +#include <unistd.h> + +#include "imsg.h" + +int imsg_fd_overhead = 0; + +int imsg_get_fd(struct imsgbuf *); + +int +available_fds(unsigned int n) +{ + unsigned int i; + int ret, fds[256]; + + if (n > (sizeof(fds)/sizeof(fds[0]))) + return (1); + + ret = 0; + for (i = 0; i < n; i++) { + fds[i] = -1; + if ((fds[i] = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + ret = 1; + break; + } + } + + for (i = 0; i < n && fds[i] >= 0; i++) + close(fds[i]); + + return (ret); +} + +void +imsg_init(struct imsgbuf *ibuf, int fd) +{ + msgbuf_init(&ibuf->w); + bzero(&ibuf->r, sizeof(ibuf->r)); + ibuf->fd = fd; + ibuf->w.fd = fd; + ibuf->pid = getpid(); + TAILQ_INIT(&ibuf->fds); +} + +ssize_t +imsg_read(struct imsgbuf *ibuf) +{ + struct msghdr msg; + struct cmsghdr *cmsg; + union { + struct cmsghdr hdr; + char buf[CMSG_SPACE(sizeof(int) * 1)]; + } cmsgbuf; + struct iovec iov; + ssize_t n = -1; + int fd; + struct imsg_fd *ifd; + + bzero(&msg, sizeof(msg)); + + iov.iov_base = ibuf->r.buf + ibuf->r.wpos; + iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = &cmsgbuf.buf; + msg.msg_controllen = sizeof(cmsgbuf.buf); + + if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL) + return (-1); + +again: + if (available_fds(imsg_fd_overhead + + (CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int))) { + errno = EAGAIN; + free(ifd); + return (-1); + } + + if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) { + if (errno == EMSGSIZE) + goto fail; + if (errno != EINTR && errno != EAGAIN) + goto fail; + goto again; + } + + ibuf->r.wpos += n; + + for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; + cmsg = CMSG_NXTHDR(&msg, cmsg)) { + if (cmsg->cmsg_level == SOL_SOCKET && + cmsg->cmsg_type == SCM_RIGHTS) { + int i; + int j; + + /* + * We only accept one file descriptor. Due to C + * padding rules, our control buffer might contain + * more than one fd, and we must close them. + */ + j = ((char *)cmsg + cmsg->cmsg_len - + (char *)CMSG_DATA(cmsg)) / sizeof(int); + for (i = 0; i < j; i++) { + fd = ((int *)CMSG_DATA(cmsg))[i]; + if (ifd != NULL) { + ifd->fd = fd; + TAILQ_INSERT_TAIL(&ibuf->fds, ifd, + entry); + ifd = NULL; + } else + close(fd); + } + } + /* we do not handle other ctl data level */ + } + +fail: + if (ifd) + free(ifd); + return (n); +} + +ssize_t +imsg_get(struct imsgbuf *ibuf, struct imsg *imsg) +{ + size_t av, left, datalen; + + av = ibuf->r.wpos; + + if (IMSG_HEADER_SIZE > av) + return (0); + + memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr)); + if (imsg->hdr.len < IMSG_HEADER_SIZE || + imsg->hdr.len > MAX_IMSGSIZE) { + errno = ERANGE; + return (-1); + } + if (imsg->hdr.len > av) + return (0); + datalen = imsg->hdr.len - IMSG_HEADER_SIZE; + ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE; + if ((imsg->data = malloc(datalen)) == NULL) + return (-1); + + if (imsg->hdr.flags & IMSGF_HASFD) + imsg->fd = imsg_get_fd(ibuf); + else + imsg->fd = -1; + + memcpy(imsg->data, ibuf->r.rptr, datalen); + + if (imsg->hdr.len < av) { + left = av - imsg->hdr.len; + memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left); + ibuf->r.wpos = left; + } else + ibuf->r.wpos = 0; + + return (datalen + IMSG_HEADER_SIZE); +} + +int +imsg_compose(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, + pid_t pid, int fd, const void *data, uint16_t datalen) +{ + struct ibuf *wbuf; + + if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL) + return (-1); + + if (imsg_add(wbuf, data, datalen) == -1) + return (-1); + + wbuf->fd = fd; + + imsg_close(ibuf, wbuf); + + return (1); +} + +int +imsg_composev(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, + pid_t pid, int fd, const struct iovec *iov, int iovcnt) +{ + struct ibuf *wbuf; + int i, datalen = 0; + + for (i = 0; i < iovcnt; i++) + datalen += iov[i].iov_len; + + if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL) + return (-1); + + for (i = 0; i < iovcnt; i++) + if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1) + return (-1); + + wbuf->fd = fd; + + imsg_close(ibuf, wbuf); + + return (1); +} + +/* ARGSUSED */ +struct ibuf * +imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, + pid_t pid, uint16_t datalen) +{ + struct ibuf *wbuf; + struct imsg_hdr hdr; + + datalen += IMSG_HEADER_SIZE; + if (datalen > MAX_IMSGSIZE) { + errno = ERANGE; + return (NULL); + } + + hdr.type = type; + hdr.flags = 0; + hdr.peerid = peerid; + if ((hdr.pid = pid) == 0) + hdr.pid = ibuf->pid; + if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) { + return (NULL); + } + if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1) + return (NULL); + + return (wbuf); +} + +int +imsg_add(struct ibuf *msg, const void *data, uint16_t datalen) +{ + if (datalen) + if (ibuf_add(msg, data, datalen) == -1) { + ibuf_free(msg); + return (-1); + } + return (datalen); +} + +void +imsg_close(struct imsgbuf *ibuf, struct ibuf *msg) +{ + struct imsg_hdr *hdr; + + hdr = (struct imsg_hdr *)msg->buf; + + hdr->flags &= ~IMSGF_HASFD; + if (msg->fd != -1) + hdr->flags |= IMSGF_HASFD; + + hdr->len = (uint16_t)msg->wpos; + + ibuf_close(&ibuf->w, msg); +} + +void +imsg_free(struct imsg *imsg) +{ + free(imsg->data); +} + +int +imsg_get_fd(struct imsgbuf *ibuf) +{ + int fd; + struct imsg_fd *ifd; + + if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL) + return (-1); + + fd = ifd->fd; + TAILQ_REMOVE(&ibuf->fds, ifd, entry); + free(ifd); + + return (fd); +} + +int +imsg_flush(struct imsgbuf *ibuf) +{ + while (ibuf->w.queued) + if (msgbuf_write(&ibuf->w) <= 0) + return (-1); + return (0); +} + +void +imsg_clear(struct imsgbuf *ibuf) +{ + int fd; + + msgbuf_clear(&ibuf->w); + while ((fd = imsg_get_fd(ibuf)) != -1) + close(fd); +} diff --git a/openbsd-compat/imsg.h b/openbsd-compat/imsg.h new file mode 100644 index 00000000..3757c8b9 --- /dev/null +++ b/openbsd-compat/imsg.h @@ -0,0 +1,115 @@ +/* $OpenBSD: imsg.h,v 1.3 2013/12/26 17:32:33 eric Exp $ */ + +/* + * Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org> + * Copyright (c) 2006, 2007, 2008 Reyk Floeter <reyk@openbsd.org> + * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> + * + * 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 _IMSG_H_ +#define _IMSG_H_ + +#define IBUF_READ_SIZE 65535 +#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr) +#define MAX_IMSGSIZE 16384 + +#include "defines.h" + +struct ibuf { + TAILQ_ENTRY(ibuf) entry; + u_char *buf; + size_t size; + size_t max; + size_t wpos; + size_t rpos; + int fd; +}; + +struct msgbuf { + TAILQ_HEAD(, ibuf) bufs; + uint32_t queued; + int fd; +}; + +struct ibuf_read { + u_char buf[IBUF_READ_SIZE]; + u_char *rptr; + size_t wpos; +}; + +struct imsg_fd { + TAILQ_ENTRY(imsg_fd) entry; + int fd; +}; + +struct imsgbuf { + TAILQ_HEAD(, imsg_fd) fds; + struct ibuf_read r; + struct msgbuf w; + int fd; + pid_t pid; +}; + +#define IMSGF_HASFD 1 + +struct imsg_hdr { + uint32_t type; + uint16_t len; + uint16_t flags; + uint32_t peerid; + uint32_t pid; +}; + +struct imsg { + struct imsg_hdr hdr; + int fd; + void *data; +}; + + +/* buffer.c */ +struct ibuf *ibuf_open(size_t); +struct ibuf *ibuf_dynamic(size_t, size_t); +int ibuf_add(struct ibuf *, const void *, size_t); +void *ibuf_reserve(struct ibuf *, size_t); +void *ibuf_seek(struct ibuf *, size_t, size_t); +size_t ibuf_size(struct ibuf *); +size_t ibuf_left(struct ibuf *); +void ibuf_close(struct msgbuf *, struct ibuf *); +int ibuf_write(struct msgbuf *); +void ibuf_free(struct ibuf *); +void msgbuf_init(struct msgbuf *); +void msgbuf_clear(struct msgbuf *); +int msgbuf_write(struct msgbuf *); +void msgbuf_drain(struct msgbuf *, size_t); + +/* imsg.c */ +int available_fds(unsigned int); +void imsg_init(struct imsgbuf *, int); +ssize_t imsg_read(struct imsgbuf *); +ssize_t imsg_get(struct imsgbuf *, struct imsg *); +int imsg_compose(struct imsgbuf *, uint32_t, uint32_t, pid_t, + int, const void *, uint16_t); +int imsg_composev(struct imsgbuf *, uint32_t, uint32_t, pid_t, + int, const struct iovec *, int); +struct ibuf *imsg_create(struct imsgbuf *, uint32_t, uint32_t, pid_t, + uint16_t); +int imsg_add(struct ibuf *, const void *, uint16_t); +void imsg_close(struct imsgbuf *, struct ibuf *); +void imsg_free(struct imsg *); +int imsg_flush(struct imsgbuf *); +void imsg_clear(struct imsgbuf *); + +#endif diff --git a/openbsd-compat/includes.h b/openbsd-compat/includes.h new file mode 100644 index 00000000..cd044a20 --- /dev/null +++ b/openbsd-compat/includes.h @@ -0,0 +1,75 @@ +/* $OpenBSD: includes.h,v 1.54 2006/07/22 20:48:23 stevesk Exp $ */ + +/* + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * This file includes most of the needed system headers. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#ifndef INCLUDES_H +#define INCLUDES_H + +#include "config.h" + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE /* activate extra prototypes for glibc */ +#endif + +#include <sys/types.h> +#include <sys/socket.h> /* For CMSG_* */ + +#ifdef HAVE_LIMITS_H +# include <limits.h> /* For PATH_MAX */ +#endif +#ifdef HAVE_BSTRING_H +# include <bstring.h> +#endif + +#ifdef HAVE_ENDIAN_H +# include <endian.h> +#endif +#ifdef HAVE_MAILLOCK_H +# include <maillock.h> /* For _PATH_MAILDIR */ +#endif +#ifdef HAVE_PATHS_H +# include <paths.h> +#endif + +#ifdef HAVE_RPC_TYPES_H +# include <rpc/types.h> /* For INADDR_LOOPBACK */ +#endif +#ifdef USE_PAM +#if defined(HAVE_SECURITY_PAM_APPL_H) +# include <security/pam_appl.h> +#elif defined (HAVE_PAM_PAM_APPL_H) +# include <pam/pam_appl.h> +#endif +#endif +#include <errno.h> + +/* chl */ +#ifdef HAVE_NETDB_H +# include <netdb.h> +#endif +/* end of chl*/ + +#if !defined(NETDB_INTERNAL) +# define NETDB_INTERNAL (-1) +#endif + +#include <openssl/opensslv.h> /* For OPENSSL_VERSION_NUMBER */ + +#include "defines.h" + +#include "openbsd-compat.h" + +#include "entropy.h" + +#endif /* INCLUDES_H */ diff --git a/openbsd-compat/inet_net_pton.c b/openbsd-compat/inet_net_pton.c new file mode 100644 index 00000000..b65cb76f --- /dev/null +++ b/openbsd-compat/inet_net_pton.c @@ -0,0 +1,236 @@ +/* $OpenBSD: inet_net_pton.c,v 1.8 2013/11/25 18:23:51 deraadt Exp $ */ + +/* + * Copyright (c) 2012 by Gilles Chehade <gilles@openbsd.org> + * Copyright (c) 1996 by Internet Software Consortium. + * + * 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 INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +#include "includes.h" +#ifndef HAVE_INET_NET_PTON + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +static int inet_net_pton_ipv4(const char *, u_char *, size_t); +static int inet_net_pton_ipv6(const char *, u_char *, size_t); + +/* + * static int + * inet_net_pton(af, src, dst, size) + * convert network number from presentation to network format. + * accepts hex octets, hex strings, decimal octets, and /CIDR. + * "size" is in bytes and describes "dst". + * return: + * number of bits, either imputed classfully or specified with /CIDR, + * or -1 if some failure occurred (check errno). ENOENT means it was + * not a valid network specification. + * author: + * Paul Vixie (ISC), June 1996 + */ +int +inet_net_pton(int af, const char *src, void *dst, size_t size) +{ + switch (af) { + case AF_INET: + return (inet_net_pton_ipv4(src, dst, size)); + case AF_INET6: + return (inet_net_pton_ipv6(src, dst, size)); + default: + errno = EAFNOSUPPORT; + return (-1); + } +} + +/* + * static int + * inet_net_pton_ipv4(src, dst, size) + * convert IPv4 network number from presentation to network format. + * accepts hex octets, hex strings, decimal octets, and /CIDR. + * "size" is in bytes and describes "dst". + * return: + * number of bits, either imputed classfully or specified with /CIDR, + * or -1 if some failure occurred (check errno). ENOENT means it was + * not an IPv4 network specification. + * note: + * network byte order assumed. this means 192.5.5.240/28 has + * 0x11110000 in its fourth octet. + * author: + * Paul Vixie (ISC), June 1996 + */ +static int +inet_net_pton_ipv4(const char *src, u_char *dst, size_t size) +{ + static const char + xdigits[] = "0123456789abcdef", + digits[] = "0123456789"; + int n, ch, tmp, dirty, bits; + const u_char *odst = dst; + + ch = (unsigned char)*src++; + if (ch == '0' && (src[0] == 'x' || src[0] == 'X') + && isascii((unsigned char)src[1]) && isxdigit((unsigned char)src[1])) { + /* Hexadecimal: Eat nybble string. */ + if (size <= 0) + goto emsgsize; + *dst = 0, dirty = 0; + src++; /* skip x or X. */ + while ((ch = (unsigned char)*src++) != '\0' && + isascii(ch) && isxdigit(ch)) { + if (isupper(ch)) + ch = tolower(ch); + n = strchr(xdigits, ch) - xdigits; + assert(n >= 0 && n <= 15); + *dst |= n; + if (!dirty++) + *dst <<= 4; + else if (size-- > 0) + *++dst = 0, dirty = 0; + else + goto emsgsize; + } + if (dirty) + size--; + } else if (isascii(ch) && isdigit(ch)) { + /* Decimal: eat dotted digit string. */ + for (;;) { + tmp = 0; + do { + n = strchr(digits, ch) - digits; + assert(n >= 0 && n <= 9); + tmp *= 10; + tmp += n; + if (tmp > 255) + goto enoent; + } while ((ch = (unsigned char)*src++) != '\0' && + isascii(ch) && isdigit(ch)); + if (size-- <= 0) + goto emsgsize; + *dst++ = (u_char) tmp; + if (ch == '\0' || ch == '/') + break; + if (ch != '.') + goto enoent; + ch = (unsigned char)*src++; + if (!isascii(ch) || !isdigit(ch)) + goto enoent; + } + } else + goto enoent; + + bits = -1; + if (ch == '/' && isascii((unsigned char)src[0]) && + isdigit((unsigned char)src[0]) && dst > odst) { + /* CIDR width specifier. Nothing can follow it. */ + ch = (unsigned char)*src++; /* Skip over the /. */ + bits = 0; + do { + n = strchr(digits, ch) - digits; + assert(n >= 0 && n <= 9); + bits *= 10; + bits += n; + if (bits > 32) + goto emsgsize; + } while ((ch = (unsigned char)*src++) != '\0' && + isascii(ch) && isdigit(ch)); + if (ch != '\0') + goto enoent; + } + + /* Firey death and destruction unless we prefetched EOS. */ + if (ch != '\0') + goto enoent; + + /* If nothing was written to the destination, we found no address. */ + if (dst == odst) + goto enoent; + /* If no CIDR spec was given, infer width from net class. */ + if (bits == -1) { + if (*odst >= 240) /* Class E */ + bits = 32; + else if (*odst >= 224) /* Class D */ + bits = 4; + else if (*odst >= 192) /* Class C */ + bits = 24; + else if (*odst >= 128) /* Class B */ + bits = 16; + else /* Class A */ + bits = 8; + /* If imputed mask is narrower than specified octets, widen. */ + if (bits < ((dst - odst) * 8)) + bits = (dst - odst) * 8; + } + /* Extend network to cover the actual mask. */ + while (bits > ((dst - odst) * 8)) { + if (size-- <= 0) + goto emsgsize; + *dst++ = '\0'; + } + return (bits); + + enoent: + errno = ENOENT; + return (-1); + + emsgsize: + errno = EMSGSIZE; + return (-1); +} + + +static int +inet_net_pton_ipv6(const char *src, u_char *dst, size_t size) +{ + int ret; + int bits; + char buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255:255:255:255/128")]; + char *sep; + const char *errstr; + + if (strlcpy(buf, src, sizeof buf) >= sizeof buf) { + errno = EMSGSIZE; + return (-1); + } + + sep = strchr(buf, '/'); + if (sep != NULL) + *sep++ = '\0'; + + ret = inet_pton(AF_INET6, buf, dst); + if (ret != 1) + return (-1); + + if (sep == NULL) + return 128; + + bits = strtonum(sep, 0, 128, &errstr); + if (errstr) { + errno = EINVAL; + return (-1); + } + + return bits; +} + +#endif diff --git a/openbsd-compat/libasr/asr.c b/openbsd-compat/libasr/asr.c new file mode 100644 index 00000000..008544ad --- /dev/null +++ b/openbsd-compat/libasr/asr.c @@ -0,0 +1,869 @@ +/* $OpenBSD: asr.c,v 1.61 2018/10/22 17:31:24 krw Exp $ */ +/* + * Copyright (c) 2010-2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <netdb.h> + +#include <asr.h> +#include <errno.h> +#include <fcntl.h> +#include <resolv.h> +#include <poll.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <limits.h> + +#include "asr_private.h" + +#include "thread_private.h" + +#define DEFAULT_CONF "lookup file\n" +#define DEFAULT_LOOKUP "lookup bind file" + +#define RELOAD_DELAY 15 /* seconds */ + +static void asr_check_reload(struct asr *); +static struct asr_ctx *asr_ctx_create(void); +static void asr_ctx_ref(struct asr_ctx *); +static void asr_ctx_free(struct asr_ctx *); +static int asr_ctx_add_searchdomain(struct asr_ctx *, const char *); +static int asr_ctx_from_file(struct asr_ctx *, const char *); +static int asr_ctx_from_string(struct asr_ctx *, const char *); +static int asr_ctx_parse(struct asr_ctx *, const char *); +static int asr_parse_nameserver(struct sockaddr *, const char *); +static int asr_ndots(const char *); +static void pass0(char **, int, struct asr_ctx *); +static int strsplit(char *, char **, int); +static void asr_ctx_envopts(struct asr_ctx *); +static void *__THREAD_NAME(_asr); + +static struct asr *_asr = NULL; + +#ifndef HAVE_ISSETUGID +#define issetugid() ((getuid() != geteuid())) +#endif + +/* Allocate and configure an async "resolver". */ +static void * +_asr_resolver(void) +{ + static int init = 0; + struct asr *asr; + + if (init == 0) { +#ifdef DEBUG + if (getenv("ASR_DEBUG")) + _asr_debug = stderr; +#endif + init = 1; + } + + if ((asr = calloc(1, sizeof(*asr))) == NULL) + goto fail; + + asr_check_reload(asr); + if (asr->a_ctx == NULL) { + if ((asr->a_ctx = asr_ctx_create()) == NULL) + goto fail; + if (asr_ctx_from_string(asr->a_ctx, DEFAULT_CONF) == -1) + goto fail; + asr_ctx_envopts(asr->a_ctx); + } + +#ifdef DEBUG + _asr_dump_config(_asr_debug, asr); +#endif + return (asr); + + fail: + if (asr) { + if (asr->a_ctx) + asr_ctx_free(asr->a_ctx); + free(asr); + } + + return (NULL); +} + +/* + * Free the "asr" async resolver (or the thread-local resolver if NULL). + * Drop the reference to the current context. + */ +void +_asr_resolver_done(void *arg) +{ + struct asr *asr = arg; + struct asr **priv; + + if (asr == NULL) { + priv = _THREAD_PRIVATE(_asr, _asr, &_asr); + if (*priv == NULL) + return; + asr = *priv; + *priv = NULL; + } + + _asr_ctx_unref(asr->a_ctx); + free(asr); +} + +/* + * Cancel an async query. + */ +void +asr_abort(struct asr_query *as) +{ + _asr_async_free(as); +} + +/* + * Resume the "as" async query resolution. Return one of ASYNC_COND, + * or ASYNC_DONE and put query-specific return values in the user-allocated + * memory at "ar". + */ +int +asr_run(struct asr_query *as, struct asr_result *ar) +{ + int r, saved_errno = errno; + + memset(ar, 0, sizeof(*ar)); + + DPRINT("asr: asr_run(%p, %p) %s ctx=[%p]\n", as, ar, + _asr_querystr(as->as_type), as->as_ctx); + r = as->as_run(as, ar); + + DPRINT("asr: asr_run(%p, %p) -> %s", as, ar, _asr_transitionstr(r)); +#ifdef DEBUG + if (r == ASYNC_COND) +#endif + DPRINT(" fd=%i timeout=%i", ar->ar_fd, ar->ar_timeout); + DPRINT("\n"); + if (r == ASYNC_DONE) + _asr_async_free(as); + + errno = saved_errno; + + return (r); +} +DEF_WEAK(asr_run); + +static int +poll_intrsafe(struct pollfd *fds, nfds_t nfds, int timeout) +{ + struct timespec pollstart, pollend, elapsed; + int r; + + if (clock_gettime(CLOCK_MONOTONIC, &pollstart)) + return -1; + + while ((r = poll(fds, 1, timeout)) == -1 && errno == EINTR) { + if (clock_gettime(CLOCK_MONOTONIC, &pollend)) + return -1; + timespecsub(&pollend, &pollstart, &elapsed); + timeout -= elapsed.tv_sec * 1000 + elapsed.tv_nsec / 1000000; + if (timeout < 1) + return 0; + } + + return r; +} + +/* + * Same as asr_run, but run in a loop that handles the fd conditions result. + */ +int +asr_run_sync(struct asr_query *as, struct asr_result *ar) +{ + struct pollfd fds[1]; + int r, saved_errno = errno; + + while ((r = asr_run(as, ar)) == ASYNC_COND) { + fds[0].fd = ar->ar_fd; + fds[0].events = (ar->ar_cond == ASR_WANT_READ) ? POLLIN:POLLOUT; + + if (poll_intrsafe(fds, 1, ar->ar_timeout) == -1) { + memset(ar, 0, sizeof(*ar)); + ar->ar_errno = errno; + ar->ar_h_errno = NETDB_INTERNAL; + ar->ar_gai_errno = EAI_SYSTEM; + ar->ar_rrset_errno = NETDB_INTERNAL; + _asr_async_free(as); + errno = saved_errno; + return ASYNC_DONE; + } + + /* + * Otherwise, just ignore the error and let asr_run() + * catch the failure. + */ + } + + errno = saved_errno; + + return (r); +} +DEF_WEAK(asr_run_sync); + +/* + * Create a new async request of the given "type" on the async context "ac". + * Take a reference on it so it does not get deleted while the async query + * is running. + */ +struct asr_query * +_asr_async_new(struct asr_ctx *ac, int type) +{ + struct asr_query *as; + + DPRINT("asr: asr_async_new(ctx=%p) type=%i refcount=%i\n", ac, type, + ac ? ac->ac_refcount : 0); + if (ac == NULL || (as = calloc(1, sizeof(*as))) == NULL) + return (NULL); + + ac->ac_refcount += 1; + as->as_ctx = ac; + as->as_fd = -1; + as->as_type = type; + as->as_state = ASR_STATE_INIT; + + return (as); +} + +/* + * Free an async query and unref the associated context. + */ +void +_asr_async_free(struct asr_query *as) +{ + DPRINT("asr: asr_async_free(%p)\n", as); + + if (as->as_subq) + _asr_async_free(as->as_subq); + + switch (as->as_type) { + case ASR_SEND: + if (as->as_fd != -1) + close(as->as_fd); + if (as->as.dns.obuf && !(as->as_flags & ASYNC_EXTOBUF)) + free(as->as.dns.obuf); + if (as->as.dns.ibuf) + free(as->as.dns.ibuf); + if (as->as.dns.dname) + free(as->as.dns.dname); + break; + + case ASR_SEARCH: + if (as->as.search.name) + free(as->as.search.name); + break; + + case ASR_GETRRSETBYNAME: + if (as->as.rrset.name) + free(as->as.rrset.name); + break; + + case ASR_GETHOSTBYNAME: + case ASR_GETHOSTBYADDR: + if (as->as.hostnamadr.name) + free(as->as.hostnamadr.name); + break; + + case ASR_GETADDRINFO: + if (as->as.ai.aifirst) + freeaddrinfo(as->as.ai.aifirst); + if (as->as.ai.hostname) + free(as->as.ai.hostname); + if (as->as.ai.servname) + free(as->as.ai.servname); + if (as->as.ai.fqdn) + free(as->as.ai.fqdn); + break; + + case ASR_GETNAMEINFO: + break; + } + + _asr_ctx_unref(as->as_ctx); + free(as); +} + +/* + * Get a context from the given resolver. This takes a new reference to + * the returned context, which *must* be explicitly dropped when done + * using this context. + */ +struct asr_ctx * +_asr_use_resolver(void *arg) +{ + struct asr *asr = arg; + struct asr **priv; + + if (asr == NULL) { + DPRINT("using thread-local resolver\n"); + priv = _THREAD_PRIVATE(_asr, _asr, &_asr); + if (*priv == NULL) { + DPRINT("setting up thread-local resolver\n"); + *priv = _asr_resolver(); + } + asr = *priv; + } + if (asr != NULL) { + asr_check_reload(asr); + asr_ctx_ref(asr->a_ctx); + return (asr->a_ctx); + } + return (NULL); +} + +static void +asr_ctx_ref(struct asr_ctx *ac) +{ + DPRINT("asr: asr_ctx_ref(ctx=%p) refcount=%i\n", ac, ac->ac_refcount); + ac->ac_refcount += 1; +} + +/* + * Drop a reference to an async context, freeing it if the reference + * count drops to 0. + */ +void +_asr_ctx_unref(struct asr_ctx *ac) +{ + DPRINT("asr: asr_ctx_unref(ctx=%p) refcount=%i\n", ac, + ac ? ac->ac_refcount : 0); + if (ac == NULL) + return; + if (--ac->ac_refcount) + return; + + asr_ctx_free(ac); +} + +static void +asr_ctx_free(struct asr_ctx *ac) +{ + int i; + + if (ac->ac_domain) + free(ac->ac_domain); + for (i = 0; i < ASR_MAXNS; i++) + free(ac->ac_ns[i]); + for (i = 0; i < ASR_MAXDOM; i++) + free(ac->ac_dom[i]); + + free(ac); +} + +/* + * Reload the configuration file if it has changed on disk. + */ +static void +asr_check_reload(struct asr *asr) +{ + struct asr_ctx *ac; + struct stat st; + struct timespec ts; + pid_t pid; + + pid = getpid(); + if (pid != asr->a_pid) { + asr->a_pid = pid; + asr->a_rtime = 0; + } + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == -1) + return; + + if ((ts.tv_sec - asr->a_rtime) < RELOAD_DELAY && asr->a_rtime != 0) + return; + asr->a_rtime = ts.tv_sec; + + DPRINT("asr: checking for update of \"%s\"\n", _PATH_RESCONF); + if (stat(_PATH_RESCONF, &st) == -1 || + asr->a_mtime == st.st_mtime || + (ac = asr_ctx_create()) == NULL) + return; + asr->a_mtime = st.st_mtime; + + DPRINT("asr: reloading config file\n"); + if (asr_ctx_from_file(ac, _PATH_RESCONF) == -1) { + asr_ctx_free(ac); + return; + } + + asr_ctx_envopts(ac); + if (asr->a_ctx) + _asr_ctx_unref(asr->a_ctx); + asr->a_ctx = ac; +} + +/* + * Construct a fully-qualified domain name for the given name and domain. + * If "name" ends with a '.' it is considered as a FQDN by itself. + * Otherwise, the domain, which must be a FQDN, is appended to "name" (it + * may have a leading dot which would be ignored). If the domain is null, + * then "." is used. Return the length of the constructed FQDN or (0) on + * error. + */ +size_t +_asr_make_fqdn(const char *name, const char *domain, char *buf, size_t buflen) +{ + size_t len; + + if (domain == NULL) + domain = "."; + else if ((len = strlen(domain)) == 0) + return (0); + else if (domain[len -1] != '.') + return (0); + + len = strlen(name); + if (len == 0) { + if (strlcpy(buf, domain, buflen) >= buflen) + return (0); + } else if (name[len - 1] != '.') { + if (domain[0] == '.') + domain += 1; + if (strlcpy(buf, name, buflen) >= buflen || + strlcat(buf, ".", buflen) >= buflen || + strlcat(buf, domain, buflen) >= buflen) + return (0); + } else { + if (strlcpy(buf, name, buflen) >= buflen) + return (0); + } + + return (strlen(buf)); +} + +/* + * Count the dots in a string. + */ +static int +asr_ndots(const char *s) +{ + int n; + + for (n = 0; *s; s++) + if (*s == '.') + n += 1; + + return (n); +} + +/* + * Allocate a new empty context. + */ +static struct asr_ctx * +asr_ctx_create(void) +{ + struct asr_ctx *ac; + + if ((ac = calloc(1, sizeof(*ac))) == NULL) + return (NULL); + + ac->ac_options = RES_RECURSE | RES_DEFNAMES | RES_DNSRCH; + ac->ac_refcount = 1; + ac->ac_ndots = 1; +#ifndef ASR_IPV4_BEFORE_IPV6 + ac->ac_family[0] = AF_INET6; + ac->ac_family[1] = AF_INET; +#else + ac->ac_family[0] = AF_INET; + ac->ac_family[1] = AF_INET6; +#endif + ac->ac_family[2] = -1; + + ac->ac_nscount = 0; + ac->ac_nstimeout = 5; + ac->ac_nsretries = 4; + + return (ac); +} + +struct asr_ctx * +_asr_no_resolver(void) +{ + return asr_ctx_create(); +} + +/* + * Add a search domain to the async context. + */ +static int +asr_ctx_add_searchdomain(struct asr_ctx *ac, const char *domain) +{ + char buf[MAXDNAME]; + + if (ac->ac_domcount == ASR_MAXDOM) + return (-1); + + if (_asr_make_fqdn(domain, NULL, buf, sizeof(buf)) == 0) + return (-1); + + if ((ac->ac_dom[ac->ac_domcount] = strdup(buf)) == NULL) + return (0); + + ac->ac_domcount += 1; + + return (1); +} + +static int +strsplit(char *line, char **tokens, int ntokens) +{ + int ntok; + char *cp, **tp; + + for (cp = line, tp = tokens, ntok = 0; + ntok < ntokens && (*tp = strsep(&cp, " \t")) != NULL; ) + if (**tp != '\0') { + tp++; + ntok++; + } + + return (ntok); +} + +/* + * Pass on a split config line. + */ +static void +pass0(char **tok, int n, struct asr_ctx *ac) +{ + int i, j, d; + const char *e; + struct sockaddr_storage ss; + + if (!strcmp(tok[0], "nameserver")) { + if (ac->ac_nscount == ASR_MAXNS) + return; + if (n != 2) + return; + if (asr_parse_nameserver((struct sockaddr *)&ss, tok[1])) + return; + if ((ac->ac_ns[ac->ac_nscount] = calloc(1, SS_LEN(&ss))) == NULL) + return; + memmove(ac->ac_ns[ac->ac_nscount], &ss, SS_LEN(&ss)); + ac->ac_nscount += 1; + + } else if (!strcmp(tok[0], "domain")) { + if (n != 2) + return; + if (ac->ac_domain) + return; + ac->ac_domain = strdup(tok[1]); + + } else if (!strcmp(tok[0], "lookup")) { + /* ensure that each lookup is only given once */ + for (i = 1; i < n; i++) + for (j = i + 1; j < n; j++) + if (!strcmp(tok[i], tok[j])) + return; + ac->ac_dbcount = 0; + for (i = 1; i < n && ac->ac_dbcount < ASR_MAXDB; i++) { + if (!strcmp(tok[i], "yp")) { + /* silently deprecated */ + } else if (!strcmp(tok[i], "bind")) + ac->ac_db[ac->ac_dbcount++] = ASR_DB_DNS; + else if (!strcmp(tok[i], "file")) + ac->ac_db[ac->ac_dbcount++] = ASR_DB_FILE; + } + } else if (!strcmp(tok[0], "search")) { + /* resolv.conf says the last line wins */ + for (i = 0; i < ASR_MAXDOM; i++) { + free(ac->ac_dom[i]); + ac->ac_dom[i] = NULL; + } + ac->ac_domcount = 0; + for (i = 1; i < n; i++) + asr_ctx_add_searchdomain(ac, tok[i]); + + } else if (!strcmp(tok[0], "family")) { + if (n == 1 || n > 3) + return; + for (i = 1; i < n; i++) + if (strcmp(tok[i], "inet4") && strcmp(tok[i], "inet6")) + return; + for (i = 1; i < n; i++) + ac->ac_family[i - 1] = strcmp(tok[i], "inet4") ? \ + AF_INET6 : AF_INET; + ac->ac_family[i - 1] = -1; + + } else if (!strcmp(tok[0], "options")) { + for (i = 1; i < n; i++) { + if (!strcmp(tok[i], "tcp")) + ac->ac_options |= RES_USEVC; + else if (!strcmp(tok[i], "edns0")) + ac->ac_options |= RES_USE_EDNS0; + else if ((!strncmp(tok[i], "ndots:", 6))) { + e = NULL; + d = strtonum(tok[i] + 6, 1, 16, &e); + if (e == NULL) + ac->ac_ndots = d; + } + } + } +} + +/* + * Setup an async context with the config specified in the string "str". + */ +static int +asr_ctx_from_string(struct asr_ctx *ac, const char *str) +{ + char buf[512], *ch; + + asr_ctx_parse(ac, str); + + if (ac->ac_dbcount == 0) { + /* No lookup directive */ + asr_ctx_parse(ac, DEFAULT_LOOKUP); + } + + if (ac->ac_nscount == 0) + asr_ctx_parse(ac, "nameserver 127.0.0.1"); + + if (ac->ac_domain == NULL) + if (gethostname(buf, sizeof buf) == 0) { + ch = strchr(buf, '.'); + if (ch) + ac->ac_domain = strdup(ch + 1); + else /* Assume root. see resolv.conf(5) */ + ac->ac_domain = strdup(""); + } + + /* If no search domain was specified, use the local subdomains */ + if (ac->ac_domcount == 0) + for (ch = ac->ac_domain; ch; ) { + asr_ctx_add_searchdomain(ac, ch); + ch = strchr(ch, '.'); + if (ch && asr_ndots(++ch) == 0) + break; + } + + return (0); +} + +/* + * Setup the "ac" async context from the file at location "path". + */ +static int +asr_ctx_from_file(struct asr_ctx *ac, const char *path) +{ + FILE *cf; + char buf[4096]; + ssize_t r; + + cf = fopen(path, "re"); + if (cf == NULL) + return (-1); + + r = fread(buf, 1, sizeof buf - 1, cf); + if (feof(cf) == 0) { + DPRINT("asr: config file too long: \"%s\"\n", path); + r = -1; + } + fclose(cf); + if (r == -1) + return (-1); + buf[r] = '\0'; + + return asr_ctx_from_string(ac, buf); +} + +/* + * Parse lines in the configuration string. For each one, split it into + * tokens and pass them to "pass0" for processing. + */ +static int +asr_ctx_parse(struct asr_ctx *ac, const char *str) +{ + size_t len; + const char *line; + char buf[1024]; + char *tok[10]; + int ntok; + + line = str; + while (*line) { + len = strcspn(line, "\n\0"); + if (len < sizeof buf) { + memmove(buf, line, len); + buf[len] = '\0'; + } else + buf[0] = '\0'; + line += len; + if (*line == '\n') + line++; + buf[strcspn(buf, ";#")] = '\0'; + if ((ntok = strsplit(buf, tok, 10)) == 0) + continue; + + pass0(tok, ntok, ac); + } + + return (0); +} + +/* + * Check for environment variables altering the configuration as described + * in resolv.conf(5). Although not documented there, this feature is disabled + * for setuid/setgid programs. + */ +static void +asr_ctx_envopts(struct asr_ctx *ac) +{ + char buf[4096], *e; + size_t s; + + if (issetugid()) { + ac->ac_options |= RES_NOALIASES; + return; + } + + if ((e = getenv("RES_OPTIONS")) != NULL) { + strlcpy(buf, "options ", sizeof buf); + strlcat(buf, e, sizeof buf); + s = strlcat(buf, "\n", sizeof buf); + if (s < sizeof buf) + asr_ctx_parse(ac, buf); + } + + if ((e = getenv("LOCALDOMAIN")) != NULL) { + strlcpy(buf, "search ", sizeof buf); + strlcat(buf, e, sizeof buf); + s = strlcat(buf, "\n", sizeof buf); + if (s < sizeof buf) + asr_ctx_parse(ac, buf); + } +} + +/* + * Parse a resolv.conf(5) nameserver string into a sockaddr. + */ +static int +asr_parse_nameserver(struct sockaddr *sa, const char *s) +{ + in_port_t portno = 53; + + if (_asr_sockaddr_from_str(sa, PF_UNSPEC, s) == -1) + return (-1); + + if (sa->sa_family == PF_INET) + ((struct sockaddr_in *)sa)->sin_port = htons(portno); + else if (sa->sa_family == PF_INET6) + ((struct sockaddr_in6 *)sa)->sin6_port = htons(portno); + + return (0); +} + +/* + * Turn a (uncompressed) DNS domain name into a regular nul-terminated string + * where labels are separated by dots. The result is put into the "buf" buffer, + * truncated if it exceeds "max" chars. The function returns "buf". + */ +char * +_asr_strdname(const char *_dname, char *buf, size_t max) +{ + const unsigned char *dname = _dname; + char *res; + size_t left, n, count; + + if (_dname[0] == 0) { + strlcpy(buf, ".", max); + return buf; + } + + res = buf; + left = max - 1; + for (n = 0; dname[0] && left; n += dname[0]) { + count = (dname[0] < (left - 1)) ? dname[0] : (left - 1); + memmove(buf, dname + 1, count); + dname += dname[0] + 1; + left -= count; + buf += count; + if (left) { + left -= 1; + *buf++ = '.'; + } + } + buf[0] = 0; + + return (res); +} + +/* + * Read and split the next line from the given namedb file. + * Return -1 on error, or put the result in the "tokens" array of + * size "ntoken" and returns the number of token on the line. + */ +int +_asr_parse_namedb_line(FILE *file, char **tokens, int ntoken, char *lbuf, size_t sz) +{ + size_t len; + char *buf; + int ntok; + + again: + if ((buf = fgetln(file, &len)) == NULL) + return (-1); + + if (len >= sz) + goto again; + + if (buf[len - 1] == '\n') + len--; + else { + memcpy(lbuf, buf, len); + buf = lbuf; + } + + buf[len] = '\0'; + buf[strcspn(buf, "#")] = '\0'; + if ((ntok = strsplit(buf, tokens, ntoken)) == 0) + goto again; + + return (ntok); +} + +/* + * Update the async context so that it uses the next configured DB. + * Return 0 on success, or -1 if no more DBs is available. + */ +int +_asr_iter_db(struct asr_query *as) +{ + if (as->as_db_idx >= as->as_ctx->ac_dbcount) { + DPRINT("asr_iter_db: done\n"); + return (-1); + } + + as->as_db_idx += 1; + DPRINT("asr_iter_db: %i\n", as->as_db_idx); + + return (0); +} diff --git a/openbsd-compat/libasr/asr.h b/openbsd-compat/libasr/asr.h new file mode 100644 index 00000000..e9725e6b --- /dev/null +++ b/openbsd-compat/libasr/asr.h @@ -0,0 +1,95 @@ +/* $OpenBSD: asr.h,v 1.1 2014/03/26 18:13:15 eric Exp $ */ +/* + * Copyright (c) 2012-2014 Eric Faurot <eric@openbsd.org> + * + * 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. + */ + +/* + * Expected fd conditions + */ +#define ASR_WANT_READ 1 +#define ASR_WANT_WRITE 2 + +/* + * Structure through which asynchronous query results are returned when + * calling asr_run(). + */ +struct asr_result { + /* Fields set if the query is not done yet (asr_run returns 0) */ + int ar_cond; /* ASR_WANT_READ or ASR_WANT_WRITE */ + int ar_fd; /* the fd waiting for io condition */ + int ar_timeout; /* time to wait for in milliseconds */ + + /* Error fields. Depends on the query type. */ + int ar_errno; + int ar_h_errno; + int ar_gai_errno; + int ar_rrset_errno; + + /* Result for res_*_async() calls */ + int ar_count; /* number of answers in the dns reply */ + int ar_rcode; /* response code in the dns reply */ + void *ar_data; /* raw reply packet (must be freed) */ + int ar_datalen; /* reply packet length */ + struct sockaddr_storage ar_ns; /* nameserver that responded */ + + /* Result for other calls. Must be freed properly. */ + struct addrinfo *ar_addrinfo; + struct rrsetinfo *ar_rrsetinfo; + struct hostent *ar_hostent; + struct netent *ar_netent; +}; + +/* + * Asynchronous query management. + */ + +/* Forward declaration. The API uses opaque pointers as query handles. */ +struct asr_query; + +int asr_run(struct asr_query *, struct asr_result *); +int asr_run_sync(struct asr_query *, struct asr_result *); +void asr_abort(struct asr_query *); + +/* + * Asynchronous version of the resolver functions. Similar prototypes, with + * an extra context parameter at the end which must currently be set to NULL. + * All functions return a handle suitable for use with the management functions + * above. + */ +struct asr_query *res_send_async(const unsigned char *, int, void *); +struct asr_query *res_query_async(const char *, int, int, void *); +struct asr_query *res_search_async(const char *, int, int, void *); + +struct asr_query *getrrsetbyname_async(const char *, unsigned int, unsigned int, + unsigned int, void *); + +struct asr_query *gethostbyname_async(const char *, void *); +struct asr_query *gethostbyname2_async(const char *, int, void *); +struct asr_query *gethostbyaddr_async(const void *, socklen_t, int, void *); + +struct asr_query *getnetbyname_async(const char *, void *); +struct asr_query *getnetbyaddr_async(in_addr_t, int, void *); + +struct asr_query *getaddrinfo_async(const char *, const char *, + const struct addrinfo *, void *); +struct asr_query *getnameinfo_async(const struct sockaddr *, socklen_t, char *, + size_t, char *, size_t, int, void *); + +/* only there for -portable */ +void asr_freeaddrinfo(struct addrinfo *); + +/* from in event.h */ +struct event_asr * event_asr_run(struct asr_query *, + void (*)(struct asr_result *, void *), void *); diff --git a/openbsd-compat/libasr/asr_compat.c b/openbsd-compat/libasr/asr_compat.c new file mode 100644 index 00000000..ee958357 --- /dev/null +++ b/openbsd-compat/libasr/asr_compat.c @@ -0,0 +1,102 @@ +/* $OpenBSD: asr_debug.c,v 1.25 2018/04/28 15:16:49 schwarze Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <arpa/nameser.h> +#ifdef HAVE_ARPA_NAMESER_COMPAT_H +#include <arpa/nameser_compat.h> +#endif + +#include "asr_compat.h" + +#ifndef HAVE___P_CLASS +const char * +__p_class(int c) +{ + switch(c) { + case C_IN: return "IN"; + case C_CHAOS: return "CHAOS"; + case C_HS: return "HESIOD"; + case C_ANY: return "ANY"; + default: return "?"; + } +}; +#endif /* !HAVE___P_CLASS */ + +#ifndef HAVE___P_TYPE +const char * +__p_type(int t) +{ + switch(t) { + case T_A: return "A"; + case T_NS: return "NS"; + case T_MD: return "MD"; + case T_MF: return "MF"; + case T_CNAME: return "CNAME"; + case T_SOA: return "SOA"; + case T_MB: return "MB"; + case T_MG: return "MG"; + case T_MR: return "MR"; + case T_NULL: return "NULL"; + case T_WKS: return "WKS"; + case T_PTR: return "PTR"; + case T_HINFO: return "HINFO"; + case T_MINFO: return "MINFO"; + case T_MX: return "MX"; + case T_TXT: return "TXT"; + case T_RP: return "RP"; + case T_AFSDB: return "AFSDB"; + case T_X25: return "X25"; + case T_ISDN: return "ISDN"; + case T_RT: return "RT"; + case T_NSAP: return "NSAP"; + case T_NSAP_PTR:return"NSAP_PTR"; + case T_SIG: return "SIG"; + case T_KEY: return "KEY"; + case T_PX: return "PX"; + case T_GPOS: return "GPOS"; + case T_AAAA: return "AAAA"; + case T_LOC: return "LOC"; + case T_NXT: return "NXT"; + case T_EID: return "EID"; + case T_NIMLOC: return "NIMLOC"; + case T_SRV: return "SRV"; + case T_ATMA: return "ATMA"; + case T_OPT: return "OPT"; + case T_IXFR: return "IXFR"; + case T_AXFR: return "AXFR"; + case T_MAILB: return "MAILB"; + case T_MAILA: return "MAILA"; +#ifdef T_UINFO + case T_UINFO: return "UINFO"; +#endif +#ifdef T_UID + case T_UID: return "UID"; +#endif +#ifdef T_GID + case T_GID: return "GID"; +#endif + case T_NAPTR: return "NAPTR"; +#ifdef T_UNSPEC + case T_UNSPEC: return "UNSPEC"; +#endif + case T_ANY: return "ANY"; + default: return "?"; + } +} +#endif /* !HAVE___P_TYPE */ diff --git a/openbsd-compat/libasr/asr_compat.h b/openbsd-compat/libasr/asr_compat.h new file mode 100644 index 00000000..2c7686a4 --- /dev/null +++ b/openbsd-compat/libasr/asr_compat.h @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2018 Eric Faurot <eric@openbsd.org> + * + * 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. + */ + + +/* source compat */ +#define ASR_BUFSIZ 1024 + +#define DEF_WEAK(x) +#define __THREAD_NAME(x) __thread_name_ ## x + +#ifndef __BEGIN_HIDDEN_DECLS +#define __BEGIN_HIDDEN_DECLS +#endif +#ifndef __END_HIDDEN_DECLS +#define __END_HIDDEN_DECLS +#endif + +/* + * netdb.h + */ +#ifndef NETDB_SUCCESS +#define NETDB_SUCCESS 0 +#endif + +#ifndef NETDB_INTERNAL +#define NETDB_INTERNAL -1 +#endif + +#ifndef AI_FQDN +#define AI_FQDN AI_CANONNAME +#endif + +#ifndef AI_MASK +#define AI_MASK \ + (AI_PASSIVE | AI_CANONNAME | AI_NUMERICHOST | AI_NUMERICSERV | AI_ADDRCONFIG | AI_FQDN) +#endif + +#ifndef SCOPE_DELIMITER +#define SCOPE_DELIMITER '%' +#endif + +#ifndef _PATH_HOSTS +#define _PATH_HOSTS "/etc/hosts" +#endif + +#ifndef _PATH_NETWORKS +#define _PATH_NETWORKS "/etc/networks" +#endif + +/* + * arpa/nameserv.h + */ +#ifndef T_OPT +#define T_OPT 41 +#endif + +#ifndef DNS_MESSAGEEXTFLAG_DO +#define DNS_MESSAGEEXTFLAG_DO 0x8000U +#endif + +#ifndef HAVE___P_CLASS +const char * __p_class(int); +#endif + +#ifndef HAVE___P_TYPE +const char * __p_type(int); +#endif diff --git a/openbsd-compat/libasr/asr_debug.c b/openbsd-compat/libasr/asr_debug.c new file mode 100644 index 00000000..be80436a --- /dev/null +++ b/openbsd-compat/libasr/asr_debug.c @@ -0,0 +1,362 @@ +/* $OpenBSD: asr_debug.c,v 1.26 2019/07/03 03:24:03 deraadt Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#ifdef HAVE_ARPA_NAMESER_COMPAT_H +#include <arpa/nameser_compat.h> +#endif +#include <arpa/inet.h> +#include <netdb.h> + +#include <asr.h> +#include <resolv.h> +#include <string.h> + +#include "asr_private.h" + +static const char *rcodetostr(uint16_t); +static const char *print_dname(const char *, char *, size_t); +static const char *print_header(const struct asr_dns_header *, char *, size_t); +static const char *print_query(const struct asr_dns_query *, char *, size_t); +static const char *print_rr(const struct asr_dns_rr *, char *, size_t); + +FILE *_asr_debug = NULL; + +#define OPCODE_SHIFT 11 + +static const char * +rcodetostr(uint16_t v) +{ + switch (v) { + case NOERROR: return "NOERROR"; + case FORMERR: return "FORMERR"; + case SERVFAIL: return "SERVFAIL"; + case NXDOMAIN: return "NXDOMAIN"; + case NOTIMP: return "NOTIMP"; + case REFUSED: return "REFUSED"; + default: return "?"; + } +} + +static const char * +print_dname(const char *_dname, char *buf, size_t max) +{ + return (_asr_strdname(_dname, buf, max)); +} + +static const char * +print_rr(const struct asr_dns_rr *rr, char *buf, size_t max) +{ + char *res; + char tmp[256]; + char tmp2[256]; + int r; + + res = buf; + + r = snprintf(buf, max, "%s %u %s %s ", + print_dname(rr->rr_dname, tmp, sizeof tmp), + rr->rr_ttl, + __p_class(rr->rr_class), + __p_type(rr->rr_type)); + if (r < 0 || (size_t)r >= max) { + buf[0] = '\0'; + return (buf); + } + + if ((size_t)r >= max) + return (buf); + + max -= r; + buf += r; + + switch (rr->rr_type) { + case T_CNAME: + print_dname(rr->rr.cname.cname, buf, max); + break; + case T_MX: + snprintf(buf, max, "%lu %s", + (unsigned long)rr->rr.mx.preference, + print_dname(rr->rr.mx.exchange, tmp, sizeof tmp)); + break; + case T_NS: + print_dname(rr->rr.ns.nsname, buf, max); + break; + case T_PTR: + print_dname(rr->rr.ptr.ptrname, buf, max); + break; + case T_SOA: + snprintf(buf, max, "%s %s %lu %lu %lu %lu %lu", + print_dname(rr->rr.soa.rname, tmp, sizeof tmp), + print_dname(rr->rr.soa.mname, tmp2, sizeof tmp2), + (unsigned long)rr->rr.soa.serial, + (unsigned long)rr->rr.soa.refresh, + (unsigned long)rr->rr.soa.retry, + (unsigned long)rr->rr.soa.expire, + (unsigned long)rr->rr.soa.minimum); + break; + case T_A: + if (rr->rr_class != C_IN) + goto other; + snprintf(buf, max, "%s", inet_ntop(AF_INET, + &rr->rr.in_a.addr, tmp, sizeof tmp)); + break; + case T_AAAA: + if (rr->rr_class != C_IN) + goto other; + snprintf(buf, max, "%s", inet_ntop(AF_INET6, + &rr->rr.in_aaaa.addr6, tmp, sizeof tmp)); + break; + default: + other: + snprintf(buf, max, "(rdlen=%i)", (int)rr->rr.other.rdlen); + break; + } + + return (res); +} + +static const char * +print_query(const struct asr_dns_query *q, char *buf, size_t max) +{ + char b[256]; + + snprintf(buf, max, "%s %s %s", + print_dname(q->q_dname, b, sizeof b), + __p_class(q->q_class), __p_type(q->q_type)); + + return (buf); +} + +static const char * +print_header(const struct asr_dns_header *h, char *buf, size_t max) +{ + snprintf(buf, max, + "id:0x%04x %s op:%i %s %s %s %s z:%i %s %s r:%s qd:%i an:%i ns:%i ar:%i", + ((int)h->id), + (h->flags & QR_MASK) ? "QR":" ", + (int)(OPCODE(h->flags) >> OPCODE_SHIFT), + (h->flags & AA_MASK) ? "AA":" ", + (h->flags & TC_MASK) ? "TC":" ", + (h->flags & RD_MASK) ? "RD":" ", + (h->flags & RA_MASK) ? "RA":" ", + (h->flags & Z_MASK), + (h->flags & AD_MASK) ? "AD":" ", + (h->flags & CD_MASK) ? "CD":" ", + rcodetostr(RCODE(h->flags)), + h->qdcount, h->ancount, h->nscount, h->arcount); + + return (buf); +} + +void +_asr_dump_packet(FILE *f, const void *data, size_t len) +{ + char buf[1024]; + struct asr_unpack p; + struct asr_dns_header h; + struct asr_dns_query q; + struct asr_dns_rr rr; + int i, an, ns, ar, n; + + if (f == NULL) + return; + + _asr_unpack_init(&p, data, len); + + if (_asr_unpack_header(&p, &h) == -1) { + fprintf(f, ";; BAD PACKET: %s\n", strerror(p.err)); + return; + } + + fprintf(f, ";; HEADER %s\n", print_header(&h, buf, sizeof buf)); + + if (h.qdcount) + fprintf(f, ";; QUERY SECTION:\n"); + for (i = 0; i < h.qdcount; i++) { + if (_asr_unpack_query(&p, &q) == -1) + goto error; + fprintf(f, "%s\n", print_query(&q, buf, sizeof buf)); + } + + an = 0; + ns = an + h.ancount; + ar = ns + h.nscount; + n = ar + h.arcount; + + for (i = 0; i < n; i++) { + if (i == an) + fprintf(f, "\n;; ANSWER SECTION:\n"); + if (i == ns) + fprintf(f, "\n;; AUTHORITY SECTION:\n"); + if (i == ar) + fprintf(f, "\n;; ADDITIONAL SECTION:\n"); + + if (_asr_unpack_rr(&p, &rr) == -1) + goto error; + fprintf(f, "%s\n", print_rr(&rr, buf, sizeof buf)); + } + + if (p.offset != len) + fprintf(f, ";; REMAINING GARBAGE %zu\n", len - p.offset); + + error: + if (p.err) + fprintf(f, ";; ERROR AT OFFSET %zu/%zu: %s\n", p.offset, p.len, + strerror(p.err)); +} + +const char * +_asr_print_sockaddr(const struct sockaddr *sa, char *buf, size_t len) +{ + char h[256]; + int portno; + union { + const struct sockaddr *sa; + const struct sockaddr_in *sin; + const struct sockaddr_in6 *sin6; + } s; + + s.sa = sa; + + switch (sa->sa_family) { + case AF_INET: + inet_ntop(AF_INET, &s.sin->sin_addr, h, sizeof h); + portno = ntohs(s.sin->sin_port); + break; + case AF_INET6: + inet_ntop(AF_INET6, &s.sin6->sin6_addr, h, sizeof h); + portno = ntohs(s.sin6->sin6_port); + break; + default: + snprintf(buf, len, "?"); + return (buf); + } + + snprintf(buf, len, "%s:%i", h, portno); + return (buf); +} + +void +_asr_dump_config(FILE *f, struct asr *a) +{ + char buf[256]; + int i; + struct asr_ctx *ac; + unsigned int o; + + if (f == NULL) + return; + + ac = a->a_ctx; + + fprintf(f, "--------- ASR CONFIG ---------------\n"); + fprintf(f, "DOMAIN \"%s\"\n", ac->ac_domain); + fprintf(f, "SEARCH\n"); + for (i = 0; i < ac->ac_domcount; i++) + fprintf(f, " \"%s\"\n", ac->ac_dom[i]); + fprintf(f, "OPTIONS\n"); + fprintf(f, " options:"); + o = ac->ac_options; + +#define PRINTOPT(flag, n) if (o & (flag)) { fprintf(f, " " n); o &= ~(flag); } + PRINTOPT(RES_INIT, "INIT"); + PRINTOPT(RES_DEBUG, "DEBUG"); + PRINTOPT(RES_USEVC, "USEVC"); + PRINTOPT(RES_IGNTC, "IGNTC"); + PRINTOPT(RES_RECURSE, "RECURSE"); + PRINTOPT(RES_DEFNAMES, "DEFNAMES"); + PRINTOPT(RES_STAYOPEN, "STAYOPEN"); + PRINTOPT(RES_DNSRCH, "DNSRCH"); + PRINTOPT(RES_NOALIASES, "NOALIASES"); + PRINTOPT(RES_USE_EDNS0, "USE_EDNS0"); + PRINTOPT(RES_USE_DNSSEC, "USE_DNSSEC"); + if (o) + fprintf(f, " 0x%08x", o); + fprintf(f, "\n"); + + fprintf(f, " ndots: %i\n", ac->ac_ndots); + fprintf(f, " family:"); + for (i = 0; ac->ac_family[i] != -1; i++) + fprintf(f, " %s", (ac->ac_family[i] == AF_INET)?"inet4":"inet6"); + fprintf(f, "\n"); + fprintf(f, "NAMESERVERS timeout=%i retry=%i\n", + ac->ac_nstimeout, + ac->ac_nsretries); + for (i = 0; i < ac->ac_nscount; i++) + fprintf(f, " %s\n", _asr_print_sockaddr(ac->ac_ns[i], buf, + sizeof buf)); + fprintf(f, "LOOKUP %s", ac->ac_db); + fprintf(f, "\n------------------------------------\n"); +} + +#define CASE(n) case n: return #n + +const char * +_asr_statestr(int state) +{ + switch (state) { + CASE(ASR_STATE_INIT); + CASE(ASR_STATE_NEXT_DOMAIN); + CASE(ASR_STATE_NEXT_DB); + CASE(ASR_STATE_SAME_DB); + CASE(ASR_STATE_NEXT_FAMILY); + CASE(ASR_STATE_NEXT_NS); + CASE(ASR_STATE_UDP_SEND); + CASE(ASR_STATE_UDP_RECV); + CASE(ASR_STATE_TCP_WRITE); + CASE(ASR_STATE_TCP_READ); + CASE(ASR_STATE_PACKET); + CASE(ASR_STATE_SUBQUERY); + CASE(ASR_STATE_NOT_FOUND); + CASE(ASR_STATE_HALT); + default: + return "?"; + } +}; + +const char * +_asr_querystr(int type) +{ + switch (type) { + CASE(ASR_SEND); + CASE(ASR_SEARCH); + CASE(ASR_GETRRSETBYNAME); + CASE(ASR_GETHOSTBYNAME); + CASE(ASR_GETHOSTBYADDR); + CASE(ASR_GETADDRINFO); + CASE(ASR_GETNAMEINFO); + default: + return "?"; + } +} + +const char * +_asr_transitionstr(int type) +{ + switch (type) { + CASE(ASYNC_COND); + CASE(ASYNC_DONE); + default: + return "?"; + } +} diff --git a/openbsd-compat/libasr/asr_private.h b/openbsd-compat/libasr/asr_private.h new file mode 100644 index 00000000..acf0e874 --- /dev/null +++ b/openbsd-compat/libasr/asr_private.h @@ -0,0 +1,359 @@ +/* $OpenBSD: asr_private.h,v 1.47 2018/04/28 15:16:49 schwarze Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <stdio.h> + +#include "asr_compat.h" + +#define QR_MASK (0x1 << 15) +#define OPCODE_MASK (0xf << 11) +#define AA_MASK (0x1 << 10) +#define TC_MASK (0x1 << 9) +#define RD_MASK (0x1 << 8) +#define RA_MASK (0x1 << 7) +#define Z_MASK (0x1 << 6) +#define AD_MASK (0x1 << 5) +#define CD_MASK (0x1 << 4) +#define RCODE_MASK (0xf) + +#define OPCODE(v) ((v) & OPCODE_MASK) +#define RCODE(v) ((v) & RCODE_MASK) + + +struct asr_pack { + char *buf; + size_t len; + size_t offset; + int err; +}; + +struct asr_unpack { + const char *buf; + size_t len; + size_t offset; + int err; +}; + +struct asr_dns_header { + uint16_t id; + uint16_t flags; + uint16_t qdcount; + uint16_t ancount; + uint16_t nscount; + uint16_t arcount; +}; + +struct asr_dns_query { + char q_dname[MAXDNAME]; + uint16_t q_type; + uint16_t q_class; +}; + +struct asr_dns_rr { + char rr_dname[MAXDNAME]; + uint16_t rr_type; + uint16_t rr_class; + uint32_t rr_ttl; + union { + struct { + char cname[MAXDNAME]; + } cname; + struct { + uint16_t preference; + char exchange[MAXDNAME]; + } mx; + struct { + char nsname[MAXDNAME]; + } ns; + struct { + char ptrname[MAXDNAME]; + } ptr; + struct { + char mname[MAXDNAME]; + char rname[MAXDNAME]; + uint32_t serial; + uint32_t refresh; + uint32_t retry; + uint32_t expire; + uint32_t minimum; + } soa; + struct { + struct in_addr addr; + } in_a; + struct { + struct in6_addr addr6; + } in_aaaa; + struct { + uint16_t rdlen; + const void *rdata; + } other; + } rr; +}; + + +#define ASR_MAXNS 5 +#define ASR_MAXDB 3 +#define ASR_MAXDOM 10 + +enum async_type { + ASR_SEND, + ASR_SEARCH, + ASR_GETRRSETBYNAME, + ASR_GETHOSTBYNAME, + ASR_GETHOSTBYADDR, + ASR_GETADDRINFO, + ASR_GETNAMEINFO, +}; + +#define ASR_DB_FILE 'f' +#define ASR_DB_DNS 'b' + +struct asr_ctx { + int ac_refcount; + int ac_options; + int ac_ndots; + char *ac_domain; + int ac_domcount; + char *ac_dom[ASR_MAXDOM]; + int ac_dbcount; + char ac_db[ASR_MAXDB + 1]; + int ac_family[3]; + + int ac_nscount; + int ac_nstimeout; + int ac_nsretries; + struct sockaddr *ac_ns[ASR_MAXNS]; + +}; + +struct asr { + pid_t a_pid; + time_t a_mtime; + time_t a_rtime; + struct asr_ctx *a_ctx; +}; + +#define ASYNC_COND 0 +#define ASYNC_DONE 1 + +#define ASYNC_DOM_FQDN 0x00000001 +#define ASYNC_DOM_NDOTS 0x00000002 +#define ASYNC_DOM_DOMAIN 0x00000004 +#define ASYNC_DOM_ASIS 0x00000008 + +#define ASYNC_NODATA 0x00000100 +#define ASYNC_AGAIN 0x00000200 + +#define ASYNC_GETNET 0x00001000 +#define ASYNC_EXTOBUF 0x00002000 + +#define ASYNC_NO_INET 0x00010000 +#define ASYNC_NO_INET6 0x00020000 + +struct asr_query { + int (*as_run)(struct asr_query *, struct asr_result *); + struct asr_ctx *as_ctx; + int as_type; + int as_flags; + int as_state; + + /* cond */ + int as_timeout; + int as_fd; + struct asr_query *as_subq; + + /* loop indices in ctx */ + int as_dom_step; + int as_dom_idx; + int as_dom_flags; + int as_family_idx; + int as_db_idx; + + int as_count; + + union { + struct { + uint16_t reqid; + int class; + int type; + char *dname; /* not fqdn! */ + int rcode; /* response code */ + int ancount; /* answer count */ + + int nsidx; + int nsloop; + + /* io buffers for query/response */ + unsigned char *obuf; + size_t obuflen; + size_t obufsize; + unsigned char *ibuf; + size_t ibuflen; + size_t ibufsize; + size_t datalen; /* for tcp io */ + uint16_t pktlen; + } dns; + + struct { + int class; + int type; + char *name; + int saved_h_errno; + } search; + + struct { + int flags; + int class; + int type; + char *name; + } rrset; + + struct { + char *name; + int family; + char addr[16]; + int addrlen; + int subq_h_errno; + } hostnamadr; + + struct { + char *hostname; + char *servname; + int port_tcp; + int port_udp; + union { + struct sockaddr sa; + struct sockaddr_in sain; + struct sockaddr_in6 sain6; + } sa; + + struct addrinfo hints; + char *fqdn; + struct addrinfo *aifirst; + struct addrinfo *ailast; + } ai; + + struct { + char *hostname; + char *servname; + size_t hostnamelen; + size_t servnamelen; + union { + struct sockaddr sa; + struct sockaddr_in sain; + struct sockaddr_in6 sain6; + } sa; + int flags; + } ni; +#define MAXTOKEN 10 + } as; + +}; + +#define AS_DB(p) ((p)->as_ctx->ac_db[(p)->as_db_idx - 1]) +#define AS_FAMILY(p) ((p)->as_ctx->ac_family[(p)->as_family_idx]) + +enum asr_state { + ASR_STATE_INIT, + ASR_STATE_NEXT_DOMAIN, + ASR_STATE_NEXT_DB, + ASR_STATE_SAME_DB, + ASR_STATE_NEXT_FAMILY, + ASR_STATE_NEXT_NS, + ASR_STATE_UDP_SEND, + ASR_STATE_UDP_RECV, + ASR_STATE_TCP_WRITE, + ASR_STATE_TCP_READ, + ASR_STATE_PACKET, + ASR_STATE_SUBQUERY, + ASR_STATE_NOT_FOUND, + ASR_STATE_HALT, +}; + +#define MAXPACKETSZ 4096 + +__BEGIN_HIDDEN_DECLS + +/* asr_utils.c */ +void _asr_pack_init(struct asr_pack *, char *, size_t); +int _asr_pack_header(struct asr_pack *, const struct asr_dns_header *); +int _asr_pack_query(struct asr_pack *, uint16_t, uint16_t, const char *); +int _asr_pack_edns0(struct asr_pack *, uint16_t, int); +void _asr_unpack_init(struct asr_unpack *, const char *, size_t); +int _asr_unpack_header(struct asr_unpack *, struct asr_dns_header *); +int _asr_unpack_query(struct asr_unpack *, struct asr_dns_query *); +int _asr_unpack_rr(struct asr_unpack *, struct asr_dns_rr *); +int _asr_sockaddr_from_str(struct sockaddr *, int, const char *); +ssize_t _asr_dname_from_fqdn(const char *, char *, size_t); +ssize_t _asr_addr_as_fqdn(const char *, int, char *, size_t); + +/* asr.c */ +void _asr_resolver_done(void *); +struct asr_ctx *_asr_use_resolver(void *); +struct asr_ctx *_asr_no_resolver(void); +void _asr_ctx_unref(struct asr_ctx *); +struct asr_query *_asr_async_new(struct asr_ctx *, int); +void _asr_async_free(struct asr_query *); +size_t _asr_make_fqdn(const char *, const char *, char *, size_t); +char *_asr_strdname(const char *, char *, size_t); +int _asr_iter_db(struct asr_query *); +int _asr_parse_namedb_line(FILE *, char **, int, char *, size_t); + +/* *_async.c */ +struct asr_query *_res_query_async_ctx(const char *, int, int, struct asr_ctx *); +struct asr_query *_res_search_async_ctx(const char *, int, int, struct asr_ctx *); +struct asr_query *_gethostbyaddr_async_ctx(const void *, socklen_t, int, + struct asr_ctx *); + +int _asr_iter_domain(struct asr_query *, const char *, char *, size_t); + +#ifdef DEBUG + +#define DPRINT(...) do { if(_asr_debug) { \ + fprintf(_asr_debug, __VA_ARGS__); \ + } } while (0) +#define DPRINT_PACKET(n, p, s) do { if(_asr_debug) { \ + fprintf(_asr_debug, "----- %s -----\n", n); \ + _asr_dump_packet(_asr_debug, (p), (s)); \ + fprintf(_asr_debug, "--------------\n"); \ + } } while (0) + +#else /* DEBUG */ + +#define DPRINT(...) +#define DPRINT_PACKET(...) + +#endif /* DEBUG */ + +const char *_asr_querystr(int); +const char *_asr_statestr(int); +const char *_asr_transitionstr(int); +const char *_asr_print_sockaddr(const struct sockaddr *, char *, size_t); +void _asr_dump_config(FILE *, struct asr *); +void _asr_dump_packet(FILE *, const void *, size_t); + +extern FILE *_asr_debug; + +#define async_set_state(a, s) do { \ + DPRINT("asr: [%s@%p] %s -> %s\n", \ + _asr_querystr((a)->as_type), \ + as, \ + _asr_statestr((a)->as_state), \ + _asr_statestr((s))); \ + (a)->as_state = (s); } while (0) + +__END_HIDDEN_DECLS diff --git a/openbsd-compat/libasr/asr_run.3 b/openbsd-compat/libasr/asr_run.3 new file mode 100644 index 00000000..61c1b02c --- /dev/null +++ b/openbsd-compat/libasr/asr_run.3 @@ -0,0 +1,316 @@ +.\" $OpenBSD: asr_run.3,v 1.3 2017/02/18 19:23:05 jca Exp $ +.\" +.\" Copyright (c) 2012-2014, Eric Faurot <eric@openbsd.org> +.\" +.\" 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. +.\" +.Dd $Mdocdate: February 18 2017 $ +.Dt ASR_RUN 3 +.Os +.Sh NAME +.Nm asr_run , +.Nm asr_run_sync , +.Nm asr_abort , +.Nm res_send_async , +.Nm res_query_async , +.Nm res_search_async , +.Nm getrrsetbyname_async , +.Nm gethostbyname_async , +.Nm gethostbyname2_async , +.Nm gethostbyaddr_async , +.Nm getnetbyname_async , +.Nm getnetbyaddr_async , +.Nm getaddrinfo_async , +.Nm getnameinfo_async +.Nd asynchronous resolver functions +.Sh SYNOPSIS +.In sys/types.h +.In sys/socket.h +.In netdb.h +.In asr.h +.Ft int +.Fn asr_run "struct asr_query *aq" "struct asr_result *ar" +.Ft int +.Fn asr_run_sync "struct asr_query *aq" "struct asr_result *ar" +.Ft void +.Fn asr_abort "struct asr_query *aq" +.Ft struct asr_query * +.Fn res_send_async "const unsigned char *pkt" "int pktlen" "void *asr" +.Ft struct asr_query * +.Fn res_query_async "const char *name" "int class" "int type" "void *asr" +.Ft struct asr_query * +.Fn res_search_async "const char *name" "int class" "int type" "void *asr" +.Ft struct asr_query * +.Fn getrrsetbyname_async "const char *hostname" "unsigned int rdclass" "unsigned int rdtype" "unsigned int flags" "void *asr" +.Ft struct asr_query * +.Fn gethostbyname_async "const char *name" "void *asr" +.Ft struct asr_query * +.Fn gethostbyname2_async "const char *name" "int af" "void *asr" +.Ft struct asr_query * +.Fn gethostbyaddr_async "const void *addr" "socklen_t len" "int af" "void *asr" +.Ft struct asr_query * +.Fn getnetbyname_async "const char *name" "void *asr" +.Ft struct asr_query * +.Fn getnetbyaddr_async "in_addr_t net" "int type" "void *asr" +.Ft struct asr_query * +.Fn getaddrinfo_async "const char *hostname" "const char *servname" "const struct addrinfo *hints" "void *asr" +.Ft struct asr_query * +.Fn getnameinfo_async "const struct sockaddr *sa" "socklen_t salen" "char *host" "size_t hostlen" "char *serv" "size_t servlen" "int flags" "void *asr" +.Sh DESCRIPTION +The +.Nm asr +functions provide a simple interface for asynchronous address +resolution and nameserver querying. +They should be used in place of the classical resolver functions +of libc when blocking is not desirable. +.Pp +The principle of operation is as follows: +All async requests are made against an +.Nm asr +context which basically defines a list of sources to query and a +strategy to do so. +The user creates a query through one of the dedicated functions, and +gets a handle representing the internal query. +A query is a state-machine that can be run to try to fulfill a +particular request. +This is done by calling in a generic API that performs the state +transitions until it needs to give the control back to the user, +either because a result is available, or because the next transition +implies a blocking call (a file descriptor needs to be read from or +written to). +The user is responsible for dealing with the situation: either get +the result, or wait until the fd conditions are met, and then call +back into the resolving machinery when it is ready to proceed. +.Pp +The +.Fn asr_run +function drives the resolving process. +It runs the asynchronous query represented by the +.Fa aq +handle until a result is available, or until it cannot continue +without blocking. +The results are returned to the user through the +.Fa ar +parameter, which must be a valid pointer to user allocated memory. +.Fa ar +is defined as: +.Bd -literal +struct asr_result { + + /* Fields set if the query is not done yet (asr_run returns 0) */ + int ar_cond; /* ASR_WANT_READ or ASR_WANT_WRITE */ + int ar_fd; /* the fd waiting for io condition */ + int ar_timeout; /* time to wait for in milliseconds */ + + /* Error fields. Depends on the query type. */ + int ar_errno; + int ar_h_errno; + int ar_gai_errno; + int ar_rrset_errno; + + /* Result for res_*_async() calls */ + int ar_count; /* number of answers in the dns reply */ + int ar_rcode; /* response code in the dns reply */ + void *ar_data; /* raw reply packet (must be freed) */ + int ar_datalen; /* reply packet length */ + struct sockaddr_storage ar_ns; /* nameserver that responded */ + + /* Result for other calls. Must be freed properly. */ + struct addrinfo *ar_addrinfo; + struct rrsetinfo *ar_rrsetinfo; + struct hostent *ar_hostent; + struct netent *ar_netent; +}; +.Ed +.Pp +The function returns one of the following values: +.Bl -tag -width "0 " -offset indent +.It 0 +The query cannot be processed further until a specific condition on a +file descriptor becomes true. +The following members of the +.Fa ar +structure are filled: +.Pp +.Bl -tag -width "ar_timeout " -compact +.It Fa ar_cond +one of ASR_WANT_READ or ASR_WANT_WRITE, +.It Fa ar_fd +the file descriptor waiting for an IO operation, +.It Fa ar_timeout +the amount of time to wait for in milliseconds. +.El +.Pp +The caller is expected to call +.Fn asr_run +again once the condition holds or the timeout expires. +.It 1 +The query is completed. +The members relevant to the actual async query type are set accordingly, +including error conditions. +In any case, the query is cleared and its handle is invalidated. +.El +.Pp +Note that although the query itself may fail (the error being properly reported +in the +.Fa ar +structure), the +.Fn asr_run +function itself cannot fail and it always preserves errno. +.Pp +The +.Fn asr_run_sync +function is a wrapper around +.Fn asr_run +that handles the read/write conditions, thus falling back to a blocking +interface. +It only returns 1. +It also preserves errno. +.Pp +The +.Fn asr_abort +function clears a running query. +It can be called when the query is waiting on a file descriptor. +Note that a completed query is already cleared when +.Fn asr_run +returns, so +.Fn asr_abort +must not be called in this case. +.Pp +The remaining functions are used to initiate different kinds of query +on the +.Fa asr +resolver context. +The specific operational details for each of them are described below. +All functions return a handle to an internal query, or NULL if they could +not allocate the necessary resources to initiate the query. +All other errors (especially invalid parameters) are reported when calling +.Fn asr_run . +They usually have the same interface as an existing resolver function, with +an additional +.Ar asr +argument, which specifies the context to use for this request. +For now, the argument must always be NULL, which will use the default +context for the current thread. +.Pp +The +.Fn res_send_async , +.Fn res_query_async +and +.Fn res_search_async +functions are asynchronous versions of the standard libc resolver routines. +Their interface is very similar, except that the response buffer is always +allocated internally. +The return value is found upon completion in the +.Fa ar_datalen +member of the response structure. +In addition, the +.Fa ar_ns +structure contains the address of the DNS server that sent the response, +.Fa ar_rcode +contains the code returned by the server in the DNS response packet, and +.Fa ar_count +contains the number of answers in the packet. +If a response is received it is placed in a newly allocated buffer +and returned as +.Fa ar_data +member. +This buffer must be freed by the caller. +On error, the +.Fa ar_errno +and +.Fa ar_h_errno +members are set accordingly. +.Pp +The +.Fn getrrsetbyname_async +function is an asynchronous version of +.Xr getrrsetbyname 3 . +Upon completion, the return code is found in +.Fa ar_rrset_errno +and the address to the newly allocated result set is set in +.Fa ar_rrsetinfo . +As for the blocking function, it must be freed by calling +.Xr freerrset 3 . +.Pp +The +.Fn gethostbyname_async , +.Fn gethostbyname2_async +and +.Fn gethostbyaddr_async +functions provide an asynchronous version of the network host entry functions. +Upon completion, +.Ar ar_h_errno +is set and the resulting hostent address, if found, is set +in the +.Ar ar_hostent +field. +Note that unlike their blocking counterparts, these functions always return a +pointer to newly allocated memory, which must be released by the caller using +.Xr free 3 . +.Pp +Similarly, the +.Fn getnetbyname_async +and +.Fn getnetbyaddr_async +functions provide an asynchronous version of the network entry functions. +Upon completion, +.Ar ar_h_errno +is set and the resulting netent address, if found, is set +in the +.Ar ar_netent +field. +The memory there is also allocated for the request, and it must be freed by +.Xr free 3 . +.Pp +The +.Fn getaddrinfo_async +function is an asynchronous version of the +.Xr getaddrinfo 3 +call. +It provides a chain of addrinfo structures with all valid combinations of +socket address for the given +.Fa hostname , +.Fa servname +and +.Fa hints . +Those three parameters have the same meaning as for the blocking counterpart. +Upon completion the return code is set in +.Fa ar_gai_errno . +The +.Fa ar_errno +member may also be set. +On success, the +.Fa ar_addrinfo +member points to a newly allocated list of addrinfo. +This list must be freed with +.Xr freeaddrinfo 3 . +.Sh WORKING WITH THREADS +This implementation of the asynchronous resolver interface is thread-safe +and lock-free internally, but the following restriction applies: +Two different threads must not create queries on the same context or +run queries originating from the same context at the same time. +If they want to do that, all calls must be protected by a mutex around +that context. +.Pp +It is generally not a problem since the main point of the asynchronous +resolver is to multiplex queries within a single thread of control, +so sharing a resolver among threads is not useful. +.Sh SEE ALSO +.Xr getaddrinfo 3 , +.Xr gethostbyname 3 , +.Xr getnameinfo 3 , +.Xr getnetbyname 3 , +.Xr getrrsetbyname 3 , +.Xr res_send 3 , +.Xr resolv.conf 5 diff --git a/openbsd-compat/libasr/asr_utils.c b/openbsd-compat/libasr/asr_utils.c new file mode 100644 index 00000000..e3d24c93 --- /dev/null +++ b/openbsd-compat/libasr/asr_utils.c @@ -0,0 +1,574 @@ +/* $OpenBSD: asr_utils.c,v 1.18 2017/09/23 20:55:06 jca Exp $ */ +/* + * Copyright (c) 2009-2012 Eric Faurot <eric@faurot.net> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#ifdef HAVE_ARPA_NAMESER_COMPAT_H +#include <arpa/nameser_compat.h> +#endif +#include <netdb.h> + +#include <asr.h> +#include <ctype.h> +#include <errno.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "asr_private.h" + +static int dname_check_label(const char *, size_t); +static ssize_t dname_expand(const unsigned char *, size_t, size_t, size_t *, + char *, size_t); + +static int unpack_data(struct asr_unpack *, void *, size_t); +static int unpack_u16(struct asr_unpack *, uint16_t *); +static int unpack_u32(struct asr_unpack *, uint32_t *); +static int unpack_inaddr(struct asr_unpack *, struct in_addr *); +static int unpack_in6addr(struct asr_unpack *, struct in6_addr *); +static int unpack_dname(struct asr_unpack *, char *, size_t); + +static int pack_data(struct asr_pack *, const void *, size_t); +static int pack_u16(struct asr_pack *, uint16_t); +static int pack_dname(struct asr_pack *, const char *); + +static int +dname_check_label(const char *s, size_t l) +{ + if (l == 0 || l > 63) + return (-1); + + return (0); +} + +ssize_t +_asr_dname_from_fqdn(const char *str, char *dst, size_t max) +{ + ssize_t res; + size_t l, n; + char *d; + + res = 0; + + /* special case: the root domain */ + if (str[0] == '.') { + if (str[1] != '\0') + return (-1); + if (dst && max >= 1) + *dst = '\0'; + return (1); + } + + for (; *str; str = d + 1) { + + d = strchr(str, '.'); + if (d == NULL || d == str) + return (-1); + + l = (d - str); + + if (dname_check_label(str, l) == -1) + return (-1); + + res += l + 1; + + if (dst) { + *dst++ = l; + max -= 1; + n = (l > max) ? max : l; + memmove(dst, str, n); + max -= n; + if (max == 0) + dst = NULL; + else + dst += n; + } + } + + if (dst) + *dst++ = '\0'; + + return (res + 1); +} + +static ssize_t +dname_expand(const unsigned char *data, size_t len, size_t offset, + size_t *newoffset, char *dst, size_t max) +{ + size_t n, count, end, ptr, start; + ssize_t res; + + if (offset >= len) + return (-1); + + res = 0; + end = start = offset; + + for (; (n = data[offset]); ) { + if ((n & 0xc0) == 0xc0) { + if (offset + 2 > len) + return (-1); + ptr = 256 * (n & ~0xc0) + data[offset + 1]; + if (ptr >= start) + return (-1); + if (end < offset + 2) + end = offset + 2; + offset = start = ptr; + continue; + } + if (offset + n + 1 > len) + return (-1); + + if (dname_check_label(data + offset + 1, n) == -1) + return (-1); + + /* copy n + at offset+1 */ + if (dst != NULL && max != 0) { + count = (max < n + 1) ? (max) : (n + 1); + memmove(dst, data + offset, count); + dst += count; + max -= count; + } + res += n + 1; + offset += n + 1; + if (end < offset) + end = offset; + } + if (end < offset + 1) + end = offset + 1; + + if (dst != NULL && max != 0) + dst[0] = 0; + if (newoffset) + *newoffset = end; + return (res + 1); +} + +void +_asr_pack_init(struct asr_pack *pack, char *buf, size_t len) +{ + pack->buf = buf; + pack->len = len; + pack->offset = 0; + pack->err = 0; +} + +void +_asr_unpack_init(struct asr_unpack *unpack, const char *buf, size_t len) +{ + unpack->buf = buf; + unpack->len = len; + unpack->offset = 0; + unpack->err = 0; +} + +static int +unpack_data(struct asr_unpack *p, void *data, size_t len) +{ + if (p->err) + return (-1); + + if (p->len - p->offset < len) { + p->err = EOVERFLOW; + return (-1); + } + + memmove(data, p->buf + p->offset, len); + p->offset += len; + + return (0); +} + +static int +unpack_u16(struct asr_unpack *p, uint16_t *u16) +{ + if (unpack_data(p, u16, 2) == -1) + return (-1); + + *u16 = ntohs(*u16); + + return (0); +} + +static int +unpack_u32(struct asr_unpack *p, uint32_t *u32) +{ + if (unpack_data(p, u32, 4) == -1) + return (-1); + + *u32 = ntohl(*u32); + + return (0); +} + +static int +unpack_inaddr(struct asr_unpack *p, struct in_addr *a) +{ + return (unpack_data(p, a, 4)); +} + +static int +unpack_in6addr(struct asr_unpack *p, struct in6_addr *a6) +{ + return (unpack_data(p, a6, 16)); +} + +static int +unpack_dname(struct asr_unpack *p, char *dst, size_t max) +{ + ssize_t e; + + if (p->err) + return (-1); + + e = dname_expand(p->buf, p->len, p->offset, &p->offset, dst, max); + if (e == -1) { + p->err = EINVAL; + return (-1); + } + if (e < 0 || e > MAXDNAME) { + p->err = ERANGE; + return (-1); + } + + return (0); +} + +int +_asr_unpack_header(struct asr_unpack *p, struct asr_dns_header *h) +{ + if (unpack_data(p, h, HFIXEDSZ) == -1) + return (-1); + + h->flags = ntohs(h->flags); + h->qdcount = ntohs(h->qdcount); + h->ancount = ntohs(h->ancount); + h->nscount = ntohs(h->nscount); + h->arcount = ntohs(h->arcount); + + return (0); +} + +int +_asr_unpack_query(struct asr_unpack *p, struct asr_dns_query *q) +{ + unpack_dname(p, q->q_dname, sizeof(q->q_dname)); + unpack_u16(p, &q->q_type); + unpack_u16(p, &q->q_class); + + return (p->err) ? (-1) : (0); +} + +int +_asr_unpack_rr(struct asr_unpack *p, struct asr_dns_rr *rr) +{ + uint16_t rdlen; + size_t save_offset; + + unpack_dname(p, rr->rr_dname, sizeof(rr->rr_dname)); + unpack_u16(p, &rr->rr_type); + unpack_u16(p, &rr->rr_class); + unpack_u32(p, &rr->rr_ttl); + unpack_u16(p, &rdlen); + + if (p->err) + return (-1); + + if (p->len - p->offset < rdlen) { + p->err = EOVERFLOW; + return (-1); + } + + save_offset = p->offset; + + switch (rr->rr_type) { + + case T_CNAME: + unpack_dname(p, rr->rr.cname.cname, sizeof(rr->rr.cname.cname)); + break; + + case T_MX: + unpack_u16(p, &rr->rr.mx.preference); + unpack_dname(p, rr->rr.mx.exchange, sizeof(rr->rr.mx.exchange)); + break; + + case T_NS: + unpack_dname(p, rr->rr.ns.nsname, sizeof(rr->rr.ns.nsname)); + break; + + case T_PTR: + unpack_dname(p, rr->rr.ptr.ptrname, sizeof(rr->rr.ptr.ptrname)); + break; + + case T_SOA: + unpack_dname(p, rr->rr.soa.mname, sizeof(rr->rr.soa.mname)); + unpack_dname(p, rr->rr.soa.rname, sizeof(rr->rr.soa.rname)); + unpack_u32(p, &rr->rr.soa.serial); + unpack_u32(p, &rr->rr.soa.refresh); + unpack_u32(p, &rr->rr.soa.retry); + unpack_u32(p, &rr->rr.soa.expire); + unpack_u32(p, &rr->rr.soa.minimum); + break; + + case T_A: + if (rr->rr_class != C_IN) + goto other; + unpack_inaddr(p, &rr->rr.in_a.addr); + break; + + case T_AAAA: + if (rr->rr_class != C_IN) + goto other; + unpack_in6addr(p, &rr->rr.in_aaaa.addr6); + break; + default: + other: + rr->rr.other.rdata = p->buf + p->offset; + rr->rr.other.rdlen = rdlen; + p->offset += rdlen; + } + + if (p->err) + return (-1); + + /* make sure that the advertised rdlen is really ok */ + if (p->offset - save_offset != rdlen) + p->err = EINVAL; + + return (p->err) ? (-1) : (0); +} + +static int +pack_data(struct asr_pack *p, const void *data, size_t len) +{ + if (p->err) + return (-1); + + if (p->len < p->offset + len) { + p->err = EOVERFLOW; + return (-1); + } + + memmove(p->buf + p->offset, data, len); + p->offset += len; + + return (0); +} + +static int +pack_u16(struct asr_pack *p, uint16_t v) +{ + v = htons(v); + + return (pack_data(p, &v, 2)); +} + +static int +pack_dname(struct asr_pack *p, const char *dname) +{ + /* dname compression would be nice to have here. + * need additionnal context. + */ + return (pack_data(p, dname, strlen(dname) + 1)); +} + +int +_asr_pack_header(struct asr_pack *p, const struct asr_dns_header *h) +{ + struct asr_dns_header c; + + c.id = h->id; + c.flags = htons(h->flags); + c.qdcount = htons(h->qdcount); + c.ancount = htons(h->ancount); + c.nscount = htons(h->nscount); + c.arcount = htons(h->arcount); + + return (pack_data(p, &c, HFIXEDSZ)); +} + +int +_asr_pack_query(struct asr_pack *p, uint16_t type, uint16_t class, const char *dname) +{ + pack_dname(p, dname); + pack_u16(p, type); + pack_u16(p, class); + + return (p->err) ? (-1) : (0); +} + +int +_asr_pack_edns0(struct asr_pack *p, uint16_t pktsz, int dnssec_do) +{ + DPRINT("asr EDNS0 pktsz:%hu dnssec:%s\n", pktsz, + dnssec_do ? "yes" : "no"); + + pack_dname(p, ""); /* root */ + pack_u16(p, T_OPT); /* OPT */ + pack_u16(p, pktsz); /* UDP payload size */ + + /* extended RCODE and flags */ + pack_u16(p, 0); + pack_u16(p, dnssec_do ? DNS_MESSAGEEXTFLAG_DO : 0); + + pack_u16(p, 0); /* RDATA len */ + + return (p->err) ? (-1) : (0); +} + +int +_asr_sockaddr_from_str(struct sockaddr *sa, int family, const char *str) +{ + struct in_addr ina; + struct in6_addr in6a; + struct sockaddr_in *sin; + struct sockaddr_in6 *sin6; + char *cp, *str2; + const char *errstr; + + switch (family) { + case PF_UNSPEC: + if (_asr_sockaddr_from_str(sa, PF_INET, str) == 0) + return (0); + return _asr_sockaddr_from_str(sa, PF_INET6, str); + + case PF_INET: + if (inet_pton(PF_INET, str, &ina) != 1) + return (-1); + + sin = (struct sockaddr_in *)sa; + memset(sin, 0, sizeof *sin); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + sin->sin_len = sizeof(struct sockaddr_in); +#endif + sin->sin_family = PF_INET; + sin->sin_addr.s_addr = ina.s_addr; + return (0); + + case PF_INET6: + cp = strchr(str, SCOPE_DELIMITER); + if (cp) { + str2 = strdup(str); + if (str2 == NULL) + return (-1); + str2[cp - str] = '\0'; + if (inet_pton(PF_INET6, str2, &in6a) != 1) { + free(str2); + return (-1); + } + cp++; + free(str2); + } else if (inet_pton(PF_INET6, str, &in6a) != 1) + return (-1); + + sin6 = (struct sockaddr_in6 *)sa; + memset(sin6, 0, sizeof *sin6); +#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN + sin6->sin6_len = sizeof(struct sockaddr_in6); +#endif + sin6->sin6_family = PF_INET6; + sin6->sin6_addr = in6a; + + if (cp == NULL) + return (0); + + if (IN6_IS_ADDR_LINKLOCAL(&in6a) || + IN6_IS_ADDR_MC_LINKLOCAL(&in6a) || + IN6_IS_ADDR_MC_NODELOCAL(&in6a)) + if ((sin6->sin6_scope_id = if_nametoindex(cp))) + return (0); + + sin6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX, &errstr); + if (errstr) + return (-1); + return (0); + + default: + break; + } + + return (-1); +} + +ssize_t +_asr_addr_as_fqdn(const char *addr, int family, char *dst, size_t max) +{ + const struct in6_addr *in6_addr; + in_addr_t in_addr; + + switch (family) { + case AF_INET: + in_addr = ntohl(*((const in_addr_t *)addr)); + snprintf(dst, max, + "%d.%d.%d.%d.in-addr.arpa.", + in_addr & 0xff, + (in_addr >> 8) & 0xff, + (in_addr >> 16) & 0xff, + (in_addr >> 24) & 0xff); + break; + case AF_INET6: + in6_addr = (const struct in6_addr *)addr; + snprintf(dst, max, + "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." + "%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x." + "ip6.arpa.", + in6_addr->s6_addr[15] & 0xf, + (in6_addr->s6_addr[15] >> 4) & 0xf, + in6_addr->s6_addr[14] & 0xf, + (in6_addr->s6_addr[14] >> 4) & 0xf, + in6_addr->s6_addr[13] & 0xf, + (in6_addr->s6_addr[13] >> 4) & 0xf, + in6_addr->s6_addr[12] & 0xf, + (in6_addr->s6_addr[12] >> 4) & 0xf, + in6_addr->s6_addr[11] & 0xf, + (in6_addr->s6_addr[11] >> 4) & 0xf, + in6_addr->s6_addr[10] & 0xf, + (in6_addr->s6_addr[10] >> 4) & 0xf, + in6_addr->s6_addr[9] & 0xf, + (in6_addr->s6_addr[9] >> 4) & 0xf, + in6_addr->s6_addr[8] & 0xf, + (in6_addr->s6_addr[8] >> 4) & 0xf, + in6_addr->s6_addr[7] & 0xf, + (in6_addr->s6_addr[7] >> 4) & 0xf, + in6_addr->s6_addr[6] & 0xf, + (in6_addr->s6_addr[6] >> 4) & 0xf, + in6_addr->s6_addr[5] & 0xf, + (in6_addr->s6_addr[5] >> 4) & 0xf, + in6_addr->s6_addr[4] & 0xf, + (in6_addr->s6_addr[4] >> 4) & 0xf, + in6_addr->s6_addr[3] & 0xf, + (in6_addr->s6_addr[3] >> 4) & 0xf, + in6_addr->s6_addr[2] & 0xf, + (in6_addr->s6_addr[2] >> 4) & 0xf, + in6_addr->s6_addr[1] & 0xf, + (in6_addr->s6_addr[1] >> 4) & 0xf, + in6_addr->s6_addr[0] & 0xf, + (in6_addr->s6_addr[0] >> 4) & 0xf); + break; + default: + return (-1); + } + return (0); +} diff --git a/openbsd-compat/libasr/getaddrinfo.c b/openbsd-compat/libasr/getaddrinfo.c new file mode 100644 index 00000000..37fe2d4d --- /dev/null +++ b/openbsd-compat/libasr/getaddrinfo.c @@ -0,0 +1,55 @@ +/* $OpenBSD: getaddrinfo.c,v 1.9 2015/10/08 14:08:44 eric Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> + +#include <asr.h> +#include <errno.h> +#include <resolv.h> + +int +getaddrinfo(const char *hostname, const char *servname, + const struct addrinfo *hints, struct addrinfo **res) +{ + struct asr_query *as; + struct asr_result ar; + int saved_errno = errno; + + if (hints == NULL || (hints->ai_flags & AI_NUMERICHOST) == 0) + res_init(); + + as = getaddrinfo_async(hostname, servname, hints, NULL); + if (as == NULL) { + if (errno == ENOMEM) { + errno = saved_errno; + return (EAI_MEMORY); + } + return (EAI_SYSTEM); + } + + asr_run_sync(as, &ar); + + *res = ar.ar_addrinfo; + if (ar.ar_gai_errno == EAI_SYSTEM) + errno = ar.ar_errno; + + return (ar.ar_gai_errno); +} +DEF_WEAK(getaddrinfo); diff --git a/openbsd-compat/libasr/getaddrinfo_async.c b/openbsd-compat/libasr/getaddrinfo_async.c new file mode 100644 index 00000000..1fd44ff7 --- /dev/null +++ b/openbsd-compat/libasr/getaddrinfo_async.c @@ -0,0 +1,756 @@ +/* $OpenBSD: getaddrinfo_async.c,v 1.56 2018/11/03 09:13:24 eric Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#ifdef HAVE_ARPA_NAMESER_COMPAT_H +#include <arpa/nameser_compat.h> +#endif +#include <net/if.h> +#include <netdb.h> + +#include <asr.h> +#include <errno.h> +#include <ifaddrs.h> +#include <resolv.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> + +#include "asr_private.h" + +struct match { + int family; + int socktype; + int protocol; +}; + +static int getaddrinfo_async_run(struct asr_query *, struct asr_result *); +static int get_port(const char *, const char *, int); +static int iter_family(struct asr_query *, int); +static int addrinfo_add(struct asr_query *, const struct sockaddr *, const char *); +static int addrinfo_from_file(struct asr_query *, int, FILE *); +static int addrinfo_from_pkt(struct asr_query *, char *, size_t); +static int addrconfig_setup(struct asr_query *); + +static const struct match matches[] = { + { PF_INET, SOCK_DGRAM, IPPROTO_UDP }, + { PF_INET, SOCK_STREAM, IPPROTO_TCP }, + { PF_INET, SOCK_RAW, 0 }, + { PF_INET6, SOCK_DGRAM, IPPROTO_UDP }, + { PF_INET6, SOCK_STREAM, IPPROTO_TCP }, + { PF_INET6, SOCK_RAW, 0 }, + { -1, 0, 0, }, +}; + +#define MATCH_FAMILY(a, b) ((a) == matches[(b)].family || (a) == PF_UNSPEC) +#define MATCH_PROTO(a, b) ((a) == matches[(b)].protocol || (a) == 0 || matches[(b)].protocol == 0) +/* Do not match SOCK_RAW unless explicitly specified */ +#define MATCH_SOCKTYPE(a, b) ((a) == matches[(b)].socktype || ((a) == 0 && \ + matches[(b)].socktype != SOCK_RAW)) + +enum { + DOM_INIT, + DOM_DOMAIN, + DOM_DONE +}; + +struct asr_query * +getaddrinfo_async(const char *hostname, const char *servname, + const struct addrinfo *hints, void *asr) +{ + struct asr_ctx *ac; + struct asr_query *as; + + if (hints == NULL || (hints->ai_flags & AI_NUMERICHOST) == 0) + ac = _asr_use_resolver(asr); + else + ac = _asr_no_resolver(); + if ((as = _asr_async_new(ac, ASR_GETADDRINFO)) == NULL) + goto abort; /* errno set */ + as->as_run = getaddrinfo_async_run; + + if (hostname) { + if ((as->as.ai.hostname = strdup(hostname)) == NULL) + goto abort; /* errno set */ + } + if (servname && (as->as.ai.servname = strdup(servname)) == NULL) + goto abort; /* errno set */ + if (hints) + memmove(&as->as.ai.hints, hints, sizeof *hints); + else { + memset(&as->as.ai.hints, 0, sizeof as->as.ai.hints); + as->as.ai.hints.ai_family = PF_UNSPEC; + as->as.ai.hints.ai_flags = AI_ADDRCONFIG; + } + + _asr_ctx_unref(ac); + return (as); + abort: + if (as) + _asr_async_free(as); + _asr_ctx_unref(ac); + return (NULL); +} +DEF_WEAK(getaddrinfo_async); + +static int +getaddrinfo_async_run(struct asr_query *as, struct asr_result *ar) +{ + char fqdn[MAXDNAME]; + const char *str; + struct addrinfo *ai; + int i, family, r; + FILE *f; + union { + struct sockaddr sa; + struct sockaddr_in sain; + struct sockaddr_in6 sain6; + } sa; + + next: + switch (as->as_state) { + + case ASR_STATE_INIT: + + /* + * First, make sure the parameters are valid. + */ + + as->as_count = 0; + + if (as->as.ai.hostname == NULL && + as->as.ai.servname == NULL) { + ar->ar_gai_errno = EAI_NONAME; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (as->as.ai.hostname && as->as.ai.hostname[0] == '\0') { + ar->ar_gai_errno = EAI_NODATA; + async_set_state(as, ASR_STATE_HALT); + break; + } + + ai = &as->as.ai.hints; + +#ifdef EAI_BADHINTS + if (ai->ai_addrlen || + ai->ai_canonname || + ai->ai_addr || + ai->ai_next) { + ar->ar_gai_errno = EAI_BADHINTS; + async_set_state(as, ASR_STATE_HALT); + break; + } +#endif + + if (ai->ai_flags & ~AI_MASK || + (ai->ai_flags & AI_CANONNAME && ai->ai_flags & AI_FQDN)) { + ar->ar_gai_errno = EAI_BADFLAGS; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (ai->ai_family != PF_UNSPEC && + ai->ai_family != PF_INET && + ai->ai_family != PF_INET6) { + ar->ar_gai_errno = EAI_FAMILY; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (ai->ai_socktype && + ai->ai_socktype != SOCK_DGRAM && + ai->ai_socktype != SOCK_STREAM && + ai->ai_socktype != SOCK_RAW) { + ar->ar_gai_errno = EAI_SOCKTYPE; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (ai->ai_socktype == SOCK_RAW && + get_port(as->as.ai.servname, NULL, 1) != 0) { + ar->ar_gai_errno = EAI_SERVICE; + async_set_state(as, ASR_STATE_HALT); + break; + } + + /* Restrict result set to configured address families */ + if (ai->ai_flags & AI_ADDRCONFIG) { + if (addrconfig_setup(as) == -1) { + ar->ar_errno = errno; + ar->ar_gai_errno = EAI_SYSTEM; + async_set_state(as, ASR_STATE_HALT); + break; + } + } + + /* Make sure there is at least a valid combination */ + for (i = 0; matches[i].family != -1; i++) + if (MATCH_FAMILY(ai->ai_family, i) && + MATCH_SOCKTYPE(ai->ai_socktype, i) && + MATCH_PROTO(ai->ai_protocol, i)) + break; + if (matches[i].family == -1) { +#ifdef EAI_BADHINTS + ar->ar_gai_errno = EAI_BADHINTS; +#else + ar->ar_gai_errno = EAI_FAIL; +#endif + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_UDP) + as->as.ai.port_udp = get_port(as->as.ai.servname, "udp", + as->as.ai.hints.ai_flags & AI_NUMERICSERV); + if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_TCP) + as->as.ai.port_tcp = get_port(as->as.ai.servname, "tcp", + as->as.ai.hints.ai_flags & AI_NUMERICSERV); + if (as->as.ai.port_tcp == -2 || as->as.ai.port_udp == -2 || + (as->as.ai.port_tcp == -1 && as->as.ai.port_udp == -1) || + (ai->ai_protocol && (as->as.ai.port_udp == -1 || + as->as.ai.port_tcp == -1))) { + ar->ar_gai_errno = EAI_SERVICE; + async_set_state(as, ASR_STATE_HALT); + break; + } + + ar->ar_gai_errno = 0; + + /* If hostname is NULL, use local address */ + if (as->as.ai.hostname == NULL) { + for (family = iter_family(as, 1); + family != -1; + family = iter_family(as, 0)) { + /* + * We could use statically built sockaddrs for + * those, rather than parsing over and over. + */ + if (family == PF_INET) + str = (ai->ai_flags & AI_PASSIVE) ? \ + "0.0.0.0" : "127.0.0.1"; + else /* PF_INET6 */ + str = (ai->ai_flags & AI_PASSIVE) ? \ + "::" : "::1"; + /* This can't fail */ + _asr_sockaddr_from_str(&sa.sa, family, str); + if ((r = addrinfo_add(as, &sa.sa, NULL))) { + ar->ar_gai_errno = r; + break; + } + } + if (ar->ar_gai_errno == 0 && as->as_count == 0) { + ar->ar_gai_errno = EAI_NODATA; + } + async_set_state(as, ASR_STATE_HALT); + break; + } + + /* Try numeric addresses first */ + for (family = iter_family(as, 1); + family != -1; + family = iter_family(as, 0)) { + + if (_asr_sockaddr_from_str(&sa.sa, family, + as->as.ai.hostname) == -1) + continue; + + if ((r = addrinfo_add(as, &sa.sa, NULL))) + ar->ar_gai_errno = r; + break; + } + if (ar->ar_gai_errno || as->as_count) { + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (ai->ai_flags & AI_NUMERICHOST) { + ar->ar_gai_errno = EAI_NONAME; + async_set_state(as, ASR_STATE_HALT); + break; + } + + async_set_state(as, ASR_STATE_NEXT_DB); + break; + + case ASR_STATE_NEXT_DB: + if (_asr_iter_db(as) == -1) { + async_set_state(as, ASR_STATE_NOT_FOUND); + break; + } + as->as_family_idx = 0; + async_set_state(as, ASR_STATE_SAME_DB); + break; + + case ASR_STATE_NEXT_FAMILY: + as->as_family_idx += 1; + if (as->as.ai.hints.ai_family != AF_UNSPEC || + AS_FAMILY(as) == -1) { + /* The family was specified, or we have tried all + * families with this DB. + */ + if (as->as_count) { + ar->ar_gai_errno = 0; + async_set_state(as, ASR_STATE_HALT); + } else + async_set_state(as, ASR_STATE_NEXT_DOMAIN); + break; + } + async_set_state(as, ASR_STATE_SAME_DB); + break; + + case ASR_STATE_NEXT_DOMAIN: + /* domain search is only for dns */ + if (AS_DB(as) != ASR_DB_DNS) { + async_set_state(as, ASR_STATE_NEXT_DB); + break; + } + as->as_family_idx = 0; + + free(as->as.ai.fqdn); + as->as.ai.fqdn = NULL; + r = _asr_iter_domain(as, as->as.ai.hostname, fqdn, sizeof(fqdn)); + if (r == -1) { + async_set_state(as, ASR_STATE_NEXT_DB); + break; + } + if (r == 0) { + ar->ar_gai_errno = EAI_FAIL; + async_set_state(as, ASR_STATE_HALT); + break; + } + as->as.ai.fqdn = strdup(fqdn); + if (as->as.ai.fqdn == NULL) { + ar->ar_gai_errno = EAI_MEMORY; + async_set_state(as, ASR_STATE_HALT); + break; + } + + async_set_state(as, ASR_STATE_SAME_DB); + break; + + case ASR_STATE_SAME_DB: + /* query the current DB again */ + switch (AS_DB(as)) { + case ASR_DB_DNS: + if (as->as.ai.fqdn == NULL) { + /* First try, initialize domain iteration */ + as->as_dom_flags = 0; + as->as_dom_step = DOM_INIT; + async_set_state(as, ASR_STATE_NEXT_DOMAIN); + break; + } + + family = (as->as.ai.hints.ai_family == AF_UNSPEC) ? + AS_FAMILY(as) : as->as.ai.hints.ai_family; + + if (family == AF_INET && + as->as_flags & ASYNC_NO_INET) { + async_set_state(as, ASR_STATE_NEXT_FAMILY); + break; + } else if (family == AF_INET6 && + as->as_flags & ASYNC_NO_INET6) { + async_set_state(as, ASR_STATE_NEXT_FAMILY); + break; + } + + as->as_subq = _res_query_async_ctx(as->as.ai.fqdn, + C_IN, (family == AF_INET6) ? T_AAAA : T_A, + as->as_ctx); + + if (as->as_subq == NULL) { + if (errno == ENOMEM) + ar->ar_gai_errno = EAI_MEMORY; + else + ar->ar_gai_errno = EAI_FAIL; + async_set_state(as, ASR_STATE_HALT); + break; + } + async_set_state(as, ASR_STATE_SUBQUERY); + break; + + case ASR_DB_FILE: + f = fopen(_PATH_HOSTS, "re"); + if (f == NULL) { + async_set_state(as, ASR_STATE_NEXT_DB); + break; + } + family = (as->as.ai.hints.ai_family == AF_UNSPEC) ? + AS_FAMILY(as) : as->as.ai.hints.ai_family; + + r = addrinfo_from_file(as, family, f); + if (r == -1) { + if (errno == ENOMEM) + ar->ar_gai_errno = EAI_MEMORY; + else + ar->ar_gai_errno = EAI_FAIL; + async_set_state(as, ASR_STATE_HALT); + } else + async_set_state(as, ASR_STATE_NEXT_FAMILY); + fclose(f); + break; + + default: + async_set_state(as, ASR_STATE_NEXT_DB); + } + break; + + case ASR_STATE_SUBQUERY: + if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND) + return (ASYNC_COND); + + as->as_subq = NULL; + + if (ar->ar_datalen == -1) { + async_set_state(as, ASR_STATE_NEXT_FAMILY); + break; + } + + r = addrinfo_from_pkt(as, ar->ar_data, ar->ar_datalen); + if (r == -1) { + if (errno == ENOMEM) + ar->ar_gai_errno = EAI_MEMORY; + else + ar->ar_gai_errno = EAI_FAIL; + async_set_state(as, ASR_STATE_HALT); + } else + async_set_state(as, ASR_STATE_NEXT_FAMILY); + free(ar->ar_data); + break; + + case ASR_STATE_NOT_FOUND: + /* No result found. Maybe we can try again. */ + if (as->as_flags & ASYNC_AGAIN) + ar->ar_gai_errno = EAI_AGAIN; + else + ar->ar_gai_errno = EAI_NODATA; + async_set_state(as, ASR_STATE_HALT); + break; + + case ASR_STATE_HALT: + if (ar->ar_gai_errno == 0) { + ar->ar_count = as->as_count; + ar->ar_addrinfo = as->as.ai.aifirst; + as->as.ai.aifirst = NULL; + } else { + ar->ar_count = 0; + ar->ar_addrinfo = NULL; + } + return (ASYNC_DONE); + + default: + ar->ar_errno = EOPNOTSUPP; + ar->ar_gai_errno = EAI_SYSTEM; + async_set_state(as, ASR_STATE_HALT); + break; + } + goto next; +} + +/* + * Retreive the port number for the service name "servname" and + * the protocol "proto". + */ +static int +get_port(const char *servname, const char *proto, int numonly) +{ +#ifdef HAVE_GETSERVBYNAME_R_4_ARGS + struct servent se; +#endif +#ifdef HAVE_STRUCT_SERVENT_DATA + struct servent_data sed; +#endif + int port; + const char *e; + + if (servname == NULL) + return (0); + + e = NULL; + port = strtonum(servname, 0, USHRT_MAX, &e); + if (e == NULL) + return (port); + if (errno == ERANGE) + return (-2); /* invalid */ + if (numonly) + return (-2); + + port = -1; +#ifdef HAVE_STRUCT_SERVENT_DATA + memset(&sed, 0, sizeof(sed)); +#endif +#ifdef HAVE_GETSERVBYNAME_R_4_ARGS + if (getservbyname_r(servname, proto, &se, &sed) != -1) + port = ntohs(se.s_port); +#endif +#ifdef HAVE_ENDSERVENT_R + endservent_r(&sed); +#endif + + return (port); +} + +/* + * Iterate over the address families that are to be queried. Use the + * list on the async context, unless a specific family was given in hints. + */ +static int +iter_family(struct asr_query *as, int first) +{ + if (first) { + as->as_family_idx = 0; + if (as->as.ai.hints.ai_family != PF_UNSPEC) + return as->as.ai.hints.ai_family; + return AS_FAMILY(as); + } + + if (as->as.ai.hints.ai_family != PF_UNSPEC) + return (-1); + + as->as_family_idx++; + + return AS_FAMILY(as); +} + +/* + * Use the sockaddr at "sa" to extend the result list on the "as" context, + * with the specified canonical name "cname". This function adds one + * entry per protocol/socktype match. + */ +static int +addrinfo_add(struct asr_query *as, const struct sockaddr *sa, const char *cname) +{ + struct addrinfo *ai; + int i, port, proto; + + for (i = 0; matches[i].family != -1; i++) { + if (matches[i].family != sa->sa_family || + !MATCH_SOCKTYPE(as->as.ai.hints.ai_socktype, i) || + !MATCH_PROTO(as->as.ai.hints.ai_protocol, i)) + continue; + + proto = as->as.ai.hints.ai_protocol; + if (!proto) + proto = matches[i].protocol; + + if (proto == IPPROTO_TCP) + port = as->as.ai.port_tcp; + else if (proto == IPPROTO_UDP) + port = as->as.ai.port_udp; + else + port = 0; + + /* servname specified, but not defined for this protocol */ + if (port == -1) + continue; + + ai = calloc(1, sizeof(*ai) + SA_LEN(sa)); + if (ai == NULL) + return (EAI_MEMORY); + ai->ai_family = sa->sa_family; + ai->ai_socktype = matches[i].socktype; + ai->ai_protocol = proto; + ai->ai_flags = as->as.ai.hints.ai_flags; + ai->ai_addrlen = SA_LEN(sa); + ai->ai_addr = (void *)(ai + 1); + if (cname && + as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) { + if ((ai->ai_canonname = strdup(cname)) == NULL) { + free(ai); + return (EAI_MEMORY); + } + } + memmove(ai->ai_addr, sa, SA_LEN(sa)); + if (sa->sa_family == PF_INET) + ((struct sockaddr_in *)ai->ai_addr)->sin_port = + htons(port); + else if (sa->sa_family == PF_INET6) + ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = + htons(port); + + if (as->as.ai.aifirst == NULL) + as->as.ai.aifirst = ai; + if (as->as.ai.ailast) + as->as.ai.ailast->ai_next = ai; + as->as.ai.ailast = ai; + as->as_count += 1; + } + + return (0); +} + +void +asr_freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *ai_next; + + while (ai) { + ai_next = ai->ai_next; + if (ai->ai_canonname) + free(ai->ai_canonname); + free(ai); + ai = ai_next; + } +} + +static int +addrinfo_from_file(struct asr_query *as, int family, FILE *f) +{ + char *tokens[MAXTOKEN], *c, buf[ASR_BUFSIZ + 1]; + int n, i; + union { + struct sockaddr sa; + struct sockaddr_in sain; + struct sockaddr_in6 sain6; + } u; + + for (;;) { + n = _asr_parse_namedb_line(f, tokens, MAXTOKEN, buf, sizeof(buf)); + if (n == -1) + break; /* ignore errors reading the file */ + + for (i = 1; i < n; i++) { + if (strcasecmp(as->as.ai.hostname, tokens[i])) + continue; + if (_asr_sockaddr_from_str(&u.sa, family, tokens[0]) == -1) + continue; + break; + } + if (i == n) + continue; + + if (as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) + c = tokens[1]; + else + c = NULL; + + if (addrinfo_add(as, &u.sa, c)) + return (-1); /* errno set */ + } + return (0); +} + +static int +addrinfo_from_pkt(struct asr_query *as, char *pkt, size_t pktlen) +{ + struct asr_unpack p; + struct asr_dns_header h; + struct asr_dns_query q; + struct asr_dns_rr rr; + int i; + union { + struct sockaddr sa; + struct sockaddr_in sain; + struct sockaddr_in6 sain6; + } u; + char buf[MAXDNAME], *c; + + _asr_unpack_init(&p, pkt, pktlen); + _asr_unpack_header(&p, &h); + for (; h.qdcount; h.qdcount--) + _asr_unpack_query(&p, &q); + + for (i = 0; i < h.ancount; i++) { + _asr_unpack_rr(&p, &rr); + if (rr.rr_type != q.q_type || + rr.rr_class != q.q_class) + continue; + + memset(&u, 0, sizeof u); + if (rr.rr_type == T_A) { +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN + u.sain.sin_len = sizeof u.sain; +#endif + u.sain.sin_family = AF_INET; + u.sain.sin_addr = rr.rr.in_a.addr; + u.sain.sin_port = 0; + } else if (rr.rr_type == T_AAAA) { +#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN + u.sain6.sin6_len = sizeof u.sain6; +#endif + u.sain6.sin6_family = AF_INET6; + u.sain6.sin6_addr = rr.rr.in_aaaa.addr6; + u.sain6.sin6_port = 0; + } else + continue; + + if (as->as.ai.hints.ai_flags & AI_CANONNAME) { + _asr_strdname(rr.rr_dname, buf, sizeof buf); + buf[strlen(buf) - 1] = '\0'; + c = res_hnok(buf) ? buf : NULL; + } else if (as->as.ai.hints.ai_flags & AI_FQDN) + c = as->as.ai.fqdn; + else + c = NULL; + + if (addrinfo_add(as, &u.sa, c)) + return (-1); /* errno set */ + } + return (0); +} + +static int +addrconfig_setup(struct asr_query *as) +{ + struct ifaddrs *ifa, *ifa0; + struct sockaddr_in *sinp; + struct sockaddr_in6 *sin6p; + + if (getifaddrs(&ifa0) == -1) + return (-1); + + as->as_flags |= ASYNC_NO_INET | ASYNC_NO_INET6; + + for (ifa = ifa0; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL) + continue; + + switch (ifa->ifa_addr->sa_family) { + case PF_INET: + sinp = (struct sockaddr_in *)ifa->ifa_addr; + + if (sinp->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) + continue; + + as->as_flags &= ~ASYNC_NO_INET; + break; + case PF_INET6: + sin6p = (struct sockaddr_in6 *)ifa->ifa_addr; + + if (IN6_IS_ADDR_LOOPBACK(&sin6p->sin6_addr)) + continue; + + if (IN6_IS_ADDR_LINKLOCAL(&sin6p->sin6_addr)) + continue; + + as->as_flags &= ~ASYNC_NO_INET6; + break; + } + } + + freeifaddrs(ifa0); + + return (0); +} diff --git a/openbsd-compat/libasr/gethostnamadr.c b/openbsd-compat/libasr/gethostnamadr.c new file mode 100644 index 00000000..2fce46b3 --- /dev/null +++ b/openbsd-compat/libasr/gethostnamadr.c @@ -0,0 +1,200 @@ +/* $OpenBSD: gethostnamadr.c,v 1.13 2015/09/14 07:38:37 guenther Exp $ */ +/* + * Copyright (c) 2012,2013 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> /* ALIGN */ +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> + +#include <asr.h> +#include <errno.h> +#include <resolv.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +static int _gethostbyname(const char *, int, struct hostent *, char *, size_t, + int *); +static int _fillhostent(const struct hostent *, struct hostent *, char *, + size_t); + +static struct hostent _hostent; +static char _entbuf[4096]; + +static char *_empty[] = { NULL, }; + +static int +_fillhostent(const struct hostent *h, struct hostent *r, char *buf, size_t len) +{ + char **ptr, *end, *pos; + size_t n, i; + int naliases, naddrs; + + bzero(buf, len); + bzero(r, sizeof(*r)); + r->h_aliases = _empty; + r->h_addr_list = _empty; + + end = buf + len; + ptr = (char **)ALIGN(buf); + + if ((char *)ptr >= end) + return (ERANGE); + + for (naliases = 0; h->h_aliases[naliases]; naliases++) + ; + for (naddrs = 0; h->h_addr_list[naddrs]; naddrs++) + ; + + pos = (char *)(ptr + (naliases + 1) + (naddrs + 1)); + if (pos >= end) + return (ERANGE); + + r->h_name = NULL; + r->h_addrtype = h->h_addrtype; + r->h_length = h->h_length; + r->h_aliases = ptr; + r->h_addr_list = ptr + naliases + 1; + + n = strlcpy(pos, h->h_name, end - pos); + if (n >= end - pos) + return (ERANGE); + r->h_name = pos; + pos += n + 1; + + for (i = 0; i < naliases; i++) { + n = strlcpy(pos, h->h_aliases[i], end - pos); + if (n >= end - pos) + return (ERANGE); + r->h_aliases[i] = pos; + pos += n + 1; + } + + pos = (char *)ALIGN(pos); + if (pos >= end) + return (ERANGE); + + for (i = 0; i < naddrs; i++) { + if (r->h_length > end - pos) + return (ERANGE); + memmove(pos, h->h_addr_list[i], r->h_length); + r->h_addr_list[i] = pos; + pos += r->h_length; + } + + return (0); +} + +static int +_gethostbyname(const char *name, int af, struct hostent *ret, char *buf, + size_t buflen, int *h_errnop) +{ + struct asr_query *as; + struct asr_result ar; + int r; + + if (af == -1) + as = gethostbyname_async(name, NULL); + else + as = gethostbyname2_async(name, af, NULL); + + if (as == NULL) + return (errno); + + asr_run_sync(as, &ar); + + errno = ar.ar_errno; + *h_errnop = ar.ar_h_errno; + if (ar.ar_hostent == NULL) + return (0); + + r = _fillhostent(ar.ar_hostent, ret, buf, buflen); + free(ar.ar_hostent); + + return (r); +} + +struct hostent * +gethostbyname(const char *name) +{ + struct hostent *h; + + res_init(); + + if (_res.options & RES_USE_INET6 && + (h = gethostbyname2(name, AF_INET6))) + return (h); + + return gethostbyname2(name, AF_INET); +} +DEF_WEAK(gethostbyname); + +struct hostent * +gethostbyname2(const char *name, int af) +{ + int r; + + res_init(); + + r = _gethostbyname(name, af, &_hostent, _entbuf, sizeof(_entbuf), + &h_errno); + if (r) { + h_errno = NETDB_INTERNAL; + errno = r; + } + + if (h_errno) + return (NULL); + + return (&_hostent); +} +DEF_WEAK(gethostbyname2); + +struct hostent * +gethostbyaddr(const void *addr, socklen_t len, int af) +{ + struct asr_query *as; + struct asr_result ar; + int r; + + res_init(); + + as = gethostbyaddr_async(addr, len, af, NULL); + if (as == NULL) { + h_errno = NETDB_INTERNAL; + return (NULL); + } + + asr_run_sync(as, &ar); + + errno = ar.ar_errno; + h_errno = ar.ar_h_errno; + if (ar.ar_hostent == NULL) + return (NULL); + + r = _fillhostent(ar.ar_hostent, &_hostent, _entbuf, sizeof(_entbuf)); + free(ar.ar_hostent); + + if (r) { + h_errno = NETDB_INTERNAL; + errno = r; + return (NULL); + } + + return (&_hostent); +} diff --git a/openbsd-compat/libasr/gethostnamadr_async.c b/openbsd-compat/libasr/gethostnamadr_async.c new file mode 100644 index 00000000..2636e848 --- /dev/null +++ b/openbsd-compat/libasr/gethostnamadr_async.c @@ -0,0 +1,676 @@ +/* $OpenBSD: gethostnamadr_async.c,v 1.45 2019/06/27 05:26:37 martijn Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#ifdef HAVE_ARPA_NAMESER_COMPAT_H +#include <arpa/nameser_compat.h> +#endif +#include <netdb.h> + +#include <asr.h> +#include <ctype.h> +#include <errno.h> +#include <resolv.h> /* for res_hnok */ +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> + +#include "asr_private.h" + +#define MAXALIASES 35 +#define MAXADDRS 35 + +struct hostent_ext { + struct hostent h; + char *aliases[MAXALIASES + 1]; + char *addrs[MAXADDRS + 1]; + char *end; + char *pos; +}; + +struct netent_ext { + struct netent n; + char *aliases[MAXALIASES + 1]; + char *end; + char *pos; +}; + +static int gethostnamadr_async_run(struct asr_query *, struct asr_result *); +static struct hostent_ext *hostent_alloc(int); +static int hostent_set_cname(struct hostent_ext *, const char *, int); +static int hostent_add_alias(struct hostent_ext *, const char *, int); +static int hostent_add_addr(struct hostent_ext *, const void *, size_t); +static struct hostent_ext *hostent_from_addr(int, const char *, const char *); +static struct hostent_ext *hostent_file_match(FILE *, int, int, const char *, + int); +static struct hostent_ext *hostent_from_packet(int, int, char *, size_t); +static void netent_from_hostent(struct asr_result *ar); + +struct asr_query * +gethostbyname_async(const char *name, void *asr) +{ + return gethostbyname2_async(name, AF_INET, asr); +} +DEF_WEAK(gethostbyname_async); + +struct asr_query * +gethostbyname2_async(const char *name, int af, void *asr) +{ + struct asr_ctx *ac; + struct asr_query *as; + + /* the original segfaults */ + if (name == NULL) { + errno = EINVAL; + return (NULL); + } + + ac = _asr_use_resolver(asr); + if ((as = _asr_async_new(ac, ASR_GETHOSTBYNAME)) == NULL) + goto abort; /* errno set */ + as->as_run = gethostnamadr_async_run; + + as->as.hostnamadr.family = af; + if (af == AF_INET) + as->as.hostnamadr.addrlen = INADDRSZ; + else if (af == AF_INET6) + as->as.hostnamadr.addrlen = IN6ADDRSZ; + as->as.hostnamadr.name = strdup(name); + if (as->as.hostnamadr.name == NULL) + goto abort; /* errno set */ + + _asr_ctx_unref(ac); + return (as); + + abort: + if (as) + _asr_async_free(as); + _asr_ctx_unref(ac); + return (NULL); +} +DEF_WEAK(gethostbyname2_async); + +struct asr_query * +gethostbyaddr_async(const void *addr, socklen_t len, int af, void *asr) +{ + struct asr_ctx *ac; + struct asr_query *as; + + ac = _asr_use_resolver(asr); + as = _gethostbyaddr_async_ctx(addr, len, af, ac); + _asr_ctx_unref(ac); + + return (as); +} +DEF_WEAK(gethostbyaddr_async); + +struct asr_query * +_gethostbyaddr_async_ctx(const void *addr, socklen_t len, int af, + struct asr_ctx *ac) +{ + struct asr_query *as; + + if ((as = _asr_async_new(ac, ASR_GETHOSTBYADDR)) == NULL) + goto abort; /* errno set */ + as->as_run = gethostnamadr_async_run; + + as->as.hostnamadr.family = af; + as->as.hostnamadr.addrlen = len; + if (len > 0) + memmove(as->as.hostnamadr.addr, addr, (len > 16) ? 16 : len); + + return (as); + + abort: + if (as) + _asr_async_free(as); + return (NULL); +} + +static int +gethostnamadr_async_run(struct asr_query *as, struct asr_result *ar) +{ + struct hostent_ext *h; + int r, type, saved_errno; + FILE *f; + char name[MAXDNAME], *data, addr[16], *c; + + next: + switch (as->as_state) { + + case ASR_STATE_INIT: + + if (as->as.hostnamadr.family != AF_INET && + as->as.hostnamadr.family != AF_INET6) { + ar->ar_h_errno = NETDB_INTERNAL; + ar->ar_errno = EAFNOSUPPORT; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if ((as->as.hostnamadr.family == AF_INET && + as->as.hostnamadr.addrlen != INADDRSZ) || + (as->as.hostnamadr.family == AF_INET6 && + as->as.hostnamadr.addrlen != IN6ADDRSZ)) { + ar->ar_h_errno = NETDB_INTERNAL; + ar->ar_errno = EINVAL; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (as->as_type == ASR_GETHOSTBYNAME) { + + if (as->as.hostnamadr.name[0] == '\0') { + ar->ar_h_errno = NO_DATA; + async_set_state(as, ASR_STATE_HALT); + break; + } + + /* Name might be an IP address string */ + for (c = as->as.hostnamadr.name; *c; c++) + if (!isdigit((unsigned char)*c) && + *c != '.' && *c != ':') + break; + if (*c == 0 && + inet_pton(as->as.hostnamadr.family, + as->as.hostnamadr.name, addr) == 1) { + h = hostent_from_addr(as->as.hostnamadr.family, + as->as.hostnamadr.name, addr); + if (h == NULL) { + ar->ar_errno = errno; + ar->ar_h_errno = NETDB_INTERNAL; + } + else { + ar->ar_hostent = &h->h; + ar->ar_h_errno = NETDB_SUCCESS; + } + async_set_state(as, ASR_STATE_HALT); + break; + } + } + async_set_state(as, ASR_STATE_NEXT_DB); + break; + + case ASR_STATE_NEXT_DB: + + if (_asr_iter_db(as) == -1) { + async_set_state(as, ASR_STATE_NOT_FOUND); + break; + } + + switch (AS_DB(as)) { + + case ASR_DB_DNS: + + /* Create a subquery to do the DNS lookup */ + + if (as->as_type == ASR_GETHOSTBYNAME) { + type = (as->as.hostnamadr.family == AF_INET) ? + T_A : T_AAAA; + as->as_subq = _res_search_async_ctx( + as->as.hostnamadr.name, + C_IN, type, as->as_ctx); + } else { + _asr_addr_as_fqdn(as->as.hostnamadr.addr, + as->as.hostnamadr.family, + name, sizeof(name)); + as->as_subq = _res_query_async_ctx( + name, C_IN, T_PTR, as->as_ctx); + } + + if (as->as_subq == NULL) { + ar->ar_errno = errno; + ar->ar_h_errno = NETDB_INTERNAL; + async_set_state(as, ASR_STATE_HALT); + break; + } + + async_set_state(as, ASR_STATE_SUBQUERY); + break; + + case ASR_DB_FILE: + + /* Try to find a match in the host file */ + + if ((f = fopen(_PATH_HOSTS, "re")) == NULL) + break; + + if (as->as_type == ASR_GETHOSTBYNAME) + data = as->as.hostnamadr.name; + else + data = as->as.hostnamadr.addr; + + h = hostent_file_match(f, as->as_type, + as->as.hostnamadr.family, data, + as->as.hostnamadr.addrlen); + saved_errno = errno; + fclose(f); + errno = saved_errno; + + if (h == NULL) { + if (errno) { + ar->ar_errno = errno; + ar->ar_h_errno = NETDB_INTERNAL; + async_set_state(as, ASR_STATE_HALT); + } + /* otherwise not found */ + break; + } + ar->ar_hostent = &h->h; + ar->ar_h_errno = NETDB_SUCCESS; + async_set_state(as, ASR_STATE_HALT); + break; + } + break; + + case ASR_STATE_SUBQUERY: + + /* Run the DNS subquery. */ + + if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND) + return (ASYNC_COND); + + /* Done. */ + as->as_subq = NULL; + + /* + * We either got no packet or a packet without an answer. + * Saveguard the h_errno and use the next DB. + */ + if (ar->ar_count == 0) { + free(ar->ar_data); + as->as.hostnamadr.subq_h_errno = ar->ar_h_errno; + async_set_state(as, ASR_STATE_NEXT_DB); + break; + } + + /* Read the hostent from the packet. */ + + h = hostent_from_packet(as->as_type, + as->as.hostnamadr.family, ar->ar_data, ar->ar_datalen); + free(ar->ar_data); + if (h == NULL) { + ar->ar_errno = errno; + ar->ar_h_errno = NETDB_INTERNAL; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (as->as_type == ASR_GETHOSTBYADDR) { + if (hostent_add_addr(h, as->as.hostnamadr.addr, + as->as.hostnamadr.addrlen) == -1) { + free(h); + ar->ar_errno = errno; + ar->ar_h_errno = NETDB_INTERNAL; + async_set_state(as, ASR_STATE_HALT); + break; + } + } + + /* + * No valid hostname or address found in the dns packet. + * Ignore it. + */ + if ((as->as_type == ASR_GETHOSTBYNAME && + h->h.h_addr_list[0] == NULL) || + h->h.h_name == NULL) { + free(h); + async_set_state(as, ASR_STATE_NEXT_DB); + break; + } + + ar->ar_hostent = &h->h; + ar->ar_h_errno = NETDB_SUCCESS; + async_set_state(as, ASR_STATE_HALT); + break; + + case ASR_STATE_NOT_FOUND: + ar->ar_errno = 0; + if (as->as.hostnamadr.subq_h_errno) + ar->ar_h_errno = as->as.hostnamadr.subq_h_errno; + else + ar->ar_h_errno = HOST_NOT_FOUND; + async_set_state(as, ASR_STATE_HALT); + break; + + case ASR_STATE_HALT: + if (ar->ar_h_errno == NETDB_SUCCESS && + as->as_flags & ASYNC_GETNET) + netent_from_hostent(ar); + if (ar->ar_h_errno) { + ar->ar_hostent = NULL; + ar->ar_netent = NULL; + } else + ar->ar_errno = 0; + return (ASYNC_DONE); + + default: + ar->ar_errno = EOPNOTSUPP; + ar->ar_h_errno = NETDB_INTERNAL; + ar->ar_gai_errno = EAI_SYSTEM; + async_set_state(as, ASR_STATE_HALT); + break; + } + goto next; +} + +/* + * Create a hostent from a numeric address string. + */ +static struct hostent_ext * +hostent_from_addr(int family, const char *name, const char *addr) +{ + struct hostent_ext *h; + + if ((h = hostent_alloc(family)) == NULL) + return (NULL); + if (hostent_set_cname(h, name, 0) == -1) + goto fail; + if (hostent_add_addr(h, addr, h->h.h_length) == -1) + goto fail; + return (h); +fail: + free(h); + return (NULL); +} + +/* + * Lookup the first matching entry in the hostfile, either by address or by + * name depending on reqtype, and build a hostent from the line. + */ +static struct hostent_ext * +hostent_file_match(FILE *f, int reqtype, int family, const char *data, + int datalen) +{ + char *tokens[MAXTOKEN], addr[16], buf[ASR_BUFSIZ + 1]; + struct hostent_ext *h; + int n, i; + + for (;;) { + n = _asr_parse_namedb_line(f, tokens, MAXTOKEN, buf, sizeof(buf)); + if (n == -1) { + errno = 0; /* ignore errors reading the file */ + return (NULL); + } + + /* there must be an address and at least one name */ + if (n < 2) + continue; + + if (reqtype == ASR_GETHOSTBYNAME) { + for (i = 1; i < n; i++) { + if (strcasecmp(data, tokens[i])) + continue; + if (inet_pton(family, tokens[0], addr) == 1) + goto found; + } + } else { + if (inet_pton(family, tokens[0], addr) == 1 && + memcmp(addr, data, datalen) == 0) + goto found; + } + } + +found: + if ((h = hostent_alloc(family)) == NULL) + return (NULL); + if (hostent_set_cname(h, tokens[1], 0) == -1) + goto fail; + for (i = 2; i < n; i ++) + if (hostent_add_alias(h, tokens[i], 0) == -1) + goto fail; + if (hostent_add_addr(h, addr, h->h.h_length) == -1) + goto fail; + return (h); +fail: + free(h); + return (NULL); +} + +/* + * Fill the hostent from the given DNS packet. + */ +static struct hostent_ext * +hostent_from_packet(int reqtype, int family, char *pkt, size_t pktlen) +{ + struct hostent_ext *h; + struct asr_unpack p; + struct asr_dns_header hdr; + struct asr_dns_query q; + struct asr_dns_rr rr; + char dname[MAXDNAME]; + + if ((h = hostent_alloc(family)) == NULL) + return (NULL); + + _asr_unpack_init(&p, pkt, pktlen); + _asr_unpack_header(&p, &hdr); + for (; hdr.qdcount; hdr.qdcount--) + _asr_unpack_query(&p, &q); + strlcpy(dname, q.q_dname, sizeof(dname)); + + for (; hdr.ancount; hdr.ancount--) { + _asr_unpack_rr(&p, &rr); + if (rr.rr_class != C_IN) + continue; + switch (rr.rr_type) { + + case T_CNAME: + if (reqtype == ASR_GETHOSTBYNAME) { + if (hostent_add_alias(h, rr.rr_dname, 1) == -1) + goto fail; + } else { + if (strcasecmp(rr.rr_dname, dname) == 0) + strlcpy(dname, rr.rr.cname.cname, + sizeof(dname)); + } + break; + + case T_PTR: + if (reqtype != ASR_GETHOSTBYADDR) + break; + if (strcasecmp(rr.rr_dname, dname) != 0) + continue; + if (hostent_set_cname(h, rr.rr.ptr.ptrname, 1) == -1) + hostent_add_alias(h, rr.rr.ptr.ptrname, 1); + break; + + case T_A: + if (reqtype != ASR_GETHOSTBYNAME) + break; + if (family != AF_INET) + break; + if (hostent_set_cname(h, rr.rr_dname, 1) == -1) + ; + if (hostent_add_addr(h, &rr.rr.in_a.addr, 4) == -1) + goto fail; + break; + + case T_AAAA: + if (reqtype != ASR_GETHOSTBYNAME) + break; + if (family != AF_INET6) + break; + if (hostent_set_cname(h, rr.rr_dname, 1) == -1) + ; + if (hostent_add_addr(h, &rr.rr.in_aaaa.addr6, 16) == -1) + goto fail; + break; + } + } + + return (h); +fail: + free(h); + return (NULL); +} + +static struct hostent_ext * +hostent_alloc(int family) +{ + struct hostent_ext *h; + size_t alloc; + + alloc = sizeof(*h) + 1024; + if ((h = calloc(1, alloc)) == NULL) + return (NULL); + + h->h.h_addrtype = family; + h->h.h_length = (family == AF_INET) ? 4 : 16; + h->h.h_aliases = h->aliases; + h->h.h_addr_list = h->addrs; + h->pos = (char *)(h) + sizeof(*h); + h->end = h->pos + 1024; + + return (h); +} + +static int +hostent_set_cname(struct hostent_ext *h, const char *name, int isdname) +{ + char buf[MAXDNAME]; + size_t n; + + if (h->h.h_name) + return (-1); + + if (isdname) { + _asr_strdname(name, buf, sizeof buf); + buf[strlen(buf) - 1] = '\0'; + if (!res_hnok(buf)) + return (-1); + name = buf; + } + + n = strlen(name) + 1; + if (h->pos + n >= h->end) + return (-1); + + h->h.h_name = h->pos; + memmove(h->pos, name, n); + h->pos += n; + return (0); +} + +static int +hostent_add_alias(struct hostent_ext *h, const char *name, int isdname) +{ + char buf[MAXDNAME]; + size_t i, n; + + for (i = 0; i < MAXALIASES; i++) + if (h->aliases[i] == NULL) + break; + if (i == MAXALIASES) + return (0); + + if (isdname) { + _asr_strdname(name, buf, sizeof buf); + buf[strlen(buf)-1] = '\0'; + if (!res_hnok(buf)) + return (-1); + name = buf; + } + + n = strlen(name) + 1; + if (h->pos + n >= h->end) + return (0); + + h->aliases[i] = h->pos; + memmove(h->pos, name, n); + h->pos += n; + return (0); +} + +static int +hostent_add_addr(struct hostent_ext *h, const void *addr, size_t size) +{ + int i; + + for (i = 0; i < MAXADDRS; i++) + if (h->addrs[i] == NULL) + break; + if (i == MAXADDRS) + return (0); + + if (h->pos + size >= h->end) + return (0); + + h->addrs[i] = h->pos; + memmove(h->pos, addr, size); + h->pos += size; + return (0); +} + +static void +netent_from_hostent(struct asr_result *ar) +{ + struct in_addr *addr; + struct netent_ext *n; + struct hostent_ext *h; + char **na, **ha; + size_t sz; + + /* Allocate and initialize the output. */ + if ((n = calloc(1, sizeof(*n) + 1024)) == NULL) { + ar->ar_h_errno = NETDB_INTERNAL; + ar->ar_errno = errno; + goto out; + } + n->pos = (char *)(n) + sizeof(*n); + n->end = n->pos + 1024; + n->n.n_name = n->pos; + n->n.n_aliases = n->aliases; + + /* Copy the fixed-size data. */ + h = (struct hostent_ext *)ar->ar_hostent; + addr = (struct in_addr *)h->h.h_addr; + n->n.n_net = ntohl(addr->s_addr); + n->n.n_addrtype = h->h.h_addrtype; + + /* Copy the network name. */ + sz = strlen(h->h.h_name) + 1; + memcpy(n->pos, h->h.h_name, sz); + n->pos += sz; + + /* + * Copy the aliases. + * No overflow check is needed because we are merely copying + * a part of the data from a structure of the same size. + */ + na = n->aliases; + for (ha = h->aliases; *ha != NULL; ha++) { + sz = strlen(*ha) + 1; + memcpy(n->pos, *ha, sz); + *na++ = n->pos; + n->pos += sz; + } + *na = NULL; + + /* Handle the return values. */ + ar->ar_netent = &n->n; +out: + free(ar->ar_hostent); + ar->ar_hostent = NULL; +} diff --git a/openbsd-compat/libasr/getnameinfo.c b/openbsd-compat/libasr/getnameinfo.c new file mode 100644 index 00000000..7bee468d --- /dev/null +++ b/openbsd-compat/libasr/getnameinfo.c @@ -0,0 +1,205 @@ +/* $OpenBSD: getnameinfo.c,v 1.9 2019/07/03 03:24:03 deraadt Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <net/if.h> +#include <arpa/inet.h> +#include <netinet/in.h> +#include <netdb.h> + +#include <asr.h> +#include <errno.h> +#include <resolv.h> +#include <string.h> + +static size_t asr_print_addr(const struct sockaddr *, char *, size_t); +static size_t asr_print_port(const struct sockaddr *, const char *, char *, size_t); + +#define SA_IN(sa) ((struct sockaddr_in*)(sa)) +#define SA_IN6(sa) ((struct sockaddr_in6*)(sa)) + +/* + * Print the textual representation (as given by inet_ntop(3)) of the address + * set in "sa". + * + * Return the total length of the string it tried to create or 0 if an error + * occured, in which case errno is set. On success, the constructed string + * is guaranteed to be NUL-terminated. Overflow must be detected by checking + * the returned size against buflen. + * + */ +static size_t +asr_print_addr(const struct sockaddr *sa, char *buf, size_t buflen) +{ + unsigned int ifidx; + char tmp[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; + char scope[IF_NAMESIZE + 1], *ifname; + const void *addr; + size_t s; + + switch(sa->sa_family) { + case AF_INET: + addr = &SA_IN(sa)->sin_addr; + break; + case AF_INET6: + addr = &SA_IN6(sa)->sin6_addr; + break; + default: + errno = EINVAL; + return (0); + } + + if (inet_ntop(sa->sa_family, addr, tmp, sizeof(tmp)) == NULL) + return (0); /* errno set */ + + s = strlcpy(buf, tmp, buflen); + + if (sa->sa_family == AF_INET6 && SA_IN6(sa)->sin6_scope_id) { + + scope[0] = SCOPE_DELIMITER; + scope[1] = '\0'; + + ifidx = SA_IN6(sa)->sin6_scope_id; + ifname = NULL; + + if (IN6_IS_ADDR_LINKLOCAL(&(SA_IN6(sa)->sin6_addr)) || + IN6_IS_ADDR_MC_LINKLOCAL(&(SA_IN6(sa)->sin6_addr)) || + IN6_IS_ADDR_MC_INTFACELOCAL(&(SA_IN6(sa)->sin6_addr))) + ifname = if_indextoname(ifidx, scope + 1); + + if (ifname == NULL) + (void)snprintf(scope + 1, sizeof(scope) - 1, "%u", ifidx); + + if (s < buflen) + (void)strlcat(buf, scope, buflen); + + s += strlen(scope); + } + + return (s); +} + +/* + * Print the textual representation of the port set on "sa". + * + * If proto is not NULL, it is used as parameter to "getservbyport_r(3)" to + * return a service name. If it's not set, or if no matching service is found, + * it prints the portno. + * + * Return the total length of the string it tried to create or 0 if an error + * occured, in which case errno is set. On success, the constructed string + * is guaranteed to be NUL-terminated. Overflow must be detected by checking + * the returned size against buflen. + */ +static size_t +asr_print_port(const struct sockaddr *sa, const char *proto, char *buf, size_t buflen) +{ + struct servent s; + struct servent_data sd; + int port, r, saved_errno; + size_t n; + + switch(sa->sa_family) { + case AF_INET: + port = SA_IN(sa)->sin_port; + break; + case AF_INET6: + port = SA_IN6(sa)->sin6_port; + break; + default: + errno = EINVAL; + return (0); + } + + if (proto) { + memset(&sd, 0, sizeof (sd)); + saved_errno = errno; + if (getservbyport_r(port, proto, &s, &sd) != -1) { + n = strlcpy(buf, s.s_name, buflen); + endservent_r(&sd); + return (n); + } + errno = saved_errno; + } + + r = snprintf(buf, buflen, "%u", ntohs(port)); + if (r < 0 || r >= buflen) /* Actually, this can not happen */ + return (0); + + return (r); +} + +int +getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, + size_t hostlen, char *serv, size_t servlen, int flags) +{ + struct asr_query *as; + struct asr_result ar; + int saved_errno = errno; + const char *proto; + size_t r; + + /* + * Take a shortcut if we don't care about hostname, + * or if NI_NUMERICHOST is set. + */ + if (host == NULL || hostlen == 0 || + (host && hostlen && (flags & NI_NUMERICHOST))) { + if (host) { + r = asr_print_addr(sa, host, hostlen); + if (r == 0) + return (EAI_SYSTEM); /* errno set */ + if (r >= hostlen) + return (EAI_OVERFLOW); + } + + if (serv && servlen) { + if (flags & NI_NUMERICSERV) + proto = NULL; + else + proto = (flags & NI_DGRAM) ? "udp" : "tcp"; + r = asr_print_port(sa, proto, serv, servlen); + if (r == 0) + return (EAI_SYSTEM); /* errno set */ + if (r >= servlen) + return (EAI_OVERFLOW); + } + + errno = saved_errno; + return (0); + } + + res_init(); + + as = getnameinfo_async(sa, salen, host, hostlen, serv, servlen, flags, + NULL); + if (as == NULL) { + if (errno == ENOMEM) { + errno = saved_errno; + return (EAI_MEMORY); + } + return (EAI_SYSTEM); + } + + asr_run_sync(as, &ar); + if (ar.ar_gai_errno == EAI_SYSTEM) + errno = ar.ar_errno; + + return (ar.ar_gai_errno); +} +DEF_WEAK(getnameinfo); diff --git a/openbsd-compat/libasr/getnameinfo_async.c b/openbsd-compat/libasr/getnameinfo_async.c new file mode 100644 index 00000000..faba8860 --- /dev/null +++ b/openbsd-compat/libasr/getnameinfo_async.c @@ -0,0 +1,300 @@ +/* $OpenBSD: getnameinfo_async.c,v 1.14 2019/07/03 03:24:03 deraadt Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <net/if.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <netdb.h> + +#include <asr.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "asr_private.h" + +static int getnameinfo_async_run(struct asr_query *, struct asr_result *); +static int _servname(struct asr_query *); +static int _numerichost(struct asr_query *); + +struct asr_query * +getnameinfo_async(const struct sockaddr *sa, socklen_t slen, char *host, + size_t hostlen, char *serv, size_t servlen, int flags, void *asr) +{ + struct asr_ctx *ac; + struct asr_query *as; + + ac = _asr_use_resolver(asr); + if ((as = _asr_async_new(ac, ASR_GETNAMEINFO)) == NULL) + goto abort; /* errno set */ + as->as_run = getnameinfo_async_run; + + if (sa->sa_family == AF_INET) + memmove(&as->as.ni.sa.sa, sa, sizeof (as->as.ni.sa.sain)); + else if (sa->sa_family == AF_INET6) + memmove(&as->as.ni.sa.sa, sa, sizeof (as->as.ni.sa.sain6)); + +#ifdef HAVE_STRUCT_SOCKADDR_SA_LEN + as->as.ni.sa.sa.sa_len = slen; +#endif + as->as.ni.hostname = host; + as->as.ni.hostnamelen = hostlen; + as->as.ni.servname = serv; + as->as.ni.servnamelen = servlen; + as->as.ni.flags = flags; + + _asr_ctx_unref(ac); + return (as); + + abort: + if (as) + _asr_async_free(as); + _asr_ctx_unref(ac); + return (NULL); +} +DEF_WEAK(getnameinfo_async); + +static int +getnameinfo_async_run(struct asr_query *as, struct asr_result *ar) +{ + void *addr; + socklen_t addrlen; + int r; + + next: + switch (as->as_state) { + + case ASR_STATE_INIT: + + /* Make sure the parameters are all valid. */ + + if (as->as.ni.sa.sa.sa_family != AF_INET && + as->as.ni.sa.sa.sa_family != AF_INET6) { + ar->ar_gai_errno = EAI_FAMILY; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if ((as->as.ni.sa.sa.sa_family == AF_INET && + (SA_LEN(&as->as.ni.sa.sa) != sizeof (as->as.ni.sa.sain))) || + (as->as.ni.sa.sa.sa_family == AF_INET6 && + (SA_LEN(&as->as.ni.sa.sa) != sizeof (as->as.ni.sa.sain6)))) { + ar->ar_gai_errno = EAI_FAIL; + async_set_state(as, ASR_STATE_HALT); + break; + } + + /* Set the service name first, if needed. */ + if (_servname(as) == -1) { + ar->ar_gai_errno = EAI_OVERFLOW; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (as->as.ni.hostname == NULL || as->as.ni.hostnamelen == 0) { + ar->ar_gai_errno = 0; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (as->as.ni.flags & NI_NUMERICHOST) { + if (_numerichost(as) == -1) { + if (errno == ENOMEM) + ar->ar_gai_errno = EAI_MEMORY; + else if (errno == ENOSPC) + ar->ar_gai_errno = EAI_OVERFLOW; + else { + ar->ar_errno = errno; + ar->ar_gai_errno = EAI_SYSTEM; + } + } else + ar->ar_gai_errno = 0; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (as->as.ni.sa.sa.sa_family == AF_INET) { + addrlen = sizeof(as->as.ni.sa.sain.sin_addr); + addr = &as->as.ni.sa.sain.sin_addr; + } else { + addrlen = sizeof(as->as.ni.sa.sain6.sin6_addr); + addr = &as->as.ni.sa.sain6.sin6_addr; + } + + /* + * Create a subquery to lookup the address. + */ + as->as_subq = _gethostbyaddr_async_ctx(addr, addrlen, + as->as.ni.sa.sa.sa_family, + as->as_ctx); + if (as->as_subq == NULL) { + ar->ar_gai_errno = EAI_MEMORY; + async_set_state(as, ASR_STATE_HALT); + break; + } + + async_set_state(as, ASR_STATE_SUBQUERY); + break; + + case ASR_STATE_SUBQUERY: + + if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND) + return (ASYNC_COND); + + /* + * Request done. + */ + as->as_subq = NULL; + + if (ar->ar_hostent == NULL) { + if (as->as.ni.flags & NI_NAMEREQD) { + ar->ar_gai_errno = EAI_NONAME; + } else if (_numerichost(as) == -1) { + if (errno == ENOMEM) + ar->ar_gai_errno = EAI_MEMORY; + else if (errno == ENOSPC) + ar->ar_gai_errno = EAI_OVERFLOW; + else { + ar->ar_errno = errno; + ar->ar_gai_errno = EAI_SYSTEM; + } + } else + ar->ar_gai_errno = 0; + } else { + if (strlcpy(as->as.ni.hostname, + ar->ar_hostent->h_name, + as->as.ni.hostnamelen) >= as->as.ni.hostnamelen) + ar->ar_gai_errno = EAI_OVERFLOW; + else + ar->ar_gai_errno = 0; + free(ar->ar_hostent); + } + + async_set_state(as, ASR_STATE_HALT); + break; + + case ASR_STATE_HALT: + return (ASYNC_DONE); + + default: + ar->ar_errno = EOPNOTSUPP; + ar->ar_gai_errno = EAI_SYSTEM; + async_set_state(as, ASR_STATE_HALT); + break; + } + goto next; +} + + +/* + * Set the service name on the result buffer is not NULL. + * return (-1) if the buffer is too small. + */ +static int +_servname(struct asr_query *as) +{ + struct servent s; +#ifdef HAVE_STRUCT_SERVENT_DATA + struct servent_data sd; +#endif + int port, r; + char *buf = as->as.ni.servname; + size_t buflen = as->as.ni.servnamelen; + + if (as->as.ni.servname == NULL || as->as.ni.servnamelen == 0) + return (0); + + if (as->as.ni.sa.sa.sa_family == AF_INET) + port = as->as.ni.sa.sain.sin_port; + else + port = as->as.ni.sa.sain6.sin6_port; + + if (!(as->as.ni.flags & NI_NUMERICSERV)) { +#ifdef HAVE_STRUCT_SERVENT_DATA + memset(&sd, 0, sizeof (sd)); +#endif +#ifdef HAVE_GETSERVBYPORT_R_4_ARGS + r = getservbyport_r(port, + (as->as.ni.flags & NI_DGRAM) ? "udp" : "tcp", + &s, &sd); +#else + r = -1; +#endif + if (r != -1) { + r = strlcpy(buf, s.s_name, buflen) >= buflen; +#ifdef HAVE_ENDSERVENT_R + endservent_r(&sd); +#endif + return (r ? -1 : 0); + } + } + + r = snprintf(buf, buflen, "%u", ntohs(port)); + if (r < 0 || (size_t)r >= buflen) + return (-1); + + return (0); +} + +/* + * Write the numeric address + */ +static int +_numerichost(struct asr_query *as) +{ + unsigned int ifidx; + char scope[IF_NAMESIZE + 1], *ifname; + void *addr; + char *buf = as->as.ni.hostname; + size_t buflen = as->as.ni.hostnamelen; + + if (as->as.ni.sa.sa.sa_family == AF_INET) + addr = &as->as.ni.sa.sain.sin_addr; + else + addr = &as->as.ni.sa.sain6.sin6_addr; + + if (inet_ntop(as->as.ni.sa.sa.sa_family, addr, buf, buflen) == NULL) + return (-1); /* errno set */ + + if (as->as.ni.sa.sa.sa_family == AF_INET6 && + as->as.ni.sa.sain6.sin6_scope_id) { + + scope[0] = SCOPE_DELIMITER; + scope[1] = '\0'; + + ifidx = as->as.ni.sa.sain6.sin6_scope_id; + ifname = NULL; + + if (IN6_IS_ADDR_LINKLOCAL(&as->as.ni.sa.sain6.sin6_addr) || + IN6_IS_ADDR_MC_LINKLOCAL(&as->as.ni.sa.sain6.sin6_addr) || + IN6_IS_ADDR_MC_NODELOCAL(&as->as.ni.sa.sain6.sin6_addr)) + ifname = if_indextoname(ifidx, scope + 1); + + if (ifname == NULL) + snprintf(scope + 1, sizeof(scope) - 1, "%u", ifidx); + + strlcat(buf, scope, buflen); + } + + return (0); +} diff --git a/openbsd-compat/libasr/getnetnamadr.c b/openbsd-compat/libasr/getnetnamadr.c new file mode 100644 index 00000000..141b4a9a --- /dev/null +++ b/openbsd-compat/libasr/getnetnamadr.c @@ -0,0 +1,134 @@ +/* $OpenBSD: getnetnamadr.c,v 1.9 2015/01/16 16:48:51 deraadt Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> /* ALIGN */ +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> + +#include <asr.h> +#include <errno.h> +#include <resolv.h> +#include <stdlib.h> +#include <string.h> + +static void _fillnetent(const struct netent *, struct netent *, char *buf, + size_t); + +static struct netent _netent; +static char _entbuf[4096]; + +static char *_empty[] = { NULL, }; + +static void +_fillnetent(const struct netent *e, struct netent *r, char *buf, size_t len) +{ + char **ptr, *end, *pos; + size_t n, i; + int naliases; + + bzero(buf, len); + bzero(r, sizeof(*r)); + r->n_aliases = _empty; + + end = buf + len; + ptr = (char **)ALIGN(buf); + + if ((char *)ptr >= end) + return; + + for (naliases = 0; e->n_aliases[naliases]; naliases++) + ; + + r->n_name = NULL; + r->n_addrtype = e->n_addrtype; + r->n_net = e->n_net; + r->n_aliases = ptr; + + pos = (char *)(ptr + (naliases + 1)); + if (pos > end) + r->n_aliases = _empty; + + n = strlcpy(pos, e->n_name, end - pos); + if (n >= end - pos) + return; + r->n_name = pos; + pos += n + 1; + + for (i = 0; i < naliases; i++) { + n = strlcpy(pos, e->n_aliases[i], end - pos); + if (n >= end - pos) + return; + r->n_aliases[i] = pos; + pos += n + 1; + } +} + +struct netent * +getnetbyname(const char *name) +{ + struct asr_query *as; + struct asr_result ar; + + res_init(); + + as = getnetbyname_async(name, NULL); + if (as == NULL) { + h_errno = NETDB_INTERNAL; + return (NULL); + } + + asr_run_sync(as, &ar); + + errno = ar.ar_errno; + h_errno = ar.ar_h_errno; + if (ar.ar_netent == NULL) + return (NULL); + + _fillnetent(ar.ar_netent, &_netent, _entbuf, sizeof(_entbuf)); + free(ar.ar_netent); + + return (&_netent); +} + +struct netent * +getnetbyaddr(in_addr_t net, int type) +{ + struct asr_query *as; + struct asr_result ar; + + res_init(); + + as = getnetbyaddr_async(net, type, NULL); + if (as == NULL) { + h_errno = NETDB_INTERNAL; + return (NULL); + } + + asr_run_sync(as, &ar); + + errno = ar.ar_errno; + h_errno = ar.ar_h_errno; + if (ar.ar_netent == NULL) + return (NULL); + + _fillnetent(ar.ar_netent, &_netent, _entbuf, sizeof(_entbuf)); + free(ar.ar_netent); + + return (&_netent); +} diff --git a/openbsd-compat/libasr/getnetnamadr_async.c b/openbsd-compat/libasr/getnetnamadr_async.c new file mode 100644 index 00000000..1e02d008 --- /dev/null +++ b/openbsd-compat/libasr/getnetnamadr_async.c @@ -0,0 +1,52 @@ +/* $OpenBSD: getnetnamadr_async.c,v 1.26 2018/04/28 15:16:49 schwarze Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <netdb.h> +#include <asr.h> + +#include "asr_private.h" + +struct asr_query * +getnetbyname_async(const char *name, void *asr) +{ + struct asr_query *as; + + if ((as = gethostbyname_async(name, asr)) != NULL) + as->as_flags |= ASYNC_GETNET; + return (as); +} +DEF_WEAK(getnetbyname_async); + +struct asr_query * +getnetbyaddr_async(in_addr_t net, int family, void *asr) +{ + struct in_addr in; + struct asr_query *as; + + in.s_addr = htonl(net); + as = gethostbyaddr_async(&in, sizeof(in), family, asr); + if (as != NULL) + as->as_flags |= ASYNC_GETNET; + return (as); +} +DEF_WEAK(getnetbyaddr_async); diff --git a/openbsd-compat/libasr/getrrsetbyname.c b/openbsd-compat/libasr/getrrsetbyname.c new file mode 100644 index 00000000..24df2c8b --- /dev/null +++ b/openbsd-compat/libasr/getrrsetbyname.c @@ -0,0 +1,83 @@ +/* $OpenBSD: getrrsetbyname.c,v 1.6 2015/09/14 07:38:37 guenther Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> + +#include <asr.h> +#include <errno.h> +#include <resolv.h> +#include <stdlib.h> + +int +getrrsetbyname(const char *name, unsigned int class, unsigned int type, + unsigned int flags, struct rrsetinfo **res) +{ + struct asr_query *as; + struct asr_result ar; + int r, saved_errno = errno; + + res_init(); + + as = getrrsetbyname_async(name, class, type, flags, NULL); + if (as == NULL) { + r = (errno == ENOMEM) ? ERRSET_NOMEMORY : ERRSET_FAIL; + errno = saved_errno; + return (r); + } + + asr_run_sync(as, &ar); + + *res = ar.ar_rrsetinfo; + + return (ar.ar_rrset_errno); +} + +/* from net/getrrsetbyname.c */ +void +freerrset(struct rrsetinfo *rrset) +{ + u_int16_t i; + + if (rrset == NULL) + return; + + if (rrset->rri_rdatas) { + for (i = 0; i < rrset->rri_nrdatas; i++) { + if (rrset->rri_rdatas[i].rdi_data == NULL) + break; + free(rrset->rri_rdatas[i].rdi_data); + } + free(rrset->rri_rdatas); + } + + if (rrset->rri_sigs) { + for (i = 0; i < rrset->rri_nsigs; i++) { + if (rrset->rri_sigs[i].rdi_data == NULL) + break; + free(rrset->rri_sigs[i].rdi_data); + } + free(rrset->rri_sigs); + } + + if (rrset->rri_name) + free(rrset->rri_name); + free(rrset); +} +DEF_WEAK(freerrset); diff --git a/openbsd-compat/libasr/getrrsetbyname_async.c b/openbsd-compat/libasr/getrrsetbyname_async.c new file mode 100644 index 00000000..9ff7f6c7 --- /dev/null +++ b/openbsd-compat/libasr/getrrsetbyname_async.c @@ -0,0 +1,590 @@ +/* $OpenBSD: getrrsetbyname_async.c,v 1.11 2017/02/23 17:04:02 eric Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <netdb.h> + +#include <asr.h> +#include <errno.h> +#include <resolv.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "asr_private.h" + +static int getrrsetbyname_async_run(struct asr_query *, struct asr_result *); +static void get_response(struct asr_result *, const char *, int); + +struct asr_query * +getrrsetbyname_async(const char *hostname, unsigned int rdclass, + unsigned int rdtype, unsigned int flags, void *asr) +{ + struct asr_ctx *ac; + struct asr_query *as; + + ac = _asr_use_resolver(asr); + if ((as = _asr_async_new(ac, ASR_GETRRSETBYNAME)) == NULL) + goto abort; /* errno set */ + as->as_run = getrrsetbyname_async_run; + + as->as.rrset.flags = flags; + as->as.rrset.class = rdclass; + as->as.rrset.type = rdtype; + as->as.rrset.name = strdup(hostname); + if (as->as.rrset.name == NULL) + goto abort; /* errno set */ + + _asr_ctx_unref(ac); + return (as); + abort: + if (as) + _asr_async_free(as); + + _asr_ctx_unref(ac); + return (NULL); +} +DEF_WEAK(getrrsetbyname_async); + +static int +getrrsetbyname_async_run(struct asr_query *as, struct asr_result *ar) +{ + next: + switch (as->as_state) { + + case ASR_STATE_INIT: + + /* Check for invalid class and type. */ + if (as->as.rrset.class > 0xffff || as->as.rrset.type > 0xffff) { + ar->ar_rrset_errno = ERRSET_INVAL; + async_set_state(as, ASR_STATE_HALT); + break; + } + + /* Do not allow queries of class or type ANY. */ + if (as->as.rrset.class == 0xff || as->as.rrset.type == 0xff) { + ar->ar_rrset_errno = ERRSET_INVAL; + async_set_state(as, ASR_STATE_HALT); + break; + } + + /* Do not allow flags yet, unimplemented. */ + if (as->as.rrset.flags) { + ar->ar_rrset_errno = ERRSET_INVAL; + async_set_state(as, ASR_STATE_HALT); + break; + } + + /* Create a delegate the lookup to a subquery. */ + as->as_subq = _res_query_async_ctx( + as->as.rrset.name, + as->as.rrset.class, + as->as.rrset.type, + as->as_ctx); + if (as->as_subq == NULL) { + ar->ar_rrset_errno = ERRSET_FAIL; + async_set_state(as, ASR_STATE_HALT); + break; + } + + async_set_state(as, ASR_STATE_SUBQUERY); + break; + + case ASR_STATE_SUBQUERY: + + if ((asr_run(as->as_subq, ar)) == ASYNC_COND) + return (ASYNC_COND); + + as->as_subq = NULL; + + /* No packet received.*/ + if (ar->ar_datalen == -1) { + switch (ar->ar_h_errno) { + case HOST_NOT_FOUND: + ar->ar_rrset_errno = ERRSET_NONAME; + break; + case NO_DATA: + ar->ar_rrset_errno = ERRSET_NODATA; + break; + default: + ar->ar_rrset_errno = ERRSET_FAIL; + break; + } + async_set_state(as, ASR_STATE_HALT); + break; + } + + /* Got a packet but no answer. */ + if (ar->ar_count == 0) { + free(ar->ar_data); + switch (ar->ar_rcode) { + case NXDOMAIN: + ar->ar_rrset_errno = ERRSET_NONAME; + break; + case NOERROR: + ar->ar_rrset_errno = ERRSET_NODATA; + break; + default: + ar->ar_rrset_errno = ERRSET_FAIL; + break; + } + async_set_state(as, ASR_STATE_HALT); + break; + } + + get_response(ar, ar->ar_data, ar->ar_datalen); + free(ar->ar_data); + async_set_state(as, ASR_STATE_HALT); + break; + + case ASR_STATE_HALT: + if (ar->ar_rrset_errno) + ar->ar_rrsetinfo = NULL; + return (ASYNC_DONE); + + default: + ar->ar_rrset_errno = ERRSET_FAIL; + async_set_state(as, ASR_STATE_HALT); + break; + } + goto next; +} + +/* The rest of this file is taken from the orignal implementation. */ + +/* $OpenBSD: getrrsetbyname_async.c,v 1.11 2017/02/23 17:04:02 eric Exp $ */ + +/* + * Copyright (c) 2001 Jakob Schlyter. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * Portions Copyright (c) 1999-2001 Internet Software Consortium. + * + * 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 INTERNET SOFTWARE CONSORTIUM + * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL + * INTERNET SOFTWARE CONSORTIUM 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. + */ + +#define MAXPACKET 1024*64 + +struct dns_query { + char *name; + u_int16_t type; + u_int16_t class; + struct dns_query *next; +}; + +struct dns_rr { + char *name; + u_int16_t type; + u_int16_t class; + u_int16_t ttl; + u_int16_t size; + void *rdata; + struct dns_rr *next; +}; + +struct dns_response { + HEADER header; + struct dns_query *query; + struct dns_rr *answer; + struct dns_rr *authority; + struct dns_rr *additional; +}; + +static struct dns_response *parse_dns_response(const u_char *, int); +static struct dns_query *parse_dns_qsection(const u_char *, int, + const u_char **, int); +static struct dns_rr *parse_dns_rrsection(const u_char *, int, const u_char **, + int); + +static void free_dns_query(struct dns_query *); +static void free_dns_rr(struct dns_rr *); +static void free_dns_response(struct dns_response *); + +static int count_dns_rr(struct dns_rr *, u_int16_t, u_int16_t); + +static void +get_response(struct asr_result *ar, const char *pkt, int pktlen) +{ + struct rrsetinfo *rrset = NULL; + struct dns_response *response = NULL; + struct dns_rr *rr; + struct rdatainfo *rdata; + unsigned int index_ans, index_sig; + + /* parse result */ + response = parse_dns_response(pkt, pktlen); + if (response == NULL) { + ar->ar_rrset_errno = ERRSET_FAIL; + goto fail; + } + + if (response->header.qdcount != 1) { + ar->ar_rrset_errno = ERRSET_FAIL; + goto fail; + } + + /* initialize rrset */ + rrset = calloc(1, sizeof(struct rrsetinfo)); + if (rrset == NULL) { + ar->ar_rrset_errno = ERRSET_NOMEMORY; + goto fail; + } + rrset->rri_rdclass = response->query->class; + rrset->rri_rdtype = response->query->type; + rrset->rri_ttl = response->answer->ttl; + rrset->rri_nrdatas = response->header.ancount; + + /* check for authenticated data */ + if (response->header.ad == 1) + rrset->rri_flags |= RRSET_VALIDATED; + + /* copy name from answer section */ + rrset->rri_name = strdup(response->answer->name); + if (rrset->rri_name == NULL) { + ar->ar_rrset_errno = ERRSET_NOMEMORY; + goto fail; + } + + /* count answers */ + rrset->rri_nrdatas = count_dns_rr(response->answer, rrset->rri_rdclass, + rrset->rri_rdtype); + rrset->rri_nsigs = count_dns_rr(response->answer, rrset->rri_rdclass, + T_RRSIG); + + /* allocate memory for answers */ + rrset->rri_rdatas = calloc(rrset->rri_nrdatas, + sizeof(struct rdatainfo)); + if (rrset->rri_rdatas == NULL) { + ar->ar_rrset_errno = ERRSET_NOMEMORY; + goto fail; + } + + /* allocate memory for signatures */ + rrset->rri_sigs = calloc(rrset->rri_nsigs, sizeof(struct rdatainfo)); + if (rrset->rri_sigs == NULL) { + ar->ar_rrset_errno = ERRSET_NOMEMORY; + goto fail; + } + + /* copy answers & signatures */ + for (rr = response->answer, index_ans = 0, index_sig = 0; + rr; rr = rr->next) { + + rdata = NULL; + + if (rr->class == rrset->rri_rdclass && + rr->type == rrset->rri_rdtype) + rdata = &rrset->rri_rdatas[index_ans++]; + + if (rr->class == rrset->rri_rdclass && + rr->type == T_RRSIG) + rdata = &rrset->rri_sigs[index_sig++]; + + if (rdata) { + rdata->rdi_length = rr->size; + rdata->rdi_data = malloc(rr->size); + + if (rdata->rdi_data == NULL) { + ar->ar_rrset_errno = ERRSET_NOMEMORY; + goto fail; + } + memcpy(rdata->rdi_data, rr->rdata, rr->size); + } + } + free_dns_response(response); + + ar->ar_rrsetinfo = rrset; + ar->ar_rrset_errno = ERRSET_SUCCESS; + return; + +fail: + if (rrset != NULL) + freerrset(rrset); + if (response != NULL) + free_dns_response(response); +} + +/* + * DNS response parsing routines + */ +static struct dns_response * +parse_dns_response(const u_char *answer, int size) +{ + struct dns_response *resp; + const u_char *cp; + + /* allocate memory for the response */ + resp = calloc(1, sizeof(*resp)); + if (resp == NULL) + return (NULL); + + /* initialize current pointer */ + cp = answer; + + /* copy header */ + memcpy(&resp->header, cp, HFIXEDSZ); + cp += HFIXEDSZ; + + /* fix header byte order */ + resp->header.qdcount = ntohs(resp->header.qdcount); + resp->header.ancount = ntohs(resp->header.ancount); + resp->header.nscount = ntohs(resp->header.nscount); + resp->header.arcount = ntohs(resp->header.arcount); + + /* there must be at least one query */ + if (resp->header.qdcount < 1) { + free_dns_response(resp); + return (NULL); + } + + /* parse query section */ + resp->query = parse_dns_qsection(answer, size, &cp, + resp->header.qdcount); + if (resp->header.qdcount && resp->query == NULL) { + free_dns_response(resp); + return (NULL); + } + + /* parse answer section */ + resp->answer = parse_dns_rrsection(answer, size, &cp, + resp->header.ancount); + if (resp->header.ancount && resp->answer == NULL) { + free_dns_response(resp); + return (NULL); + } + + /* parse authority section */ + resp->authority = parse_dns_rrsection(answer, size, &cp, + resp->header.nscount); + if (resp->header.nscount && resp->authority == NULL) { + free_dns_response(resp); + return (NULL); + } + + /* parse additional section */ + resp->additional = parse_dns_rrsection(answer, size, &cp, + resp->header.arcount); + if (resp->header.arcount && resp->additional == NULL) { + free_dns_response(resp); + return (NULL); + } + + return (resp); +} + +static struct dns_query * +parse_dns_qsection(const u_char *answer, int size, const u_char **cp, int count) +{ + struct dns_query *head, *curr, *prev; + int i, length; + char name[MAXDNAME]; + + for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) { + + /* allocate and initialize struct */ + curr = calloc(1, sizeof(struct dns_query)); + if (curr == NULL) { + free_dns_query(head); + return (NULL); + } + if (head == NULL) + head = curr; + if (prev != NULL) + prev->next = curr; + + /* name */ + length = dn_expand(answer, answer + size, *cp, name, + sizeof(name)); + if (length < 0) { + free_dns_query(head); + return (NULL); + } + curr->name = strdup(name); + if (curr->name == NULL) { + free_dns_query(head); + return (NULL); + } + *cp += length; + + /* type */ + curr->type = _getshort(*cp); + *cp += INT16SZ; + + /* class */ + curr->class = _getshort(*cp); + *cp += INT16SZ; + } + + return (head); +} + +static struct dns_rr * +parse_dns_rrsection(const u_char *answer, int size, const u_char **cp, + int count) +{ + struct dns_rr *head, *curr, *prev; + int i, length; + char name[MAXDNAME]; + + for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) { + + /* allocate and initialize struct */ + curr = calloc(1, sizeof(struct dns_rr)); + if (curr == NULL) { + free_dns_rr(head); + return (NULL); + } + if (head == NULL) + head = curr; + if (prev != NULL) + prev->next = curr; + + /* name */ + length = dn_expand(answer, answer + size, *cp, name, + sizeof(name)); + if (length < 0) { + free_dns_rr(head); + return (NULL); + } + curr->name = strdup(name); + if (curr->name == NULL) { + free_dns_rr(head); + return (NULL); + } + *cp += length; + + /* type */ + curr->type = _getshort(*cp); + *cp += INT16SZ; + + /* class */ + curr->class = _getshort(*cp); + *cp += INT16SZ; + + /* ttl */ + curr->ttl = _getlong(*cp); + *cp += INT32SZ; + + /* rdata size */ + curr->size = _getshort(*cp); + *cp += INT16SZ; + + /* rdata itself */ + curr->rdata = malloc(curr->size); + if (curr->rdata == NULL) { + free_dns_rr(head); + return (NULL); + } + memcpy(curr->rdata, *cp, curr->size); + *cp += curr->size; + } + + return (head); +} + +static void +free_dns_query(struct dns_query *p) +{ + if (p == NULL) + return; + + if (p->name) + free(p->name); + free_dns_query(p->next); + free(p); +} + +static void +free_dns_rr(struct dns_rr *p) +{ + if (p == NULL) + return; + + if (p->name) + free(p->name); + if (p->rdata) + free(p->rdata); + free_dns_rr(p->next); + free(p); +} + +static void +free_dns_response(struct dns_response *p) +{ + if (p == NULL) + return; + + free_dns_query(p->query); + free_dns_rr(p->answer); + free_dns_rr(p->authority); + free_dns_rr(p->additional); + free(p); +} + +static int +count_dns_rr(struct dns_rr *p, u_int16_t class, u_int16_t type) +{ + int n = 0; + + while (p) { + if (p->class == class && p->type == type) + n++; + p = p->next; + } + + return (n); +} diff --git a/openbsd-compat/libasr/libasr.la b/openbsd-compat/libasr/libasr.la new file mode 100644 index 00000000..71346e8a --- /dev/null +++ b/openbsd-compat/libasr/libasr.la @@ -0,0 +1,41 @@ +# libasr.la - a libtool library file +# Generated by libtool (GNU libtool) 2.4.6 +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='libasr.0.dylib' + +# Names of this library. +library_names='libasr.0.dylib libasr.dylib' + +# The name of the static archive. +old_library='libasr.a' + +# Linker flags that cannot go in dependency_libs. +inherited_linker_flags=' ' + +# Libraries that this one depends upon. +dependency_libs=' -L/usr/local/Cellar/openssl@1.1/1.1.1d//lib -L/usr/local/lib -lz -lcrypto -lssl -levent -lresolv' + +# Names of additional weak libraries provided by this library +weak_library_names='' + +# Version information for libasr. +current=0 +age=0 +revision=3 + +# Is this an already installed library? +installed=no + +# Should we warn about portability when linking against -modules? +shouldnotlink=no + +# Files to dlopen/dlpreopen +dlopen='' +dlpreopen='' + +# Directory that this library needs to be installed in: +libdir='/tmp/lib' diff --git a/openbsd-compat/libasr/res_debug.c b/openbsd-compat/libasr/res_debug.c new file mode 100644 index 00000000..ca9c5ee0 --- /dev/null +++ b/openbsd-compat/libasr/res_debug.c @@ -0,0 +1,2 @@ +/* $OpenBSD: res_debug.c,v 1.1 2012/09/08 11:08:21 eric Exp $ */ +/* NOTHING */ diff --git a/openbsd-compat/libasr/res_init.c b/openbsd-compat/libasr/res_init.c new file mode 100644 index 00000000..04243c47 --- /dev/null +++ b/openbsd-compat/libasr/res_init.c @@ -0,0 +1,103 @@ +/* $OpenBSD: res_init.c,v 1.11 2019/06/17 05:54:45 otto Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <arpa/nameser.h> +#include <netinet/in.h> +#include <netdb.h> + +#include <asr.h> +#include <resolv.h> +#include <string.h> + +#include "asr_private.h" +#include "thread_private.h" + + +struct __res_state _res; +struct __res_state_ext _res_ext; + +int h_errno; + +int +res_init(void) +{ + static void *resinit_mutex; + struct asr_ctx *ac; + int i; + + ac = _asr_use_resolver(NULL); + + /* + * The first thread to call res_init() will setup the global _res + * structure from the async context, not overriding fields set early + * by the user. + */ + _MUTEX_LOCK(&resinit_mutex); + if (!(_res.options & RES_INIT)) { + if (_res.retry == 0) + _res.retry = ac->ac_nsretries; + if (_res.retrans == 0) + _res.retrans = ac->ac_nstimeout; + if (_res.options == 0) + _res.options = ac->ac_options; + if (_res.lookups[0] == '\0') + strlcpy(_res.lookups, ac->ac_db, sizeof(_res.lookups)); + + for (i = 0; i < ac->ac_nscount && i < MAXNS; i++) { + /* + * No need to check for length since we copy to a + * struct sockaddr_storage with a size of 256 bytes + * and sa_len has only 8 bits. + */ + memcpy(&_res_ext.nsaddr_list[i], ac->ac_ns[i], + ac->ac_ns[i]->sa_len); + if (ac->ac_ns[i]->sa_len <= sizeof(_res.nsaddr_list[i])) + memcpy(&_res.nsaddr_list[i], ac->ac_ns[i], + ac->ac_ns[i]->sa_len); + else + memset(&_res.nsaddr_list[i], 0, + sizeof(_res.nsaddr_list[i])); + } + _res.nscount = i; + _res.options |= RES_INIT; + } + _MUTEX_UNLOCK(&resinit_mutex); + + /* + * If the program is not threaded, we want to reflect (some) changes + * made by the user to the global _res structure. + * This is a bit of a hack: if there is already an async query on + * this context, it might change things in its back. It is ok + * as long as the user only uses the blocking resolver API. + * If needed we could consider cloning the context if there is + * a running query. + */ + if (!__isthreaded) { + ac->ac_nsretries = _res.retry; + ac->ac_nstimeout = _res.retrans; + ac->ac_options = _res.options; + strlcpy(ac->ac_db, _res.lookups, sizeof(ac->ac_db)); + ac->ac_dbcount = strlen(ac->ac_db); + } + + _asr_ctx_unref(ac); + + return (0); +} +DEF_WEAK(res_init); diff --git a/openbsd-compat/libasr/res_mkquery.c b/openbsd-compat/libasr/res_mkquery.c new file mode 100644 index 00000000..959ecc47 --- /dev/null +++ b/openbsd-compat/libasr/res_mkquery.c @@ -0,0 +1,119 @@ +/* $OpenBSD: res_mkquery.c,v 1.13 2019/01/14 06:49:42 otto Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/nameser.h> /* for MAXDNAME */ +#include <netdb.h> + +#include <asr.h> +#include <errno.h> +#include <resolv.h> +#include <string.h> + +#include "asr_private.h" + +/* This function is apparently needed by some ports. */ +int +res_mkquery(int op, const char *dname, int class, int type, + const unsigned char *data, int datalen, const unsigned char *newrr, + unsigned char *buf, int buflen) +{ + struct asr_ctx *ac; + struct asr_pack p; + struct asr_dns_header h; + char fqdn[MAXDNAME]; + char dn[MAXDNAME]; + + /* we currently only support QUERY */ + if (op != QUERY || data) + return (-1); + + if (dname[0] == '\0' || dname[strlen(dname) - 1] != '.') { + if (strlcpy(fqdn, dname, sizeof(fqdn)) >= sizeof(fqdn) || + strlcat(fqdn, ".", sizeof(fqdn)) >= sizeof(fqdn)) + return (-1); + dname = fqdn; + } + + if (_asr_dname_from_fqdn(dname, dn, sizeof(dn)) == -1) + return (-1); + + ac = _asr_use_resolver(NULL); + + memset(&h, 0, sizeof h); + h.id = res_randomid(); + if (ac->ac_options & RES_RECURSE) + h.flags |= RD_MASK; +#ifdef RES_USE_CD + if (ac->ac_options & RES_USE_CD) + h.flags |= CD_MASK; +#endif + h.qdcount = 1; + if (ac->ac_options & (RES_USE_EDNS0 | RES_USE_DNSSEC)) + h.arcount = 1; + + _asr_pack_init(&p, buf, buflen); + _asr_pack_header(&p, &h); + _asr_pack_query(&p, type, class, dn); + if (ac->ac_options & (RES_USE_EDNS0 | RES_USE_DNSSEC)) + _asr_pack_edns0(&p, MAXPACKETSZ, + ac->ac_options & RES_USE_DNSSEC); + + _asr_ctx_unref(ac); + + if (p.err) + return (-1); + + return (p.offset); +} + +/* + * This function is not documented, but used by sendmail. + * Put here because it uses asr_private.h too. + */ +int +res_querydomain(const char *name, + const char *domain, + int class, + int type, + u_char *answer, + int anslen) +{ + char fqdn[MAXDNAME], ndom[MAXDNAME]; + size_t n; + + /* we really want domain to end with a dot for now */ + if (domain && ((n = strlen(domain)) == 0 || domain[n - 1 ] != '.')) { + if (strlcpy(ndom, domain, sizeof(ndom)) >= sizeof(ndom) || + strlcat(ndom, ".", sizeof(ndom)) >= sizeof(ndom)) { + h_errno = NETDB_INTERNAL; + errno = EINVAL; + return (-1); + } + domain = ndom; + } + + if (_asr_make_fqdn(name, domain, fqdn, sizeof fqdn) == 0) { + h_errno = NETDB_INTERNAL; + errno = EINVAL; + return (-1); + } + + return (res_query(fqdn, class, type, answer, anslen)); +} diff --git a/openbsd-compat/libasr/res_query.c b/openbsd-compat/libasr/res_query.c new file mode 100644 index 00000000..3f891416 --- /dev/null +++ b/openbsd-compat/libasr/res_query.c @@ -0,0 +1,112 @@ +/* $OpenBSD: res_query.c,v 1.9 2015/10/05 02:57:16 guenther Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> + +#include <asr.h> +#include <errno.h> +#include <resolv.h> +#include <string.h> +#include <stdlib.h> + +int +res_query(const char *name, int class, int type, u_char *ans, int anslen) +{ + struct asr_query *as; + struct asr_result ar; + size_t len; + + res_init(); + + if (ans == NULL || anslen <= 0) { + h_errno = NO_RECOVERY; + errno = EINVAL; + return (-1); + } + + as = res_query_async(name, class, type, NULL); + if (as == NULL) { + if (errno == EINVAL) + h_errno = NO_RECOVERY; + else + h_errno = NETDB_INTERNAL; + return (-1); /* errno set */ + } + + asr_run_sync(as, &ar); + + if (ar.ar_errno) + errno = ar.ar_errno; + h_errno = ar.ar_h_errno; + + if (ar.ar_h_errno != NETDB_SUCCESS) + return (-1); + + len = anslen; + if (ar.ar_datalen < len) + len = ar.ar_datalen; + memmove(ans, ar.ar_data, len); + free(ar.ar_data); + + return (ar.ar_datalen); +} +DEF_WEAK(res_query); + +int +res_search(const char *name, int class, int type, u_char *ans, int anslen) +{ + struct asr_query *as; + struct asr_result ar; + size_t len; + + res_init(); + + if (ans == NULL || anslen <= 0) { + h_errno = NO_RECOVERY; + errno = EINVAL; + return (-1); + } + + as = res_search_async(name, class, type, NULL); + if (as == NULL) { + if (errno == EINVAL) + h_errno = NO_RECOVERY; + else + h_errno = NETDB_INTERNAL; + return (-1); /* errno set */ + } + + asr_run_sync(as, &ar); + + if (ar.ar_errno) + errno = ar.ar_errno; + h_errno = ar.ar_h_errno; + + if (ar.ar_h_errno != NETDB_SUCCESS) + return (-1); + + len = anslen; + if (ar.ar_datalen < len) + len = ar.ar_datalen; + memmove(ans, ar.ar_data, len); + free(ar.ar_data); + + return (ar.ar_datalen); +} diff --git a/openbsd-compat/libasr/res_search_async.c b/openbsd-compat/libasr/res_search_async.c new file mode 100644 index 00000000..6436ab85 --- /dev/null +++ b/openbsd-compat/libasr/res_search_async.c @@ -0,0 +1,327 @@ +/* $OpenBSD: res_search_async.c,v 1.21 2017/02/27 10:44:46 jca Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <arpa/nameser.h> +#include <netdb.h> + +#include <asr.h> +#include <errno.h> +#include <resolv.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "asr_private.h" + +static int res_search_async_run(struct asr_query *, struct asr_result *); +static size_t domcat(const char *, const char *, char *, size_t); + +/* + * Unlike res_query_async(), this function returns a valid packet only if + * h_errno is NETDB_SUCCESS. + */ +struct asr_query * +res_search_async(const char *name, int class, int type, void *asr) +{ + struct asr_ctx *ac; + struct asr_query *as; + + DPRINT("asr: res_search_async(\"%s\", %i, %i)\n", name, class, type); + + ac = _asr_use_resolver(asr); + as = _res_search_async_ctx(name, class, type, ac); + _asr_ctx_unref(ac); + + return (as); +} +DEF_WEAK(res_search_async); + +struct asr_query * +_res_search_async_ctx(const char *name, int class, int type, struct asr_ctx *ac) +{ + struct asr_query *as; + + DPRINT("asr: res_search_async_ctx(\"%s\", %i, %i)\n", name, class, + type); + + if ((as = _asr_async_new(ac, ASR_SEARCH)) == NULL) + goto err; /* errno set */ + as->as_run = res_search_async_run; + if ((as->as.search.name = strdup(name)) == NULL) + goto err; /* errno set */ + + as->as.search.class = class; + as->as.search.type = type; + + return (as); + err: + if (as) + _asr_async_free(as); + return (NULL); +} + +#define HERRNO_UNSET -2 + +static int +res_search_async_run(struct asr_query *as, struct asr_result *ar) +{ + int r; + char fqdn[MAXDNAME]; + + next: + switch (as->as_state) { + + case ASR_STATE_INIT: + + if (as->as.search.name[0] == '\0') { + ar->ar_h_errno = NO_DATA; + async_set_state(as, ASR_STATE_HALT); + break; + } + + as->as.search.saved_h_errno = HERRNO_UNSET; + async_set_state(as, ASR_STATE_NEXT_DOMAIN); + break; + + case ASR_STATE_NEXT_DOMAIN: + /* + * Reset flags to be able to identify the case in + * STATE_SUBQUERY. + */ + as->as_dom_flags = 0; + + r = _asr_iter_domain(as, as->as.search.name, fqdn, sizeof(fqdn)); + if (r == -1) { + async_set_state(as, ASR_STATE_NOT_FOUND); + break; + } + if (r == 0) { + ar->ar_errno = EINVAL; + ar->ar_h_errno = NO_RECOVERY; + ar->ar_datalen = -1; + ar->ar_data = NULL; + async_set_state(as, ASR_STATE_HALT); + break; + } + as->as_subq = _res_query_async_ctx(fqdn, + as->as.search.class, as->as.search.type, as->as_ctx); + if (as->as_subq == NULL) { + ar->ar_errno = errno; + if (errno == EINVAL) + ar->ar_h_errno = NO_RECOVERY; + else + ar->ar_h_errno = NETDB_INTERNAL; + ar->ar_datalen = -1; + ar->ar_data = NULL; + async_set_state(as, ASR_STATE_HALT); + break; + } + async_set_state(as, ASR_STATE_SUBQUERY); + break; + + case ASR_STATE_SUBQUERY: + + if ((r = asr_run(as->as_subq, ar)) == ASYNC_COND) + return (ASYNC_COND); + as->as_subq = NULL; + + if (ar->ar_h_errno == NETDB_SUCCESS) { + async_set_state(as, ASR_STATE_HALT); + break; + } + + /* + * The original res_search() does this in the domain search + * loop, but only for ECONNREFUSED. I think we can do better + * because technically if we get an errno, it means + * we couldn't reach any nameserver, so there is no point + * in trying further. + */ + if (ar->ar_errno) { + async_set_state(as, ASR_STATE_HALT); + break; + } + + free(ar->ar_data); + + /* + * The original resolver does something like this. + */ + if (as->as_dom_flags & ASYNC_DOM_NDOTS) + as->as.search.saved_h_errno = ar->ar_h_errno; + + if (as->as_dom_flags & ASYNC_DOM_DOMAIN) { + if (ar->ar_h_errno == NO_DATA) + as->as_flags |= ASYNC_NODATA; + else if (ar->ar_h_errno == TRY_AGAIN) + as->as_flags |= ASYNC_AGAIN; + } + + async_set_state(as, ASR_STATE_NEXT_DOMAIN); + break; + + case ASR_STATE_NOT_FOUND: + + if (as->as.search.saved_h_errno != HERRNO_UNSET) + ar->ar_h_errno = as->as.search.saved_h_errno; + else if (as->as_flags & ASYNC_NODATA) + ar->ar_h_errno = NO_DATA; + else if (as->as_flags & ASYNC_AGAIN) + ar->ar_h_errno = TRY_AGAIN; + /* + * Else, we got the ar_h_errno value set by res_query_async() + * for the last domain. + */ + ar->ar_datalen = -1; + ar->ar_data = NULL; + async_set_state(as, ASR_STATE_HALT); + break; + + case ASR_STATE_HALT: + + return (ASYNC_DONE); + + default: + ar->ar_errno = EOPNOTSUPP; + ar->ar_h_errno = NETDB_INTERNAL; + async_set_state(as, ASR_STATE_HALT); + break; + } + goto next; +} + +/* + * Concatenate a name and a domain name. The result has no trailing dot. + * Return the resulting string length, or 0 in case of error. + */ +static size_t +domcat(const char *name, const char *domain, char *buf, size_t buflen) +{ + size_t r; + + r = _asr_make_fqdn(name, domain, buf, buflen); + if (r == 0) + return (0); + buf[r - 1] = '\0'; + + return (r - 1); +} + +enum { + DOM_INIT, + DOM_DOMAIN, + DOM_DONE +}; + +/* + * Implement the search domain strategy. + * + * This function works as a generator that constructs complete domains in + * buffer "buf" of size "len" for the given host name "name", according to the + * search rules defined by the resolving context. It is supposed to be called + * multiple times (with the same name) to generate the next possible domain + * name, if any. + * + * It returns -1 if all possibilities have been exhausted, 0 if there was an + * error generating the next name, or the resulting name length. + */ +int +_asr_iter_domain(struct asr_query *as, const char *name, char * buf, size_t len) +{ + const char *c; + int dots; + + switch (as->as_dom_step) { + + case DOM_INIT: + /* First call */ + + /* + * If "name" is an FQDN, that's the only result and we + * don't try anything else. + */ + if (strlen(name) && name[strlen(name) - 1] == '.') { + DPRINT("asr: iter_domain(\"%s\") fqdn\n", name); + as->as_dom_flags |= ASYNC_DOM_FQDN; + as->as_dom_step = DOM_DONE; + return (domcat(name, NULL, buf, len)); + } + + /* + * Otherwise, we iterate through the specified search domains. + */ + as->as_dom_step = DOM_DOMAIN; + as->as_dom_idx = 0; + + /* + * If "name" as enough dots, use it as-is first, as indicated + * in resolv.conf(5). + */ + dots = 0; + for (c = name; *c; c++) + dots += (*c == '.'); + if (dots >= as->as_ctx->ac_ndots) { + DPRINT("asr: iter_domain(\"%s\") ndots\n", name); + as->as_dom_flags |= ASYNC_DOM_NDOTS; + if (strlcpy(buf, name, len) >= len) + return (0); + return (strlen(buf)); + } + /* Otherwise, starts using the search domains */ + /* FALLTHROUGH */ + + case DOM_DOMAIN: + if (as->as_dom_idx < as->as_ctx->ac_domcount && + (as->as_ctx->ac_options & RES_DNSRCH || ( + as->as_ctx->ac_options & RES_DEFNAMES && + as->as_dom_idx == 0 && + strchr(name, '.') == NULL))) { + DPRINT("asr: iter_domain(\"%s\") domain \"%s\"\n", + name, as->as_ctx->ac_dom[as->as_dom_idx]); + as->as_dom_flags |= ASYNC_DOM_DOMAIN; + return (domcat(name, + as->as_ctx->ac_dom[as->as_dom_idx++], buf, len)); + } + + /* No more domain to try. */ + + as->as_dom_step = DOM_DONE; + + /* + * If the name was not tried as an absolute name before, + * do it now. + */ + if (!(as->as_dom_flags & ASYNC_DOM_NDOTS)) { + DPRINT("asr: iter_domain(\"%s\") as is\n", name); + as->as_dom_flags |= ASYNC_DOM_ASIS; + if (strlcpy(buf, name, len) >= len) + return (0); + return (strlen(buf)); + } + /* Otherwise, we are done. */ + + case DOM_DONE: + default: + DPRINT("asr: iter_domain(\"%s\") done\n", name); + return (-1); + } +} diff --git a/openbsd-compat/libasr/res_send.c b/openbsd-compat/libasr/res_send.c new file mode 100644 index 00000000..32c94081 --- /dev/null +++ b/openbsd-compat/libasr/res_send.c @@ -0,0 +1,61 @@ +/* $OpenBSD: res_send.c,v 1.8 2014/03/26 18:13:15 eric Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> + +#include <asr.h> +#include <errno.h> +#include <resolv.h> +#include <string.h> +#include <stdlib.h> + +int +res_send(const u_char *buf, int buflen, u_char *ans, int anslen) +{ + struct asr_query *as; + struct asr_result ar; + size_t len; + + res_init(); + + if (ans == NULL || anslen <= 0) { + errno = EINVAL; + return (-1); + } + + as = res_send_async(buf, buflen, NULL); + if (as == NULL) + return (-1); /* errno set */ + + asr_run_sync(as, &ar); + + if (ar.ar_errno) { + errno = ar.ar_errno; + return (-1); + } + + len = anslen; + if (ar.ar_datalen < len) + len = ar.ar_datalen; + memmove(ans, ar.ar_data, len); + free(ar.ar_data); + + return (ar.ar_datalen); +} diff --git a/openbsd-compat/libasr/res_send_async.c b/openbsd-compat/libasr/res_send_async.c new file mode 100644 index 00000000..7eeeef48 --- /dev/null +++ b/openbsd-compat/libasr/res_send_async.c @@ -0,0 +1,806 @@ +/* $OpenBSD: res_send_async.c,v 1.39 2019/09/28 11:21:07 eric Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#ifdef HAVE_ARPA_NAMESER_COMPAT_H +#include <arpa/nameser_compat.h> +#endif +#include <netdb.h> + +#include <asr.h> +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <resolv.h> /* for res_random */ +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "asr_private.h" + +#define OP_QUERY (0) + +static int res_send_async_run(struct asr_query *, struct asr_result *); +static int sockaddr_connect(const struct sockaddr *, int); +static int udp_send(struct asr_query *); +static int udp_recv(struct asr_query *); +static int tcp_write(struct asr_query *); +static int tcp_read(struct asr_query *); +static int validate_packet(struct asr_query *); +static int setup_query(struct asr_query *, const char *, const char *, int, int); +static int ensure_ibuf(struct asr_query *, size_t); +static int iter_ns(struct asr_query *); + +#define AS_NS_SA(p) ((p)->as_ctx->ac_ns[(p)->as.dns.nsidx - 1]) + + +struct asr_query * +res_send_async(const unsigned char *buf, int buflen, void *asr) +{ + struct asr_ctx *ac; + struct asr_query *as; + struct asr_unpack p; + struct asr_dns_header h; + struct asr_dns_query q; + + DPRINT_PACKET("asr: res_send_async()", buf, buflen); + + ac = _asr_use_resolver(asr); + if ((as = _asr_async_new(ac, ASR_SEND)) == NULL) { + _asr_ctx_unref(ac); + return (NULL); /* errno set */ + } + as->as_run = res_send_async_run; + + as->as_flags |= ASYNC_EXTOBUF; + as->as.dns.obuf = (unsigned char *)buf; + as->as.dns.obuflen = buflen; + as->as.dns.obufsize = buflen; + + _asr_unpack_init(&p, buf, buflen); + _asr_unpack_header(&p, &h); + _asr_unpack_query(&p, &q); + if (p.err) { + errno = EINVAL; + goto err; + } + as->as.dns.reqid = h.id; + as->as.dns.type = q.q_type; + as->as.dns.class = q.q_class; + as->as.dns.dname = strdup(q.q_dname); + if (as->as.dns.dname == NULL) + goto err; /* errno set */ + + _asr_ctx_unref(ac); + return (as); + err: + if (as) + _asr_async_free(as); + _asr_ctx_unref(ac); + return (NULL); +} +DEF_WEAK(res_send_async); + +/* + * Unlike res_query(), this version will actually return the packet + * if it has received a valid one (errno == 0) even if h_errno is + * not NETDB_SUCCESS. So the packet *must* be freed if necessary. + */ +struct asr_query * +res_query_async(const char *name, int class, int type, void *asr) +{ + struct asr_ctx *ac; + struct asr_query *as; + + DPRINT("asr: res_query_async(\"%s\", %i, %i)\n", name, class, type); + + ac = _asr_use_resolver(asr); + as = _res_query_async_ctx(name, class, type, ac); + _asr_ctx_unref(ac); + + return (as); +} +DEF_WEAK(res_query_async); + +struct asr_query * +_res_query_async_ctx(const char *name, int class, int type, struct asr_ctx *a_ctx) +{ + struct asr_query *as; + + DPRINT("asr: res_query_async_ctx(\"%s\", %i, %i)\n", name, class, type); + + if ((as = _asr_async_new(a_ctx, ASR_SEND)) == NULL) + return (NULL); /* errno set */ + as->as_run = res_send_async_run; + + /* This adds a "." to name if it doesn't already has one. + * That's how res_query() behaves (through res_mkquery"). + */ + if (setup_query(as, name, NULL, class, type) == -1) + goto err; /* errno set */ + + return (as); + + err: + if (as) + _asr_async_free(as); + + return (NULL); +} + +static int +res_send_async_run(struct asr_query *as, struct asr_result *ar) +{ + next: + switch (as->as_state) { + + case ASR_STATE_INIT: + + if (as->as_ctx->ac_nscount == 0) { + ar->ar_errno = ECONNREFUSED; + async_set_state(as, ASR_STATE_HALT); + break; + } + + async_set_state(as, ASR_STATE_NEXT_NS); + break; + + case ASR_STATE_NEXT_NS: + + if (iter_ns(as) == -1) { + ar->ar_errno = ETIMEDOUT; + async_set_state(as, ASR_STATE_HALT); + break; + } + + if (as->as_ctx->ac_options & RES_USEVC || + as->as.dns.obuflen > PACKETSZ) + async_set_state(as, ASR_STATE_TCP_WRITE); + else + async_set_state(as, ASR_STATE_UDP_SEND); + break; + + case ASR_STATE_UDP_SEND: + + if (udp_send(as) == -1) { + async_set_state(as, ASR_STATE_NEXT_NS); + break; + } + async_set_state(as, ASR_STATE_UDP_RECV); + ar->ar_cond = ASR_WANT_READ; + ar->ar_fd = as->as_fd; + ar->ar_timeout = as->as_timeout; + return (ASYNC_COND); + break; + + case ASR_STATE_UDP_RECV: + + if (udp_recv(as) == -1) { + if (errno == ENOMEM) { + ar->ar_errno = errno; + async_set_state(as, ASR_STATE_HALT); + break; + } + if (errno != EOVERFLOW) { + /* Fail or timeout */ + async_set_state(as, ASR_STATE_NEXT_NS); + break; + } + if (as->as_ctx->ac_options & RES_IGNTC) + async_set_state(as, ASR_STATE_PACKET); + else + async_set_state(as, ASR_STATE_TCP_WRITE); + } else + async_set_state(as, ASR_STATE_PACKET); + break; + + case ASR_STATE_TCP_WRITE: + + switch (tcp_write(as)) { + case -1: /* fail or timeout */ + async_set_state(as, ASR_STATE_NEXT_NS); + break; + case 0: + async_set_state(as, ASR_STATE_TCP_READ); + ar->ar_cond = ASR_WANT_READ; + ar->ar_fd = as->as_fd; + ar->ar_timeout = as->as_timeout; + return (ASYNC_COND); + case 1: + ar->ar_cond = ASR_WANT_WRITE; + ar->ar_fd = as->as_fd; + ar->ar_timeout = as->as_timeout; + return (ASYNC_COND); + } + break; + + case ASR_STATE_TCP_READ: + + switch (tcp_read(as)) { + case -1: /* Fail or timeout */ + if (errno == ENOMEM) { + ar->ar_errno = errno; + async_set_state(as, ASR_STATE_HALT); + } else + async_set_state(as, ASR_STATE_NEXT_NS); + break; + case 0: + async_set_state(as, ASR_STATE_PACKET); + break; + case 1: + ar->ar_cond = ASR_WANT_READ; + ar->ar_fd = as->as_fd; + ar->ar_timeout = as->as_timeout; + return (ASYNC_COND); + } + break; + + case ASR_STATE_PACKET: + + memmove(&ar->ar_ns, AS_NS_SA(as), SA_LEN(AS_NS_SA(as))); + ar->ar_datalen = as->as.dns.ibuflen; + ar->ar_data = as->as.dns.ibuf; + as->as.dns.ibuf = NULL; + ar->ar_errno = 0; + ar->ar_rcode = as->as.dns.rcode; + async_set_state(as, ASR_STATE_HALT); + break; + + case ASR_STATE_HALT: + + if (ar->ar_errno) { + ar->ar_h_errno = TRY_AGAIN; + ar->ar_count = 0; + ar->ar_datalen = -1; + ar->ar_data = NULL; + } else if (as->as.dns.ancount) { + ar->ar_h_errno = NETDB_SUCCESS; + ar->ar_count = as->as.dns.ancount; + } else { + ar->ar_count = 0; + switch (as->as.dns.rcode) { + case NXDOMAIN: + ar->ar_h_errno = HOST_NOT_FOUND; + break; + case SERVFAIL: + ar->ar_h_errno = TRY_AGAIN; + break; + case NOERROR: + ar->ar_h_errno = NO_DATA; + break; + default: + ar->ar_h_errno = NO_RECOVERY; + } + } + return (ASYNC_DONE); + + default: + + ar->ar_errno = EOPNOTSUPP; + ar->ar_h_errno = NETDB_INTERNAL; + async_set_state(as, ASR_STATE_HALT); + break; + } + goto next; +} + +static int +sockaddr_connect(const struct sockaddr *sa, int socktype) +{ + int errno_save, sock, flags; + + if ((sock = socket(sa->sa_family, socktype, 0)) == -1) + goto fail; + + if ((flags = fcntl(sock, F_GETFL, 0)) == -1) + goto fail; + + flags |= O_NONBLOCK; + + if ((flags = fcntl(sock, F_SETFL, flags)) == -1) + goto fail; + + if (connect(sock, sa, SA_LEN(sa)) == -1) { + /* + * In the TCP case, the caller will be asked to poll for + * POLLOUT so that we start writing the packet in tcp_write() + * when the connection is established, or fail there on error. + */ + if (errno == EINPROGRESS) + return (sock); + goto fail; + } + + return (sock); + + fail: + + if (sock != -1) { + errno_save = errno; + close(sock); + errno = errno_save; + } + + return (-1); +} + +/* + * Prepare the DNS packet for the query type "type", class "class" and domain + * name created by the concatenation on "name" and "dom". + * Return 0 on success, set errno and return -1 on error. + */ +static int +setup_query(struct asr_query *as, const char *name, const char *dom, + int class, int type) +{ + struct asr_pack p; + struct asr_dns_header h; + char fqdn[MAXDNAME]; + char dname[MAXDNAME]; + + if (as->as_flags & ASYNC_EXTOBUF) { + errno = EINVAL; + DPRINT("attempting to write in user packet"); + return (-1); + } + + if (_asr_make_fqdn(name, dom, fqdn, sizeof(fqdn)) > sizeof(fqdn)) { + errno = EINVAL; + DPRINT("asr_make_fqdn: name too long\n"); + return (-1); + } + + if (_asr_dname_from_fqdn(fqdn, dname, sizeof(dname)) == -1) { + errno = EINVAL; + DPRINT("asr_dname_from_fqdn: invalid\n"); + return (-1); + } + + if (as->as.dns.obuf == NULL) { + as->as.dns.obufsize = PACKETSZ; + as->as.dns.obuf = malloc(as->as.dns.obufsize); + if (as->as.dns.obuf == NULL) + return (-1); /* errno set */ + } + as->as.dns.obuflen = 0; + + memset(&h, 0, sizeof h); + h.id = res_randomid(); + if (as->as_ctx->ac_options & RES_RECURSE) + h.flags |= RD_MASK; +#ifdef RES_USE_CD + if (as->as_ctx->ac_options & RES_USE_CD) + h.flags |= CD_MASK; +#endif + h.qdcount = 1; + if (as->as_ctx->ac_options & (RES_USE_EDNS0 | RES_USE_DNSSEC)) + h.arcount = 1; + + _asr_pack_init(&p, as->as.dns.obuf, as->as.dns.obufsize); + _asr_pack_header(&p, &h); + _asr_pack_query(&p, type, class, dname); + if (as->as_ctx->ac_options & (RES_USE_EDNS0 | RES_USE_DNSSEC)) + _asr_pack_edns0(&p, MAXPACKETSZ, + as->as_ctx->ac_options & RES_USE_DNSSEC); + if (p.err) { + DPRINT("error packing query"); + errno = EINVAL; + return (-1); + } + + /* Remember the parameters. */ + as->as.dns.reqid = h.id; + as->as.dns.type = type; + as->as.dns.class = class; + if (as->as.dns.dname) + free(as->as.dns.dname); + as->as.dns.dname = strdup(dname); + if (as->as.dns.dname == NULL) { + DPRINT("strdup"); + return (-1); /* errno set */ + } + as->as.dns.obuflen = p.offset; + + DPRINT_PACKET("asr_setup_query", as->as.dns.obuf, as->as.dns.obuflen); + + return (0); +} + +/* + * Create a connect UDP socket and send the output packet. + * + * Return 0 on success, or -1 on error (errno set). + */ +static int +udp_send(struct asr_query *as) +{ + ssize_t n; + int save_errno; +#ifdef DEBUG + char buf[256]; +#endif + + DPRINT("asr: [%p] connecting to %s UDP\n", as, + _asr_print_sockaddr(AS_NS_SA(as), buf, sizeof buf)); + + as->as_fd = sockaddr_connect(AS_NS_SA(as), SOCK_DGRAM); + if (as->as_fd == -1) + return (-1); /* errno set */ + + n = send(as->as_fd, as->as.dns.obuf, as->as.dns.obuflen, 0); + if (n == -1) { + save_errno = errno; + close(as->as_fd); + errno = save_errno; + as->as_fd = -1; + return (-1); + } + + return (0); +} + +/* + * Try to receive a valid packet from the current UDP socket. + * + * Return 0 if a full packet could be read, or -1 on error (errno set). + */ +static int +udp_recv(struct asr_query *as) +{ + ssize_t n; + int save_errno; + + if (ensure_ibuf(as, MAXPACKETSZ) == -1) { + save_errno = errno; + close(as->as_fd); + errno = save_errno; + as->as_fd = -1; + return (-1); + } + + n = recv(as->as_fd, as->as.dns.ibuf, as->as.dns.ibufsize, 0); + save_errno = errno; + close(as->as_fd); + errno = save_errno; + as->as_fd = -1; + if (n == -1) + return (-1); + + as->as.dns.ibuflen = n; + + DPRINT_PACKET("asr_udp_recv()", as->as.dns.ibuf, as->as.dns.ibuflen); + + if (validate_packet(as) == -1) + return (-1); /* errno set */ + + return (0); +} + +/* + * Write the output packet to the TCP socket. + * + * Return 0 when all bytes have been sent, 1 there is no buffer space on the + * socket or it is not connected yet, or -1 on error (errno set). + */ +static int +tcp_write(struct asr_query *as) +{ + struct msghdr msg; + struct iovec iov[2]; + uint16_t len; + ssize_t n; + size_t offset; + int i; +#ifdef DEBUG + char buf[256]; +#endif + + /* First try to connect if not already */ + if (as->as_fd == -1) { + DPRINT("asr: [%p] connecting to %s TCP\n", as, + _asr_print_sockaddr(AS_NS_SA(as), buf, sizeof buf)); + as->as_fd = sockaddr_connect(AS_NS_SA(as), SOCK_STREAM); + if (as->as_fd == -1) + return (-1); /* errno set */ +/* + * Some systems (MacOS X) have SO_NOSIGPIPE instead of MSG_NOSIGNAL. + * If neither is available the system is probably broken. We might + * want to detect this at configure time. + */ +#ifdef SO_NOSIGPIPE + i = 1; + if (setsockopt(as->as_fd, SOL_SOCKET, SO_NOSIGPIPE, (void *)&i, + sizeof(i)) == -1) + return (-1); /* errno set */ +#endif + as->as.dns.datalen = 0; /* bytes sent */ + return (1); + } + + i = 0; + + /* Prepend de packet length if not sent already. */ + if (as->as.dns.datalen < sizeof(len)) { + offset = 0; + len = htons(as->as.dns.obuflen); + iov[i].iov_base = (char *)(&len) + as->as.dns.datalen; + iov[i].iov_len = sizeof(len) - as->as.dns.datalen; + i++; + } else + offset = as->as.dns.datalen - sizeof(len); + + iov[i].iov_base = as->as.dns.obuf + offset; + iov[i].iov_len = as->as.dns.obuflen - offset; + i++; + + memset(&msg, 0, sizeof msg); + msg.msg_iov = iov; + msg.msg_iovlen = i; + + send_again: +/* See above. */ +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif + n = sendmsg(as->as_fd, &msg, MSG_NOSIGNAL); + if (n == -1) { + if (errno == EINTR) + goto send_again; + goto close; /* errno set */ + } + + as->as.dns.datalen += n; + + if (as->as.dns.datalen == as->as.dns.obuflen + sizeof(len)) { + /* All sent. Prepare for TCP read */ + as->as.dns.datalen = 0; + return (0); + } + + /* More data to write */ + return (1); + +close: + close(as->as_fd); + as->as_fd = -1; + return (-1); +} + +/* + * Try to read a valid packet from the current TCP socket. + * + * Return 0 if a full packet could be read, 1 if more data is needed and the + * socket must be read again, or -1 on error (errno set). + */ +static int +tcp_read(struct asr_query *as) +{ + ssize_t n; + size_t offset, len; + char *pos; + int save_errno, nfds; + struct pollfd pfd; + + /* We must read the packet len first */ + if (as->as.dns.datalen < sizeof(as->as.dns.pktlen)) { + + pos = (char *)(&as->as.dns.pktlen) + as->as.dns.datalen; + len = sizeof(as->as.dns.pktlen) - as->as.dns.datalen; + + read_again0: + n = read(as->as_fd, pos, len); + if (n == -1) { + if (errno == EINTR) + goto read_again0; + goto close; /* errno set */ + } + if (n == 0) { + errno = ECONNRESET; + goto close; + } + as->as.dns.datalen += n; + if (as->as.dns.datalen < sizeof(as->as.dns.pktlen)) + return (1); /* need more data */ + + as->as.dns.ibuflen = ntohs(as->as.dns.pktlen); + if (ensure_ibuf(as, as->as.dns.ibuflen) == -1) + goto close; /* errno set */ + + pfd.fd = as->as_fd; + pfd.events = POLLIN; + poll_again: + nfds = poll(&pfd, 1, 0); + if (nfds == -1) { + if (errno == EINTR) + goto poll_again; + goto close; /* errno set */ + } + if (nfds == 0) + return (1); /* no more data available */ + } + + offset = as->as.dns.datalen - sizeof(as->as.dns.pktlen); + pos = as->as.dns.ibuf + offset; + len = as->as.dns.ibuflen - offset; + + read_again: + n = read(as->as_fd, pos, len); + if (n == -1) { + if (errno == EINTR) + goto read_again; + goto close; /* errno set */ + } + if (n == 0) { + errno = ECONNRESET; + goto close; + } + as->as.dns.datalen += n; + + /* See if we got all the advertised bytes. */ + if (as->as.dns.datalen != as->as.dns.ibuflen + sizeof(as->as.dns.pktlen)) + return (1); + + DPRINT_PACKET("asr_tcp_read()", as->as.dns.ibuf, as->as.dns.ibuflen); + + if (validate_packet(as) == -1) + goto close; /* errno set */ + + errno = 0; +close: + save_errno = errno; + close(as->as_fd); + errno = save_errno; + as->as_fd = -1; + return (errno ? -1 : 0); +} + +/* + * Make sure the input buffer is at least "n" bytes long, and allocate or + * extend it if necessary. Return 0 on success, or set errno and return -1. + */ +static int +ensure_ibuf(struct asr_query *as, size_t n) +{ + char *t; + + if (as->as.dns.ibufsize >= n) + return (0); + + t = recallocarray(as->as.dns.ibuf, as->as.dns.ibufsize, n, 1); + if (t == NULL) + return (-1); /* errno set */ + as->as.dns.ibuf = t; + as->as.dns.ibufsize = n; + + return (0); +} + +/* + * Check if the received packet is valid. + * Return 0 on success, or set errno and return -1. + */ +static int +validate_packet(struct asr_query *as) +{ + struct asr_unpack p; + struct asr_dns_header h; + struct asr_dns_query q; + struct asr_dns_rr rr; + int r; + + _asr_unpack_init(&p, as->as.dns.ibuf, as->as.dns.ibuflen); + + _asr_unpack_header(&p, &h); + if (p.err) + goto inval; + + if (h.id != as->as.dns.reqid) { + DPRINT("incorrect reqid\n"); + goto inval; + } + if (h.qdcount != 1) + goto inval; + /* Should be zero, we could allow this */ + if ((h.flags & Z_MASK) != 0) + goto inval; + /* Actually, it depends on the request but we only use OP_QUERY */ + if (OPCODE(h.flags) != OP_QUERY) + goto inval; + /* Must be a response */ + if ((h.flags & QR_MASK) == 0) + goto inval; + + as->as.dns.rcode = RCODE(h.flags); + as->as.dns.ancount = h.ancount; + + _asr_unpack_query(&p, &q); + if (p.err) + goto inval; + + if (q.q_type != as->as.dns.type || + q.q_class != as->as.dns.class || + strcasecmp(q.q_dname, as->as.dns.dname)) { + DPRINT("incorrect type/class/dname '%s' != '%s'\n", + q.q_dname, as->as.dns.dname); + goto inval; + } + + /* Check for truncation */ + if (h.flags & TC_MASK && !(as->as_ctx->ac_options & RES_IGNTC)) { + DPRINT("truncated\n"); + errno = EOVERFLOW; + return (-1); + } + + /* Validate the rest of the packet */ + for (r = h.ancount + h.nscount + h.arcount; r; r--) + _asr_unpack_rr(&p, &rr); + + /* Report any error found when unpacking the RRs. */ + if (p.err) { + DPRINT("unpack: %s\n", strerror(p.err)); + errno = p.err; + return (-1); + } + + if (p.offset != as->as.dns.ibuflen) { + DPRINT("trailing garbage\n"); + errno = EMSGSIZE; + return (-1); + } + + return (0); + + inval: + errno = EINVAL; + return (-1); +} + +/* + * Set the async context nameserver index to the next nameserver, cycling + * over the list until the maximum retry counter is reached. Return 0 on + * success, or -1 if all nameservers were used. + */ +static int +iter_ns(struct asr_query *as) +{ + for (;;) { + if (as->as.dns.nsloop >= as->as_ctx->ac_nsretries) + return (-1); + + as->as.dns.nsidx += 1; + if (as->as.dns.nsidx <= as->as_ctx->ac_nscount) + break; + as->as.dns.nsidx = 0; + as->as.dns.nsloop++; + DPRINT("asr: iter_ns(): cycle %i\n", as->as.dns.nsloop); + } + + as->as_timeout = 1000 * (as->as_ctx->ac_nstimeout << as->as.dns.nsloop); + if (as->as.dns.nsloop > 0) + as->as_timeout /= as->as_ctx->ac_nscount; + if (as->as_timeout < 1000) + as->as_timeout = 1000; + + return (0); +} diff --git a/openbsd-compat/libasr/sethostent.c b/openbsd-compat/libasr/sethostent.c new file mode 100644 index 00000000..61fa3e2f --- /dev/null +++ b/openbsd-compat/libasr/sethostent.c @@ -0,0 +1,36 @@ +/* $OpenBSD: sethostent.c,v 1.2 2018/04/28 15:09:35 schwarze Exp $ */ +/* + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <netdb.h> +#include <stddef.h> + +void +sethostent(int stayopen) +{ +} + +void +endhostent(void) +{ +} + +struct hostent * +gethostent(void) +{ + h_errno = NETDB_INTERNAL; + return NULL; +} diff --git a/openbsd-compat/libasr/thread_private.h b/openbsd-compat/libasr/thread_private.h new file mode 100644 index 00000000..23951975 --- /dev/null +++ b/openbsd-compat/libasr/thread_private.h @@ -0,0 +1,8 @@ +/* + * + */ +#define __is_threaded (0) +#define _THREAD_PRIVATE_MUTEX(x) +#define _THREAD_PRIVATE_MUTEX_LOCK(x) +#define _THREAD_PRIVATE_MUTEX_UNLOCK(x) +#define _THREAD_PRIVATE(a, b, c) (c) diff --git a/openbsd-compat/libressl.c b/openbsd-compat/libressl.c new file mode 100644 index 00000000..f4f2b52e --- /dev/null +++ b/openbsd-compat/libressl.c @@ -0,0 +1,131 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +#include <sys/types.h> + +#include <unistd.h> +#include <stdio.h> + +#include <openssl/err.h> +#include <openssl/bio.h> +#include <openssl/objects.h> +#include <openssl/evp.h> +#include <openssl/x509.h> +#include <openssl/pem.h> +#include <openssl/ssl.h> + +int +SSL_CTX_use_certificate_chain(SSL_CTX *ctx, char *buf, off_t len) +{ + int ret; + BIO*in; + X509*x; + X509*ca; + unsigned long err; + + ret = 0; + x = ca = NULL; + + if ((in = BIO_new_mem_buf(buf, len)) == NULL) { + SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_BUF_LIB); + goto end; + } + + if ((x = PEM_read_bio_X509(in, NULL, + ctx->default_passwd_callback, + ctx->default_passwd_callback_userdata)) == NULL) { + SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_PEM_LIB); + goto end; + } + + if (!SSL_CTX_use_certificate(ctx, x) || ERR_peek_error() != 0) + goto end; + + /* If we could set up our certificate, now proceed to + * the CA certificates. + */ + + if (ctx->extra_certs != NULL) { + sk_X509_pop_free(ctx->extra_certs, X509_free); + ctx->extra_certs = NULL; + } + + while ((ca = PEM_read_bio_X509(in, NULL, + ctx->default_passwd_callback, + ctx->default_passwd_callback_userdata)) != NULL) { + + if (!SSL_CTX_add_extra_chain_cert(ctx, ca)) + goto end; + } + + err = ERR_peek_last_error(); + if (ERR_GET_LIB(err) == ERR_LIB_PEM && + ERR_GET_REASON(err) == PEM_R_NO_START_LINE) + ERR_clear_error(); + else + goto end; + + ret = 1; +end: + if (ca != NULL) + X509_free(ca); + if (x != NULL) + X509_free(x); + if (in != NULL) + BIO_free(in); + return (ret); +} diff --git a/openbsd-compat/nanosleep.c b/openbsd-compat/nanosleep.c new file mode 100644 index 00000000..1256c0b5 --- /dev/null +++ b/openbsd-compat/nanosleep.c @@ -0,0 +1,63 @@ + +/* + * Copyright (c) 1999-2004 Damien Miller <djm@mindrot.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/types.h> +#ifdef HAVE_SYS_SELECT_H +# include <sys/select.h> +#endif +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#endif + +#include <err.h> +#include <string.h> +#include <signal.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +int +nanosleep(const struct timespec *req, struct timespec *rem) +{ + int rc, saverrno; + extern int errno; + struct timeval tstart, tstop, tremain, time2wait; + + TIMESPEC_TO_TIMEVAL(&time2wait, req); + (void) gettimeofday(&tstart, NULL); + rc = select(0, NULL, NULL, NULL, &time2wait); + if (rc == -1) { + saverrno = errno; + (void) gettimeofday (&tstop, NULL); + errno = saverrno; + tremain.tv_sec = time2wait.tv_sec - + (tstop.tv_sec - tstart.tv_sec); + tremain.tv_usec = time2wait.tv_usec - + (tstop.tv_usec - tstart.tv_usec); + tremain.tv_sec += tremain.tv_usec / 1000000L; + tremain.tv_usec %= 1000000L; + } else { + tremain.tv_sec = 0; + tremain.tv_usec = 0; + } + if (rem != NULL) + TIMEVAL_TO_TIMESPEC(&tremain, rem); + + return(rc); +} diff --git a/openbsd-compat/openbsd-compat.h b/openbsd-compat/openbsd-compat.h new file mode 100644 index 00000000..dcb643f1 --- /dev/null +++ b/openbsd-compat/openbsd-compat.h @@ -0,0 +1,342 @@ +/* $Id: openbsd-compat.h,v 1.51 2010/10/07 10:25:29 djm Exp $ */ + +/* + * Copyright (c) 1999-2003 Damien Miller. All rights reserved. + * Copyright (c) 2003 Ben Lindstrom. All rights reserved. + * Copyright (c) 2002 Tim Rice. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 _OPENBSD_COMPAT_H +#define _OPENBSD_COMPAT_H + +#include "includes.h" + +#include <sys/types.h> + +#include <sys/socket.h> +#include <netinet/in.h> + +/* OpenBSD function replacements */ +#include "base64.h" + +#include <sys/queue.h> +#include <sys/tree.h> +#include "bsd-vis.h" + +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +#ifndef HAVE_BASENAME +char *basename(const char *path); +#endif + +#ifndef HAVE_CLOSEFROM +void closefrom(int); +#endif + +#if !defined(HAVE_REALPATH) || defined(BROKEN_REALPATH) +char *realpath(const char *path, char *resolved); +#endif + +#if !HAVE_DECL_STRLCPY +size_t strlcpy(char *dst, const char *src, size_t size); +#endif + +#if !HAVE_DECL_STRLCAT +size_t strlcat(char *dst, const char *src, size_t size); +#endif + +#ifndef HAVE_STRMODE +void strmode(int mode, char *p); +#endif + +#ifndef HAVE_DAEMON +int daemon(int nochdir, int noclose); +#endif + +#ifndef HAVE_DIRNAME +char *dirname(const char *path); +#endif + +#ifndef HAVE_FMT_SCALED +#define FMT_SCALED_STRSIZE 7 +int fmt_scaled(long long number, char *result); +#endif + +#ifndef HAVE_SCAN_SCALED +int scan_scaled(char *, long long *); +#endif + +#ifndef HAVE_INET_NTOP +const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); +#endif + +#ifndef HAVE_STRSEP +char *strsep(char **stringp, const char *delim); +#endif + +#ifdef NEED_SETPROCTITLE +void setproctitle(const char *fmt, ...); +void compat_init_setproctitle(int argc, char *argv[]); +#endif + +#if !defined(HAVE_GETOPT) || !defined(HAVE_GETOPT_OPTRESET) +int BSDgetopt(int argc, char * const *argv, const char *opts); +#endif + +/* Home grown routines */ +#include "bsd-misc.h" +/* #include "bsd-setres_id.h" */ +/* #include "bsd-statvfs.h" */ +#include "bsd-waitpid.h" +/* #include "bsd-poll.h" */ + +#ifndef HAVE_GETPEEREID +int getpeereid(int , uid_t *, gid_t *); +#endif + +#if !defined(HAVE_ARC4RANDOM) || defined(LIBRESSL_VERSION_NUMBER) +unsigned int arc4random(void); +#endif + +#if 0 +#if defined(LIBRESSL_VERSION_NUMBER) +# define arc4random_stir() +#elif defined(HAVE_ARC4RANDOM_STIR) +void arc4random_stir(void); +#elif defined(HAVE_ARC4RANDOM) +/* Recent system/libressl implementation; no need for explicit stir */ +# define arc4random_stir() +#else +/* openbsd-compat/arc4random.c provides arc4random_stir() */ +void arc4random_stir(void); +#endif +#endif + +#if !defined(HAVE_ARC4RANDOM_BUF) || defined(LIBRESSL_VERSION_NUMBER) +void arc4random_buf(void *, size_t); +#endif + +#if !defined(HAVE_ARC4RANDOM_UNIFORM) || defined(LIBRESSL_VERSION_NUMBER) +uint32_t arc4random_uniform(uint32_t); +#endif + +#if !defined(SSL_OP_NO_CLIENT_RENEGOTIATION) && !defined(LIBRESSL_VERSION_NUMBER) +#define SSL_OP_NO_CLIENT_RENEGOTIATION 0 +#endif + +#ifndef HAVE_ASPRINTF +int asprintf(char **, const char *, ...); +#endif + +/* #include <sys/types.h> XXX needed? For size_t */ + +#ifndef HAVE_SNPRINTF +int snprintf(char *, size_t, const char *, ...); +#endif + +#ifndef HAVE_STRTOLL +long long strtoll(const char *, char **, int); +#endif + +#ifndef HAVE_STRTOUL +unsigned long strtoul(const char *, char **, int); +#endif + +#ifndef HAVE_STRTOULL +unsigned long long strtoull(const char *, char **, int); +#endif + +#ifndef HAVE_STRTONUM +long long strtonum(const char *nptr, long long minval, long long maxval, const char **errstr); +#endif + +#if !defined(HAVE_VASPRINTF) || !defined(HAVE_VSNPRINTF) +# include <stdarg.h> +#endif + +#ifndef HAVE_VASPRINTF +int vasprintf(char **, const char *, va_list); +#endif + +#ifndef HAVE_VSNPRINTF +int vsnprintf(char *, size_t, const char *, va_list); +#endif + +#if !defined(HAVE_EXPLICIT_BZERO) || defined(LIBRESSL_VERSION_NUMBER) +void explicit_bzero(void *p, size_t n); +#endif + +/* OpenSMTPD-portable specific entries */ + +#ifndef HAVE_FGETLN +#include <stdio.h> +#include <string.h> +char * fgetln(FILE *stream, size_t *len); +#endif + +#ifndef HAVE_FPARSELN +#include <stdio.h> +#include <string.h> +char * fparseln(FILE *fp, size_t *size, size_t *lineno, const char str[3], int flags); +#endif + +#ifndef HAVE_FREEZERO +void freezero(void *, size_t); +#endif + +#ifndef HAVE_PIDFILE +int pidfile(const char *basename); +#endif + +#ifndef HAVE_PW_DUP +struct passwd *pw_dup(const struct passwd *); +#endif + +#if !defined(HAVE_REALLOCARRAY) || defined(LIBRESSL_VERSION_NUMBER) +void *reallocarray(void *, size_t, size_t); +#endif + +#if !defined(HAVE_RECALLOCARRAY) || defined(LIBRESSL_VERSION_NUMBER) +void *recallocarray(void *, size_t, size_t, size_t); +#endif + +#ifndef HAVE_ERRC +__attribute__ ((noreturn)) +void errc(int, int, const char *, ...); +#endif + +#ifndef HAVE_INET_NET_PTON +int inet_net_pton(int, const char *, void *, size_t); +#endif + +#ifndef HAVE_PLEDGE +#define pledge(promises, paths) 0 +#endif + +#ifndef HAVE_MALLOC_CONCEAL +#define malloc_conceal malloc +#endif + +#ifndef HAVE_CALLOC_CONCEAL +#define calloc_conceal calloc +#endif + +#ifndef HAVE_RES_HNOK +int res_hnok(const char *); +#endif + +#if !HAVE_DECL_AF_LOCAL +#define AF_LOCAL AF_UNIX +#endif + +#if !HAVE_DECL_PF_LOCAL +#define PF_LOCAL PF_UNIX +#endif + +#if !HAVE_DECL_WAIT_MYPGRP +#define WAIT_MYPGRP 0 +#endif + +#if !HAVE_DECL_IPPORT_HILASTAUTO +#define IPPORT_HILASTAUTO 65535 +#endif + +#ifndef HAVE_FLOCK +int flock(int, int); +#endif + +#ifndef HAVE_SETRESGID +int setresgid(uid_t, uid_t, uid_t); +#endif + +#ifndef HAVE_SETRESUID +int setresuid(uid_t, uid_t, uid_t); +#endif + +#ifndef HAVE_GETLINE +ssize_t getline(char **, size_t *, FILE *); +#endif + +#ifndef HAVE_CRYPT_CHECKPASS +int crypt_checkpass(const char *, const char *); +#endif + +#ifndef HAVE_STRNDUP +char * strndup(const char *, size_t); +#endif + +#ifndef HAVE_STRNLEN +char * strnlen(const char *, size_t); +#endif + +#ifndef HAVE_STRUCT_TIMEVAL +struct timeval { + long tv_sec; + long tv_usec; +} +#endif + +#ifdef NEED_NANOSLEEP +#ifndef HAVE_STRUCT_TIMESPEC +struct timespec { + time_t tv_sec; + long tv_nsec; +}; +#endif +int nanosleep(const struct timespec *, struct timespec *); +#endif + +#ifdef NEED_SETEGID +int setegid(uid_t); +#endif + +#ifdef NEED_SETEUID +int seteuid(uid_t); +#endif + +#ifdef NEED_SETSID +#define setsid() setpgrp(0, getpid()) +#endif + +#ifdef NEED_SIGNAL +typedef void (*mysig_t)(int); +mysig_t mysignal(int sig, mysig_t act); +#define signal(a,b) mysignal(a,b) +#endif + +#ifdef NEED_STRERROR +const char *strerror(int); +#endif + +#ifdef NEED_USLEEP +int usleep(unsigned int useconds); +#endif + +int pipe2(int pipefd[2], int flags); + +char *get_progname(char *); + + +#endif /* _OPENBSD_COMPAT_H */ diff --git a/openbsd-compat/paths_h/paths.h b/openbsd-compat/paths_h/paths.h new file mode 100644 index 00000000..6b66a9c1 --- /dev/null +++ b/openbsd-compat/paths_h/paths.h @@ -0,0 +1,8 @@ +#ifndef PATHS_H +#define PATHS_H + +#ifndef _PATH_DEFPATH +#define _PATH_DEFPATH "/bin:/usr/bin" +#endif + +#endif diff --git a/openbsd-compat/pidfile.c b/openbsd-compat/pidfile.c new file mode 100644 index 00000000..d6f83880 --- /dev/null +++ b/openbsd-compat/pidfile.c @@ -0,0 +1,112 @@ +/* $OpenBSD: pidfile.c,v 1.8 2008/06/26 05:42:05 ray Exp $ */ +/* $NetBSD: pidfile.c,v 1.4 2001/02/19 22:43:42 cgd Exp $ */ + +/*- + * Copyright (c) 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Jason R. Thorpe. + * + * 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. + */ + +/* OPENBSD ORIGINAL: lib/libutil/pidfile.c */ + +#include "includes.h" +#ifndef HAVE_PIDFILE + +#include <sys/param.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +static char *pidfile_path; +static pid_t pidfile_pid; + +static void pidfile_cleanup(void); + +extern char *__progname; + +int +pidfile(const char *basename) +{ + int save_errno; + pid_t pid; + FILE *f; + + if (basename == NULL) + basename = __progname; + + if (pidfile_path != NULL) { + free(pidfile_path); + pidfile_path = NULL; + } + + (void) asprintf(&pidfile_path, "%s/%s.pid", SMTPD_PIDDIR, basename); + if (pidfile_path == NULL) + return (-1); + + if ((f = fopen(pidfile_path, "w")) == NULL) { + save_errno = errno; + free(pidfile_path); + pidfile_path = NULL; + errno = save_errno; + return (-1); + } + + pid = getpid(); + if (fprintf(f, "%ld\n", (long)pid) <= 0) { + fclose(f); + save_errno = errno; + (void) unlink(pidfile_path); + free(pidfile_path); + pidfile_path = NULL; + errno = save_errno; + return (-1); + } + + fclose(f); + pidfile_pid = pid; + if (atexit(pidfile_cleanup) < 0) { + save_errno = errno; + (void) unlink(pidfile_path); + free(pidfile_path); + pidfile_path = NULL; + pidfile_pid = 0; + errno = save_errno; + return (-1); + } + + return (0); +} + +static void +pidfile_cleanup(void) +{ + + if (pidfile_path != NULL && pidfile_pid == getpid()) + (void) unlink(pidfile_path); +} + +#endif diff --git a/openbsd-compat/pipe2.c b/openbsd-compat/pipe2.c new file mode 100644 index 00000000..fd5c3f49 --- /dev/null +++ b/openbsd-compat/pipe2.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020 Gilles Chehade <gilles@poolp.org> + * Copyright (C) 2020 Jason A. Donenfeld <Jason@zx2c4.com>. All Rights Reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <fcntl.h> +#include <unistd.h> + +int +pipe2(int pipefd[2], int flags) +{ + if (pipe(pipefd) == -1) + return -1; + + if ((flags & O_NONBLOCK) && + (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) == -1 || + fcntl(pipefd[1], F_SETFL, O_NONBLOCK) == -1)) { + close(pipefd[0]); + close(pipefd[1]); + return -1; + } + + if ((flags & O_CLOEXEC) && + (fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) == -1 || + fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) == -1)) { + close(pipefd[0]); + close(pipefd[1]); + return -1; + } + + return 0; +} diff --git a/openbsd-compat/progname.c b/openbsd-compat/progname.c new file mode 100644 index 00000000..b8b9ae30 --- /dev/null +++ b/openbsd-compat/progname.c @@ -0,0 +1,62 @@ + +/* + * Copyright (c) 1999-2004 Damien Miller <djm@mindrot.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/types.h> +#ifdef HAVE_SYS_SELECT_H +# include <sys/select.h> +#endif +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#endif + +#include <err.h> +#include <string.h> +#include <signal.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +/* + * NB. duplicate __progname in case it is an alias for argv[0] + * Otherwise it may get clobbered by setproctitle() + */ +char *get_progname(char *argv0) +{ + char *retp; +#ifdef HAVE___PROGNAME + extern char *__progname; + + if ((retp = strdup(__progname)) == NULL) + err(1, NULL); +#else + char *p; + + if (argv0 == NULL) + return ("unknown"); /* XXX */ + p = strrchr(argv0, '/'); + if (p == NULL) + p = argv0; + else + p++; + + if ((retp = strdup(p)) == NULL) + err(1, NULL); +#endif + return retp; +} diff --git a/openbsd-compat/reallocarray.c b/openbsd-compat/reallocarray.c new file mode 100644 index 00000000..9beec719 --- /dev/null +++ b/openbsd-compat/reallocarray.c @@ -0,0 +1,42 @@ +/* $OpenBSD: reallocarray.c,v 1.1 2014/05/08 21:43:49 deraadt Exp $ */ +/* + * Copyright (c) 2008 Otto Moerbeek <otto@drijf.net> + * + * 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. + */ + +/* OPENBSD ORIGINAL: lib/libc/stdlib/reallocarray.c */ + +#include "includes.h" + +#include <sys/types.h> +#include <errno.h> +#include <stdint.h> +#include <stdlib.h> + +/* + * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX + * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW + */ +#define MUL_NO_OVERFLOW (1UL << (sizeof(size_t) * 4)) + +void * +reallocarray(void *optr, size_t nmemb, size_t size) +{ + if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + nmemb > 0 && SIZE_MAX / nmemb < size) { + errno = ENOMEM; + return NULL; + } + return realloc(optr, size * nmemb); +} diff --git a/openbsd-compat/recallocarray.c b/openbsd-compat/recallocarray.c new file mode 100644 index 00000000..fc0b5a8a --- /dev/null +++ b/openbsd-compat/recallocarray.c @@ -0,0 +1,84 @@ +/* $OpenBSD: recallocarray.c,v 1.1 2017/03/06 18:44:21 otto Exp $ */ +/* + * Copyright (c) 2008, 2017 Otto Moerbeek <otto@drijf.net> + * + * 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. + */ + +/* OPENBSD ORIGINAL: lib/libc/stdlib/recallocarray.c */ + +#include "includes.h" + +#include <errno.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> + +/* + * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX + * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW + */ +#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) + +void * +recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size) +{ + size_t oldsize, newsize; + void *newptr; + + if (ptr == NULL) + return calloc(newnmemb, size); + + if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + newnmemb > 0 && SIZE_MAX / newnmemb < size) { + errno = ENOMEM; + return NULL; + } + newsize = newnmemb * size; + + if ((oldnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + oldnmemb > 0 && SIZE_MAX / oldnmemb < size) { + errno = EINVAL; + return NULL; + } + oldsize = oldnmemb * size; + + /* + * Don't bother too much if we're shrinking just a bit, + * we do not shrink for series of small steps, oh well. + */ + if (newsize <= oldsize) { + size_t d = oldsize - newsize; + + if (d < oldsize / 2 && d < (size_t)getpagesize()) { + memset((char *)ptr + newsize, 0, d); + return ptr; + } + } + + newptr = malloc(newsize); + if (newptr == NULL) + return NULL; + + if (newsize > oldsize) { + memcpy(newptr, ptr, oldsize); + memset((char *)newptr + oldsize, 0, newsize - oldsize); + } else + memcpy(newptr, ptr, newsize); + + explicit_bzero(ptr, oldsize); + free(ptr); + + return newptr; +} diff --git a/openbsd-compat/res_hnok.c b/openbsd-compat/res_hnok.c new file mode 100644 index 00000000..a4b54baf --- /dev/null +++ b/openbsd-compat/res_hnok.c @@ -0,0 +1,169 @@ +/* $OpenBSD: res_comp.c,v 1.14 2008/04/16 22:35:23 deraadt Exp $ */ + +/* + * ++Copyright++ 1985, 1993 + * - + * Copyright (c) 1985, 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. + * - + * Portions Copyright (c) 1993 by Digital Equipment Corporation. + * + * 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, and that + * the name of Digital Equipment Corporation not be used in advertising or + * publicity pertaining to distribution of the document or software without + * specific, written prior permission. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT + * CORPORATION 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. + * - + * --Copyright-- + */ + +/* OPENBSD ORIGINAL: lib/libc/net/res_comp.c */ + +#include "includes.h" + +/* + * Verify that a domain name uses an acceptable character set. + */ + +/* + * Note the conspicuous absence of ctype macros in these definitions. On + * non-ASCII hosts, we can't depend on string literals or ctype macros to + * tell us anything about network-format data. The rest of the BIND system + * is not careful about this, but for some reason, we're doing it right here. + */ +#define PERIOD 0x2e +#define hyphenchar(c) ((c) == 0x2d) +#define bslashchar(c) ((c) == 0x5c) +#define underscorechar(c) ((c) == 0x5f) +#define periodchar(c) ((c) == PERIOD) +#define asterchar(c) ((c) == 0x2a) +#define alphachar(c) (((c) >= 0x41 && (c) <= 0x5a) \ + || ((c) >= 0x61 && (c) <= 0x7a)) +#define digitchar(c) ((c) >= 0x30 && (c) <= 0x39) + +#define borderchar(c) (alphachar(c) || digitchar(c)) +#define middlechar(c) (borderchar(c) || hyphenchar(c) || underscorechar(c)) +#define domainchar(c) ((c) > 0x20 && (c) < 0x7f) + +int +res_hnok(const char *dn) +{ + int pch = PERIOD, ch = *dn++; + + while (ch != '\0') { + int nch = *dn++; + + if (periodchar(ch)) { + ; + } else if (periodchar(pch)) { + if (!borderchar(ch)) + return (0); + } else if (periodchar(nch) || nch == '\0') { + if (!borderchar(ch)) + return (0); + } else { + if (!middlechar(ch)) + return (0); + } + pch = ch, ch = nch; + } + return (1); +} + +#if 0 + +/* + * hostname-like (A, MX, WKS) owners can have "*" as their first label + * but must otherwise be as a host name. + */ +int +res_ownok(const char *dn) +{ + if (asterchar(dn[0])) { + if (periodchar(dn[1])) + return (res_hnok(dn+2)); + if (dn[1] == '\0') + return (1); + } + return (res_hnok(dn)); +} + +/* + * SOA RNAMEs and RP RNAMEs can have any printable character in their first + * label, but the rest of the name has to look like a host name. + */ +int +res_mailok(const char *dn) +{ + int ch, escaped = 0; + + /* "." is a valid missing representation */ + if (*dn == '\0') + return(1); + + /* otherwise <label>.<hostname> */ + while ((ch = *dn++) != '\0') { + if (!domainchar(ch)) + return (0); + if (!escaped && periodchar(ch)) + break; + if (escaped) + escaped = 0; + else if (bslashchar(ch)) + escaped = 1; + } + if (periodchar(ch)) + return (res_hnok(dn)); + return(0); +} + +/* + * This function is quite liberal, since RFC 1034's character sets are only + * recommendations. + */ +int +res_dnok(const char *dn) +{ + int ch; + + while ((ch = *dn++) != '\0') + if (!domainchar(ch)) + return (0); + return (1); +} + +#endif /* if 0 */ diff --git a/openbsd-compat/res_randomid.c b/openbsd-compat/res_randomid.c new file mode 100644 index 00000000..c848c41d --- /dev/null +++ b/openbsd-compat/res_randomid.c @@ -0,0 +1,13 @@ +#include "includes.h" + +#include <time.h> + +unsigned int +res_randomid(void) +{ + struct timespec ts; + + /* This is from musl C library */ + clock_gettime(CLOCK_MONOTONIC, &ts); + return ts.tv_nsec + ts.tv_nsec / 65536UL & 0xffff; +} diff --git a/openbsd-compat/setegid.c b/openbsd-compat/setegid.c new file mode 100644 index 00000000..9e056351 --- /dev/null +++ b/openbsd-compat/setegid.c @@ -0,0 +1,39 @@ + +/* + * Copyright (c) 1999-2004 Damien Miller <djm@mindrot.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/types.h> +#ifdef HAVE_SYS_SELECT_H +# include <sys/select.h> +#endif +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#endif + +#include <err.h> +#include <string.h> +#include <signal.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +int +setegid(uid_t egid) +{ + return(setresgid(-1, egid, -1)); +} diff --git a/openbsd-compat/seteuid.c b/openbsd-compat/seteuid.c new file mode 100644 index 00000000..6f581125 --- /dev/null +++ b/openbsd-compat/seteuid.c @@ -0,0 +1,39 @@ + +/* + * Copyright (c) 1999-2004 Damien Miller <djm@mindrot.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/types.h> +#ifdef HAVE_SYS_SELECT_H +# include <sys/select.h> +#endif +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#endif + +#include <err.h> +#include <string.h> +#include <signal.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +int +seteuid(uid_t euid) +{ + return (setreuid(-1, euid)); +} diff --git a/openbsd-compat/setproctitle.c b/openbsd-compat/setproctitle.c new file mode 100644 index 00000000..71e1595e --- /dev/null +++ b/openbsd-compat/setproctitle.c @@ -0,0 +1,164 @@ +/* Based on conf.c from UCB sendmail 8.8.8 */ + +/* + * Copyright 2003 Damien Miller + * Copyright (c) 1983, 1995-1997 Eric P. Allman + * Copyright (c) 1988, 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. + */ + +#include "includes.h" + +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#ifdef HAVE_SYS_PSTAT_H +#include <sys/pstat.h> +#endif +#include <string.h> + +#if defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) +#include <vis.h> +#else +#include "bsd-vis.h" +#endif + +#define SPT_NONE 0 /* don't use it at all */ +#define SPT_PSTAT 1 /* use pstat(PSTAT_SETCMD, ...) */ +#define SPT_REUSEARGV 2 /* cover argv with title information */ + +#ifndef SPT_TYPE +# define SPT_TYPE SPT_NONE +#endif + +#ifndef SPT_PADCHAR +# define SPT_PADCHAR '\0' +#endif + +#if SPT_TYPE == SPT_REUSEARGV +static char *argv_start = NULL; +static size_t argv_env_len = 0; +#endif + +void +compat_init_setproctitle(int argc, char *argv[]) +{ +#if defined(SPT_TYPE) && SPT_TYPE == SPT_REUSEARGV + extern char **environ; + char *lastargv = NULL; + char **envp = environ; + int i; + + /* + * NB: This assumes that argv has already been copied out of the + * way. This is true for sshd, but may not be true for other + * programs. Beware. + */ + + if (argc == 0 || argv[0] == NULL) + return; + + /* Fail if we can't allocate room for the new environment */ + for (i = 0; envp[i] != NULL; i++) + ; + if ((environ = calloc(i + 1, sizeof(*environ))) == NULL) { + environ = envp; /* put it back */ + return; + } + + /* + * Find the last argv string or environment variable within + * our process memory area. + */ + for (i = 0; i < argc; i++) { + if (lastargv == NULL || lastargv + 1 == argv[i]) + lastargv = argv[i] + strlen(argv[i]); + } + for (i = 0; envp[i] != NULL; i++) { + if (lastargv + 1 == envp[i]) + lastargv = envp[i] + strlen(envp[i]); + } + + argv[1] = NULL; + argv_start = argv[0]; + argv_env_len = lastargv - argv[0] - 1; + + /* + * Copy environment + * XXX - will truncate env on strdup fail + */ + for (i = 0; envp[i] != NULL; i++) + environ[i] = strdup(envp[i]); + environ[i] = NULL; +#endif /* SPT_REUSEARGV */ +} + +void +setproctitle(const char *fmt, ...) +{ +#if SPT_TYPE != SPT_NONE + va_list ap; + char buf[1024], ptitle[1024]; + size_t len; + int r; + extern char *__progname; +#if SPT_TYPE == SPT_PSTAT + union pstun pst; +#endif + +#if SPT_TYPE == SPT_REUSEARGV + if (argv_env_len <= 0) + return; +#endif + + strlcpy(buf, __progname, sizeof(buf)); + + r = -1; + va_start(ap, fmt); + if (fmt != NULL) { + len = strlcat(buf, ": ", sizeof(buf)); + if (len < sizeof(buf)) + r = vsnprintf(buf + len, sizeof(buf) - len , fmt, ap); + } + va_end(ap); + if (r == -1 || (size_t)r >= sizeof(buf) - len) + return; + strnvis(ptitle, buf, sizeof(ptitle), + VIS_CSTYLE|VIS_NL|VIS_TAB|VIS_OCTAL); + +#if SPT_TYPE == SPT_PSTAT + pst.pst_command = ptitle; + pstat(PSTAT_SETCMD, pst, strlen(ptitle), 0, 0); +#elif SPT_TYPE == SPT_REUSEARGV + len = strlcpy(argv_start, ptitle, argv_env_len); + for(; len < argv_env_len; len++) + argv_start[len] = SPT_PADCHAR; +#endif + +#endif /* SPT_NONE */ +} diff --git a/openbsd-compat/setresgid.c b/openbsd-compat/setresgid.c new file mode 100644 index 00000000..e798cdad --- /dev/null +++ b/openbsd-compat/setresgid.c @@ -0,0 +1,41 @@ +/* Subset of uidswap.c from portable OpenSSH */ + +/* $OpenBSD: uidswap.c,v 1.35 2006/08/03 03:34:42 deraadt Exp $ */ +/* + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * Code for uid-swapping. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#include "includes.h" + +#include <stdarg.h> +#include <string.h> +#include <unistd.h> + +#include "log.h" + +int setresgid(uid_t rgid, uid_t egid, uid_t sgid) +{ + +#if defined(HAVE_SETRESGID) && !defined(BROKEN_SETRESGID) + if (setresgid(rgid, egid, sgid) < 0) + fatal("setresgid %u: %.100s", (u_int)rgid, strerror(errno)); +#elif defined(HAVE_SETREGID) && !defined(BROKEN_SETREGID) + if (setregid(rgid, egid) < 0) + fatal("setregid %u: %.100s", (u_int)rgid, strerror(errno)); +#else + if (setegid(egid) < 0) + fatal("setegid %u: %.100s", (u_int)egid, strerror(errno)); + if (setgid(rgid) < 0) + fatal("setgid %u: %.100s", (u_int)rgid, strerror(errno)); +#endif + return (0); +} diff --git a/openbsd-compat/setresuid.c b/openbsd-compat/setresuid.c new file mode 100644 index 00000000..36df39e9 --- /dev/null +++ b/openbsd-compat/setresuid.c @@ -0,0 +1,43 @@ +/* Subset of uidswap.c from portable OpenSSH */ + +/* $OpenBSD: uidswap.c,v 1.35 2006/08/03 03:34:42 deraadt Exp $ */ +/* + * Author: Tatu Ylonen <ylo@cs.hut.fi> + * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland + * All rights reserved + * Code for uid-swapping. + * + * As far as I am concerned, the code I have written for this software + * can be used freely for any purpose. Any derived versions of this + * software must be clearly marked as such, and if the derived work is + * incompatible with the protocol description in the RFC file, it must be + * called by a name other than "ssh" or "Secure Shell". + */ + +#include "includes.h" + +#include <stdarg.h> +#include <string.h> +#include <unistd.h> + +#include "log.h" + +int setresuid(uid_t ruid, uid_t euid, uid_t suid) +{ + +#if defined(HAVE_SETRESUID) && !defined(BROKEN_SETRESUID) + if (setresuid(ruid, euid, suid) < 0) + fatal("setresuid %u: %.100s", (u_int)ruid, strerror(errno)); +#elif defined(HAVE_SETREUID) && !defined(BROKEN_SETREUID) + if (setreuid(ruid, euid) < 0) + fatal("setreuid %u: %.100s", (u_int)ruid, strerror(errno)); +#else +# ifndef SETEUID_BREAKS_SETUID + if (seteuid(euid) < 0) + fatal("seteuid %u: %.100s", (u_int)euid, strerror(errno)); +# endif + if (setuid(ruid) < 0) + fatal("setuid %u: %.100s", (u_int)ruid, strerror(errno)); +#endif + return (0); +} diff --git a/openbsd-compat/signal.c b/openbsd-compat/signal.c new file mode 100644 index 00000000..cdae0cd2 --- /dev/null +++ b/openbsd-compat/signal.c @@ -0,0 +1,60 @@ + +/* + * Copyright (c) 1999-2004 Damien Miller <djm@mindrot.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/types.h> +#ifdef HAVE_SYS_SELECT_H +# include <sys/select.h> +#endif +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#endif + +#include <err.h> +#include <string.h> +#include <signal.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +mysig_t +mysignal(int sig, mysig_t act) +{ +#ifdef HAVE_SIGACTION + struct sigaction sa, osa; + + if (sigaction(sig, NULL, &osa) == -1) + return (mysig_t) -1; + if (osa.sa_handler != act) { + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; +#ifdef SA_INTERRUPT + if (sig == SIGALRM) + sa.sa_flags |= SA_INTERRUPT; +#endif + sa.sa_handler = act; + if (sigaction(sig, &sa, NULL) == -1) + return (mysig_t) -1; + } + return (osa.sa_handler); +#else + #undef signal + return (signal(sig, act)); +#endif +} diff --git a/openbsd-compat/strerror.c b/openbsd-compat/strerror.c new file mode 100644 index 00000000..0ddad2d4 --- /dev/null +++ b/openbsd-compat/strerror.c @@ -0,0 +1,44 @@ + +/* + * Copyright (c) 1999-2004 Damien Miller <djm@mindrot.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/types.h> +#ifdef HAVE_SYS_SELECT_H +# include <sys/select.h> +#endif +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#endif + +#include <err.h> +#include <string.h> +#include <signal.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +const char *strerror(int e) +{ + extern int sys_nerr; + extern char *sys_errlist[]; + + if ((e >= 0) && (e < sys_nerr)) + return (sys_errlist[e]); + + return ("unlisted error"); +} diff --git a/openbsd-compat/strlcat.c b/openbsd-compat/strlcat.c new file mode 100644 index 00000000..979eb876 --- /dev/null +++ b/openbsd-compat/strlcat.c @@ -0,0 +1,59 @@ +/* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/string/strlcat.c */ + +#include "includes.h" + +#include <sys/types.h> +#include <string.h> + +/* + * Appends src to string dst of size siz (unlike strncat, siz is the + * full size of dst, not space left). At most siz-1 characters + * will be copied. Always NUL terminates (unless siz <= strlen(dst)). + * Returns strlen(src) + MIN(siz, strlen(initial dst)). + * If retval >= siz, truncation occurred. + */ +size_t +strlcat(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end */ + while (n-- != 0 && *d != '\0') + d++; + dlen = d - dst; + n = siz - dlen; + + if (n == 0) + return(dlen + strlen(s)); + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return(dlen + (s - src)); /* count does not include NUL */ +} diff --git a/openbsd-compat/strlcpy.c b/openbsd-compat/strlcpy.c new file mode 100644 index 00000000..2562ebbf --- /dev/null +++ b/openbsd-compat/strlcpy.c @@ -0,0 +1,55 @@ +/* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ + +/* + * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* OPENBSD ORIGINAL: lib/libc/string/strlcpy.c */ + +#include "includes.h" + +#include <sys/types.h> +#include <string.h> + +/* + * Copy src to string dst of size siz. At most siz-1 characters + * will be copied. Always NUL terminates (unless siz == 0). + * Returns strlen(src); if retval >= siz, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t siz) +{ + char *d = dst; + const char *s = src; + size_t n = siz; + + /* Copy as many bytes as will fit */ + if (n != 0) { + while (--n != 0) { + if ((*d++ = *s++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src */ + if (n == 0) { + if (siz != 0) + *d = '\0'; /* NUL-terminate dst */ + while (*s++) + ; + } + + return(s - src - 1); /* count does not include NUL */ +} diff --git a/openbsd-compat/strmode.c b/openbsd-compat/strmode.c new file mode 100644 index 00000000..2fc2bb0e --- /dev/null +++ b/openbsd-compat/strmode.c @@ -0,0 +1,146 @@ +/* $OpenBSD: strmode.c,v 1.7 2005/08/08 08:05:37 espie Exp $ */ +/*- + * Copyright (c) 1990 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. + */ + +/* OPENBSD ORIGINAL: lib/libc/string/strmode.c */ + +#include "includes.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> + +/* XXX mode should be mode_t */ + +void +strmode(int mode, char *p) +{ + /* print type */ + switch (mode & S_IFMT) { + case S_IFDIR: /* directory */ + *p++ = 'd'; + break; + case S_IFCHR: /* character special */ + *p++ = 'c'; + break; + case S_IFBLK: /* block special */ + *p++ = 'b'; + break; + case S_IFREG: /* regular */ + *p++ = '-'; + break; + case S_IFLNK: /* symbolic link */ + *p++ = 'l'; + break; +#ifdef S_IFSOCK + case S_IFSOCK: /* socket */ + *p++ = 's'; + break; +#endif +#ifdef S_IFIFO + case S_IFIFO: /* fifo */ + *p++ = 'p'; + break; +#endif + default: /* unknown */ + *p++ = '?'; + break; + } + /* usr */ + if (mode & S_IRUSR) + *p++ = 'r'; + else + *p++ = '-'; + if (mode & S_IWUSR) + *p++ = 'w'; + else + *p++ = '-'; + switch (mode & (S_IXUSR | S_ISUID)) { + case 0: + *p++ = '-'; + break; + case S_IXUSR: + *p++ = 'x'; + break; + case S_ISUID: + *p++ = 'S'; + break; + case S_IXUSR | S_ISUID: + *p++ = 's'; + break; + } + /* group */ + if (mode & S_IRGRP) + *p++ = 'r'; + else + *p++ = '-'; + if (mode & S_IWGRP) + *p++ = 'w'; + else + *p++ = '-'; + switch (mode & (S_IXGRP | S_ISGID)) { + case 0: + *p++ = '-'; + break; + case S_IXGRP: + *p++ = 'x'; + break; + case S_ISGID: + *p++ = 'S'; + break; + case S_IXGRP | S_ISGID: + *p++ = 's'; + break; + } + /* other */ + if (mode & S_IROTH) + *p++ = 'r'; + else + *p++ = '-'; + if (mode & S_IWOTH) + *p++ = 'w'; + else + *p++ = '-'; + switch (mode & (S_IXOTH | S_ISVTX)) { + case 0: + *p++ = '-'; + break; + case S_IXOTH: + *p++ = 'x'; + break; + case S_ISVTX: + *p++ = 'T'; + break; + case S_IXOTH | S_ISVTX: + *p++ = 't'; + break; + } + *p++ = ' '; /* will be a '+' if ACL's implemented */ + *p = '\0'; +} diff --git a/openbsd-compat/strndup.c b/openbsd-compat/strndup.c new file mode 100644 index 00000000..f43ba659 --- /dev/null +++ b/openbsd-compat/strndup.c @@ -0,0 +1,39 @@ +/* $OpenBSD: strndup.c,v 1.2 2015/08/31 02:53:57 guenther Exp $ */ + +/* + * Copyright (c) 2010 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> + +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +char * +strndup(const char *str, size_t maxlen) +{ + char *copy; + size_t len; + + len = strnlen(str, maxlen); + copy = malloc(len + 1); + if (copy != NULL) { + (void)memcpy(copy, str, len); + copy[len] = '\0'; + } + + return copy; +} diff --git a/openbsd-compat/strnlen.c b/openbsd-compat/strnlen.c new file mode 100644 index 00000000..a2017e19 --- /dev/null +++ b/openbsd-compat/strnlen.c @@ -0,0 +1,32 @@ +/* $OpenBSD: strnlen.c,v 1.8 2016/10/16 17:37:39 dtucker Exp $ */ + +/* + * Copyright (c) 2010 Todd C. Miller <Todd.Miller@courtesan.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/types.h> + +#include <string.h> + +size_t +strnlen(const char *str, size_t maxlen) +{ + const char *cp; + + for (cp = str; maxlen != 0 && *cp != '\0'; cp++, maxlen--) + ; + + return (size_t)(cp - str); +} diff --git a/openbsd-compat/strsep.c b/openbsd-compat/strsep.c new file mode 100644 index 00000000..b36eb8fd --- /dev/null +++ b/openbsd-compat/strsep.c @@ -0,0 +1,79 @@ +/* $OpenBSD: strsep.c,v 1.6 2005/08/08 08:05:37 espie Exp $ */ + +/*- + * Copyright (c) 1990, 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. + */ + +/* OPENBSD ORIGINAL: lib/libc/string/strsep.c */ + +#include "includes.h" + +#if !defined(HAVE_STRSEP) + +#include <string.h> +#include <stdio.h> + +/* + * Get next token from string *stringp, where tokens are possibly-empty + * strings separated by characters from delim. + * + * Writes NULs into the string at *stringp to end tokens. + * delim need not remain constant from call to call. + * On return, *stringp points past the last NUL written (if there might + * be further tokens), or is NULL (if there are definitely no more tokens). + * + * If *stringp is NULL, strsep returns NULL. + */ +char * +strsep(char **stringp, const char *delim) +{ + char *s; + const char *spanp; + int c, sc; + char *tok; + + if ((s = *stringp) == NULL) + return (NULL); + for (tok = s;;) { + c = *s++; + spanp = delim; + do { + if ((sc = *spanp++) == c) { + if (c == 0) + s = NULL; + else + s[-1] = 0; + *stringp = s; + return (tok); + } + } while (sc != 0); + } + /* NOTREACHED */ +} + +#endif /* !defined(HAVE_STRSEP) */ diff --git a/openbsd-compat/strtonum.c b/openbsd-compat/strtonum.c new file mode 100644 index 00000000..87f2f24b --- /dev/null +++ b/openbsd-compat/strtonum.c @@ -0,0 +1,72 @@ +/* $OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert Exp $ */ + +/* + * Copyright (c) 2004 Ted Unangst and Todd Miller + * All rights reserved. + * + * 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. + */ + +/* OPENBSD ORIGINAL: lib/libc/stdlib/strtonum.c */ + +#include "includes.h" + +#ifndef HAVE_STRTONUM +#include <stdlib.h> +#include <limits.h> +#include <errno.h> + +#define INVALID 1 +#define TOOSMALL 2 +#define TOOLARGE 3 + +long long +strtonum(const char *numstr, long long minval, long long maxval, + const char **errstrp) +{ + long long ll = 0; + char *ep; + int error = 0; + struct errval { + const char *errstr; + int err; + } ev[4] = { + { NULL, 0 }, + { "invalid", EINVAL }, + { "too small", ERANGE }, + { "too large", ERANGE }, + }; + + ev[0].err = errno; + errno = 0; + if (minval > maxval) + error = INVALID; + else { + ll = strtoll(numstr, &ep, 10); + if (numstr == ep || *ep != '\0') + error = INVALID; + else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) + error = TOOSMALL; + else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) + error = TOOLARGE; + } + if (errstrp != NULL) + *errstrp = ev[error].errstr; + errno = ev[error].err; + if (error) + ll = 0; + + return (ll); +} + +#endif /* HAVE_STRTONUM */ diff --git a/openbsd-compat/sys/queue.h b/openbsd-compat/sys/queue.h new file mode 100644 index 00000000..28aaaa37 --- /dev/null +++ b/openbsd-compat/sys/queue.h @@ -0,0 +1,653 @@ +/* $OpenBSD: queue.h,v 1.36 2012/04/11 13:29:14 naddy Exp $ */ +/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ + +/* + * 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 + */ + +/* OPENBSD ORIGINAL: sys/sys/queue.h */ + +#ifndef _FAKE_QUEUE_H_ +#define _FAKE_QUEUE_H_ + +/* + * Require for OS/X and other platforms that have old/broken/incomplete + * <sys/queue.h>. + */ +#undef SLIST_HEAD +#undef SLIST_HEAD_INITIALIZER +#undef SLIST_ENTRY +#undef SLIST_FOREACH_PREVPTR +#undef SLIST_FIRST +#undef SLIST_END +#undef SLIST_EMPTY +#undef SLIST_NEXT +#undef SLIST_FOREACH +#undef SLIST_INIT +#undef SLIST_INSERT_AFTER +#undef SLIST_INSERT_HEAD +#undef SLIST_REMOVE_HEAD +#undef SLIST_REMOVE +#undef SLIST_REMOVE_NEXT +#undef LIST_HEAD +#undef LIST_HEAD_INITIALIZER +#undef LIST_ENTRY +#undef LIST_FIRST +#undef LIST_END +#undef LIST_EMPTY +#undef LIST_NEXT +#undef LIST_FOREACH +#undef LIST_INIT +#undef LIST_INSERT_AFTER +#undef LIST_INSERT_BEFORE +#undef LIST_INSERT_HEAD +#undef LIST_REMOVE +#undef LIST_REPLACE +#undef SIMPLEQ_HEAD +#undef SIMPLEQ_HEAD_INITIALIZER +#undef SIMPLEQ_ENTRY +#undef SIMPLEQ_FIRST +#undef SIMPLEQ_END +#undef SIMPLEQ_EMPTY +#undef SIMPLEQ_NEXT +#undef SIMPLEQ_FOREACH +#undef SIMPLEQ_INIT +#undef SIMPLEQ_INSERT_HEAD +#undef SIMPLEQ_INSERT_TAIL +#undef SIMPLEQ_INSERT_AFTER +#undef SIMPLEQ_REMOVE_HEAD +#undef TAILQ_HEAD +#undef TAILQ_HEAD_INITIALIZER +#undef TAILQ_ENTRY +#undef TAILQ_FIRST +#undef TAILQ_END +#undef TAILQ_NEXT +#undef TAILQ_LAST +#undef TAILQ_PREV +#undef TAILQ_EMPTY +#undef TAILQ_FOREACH +#undef TAILQ_FOREACH_REVERSE +#undef TAILQ_INIT +#undef TAILQ_INSERT_HEAD +#undef TAILQ_INSERT_TAIL +#undef TAILQ_INSERT_AFTER +#undef TAILQ_INSERT_BEFORE +#undef TAILQ_REMOVE +#undef TAILQ_REPLACE +#undef CIRCLEQ_HEAD +#undef CIRCLEQ_HEAD_INITIALIZER +#undef CIRCLEQ_ENTRY +#undef CIRCLEQ_FIRST +#undef CIRCLEQ_LAST +#undef CIRCLEQ_END +#undef CIRCLEQ_NEXT +#undef CIRCLEQ_PREV +#undef CIRCLEQ_EMPTY +#undef CIRCLEQ_FOREACH +#undef CIRCLEQ_FOREACH_REVERSE +#undef CIRCLEQ_INIT +#undef CIRCLEQ_INSERT_AFTER +#undef CIRCLEQ_INSERT_BEFORE +#undef CIRCLEQ_INSERT_HEAD +#undef CIRCLEQ_INSERT_TAIL +#undef CIRCLEQ_REMOVE +#undef CIRCLEQ_REPLACE + +/* + * This file defines five types of data structures: singly-linked lists, + * lists, simple queues, tail queues, and circular 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 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 only be traversed in the forward direction. + * + * A simple queue is headed by a pair of pointers, one the head of the + * list and the other to the tail of the list. The elements are singly + * linked to save space, so elements can only be removed from the + * head of 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 simple queue may only be traversed in the forward 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 circle 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 circle queue may be traversed in either direction, but has a more + * complex end of list detection. + * + * For details on the use of these macros, see the queue(3) manual page. + */ + +#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC)) +#define _Q_INVALIDATE(a) (a) = ((void *)-1) +#else +#define _Q_INVALIDATE(a) +#endif + +/* + * Singly-linked List definitions. + */ +#define SLIST_HEAD(name, type) \ +struct name { \ + struct type *slh_first; /* first element */ \ +} + +#define SLIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define SLIST_ENTRY(type) \ +struct { \ + struct type *sle_next; /* next element */ \ +} + +/* + * Singly-linked List access methods. + */ +#define SLIST_FIRST(head) ((head)->slh_first) +#define SLIST_END(head) NULL +#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) +#define SLIST_NEXT(elm, field) ((elm)->field.sle_next) + +#define SLIST_FOREACH(var, head, field) \ + for((var) = SLIST_FIRST(head); \ + (var) != SLIST_END(head); \ + (var) = SLIST_NEXT(var, field)) + +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST(head); \ + (var) && ((tvar) = SLIST_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Singly-linked List functions. + */ +#define SLIST_INIT(head) { \ + SLIST_FIRST(head) = SLIST_END(head); \ +} + +#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \ + (elm)->field.sle_next = (slistelm)->field.sle_next; \ + (slistelm)->field.sle_next = (elm); \ +} while (0) + +#define SLIST_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.sle_next = (head)->slh_first; \ + (head)->slh_first = (elm); \ +} while (0) + +#define SLIST_REMOVE_AFTER(elm, field) do { \ + (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE_HEAD(head, field) do { \ + (head)->slh_first = (head)->slh_first->field.sle_next; \ +} while (0) + +#define SLIST_REMOVE(head, elm, type, field) do { \ + if ((head)->slh_first == (elm)) { \ + SLIST_REMOVE_HEAD((head), field); \ + } else { \ + struct type *curelm = (head)->slh_first; \ + \ + while (curelm->field.sle_next != (elm)) \ + curelm = curelm->field.sle_next; \ + curelm->field.sle_next = \ + curelm->field.sle_next->field.sle_next; \ + _Q_INVALIDATE((elm)->field.sle_next); \ + } \ +} while (0) + +/* + * List definitions. + */ +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List access methods + */ +#define LIST_FIRST(head) ((head)->lh_first) +#define LIST_END(head) NULL +#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head)) +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#define LIST_FOREACH(var, head, field) \ + for((var) = LIST_FIRST(head); \ + (var)!= LIST_END(head); \ + (var) = LIST_NEXT(var, field)) + +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST(head); \ + (var) && ((tvar) = LIST_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * List functions. + */ +#define LIST_INIT(head) do { \ + LIST_FIRST(head) = LIST_END(head); \ +} while (0) + +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ + (listelm)->field.le_next->field.le_prev = \ + &(elm)->field.le_next; \ + (listelm)->field.le_next = (elm); \ + (elm)->field.le_prev = &(listelm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + (elm)->field.le_next = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &(elm)->field.le_next; \ +} while (0) + +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.le_next = (head)->lh_first) != NULL) \ + (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ + (head)->lh_first = (elm); \ + (elm)->field.le_prev = &(head)->lh_first; \ +} while (0) + +#define LIST_REMOVE(elm, field) do { \ + if ((elm)->field.le_next != NULL) \ + (elm)->field.le_next->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = (elm)->field.le_next; \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +#define LIST_REPLACE(elm, elm2, field) do { \ + if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ + (elm2)->field.le_next->field.le_prev = \ + &(elm2)->field.le_next; \ + (elm2)->field.le_prev = (elm)->field.le_prev; \ + *(elm2)->field.le_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.le_prev); \ + _Q_INVALIDATE((elm)->field.le_next); \ +} while (0) + +/* + * Simple queue definitions. + */ +#define SIMPLEQ_HEAD(name, type) \ +struct name { \ + struct type *sqh_first; /* first element */ \ + struct type **sqh_last; /* addr of last next element */ \ +} + +#define SIMPLEQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).sqh_first } + +#define SIMPLEQ_ENTRY(type) \ +struct { \ + struct type *sqe_next; /* next element */ \ +} + +/* + * Simple queue access methods. + */ +#define SIMPLEQ_FIRST(head) ((head)->sqh_first) +#define SIMPLEQ_END(head) NULL +#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) +#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) + +#define SIMPLEQ_FOREACH(var, head, field) \ + for((var) = SIMPLEQ_FIRST(head); \ + (var) != SIMPLEQ_END(head); \ + (var) = SIMPLEQ_NEXT(var, field)) + +#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SIMPLEQ_FIRST(head); \ + (var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \ + (var) = (tvar)) + +/* + * Simple queue functions. + */ +#define SIMPLEQ_INIT(head) do { \ + (head)->sqh_first = NULL; \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (head)->sqh_first = (elm); \ +} while (0) + +#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + *(head)->sqh_last = (elm); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ + (head)->sqh_last = &(elm)->field.sqe_next; \ + (listelm)->field.sqe_next = (elm); \ +} while (0) + +#define SIMPLEQ_REMOVE_HEAD(head, field) do { \ + if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ + (head)->sqh_last = &(head)->sqh_first; \ +} while (0) + +#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \ + if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \ + == NULL) \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (0) + +/* + * Tail queue definitions. + */ +#define TAILQ_HEAD(name, type) \ +struct name { \ + struct type *tqh_first; /* first element */ \ + struct type **tqh_last; /* addr of last next element */ \ +} + +#define TAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).tqh_first } + +#define TAILQ_ENTRY(type) \ +struct { \ + struct type *tqe_next; /* next element */ \ + struct type **tqe_prev; /* address of previous next element */ \ +} + +/* + * tail queue access methods + */ +#define TAILQ_FIRST(head) ((head)->tqh_first) +#define TAILQ_END(head) NULL +#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#define TAILQ_LAST(head, headname) \ + (*(((struct headname *)((head)->tqh_last))->tqh_last)) +/* XXX */ +#define TAILQ_PREV(elm, headname, field) \ + (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#define TAILQ_EMPTY(head) \ + (TAILQ_FIRST(head) == TAILQ_END(head)) + +#define TAILQ_FOREACH(var, head, field) \ + for((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_NEXT(var, field)) + +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head) && \ + ((tvar) = TAILQ_NEXT(var, field), 1); \ + (var) = (tvar)) + + +#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \ + for((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head); \ + (var) = TAILQ_PREV(var, headname, field)) + +#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = TAILQ_LAST(head, headname); \ + (var) != TAILQ_END(head) && \ + ((tvar) = TAILQ_PREV(var, headname, field), 1); \ + (var) = (tvar)) + +/* + * Tail queue functions. + */ +#define TAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_prev = &(head)->tqh_first; \ +} while (0) + +#define TAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_prev = (head)->tqh_last; \ + *(head)->tqh_last = (elm); \ + (head)->tqh_last = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_prev = \ + &(elm)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm)->field.tqe_next; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ +} while (0) + +#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ + (elm)->field.tqe_next = (listelm); \ + *(listelm)->field.tqe_prev = (elm); \ + (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +} while (0) + +#define TAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_prev = \ + (elm)->field.tqe_prev; \ + else \ + (head)->tqh_last = (elm)->field.tqe_prev; \ + *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +#define TAILQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ + (elm2)->field.tqe_next->field.tqe_prev = \ + &(elm2)->field.tqe_next; \ + else \ + (head)->tqh_last = &(elm2)->field.tqe_next; \ + (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ + *(elm2)->field.tqe_prev = (elm2); \ + _Q_INVALIDATE((elm)->field.tqe_prev); \ + _Q_INVALIDATE((elm)->field.tqe_next); \ +} while (0) + +/* + * Circular queue definitions. + */ +#define CIRCLEQ_HEAD(name, type) \ +struct name { \ + struct type *cqh_first; /* first element */ \ + struct type *cqh_last; /* last element */ \ +} + +#define CIRCLEQ_HEAD_INITIALIZER(head) \ + { CIRCLEQ_END(&head), CIRCLEQ_END(&head) } + +#define CIRCLEQ_ENTRY(type) \ +struct { \ + struct type *cqe_next; /* next element */ \ + struct type *cqe_prev; /* previous element */ \ +} + +/* + * Circular queue access methods + */ +#define CIRCLEQ_FIRST(head) ((head)->cqh_first) +#define CIRCLEQ_LAST(head) ((head)->cqh_last) +#define CIRCLEQ_END(head) ((void *)(head)) +#define CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) +#define CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) +#define CIRCLEQ_EMPTY(head) \ + (CIRCLEQ_FIRST(head) == CIRCLEQ_END(head)) + +#define CIRCLEQ_FOREACH(var, head, field) \ + for((var) = CIRCLEQ_FIRST(head); \ + (var) != CIRCLEQ_END(head); \ + (var) = CIRCLEQ_NEXT(var, field)) + +#define CIRCLEQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = CIRCLEQ_FIRST(head); \ + (var) != CIRCLEQ_END(head) && \ + ((tvar) = CIRCLEQ_NEXT(var, field), 1); \ + (var) = (tvar)) + +#define CIRCLEQ_FOREACH_REVERSE(var, head, field) \ + for((var) = CIRCLEQ_LAST(head); \ + (var) != CIRCLEQ_END(head); \ + (var) = CIRCLEQ_PREV(var, field)) + +#define CIRCLEQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \ + for ((var) = CIRCLEQ_LAST(head, headname); \ + (var) != CIRCLEQ_END(head) && \ + ((tvar) = CIRCLEQ_PREV(var, headname, field), 1); \ + (var) = (tvar)) + +/* + * Circular queue functions. + */ +#define CIRCLEQ_INIT(head) do { \ + (head)->cqh_first = CIRCLEQ_END(head); \ + (head)->cqh_last = CIRCLEQ_END(head); \ +} while (0) + +#define CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm)->field.cqe_next; \ + (elm)->field.cqe_prev = (listelm); \ + if ((listelm)->field.cqe_next == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm); \ + else \ + (listelm)->field.cqe_next->field.cqe_prev = (elm); \ + (listelm)->field.cqe_next = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ + (elm)->field.cqe_next = (listelm); \ + (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ + if ((listelm)->field.cqe_prev == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm); \ + else \ + (listelm)->field.cqe_prev->field.cqe_next = (elm); \ + (listelm)->field.cqe_prev = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ + (elm)->field.cqe_next = (head)->cqh_first; \ + (elm)->field.cqe_prev = CIRCLEQ_END(head); \ + if ((head)->cqh_last == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm); \ + else \ + (head)->cqh_first->field.cqe_prev = (elm); \ + (head)->cqh_first = (elm); \ +} while (0) + +#define CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.cqe_next = CIRCLEQ_END(head); \ + (elm)->field.cqe_prev = (head)->cqh_last; \ + if ((head)->cqh_first == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm); \ + else \ + (head)->cqh_last->field.cqe_next = (elm); \ + (head)->cqh_last = (elm); \ +} while (0) + +#define CIRCLEQ_REMOVE(head, elm, field) do { \ + if ((elm)->field.cqe_next == CIRCLEQ_END(head)) \ + (head)->cqh_last = (elm)->field.cqe_prev; \ + else \ + (elm)->field.cqe_next->field.cqe_prev = \ + (elm)->field.cqe_prev; \ + if ((elm)->field.cqe_prev == CIRCLEQ_END(head)) \ + (head)->cqh_first = (elm)->field.cqe_next; \ + else \ + (elm)->field.cqe_prev->field.cqe_next = \ + (elm)->field.cqe_next; \ + _Q_INVALIDATE((elm)->field.cqe_prev); \ + _Q_INVALIDATE((elm)->field.cqe_next); \ +} while (0) + +#define CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ + if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ + CIRCLEQ_END(head)) \ + (head).cqh_last = (elm2); \ + else \ + (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ + if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ + CIRCLEQ_END(head)) \ + (head).cqh_first = (elm2); \ + else \ + (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ + _Q_INVALIDATE((elm)->field.cqe_prev); \ + _Q_INVALIDATE((elm)->field.cqe_next); \ +} while (0) + +#endif /* !_FAKE_QUEUE_H_ */ diff --git a/openbsd-compat/sys/tree.h b/openbsd-compat/sys/tree.h new file mode 100644 index 00000000..7f7546ec --- /dev/null +++ b/openbsd-compat/sys/tree.h @@ -0,0 +1,755 @@ +/* $OpenBSD: tree.h,v 1.13 2011/07/09 00:19:45 pirofti Exp $ */ +/* + * Copyright 2002 Niels Provos <provos@citi.umich.edu> + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* OPENBSD ORIGINAL: sys/sys/tree.h */ + +#include "config.h" +#ifdef NO_ATTRIBUTE_ON_RETURN_TYPE +# define __attribute__(x) +#endif + +#ifndef _SYS_TREE_H_ +#define _SYS_TREE_H_ + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute. It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + * same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + struct type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (0) + +#define SPLAY_ENTRY(type) \ +struct { \ + struct type *spe_left; /* left element */ \ + struct type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(struct name *, struct type *); \ +void name##_SPLAY_MINMAX(struct name *, int); \ +struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ +struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ + \ +/* Finds the node with the same key as elm */ \ +static __inline struct type * \ +name##_SPLAY_FIND(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_NEXT(struct name *head, struct type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_MIN_MAX(struct name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_SPLAY_INSERT(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + int __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if(__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +struct type * \ +name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(struct name *head, struct type *elm) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + int __comp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while ((__comp = (cmp)(elm, (head)->sph_root))) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(struct name *head, int __comp) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-black tree */ +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (0) + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_ENTRY(type) \ +struct { \ + struct type *rbe_left; /* left element */ \ + struct type *rbe_right; /* right element */ \ + struct type *rbe_parent; /* parent element */ \ + int rbe_color; /* node color */ \ +} + +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_COLOR(elm, field) (elm)->field.rbe_color +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(elm, parent, field) do { \ + RB_PARENT(elm, field) = parent; \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ + RB_COLOR(elm, field) = RB_RED; \ +} while (0) + +#define RB_SET_BLACKRED(black, red, field) do { \ + RB_COLOR(black, field) = RB_BLACK; \ + RB_COLOR(red, field) = RB_RED; \ +} while (0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x) do {} while (0) +#endif + +#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ + RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_LEFT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ + RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_RIGHT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) +#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) +#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ +attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ +attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ +attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ +attr struct type *name##_RB_INSERT(struct name *, struct type *); \ +attr struct type *name##_RB_FIND(struct name *, struct type *); \ +attr struct type *name##_RB_NFIND(struct name *, struct type *); \ +attr struct type *name##_RB_NEXT(struct type *); \ +attr struct type *name##_RB_PREV(struct type *); \ +attr struct type *name##_RB_MINMAX(struct name *, int); \ + \ + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp,) +#define RB_GENERATE_STATIC(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) +#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ +attr void \ +name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ +{ \ + struct type *parent, *gparent, *tmp; \ + while ((parent = RB_PARENT(elm, field)) && \ + RB_COLOR(parent, field) == RB_RED) { \ + gparent = RB_PARENT(parent, field); \ + if (parent == RB_LEFT(gparent, field)) { \ + tmp = RB_RIGHT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_RIGHT(parent, field) == elm) { \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_RIGHT(head, gparent, tmp, field); \ + } else { \ + tmp = RB_LEFT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_LEFT(parent, field) == elm) { \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_LEFT(head, gparent, tmp, field); \ + } \ + } \ + RB_COLOR(head->rbh_root, field) = RB_BLACK; \ +} \ + \ +attr void \ +name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ +{ \ + struct type *tmp; \ + while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ + elm != RB_ROOT(head)) { \ + if (RB_LEFT(parent, field) == elm) { \ + tmp = RB_RIGHT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ + struct type *oleft; \ + if ((oleft = RB_LEFT(tmp, field)))\ + RB_COLOR(oleft, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_RIGHT(head, tmp, oleft, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_RIGHT(tmp, field)) \ + RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } else { \ + tmp = RB_LEFT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ + struct type *oright; \ + if ((oright = RB_RIGHT(tmp, field)))\ + RB_COLOR(oright, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_LEFT(head, tmp, oright, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_LEFT(tmp, field)) \ + RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } \ + } \ + if (elm) \ + RB_COLOR(elm, field) = RB_BLACK; \ +} \ + \ +attr struct type * \ +name##_RB_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *child, *parent, *old = elm; \ + int color; \ + if (RB_LEFT(elm, field) == NULL) \ + child = RB_RIGHT(elm, field); \ + else if (RB_RIGHT(elm, field) == NULL) \ + child = RB_LEFT(elm, field); \ + else { \ + struct type *left; \ + elm = RB_RIGHT(elm, field); \ + while ((left = RB_LEFT(elm, field))) \ + elm = left; \ + child = RB_RIGHT(elm, field); \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ + if (RB_PARENT(elm, field) == old) \ + parent = elm; \ + (elm)->field = (old)->field; \ + if (RB_PARENT(old, field)) { \ + if (RB_LEFT(RB_PARENT(old, field), field) == old)\ + RB_LEFT(RB_PARENT(old, field), field) = elm;\ + else \ + RB_RIGHT(RB_PARENT(old, field), field) = elm;\ + RB_AUGMENT(RB_PARENT(old, field)); \ + } else \ + RB_ROOT(head) = elm; \ + RB_PARENT(RB_LEFT(old, field), field) = elm; \ + if (RB_RIGHT(old, field)) \ + RB_PARENT(RB_RIGHT(old, field), field) = elm; \ + if (parent) { \ + left = parent; \ + do { \ + RB_AUGMENT(left); \ + } while ((left = RB_PARENT(left, field))); \ + } \ + goto color; \ + } \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ +color: \ + if (color == RB_BLACK) \ + name##_RB_REMOVE_COLOR(head, parent, child); \ + return (old); \ +} \ + \ +/* Inserts a node into the RB tree */ \ +attr struct type * \ +name##_RB_INSERT(struct name *head, struct type *elm) \ +{ \ + struct type *tmp; \ + struct type *parent = NULL; \ + int comp = 0; \ + tmp = RB_ROOT(head); \ + while (tmp) { \ + parent = tmp; \ + comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + RB_SET(elm, parent, field); \ + if (parent != NULL) { \ + if (comp < 0) \ + RB_LEFT(parent, field) = elm; \ + else \ + RB_RIGHT(parent, field) = elm; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_INSERT_COLOR(head, elm); \ + return (NULL); \ +} \ + \ +/* Finds the node with the same key as elm */ \ +attr struct type * \ +name##_RB_FIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} \ + \ +/* Finds the first node greater than or equal to the search key */ \ +attr struct type * \ +name##_RB_NFIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *res = NULL; \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) { \ + res = tmp; \ + tmp = RB_LEFT(tmp, field); \ + } \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (res); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_NEXT(struct type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_PREV(struct type *elm) \ +{ \ + if (RB_LEFT(elm, field)) { \ + elm = RB_LEFT(elm, field); \ + while (RB_RIGHT(elm, field)) \ + elm = RB_RIGHT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +attr struct type * \ +name##_RB_MINMAX(struct name *head, int val) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_SAFE(x, name, head, y) \ + for ((x) = RB_MIN(name, head); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE(x, name, head) \ + for ((x) = RB_MAX(name, head); \ + (x) != NULL; \ + (x) = name##_RB_PREV(x)) + +#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ + for ((x) = RB_MAX(name, head); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \ + (x) = (y)) + +#endif /* _SYS_TREE_H_ */ diff --git a/openbsd-compat/usleep.c b/openbsd-compat/usleep.c new file mode 100644 index 00000000..946befc9 --- /dev/null +++ b/openbsd-compat/usleep.c @@ -0,0 +1,43 @@ + +/* + * Copyright (c) 1999-2004 Damien Miller <djm@mindrot.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/types.h> +#ifdef HAVE_SYS_SELECT_H +# include <sys/select.h> +#endif +#ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +#endif + +#include <err.h> +#include <string.h> +#include <signal.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +int +usleep(unsigned int useconds) +{ + struct timespec ts; + + ts.tv_sec = useconds / 1000000; + ts.tv_nsec = (useconds % 1000000) * 1000; + return nanosleep(&ts, NULL); +} diff --git a/openbsd-compat/vis.c b/openbsd-compat/vis.c new file mode 100644 index 00000000..a49b4dc3 --- /dev/null +++ b/openbsd-compat/vis.c @@ -0,0 +1,222 @@ +/* $OpenBSD: vis.c,v 1.19 2005/09/01 17:15:49 millert Exp $ */ +/*- + * Copyright (c) 1989, 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. + */ + +/* OPENBSD ORIGINAL: lib/libc/gen/vis.c */ + +#include "includes.h" + +#include <ctype.h> +#include <string.h> + +#include "bsd-vis.h" + +#define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') +#define isvisible(c) \ + (((u_int)(c) <= UCHAR_MAX && isascii((u_char)(c)) && \ + (((c) != '*' && (c) != '?' && (c) != '[' && (c) != '#') || \ + (flag & VIS_GLOB) == 0) && isgraph((u_char)(c))) || \ + ((flag & VIS_SP) == 0 && (c) == ' ') || \ + ((flag & VIS_TAB) == 0 && (c) == '\t') || \ + ((flag & VIS_NL) == 0 && (c) == '\n') || \ + ((flag & VIS_SAFE) && ((c) == '\b' || \ + (c) == '\007' || (c) == '\r' || \ + isgraph((u_char)(c))))) + +/* + * vis - visually encode characters + */ +char * +vis(char *dst, int c, int flag, int nextc) +{ + if (isvisible(c)) { + *dst++ = c; + if (c == '\\' && (flag & VIS_NOSLASH) == 0) + *dst++ = '\\'; + *dst = '\0'; + return (dst); + } + + if (flag & VIS_CSTYLE) { + switch(c) { + case '\n': + *dst++ = '\\'; + *dst++ = 'n'; + goto done; + case '\r': + *dst++ = '\\'; + *dst++ = 'r'; + goto done; + case '\b': + *dst++ = '\\'; + *dst++ = 'b'; + goto done; + case '\a': + *dst++ = '\\'; + *dst++ = 'a'; + goto done; + case '\v': + *dst++ = '\\'; + *dst++ = 'v'; + goto done; + case '\t': + *dst++ = '\\'; + *dst++ = 't'; + goto done; + case '\f': + *dst++ = '\\'; + *dst++ = 'f'; + goto done; + case ' ': + *dst++ = '\\'; + *dst++ = 's'; + goto done; + case '\0': + *dst++ = '\\'; + *dst++ = '0'; + if (isoctal(nextc)) { + *dst++ = '0'; + *dst++ = '0'; + } + goto done; + } + } + if (((c & 0177) == ' ') || (flag & VIS_OCTAL) || + ((flag & VIS_GLOB) && (c == '*' || c == '?' || c == '[' || c == '#'))) { + *dst++ = '\\'; + *dst++ = ((u_char)c >> 6 & 07) + '0'; + *dst++ = ((u_char)c >> 3 & 07) + '0'; + *dst++ = ((u_char)c & 07) + '0'; + goto done; + } + if ((flag & VIS_NOSLASH) == 0) + *dst++ = '\\'; + if (c & 0200) { + c &= 0177; + *dst++ = 'M'; + } + if (iscntrl((u_char)c)) { + *dst++ = '^'; + if (c == 0177) + *dst++ = '?'; + else + *dst++ = c + '@'; + } else { + *dst++ = '-'; + *dst++ = c; + } +done: + *dst = '\0'; + return (dst); +} + +/* + * strvis, strnvis, strvisx - visually encode characters from src into dst + * + * Dst must be 4 times the size of src to account for possible + * expansion. The length of dst, not including the trailing NULL, + * is returned. + * + * Strnvis will write no more than siz-1 bytes (and will NULL terminate). + * The number of bytes needed to fully encode the string is returned. + * + * Strvisx encodes exactly len bytes from src into dst. + * This is useful for encoding a block of data. + */ +int +strvis(char *dst, const char *src, int flag) +{ + char c; + char *start; + + for (start = dst; (c = *src);) + dst = vis(dst, c, flag, *++src); + *dst = '\0'; + return (dst - start); +} + +int +strnvis(char *dst, const char *src, size_t siz, int flag) +{ + char *start, *end; + char tbuf[5]; + int c, i; + + i = 0; + for (start = dst, end = start + siz - 1; (c = *src) && dst < end; ) { + if (isvisible(c)) { + i = 1; + *dst++ = c; + if (c == '\\' && (flag & VIS_NOSLASH) == 0) { + /* need space for the extra '\\' */ + if (dst < end) + *dst++ = '\\'; + else { + dst--; + i = 2; + break; + } + } + src++; + } else { + i = vis(tbuf, c, flag, *++src) - tbuf; + if (dst + i <= end) { + memcpy(dst, tbuf, i); + dst += i; + } else { + src--; + break; + } + } + } + if (siz > 0) + *dst = '\0'; + if (dst + i > end) { + /* adjust return value for truncation */ + while ((c = *src)) + dst += vis(tbuf, c, flag, *++src) - tbuf; + } + return (dst - start); +} + +int +strvisx(char *dst, const char *src, size_t len, int flag) +{ + char c; + char *start; + + for (start = dst; len > 1; len--) { + c = *src; + dst = vis(dst, c, flag, *++src); + } + if (len) + dst = vis(dst, *src, flag, '\0'); + *dst = '\0'; + return (dst - start); +} diff --git a/regress/config/Makefile b/regress/config/Makefile deleted file mode 100644 index ba2dded8..00000000 --- a/regress/config/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -FILES = test0.conf -FILES+= test1.conf -FILES+= test2.conf -FILES+= test3.conf -FILES+= test4.conf -FILES+= test5.conf -FILES+= test6.conf -FILES+= test7.conf -FILES+= test8.conf -FILES+= test9.conf -FILES+= test10.conf -FILES+= test11.conf - -test: -.for FILE in $(FILES) - smtpd -n -f $(FILE) -.endfor diff --git a/regress/config/include.conf b/regress/config/include.conf deleted file mode 100644 index 667bc640..00000000 --- a/regress/config/include.conf +++ /dev/null @@ -1 +0,0 @@ -table sources { 127.0.0.1 } diff --git a/regress/config/test0.conf b/regress/config/test0.conf deleted file mode 100644 index cbd2a2f1..00000000 --- a/regress/config/test0.conf +++ /dev/null @@ -1,3 +0,0 @@ -listen on lo0 - -accept for local deliver to mbox diff --git a/regress/config/test1.conf b/regress/config/test1.conf deleted file mode 100644 index 8983f6c8..00000000 --- a/regress/config/test1.conf +++ /dev/null @@ -1,5 +0,0 @@ -listen on lo0 - -include "include.conf" - -accept from <sources> for local deliver to mbox diff --git a/regress/config/test10.conf b/regress/config/test10.conf deleted file mode 100644 index b40480d6..00000000 --- a/regress/config/test10.conf +++ /dev/null @@ -1,6 +0,0 @@ -listen on lo0 - -accept from any for local deliver to mbox -accept from any for local deliver to maildir -accept from any for local deliver to maildir "~/Maildir" -accept from any for local deliver to mda "/bin/cat" diff --git a/regress/config/test11.conf b/regress/config/test11.conf deleted file mode 100644 index 3b43f72d..00000000 --- a/regress/config/test11.conf +++ /dev/null @@ -1,25 +0,0 @@ -listen on lo0 - -accept for any relay -accept for any relay as example -accept for any relay as "@example.org" -accept for any relay as example@example.org - -accept for any relay backup mx1.example.org -accept for any relay backup mx1.example.org as example -accept for any relay backup mx1.example.org as "@example.org" -accept for any relay backup mx1.example.org as example@example.org - -accept for any relay via mx1.example.org -accept for any relay via smtp://mx1.example.org -accept for any relay via smtp://mx1.example.org:25 -accept for any relay via tls://mx1.example.org:25 -accept for any relay via smtps://mx1.example.org - -accept for any relay via mx1.example.org as example@example.org -accept for any relay via smtp://mx1.example.org as example@example.org -accept for any relay via smtp://mx1.example.org:25 as example@example.org -accept for any relay via tls://mx1.example.org:25 as example@example.org -accept for any relay via smtps://mx1.example.org as example@example.org - - diff --git a/regress/config/test2.conf b/regress/config/test2.conf deleted file mode 100644 index dc67a4e4..00000000 --- a/regress/config/test2.conf +++ /dev/null @@ -1,5 +0,0 @@ -listen on lo0 - -expire 4d - -accept for local deliver to mbox diff --git a/regress/config/test3.conf b/regress/config/test3.conf deleted file mode 100644 index 8ca91bf4..00000000 --- a/regress/config/test3.conf +++ /dev/null @@ -1,5 +0,0 @@ -listen on lo0 - -hostname test.org - -accept for local deliver to mbox diff --git a/regress/config/test4.conf b/regress/config/test4.conf deleted file mode 100644 index 6e5f4b04..00000000 --- a/regress/config/test4.conf +++ /dev/null @@ -1,4 +0,0 @@ -listen on lo0 port 25 -listen on lo0 port smtp - -accept for local deliver to mbox diff --git a/regress/config/test5.conf b/regress/config/test5.conf deleted file mode 100644 index dc1c42ad..00000000 --- a/regress/config/test5.conf +++ /dev/null @@ -1,9 +0,0 @@ -listen on lo0 port 25 -listen on lo0 port smtp - -table aliases_dynamic "/etc/mail/aliases" -table aliases_static { root => test } - -accept for local alias <aliases_dynamic> deliver to mbox -accept for local alias <aliases_static> deliver to mbox -accept for local alias { k => v } deliver to mbox diff --git a/regress/config/test6.conf b/regress/config/test6.conf deleted file mode 100644 index a2a6ded2..00000000 --- a/regress/config/test6.conf +++ /dev/null @@ -1,5 +0,0 @@ -listen on lo0 - -max-message-size 1G - -accept for local deliver to mbox diff --git a/regress/config/test7.conf b/regress/config/test7.conf deleted file mode 100644 index 274273bd..00000000 --- a/regress/config/test7.conf +++ /dev/null @@ -1,12 +0,0 @@ -listen on lo0 - -table sources { 192.168.1.2 } - -accept from any for local deliver to mbox -accept from local for local deliver to mbox -accept from 192.168.1.0/24 for local deliver to mbox -accept from 192.168.1.1 for local deliver to mbox -accept from { 192.168.1.1 } for local deliver to mbox -accept from { 192.168.1.1, 192.168.1.2 } for local deliver to mbox -accept from <sources> for local deliver to mbox - diff --git a/regress/config/test8.conf b/regress/config/test8.conf deleted file mode 100644 index 9cf0c269..00000000 --- a/regress/config/test8.conf +++ /dev/null @@ -1,13 +0,0 @@ -listen on lo0 - -table aliases "/etc/mail/aliases" -table domains { example.org, example.com } - -accept from any for any deliver to mbox -accept from any for local deliver to mbox -accept from any for domain example.org deliver to mbox -accept from any for domain example.org alias <aliases> deliver to mbox -accept from any for domain <domains> deliver to mbox -accept from any for domain <domains> alias <aliases> deliver to mbox -accept from any for domain { example.org, example.com } deliver to mbox -accept from any for domain { example.org, example.com } alias <aliases> deliver to mbox diff --git a/regress/config/test9.conf b/regress/config/test9.conf deleted file mode 100644 index 7bcb1fcb..00000000 --- a/regress/config/test9.conf +++ /dev/null @@ -1,8 +0,0 @@ -listen on lo0 - -table vusers "/etc/mail/aliases" -table domains { example.org, example.com } -accept from any for domain "*" virtual <vusers> deliver to mbox -accept from any for domain example.org virtual <vusers> deliver to mbox -accept from any for domain <domains> virtual <vusers> deliver to mbox -accept from any for domain { example.org, example.com } virtual <vusers> deliver to mbox diff --git a/regress/expand/Makefile b/regress/expand/Makefile deleted file mode 100644 index aa1781c9..00000000 --- a/regress/expand/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -PROG= expand -NOMAN= 1 - -LDFLAGS+= -lutil - -.include <bsd.prog.mk> diff --git a/regress/expand/expand.c b/regress/expand/expand.c deleted file mode 100644 index bfed154b..00000000 --- a/regress/expand/expand.c +++ /dev/null @@ -1,129 +0,0 @@ -/* $OpenBSD: expand.c,v 1.18 2012/10/10 18:02:37 eric Exp $ */ - -/* - * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <err.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <util.h> - -#define MAX_LINE_SIZE 2048 - -static char * -strip(char *s) -{ - size_t l; - - while (*s == ' ' || *s == '\t') - s++; - - for (l = strlen(s); l; l--) { - if (s[l-1] != ' ' && s[l-1] != '\t') - break; - s[l-1] = '\0'; - } - - return (s); -} - -static int -expand_line_split(char **line, char **ret) -{ - static char buffer[MAX_LINE_SIZE]; - int esc, i, dq, sq; - char *s; - - bzero(buffer, sizeof buffer); - esc = dq = sq = i = 0; - for (s = *line; (*s) && (i < (int)sizeof(buffer)); ++s) { - if (esc) { - buffer[i++] = *s; - esc = 0; - continue; - } - if (*s == '\\') { - esc = 1; - continue; - } - if (*s == ',' && !dq && !sq) { - *ret = buffer; - *line = s+1; - return (1); - } - - buffer[i++] = *s; - esc = 0; - - if (*s == '"' && !sq) - dq ^= 1; - if (*s == '\'' && !dq) - sq ^= 1; - } - - if (esc || dq || sq || i == sizeof(buffer)) - return (-1); - - *ret = buffer; - *line = s; - return (i ? 1 : 0); -} - -int -expand_line(const char *s) -{ - char buffer[MAX_LINE_SIZE]; - char *p, *subrcpt; - int ret; - - bzero(buffer, sizeof buffer); - if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer) - return 0; - - p = buffer; - while ((ret = expand_line_split(&p, &subrcpt)) > 0) { - printf(" -> [%s]", subrcpt); - printf(" (%s)\n", strip(subrcpt)); - } - - if (ret >= 0) - return 1; - return 0; -} - -int -main(int argc, char **argv) -{ - FILE *fp; - char *line; - size_t len; - size_t lineno; - - fp = fopen(argv[1], "r"); - if (fp == NULL) - err(1, "fopen"); - - while ((line = fparseln(fp, &len, &lineno, NULL, 0)) != NULL) { - printf("=> [%s]\n", line); - if (! expand_line(line)) - printf("error!\n"); - free(line); - } - - return (0); -} diff --git a/regress/expand/test0 b/regress/expand/test0 deleted file mode 100644 index 456f6a67..00000000 --- a/regress/expand/test0 +++ /dev/null @@ -1,7 +0,0 @@ -# -/dev/null # test -/dev/null, eric, foo, bar, |/some/mda "test,comma" -a b \, \" \' d -a"bc'd", e'fg"h'i, "zer,tyu", 'qsd,fgh' -lsf qls mksq jmlq ksjmlsq kf -lskdjf ,,fsk ,foskfs,,,fsdf,s,f,sf diff --git a/regress/filters/Makefile b/regress/filters/Makefile deleted file mode 100644 index dcbb48d2..00000000 --- a/regress/filters/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -CFLAGS = -I../../smtpd/ -LDFLAGS = -L../../smtpd/libsmtpdfilter -lsmtpdfilter -levent -lutil - -SRCS = filter.c - -INSTALLPATH= /usr/libexec/smtpd - -all: - $(CC) -o $(INSTALLPATH)/filter $(CFLAGS) $(SRCS) $(LDFLAGS) diff --git a/regress/smtp/test.base b/regress/smtp/test.base deleted file mode 100755 index 02e76cdb..00000000 --- a/regress/smtp/test.base +++ /dev/null @@ -1,56 +0,0 @@ -#! /usr/bin/smtpscript -# -# This file is simply used to test the grammar and the behaviour of -# the scripting engine. Not SMTP related. -# - -test-case no-autoconnect { -} - -test-case no-autoconnect expect fail { - fail "on purpose" -} - -test-case no-autoconnect { - fail "ahaha! I failed" -} - -test-case no-autoconnect skip { - fail "should be skipped!" -} - -test-case no-autoconnect { - sleep 500 -} - -test-case no-autoconnect { - connect "localhost" port 25 -} - -proc proc0 %foo %bar { - sleep 1 - sleep 2 - sleep 3 -} - -proc proc1 { - noop -} - -test-case no-autoconnect { - - repeat 2 repeat 3 repeat 4 sleep 7 - sleep 8 - { - call proc0 - sleep 10 - } - - repeat 5 random { - call proc1 - { sleep 1 } - { sleep 2 } - } - - random {} -} diff --git a/regress/smtp/test.mailfrom b/regress/smtp/test.mailfrom deleted file mode 100644 index 0420f1c2..00000000 --- a/regress/smtp/test.mailfrom +++ /dev/null @@ -1,110 +0,0 @@ -proc init-helo { - expect smtp ok - writeln "HELO regress" - expect smtp helo -} - -# Allow emtpy MAIL FROM address (bounce messages) -test-case name "mailfrom.empty" { - call init-helo - writeln "MAIL FROM: <>" - expect smtp ok -} - -# Allow emtpy MAIL FROM address (bounce messages) -test-case name "mailfrom.at-sign" { - call init-helo - writeln "MAIL FROM: <@>" - expect smtp ok -} - -# Reject address without an '@' -test-case name "mailfrom.no-at-sign" { - call init-helo - writeln "MAIL FROM: <a>" - expect smtp permfail -} - -# Reject address with empty domain -test-case name "mailfrom.no-domain" { - call init-helo - writeln "MAIL FROM: <a@>" - expect smtp permfail -} - -# Reject address with empty user -test-case name "mailfrom.no-user" { - call init-helo - writeln "MAIL FROM: <@a>" - expect smtp permfail -} - -# Accept address after source-route stripping -test-case name "mailfrom.src-route" { - call init-helo - writeln "MAIL FROM: <whatever:a@a>" - expect smtp ok -} - -# Reject address with empty user after source-route stripping -test-case name "mailfrom.src-route-no-user" { - call init-helo - writeln "MAIL FROM: <a:@a>" - expect smtp permfail -} - -# Reject space as user -test-case name "mailfrom.space-as-user" { - call init-helo - writeln "MAIL FROM: < @a>" - expect smtp permfail -} - -# Reject space as domain -test-case name "mailfrom.space-as-domain" { - call init-helo - writeln "MAIL FROM: <a@ >" - expect smtp permfail -} - -# Reject unsupported options -test-case name "mailfrom.option" { - call init-helo - writeln "MAIL FROM: <user@domain> OPT" - expect smtp permfail -} - -# Allow SIZE=* -test-case name "mailfrom.option-size" { - call init-helo - writeln "MAIL FROM: <user@domain> SIZE=3005" - expect smtp ok -} - -# Allow BODY=7BIT -test-case name "mailfrom.option-7bit" { - call init-helo - writeln "MAIL FROM: <user@domain> BODY=7BIT" - expect smtp ok -} - -# Allow BODY=8BITMIME -test-case name "mailfrom.option-8bitmime" { - call init-helo - writeln "MAIL FROM: <user@domain> BODY=8BITMIME" - expect smtp ok -} - -# Allow multiple options -test-case name "mailfrom.option-8bitmime-size" { - call init-helo - writeln "MAIL FROM: <user@domain> BODY=8BITMIME SIZE=100" - expect smtp ok -} - -# Allow AUTH= (ignored) -test-case name "mailfrom.option-auth" { - call init-helo - writeln "MAIL FROM: <user@domain> AUTH=WHATEVER" - expect smtp ok -} diff --git a/regress/smtp/test.rcptto b/regress/smtp/test.rcptto deleted file mode 100644 index f6cfd888..00000000 --- a/regress/smtp/test.rcptto +++ /dev/null @@ -1,112 +0,0 @@ -proc init-helo-mailfrom { - expect smtp ok - writeln "HELO regress" - expect smtp helo - writeln "MAIL FROM: <>" - expect smtp ok -} - -# RCPT TO can't be empty -test-case name "rcptto.empty" { - call init-helo-mailfrom - writeln "RCPT TO: <>" - expect smtp permfail -} - -# RCPT TO can't be empty -test-case name "rcptto.at-sign" { - call init-helo-mailfrom - writeln "RCPT TO: <@>" - expect smtp permfail -} - -# Reject address without a '@' -test-case name "rcptto.no-at-sign" { - call init-helo-mailfrom - writeln "RCPT TO: <a>" - expect smtp permfail -} - -# Reject address with empty domain -test-case name "rcptto.no-domain" { - call init-helo-mailfrom - writeln "RCPT TO: <a@>" - expect smtp permfail -} - -# Reject address with empty user -test-case name "rcptto.no-user" { - call init-helo-mailfrom - writeln "RCPT TO: <@a>" - expect smtp permfail -} - -# Accept address after source-route stripping -test-case name "rcptto.src-route" { - call init-helo-mailfrom - writeln "RCPT TO: <whatever:a@a>" - expect smtp ok -} - -# Reject address with empty user after source-route stripping -test-case name "rcptto.src-route-no-user" { - call init-helo-mailfrom - writeln "RCPT TO: <a:@a>" - expect smtp permfail -} - -# Reject address with space as user -test-case name "rcptto.space-as-user" { - call init-helo-mailfrom - writeln "RCPT TO: < @a>" - expect smtp permfail -} - -# Reject address with space as domain -test-case name "rcptto.space-as-domain" { - call init-helo-mailfrom - writeln "RCPT TO: <a@ >" - expect smtp permfail -} - -# Reject options -test-case name "rcptto.option" { - call init-helo-mailfrom - writeln "RCPT TO: <user@domain> OPT" - expect smtp permfail -} - -# Reject options -test-case name "rcptto.option-size" { - call init-helo-mailfrom - writeln "RCPT TO: <user@domain> SIZE=3005" - expect smtp permfail -} - -# Reject options -test-case name "rcptto.option-7bit" { - call init-helo-mailfrom - writeln "RCPT TO: <user@domain> BODY=7BIT" - expect smtp permfail -} - -# Reject options -test-case name "rcptto.option-8bitmime" { - call init-helo-mailfrom - writeln "RCPT TO: <user@domain> BODY=8BITMIME" - expect smtp permfail -} - -# Reject options -test-case name "rcptto.option-8bitmime-size" { - call init-helo-mailfrom - writeln "RCPT TO: <user@domain> BODY=8BITMIME" - expect smtp permfail -} - -# Reject options -test-case name "rcptto.option-auth" { - call init-helo-mailfrom - writeln "RCPT TO: <user@domain> AUTH=WHATEVER" - expect smtp permfail -} diff --git a/regress/smtp/test.smtp0 b/regress/smtp/test.smtp0 deleted file mode 100755 index c56d002e..00000000 --- a/regress/smtp/test.smtp0 +++ /dev/null @@ -1,89 +0,0 @@ -#! /usr/bin/smtpscript -# -# Simple test cases for the SMTP server. -# - -# Make sure we get an smtp welcome message -test-case { - expect smtp -} - -# Make sure that the server accept QUIT and disconnect immediatly -test-case { - expect smtp - writeln "QUIT" - expect smtp ok - expect disconnect -} - -# Make sure that the server rejects an empty HELO -test-case { - expect smtp - writeln "HELO" - expect smtp permfail -} - -# Make sure that the server rejects an empty EHLO -test-case { - expect smtp - writeln "EHLO" - expect smtp permfail -} - -# Make sure that the server accepts HELO -test-case { - expect smtp - writeln "HELO myself" - expect smtp ok -} - -# Make sure that the server accepts EHLO -test-case { - expect smtp - writeln "EHLO myself" - expect smtp helo -} - -# Make sure that the server doesn't accept mail before HELO -test-case { - expect smtp - writeln "MAIL FROM: <me@localhost>" - expect smtp permfail -} - -# Simple mail transfer test, without pipelining -test-case { - expect smtp - writeln "EHLO fkf" - expect smtp helo - writeln "MAIL FROM: <test@blabla>" - expect smtp ok - writeln "RCPT TO: <gilles -@localhost>" - expect smtp ok -# writeln "DATA" -# expect smtp ok -# writeln "foo" -# writeln "." -# expect smtp ok -# writeln "QUIT" -# expect smtp -# expect disconnect -} - -# Simple mail transfer test, with pipelining -test-case { - expect smtp - writeln "EHLO fkf" - expect smtp helo - writeln "MAIL FROM: <test@blabla>" - writeln "RCPT TO: <test@localhost>" - writeln "DATA" - repeat 3 expect smtp ok - writeln "foo" - writeln "." - expect smtp ok - writeln "QUIT" - expect smtp - expect disconnect -} diff --git a/regress/smtp/test.smtp1 b/regress/smtp/test.smtp1 deleted file mode 100755 index 9d4d8c43..00000000 --- a/regress/smtp/test.smtp1 +++ /dev/null @@ -1,88 +0,0 @@ -#! /usr/bin/smtpscript -# -# Simple test cases for the SMTP server. -# - -# Make sure we get an smtp welcome message -test-case { - expect smtp -} - -# Make sure that the server accept QUIT and disconnect immediatly -test-case { - expect smtp - writeln "QUIT" - expect smtp ok - expect disconnect -} - -# Make sure that the server rejects an empty HELO -test-case { - expect smtp - writeln "HELO" - expect smtp permfail -} - -# Make sure that the server rejects an empty EHLO -test-case { - expect smtp - writeln "EHLO" - expect smtp permfail -} - -# Make sure that the server accepts HELO -test-case { - expect smtp - writeln "HELO myself" - expect smtp ok -} - -# Make sure that the server accepts EHLO -test-case { - expect smtp - writeln "EHLO myself" - expect smtp helo -} - -# Make sure that the server doesn't accept mail before HELO -test-case { - expect smtp - writeln "MAIL FROM: <me@localhost>" - expect smtp permfail -} - -# Simple mail transfer test, without pipelining -test-case { - expect smtp - writeln "EHLO fkf" - expect smtp helo - writeln "MAIL FROM: <test@blabla>" - expect smtp ok - writeln "RCPT TO: <gilles@localhost>" - expect smtp ok - writeln "DATA" - expect smtp ok - writeln "foo" - writeln "." - expect smtp ok - writeln "QUIT" - expect smtp - expect disconnect -} - -# Simple mail transfer test, with pipelining -test-case { - expect smtp - writeln "EHLO fkf" - expect smtp helo - writeln "MAIL FROM: <test@blabla>" - writeln "RCPT TO: <gilles@localhost>" - writeln "DATA" - repeat 3 expect smtp ok - writeln "foo" - writeln "." - expect smtp ok - writeln "QUIT" - expect smtp - expect disconnect -} diff --git a/regress/smtp/test.smtp2 b/regress/smtp/test.smtp2 deleted file mode 100755 index 1d5fb841..00000000 --- a/regress/smtp/test.smtp2 +++ /dev/null @@ -1,50 +0,0 @@ -#! /usr/bin/smtpscript -# -# Make sure that the SMTP limits are ok -# - -# Make sure the server disconnects on the 5th useless command -test-case { - expect smtp - repeat 4 { - writeln "FOO" - expect smtp permfail - } - writeln "FOO" - expect disconnect -} - -# 1000 RCPT per message -test-case { - expect smtp - writeln "EHLO fkf" - expect smtp helo - writeln "MAIL FROM: <test@blabla>" - expect smtp ok - repeat 1000 { - writeln "RCPT TO: <test@localhost>" - expect smtp ok - } - writeln "RCPT TO: <test@localhost>" - expect smtp tempfail -} - -# 100 messages per session -test-case { - expect smtp - writeln "EHLO fkf" - expect smtp helo - repeat 100 { - writeln "MAIL FROM: <test@blabla>" - expect smtp ok - writeln "RCPT TO: <test@foo.bar>" - expect smtp ok - writeln "DATA" - expect smtp ok - writeln "foo" - writeln "." - expect smtp ok - } - writeln "MAIL FROM: <test@blabla>" - expect smtp tempfail -} diff --git a/regress/smtp/test.smtp4 b/regress/smtp/test.smtp4 deleted file mode 100755 index a2c5299e..00000000 --- a/regress/smtp/test.smtp4 +++ /dev/null @@ -1,155 +0,0 @@ -#! /usr/bin/smtpscript -# -# Make sure that broken sessions are handled as they should -# - -test-case { - expect smtp - writeln "EHLO" - expect smtp permfail -} - -test-case { - expect smtp - writeln "HELO" - expect smtp permfail -} - -test-case { - expect smtp - writeln "HELO " - expect smtp permfail -} - -test-case { - expect smtp - writeln " HELO " - expect smtp permfail -} - -test-case { - expect smtp - writeln "HELO l" - expect smtp helo -} - - -test-case { - expect smtp - writeln "HELO l" - expect smtp ok -} - -test-case { - expect smtp - writeln "EHLO l" - expect smtp helo -} - -test-case { - expect smtp - writeln "EHLO l" - expect smtp helo - writeln "RCPT TO:" - expect smtp permfail -} - -test-case { - expect smtp - writeln "EHLO l" - expect smtp helo - writeln "DATA" - expect smtp permfail -} - -test-case { - expect smtp - writeln "EHLO l" - expect smtp helo - writeln "MAIL FROM" - expect smtp permfail -} - -test-case { - expect smtp - writeln "EHLO l" - expect smtp helo - writeln "MAIL FROM:<opensmtpd@opensmtpd.org>" - expect smtp ok - writeln "DATA" - expect smtp permfail -} - -test-case { - expect smtp - writeln "EHLO l" - expect smtp helo - writeln "MAIL FROM:<opensmtpd@opensmtpd.org>" - expect smtp ok - writeln "RCPT TO:" - expect smtp permfail -} - -test-case { - expect smtp - writeln "EHLO l" - expect smtp helo - writeln "MAIL FROM:<opensmtpd@opensmtpd.org>" - expect smtp ok - writeln "RCPT TO:<root@localhost>" - expect smtp ok - writeln "RSET" - expect smtp ok - writeln "DATA" - expect smtp permfail -} - -test-case { - expect smtp - writeln "EHLO l" - expect smtp helo - writeln "MAIL FROM:<opensmtpd@opensmtpd.org>" - expect smtp ok - writeln "RCPT TO:<root@localhost>" - expect smtp ok - writeln "RSET" - expect smtp ok - writeln "MAIL FROM:<opensmtpd@opensmtpd.org>" - expect smtp ok - writeln "DATA" - expect smtp permfail -} - -test-case { - expect smtp ok - writeln "EHLO l" - expect smtp helo - writeln "MAIL FROM:<opensmtpd@opensmtpd.org>" - expect smtp ok - writeln "RCPT TO:<root@localhost>" - expect smtp ok - writeln "RSET" - expect smtp ok - writeln "MAIL FROM:<opensmtpd@opensmtpd.org>" - expect smtp ok - writeln "RCPT TO:<root@localhost>" - expect smtp ok - writeln "DATA" - expect smtp ok -} - -test-case { - expect smtp ok - writeln "EHLO l" - expect smtp helo - writeln "MAIL FROM:<opensmtpd@opensmtpd.org> SIZE=1000" - expect smtp ok -} - -test-case { - expect smtp ok - writeln "HELO l" - expect smtp ok - writeln "MAIL FROM:<opensmtpd@opensmtpd.org> SIZE=1000" - expect smtp ok -} diff --git a/regress/smtp/test.tls b/regress/smtp/test.tls deleted file mode 100644 index f7ebdf19..00000000 --- a/regress/smtp/test.tls +++ /dev/null @@ -1,10 +0,0 @@ -test-case name "starttls" { - expect smtp ok - writeln "EHLO regress" - expect smtp helo - writeln "STARTTLS" - expect smtp ok - starttls - writeln "EHLO regress" - expect smtp helo -} diff --git a/smtpd/lka_proc.c b/smtpd/lka_proc.c deleted file mode 100644 index f90d006f..00000000 --- a/smtpd/lka_proc.c +++ /dev/null @@ -1,188 +0,0 @@ -/* $OpenBSD: lka_proc.c,v 1.12 2019/09/30 13:27:12 gilles Exp $ */ - -/* - * Copyright (c) 2018 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <netinet/in.h> - -#include <errno.h> -#include <event.h> -#include <imsg.h> -#include <inttypes.h> -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "smtpd.h" -#include "log.h" - -static int inited = 0; -static struct dict processors; - -struct processor_instance { - char *name; - struct io *io; - struct io *errfd; - int ready; -}; - -static void processor_io(struct io *, int, void *); -static void processor_errfd(struct io *, int, void *); -void lka_filter_process_response(const char *, const char *); - -int -lka_proc_ready(void) -{ - void *iter; - struct processor_instance *pi; - - iter = NULL; - while (dict_iter(&processors, &iter, NULL, (void **)&pi)) - if (!pi->ready) - return 0; - return 1; -} - -static void -lka_proc_config(struct processor_instance *pi) -{ - io_printf(pi->io, "config|smtpd-version|%s\n", SMTPD_VERSION); - io_printf(pi->io, "config|smtp-session-timeout|%d\n", SMTPD_SESSION_TIMEOUT); - io_printf(pi->io, "config|ready\n"); -} - -void -lka_proc_forked(const char *name, int fd) -{ - struct processor_instance *processor; - - if (!inited) { - dict_init(&processors); - inited = 1; - } - - processor = xcalloc(1, sizeof *processor); - processor->name = xstrdup(name); - processor->io = io_new(); - - io_set_nonblocking(fd); - - io_set_fd(processor->io, fd); - io_set_callback(processor->io, processor_io, processor->name); - dict_xset(&processors, name, processor); -} - -void -lka_proc_errfd(const char *name, int fd) -{ - struct processor_instance *processor; - - processor = dict_xget(&processors, name); - - io_set_nonblocking(fd); - - processor->errfd = io_new(); - io_set_fd(processor->errfd, fd); - io_set_callback(processor->errfd, processor_errfd, processor->name); - - lka_proc_config(processor); -} - -struct io * -lka_proc_get_io(const char *name) -{ - struct processor_instance *processor; - - processor = dict_xget(&processors, name); - - return processor->io; -} - -static void -processor_register(const char *name, const char *line) -{ - struct processor_instance *processor; - - processor = dict_xget(&processors, name); - - if (strcmp(line, "register|ready") == 0) { - processor->ready = 1; - return; - } - - if (strncmp(line, "register|report|", 16) == 0) { - lka_report_register_hook(name, line+16); - return; - } - - if (strncmp(line, "register|filter|", 16) == 0) { - lka_filter_register_hook(name, line+16); - return; - } - - fatalx("Invalid register line received: %s", line); -} - -static void -processor_io(struct io *io, int evt, void *arg) -{ - struct processor_instance *processor; - const char *name = arg; - char *line = NULL; - ssize_t len; - - switch (evt) { - case IO_DATAIN: - while ((line = io_getline(io, &len)) != NULL) { - if (strncmp("register|", line, 9) == 0) { - processor_register(name, line); - continue; - } - - processor = dict_xget(&processors, name); - if (!processor->ready) - fatalx("Non-register message before register|" - "ready: %s", line); - else if (strncmp(line, "filter-result|", 14) == 0 || - strncmp(line, "filter-dataline|", 16) == 0) - lka_filter_process_response(name, line); - else if (strncmp(line, "report|", 7) == 0) - lka_report_proc(name, line); - else - fatalx("Invalid filter message type: %s", line); - } - } -} - -static void -processor_errfd(struct io *io, int evt, void *arg) -{ - const char *name = arg; - char *line = NULL; - ssize_t len; - - switch (evt) { - case IO_DATAIN: - while ((line = io_getline(io, &len)) != NULL) - log_warnx("%s: %s", name, line); - } -} diff --git a/smtpd/lka_report.c b/smtpd/lka_report.c deleted file mode 100644 index 95562e84..00000000 --- a/smtpd/lka_report.c +++ /dev/null @@ -1,509 +0,0 @@ -/* $OpenBSD: lka_report.c,v 1.34 2019/10/03 05:42:57 gilles Exp $ */ - -/* - * Copyright (c) 2018 Gilles Chehade <gilles@poolp.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> - -#include <netinet/in.h> - -#include <errno.h> -#include <event.h> -#include <imsg.h> -#include <inttypes.h> -#include <limits.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "smtpd.h" -#include "log.h" - -#define PROTOCOL_VERSION "0.4" - -struct reporter_proc { - TAILQ_ENTRY(reporter_proc) entries; - const char *name; -}; -TAILQ_HEAD(reporters, reporter_proc); - -static struct dict smtp_in; -static struct dict smtp_out; - -static struct smtp_events { - const char *event; -} smtp_events[] = { - { "link-connect" }, - { "link-disconnect" }, - { "link-greeting" }, - { "link-identify" }, - { "link-tls" }, - { "link-auth" }, - - { "tx-reset" }, - { "tx-begin" }, - { "tx-mail" }, - { "tx-rcpt" }, - { "tx-envelope" }, - { "tx-data" }, - { "tx-commit" }, - { "tx-rollback" }, - - { "protocol-client" }, - { "protocol-server" }, - - { "filter-report" }, - { "filter-response" }, - - { "timeout" }, -}; - -static void -report_smtp_broadcast(uint64_t, const char *, struct timeval *, const char *, - const char *, ...) __attribute__((__format__ (printf, 5, 6))); - -void -lka_report_init(void) -{ - struct reporters *tailq; - size_t i; - - dict_init(&smtp_in); - dict_init(&smtp_out); - - for (i = 0; i < nitems(smtp_events); ++i) { - tailq = xcalloc(1, sizeof (struct reporters)); - TAILQ_INIT(tailq); - dict_xset(&smtp_in, smtp_events[i].event, tailq); - - tailq = xcalloc(1, sizeof (struct reporters)); - TAILQ_INIT(tailq); - dict_xset(&smtp_out, smtp_events[i].event, tailq); - } -} - -void -lka_report_register_hook(const char *name, const char *hook) -{ - struct dict *subsystem; - struct reporter_proc *rp; - struct reporters *tailq; - void *iter; - size_t i; - - if (strncmp(hook, "smtp-in|", 8) == 0) { - subsystem = &smtp_in; - hook += 8; - } -#if 0 - /* No smtp-out event has been implemented yet */ - else if (strncmp(hook, "smtp-out|", 9) == 0) { - subsystem = &smtp_out; - hook += 9; - } -#endif - else - fatalx("Invalid message direction: %s", hook); - - if (strcmp(hook, "*") == 0) { - iter = NULL; - while (dict_iter(subsystem, &iter, NULL, (void **)&tailq)) { - rp = xcalloc(1, sizeof *rp); - rp->name = xstrdup(name); - TAILQ_INSERT_TAIL(tailq, rp, entries); - } - return; - } - - for (i = 0; i < nitems(smtp_events); i++) - if (strcmp(hook, smtp_events[i].event) == 0) - break; - if (i == nitems(smtp_events)) - fatalx("Unrecognized report name: %s", hook); - - tailq = dict_get(subsystem, hook); - rp = xcalloc(1, sizeof *rp); - rp->name = xstrdup(name); - TAILQ_INSERT_TAIL(tailq, rp, entries); -} - -static void -report_smtp_broadcast(uint64_t reqid, const char *direction, struct timeval *tv, const char *event, - const char *format, ...) -{ - va_list ap; - struct dict *d; - struct reporters *tailq; - struct reporter_proc *rp; - - if (strcmp("smtp-in", direction) == 0) - d = &smtp_in; - - else if (strcmp("smtp-out", direction) == 0) - d = &smtp_out; - - else - fatalx("unexpected direction: %s", direction); - - tailq = dict_xget(d, event); - TAILQ_FOREACH(rp, tailq, entries) { - if (!lka_filter_proc_in_session(reqid, rp->name)) - continue; - - va_start(ap, format); - if (io_printf(lka_proc_get_io(rp->name), - "report|%s|%lld.%06ld|%s|%s|%016"PRIx64"%s", - PROTOCOL_VERSION, tv->tv_sec, tv->tv_usec, direction, - event, reqid, format[0] != '\n' ? "|" : "") == -1 || - io_vprintf(lka_proc_get_io(rp->name), format, ap) == -1) - fatalx("failed to write to processor"); - va_end(ap); - } -} - -void -lka_report_smtp_link_connect(const char *direction, struct timeval *tv, uint64_t reqid, const char *rdns, - int fcrdns, - const struct sockaddr_storage *ss_src, - const struct sockaddr_storage *ss_dest) -{ - char src[NI_MAXHOST + 5]; - char dest[NI_MAXHOST + 5]; - uint16_t src_port = 0; - uint16_t dest_port = 0; - const char *fcrdns_str; - - if (ss_src->ss_family == AF_INET) - src_port = ntohs(((const struct sockaddr_in *)ss_src)->sin_port); - else if (ss_src->ss_family == AF_INET6) - src_port = ntohs(((const struct sockaddr_in6 *)ss_src)->sin6_port); - - if (ss_dest->ss_family == AF_INET) - dest_port = ntohs(((const struct sockaddr_in *)ss_dest)->sin_port); - else if (ss_dest->ss_family == AF_INET6) - dest_port = ntohs(((const struct sockaddr_in6 *)ss_dest)->sin6_port); - - if (strcmp(ss_to_text(ss_src), "local") == 0) { - (void)snprintf(src, sizeof src, "unix:%s", SMTPD_SOCKET); - (void)snprintf(dest, sizeof dest, "unix:%s", SMTPD_SOCKET); - } else { - (void)snprintf(src, sizeof src, "%s:%d", ss_to_text(ss_src), src_port); - (void)snprintf(dest, sizeof dest, "%s:%d", ss_to_text(ss_dest), dest_port); - } - - switch (fcrdns) { - case 1: - fcrdns_str = "pass"; - break; - case 0: - fcrdns_str = "fail"; - break; - default: - fcrdns_str = "error"; - break; - } - - report_smtp_broadcast(reqid, direction, tv, "link-connect", - "%s|%s|%s|%s\n", rdns, fcrdns_str, src, dest); -} - -void -lka_report_smtp_link_disconnect(const char *direction, struct timeval *tv, uint64_t reqid) -{ - report_smtp_broadcast(reqid, direction, tv, "link-disconnect", "\n"); -} - -void -lka_report_smtp_link_greeting(const char *direction, uint64_t reqid, - struct timeval *tv, const char *domain) -{ - report_smtp_broadcast(reqid, direction, tv, "link-greeting", "%s\n", - domain); -} - -void -lka_report_smtp_link_auth(const char *direction, struct timeval *tv, uint64_t reqid, - const char *username, const char *result) -{ - report_smtp_broadcast(reqid, direction, tv, "link-auth", "%s|%s\n", - username, result); -} - -void -lka_report_smtp_link_identify(const char *direction, struct timeval *tv, - uint64_t reqid, const char *method, const char *heloname) -{ - report_smtp_broadcast(reqid, direction, tv, "link-identify", "%s|%s\n", - method, heloname); -} - -void -lka_report_smtp_link_tls(const char *direction, struct timeval *tv, uint64_t reqid, const char *ciphers) -{ - report_smtp_broadcast(reqid, direction, tv, "link-tls", "%s\n", - ciphers); -} - -void -lka_report_smtp_tx_reset(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid) -{ - report_smtp_broadcast(reqid, direction, tv, "tx-reset", "%08x\n", - msgid); -} - -void -lka_report_smtp_tx_begin(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid) -{ - report_smtp_broadcast(reqid, direction, tv, "tx-begin", "%08x\n", - msgid); -} - -void -lka_report_smtp_tx_mail(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, const char *address, int ok) -{ - const char *result; - - switch (ok) { - case 1: - result = "ok"; - break; - case 0: - result = "permfail"; - break; - default: - result = "tempfail"; - break; - } - report_smtp_broadcast(reqid, direction, tv, "tx-mail", "%08x|%s|%s\n", - msgid, address, result); -} - -void -lka_report_smtp_tx_rcpt(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, const char *address, int ok) -{ - const char *result; - - switch (ok) { - case 1: - result = "ok"; - break; - case 0: - result = "permfail"; - break; - default: - result = "tempfail"; - break; - } - report_smtp_broadcast(reqid, direction, tv, "tx-rcpt", "%08x|%s|%s\n", - msgid, address, result); -} - -void -lka_report_smtp_tx_envelope(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, uint64_t evpid) -{ - report_smtp_broadcast(reqid, direction, tv, "tx-envelope", - "%08x|%016"PRIx64"\n", msgid, evpid); -} - -void -lka_report_smtp_tx_data(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, int ok) -{ - const char *result; - - switch (ok) { - case 1: - result = "ok"; - break; - case 0: - result = "permfail"; - break; - default: - result = "tempfail"; - break; - } - report_smtp_broadcast(reqid, direction, tv, "tx-data", "%08x|%s\n", - msgid, result); -} - -void -lka_report_smtp_tx_commit(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, size_t msgsz) -{ - report_smtp_broadcast(reqid, direction, tv, "tx-commit", "%08x|%zd\n", - msgid, msgsz); -} - -void -lka_report_smtp_tx_rollback(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid) -{ - report_smtp_broadcast(reqid, direction, tv, "tx-rollback", "%08x\n", - msgid); -} - -void -lka_report_smtp_protocol_client(const char *direction, struct timeval *tv, uint64_t reqid, const char *command) -{ - report_smtp_broadcast(reqid, direction, tv, "protocol-client", "%s\n", - command); -} - -void -lka_report_smtp_protocol_server(const char *direction, struct timeval *tv, uint64_t reqid, const char *response) -{ - report_smtp_broadcast(reqid, direction, tv, "protocol-server", "%s\n", - response); -} - -void -lka_report_smtp_filter_response(const char *direction, struct timeval *tv, uint64_t reqid, - int phase, int response, const char *param) -{ - const char *phase_name; - const char *response_name; - - switch (phase) { - case FILTER_CONNECT: - phase_name = "connected"; - break; - case FILTER_HELO: - phase_name = "helo"; - break; - case FILTER_EHLO: - phase_name = "ehlo"; - break; - case FILTER_STARTTLS: - phase_name = "tls"; - break; - case FILTER_AUTH: - phase_name = "auth"; - break; - case FILTER_MAIL_FROM: - phase_name = "mail-from"; - break; - case FILTER_RCPT_TO: - phase_name = "rcpt-to"; - break; - case FILTER_DATA: - phase_name = "data"; - break; - case FILTER_DATA_LINE: - phase_name = "data-line"; - break; - case FILTER_RSET: - phase_name = "rset"; - break; - case FILTER_QUIT: - phase_name = "quit"; - break; - case FILTER_NOOP: - phase_name = "noop"; - break; - case FILTER_HELP: - phase_name = "help"; - break; - case FILTER_WIZ: - phase_name = "wiz"; - break; - case FILTER_COMMIT: - phase_name = "commit"; - break; - default: - phase_name = ""; - } - - switch (response) { - case FILTER_PROCEED: - response_name = "proceed"; - break; - case FILTER_JUNK: - response_name = "junk"; - break; - case FILTER_REWRITE: - response_name = "rewrite"; - break; - case FILTER_REJECT: - response_name = "reject"; - break; - case FILTER_DISCONNECT: - response_name = "disconnect"; - break; - default: - response_name = ""; - } - - report_smtp_broadcast(reqid, direction, tv, "filter-response", - "%s|%s%s%s\n", phase_name, response_name, param ? "|" : "", - param ? param : ""); -} - -void -lka_report_smtp_timeout(const char *direction, struct timeval *tv, uint64_t reqid) -{ - report_smtp_broadcast(reqid, direction, tv, "timeout", "\n"); -} - -void -lka_report_filter_report(uint64_t reqid, const char *name, int builtin, - const char *direction, struct timeval *tv, const char *message) -{ - report_smtp_broadcast(reqid, direction, tv, "filter-report", - "%s|%s|%s\n", builtin ? "builtin" : "proc", - name, message); -} - -void -lka_report_proc(const char *name, const char *line) -{ - char buffer[LINE_MAX]; - struct timeval tv; - char *ep, *sp, *direction; - uint64_t reqid; - - if (strlcpy(buffer, line + 7, sizeof(buffer)) >= sizeof(buffer)) - fatalx("Invalid report: line too long: %s", line); - - errno = 0; - tv.tv_sec = strtoll(buffer, &ep, 10); - if (ep[0] != '.' || errno != 0) - fatalx("Invalid report: invalid time: %s", line); - sp = ep + 1; - tv.tv_usec = strtol(sp, &ep, 10); - if (ep[0] != '|' || errno != 0) - fatalx("Invalid report: invalid time: %s", line); - if (ep - sp != 6) - fatalx("Invalid report: invalid time: %s", line); - - direction = ep + 1; - if (strncmp(direction, "smtp-in|", 8) == 0) { - direction[7] = '\0'; - direction += 7; -#if 0 - } else if (strncmp(direction, "smtp-out|", 9) == 0) { - direction[8] = '\0'; - direction += 8; -#endif - } else - fatalx("Invalid report: invalid direction: %s", line); - - reqid = strtoull(sp, &ep, 16); - if (ep[0] != '|' || errno != 0) - fatalx("Invalid report: invalid reqid: %s", line); - sp = ep + 1; - - lka_report_filter_report(reqid, name, 0, direction, &tv, sp); -} diff --git a/smtpd/smtpctl/CVS/Entries b/smtpd/smtpctl/CVS/Entries deleted file mode 100644 index 4c1dedd7..00000000 --- a/smtpd/smtpctl/CVS/Entries +++ /dev/null @@ -1,2 +0,0 @@ -/Makefile/1.46/Wed Apr 25 07:51:42 2018// -D diff --git a/smtpd/smtpctl/CVS/Repository b/smtpd/smtpctl/CVS/Repository deleted file mode 100644 index edc50474..00000000 --- a/smtpd/smtpctl/CVS/Repository +++ /dev/null @@ -1 +0,0 @@ -src/usr.sbin/smtpd/smtpctl diff --git a/smtpd/smtpctl/CVS/Root b/smtpd/smtpctl/CVS/Root deleted file mode 100644 index 8a89d1a7..00000000 --- a/smtpd/smtpctl/CVS/Root +++ /dev/null @@ -1 +0,0 @@ -gilles@cvs.openbsd.org:/cvs diff --git a/smtpd/smtpd/CVS/Repository b/smtpd/smtpd/CVS/Repository deleted file mode 100644 index 11be251c..00000000 --- a/smtpd/smtpd/CVS/Repository +++ /dev/null @@ -1 +0,0 @@ -src/usr.sbin/smtpd/smtpd diff --git a/smtpd/smtpd/CVS/Root b/smtpd/smtpd/CVS/Root deleted file mode 100644 index 8a89d1a7..00000000 --- a/smtpd/smtpd/CVS/Root +++ /dev/null @@ -1 +0,0 @@ -gilles@cvs.openbsd.org:/cvs diff --git a/smtpscript/iobuf.c b/smtpscript/iobuf.c index 7dcd45b9..05a9cd59 100644 --- a/smtpscript/iobuf.c +++ b/smtpscript/iobuf.c @@ -26,7 +26,7 @@ #include <string.h> #include <unistd.h> -#ifdef IO_SSL +#ifdef IO_TLS #include <openssl/err.h> #include <openssl/ssl.h> #endif @@ -387,7 +387,7 @@ iobuf_flush(struct iobuf *io, int fd) return (0); } -#ifdef IO_SSL +#ifdef IO_TLS int iobuf_flush_ssl(struct iobuf *io, void *ssl) @@ -463,4 +463,4 @@ iobuf_read_ssl(struct iobuf *io, void *ssl) return (n); } -#endif /* IO_SSL */ +#endif /* IO_TLS */ diff --git a/smtpscript/smtpscript/Makefile b/smtpscript/smtpscript/Makefile index 3679481c..0520606f 100644 --- a/smtpscript/smtpscript/Makefile +++ b/smtpscript/smtpscript/Makefile @@ -7,6 +7,6 @@ NOMAN= noman LDADD+= -lutil -lssl -lcrypto DPADD+= ${LIBEVENT} ${LIBUTIL} ${LIBSSL} ${LIBCRYPTO} CPPFLAGS+= -I${.CURDIR}/.. -CPPFLAGS+= -DIO_SSL +CPPFLAGS+= -DIO_TLS .include <bsd.prog.mk> diff --git a/smtpd/Makefile b/usr.sbin/smtpd/Makefile index a3dbc9d1..a3dbc9d1 100644 --- a/smtpd/Makefile +++ b/usr.sbin/smtpd/Makefile diff --git a/smtpd/aliases.5 b/usr.sbin/smtpd/aliases.5 index 802ca9a6..7c250c81 100644 --- a/smtpd/aliases.5 +++ b/usr.sbin/smtpd/aliases.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: aliases.5,v 1.15 2018/05/24 11:38:24 gilles Exp $ +.\" $OpenBSD: aliases.5,v 1.16 2020/04/23 21:28:10 jmc Exp $ .\" .\" Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: May 24 2018 $ +.Dd $Mdocdate: April 23 2020 $ .Dt ALIASES 5 .Os .Sh NAME @@ -77,7 +77,7 @@ will only use the part that precedes .Sq + as a .Em key . -.It Ar error : Ns Ar code Ar message +.It Ar error : Ns Ar code message A status code and message to return. The code must be 3 digits, starting 4XX (TempFail) or 5XX (PermFail). diff --git a/smtpd/aliases.c b/usr.sbin/smtpd/aliases.c index 884f6963..0f8a5c1e 100644 --- a/smtpd/aliases.c +++ b/usr.sbin/smtpd/aliases.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aliases.c,v 1.77 2018/12/28 12:47:28 eric Exp $ */ +/* $OpenBSD: aliases.c,v 1.78 2020/04/28 21:46:43 eric Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -29,7 +31,12 @@ #include <stdlib.h> #include <string.h> #include <limits.h> +#ifdef HAVE_UTIL_H #include <util.h> +#endif +#ifdef HAVE_LIBUTIL_H +#include <libutil.h> +#endif #include "smtpd.h" #include "log.h" @@ -164,6 +171,10 @@ aliases_virtual_get(struct expand *expand, const struct mailaddr *maddr) if (ret) goto expand; + /* Do not try catch-all entries if there is no domain */ + if (domain[0] == '\0') + return 0; + if (!bsnprintf(buf, sizeof(buf), "@%s", domain)) return 0; /* Failed ? We lookup for catch all for virtual domain */ diff --git a/smtpd/bounce.c b/usr.sbin/smtpd/bounce.c index 02239988..4a4a0992 100644 --- a/smtpd/bounce.c +++ b/usr.sbin/smtpd/bounce.c @@ -1,4 +1,4 @@ -/* $OpenBSD: bounce.c,v 1.80 2018/12/08 08:01:15 sunil Exp $ */ +/* $OpenBSD: bounce.c,v 1.82 2020/04/24 11:34:07 eric Exp $ */ /* * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org> @@ -18,6 +18,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -184,7 +186,7 @@ bounce_add(uint64_t evpid) line = evp.errorline; if (strlen(line) > 4 && (*line == '1' || *line == '6')) line += 4; - (void)snprintf(buf, sizeof(buf), "%s@%s: %s\n", evp.dest.user, + (void)snprintf(buf, sizeof(buf), "%s@%s: %s", evp.dest.user, evp.dest.domain, line); be = xmalloc(sizeof *be); @@ -196,8 +198,7 @@ bounce_add(uint64_t evpid) be->esc_class = evp.esc_class; be->esc_code = evp.esc_code; TAILQ_INSERT_TAIL(&msg->envelopes, be, entry); - buf[strcspn(buf, "\n")] = '\0'; - log_debug("debug: bounce: adding report %16"PRIx64": %s", be->id, buf); + log_debug("debug: bounce: adding report %16"PRIx64": %s", be->id, be->report); msg->timeout = time(NULL) + 1; TAILQ_INSERT_TAIL(&pending, msg, entry); @@ -311,7 +312,7 @@ bounce_send(struct bounce_session *s, const char *fmt, ...) log_trace(TRACE_BOUNCE, "bounce: %p: >>> %s", s, p); - io_xprintf(s->io, "%s\n", p); + io_xprintf(s->io, "%s\r\n", p); free(p); } @@ -343,27 +344,27 @@ bounce_duration(long long int d) } #define NOTICE_INTRO \ - " Hi!\n\n" \ - " This is the MAILER-DAEMON, please DO NOT REPLY to this email.\n" + " Hi!\r\n\r\n" \ + " This is the MAILER-DAEMON, please DO NOT REPLY to this email.\r\n" const char *notice_error = - " An error has occurred while attempting to deliver a message for\n" - " the following list of recipients:\n\n"; + " An error has occurred while attempting to deliver a message for\r\n" + " the following list of recipients:\r\n\r\n"; const char *notice_warning = - " A message is delayed for more than %s for the following\n" - " list of recipients:\n\n"; + " A message is delayed for more than %s for the following\r\n" + " list of recipients:\r\n\r\n"; const char *notice_warning2 = - " Please note that this is only a temporary failure report.\n" - " The message is kept in the queue for up to %s.\n" - " You DO NOT NEED to re-send the message to these recipients.\n\n"; + " Please note that this is only a temporary failure report.\r\n" + " The message is kept in the queue for up to %s.\r\n" + " You DO NOT NEED to re-send the message to these recipients.\r\n\r\n"; const char *notice_success = - " Your message was successfully delivered to these recipients.\n\n"; + " Your message was successfully delivered to these recipients.\r\n\r\n"; const char *notice_relay = - " Your message was relayed to these recipients.\n\n"; + " Your message was relayed to these recipients.\r\n\r\n"; static int bounce_next_message(struct bounce_session *s) @@ -451,16 +452,16 @@ bounce_next(struct bounce_session *s) /* Construct an appropriate notice. */ io_xprintf(s->io, - "Subject: Delivery status notification: %s\n" - "From: Mailer Daemon <MAILER-DAEMON@%s>\n" - "To: %s\n" - "Date: %s\n" - "MIME-Version: 1.0\n" + "Subject: Delivery status notification: %s\r\n" + "From: Mailer Daemon <MAILER-DAEMON@%s>\r\n" + "To: %s\r\n" + "Date: %s\r\n" + "MIME-Version: 1.0\r\n" "Content-Type: multipart/mixed;" - "boundary=\"%16" PRIu64 "/%s\"\n" - "\n" - "This is a MIME-encapsulated message.\n" - "\n", + "boundary=\"%16" PRIu64 "/%s\"\r\n" + "\r\n" + "This is a MIME-encapsulated message.\r\n" + "\r\n", action_str(&s->msg->bounce), s->smtpname, s->msg->to, @@ -469,12 +470,12 @@ bounce_next(struct bounce_session *s) s->smtpname); io_xprintf(s->io, - "--%16" PRIu64 "/%s\n" - "Content-Description: Notification\n" - "Content-Type: text/plain; charset=us-ascii\n" - "\n" + "--%16" PRIu64 "/%s\r\n" + "Content-Description: Notification\r\n" + "Content-Type: text/plain; charset=us-ascii\r\n" + "\r\n" NOTICE_INTRO - "\n", + "\r\n", s->boundary, s->smtpname); switch (s->msg->bounce.type) { @@ -495,35 +496,36 @@ bounce_next(struct bounce_session *s) TAILQ_FOREACH(evp, &s->msg->envelopes, entry) { io_xprint(s->io, evp->report); + io_xprint(s->io, "\r\n"); } - io_xprint(s->io, "\n"); + io_xprint(s->io, "\r\n"); if (s->msg->bounce.type == B_DELAYED) io_xprintf(s->io, notice_warning2, bounce_duration(s->msg->bounce.ttl)); io_xprintf(s->io, - " Below is a copy of the original message:\n" - "\n"); + " Below is a copy of the original message:\r\n" + "\r\n"); io_xprintf(s->io, - "--%16" PRIu64 "/%s\n" - "Content-Description: Delivery Report\n" - "Content-Type: message/delivery-status\n" - "\n", + "--%16" PRIu64 "/%s\r\n" + "Content-Description: Delivery Report\r\n" + "Content-Type: message/delivery-status\r\n" + "\r\n", s->boundary, s->smtpname); io_xprintf(s->io, - "Reporting-MTA: dns; %s\n" - "\n", + "Reporting-MTA: dns; %s\r\n" + "\r\n", s->smtpname); TAILQ_FOREACH(evp, &s->msg->envelopes, entry) { io_xprintf(s->io, - "Final-Recipient: rfc822; %s@%s\n" - "Action: %s\n" - "Status: %s\n" - "\n", + "Final-Recipient: rfc822; %s@%s\r\n" + "Action: %s\r\n" + "Status: %s\r\n" + "\r\n", evp->dest.user, evp->dest.domain, action_str(&s->msg->bounce), @@ -538,10 +540,10 @@ bounce_next(struct bounce_session *s) case BOUNCE_DATA_MESSAGE: io_xprintf(s->io, - "--%16" PRIu64 "/%s\n" - "Content-Description: Message headers\n" - "Content-Type: text/rfc822-headers\n" - "\n", + "--%16" PRIu64 "/%s\r\n" + "Content-Description: Message headers\r\n" + "Content-Type: text/rfc822-headers\r\n" + "\r\n", s->boundary, s->smtpname); n = io_queued(s->io); @@ -555,14 +557,14 @@ bounce_next(struct bounce_session *s) fclose(s->msgfp); s->msgfp = NULL; io_xprintf(s->io, - "\n--%16" PRIu64 "/%s--\n", s->boundary, + "\r\n--%16" PRIu64 "/%s--\r\n", s->boundary, s->smtpname); bounce_send(s, "."); s->state = BOUNCE_DATA_END; return (0); } line[len - 1] = '\0'; - io_xprintf(s->io, "%s%s\n", + io_xprintf(s->io, "%s%s\r\n", (len == 2 && line[0] == '.') ? "." : "", line); } free(line); @@ -577,7 +579,7 @@ bounce_next(struct bounce_session *s) } io_xprintf(s->io, - "\n--%16" PRIu64 "/%s--\n", s->boundary, s->smtpname); + "\r\n--%16" PRIu64 "/%s--\r\n", s->boundary, s->smtpname); log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]", s, io_queued(s->io) - n); @@ -728,6 +730,10 @@ bounce_io(struct io *io, int evt, void *arg) if (line == NULL) break; + /* Strip trailing '\r' */ + if (len && line[len - 1] == '\r') + line[--len] = '\0'; + log_trace(TRACE_BOUNCE, "bounce: %p: <<< %s", s, line); if ((error = parse_smtp_response(line, len, &msg, &cont))) { diff --git a/smtpd/ca.c b/usr.sbin/smtpd/ca.c index fdc177e2..b36033b4 100644 --- a/smtpd/ca.c +++ b/usr.sbin/smtpd/ca.c @@ -17,11 +17,14 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/socket.h> #include <sys/tree.h> +#include <grp.h> /* needed for setgroups */ #include <err.h> #include <imsg.h> #include <limits.h> @@ -68,7 +71,6 @@ static int ecdsae_sign_setup(EC_KEY *, BN_CTX *, BIGNUM **, BIGNUM **); static int ecdsae_do_verify(const unsigned char *, int, const ECDSA_SIG *, EC_KEY *); - static uint64_t reqid = 0; static void @@ -115,8 +117,10 @@ ca(void) /* Ignore them until we get our config */ mproc_disable(p_pony); +#if HAVE_PLEDGE if (pledge("stdio", NULL) == -1) err(1, "pledge"); +#endif event_dispatch(); fatalx("exited event loop"); @@ -318,6 +322,7 @@ ca_imsg(struct mproc *p, struct imsg *imsg) buf_len = ECDSA_size(ecdsa); if ((to = calloc(1, buf_len)) == NULL) fatalx("ca_imsg: calloc"); + ret = ECDSA_sign(0, from, flen, to, &buf_len, ecdsa); m_create(p, imsg->hdr.type, 0, 0, -1); m_add_id(p, id); @@ -329,7 +334,6 @@ ca_imsg(struct mproc *p, struct imsg *imsg) EC_KEY_free(ecdsa); return; } - errx(1, "ca_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type)); } @@ -503,6 +507,7 @@ rsae_keygen(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb) * ECDSA privsep engine (called from unprivileged processes) */ +#if defined(SUPPORT_ECDSA) const ECDSA_METHOD *ecdsa_default = NULL; static ECDSA_METHOD *ecdsae_method = NULL; @@ -526,6 +531,11 @@ ECDSA_METHOD_new_temporary(const char *name, int flags) ecdsa->flags = flags; return ecdsa; } +#else +const EC_KEY_METHOD *ecdsa_default = NULL; + +static EC_KEY_METHOD *ecdsae_method = NULL; +#endif static ECDSA_SIG * ecdsae_send_enc_imsg(const unsigned char *dgst, int dgst_len, @@ -542,8 +552,13 @@ ecdsae_send_enc_imsg(const unsigned char *dgst, int dgst_len, uint64_t id; ECDSA_SIG *sig = NULL; +#if defined(SUPPORT_ECDSA) if ((pkiname = ECDSA_get_ex_data(eckey, 0)) == NULL) return (0); +#else + if ((pkiname = EC_KEY_get_ex_data(eckey, 0)) == NULL) + return (0); +#endif /* * Send a synchronous imsg because we cannot defer the ECDSA @@ -601,6 +616,7 @@ ecdsae_send_enc_imsg(const unsigned char *dgst, int dgst_len, return (sig); } +#if defined(SUPPORT_ECDSA) ECDSA_SIG * ecdsae_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey) @@ -609,6 +625,7 @@ ecdsae_do_sign(const unsigned char *dgst, int dgst_len, if (ECDSA_get_ex_data(eckey, 0) != NULL) return (ecdsae_send_enc_imsg(dgst, dgst_len, inv, rp, eckey)); return (ecdsa_default->ecdsa_do_sign(dgst, dgst_len, inv, rp, eckey)); + } int @@ -626,7 +643,115 @@ ecdsae_do_verify(const unsigned char *dgst, int dgst_len, log_debug("debug: %s: %s", proc_name(smtpd_process), __func__); return (ecdsa_default->ecdsa_do_verify(dgst, dgst_len, sig, eckey)); } +#else +int +ecdsae_keygen(EC_KEY *eckey) +{ + int (*keygen)(EC_KEY *key); + EC_KEY_METHOD_get_keygen(ecdsa_default, + &keygen); + return keygen(eckey); +} + +int +ecdsae_compute_key(unsigned char **psec, size_t *pseclen, + const EC_POINT *pub_key, const EC_KEY *ecdh) +{ + int (*ckey)(unsigned char **psec, + size_t *pseclen, + const EC_POINT *pub_key, + const EC_KEY *ecdh); + EC_KEY_METHOD_get_compute_key(ecdsa_default, + &ckey); + return ckey(psec, pseclen, pub_key, ecdh); +} + +int +ecdsae_sign(int type, const unsigned char *dgst, int dlen, unsigned char *sig, + unsigned int *siglen, const BIGNUM *kinv, const BIGNUM *r, EC_KEY *eckey) +{ + int (*sign)(int type, const unsigned char *dgst, + int dlen, unsigned char *sig, + unsigned int *siglen, + const BIGNUM *kinv, const BIGNUM *r, + EC_KEY *eckey); + + log_debug("debug: %s: %s", proc_name(smtpd_process), __func__); + EC_KEY_METHOD_get_sign(ecdsa_default, + &sign, + NULL, + NULL); + return (sign(type, dgst, dlen, sig, siglen, kinv, r, eckey)); +} + + +ECDSA_SIG * +ecdsae_do_sign(const unsigned char *dgst, int dgst_len, + const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey) +{ + ECDSA_SIG *(*psign_sig)(const unsigned char *dgst, + int dgst_len, + const BIGNUM *in_kinv, + const BIGNUM *in_r, + EC_KEY *eckey); + + log_debug("debug: %s: %s", proc_name(smtpd_process), __func__); + if (EC_KEY_get_ex_data(eckey, 0) != NULL) + return (ecdsae_send_enc_imsg(dgst, dgst_len, inv, rp, eckey)); + EC_KEY_METHOD_get_sign(ecdsa_default, + NULL, + NULL, + &psign_sig); + return (psign_sig(dgst, dgst_len, inv, rp, eckey)); +} + +int +ecdsae_sign_setup(EC_KEY *eckey, BN_CTX *ctx, BIGNUM **kinv, + BIGNUM **r) +{ + int (*psign_setup)(EC_KEY *eckey, BN_CTX *ctx_in, + BIGNUM **kinvp, BIGNUM **rp); + + log_debug("debug: %s: %s", proc_name(smtpd_process), __func__); + EC_KEY_METHOD_get_sign(ecdsa_default, + NULL, + &psign_setup, + NULL); + return (psign_setup(eckey, ctx, kinv, r)); +} + +int +ecdsae_verify(int type, const unsigned char *dgst, int dgst_len, + const unsigned char *sigbuf, int sig_len, EC_KEY *eckey) +{ + int (*verify)(int type, const unsigned + char *dgst, int dgst_len, + const unsigned char *sigbuf, + int sig_len, EC_KEY *eckey); + + log_debug("debug: %s: %s", proc_name(smtpd_process), __func__); + EC_KEY_METHOD_get_verify(ecdsa_default, + &verify, + NULL); + return (verify(type, dgst, dgst_len, sigbuf, sig_len, eckey)); +} +int +ecdsae_do_verify(const unsigned char *dgst, int dgst_len, + const ECDSA_SIG *sig, EC_KEY *eckey) +{ + int (*pverify_sig)(const unsigned char *dgst, + int dgst_len, + const ECDSA_SIG *sig, + EC_KEY *eckey); + + log_debug("debug: %s: %s", proc_name(smtpd_process), __func__); + EC_KEY_METHOD_get_verify(ecdsa_default, + NULL, + &pverify_sig); + return (pverify_sig(dgst, dgst_len, sig, eckey)); +} +#endif static void rsa_engine_init(void) @@ -699,6 +824,7 @@ rsa_engine_init(void) fatalx("%s", errstr); } +#if defined(SUPPORT_ECDSA) static void ecdsa_engine_init(void) { @@ -752,6 +878,62 @@ ecdsa_engine_init(void) ssl_error(errstr); fatalx("%s", errstr); } +#else +static void +ecdsa_engine_init(void) +{ + ENGINE *e; + const char *errstr, *name; + + if ((ecdsae_method = EC_KEY_METHOD_new(NULL)) == NULL) { + errstr = "EC_KEY_new"; + goto fail; + } + + EC_KEY_METHOD_set_keygen(ecdsae_method, ecdsae_keygen); + EC_KEY_METHOD_set_compute_key(ecdsae_method, ecdsae_compute_key); + EC_KEY_METHOD_set_sign(ecdsae_method, ecdsae_sign, ecdsae_sign_setup, + ecdsae_do_sign); + EC_KEY_METHOD_set_verify(ecdsae_method, ecdsae_verify, ecdsae_do_verify); + + if ((e = ENGINE_get_default_EC()) == NULL) { + if ((e = ENGINE_new()) == NULL) { + errstr = "ENGINE_new"; + goto fail; + } + if (!ENGINE_set_name(e, "ECDSA privsep engine")) { + errstr = "ENGINE_set_name"; + goto fail; + } + if ((ecdsa_default = EC_KEY_get_default_method()) == NULL) { + errstr = "EC_KEY_get_default_method"; + goto fail; + } + } else if ((ecdsa_default = ENGINE_get_EC(e)) == NULL) { + errstr = "ENGINE_get_EC"; + goto fail; + } + + if ((name = ENGINE_get_name(e)) == NULL) + name = "unknown ECDSA engine"; + + log_debug("debug: %s: using %s", __func__, name); + + if (!ENGINE_set_EC(e, ecdsae_method)) { + errstr = "ENGINE_set_EC"; + goto fail; + } + if (!ENGINE_set_default_EC(e)) { + errstr = "ENGINE_set_default_EC"; + goto fail; + } + return; + + fail: + ssl_error(errstr); + fatalx("%s", errstr); +} +#endif void ca_engine_init(void) diff --git a/smtpd/cert.c b/usr.sbin/smtpd/cert.c index 05aff418..79b1df91 100644 --- a/smtpd/cert.c +++ b/usr.sbin/smtpd/cert.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/socket.h> #include <sys/tree.h> diff --git a/smtpd/compress_backend.c b/usr.sbin/smtpd/compress_backend.c index 516dd1ee..1b974662 100644 --- a/smtpd/compress_backend.c +++ b/usr.sbin/smtpd/compress_backend.c @@ -17,6 +17,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> diff --git a/smtpd/compress_gzip.c b/usr.sbin/smtpd/compress_gzip.c index e7421cec..dd60aeec 100644 --- a/smtpd/compress_gzip.c +++ b/usr.sbin/smtpd/compress_gzip.c @@ -17,6 +17,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> diff --git a/smtpd/config.c b/usr.sbin/smtpd/config.c index cc53272a..8fe983d6 100644 --- a/smtpd/config.c +++ b/usr.sbin/smtpd/config.c @@ -1,4 +1,4 @@ -/* $OpenBSD: config.c,v 1.50 2019/09/20 17:46:05 gilles Exp $ */ +/* $OpenBSD: config.c,v 1.51 2019/12/18 10:00:39 gilles Exp $ */ /* * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -89,7 +91,7 @@ config_default(void) conf->sc_ssl_dict = calloc(1, sizeof(*conf->sc_ssl_dict)); conf->sc_limits_dict = calloc(1, sizeof(*conf->sc_limits_dict)); conf->sc_mda_wrappers = calloc(1, sizeof(*conf->sc_mda_wrappers)); - conf->sc_processors_dict = calloc(1, sizeof(*conf->sc_processors_dict)); + conf->sc_filter_processes_dict = calloc(1, sizeof(*conf->sc_filter_processes_dict)); conf->sc_dispatcher_bounce = calloc(1, sizeof(*conf->sc_dispatcher_bounce)); conf->sc_filters_dict = calloc(1, sizeof(*conf->sc_filters_dict)); limits = calloc(1, sizeof(*limits)); @@ -103,7 +105,7 @@ config_default(void) conf->sc_ssl_dict == NULL || conf->sc_limits_dict == NULL || conf->sc_mda_wrappers == NULL || - conf->sc_processors_dict == NULL || + conf->sc_filter_processes_dict == NULL || conf->sc_dispatcher_bounce == NULL || conf->sc_filters_dict == NULL || limits == NULL) @@ -116,7 +118,7 @@ config_default(void) dict_init(conf->sc_ssl_dict); dict_init(conf->sc_tables_dict); dict_init(conf->sc_limits_dict); - dict_init(conf->sc_processors_dict); + dict_init(conf->sc_filter_processes_dict); limit_mta_set_defaults(limits); @@ -155,7 +157,7 @@ error: free(conf->sc_ssl_dict); free(conf->sc_limits_dict); free(conf->sc_mda_wrappers); - free(conf->sc_processors_dict); + free(conf->sc_filter_processes_dict); free(conf->sc_dispatcher_bounce); free(conf->sc_filters_dict); free(limits); @@ -203,7 +205,9 @@ set_localaddrs(struct smtpd *conf, struct table *localnames) case AF_INET: sain = (struct sockaddr_in *)&ss; *sain = *(struct sockaddr_in *)p->ifa_addr; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sain->sin_len = sizeof(struct sockaddr_in); +#endif table_add(t, ss_to_text(&ss), NULL); table_add(localnames, ss_to_text(&ss), NULL); (void)snprintf(buf, sizeof buf, "[%s]", ss_to_text(&ss)); @@ -213,7 +217,9 @@ set_localaddrs(struct smtpd *conf, struct table *localnames) case AF_INET6: sin6 = (struct sockaddr_in6 *)&ss; *sin6 = *(struct sockaddr_in6 *)p->ifa_addr; +#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN sin6->sin6_len = sizeof(struct sockaddr_in6); +#endif table_add(t, ss_to_text(&ss), NULL); table_add(localnames, ss_to_text(&ss), NULL); (void)snprintf(buf, sizeof buf, "[%s]", ss_to_text(&ss)); @@ -304,7 +310,8 @@ config_process(enum smtp_proc_type proc) fatal("fdlimit: getrlimit"); rl.rlim_cur = rl.rlim_max; if (setrlimit(RLIMIT_NOFILE, &rl) == -1) - fatal("fdlimit: setrlimit"); + if (errno != EINVAL) + fatal("fdlimit: setrlimit"); } void diff --git a/smtpd/control.c b/usr.sbin/smtpd/control.c index 6f9c9aca..dbb2840d 100644 --- a/smtpd/control.c +++ b/usr.sbin/smtpd/control.c @@ -1,4 +1,4 @@ -/* $OpenBSD: control.c,v 1.123 2018/05/31 21:06:12 gilles Exp $ */ +/* $OpenBSD: control.c,v 1.125 2020/09/23 19:11:50 martijn Exp $ */ /* * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> @@ -18,6 +18,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -29,6 +31,7 @@ #include <errno.h> #include <event.h> #include <fcntl.h> +#include <grp.h> /* needed for setgroups */ #include <imsg.h> #include <pwd.h> #include <signal.h> @@ -253,8 +256,10 @@ control(void) control_listen(); +#if HAVE_PLEDGE if (pledge("stdio unix recvfd sendfd", NULL) == -1) err(1, "pledge"); +#endif event_dispatch(); fatalx("exited event loop"); @@ -292,8 +297,13 @@ control_accept(int listenfd, short event, void *arg) uid_t euid; gid_t egid; +#if defined(HAVE_GETDTABLESIZE) && defined(HAVE_GETDTABLECOUNT) if (getdtablesize() - getdtablecount() < CONTROL_FD_RESERVE) goto pause; +#else + if (available_fds(CONTROL_FD_RESERVE)) + goto pause; +#endif len = sizeof(s_un); if ((connfd = accept(listenfd, (struct sockaddr *)&s_un, &len)) == -1) { @@ -366,8 +376,13 @@ control_close(struct ctl_conn *c) stat_backend->decrement("control.session", 1); +#if defined(HAVE_GETDTABLESIZE) && defined(HAVE_GETDTABLECOUNT) if (getdtablesize() - getdtablecount() < CONTROL_FD_RESERVE) return; +#else + if (available_fds(CONTROL_FD_RESERVE)) + return; +#endif if (!event_pending(&control_state.ev, EV_READ, NULL)) { log_warnx("warn: re-enabling ctl connections"); diff --git a/smtpd/crypto.c b/usr.sbin/smtpd/crypto.c index b6a7547c..20a422cd 100644 --- a/smtpd/crypto.c +++ b/usr.sbin/smtpd/crypto.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/stat.h> diff --git a/smtpd/dict.c b/usr.sbin/smtpd/dict.c index 91156d5a..e660f0a5 100644 --- a/smtpd/dict.c +++ b/usr.sbin/smtpd/dict.c @@ -17,6 +17,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/tree.h> diff --git a/smtpd/dict.h b/usr.sbin/smtpd/dict.h index c5d47e1a..c5d47e1a 100644 --- a/smtpd/dict.h +++ b/usr.sbin/smtpd/dict.h diff --git a/smtpd/dns.c b/usr.sbin/smtpd/dns.c index d5b66af9..a3107e89 100644 --- a/smtpd/dns.c +++ b/usr.sbin/smtpd/dns.c @@ -18,6 +18,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/socket.h> #include <sys/tree.h> @@ -27,10 +29,15 @@ #include <netinet/in.h> #include <arpa/inet.h> #include <arpa/nameser.h> +#ifdef HAVE_ARPA_NAMESER_COMPAT_H +#include <arpa/nameser_compat.h> +#endif #include <netdb.h> #include <asr.h> #include <event.h> +#include <netdb.h> +#include <resolv.h> #include <imsg.h> #include <stdio.h> #include <stdlib.h> @@ -41,6 +48,11 @@ #include "log.h" #include "unpack_dns.h" +/* On OpenBSD, this function is not needed because we don't free addrinfo */ +#if defined(NOOP_ASR_FREEADDRINFO) +#define asr_freeaddrinfo(x) do { } while(0); +#endif + struct dns_lookup { struct dns_session *session; char *host; @@ -218,7 +230,7 @@ dns_dispatch_host(struct asr_result *ar, void *arg) free(lookup->host); free(lookup); if (ar->ar_addrinfo) - freeaddrinfo(ar->ar_addrinfo); + asr_freeaddrinfo(ar->ar_addrinfo); if (ar->ar_gai_errno) s->error = ar->ar_gai_errno; diff --git a/smtpd/enqueue.c b/usr.sbin/smtpd/enqueue.c index d8943953..0ef694b5 100644 --- a/smtpd/enqueue.c +++ b/usr.sbin/smtpd/enqueue.c @@ -1,4 +1,4 @@ -/* $OpenBSD: enqueue.c,v 1.116 2019/07/02 09:36:20 martijn Exp $ */ +/* $OpenBSD: enqueue.c,v 1.118 2020/03/18 20:17:14 eric Exp $ */ /* * Copyright (c) 2005 Henning Brauer <henning@bulabula.org> @@ -18,6 +18,8 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/socket.h> @@ -134,32 +136,51 @@ struct { char buf[SMTP_LINELEN]; } pstate; +#define QP_TEST_WRAP(fp, buf, linelen, size) do { \ + if (((linelen) += (size)) + 1 > 76) { \ + fprintf((fp), "=\r\n"); \ + if (buf[0] == '.') \ + fprintf((fp), "."); \ + (linelen) = (size); \ + } \ +} while (0) + +/* RFC 2045 section 6.7 */ static void -qp_encoded_write(FILE *fp, char *buf, size_t len) +qp_encoded_write(FILE *fp, char *buf) { - while (len) { - if (*buf == '=') - fprintf(fp, "=3D"); - else if (*buf == ' ' || *buf == '\t') { - char *p = buf; - - while (*p != '\n') { - if (*p != ' ' && *p != '\t') - break; - p++; - } - if (*p == '\n') + size_t linelen = 0; + + for (;buf[0] != '\0' && buf[0] != '\n'; buf++) { + /* + * Point 3: Any TAB (HT) or SPACE characters on an encoded line + * MUST thus be followed on that line by a printable character. + * + * Ergo, only encode if the next character is EOL. + */ + if (buf[0] == ' ' || buf[0] == '\t') { + if (buf[1] == '\n') { + QP_TEST_WRAP(fp, buf, linelen, 3); fprintf(fp, "=%2X", *buf & 0xff); - else + } else { + QP_TEST_WRAP(fp, buf, linelen, 1); fprintf(fp, "%c", *buf & 0xff); - } - else if (!isprint((unsigned char)*buf) && *buf != '\n') + } + /* + * Point 1, with exclusion of point 2, skip EBCDIC NOTE. + * Do this after whitespace check, else they would match here. + */ + } else if (!((buf[0] >= 33 && buf[0] <= 60) || + (buf[0] >= 62 && buf[0] <= 126))) { + QP_TEST_WRAP(fp, buf, linelen, 3); fprintf(fp, "=%2X", *buf & 0xff); - else + /* Point 2: 33 through 60 inclusive, and 62 through 126 */ + } else { + QP_TEST_WRAP(fp, buf, linelen, 1); fprintf(fp, "%c", *buf); - buf++; - len--; + } } + fprintf(fp, "\r\n"); } int @@ -172,7 +193,6 @@ enqueue(int argc, char *argv[], FILE *ofp) size_t sz = 0, envid_sz = 0; ssize_t len; char *line; - int dotted; int inheaders = 1; int save_argc; char **save_argv; @@ -282,17 +302,20 @@ enqueue(int argc, char *argv[], FILE *ofp) /* If the server is not running, enqueue the message offline */ if (!srv_connected()) { +#if HAVE_PLEDGE if (pledge("stdio", NULL) == -1) err(1, "pledge"); - +#endif return (enqueue_offline(save_argc, save_argv, fp, ofp)); } if ((msg.fd = open_connection()) == -1) errx(EX_UNAVAILABLE, "server too busy"); +#if HAVE_PLEDGE if (pledge("stdio wpath cpath", NULL) == -1) err(1, "pledge"); +#endif fout = fdopen(msg.fd, "a+"); if (fout == NULL) @@ -307,7 +330,7 @@ enqueue(int argc, char *argv[], FILE *ofp) if (!get_responses(fout, 1)) goto fail; - if (!send_line(fout, verbose, "EHLO localhost\n")) + if (!send_line(fout, verbose, "EHLO localhost\r\n")) goto fail; if (!get_responses(fout, 1)) goto fail; @@ -315,7 +338,7 @@ enqueue(int argc, char *argv[], FILE *ofp) if (msg.dsn_envid != NULL) envid_sz = strlen(msg.dsn_envid); - if (!send_line(fout, verbose, "MAIL FROM:<%s> %s%s %s%s\n", + if (!send_line(fout, verbose, "MAIL FROM:<%s> %s%s %s%s\r\n", msg.from, msg.dsn_ret ? "RET=" : "", msg.dsn_ret ? msg.dsn_ret : "", @@ -326,7 +349,7 @@ enqueue(int argc, char *argv[], FILE *ofp) goto fail; for (i = 0; i < msg.rcpt_cnt; i++) { - if (!send_line(fout, verbose, "RCPT TO:<%s> %s%s\n", + if (!send_line(fout, verbose, "RCPT TO:<%s> %s%s\r\n", msg.rcpts[i], msg.dsn_notify ? "NOTIFY=" : "", msg.dsn_notify ? msg.dsn_notify : "")) @@ -335,41 +358,41 @@ enqueue(int argc, char *argv[], FILE *ofp) goto fail; } - if (!send_line(fout, verbose, "DATA\n")) + if (!send_line(fout, verbose, "DATA\r\n")) goto fail; if (!get_responses(fout, 1)) goto fail; /* add From */ - if (!msg.saw_from && !send_line(fout, 0, "From: %s%s<%s>\n", + if (!msg.saw_from && !send_line(fout, 0, "From: %s%s<%s>\r\n", msg.fromname ? msg.fromname : "", msg.fromname ? " " : "", msg.from)) goto fail; /* add Date */ - if (!msg.saw_date && !send_line(fout, 0, "Date: %s\n", + if (!msg.saw_date && !send_line(fout, 0, "Date: %s\r\n", time_to_text(timestamp))) goto fail; if (msg.need_linesplit) { /* we will always need to mime encode for long lines */ if (!msg.saw_mime_version && !send_line(fout, 0, - "MIME-Version: 1.0\n")) + "MIME-Version: 1.0\r\n")) goto fail; if (!msg.saw_content_type && !send_line(fout, 0, - "Content-Type: text/plain; charset=unknown-8bit\n")) + "Content-Type: text/plain; charset=unknown-8bit\r\n")) goto fail; if (!msg.saw_content_disposition && !send_line(fout, 0, - "Content-Disposition: inline\n")) + "Content-Disposition: inline\r\n")) goto fail; if (!msg.saw_content_transfer_encoding && !send_line(fout, 0, - "Content-Transfer-Encoding: quoted-printable\n")) + "Content-Transfer-Encoding: quoted-printable\r\n")) goto fail; } /* add separating newline */ if (msg.noheader) { - if (!send_line(fout, 0, "\n")) + if (!send_line(fout, 0, "\r\n")) goto fail; inheaders = 0; } @@ -385,12 +408,11 @@ enqueue(int argc, char *argv[], FILE *ofp) /* newlines have been normalized on first parsing */ if (buf[len-1] != '\n') errx(EX_SOFTWARE, "expect EOL"); + len--; - dotted = 0; if (buf[0] == '.') { if (fputc('.', fout) == EOF) goto fail; - dotted = 1; } line = buf; @@ -404,7 +426,7 @@ enqueue(int argc, char *argv[], FILE *ofp) if (msg.saw_content_transfer_encoding || msg.noheader || inheaders || !msg.need_linesplit) { - if (!send_line(fout, 0, "%.*s", (int)len, line)) + if (!send_line(fout, 0, "%.*s\r\n", (int)len, line)) goto fail; if (inheaders && buf[0] == '\n') inheaders = 0; @@ -412,28 +434,15 @@ enqueue(int argc, char *argv[], FILE *ofp) } /* we don't have a content transfer encoding, use our default */ - do { - if (len < LINESPLIT) { - qp_encoded_write(fout, line, len); - break; - } - else { - qp_encoded_write(fout, line, - LINESPLIT - 2 - dotted); - if (!send_line(fout, 0, "=\n")) - goto fail; - line += LINESPLIT - 2 - dotted; - len -= LINESPLIT - 2 - dotted; - } - } while (len); + qp_encoded_write(fout, line); } free(buf); - if (!send_line(fout, verbose, ".\n")) + if (!send_line(fout, verbose, ".\r\n")) goto fail; if (!get_responses(fout, 1)) goto fail; - if (!send_line(fout, verbose, "QUIT\n")) + if (!send_line(fout, verbose, "QUIT\r\n")) goto fail; if (!get_responses(fout, 1)) goto fail; @@ -552,12 +561,12 @@ build_from(char *fake_from, struct passwd *pw) apos, pw->pw_gecos, pw->pw_name, len - apos - 1, p + 1) == -1) - err(1, NULL); + err(1, "asprintf"); msg.fromname[apos] = toupper((unsigned char)msg.fromname[apos]); } else { if (asprintf(&msg.fromname, "%.*s", len, pw->pw_gecos) == -1) - err(1, NULL); + err(1, "asprintf"); } } } diff --git a/smtpd/envelope.c b/usr.sbin/smtpd/envelope.c index 01957dc5..35d98b79 100644 --- a/smtpd/envelope.c +++ b/usr.sbin/smtpd/envelope.c @@ -1,4 +1,4 @@ -/* $OpenBSD: envelope.c,v 1.46 2019/09/19 16:00:59 gilles Exp $ */ +/* $OpenBSD: envelope.c,v 1.47 2019/11/25 14:18:32 gilles Exp $ */ /* * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> @@ -17,6 +17,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -177,6 +179,7 @@ envelope_dump_buffer(const struct envelope *ep, char *dest, size_t len) envelope_ascii_dump(ep, &dest, &len, "smtpname"); envelope_ascii_dump(ep, &dest, &len, "helo"); envelope_ascii_dump(ep, &dest, &len, "hostname"); + envelope_ascii_dump(ep, &dest, &len, "username"); envelope_ascii_dump(ep, &dest, &len, "errorline"); envelope_ascii_dump(ep, &dest, &len, "sockaddr"); envelope_ascii_dump(ep, &dest, &len, "sender"); @@ -302,7 +305,9 @@ ascii_load_sockaddr(struct sockaddr_storage *ss, char *buf) return 0; ssin6.sin6_family = AF_INET6; memcpy(ss, &ssin6, sizeof(ssin6)); +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN ss->ss_len = sizeof(struct sockaddr_in6); +#endif } else if (buf[0] == '[' && buf[strlen(buf)-1] == ']') { buf[strlen(buf)-1] = '\0'; @@ -310,14 +315,18 @@ ascii_load_sockaddr(struct sockaddr_storage *ss, char *buf) return 0; ssin6.sin6_family = AF_INET6; memcpy(ss, &ssin6, sizeof(ssin6)); +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN ss->ss_len = sizeof(struct sockaddr_in6); +#endif } else { if (inet_pton(AF_INET, buf, &ssin.sin_addr) != 1) return 0; ssin.sin_family = AF_INET; memcpy(ss, &ssin, sizeof(ssin)); +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN ss->ss_len = sizeof(struct sockaddr_in); +#endif } return 1; } @@ -400,6 +409,9 @@ ascii_load_field(const char *field, struct envelope *ep, char *buf) if (strcasecmp("dest", field) == 0) return ascii_load_mailaddr(&ep->dest, buf); + if (strcasecmp("username", field) == 0) + return ascii_load_string(ep->username, buf, sizeof(ep->username)); + if (strcasecmp("errorline", field) == 0) return ascii_load_string(ep->errorline, buf, sizeof ep->errorline); @@ -646,6 +658,12 @@ ascii_dump_field(const char *field, const struct envelope *ep, if (strcasecmp(field, "dest") == 0) return ascii_dump_mailaddr(&ep->dest, buf, len); + if (strcasecmp(field, "username") == 0) { + if (ep->username[0]) + return ascii_dump_string(ep->username, buf, len); + return 1; + } + if (strcasecmp(field, "errorline") == 0) return ascii_dump_string(ep->errorline, buf, len); diff --git a/smtpd/esc.c b/usr.sbin/smtpd/esc.c index a07320ce..64a44c79 100644 --- a/smtpd/esc.c +++ b/usr.sbin/smtpd/esc.c @@ -16,12 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> +#include "includes.h" -#include <netinet/in.h> -#include <netdb.h> #include <stdio.h> #include <limits.h> diff --git a/smtpd/expand.c b/usr.sbin/smtpd/expand.c index 99b25d51..a4306fc0 100644 --- a/smtpd/expand.c +++ b/usr.sbin/smtpd/expand.c @@ -17,6 +17,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -29,6 +31,12 @@ #include <limits.h> #include <stdlib.h> #include <string.h> +#ifdef HAVE_UTIL_H +#include <util.h> +#endif +#ifdef HAVE_LIBUTIL_H +#include <libutil.h> +#endif #include "smtpd.h" #include "log.h" diff --git a/usr.sbin/smtpd/filter.c b/usr.sbin/smtpd/filter.c new file mode 100644 index 00000000..614486b7 --- /dev/null +++ b/usr.sbin/smtpd/filter.c @@ -0,0 +1,868 @@ +/* $OpenBSD: filter.c,v 1.25 2017/01/09 09:53:23 reyk Exp $ */ + +/* + * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org> + * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/tree.h> +#include <sys/socket.h> +#include <sys/wait.h> + +#include <netinet/in.h> + +#include <ctype.h> +#include <errno.h> +#include <event.h> +#include <imsg.h> +#include <inttypes.h> +#include <limits.h> +#include <resolv.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "smtpd.h" +#include "log.h" + +enum { + QUERY_READY, + QUERY_RUNNING, + QUERY_DONE +}; + + +struct filter_proc { + TAILQ_ENTRY(filter_proc) entry; + struct mproc mproc; + int hooks; + int flags; + int ready; +}; + +struct filter { + TAILQ_ENTRY(filter) entry; + struct filter_proc *proc; +}; +TAILQ_HEAD(filter_lst, filter); + +TAILQ_HEAD(filter_query_lst, filter_query); +struct filter_session { + uint64_t id; + int terminate; + struct filter_lst *filters; + struct filter *fcurr; + + int error; + struct io *iev; + size_t idatalen; + FILE *ofile; + + struct filter_query *eom; +}; + +struct filter_query { + uint64_t qid; + int type; + struct filter_session *session; + + int state; + struct filter *current; + + /* current data */ + union { + struct { + struct sockaddr_storage local; + struct sockaddr_storage remote; + char hostname[HOST_NAME_MAX+1]; + } connect; + char line[LINE_MAX]; + struct mailaddr maddr; + size_t datalen; + } u; + + /* current response */ + struct { + int status; + int code; + char *response; + } smtp; +}; + +static void filter_imsg(struct mproc *, struct imsg *); +static void filter_post_event(uint64_t, int, struct filter *, struct filter *); +static struct filter_query *filter_query(struct filter_session *, int); +static void filter_drain_query(struct filter_query *); +static void filter_run_query(struct filter *, struct filter_query *); +static void filter_end_query(struct filter_query *); +static void filter_set_sink(struct filter_session *, int); +static int filter_tx(struct filter_session *, int); +static void filter_tx_io(struct io *, int, void *); + +static TAILQ_HEAD(, filter_proc) procs; +struct dict chains; + +static const char * filter_session_to_text(struct filter_session *); +static const char * filter_query_to_text(struct filter_query *); +static const char * filter_to_text(struct filter *); +static const char * filter_proc_to_text(struct filter_proc *); +static const char * query_to_str(int); +static const char * event_to_str(int); +static const char * status_to_str(int); +static const char * filterimsg_to_str(int); + +struct tree sessions; +struct tree queries; + +static void +filter_add_arg(struct filter_conf *filter, char *arg) +{ + if (filter->argc == MAX_FILTER_ARGS) { + log_warnx("warn: filter \"%s\" is full", filter->name); + fatalx("exiting"); + } + filter->argv[filter->argc++] = arg; +} + +static void +filter_extend_chain(struct filter_lst *chain, const char *name) +{ + struct filter *n; + struct filter_lst *fchain; + struct filter_conf *fconf; + int i; + + fconf = dict_xget(&env->sc_filters, name); + if (fconf->chain) { + log_debug("filter: extending with \"%s\"", name); + for (i = 0; i < fconf->argc; i++) + filter_extend_chain(chain, fconf->argv[i]); + } + else { + log_debug("filter: adding filter \"%s\"", name); + n = xcalloc(1, sizeof(*n), "filter_extend_chain"); + fchain = dict_get(&chains, name); + n->proc = TAILQ_FIRST(fchain)->proc; + TAILQ_INSERT_TAIL(chain, n, entry); + } +} + +void +filter_postfork(void) +{ + static int prepare = 0; + struct filter_conf *filter; + void *iter; + struct filter_proc *proc; + struct filter_lst *fchain; + struct filter *f; + struct mproc *p; + int done, i; + + if (prepare) + return; + prepare = 1; + + TAILQ_INIT(&procs); + dict_init(&chains); + + log_debug("filter: building simple chains..."); + + /* create all filter proc and associated chains */ + iter = NULL; + while (dict_iter(&env->sc_filters, &iter, NULL, (void **)&filter)) { + if (filter->chain) + continue; + + log_debug("filter: building simple chain \"%s\"", filter->name); + proc = xcalloc(1, sizeof(*proc), "filter_postfork"); + p = &proc->mproc; + p->handler = filter_imsg; + p->proc = PROC_FILTER; + p->name = xstrdup(filter->name, "filter_postfork"); + p->data = proc; + if (tracing & TRACE_DEBUG) + filter_add_arg(filter, "-v"); + if (foreground_log) + filter_add_arg(filter, "-d"); + if (mproc_fork(p, filter->path, filter->argv) < 0) + fatalx("filter_postfork"); + + log_debug("filter: registering proc \"%s\"", filter->name); + f = xcalloc(1, sizeof(*f), "filter_postfork"); + f->proc = proc; + + TAILQ_INSERT_TAIL(&procs, proc, entry); + fchain = xcalloc(1, sizeof(*fchain), "filter_postfork"); + TAILQ_INIT(fchain); + TAILQ_INSERT_TAIL(fchain, f, entry); + dict_xset(&chains, filter->name, fchain); + filter->done = 1; + } + + log_debug("filter: building complex chains..."); + + /* resolve all chains */ + done = 0; + while (!done) { + done = 1; + iter = NULL; + while (dict_iter(&env->sc_filters, &iter, NULL, + (void **)&filter)) { + if (filter->done) + continue; + done = 0; + filter->done = 1; + for (i = 0; i < filter->argc; i++) { + if (!dict_get(&chains, filter->argv[i])) { + filter->done = 0; + break; + } + } + if (filter->done == 0) + continue; + fchain = xcalloc(1, sizeof(*fchain), "filter_postfork"); + TAILQ_INIT(fchain); + log_debug("filter: building chain \"%s\"...", + filter->name); + for (i = 0; i < filter->argc; i++) + filter_extend_chain(fchain, filter->argv[i]); + log_debug("filter: done building chain \"%s\"", + filter->name); + dict_xset(&chains, filter->name, fchain); + } + } + log_debug("filter: done building complex chains"); + + fchain = xcalloc(1, sizeof(*fchain), "filter_postfork"); + TAILQ_INIT(fchain); + dict_xset(&chains, "<no-filter>", fchain); +} + +void +filter_configure(void) +{ + static int init = 0; + struct filter_proc *p; + + if (init) + return; + init = 1; + + tree_init(&sessions); + tree_init(&queries); + + TAILQ_FOREACH(p, &procs, entry) { + m_create(&p->mproc, IMSG_FILTER_REGISTER, 0, 0, -1); + m_add_u32(&p->mproc, FILTER_API_VERSION); + m_add_string(&p->mproc, p->mproc.name); + m_close(&p->mproc); + mproc_enable(&p->mproc); + } + + if (TAILQ_FIRST(&procs) == NULL) + smtp_configure(); +} + +void +filter_event(uint64_t id, int event) +{ + struct filter_session *s; + + if (event == EVENT_DISCONNECT) + /* On disconnect, the session is virtualy dead */ + s = tree_xpop(&sessions, id); + else + s = tree_xget(&sessions, id); + + filter_post_event(id, event, TAILQ_FIRST(s->filters), NULL); + + if (event == EVENT_DISCONNECT) { + if (s->iev) + io_free(s->iev); + if (s->ofile) + fclose(s->ofile); + free(s); + } +} + +void +filter_connect(uint64_t id, const struct sockaddr *local, + const struct sockaddr *remote, const char *host, const char *filter) +{ + struct filter_session *s; + struct filter_query *q; + + s = xcalloc(1, sizeof(*s), "filter_event"); + s->id = id; + if (filter == NULL) + filter = "<no-filter>"; + s->filters = dict_xget(&chains, filter); + tree_xset(&sessions, s->id, s); + + filter_event(id, EVENT_CONNECT); + q = filter_query(s, QUERY_CONNECT); + + memmove(&q->u.connect.local, local, SA_LEN(local)); + memmove(&q->u.connect.remote, remote, SA_LEN(remote)); + strlcpy(q->u.connect.hostname, host, sizeof(q->u.connect.hostname)); + + q->smtp.status = FILTER_OK; + q->smtp.code = 0; + q->smtp.response = NULL; + + filter_drain_query(q); +} + +void +filter_mailaddr(uint64_t id, int type, const struct mailaddr *maddr) +{ + struct filter_session *s; + struct filter_query *q; + + s = tree_xget(&sessions, id); + q = filter_query(s, type); + + strlcpy(q->u.maddr.user, maddr->user, sizeof(q->u.maddr.user)); + strlcpy(q->u.maddr.domain, maddr->domain, sizeof(q->u.maddr.domain)); + + filter_drain_query(q); +} + +void +filter_line(uint64_t id, int type, const char *line) +{ + struct filter_session *s; + struct filter_query *q; + + s = tree_xget(&sessions, id); + q = filter_query(s, type); + + if (line) + strlcpy(q->u.line, line, sizeof(q->u.line)); + + filter_drain_query(q); +} + +void +filter_eom(uint64_t id, int type, size_t datalen) +{ + struct filter_session *s; + struct filter_query *q; + + s = tree_xget(&sessions, id); + q = filter_query(s, type); + q->u.datalen = datalen; + + filter_drain_query(q); +} + +static void +filter_set_sink(struct filter_session *s, int sink) +{ + struct mproc *p; + + while (s->fcurr) { + if (s->fcurr->proc->hooks & HOOK_DATALINE) { + log_trace(TRACE_FILTERS, "filter: sending fd %d to %s", + sink, filter_to_text(s->fcurr)); + p = &s->fcurr->proc->mproc; + m_create(p, IMSG_FILTER_PIPE, 0, 0, sink); + m_add_id(p, s->id); + m_close(p); + return; + } + s->fcurr = TAILQ_PREV(s->fcurr, filter_lst, entry); + } + + log_trace(TRACE_FILTERS, "filter: chain input is %d", sink); + smtp_filter_fd(s->id, sink); +} + +void +filter_build_fd_chain(uint64_t id, int sink) +{ + struct filter_session *s; + int fd; + + s = tree_xget(&sessions, id); + s->fcurr = TAILQ_LAST(s->filters, filter_lst); + + fd = filter_tx(s, sink); + filter_set_sink(s, fd); +} + +void +filter_post_event(uint64_t id, int event, struct filter *f, struct filter *end) +{ + for(; f && f != end; f = TAILQ_NEXT(f, entry)) { + log_trace(TRACE_FILTERS, "filter: post-event event=%s filter=%s", + event_to_str(event), f->proc->mproc.name); + + m_create(&f->proc->mproc, IMSG_FILTER_EVENT, 0, 0, -1); + m_add_id(&f->proc->mproc, id); + m_add_int(&f->proc->mproc, event); + m_close(&f->proc->mproc); + } +} + +static struct filter_query * +filter_query(struct filter_session *s, int type) +{ + struct filter_query *q; + + q = xcalloc(1, sizeof(*q), "filter_query"); + q->qid = generate_uid(); + q->session = s; + q->type = type; + + q->state = QUERY_READY; + q->current = TAILQ_FIRST(s->filters); + + log_trace(TRACE_FILTERS, "filter: new query %s", query_to_str(type)); + + return (q); +} + +static void +filter_drain_query(struct filter_query *q) +{ + log_trace(TRACE_FILTERS, "filter: filter_drain_query %s", + filter_query_to_text(q)); + + /* + * The query must be passed through all filters that registered + * a hook, until one rejects it. + */ + while (q->state != QUERY_DONE) { + /* Walk over all filters */ + while (q->current) { + filter_run_query(q->current, q); + if (q->state == QUERY_RUNNING) { + log_trace(TRACE_FILTERS, + "filter: waiting for running query %s", + filter_query_to_text(q)); + return; + } + } + q->state = QUERY_DONE; + } + + /* Defer the response if the file is not closed yet. */ + if (q->type == QUERY_EOM && q->session->ofile && q->smtp.status == FILTER_OK) { + log_debug("filter: deferring eom query..."); + q->session->eom = q; + return; + } + + filter_end_query(q); +} + +static void +filter_run_query(struct filter *f, struct filter_query *q) +{ + log_trace(TRACE_FILTERS, + "filter: running filter %s for query %s", + filter_to_text(f), filter_query_to_text(q)); + + m_create(&f->proc->mproc, IMSG_FILTER_QUERY, 0, 0, -1); + m_add_id(&f->proc->mproc, q->session->id); + m_add_id(&f->proc->mproc, q->qid); + m_add_int(&f->proc->mproc, q->type); + + switch (q->type) { + case QUERY_CONNECT: + m_add_sockaddr(&f->proc->mproc, + (struct sockaddr *)&q->u.connect.local); + m_add_sockaddr(&f->proc->mproc, + (struct sockaddr *)&q->u.connect.remote); + m_add_string(&f->proc->mproc, q->u.connect.hostname); + break; + case QUERY_HELO: + m_add_string(&f->proc->mproc, q->u.line); + break; + case QUERY_MAIL: + case QUERY_RCPT: + m_add_mailaddr(&f->proc->mproc, &q->u.maddr); + break; + case QUERY_EOM: + m_add_u32(&f->proc->mproc, q->u.datalen); + break; + default: + break; + } + m_close(&f->proc->mproc); + + tree_xset(&queries, q->qid, q); + q->state = QUERY_RUNNING; +} + +static void +filter_end_query(struct filter_query *q) +{ + struct filter_session *s = q->session; + const char *response = q->smtp.response; + + log_trace(TRACE_FILTERS, "filter: filter_end_query %s", + filter_query_to_text(q)); + + if (q->type == QUERY_EOM && q->smtp.status == FILTER_OK) { + if (s->error || q->u.datalen != s->idatalen) { + response = "Internal error"; + q->smtp.code = 451; + q->smtp.status = FILTER_FAIL; + if (!s->error) + log_warnx("filter: datalen mismatch on session %" PRIx64 + ": %zu/%zu", s->id, s->idatalen, q->u.datalen); + } + } + + log_trace(TRACE_FILTERS, + "filter: query %016"PRIx64" done: " + "status=%s code=%d response=\"%s\"", + q->qid, + status_to_str(q->smtp.status), + q->smtp.code, + response); + + smtp_filter_response(s->id, q->type, q->smtp.status, q->smtp.code, + response); + free(q->smtp.response); + free(q); +} + +static void +filter_imsg(struct mproc *p, struct imsg *imsg) +{ + struct filter_proc *proc = p->data; + struct filter_session *s; + struct filter_query *q; + struct msg m; + const char *line; + uint64_t qid; + uint32_t datalen; + int type, status, code; + + if (imsg == NULL) { + log_warnx("warn: filter \"%s\" closed unexpectedly", p->name); + fatalx("exiting"); + } + + log_trace(TRACE_FILTERS, "filter: imsg %s from procfilter %s", + filterimsg_to_str(imsg->hdr.type), + filter_proc_to_text(proc)); + + switch (imsg->hdr.type) { + + case IMSG_FILTER_REGISTER: + if (proc->ready) { + log_warnx("warn: filter \"%s\" already registered", + proc->mproc.name); + exit(1); + } + + m_msg(&m, imsg); + m_get_int(&m, &proc->hooks); + m_get_int(&m, &proc->flags); + m_end(&m); + proc->ready = 1; + + log_debug("debug: filter \"%s\": hooks 0x%08x flags 0x%04x", + proc->mproc.name, proc->hooks, proc->flags); + + TAILQ_FOREACH(proc, &procs, entry) + if (!proc->ready) + return; + + smtp_configure(); + break; + + case IMSG_FILTER_RESPONSE: + m_msg(&m, imsg); + m_get_id(&m, &qid); + m_get_int(&m, &type); + if (type == QUERY_EOM) + m_get_u32(&m, &datalen); + m_get_int(&m, &status); + m_get_int(&m, &code); + if (m_is_eom(&m)) + line = NULL; + else + m_get_string(&m, &line); + m_end(&m); + + q = tree_xpop(&queries, qid); + if (q->type != type) { + log_warnx("warn: filter: type mismatch %d != %d", + q->type, type); + fatalx("exiting"); + } + q->smtp.status = status; + if (code) + q->smtp.code = code; + if (line) { + free(q->smtp.response); + q->smtp.response = xstrdup(line, "filter_imsg"); + } + q->state = (status == FILTER_OK) ? QUERY_READY : QUERY_DONE; + if (type == QUERY_EOM) + q->u.datalen = datalen; + + q->current = TAILQ_NEXT(q->current, entry); + filter_drain_query(q); + break; + + case IMSG_FILTER_PIPE: + m_msg(&m, imsg); + m_get_id(&m, &qid); + m_end(&m); + + s = tree_xget(&sessions, qid); + s->fcurr = TAILQ_PREV(s->fcurr, filter_lst, entry); + filter_set_sink(s, imsg->fd); + break; + + default: + log_warnx("warn: bad imsg from filter %s", p->name); + exit(1); + } +} + +static int +filter_tx(struct filter_session *s, int sink) +{ + int sp[2]; + + s->idatalen = 0; + s->eom = NULL; + s->error = 0; + + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) { + log_warn("warn: filter: socketpair"); + return (-1); + } + + if ((s->ofile = fdopen(sink, "w")) == NULL) { + log_warn("warn: filter: fdopen"); + close(sp[0]); + close(sp[1]); + return (-1); + } + + io_set_nonblocking(sp[0]); + io_set_nonblocking(sp[1]); + + s->iev = io_new(); + io_set_callback(s->iev, filter_tx_io, s); + io_set_fd(s->iev, sp[0]); + io_set_read(s->iev); + + return (sp[1]); +} + +static void +filter_tx_io(struct io *io, int evt, void *arg) +{ + struct filter_session *s = arg; + size_t len, n; + char *data; + + log_trace(TRACE_FILTERS, "filter: filter_tx_io(%p, %s)", + s, io_strevent(evt)); + + switch (evt) { + case IO_DATAIN: + data = io_data(s->iev); + len = io_datalen(s->iev); + + log_trace(TRACE_FILTERS, + "filter: filter_tx_io: datain (%zu) for req %016"PRIx64"", + len, s->id); + + n = fwrite(data, 1, len, s->ofile); + if (n != len) { + log_warnx("warn: filter_tx_io: fwrite %zu/%zu", n, len); + s->error = 1; + break; + } + s->idatalen += n; + io_drop(s->iev, n); + return; + + case IO_DISCONNECTED: + log_trace(TRACE_FILTERS, + "debug: filter: tx done (%zu) for req %016"PRIx64, + s->idatalen, s->id); + break; + + default: + log_warn("warn: filter_tx_io: bad evt (%d) for req %016"PRIx64, + evt, s->id); + s->error = 1; + break; + } + + io_free(s->iev); + s->iev = NULL; + fclose(s->ofile); + s->ofile = NULL; + + /* deferred eom request */ + if (s->eom) { + log_debug("filter: running eom query..."); + filter_end_query(s->eom); + } else { + log_debug("filter: eom not received yet"); + } +} + +static const char * +filter_query_to_text(struct filter_query *q) +{ + static char buf[1024]; + char tmp[1024]; + + tmp[0] = '\0'; + + switch (q->type) { + case QUERY_CONNECT: + strlcat(tmp, "=", sizeof tmp); + strlcat(tmp, ss_to_text(&q->u.connect.local), + sizeof tmp); + strlcat(tmp, " <-> ", sizeof tmp); + strlcat(tmp, ss_to_text(&q->u.connect.remote), + sizeof tmp); + strlcat(tmp, "(", sizeof tmp); + strlcat(tmp, q->u.connect.hostname, sizeof tmp); + strlcat(tmp, ")", sizeof tmp); + break; + case QUERY_MAIL: + case QUERY_RCPT: + snprintf(tmp, sizeof tmp, "=%s@%s", + q->u.maddr.user, q->u.maddr.domain); + break; + case QUERY_HELO: + snprintf(tmp, sizeof tmp, "=%s", q->u.line); + break; + default: + break; + } + snprintf(buf, sizeof buf, "%016"PRIx64"[%s%s,%s]", + q->qid, query_to_str(q->type), tmp, + filter_session_to_text(q->session)); + + return (buf); +} + +static const char * +filter_session_to_text(struct filter_session *s) +{ + static char buf[1024]; + + if (s == NULL) + return "filter_session@NULL"; + + snprintf(buf, sizeof(buf), + "filter_session@%p[datalen=%zu,eom=%p,ofile=%p]", + s, s->idatalen, s->eom, s->ofile); + + return buf; +} + +static const char * +filter_to_text(struct filter *f) +{ + static char buf[1024]; + + snprintf(buf, sizeof buf, "filter:%s", filter_proc_to_text(f->proc)); + + return (buf); +} + +static const char * +filter_proc_to_text(struct filter_proc *proc) +{ + static char buf[1024]; + + snprintf(buf, sizeof buf, "%s[hooks=0x%08x,flags=0x%04x]", + proc->mproc.name, proc->hooks, proc->flags); + + return (buf); +} + +#define CASE(x) case x : return #x + +static const char * +filterimsg_to_str(int imsg) +{ + switch (imsg) { + CASE(IMSG_FILTER_REGISTER); + CASE(IMSG_FILTER_EVENT); + CASE(IMSG_FILTER_QUERY); + CASE(IMSG_FILTER_PIPE); + CASE(IMSG_FILTER_RESPONSE); + default: + return "IMSG_FILTER_???"; + } +} + +static const char * +query_to_str(int query) +{ + switch (query) { + CASE(QUERY_CONNECT); + CASE(QUERY_HELO); + CASE(QUERY_MAIL); + CASE(QUERY_RCPT); + CASE(QUERY_DATA); + CASE(QUERY_EOM); + CASE(QUERY_DATALINE); + default: + return "QUERY_???"; + } +} + +static const char * +event_to_str(int event) +{ + switch (event) { + CASE(EVENT_CONNECT); + CASE(EVENT_RESET); + CASE(EVENT_DISCONNECT); + CASE(EVENT_TX_BEGIN); + CASE(EVENT_TX_COMMIT); + CASE(EVENT_TX_ROLLBACK); + default: + return "EVENT_???"; + } +} + +static const char * +status_to_str(int status) +{ + switch (status) { + CASE(FILTER_OK); + CASE(FILTER_FAIL); + CASE(FILTER_CLOSE); + default: + return "FILTER_???"; + } +} diff --git a/smtpd/forward.5 b/usr.sbin/smtpd/forward.5 index 5a68f229..5a68f229 100644 --- a/smtpd/forward.5 +++ b/usr.sbin/smtpd/forward.5 diff --git a/smtpd/forward.c b/usr.sbin/smtpd/forward.c index d30d55e9..7494c6ce 100644 --- a/smtpd/forward.c +++ b/usr.sbin/smtpd/forward.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -28,7 +30,12 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#ifdef HAVE_UTIL_H #include <util.h> +#endif +#ifdef HAVE_LIBUTIL_H +#include <libutil.h> +#endif #include <unistd.h> #include <limits.h> diff --git a/smtpd/iobuf.c b/usr.sbin/smtpd/iobuf.c index f5d8b20a..dec10660 100644 --- a/smtpd/iobuf.c +++ b/usr.sbin/smtpd/iobuf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: iobuf.c,v 1.12 2019/10/03 07:03:23 gilles Exp $ */ +/* $OpenBSD: iobuf.c,v 1.13 2020/04/24 11:34:07 eric Exp $ */ /* * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> * @@ -15,6 +15,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/socket.h> #include <sys/uio.h> @@ -174,10 +176,9 @@ iobuf_getline(struct iobuf *iobuf, size_t *rlen) * the next call to iobuf_normalize() or iobuf_extend(). */ iobuf_drop(iobuf, i + 1); - len = (i && buf[i - 1] == '\r') ? i - 1 : i; - buf[len] = '\0'; + buf[i] = '\0'; if (rlen) - *rlen = len; + *rlen = i; return (buf); } diff --git a/smtpd/iobuf.h b/usr.sbin/smtpd/iobuf.h index c454d0a1..c454d0a1 100644 --- a/smtpd/iobuf.h +++ b/usr.sbin/smtpd/iobuf.h diff --git a/smtpd/ioev.c b/usr.sbin/smtpd/ioev.c index d36210fd..e0a8a096 100644 --- a/smtpd/ioev.c +++ b/usr.sbin/smtpd/ioev.c @@ -15,6 +15,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/socket.h> @@ -749,10 +751,10 @@ io_connect(struct io *io, const struct sockaddr *sa, const struct sockaddr *bsa) io_set_nonblocking(sock); io_set_nolinger(sock); - if (bsa && bind(sock, bsa, bsa->sa_len) == -1) + if (bsa && bind(sock, bsa, SA_LEN(bsa)) == -1) goto fail; - if (connect(sock, sa, sa->sa_len) == -1) + if (connect(sock, sa, SA_LEN(sa)) == -1) if (errno != EINPROGRESS) goto fail; diff --git a/smtpd/ioev.h b/usr.sbin/smtpd/ioev.h index f155a7fc..f155a7fc 100644 --- a/smtpd/ioev.h +++ b/usr.sbin/smtpd/ioev.h diff --git a/usr.sbin/smtpd/libressl.c b/usr.sbin/smtpd/libressl.c new file mode 100644 index 00000000..57d74389 --- /dev/null +++ b/usr.sbin/smtpd/libressl.c @@ -0,0 +1,213 @@ +/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) + * All rights reserved. + * + * This package is an SSL implementation written + * by Eric Young (eay@cryptsoft.com). + * The implementation was written so as to conform with Netscapes SSL. + * + * This library is free for commercial and non-commercial use as long as + * the following conditions are aheared to. The following conditions + * apply to all code found in this distribution, be it the RC4, RSA, + * lhash, DES, etc., code; not just the SSL code. The SSL documentation + * included with this distribution is covered by the same copyright terms + * except that the holder is Tim Hudson (tjh@cryptsoft.com). + * + * Copyright remains Eric Young's, and as such any Copyright notices in + * the code are not to be removed. + * If this package is used in a product, Eric Young should be given attribution + * as the author of the parts of the library used. + * This can be in the form of a textual message at program startup or + * in documentation (online or textual) provided with the package. + * + * 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 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * "This product includes cryptographic software written by + * Eric Young (eay@cryptsoft.com)" + * The word 'cryptographic' can be left out if the rouines from the library + * being used are not cryptographic related :-). + * 4. If you include any Windows specific code (or a derivative thereof) from + * the apps directory (application code) you must include an acknowledgement: + * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" + * + * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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. + * + * The licence and distribution terms for any publically available version or + * derivative of this code cannot be changed. i.e. this code cannot simply be + * copied and put under another distribution licence + * [including the GNU Public Licence.] + */ + +/* + * SSL operations needed when running in a privilege separated environment. + * Adapted from openssl's ssl_rsa.c by Pierre-Yves Ritschard . + */ + +#include "includes.h" + +#include <sys/types.h> + +#include <limits.h> +#include <unistd.h> +#include <stdio.h> + +#include <openssl/err.h> +#include <openssl/bio.h> +#include <openssl/objects.h> +#include <openssl/evp.h> +#include <openssl/x509.h> +#include <openssl/pem.h> +#include <openssl/ssl.h> + +#include "log.h" +#include "ssl.h" + +#define SSL_ECDH_CURVE "prime256v1" + +/* + * Read a bio that contains our certificate in "PEM" format, + * possibly followed by a sequence of CA certificates that should be + * sent to the peer in the Certificate message. + */ +static int +ssl_ctx_use_certificate_chain_bio(SSL_CTX *ctx, BIO *in) +{ + int ret = 0; + X509 *x = NULL; + + ERR_clear_error(); /* clear error stack for SSL_CTX_use_certificate() */ + + x = PEM_read_bio_X509_AUX(in, NULL, ctx->default_passwd_callback, + ctx->default_passwd_callback_userdata); + if (x == NULL) { + SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_PEM_LIB); + goto end; + } + + ret = SSL_CTX_use_certificate(ctx, x); + + if (ERR_peek_error() != 0) + ret = 0; + /* Key/certificate mismatch doesn't imply ret==0 ... */ + if (ret) { + /* + * If we could set up our certificate, now proceed to + * the CA certificates. + */ + X509 *ca; + int r; + unsigned long err; + + if (ctx->extra_certs != NULL) { + sk_X509_pop_free(ctx->extra_certs, X509_free); + ctx->extra_certs = NULL; + } + + while ((ca = PEM_read_bio_X509(in, NULL, + ctx->default_passwd_callback, + ctx->default_passwd_callback_userdata)) != NULL) { + r = SSL_CTX_add_extra_chain_cert(ctx, ca); + if (!r) { + X509_free(ca); + ret = 0; + goto end; + } + /* + * Note that we must not free r if it was successfully + * added to the chain (while we must free the main + * certificate, since its reference count is increased + * by SSL_CTX_use_certificate). + */ + } + + /* When the while loop ends, it's usually just EOF. */ + err = ERR_peek_last_error(); + if (ERR_GET_LIB(err) == ERR_LIB_PEM && + ERR_GET_REASON(err) == PEM_R_NO_START_LINE) + ERR_clear_error(); + else + ret = 0; /* some real error */ + } + +end: + if (x != NULL) + X509_free(x); + return (ret); +} + +int +SSL_CTX_use_certificate_chain_mem(SSL_CTX *ctx, void *buf, int len) +{ + BIO *in; + int ret = 0; + + in = BIO_new_mem_buf(buf, len); + if (in == NULL) { + SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_CHAIN_FILE, ERR_R_BUF_LIB); + goto end; + } + + ret = ssl_ctx_use_certificate_chain_bio(ctx, in); + +end: + BIO_free(in); + return (ret); +} + +#ifndef HAVE_SSL_CTX_SET_ECDH_AUTO +void +SSL_CTX_set_ecdh_auto(SSL_CTX *ctx, int enable) +{ + int nid; + EC_KEY *ecdh; + + if (!enable) + return; + + if ((nid = OBJ_sn2nid(SSL_ECDH_CURVE)) == 0) { + ssl_error("ssl_set_ecdh_auto"); + fatal("ssl_set_ecdh_auto: unknown curve name " + SSL_ECDH_CURVE); + } + + if ((ecdh = EC_KEY_new_by_curve_name(nid)) == NULL) { + ssl_error("ssl_set_ecdh_auto"); + fatal("ssl_set_ecdh_auto: unable to create curve " + SSL_ECDH_CURVE); + } + + SSL_CTX_set_tmp_ecdh(ctx, ecdh); + SSL_CTX_set_options(ctx, SSL_OP_SINGLE_ECDH_USE); + EC_KEY_free(ecdh); +} +#endif + +#ifndef HAVE_SSL_CTX_SET_DH_AUTO +void +SSL_CTX_set_dh_auto(SSL_CTX *ctx, int enable) +{ + if (!enable) + return; + + /* stub until OpenSSL catches up with this ... */ + log_warnx("OpenSSL does not support SSL_CTX_set_dh_auto (yet ?)"); + return; +} +#endif diff --git a/smtpd/limit.c b/usr.sbin/smtpd/limit.c index e7d0cb17..25e7a026 100644 --- a/smtpd/limit.c +++ b/usr.sbin/smtpd/limit.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> diff --git a/smtpd/lka.c b/usr.sbin/smtpd/lka.c index 0d4b43db..6ac21245 100644 --- a/smtpd/lka.c +++ b/usr.sbin/smtpd/lka.c @@ -1,4 +1,4 @@ -/* $OpenBSD: lka.c,v 1.240 2019/08/28 15:50:36 martijn Exp $ */ +/* $OpenBSD: lka.c,v 1.243 2019/12/21 10:23:37 gilles Exp $ */ /* * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -18,6 +18,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -31,6 +33,8 @@ #include <err.h> #include <errno.h> #include <event.h> +#include <netdb.h> +#include <grp.h> /* needed for setgroups */ #include <imsg.h> #include <openssl/err.h> #include <openssl/ssl.h> @@ -94,6 +98,7 @@ lka_imsg(struct mproc *p, struct imsg *imsg) int filter_phase; const char *filter_param; uint32_t msgid; + uint32_t subsystems; uint64_t evpid; size_t msgsz; int ok; @@ -274,6 +279,7 @@ lka_imsg(struct mproc *p, struct imsg *imsg) case IMSG_MTA_LOOKUP_SMARTHOST: m_msg(&m, imsg); m_get_id(&m, &reqid); + m_get_string(&m, &domain); m_get_string(&m, &tablename); m_end(&m); @@ -287,7 +293,11 @@ lka_imsg(struct mproc *p, struct imsg *imsg) m_add_int(p, LKA_TEMPFAIL); } else { - ret = table_fetch(table, K_RELAYHOST, &lk); + if (domain == NULL) + ret = table_fetch(table, K_RELAYHOST, &lk); + else + ret = table_lookup(table, K_RELAYHOST, domain, &lk); + if (ret == -1) m_add_int(p, LKA_TEMPFAIL); else if (ret == 0) @@ -310,10 +320,12 @@ lka_imsg(struct mproc *p, struct imsg *imsg) /* fork & exec tables that need it */ table_open_all(env); +#if HAVE_PLEDGE /* revoke proc & exec */ if (pledge("stdio rpath inet dns getpw recvfd sendfd", NULL) == -1) err(1, "pledge"); +#endif /* setup proc registering task */ evtimer_set(&ev_proc_ready, proc_timeout, &ev_proc_ready); @@ -362,13 +374,14 @@ lka_imsg(struct mproc *p, struct imsg *imsg) case IMSG_LKA_PROCESSOR_FORK: m_msg(&m, imsg); m_get_string(&m, &procname); + m_get_u32(&m, &subsystems); m_end(&m); m_create(p, IMSG_LKA_PROCESSOR_ERRFD, 0, 0, -1); m_add_string(p, procname); m_close(p); - lka_proc_forked(procname, imsg->fd); + lka_proc_forked(procname, subsystems, imsg->fd); return; case IMSG_LKA_PROCESSOR_ERRFD: @@ -605,13 +618,9 @@ lka_imsg(struct mproc *p, struct imsg *imsg) m_msg(&m, imsg); m_get_id(&m, &reqid); m_get_string(&m, &filter_name); - m_get_sockaddr(&m, (struct sockaddr *)&ss_src); - m_get_sockaddr(&m, (struct sockaddr *)&ss_dest); - m_get_string(&m, &rdns); - m_get_int(&m, &fcrdns); m_end(&m); - lka_filter_begin(reqid, filter_name, &ss_src, &ss_dest, rdns, fcrdns); + lka_filter_begin(reqid, filter_name); return; case IMSG_FILTER_SMTP_END: @@ -706,9 +715,11 @@ lka(void) lka_report_init(); lka_filter_init(); +#if HAVE_PLEDGE /* proc & exec will be revoked before serving requests */ if (pledge("stdio rpath inet dns getpw recvfd sendfd proc exec", NULL) == -1) err(1, "pledge"); +#endif event_dispatch(); fatalx("exited event loop"); diff --git a/smtpd/lka_filter.c b/usr.sbin/smtpd/lka_filter.c index 695ce4c3..7931ede4 100644 --- a/smtpd/lka_filter.c +++ b/usr.sbin/smtpd/lka_filter.c @@ -1,4 +1,4 @@ -/* $OpenBSD: lka_filter.c,v 1.50 2019/09/11 20:06:26 gilles Exp $ */ +/* $OpenBSD: lka_filter.c,v 1.63 2020/09/16 11:19:42 martijn Exp $ */ /* * Copyright (c) 2018 Gilles Chehade <gilles@poolp.org> @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -35,7 +37,7 @@ #include "smtpd.h" #include "log.h" -#define PROTOCOL_VERSION "0.4" +#define PROTOCOL_VERSION "0.6" struct filter; struct filter_session; @@ -54,6 +56,8 @@ static int filter_builtins_connect(struct filter_session *, struct filter *, uin static int filter_builtins_helo(struct filter_session *, struct filter *, uint64_t, const char *); static int filter_builtins_mail_from(struct filter_session *, struct filter *, uint64_t, const char *); static int filter_builtins_rcpt_to(struct filter_session *, struct filter *, uint64_t, const char *); +static int filter_builtins_data(struct filter_session *, struct filter *, uint64_t, const char *); +static int filter_builtins_commit(struct filter_session *, struct filter *, uint64_t, const char *); static void filter_result_proceed(uint64_t); static void filter_result_junk(uint64_t); @@ -78,6 +82,7 @@ struct filter_session { int fcrdns; char *helo; + char *username; char *mail_from; enum filter_phase phase; @@ -95,14 +100,14 @@ static struct filter_exec { { FILTER_AUTH, "auth", filter_builtins_notimpl }, { FILTER_MAIL_FROM, "mail-from", filter_builtins_mail_from }, { FILTER_RCPT_TO, "rcpt-to", filter_builtins_rcpt_to }, - { FILTER_DATA, "data", filter_builtins_notimpl }, + { FILTER_DATA, "data", filter_builtins_data }, { FILTER_DATA_LINE, "data-line", filter_builtins_notimpl }, { FILTER_RSET, "rset", filter_builtins_notimpl }, { FILTER_QUIT, "quit", filter_builtins_notimpl }, { FILTER_NOOP, "noop", filter_builtins_notimpl }, { FILTER_HELP, "help", filter_builtins_notimpl }, { FILTER_WIZ, "wiz", filter_builtins_notimpl }, - { FILTER_COMMIT, "commit", filter_builtins_notimpl }, + { FILTER_COMMIT, "commit", filter_builtins_commit }, }; struct filter { @@ -126,13 +131,210 @@ struct filter_chain { TAILQ_HEAD(, filter_entry) chain[nitems(filter_execs)]; }; -static struct dict smtp_in; +static struct dict filter_smtp_in; static struct tree sessions; -static int inited; +static int filters_inited; static struct dict filter_chains; +struct reporter_proc { + TAILQ_ENTRY(reporter_proc) entries; + const char *name; +}; +TAILQ_HEAD(reporters, reporter_proc); + +static struct dict report_smtp_in; +static struct dict report_smtp_out; + +static struct smtp_events { + const char *event; +} smtp_events[] = { + { "link-connect" }, + { "link-disconnect" }, + { "link-greeting" }, + { "link-identify" }, + { "link-tls" }, + { "link-auth" }, + + { "tx-reset" }, + { "tx-begin" }, + { "tx-mail" }, + { "tx-rcpt" }, + { "tx-envelope" }, + { "tx-data" }, + { "tx-commit" }, + { "tx-rollback" }, + + { "protocol-client" }, + { "protocol-server" }, + + { "filter-report" }, + { "filter-response" }, + + { "timeout" }, +}; + +static int processors_inited = 0; +static struct dict processors; + +struct processor_instance { + char *name; + struct io *io; + struct io *errfd; + int ready; + uint32_t subsystems; +}; + +static void processor_io(struct io *, int, void *); +static void processor_errfd(struct io *, int, void *); +void lka_filter_process_response(const char *, const char *); + +int +lka_proc_ready(void) +{ + void *iter; + struct processor_instance *pi; + + iter = NULL; + while (dict_iter(&processors, &iter, NULL, (void **)&pi)) + if (!pi->ready) + return 0; + return 1; +} + +static void +lka_proc_config(struct processor_instance *pi) +{ + io_printf(pi->io, "config|smtpd-version|%s\n", SMTPD_VERSION); + io_printf(pi->io, "config|smtp-session-timeout|%d\n", SMTPD_SESSION_TIMEOUT); + if (pi->subsystems & FILTER_SUBSYSTEM_SMTP_IN) + io_printf(pi->io, "config|subsystem|smtp-in\n"); + if (pi->subsystems & FILTER_SUBSYSTEM_SMTP_OUT) + io_printf(pi->io, "config|subsystem|smtp-out\n"); + io_printf(pi->io, "config|admd|%s\n", + env->sc_admd != NULL ? env->sc_admd : env->sc_hostname); + io_printf(pi->io, "config|ready\n"); +} + +void +lka_proc_forked(const char *name, uint32_t subsystems, int fd) +{ + struct processor_instance *processor; + + if (!processors_inited) { + dict_init(&processors); + processors_inited = 1; + } + + processor = xcalloc(1, sizeof *processor); + processor->name = xstrdup(name); + processor->io = io_new(); + processor->subsystems = subsystems; + + io_set_nonblocking(fd); + + io_set_fd(processor->io, fd); + io_set_callback(processor->io, processor_io, processor->name); + dict_xset(&processors, name, processor); +} + +void +lka_proc_errfd(const char *name, int fd) +{ + struct processor_instance *processor; + + processor = dict_xget(&processors, name); + + io_set_nonblocking(fd); + + processor->errfd = io_new(); + io_set_fd(processor->errfd, fd); + io_set_callback(processor->errfd, processor_errfd, processor->name); + + lka_proc_config(processor); +} + +struct io * +lka_proc_get_io(const char *name) +{ + struct processor_instance *processor; + + processor = dict_xget(&processors, name); + + return processor->io; +} + +static void +processor_register(const char *name, const char *line) +{ + struct processor_instance *processor; + + processor = dict_xget(&processors, name); + + if (strcmp(line, "register|ready") == 0) { + processor->ready = 1; + return; + } + + if (strncmp(line, "register|report|", 16) == 0) { + lka_report_register_hook(name, line+16); + return; + } + + if (strncmp(line, "register|filter|", 16) == 0) { + lka_filter_register_hook(name, line+16); + return; + } + + fatalx("Invalid register line received: %s", line); +} + +static void +processor_io(struct io *io, int evt, void *arg) +{ + struct processor_instance *processor; + const char *name = arg; + char *line = NULL; + ssize_t len; + + switch (evt) { + case IO_DATAIN: + while ((line = io_getline(io, &len)) != NULL) { + if (strncmp("register|", line, 9) == 0) { + processor_register(name, line); + continue; + } + + processor = dict_xget(&processors, name); + if (!processor->ready) + fatalx("Non-register message before register|" + "ready: %s", line); + else if (strncmp(line, "filter-result|", 14) == 0 || + strncmp(line, "filter-dataline|", 16) == 0) + lka_filter_process_response(name, line); + else if (strncmp(line, "report|", 7) == 0) + lka_report_proc(name, line); + else + fatalx("Invalid filter message type: %s", line); + } + } +} + +static void +processor_errfd(struct io *io, int evt, void *arg) +{ + const char *name = arg; + char *line = NULL; + ssize_t len; + + switch (evt) { + case IO_DATAIN: + while ((line = io_getline(io, &len)) != NULL) + log_warnx("%s: %s", name, line); + } +} + void lka_filter_init(void) { @@ -215,7 +417,7 @@ lka_filter_register_hook(const char *name, const char *hook) size_t i; if (strncasecmp(hook, "smtp-in|", 8) == 0) { - subsystem = &smtp_in; + subsystem = &filter_smtp_in; hook += 8; } else @@ -292,7 +494,7 @@ lka_filter_proc_in_session(uint64_t reqid, const char *proc) return 0; filter = dict_get(&filters, fs->filter_name); - if (filter->proc == NULL && filter->chain == NULL) + if (filter == NULL || (filter->proc == NULL && filter->chain == NULL)) return 0; if (filter->proc) @@ -307,27 +509,18 @@ lka_filter_proc_in_session(uint64_t reqid, const char *proc) } void -lka_filter_begin(uint64_t reqid, - const char *filter_name, - const struct sockaddr_storage *ss_src, - const struct sockaddr_storage *ss_dest, - const char *rdns, - int fcrdns) +lka_filter_begin(uint64_t reqid, const char *filter_name) { struct filter_session *fs; - if (!inited) { + if (!filters_inited) { tree_init(&sessions); - inited = 1; + filters_inited = 1; } fs = xcalloc(1, sizeof (struct filter_session)); fs->id = reqid; fs->filter_name = xstrdup(filter_name); - fs->ss_src = *ss_src; - fs->ss_dest = *ss_dest; - fs->rdns = xstrdup(rdns); - fs->fcrdns = fcrdns; tree_xset(&sessions, fs->id, fs); log_trace(TRACE_FILTERS, "%016"PRIx64" filters session-begin", reqid); @@ -342,6 +535,7 @@ lka_filter_end(uint64_t reqid) free(fs->rdns); free(fs->helo); free(fs->mail_from); + free(fs->username); free(fs->lastparam); free(fs); log_trace(TRACE_FILTERS, "%016"PRIx64" filters session-end", reqid); @@ -441,22 +635,22 @@ lka_filter_process_response(const char *name, const char *line) fatalx("Missing reqid: %s", line); ep[0] = '\0'; - token = strtoull(qid, &ep, 16); + reqid = strtoull(qid, &ep, 16); if (qid[0] == '\0' || *ep != '\0') - fatalx("Invalid token: %s", line); - if (errno == ERANGE && token == ULLONG_MAX) - fatal("Invalid token: %s", line); + fatalx("Invalid reqid: %s", line); + if (errno == ERANGE && reqid == ULLONG_MAX) + fatal("Invalid reqid: %s", line); qid = ep+1; if ((ep = strchr(qid, '|')) == NULL) fatal("Missing directive: %s", line); ep[0] = '\0'; - reqid = strtoull(qid, &ep, 16); + token = strtoull(qid, &ep, 16); if (qid[0] == '\0' || *ep != '\0') - fatalx("Invalid reqid: %s", line); - if (errno == ERANGE && reqid == ULLONG_MAX) - fatal("Invalid reqid: %s", line); + fatalx("Invalid token: %s", line); + if (errno == ERANGE && token == ULLONG_MAX) + fatal("Invalid token: %s", line); response = ep+1; @@ -604,6 +798,14 @@ filter_protocol_internal(struct filter_session *fs, uint64_t *token, uint64_t re gettimeofday(&tv, NULL); lka_report_filter_report(fs->id, filter->name, 1, "smtp-in", &tv, filter->config->report); + } else if (filter->config->bypass) { + log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, " + "resume=%s, action=bypass, filter=%s, query=%s", + fs->id, phase_name, resume ? "y" : "n", + filter->name, + param); + filter_result_proceed(reqid); + return; } else { log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, " "resume=%s, action=reject, filter=%s, query=%s, response=%s", @@ -653,7 +855,7 @@ filter_data_internal(struct filter_session *fs, uint64_t token, uint64_t reqid, /* no filter_entry, we either had none or reached end of chain */ if (filter_entry == NULL) { - io_printf(fs->io, "%s\r\n", line); + io_printf(fs->io, "%s\n", line); return; } @@ -690,7 +892,6 @@ filter_protocol(uint64_t reqid, enum filter_phase phase, const char *param) param = nparam; break; case FILTER_STARTTLS: - case FILTER_AUTH: /* TBD */ break; default: @@ -753,13 +954,13 @@ filter_protocol_query(struct filter *filter, uint64_t token, uint64_t reqid, con n = io_printf(lka_proc_get_io(filter->proc), "filter|%s|%lld.%06ld|smtp-in|%s|%016"PRIx64"|%016"PRIx64"|%s|%s\n", PROTOCOL_VERSION, - tv.tv_sec, tv.tv_usec, + (long long int)tv.tv_sec, tv.tv_usec, phase, reqid, token, fs->rdns, param); else n = io_printf(lka_proc_get_io(filter->proc), "filter|%s|%lld.%06ld|smtp-in|%s|%016"PRIx64"|%016"PRIx64"|%s\n", PROTOCOL_VERSION, - tv.tv_sec, tv.tv_usec, + (long long int)tv.tv_sec, tv.tv_usec, phase, reqid, token, param); if (n == -1) fatalx("failed to write to processor"); @@ -777,7 +978,7 @@ filter_data_query(struct filter *filter, uint64_t token, uint64_t reqid, const c "filter|%s|%lld.%06ld|smtp-in|data-line|" "%016"PRIx64"|%016"PRIx64"|%s\n", PROTOCOL_VERSION, - tv.tv_sec, tv.tv_usec, + (long long int)tv.tv_sec, tv.tv_usec, reqid, token, line); if (n == -1) fatalx("failed to write to processor"); @@ -914,6 +1115,47 @@ filter_check_helo_regex(struct filter *filter, const char *key) } static int +filter_check_auth(struct filter *filter, const char *username) +{ + int ret = 0; + + if (!filter->config->auth) + return 0; + + ret = username ? 1 : 0; + + return filter->config->not_auth < 0 ? !ret : ret; +} + +static int +filter_check_auth_table(struct filter *filter, enum table_service kind, const char *key) +{ + int ret = 0; + + if (filter->config->auth_table == NULL) + return 0; + + if (key && table_match(filter->config->auth_table, kind, key) > 0) + ret = 1; + + return filter->config->not_auth_table < 0 ? !ret : ret; +} + +static int +filter_check_auth_regex(struct filter *filter, const char *key) +{ + int ret = 0; + + if (filter->config->auth_regex == NULL) + return 0; + + if (key && table_match(filter->config->auth_regex, K_REGEX, key) > 0) + ret = 1; + return filter->config->not_auth_regex < 0 ? !ret : ret; +} + + +static int filter_check_mail_from_table(struct filter *filter, enum table_service kind, const char *key) { int ret = 0; @@ -1015,6 +1257,10 @@ filter_builtins_global(struct filter_session *fs, struct filter *filter, uint64_ filter_check_src_regex(filter, ss_to_text(&fs->ss_src)) || filter_check_helo_table(filter, K_DOMAIN, fs->helo) || filter_check_helo_regex(filter, fs->helo) || + filter_check_auth(filter, fs->username) || + filter_check_auth_table(filter, K_STRING, fs->username) || + filter_check_auth_table(filter, K_CREDENTIALS, fs->username) || + filter_check_auth_regex(filter, fs->username) || filter_check_mail_from_table(filter, K_MAILADDR, fs->mail_from) || filter_check_mail_from_regex(filter, fs->mail_from); } @@ -1044,3 +1290,459 @@ filter_builtins_rcpt_to(struct filter_session *fs, struct filter *filter, uint64 filter_check_rcpt_to_table(filter, K_MAILADDR, param) || filter_check_rcpt_to_regex(filter, param); } + +static int +filter_builtins_data(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param) +{ + return filter_builtins_global(fs, filter, reqid); +} + +static int +filter_builtins_commit(struct filter_session *fs, struct filter *filter, uint64_t reqid, const char *param) +{ + return filter_builtins_global(fs, filter, reqid); +} + +static void +report_smtp_broadcast(uint64_t, const char *, struct timeval *, const char *, + const char *, ...) __attribute__((__format__ (printf, 5, 6))); + +void +lka_report_init(void) +{ + struct reporters *tailq; + size_t i; + + dict_init(&report_smtp_in); + dict_init(&report_smtp_out); + + for (i = 0; i < nitems(smtp_events); ++i) { + tailq = xcalloc(1, sizeof (struct reporters)); + TAILQ_INIT(tailq); + dict_xset(&report_smtp_in, smtp_events[i].event, tailq); + + tailq = xcalloc(1, sizeof (struct reporters)); + TAILQ_INIT(tailq); + dict_xset(&report_smtp_out, smtp_events[i].event, tailq); + } +} + +void +lka_report_register_hook(const char *name, const char *hook) +{ + struct dict *subsystem; + struct reporter_proc *rp; + struct reporters *tailq; + void *iter; + size_t i; + + if (strncmp(hook, "smtp-in|", 8) == 0) { + subsystem = &report_smtp_in; + hook += 8; + } + else if (strncmp(hook, "smtp-out|", 9) == 0) { + subsystem = &report_smtp_out; + hook += 9; + } + else + fatalx("Invalid message direction: %s", hook); + + if (strcmp(hook, "*") == 0) { + iter = NULL; + while (dict_iter(subsystem, &iter, NULL, (void **)&tailq)) { + rp = xcalloc(1, sizeof *rp); + rp->name = xstrdup(name); + TAILQ_INSERT_TAIL(tailq, rp, entries); + } + return; + } + + for (i = 0; i < nitems(smtp_events); i++) + if (strcmp(hook, smtp_events[i].event) == 0) + break; + if (i == nitems(smtp_events)) + fatalx("Unrecognized report name: %s", hook); + + tailq = dict_get(subsystem, hook); + rp = xcalloc(1, sizeof *rp); + rp->name = xstrdup(name); + TAILQ_INSERT_TAIL(tailq, rp, entries); +} + +static void +report_smtp_broadcast(uint64_t reqid, const char *direction, struct timeval *tv, const char *event, + const char *format, ...) +{ + va_list ap; + struct dict *d; + struct reporters *tailq; + struct reporter_proc *rp; + + if (strcmp("smtp-in", direction) == 0) + d = &report_smtp_in; + + else if (strcmp("smtp-out", direction) == 0) + d = &report_smtp_out; + + else + fatalx("unexpected direction: %s", direction); + + tailq = dict_xget(d, event); + TAILQ_FOREACH(rp, tailq, entries) { + if (!lka_filter_proc_in_session(reqid, rp->name)) + continue; + + va_start(ap, format); + if (io_printf(lka_proc_get_io(rp->name), + "report|%s|%lld.%06ld|%s|%s|%016"PRIx64"%s", + PROTOCOL_VERSION, (long long int)tv->tv_sec, tv->tv_usec, direction, + event, reqid, format[0] != '\n' ? "|" : "") == -1 || + io_vprintf(lka_proc_get_io(rp->name), format, ap) == -1) + fatalx("failed to write to processor"); + va_end(ap); + } +} + +void +lka_report_smtp_link_connect(const char *direction, struct timeval *tv, uint64_t reqid, const char *rdns, + int fcrdns, + const struct sockaddr_storage *ss_src, + const struct sockaddr_storage *ss_dest) +{ + struct filter_session *fs; + char src[NI_MAXHOST + 5]; + char dest[NI_MAXHOST + 5]; + uint16_t src_port = 0; + uint16_t dest_port = 0; + const char *fcrdns_str; + + if (ss_src->ss_family == AF_INET) + src_port = ntohs(((const struct sockaddr_in *)ss_src)->sin_port); + else if (ss_src->ss_family == AF_INET6) + src_port = ntohs(((const struct sockaddr_in6 *)ss_src)->sin6_port); + + if (ss_dest->ss_family == AF_INET) + dest_port = ntohs(((const struct sockaddr_in *)ss_dest)->sin_port); + else if (ss_dest->ss_family == AF_INET6) + dest_port = ntohs(((const struct sockaddr_in6 *)ss_dest)->sin6_port); + + if (strcmp(ss_to_text(ss_src), "local") == 0) { + (void)snprintf(src, sizeof src, "unix:%s", SMTPD_SOCKET); + (void)snprintf(dest, sizeof dest, "unix:%s", SMTPD_SOCKET); + } else { + (void)snprintf(src, sizeof src, "%s:%d", ss_to_text(ss_src), src_port); + (void)snprintf(dest, sizeof dest, "%s:%d", ss_to_text(ss_dest), dest_port); + } + + switch (fcrdns) { + case 1: + fcrdns_str = "pass"; + break; + case 0: + fcrdns_str = "fail"; + break; + default: + fcrdns_str = "error"; + break; + } + + fs = tree_xget(&sessions, reqid); + fs->rdns = xstrdup(rdns); + fs->fcrdns = fcrdns; + fs->ss_src = *ss_src; + fs->ss_dest = *ss_dest; + + report_smtp_broadcast(reqid, direction, tv, "link-connect", + "%s|%s|%s|%s\n", rdns, fcrdns_str, src, dest); +} + +void +lka_report_smtp_link_disconnect(const char *direction, struct timeval *tv, uint64_t reqid) +{ + report_smtp_broadcast(reqid, direction, tv, "link-disconnect", "\n"); +} + +void +lka_report_smtp_link_greeting(const char *direction, uint64_t reqid, + struct timeval *tv, const char *domain) +{ + report_smtp_broadcast(reqid, direction, tv, "link-greeting", "%s\n", + domain); +} + +void +lka_report_smtp_link_auth(const char *direction, struct timeval *tv, uint64_t reqid, + const char *username, const char *result) +{ + struct filter_session *fs; + + if (strcmp(result, "pass") == 0) { + fs = tree_xget(&sessions, reqid); + fs->username = xstrdup(username); + } + report_smtp_broadcast(reqid, direction, tv, "link-auth", "%s|%s\n", + username, result); +} + +void +lka_report_smtp_link_identify(const char *direction, struct timeval *tv, + uint64_t reqid, const char *method, const char *heloname) +{ + report_smtp_broadcast(reqid, direction, tv, "link-identify", "%s|%s\n", + method, heloname); +} + +void +lka_report_smtp_link_tls(const char *direction, struct timeval *tv, uint64_t reqid, const char *ciphers) +{ + report_smtp_broadcast(reqid, direction, tv, "link-tls", "%s\n", + ciphers); +} + +void +lka_report_smtp_tx_reset(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid) +{ + report_smtp_broadcast(reqid, direction, tv, "tx-reset", "%08x\n", + msgid); +} + +void +lka_report_smtp_tx_begin(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid) +{ + report_smtp_broadcast(reqid, direction, tv, "tx-begin", "%08x\n", + msgid); +} + +void +lka_report_smtp_tx_mail(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, const char *address, int ok) +{ + const char *result; + + switch (ok) { + case 1: + result = "ok"; + break; + case 0: + result = "permfail"; + break; + default: + result = "tempfail"; + break; + } + report_smtp_broadcast(reqid, direction, tv, "tx-mail", "%08x|%s|%s\n", + msgid, result, address); +} + +void +lka_report_smtp_tx_rcpt(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, const char *address, int ok) +{ + const char *result; + + switch (ok) { + case 1: + result = "ok"; + break; + case 0: + result = "permfail"; + break; + default: + result = "tempfail"; + break; + } + report_smtp_broadcast(reqid, direction, tv, "tx-rcpt", "%08x|%s|%s\n", + msgid, result, address); +} + +void +lka_report_smtp_tx_envelope(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, uint64_t evpid) +{ + report_smtp_broadcast(reqid, direction, tv, "tx-envelope", + "%08x|%016"PRIx64"\n", msgid, evpid); +} + +void +lka_report_smtp_tx_data(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, int ok) +{ + const char *result; + + switch (ok) { + case 1: + result = "ok"; + break; + case 0: + result = "permfail"; + break; + default: + result = "tempfail"; + break; + } + report_smtp_broadcast(reqid, direction, tv, "tx-data", "%08x|%s\n", + msgid, result); +} + +void +lka_report_smtp_tx_commit(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, size_t msgsz) +{ + report_smtp_broadcast(reqid, direction, tv, "tx-commit", "%08x|%zd\n", + msgid, msgsz); +} + +void +lka_report_smtp_tx_rollback(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid) +{ + report_smtp_broadcast(reqid, direction, tv, "tx-rollback", "%08x\n", + msgid); +} + +void +lka_report_smtp_protocol_client(const char *direction, struct timeval *tv, uint64_t reqid, const char *command) +{ + report_smtp_broadcast(reqid, direction, tv, "protocol-client", "%s\n", + command); +} + +void +lka_report_smtp_protocol_server(const char *direction, struct timeval *tv, uint64_t reqid, const char *response) +{ + report_smtp_broadcast(reqid, direction, tv, "protocol-server", "%s\n", + response); +} + +void +lka_report_smtp_filter_response(const char *direction, struct timeval *tv, uint64_t reqid, + int phase, int response, const char *param) +{ + const char *phase_name; + const char *response_name; + + switch (phase) { + case FILTER_CONNECT: + phase_name = "connected"; + break; + case FILTER_HELO: + phase_name = "helo"; + break; + case FILTER_EHLO: + phase_name = "ehlo"; + break; + case FILTER_STARTTLS: + phase_name = "tls"; + break; + case FILTER_AUTH: + phase_name = "auth"; + break; + case FILTER_MAIL_FROM: + phase_name = "mail-from"; + break; + case FILTER_RCPT_TO: + phase_name = "rcpt-to"; + break; + case FILTER_DATA: + phase_name = "data"; + break; + case FILTER_DATA_LINE: + phase_name = "data-line"; + break; + case FILTER_RSET: + phase_name = "rset"; + break; + case FILTER_QUIT: + phase_name = "quit"; + break; + case FILTER_NOOP: + phase_name = "noop"; + break; + case FILTER_HELP: + phase_name = "help"; + break; + case FILTER_WIZ: + phase_name = "wiz"; + break; + case FILTER_COMMIT: + phase_name = "commit"; + break; + default: + phase_name = ""; + } + + switch (response) { + case FILTER_PROCEED: + response_name = "proceed"; + break; + case FILTER_JUNK: + response_name = "junk"; + break; + case FILTER_REWRITE: + response_name = "rewrite"; + break; + case FILTER_REJECT: + response_name = "reject"; + break; + case FILTER_DISCONNECT: + response_name = "disconnect"; + break; + default: + response_name = ""; + } + + report_smtp_broadcast(reqid, direction, tv, "filter-response", + "%s|%s%s%s\n", phase_name, response_name, param ? "|" : "", + param ? param : ""); +} + +void +lka_report_smtp_timeout(const char *direction, struct timeval *tv, uint64_t reqid) +{ + report_smtp_broadcast(reqid, direction, tv, "timeout", "\n"); +} + +void +lka_report_filter_report(uint64_t reqid, const char *name, int builtin, + const char *direction, struct timeval *tv, const char *message) +{ + report_smtp_broadcast(reqid, direction, tv, "filter-report", + "%s|%s|%s\n", builtin ? "builtin" : "proc", + name, message); +} + +void +lka_report_proc(const char *name, const char *line) +{ + char buffer[LINE_MAX]; + struct timeval tv; + char *ep, *sp, *direction; + uint64_t reqid; + + if (strlcpy(buffer, line + 7, sizeof(buffer)) >= sizeof(buffer)) + fatalx("Invalid report: line too long: %s", line); + + errno = 0; + tv.tv_sec = strtoll(buffer, &ep, 10); + if (ep[0] != '.' || errno != 0) + fatalx("Invalid report: invalid time: %s", line); + sp = ep + 1; + tv.tv_usec = strtol(sp, &ep, 10); + if (ep[0] != '|' || errno != 0) + fatalx("Invalid report: invalid time: %s", line); + if (ep - sp != 6) + fatalx("Invalid report: invalid time: %s", line); + + direction = ep + 1; + if (strncmp(direction, "smtp-in|", 8) == 0) { + direction[7] = '\0'; + direction += 7; +#if 0 + } else if (strncmp(direction, "smtp-out|", 9) == 0) { + direction[8] = '\0'; + direction += 8; +#endif + } else + fatalx("Invalid report: invalid direction: %s", line); + + reqid = strtoull(sp, &ep, 16); + if (ep[0] != '|' || errno != 0) + fatalx("Invalid report: invalid reqid: %s", line); + sp = ep + 1; + + lka_report_filter_report(reqid, name, 0, direction, &tv, sp); +} diff --git a/smtpd/lka_session.c b/usr.sbin/smtpd/lka_session.c index ed1fd36f..999e01d6 100644 --- a/smtpd/lka_session.c +++ b/usr.sbin/smtpd/lka_session.c @@ -17,6 +17,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -541,7 +543,7 @@ lka_submit(struct lka_session *lks, struct rule *rule, struct expandnode *xn) "run with %s privileges", SMTPD_USER); if (xn->type == EXPAND_FILENAME) - format = "/usr/libexec/mail.mboxfile -f %%{mbox.from} %s"; + format = PATH_LIBEXEC"/mail.mboxfile -f %%{mbox.from} %s"; else if (xn->type == EXPAND_FILTER) format = "%s"; (void)snprintf(ep->mda_exec, sizeof(ep->mda_exec), diff --git a/smtpd/log.c b/usr.sbin/smtpd/log.c index 7ec8ca42..14f681e3 100644 --- a/smtpd/log.c +++ b/usr.sbin/smtpd/log.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <stdio.h> #include <stdlib.h> #include <stdarg.h> diff --git a/smtpd/log.h b/usr.sbin/smtpd/log.h index 22bb4164..81d0973c 100644 --- a/smtpd/log.h +++ b/usr.sbin/smtpd/log.h @@ -19,8 +19,14 @@ #ifndef LOG_H #define LOG_H +#include "openbsd-compat.h" + +#include <syslog.h> + #include <stdarg.h> +#ifdef HAVE_SYS_CDEFS_H #include <sys/cdefs.h> +#endif void log_init(int, int); void log_procinit(const char *); diff --git a/smtpd/mail.lmtp.8 b/usr.sbin/smtpd/mail.lmtp.8 index 98dee00d..98dee00d 100644 --- a/smtpd/mail.lmtp.8 +++ b/usr.sbin/smtpd/mail.lmtp.8 diff --git a/smtpd/mail.lmtp.c b/usr.sbin/smtpd/mail.lmtp.c index c12561cf..90b89990 100644 --- a/smtpd/mail.lmtp.c +++ b/usr.sbin/smtpd/mail.lmtp.c @@ -14,6 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> @@ -41,20 +43,21 @@ enum phase { struct session { const char *lhlo; const char *mailfrom; + char *rcptto; char **rcpts; int n_rcpts; }; -static FILE *lmtp_connect(const char *); -static void lmtp_engine(FILE *, struct session *); +static int lmtp_connect(const char *); +static void lmtp_engine(int, struct session *); static void stream_file(FILE *); int main(int argc, char *argv[]) { int ch; - FILE *conn; + int conn; const char *destination = "localhost"; struct session session; @@ -62,9 +65,10 @@ main(int argc, char *argv[]) errx(EX_TEMPFAIL, "mail.lmtp: may not be executed as root"); session.lhlo = "localhost"; - session.mailfrom = NULL; + session.mailfrom = getenv("SENDER"); + session.rcptto = NULL; - while ((ch = getopt(argc, argv, "d:l:f:")) != -1) { + while ((ch = getopt(argc, argv, "d:l:f:ru")) != -1) { switch (ch) { case 'd': destination = optarg; @@ -75,6 +79,15 @@ main(int argc, char *argv[]) case 'f': session.mailfrom = optarg; break; + + case 'r': + session.rcptto = getenv("RECIPIENT"); + break; + + case 'u': + session.rcptto = getenv("USER"); + break; + default: break; } @@ -85,11 +98,17 @@ main(int argc, char *argv[]) if (session.mailfrom == NULL) errx(EX_TEMPFAIL, "sender must be specified with -f"); - if (argc == 0) + if (argc == 0 && session.rcptto == NULL) errx(EX_TEMPFAIL, "no recipient was specified"); - session.rcpts = argv; - session.n_rcpts = argc; + if (session.rcptto) { + session.rcpts = &session.rcptto; + session.n_rcpts = 1; + } + else { + session.rcpts = argv; + session.n_rcpts = argc; + } conn = lmtp_connect(destination); lmtp_engine(conn, &session); @@ -97,7 +116,7 @@ main(int argc, char *argv[]) return (0); } -static FILE * +static int lmtp_connect_inet(const char *destination) { struct addrinfo hints, *res, *res0; @@ -171,10 +190,10 @@ lmtp_connect_inet(const char *destination) errx(EX_TEMPFAIL, "%s", cause); free(destcopy); - return fdopen(s, "r+"); + return s; } -static FILE * +static int lmtp_connect_unix(const char *destination) { struct sockaddr_un addr; @@ -195,10 +214,10 @@ lmtp_connect_unix(const char *destination) if (connect(s, (struct sockaddr *)&addr, sizeof addr) == -1) err(EX_TEMPFAIL, "connect"); - return fdopen(s, "r+"); + return s; } -static FILE * +static int lmtp_connect(const char *destination) { if (destination[0] == '/') @@ -207,17 +226,30 @@ lmtp_connect(const char *destination) } static void -lmtp_engine(FILE *conn, struct session *session) +lmtp_engine(int fd_read, struct session *session) { + int fd_write = 0; + FILE *file_read = 0; + FILE *file_write = 0; char *line = NULL; size_t linesize = 0; ssize_t linelen; enum phase phase = PHASE_BANNER; + if ((fd_write = dup(fd_read)) == -1) + err(EX_TEMPFAIL, "dup"); + + if ((file_read = fdopen(fd_read, "r")) == NULL) + err(EX_TEMPFAIL, "fdopen"); + + if ((file_write = fdopen(fd_write, "w")) == NULL) + err(EX_TEMPFAIL, "fdopen"); + do { - fflush(conn); - if ((linelen = getline(&line, &linesize, conn)) == -1) { - if (ferror(conn)) + fflush(file_write); + + if ((linelen = getline(&line, &linesize, file_read)) == -1) { + if (ferror(file_read)) err(EX_TEMPFAIL, "getline"); else errx(EX_TEMPFAIL, "unexpected EOF from LMTP server"); @@ -226,9 +258,9 @@ lmtp_engine(FILE *conn, struct session *session) line[strcspn(line, "\r")] = '\0'; if (linelen < 4 || - !isdigit(line[0]) || - !isdigit(line[1]) || - !isdigit(line[2]) || + !isdigit((unsigned char)line[0]) || + !isdigit((unsigned char)line[1]) || + !isdigit((unsigned char)line[2]) || (line[3] != ' ' && line[3] != '-')) errx(EX_TEMPFAIL, "LMTP server sent an invalid line"); @@ -241,17 +273,17 @@ lmtp_engine(FILE *conn, struct session *session) switch (phase) { case PHASE_BANNER: - fprintf(conn, "LHLO %s\r\n", session->lhlo); + fprintf(file_write, "LHLO %s\r\n", session->lhlo); phase++; break; case PHASE_HELO: - fprintf(conn, "MAIL FROM:<%s>\r\n", session->mailfrom); + fprintf(file_write, "MAIL FROM:<%s>\r\n", session->mailfrom); phase++; break; case PHASE_MAILFROM: - fprintf(conn, "RCPT TO:<%s>\r\n", session->rcpts[session->n_rcpts - 1]); + fprintf(file_write, "RCPT TO:<%s>\r\n", session->rcpts[session->n_rcpts - 1]); if (session->n_rcpts - 1 == 0) { phase++; break; @@ -260,18 +292,18 @@ lmtp_engine(FILE *conn, struct session *session) break; case PHASE_RCPTTO: - fprintf(conn, "DATA\r\n"); + fprintf(file_write, "DATA\r\n"); phase++; break; case PHASE_DATA: - stream_file(conn); - fprintf(conn, ".\r\n"); + stream_file(file_write); + fprintf(file_write, ".\r\n"); phase++; break; case PHASE_EOM: - fprintf(conn, "QUIT\r\n"); + fprintf(file_write, "QUIT\r\n"); phase++; break; diff --git a/smtpd/mail.maildir.8 b/usr.sbin/smtpd/mail.maildir.8 index ce822698..ce822698 100644 --- a/smtpd/mail.maildir.8 +++ b/usr.sbin/smtpd/mail.maildir.8 diff --git a/smtpd/mail.maildir.c b/usr.sbin/smtpd/mail.maildir.c index 5e558884..fe6adba6 100644 --- a/smtpd/mail.maildir.c +++ b/usr.sbin/smtpd/mail.maildir.c @@ -14,6 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #ifndef nitems #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) #endif diff --git a/smtpd/mail.mboxfile.8 b/usr.sbin/smtpd/mail.mboxfile.8 index 015adcb5..015adcb5 100644 --- a/smtpd/mail.mboxfile.8 +++ b/usr.sbin/smtpd/mail.mboxfile.8 diff --git a/smtpd/mail.mboxfile.c b/usr.sbin/smtpd/mail.mboxfile.c index 96fa2e02..097a8d96 100644 --- a/smtpd/mail.mboxfile.c +++ b/usr.sbin/smtpd/mail.mboxfile.c @@ -14,6 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/stat.h> @@ -27,6 +29,7 @@ #include <stdlib.h> #include <string.h> #include <sysexits.h> +#include <time.h> #include <unistd.h> static void mboxfile_engine(const char *sender, const char *filename); @@ -72,7 +75,13 @@ mboxfile_engine(const char *sender, const char *filename) time(&now); +#ifndef O_EXLOCK +#define O_EXLOCK 0 +#endif fd = open(filename, O_CREAT | O_APPEND | O_WRONLY | O_EXLOCK, 0600); +#ifndef HAVE_O_EXLOCK + /* XXX : do something! */ +#endif if (fd == -1) err(EX_TEMPFAIL, NULL); @@ -94,7 +103,7 @@ mboxfile_engine(const char *sender, const char *filename) if (fflush(fp) == EOF || ferror(fp) || - fsync(fd) == -1 || + (fsync(fd) == -1 && errno != EINVAL) || fclose(fp) == EOF) err(EX_TEMPFAIL, NULL); } diff --git a/smtpd/mail.mda.8 b/usr.sbin/smtpd/mail.mda.8 index 61fed733..61fed733 100644 --- a/smtpd/mail.mda.8 +++ b/usr.sbin/smtpd/mail.mda.8 diff --git a/smtpd/mail.mda.c b/usr.sbin/smtpd/mail.mda.c index f9fb3236..23958071 100644 --- a/smtpd/mail.mda.c +++ b/usr.sbin/smtpd/mail.mda.c @@ -14,6 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/stat.h> #include <sys/wait.h> diff --git a/smtpd/mail/Makefile b/usr.sbin/smtpd/mail/Makefile index b2bc2a26..b2bc2a26 100644 --- a/smtpd/mail/Makefile +++ b/usr.sbin/smtpd/mail/Makefile diff --git a/smtpd/mailaddr.c b/usr.sbin/smtpd/mailaddr.c index a15470d7..0965aea8 100644 --- a/smtpd/mailaddr.c +++ b/usr.sbin/smtpd/mailaddr.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mailaddr.c,v 1.3 2018/05/31 21:06:12 gilles Exp $ */ +/* $OpenBSD: mailaddr.c,v 1.4 2020/09/22 18:04:27 martijn Exp $ */ /* * Copyright (c) 2015 Gilles Chehade <gilles@poolp.org> @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -80,12 +82,10 @@ int mailaddr_line(struct maddrmap *maddrmap, const char *s) { struct maddrnode mn; - char buffer[LINE_MAX]; - char *p, *subrcpt; + char *p, *subrcpt, *buffer; int ret; - memset(buffer, 0, sizeof buffer); - if (strlcpy(buffer, s, sizeof buffer) >= sizeof buffer) + if ((buffer = strdup(s)) == NULL) return 0; p = buffer; @@ -93,12 +93,16 @@ mailaddr_line(struct maddrmap *maddrmap, const char *s) subrcpt = strip(subrcpt); if (subrcpt[0] == '\0') continue; - if (!text_to_mailaddr(&mn.mailaddr, subrcpt)) + if (!text_to_mailaddr(&mn.mailaddr, subrcpt)) { + free(buffer); return 0; + } log_debug("subrcpt: [%s]", subrcpt); maddrmap_insert(maddrmap, &mn); } + free(buffer); + if (ret >= 0) return 1; /* expand_line_split() returned < 0 */ diff --git a/smtpd/makemap.8 b/usr.sbin/smtpd/makemap.8 index 674bef6f..674bef6f 100644 --- a/smtpd/makemap.8 +++ b/usr.sbin/smtpd/makemap.8 diff --git a/smtpd/makemap.c b/usr.sbin/smtpd/makemap.c index dd8bc8d3..10e3f555 100644 --- a/smtpd/makemap.c +++ b/usr.sbin/smtpd/makemap.c @@ -1,4 +1,4 @@ -/* $OpenBSD: makemap.c,v 1.72 2018/12/28 11:40:29 eric Exp $ */ +/* $OpenBSD: makemap.c,v 1.73 2020/02/24 16:16:07 millert Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> @@ -17,6 +17,11 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + +#ifdef HAVE_SYS_FILE_H +#include <sys/file.h> /* Needed for flock */ +#endif #include <sys/types.h> #include <sys/stat.h> #include <sys/tree.h> @@ -24,7 +29,13 @@ #include <sys/socket.h> #include <ctype.h> +#ifdef HAVE_DB_H #include <db.h> +#elif defined(HAVE_DB1_DB_H) +#include <db1/db.h> +#elif defined(HAVE_DB_185_H) +#include <db_185.h> +#endif #include <err.h> #include <errno.h> #include <event.h> @@ -36,12 +47,17 @@ #include <syslog.h> #include <unistd.h> #include <limits.h> +#ifdef HAVE_UTIL_H #include <util.h> +#endif +#ifdef HAVE_LIBUTIL_H +#include <libutil.h> +#endif #include "smtpd.h" #include "log.h" -#define PATH_ALIASES "/etc/mail/aliases" +#define PATH_ALIASES SMTPD_CONFDIR "/aliases" static void usage(void); static int parse_map(DB *, int *, char *); @@ -53,7 +69,6 @@ static int make_aliases(DBT *, char *); static char *conf_aliases(char *); static int dump_db(const char *, DBTYPE); -struct smtpd *env; char *source; static int mode; @@ -83,8 +98,13 @@ makemap(int prog_mode, int argc, char *argv[]) int ch, dbputs = 0, Uflag = 0; DBTYPE dbtype = DB_HASH; char *p; + gid_t gid; int fd = -1; + gid = getgid(); + if (setresgid(gid, gid, gid) == -1) + err(1, "setresgid"); + if ((env = config_default()) == NULL) err(1, NULL); @@ -161,9 +181,9 @@ makemap(int prog_mode, int argc, char *argv[]) errx(1, "database name too long"); } - execlp("makemap", "makemap", "-d", argv[0], "-o", dbname, "-", - (char *)NULL); - err(1, "execlp"); + execl(PATH_MAKEMAP, "makemap", "-d", argv[0], "-o", dbname, + "-", (char *)NULL); + err(1, "execl"); } if (mode == P_NEWALIASES) { diff --git a/smtpd/mda.c b/usr.sbin/smtpd/mda.c index 765acda9..5e8fec19 100644 --- a/smtpd/mda.c +++ b/usr.sbin/smtpd/mda.c @@ -19,6 +19,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -28,6 +30,7 @@ #include <err.h> #include <errno.h> #include <event.h> +#include <grp.h> /* needed for setgroups */ #include <imsg.h> #include <inttypes.h> #include <pwd.h> @@ -39,7 +42,11 @@ #include <time.h> #include <unistd.h> #include <limits.h> +#if defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) #include <vis.h> +#else +#include "bsd-vis.h" +#endif #include "smtpd.h" #include "log.h" @@ -521,8 +528,8 @@ mda_getlastline(int fd, char *dst, size_t dstsz) size_t sz = 0; ssize_t len; int out = 0; - - if (lseek(fd, 0, SEEK_SET) == -1) { + + if (lseek(fd, 0, SEEK_SET) < 0) { log_warn("warn: mda: lseek"); close(fd); return (-1); diff --git a/usr.sbin/smtpd/mda_mbox.c b/usr.sbin/smtpd/mda_mbox.c new file mode 100644 index 00000000..8918e3ee --- /dev/null +++ b/usr.sbin/smtpd/mda_mbox.c @@ -0,0 +1,94 @@ +/* $OpenBSD: mda_mbox.c,v 1.2 2020/02/03 15:41:22 gilles Exp $ */ + +/* + * Copyright (c) 2018 Gilles Chehade <gilles@poolp.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "includes.h" + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/queue.h> +#include <sys/tree.h> +#include <sys/socket.h> + +#include <err.h> +#include <errno.h> +#include <event.h> +#include <fcntl.h> +#include <imsg.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> +#include <limits.h> + +#include "smtpd.h" + + +void +mda_mbox(struct deliver *deliver) +{ + int ret; + char sender[LINE_MAX]; + char *envp[] = { + "HOME=/", + "PATH=" _PATH_DEFPATH, + "LOGNAME=root", + "USER=root", + NULL, + }; + + if (deliver->sender.user[0] == '\0' && + deliver->sender.domain[0] == '\0') + ret = snprintf(sender, sizeof sender, "MAILER-DAEMON"); + else + ret = snprintf(sender, sizeof sender, "%s@%s", + deliver->sender.user, deliver->sender.domain); + if (ret < 0 || (size_t)ret >= sizeof sender) + errx(EX_TEMPFAIL, "sender address too long"); + + execle(PATH_MAILLOCAL, PATH_MAILLOCAL, "-f", + sender, deliver->userinfo.username, (char *)NULL, envp); + perror("execl"); + _exit(EX_TEMPFAIL); +} + +void +mda_mbox_init(struct deliver *deliver) +{ + int fd; + int ret; + char buffer[LINE_MAX]; + + ret = snprintf(buffer, sizeof buffer, "%s/%s", + _PATH_MAILDIR, deliver->userinfo.username); + if (ret < 0 || (size_t)ret >= sizeof buffer) + errx(EX_TEMPFAIL, "mailbox pathname too long"); + + if ((fd = open(buffer, O_CREAT|O_EXCL, 0)) == -1) { + if (errno == EEXIST) + return; + err(EX_TEMPFAIL, "open"); + } + + if (fchown(fd, deliver->userinfo.uid, deliver->userinfo.gid) == -1) + err(EX_TEMPFAIL, "fchown"); + + if (fchmod(fd, S_IRUSR|S_IWUSR) == -1) + err(EX_TEMPFAIL, "fchown"); +} diff --git a/smtpd/mda_unpriv.c b/usr.sbin/smtpd/mda_unpriv.c index 23754070..2143b9a0 100644 --- a/smtpd/mda_unpriv.c +++ b/usr.sbin/smtpd/mda_unpriv.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mda_unpriv.c,v 1.5 2018/12/27 15:41:50 gilles Exp $ */ +/* $OpenBSD: mda_unpriv.c,v 1.6 2020/02/02 22:13:48 gilles Exp $ */ /* * Copyright (c) 2018 Gilles Chehade <gilles@poolp.org> @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -40,7 +42,7 @@ mda_unpriv(struct dispatcher *dsp, struct deliver *deliver, const char *pw_name, const char *pw_dir) { int idx; - char *mda_environ[10]; + char *mda_environ[11]; char mda_exec[LINE_MAX]; char mda_wrapper[LINE_MAX]; const char *mda_command; @@ -72,6 +74,12 @@ mda_unpriv(struct dispatcher *dsp, struct deliver *deliver, xasprintf(&mda_environ[idx++], "LOGNAME=%s", pw_name); xasprintf(&mda_environ[idx++], "USER=%s", pw_name); + if (deliver->sender.user[0]) + xasprintf(&mda_environ[idx++], "SENDER=%s@%s", + deliver->sender.user, deliver->sender.domain); + else + xasprintf(&mda_environ[idx++], "SENDER="); + if (deliver->mda_subaddress[0]) xasprintf(&mda_environ[idx++], "EXTENSION=%s", deliver->mda_subaddress); diff --git a/smtpd/mda_variables.c b/usr.sbin/smtpd/mda_variables.c index 4558305e..b672e492 100644 --- a/smtpd/mda_variables.c +++ b/usr.sbin/smtpd/mda_variables.c @@ -17,6 +17,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> diff --git a/smtpd/mproc.c b/usr.sbin/smtpd/mproc.c index aa19c7d4..dac38af2 100644 --- a/smtpd/mproc.c +++ b/usr.sbin/smtpd/mproc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mproc.c,v 1.35 2019/10/03 05:50:28 gilles Exp $ */ +/* $OpenBSD: mproc.c,v 1.36 2020/03/17 09:01:53 tobhe Exp $ */ /* * Copyright (c) 2012 Eric Faurot <eric@faurot.net> @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/socket.h> #include <sys/tree.h> @@ -60,9 +62,7 @@ mproc_fork(struct mproc *p, const char *path, char *argv[]) if (p->pid == 0) { /* child process */ dup2(sp[0], STDIN_FILENO); - if (closefrom(STDERR_FILENO + 1) == -1) - exit(1); - + closefrom(STDERR_FILENO + 1); execv(path, argv); err(1, "execv: %s", path); } @@ -90,7 +90,8 @@ mproc_clear(struct mproc *p) { log_debug("debug: clearing p=%s, fd=%d, pid=%d", p->name, p->imsgbuf.fd, p->pid); - event_del(&p->ev); + if (p->events) + event_del(&p->ev); close(p->imsgbuf.fd); imsg_clear(&p->imsgbuf); } @@ -485,8 +486,8 @@ m_add_msgid(struct mproc *m, uint32_t v) void m_add_sockaddr(struct mproc *m, const struct sockaddr *sa) { - m_add_size(m, sa->sa_len); - m_add(m, sa, sa->sa_len); + m_add_size(m, SA_LEN(sa)); + m_add(m, sa, SA_LEN(sa)); } void @@ -637,6 +638,8 @@ m_get_envelope(struct msg *m, struct envelope *evp) m_get_evpid(m, &evpid); m_get_string(m, &buf); + if (buf == NULL) + fatalx("empty envelope buffer"); if (!envelope_load_buffer(evp, buf, strlen(buf))) fatalx("failed to retrieve envelope"); diff --git a/smtpd/mta.c b/usr.sbin/smtpd/mta.c index 6c817d00..922170ae 100644 --- a/smtpd/mta.c +++ b/usr.sbin/smtpd/mta.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mta.c,v 1.232 2019/09/20 17:46:05 gilles Exp $ */ +/* $OpenBSD: mta.c,v 1.234 2019/12/21 10:34:07 gilles Exp $ */ /* * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -19,6 +19,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -31,6 +33,7 @@ #include <imsg.h> #include <inttypes.h> #include <netdb.h> +#include <grp.h> /* needed for setgroups */ #include <limits.h> #include <pwd.h> #include <signal.h> @@ -926,6 +929,10 @@ mta_query_smarthost(struct envelope *evp0) m_create(p_lka, IMSG_MTA_LOOKUP_SMARTHOST, 0, 0, -1); m_add_id(p_lka, evp->id); + if (dispatcher->u.remote.smarthost_domain) + m_add_string(p_lka, evp->dest.domain); + else + m_add_string(p_lka, NULL); m_add_string(p_lka, dispatcher->u.remote.smarthost); m_close(p_lka); @@ -1764,6 +1771,7 @@ mta_relay(struct envelope *e, struct relayhost *relayh) r = xcalloc(1, sizeof *r); TAILQ_INIT(&r->tasks); r->id = generate_uid(); + r->dispatcher = dispatcher; r->tls = key.tls; r->flags = key.flags; r->domain = key.domain; @@ -2107,13 +2115,13 @@ mta_host(const struct sockaddr *sa) struct mta_host key, *h; struct sockaddr_storage ss; - memmove(&ss, sa, sa->sa_len); + memmove(&ss, sa, SA_LEN(sa)); key.sa = (struct sockaddr*)&ss; h = SPLAY_FIND(mta_host_tree, &hosts, &key); if (h == NULL) { h = xcalloc(1, sizeof(*h)); - h->sa = xmemdup(sa, sa->sa_len); + h->sa = xmemdup(sa, SA_LEN(sa)); SPLAY_INSERT(mta_host_tree, &hosts, h); stat_increment("mta.host", 1); } @@ -2158,11 +2166,11 @@ mta_host_to_text(struct mta_host *h) static int mta_host_cmp(const struct mta_host *a, const struct mta_host *b) { - if (a->sa->sa_len < b->sa->sa_len) + if (SA_LEN(a->sa) < SA_LEN(b->sa)) return (-1); - if (a->sa->sa_len > b->sa->sa_len) + if (SA_LEN(a->sa) > SA_LEN(b->sa)) return (1); - return (memcmp(a->sa, b->sa, a->sa->sa_len)); + return (memcmp(a->sa, b->sa, SA_LEN(a->sa))); } SPLAY_GENERATE(mta_host_tree, mta_host, entry, mta_host_cmp); @@ -2237,7 +2245,7 @@ mta_source(const struct sockaddr *sa) struct sockaddr_storage ss; if (sa) { - memmove(&ss, sa, sa->sa_len); + memmove(&ss, sa, SA_LEN(sa)); key.sa = (struct sockaddr*)&ss; } else key.sa = NULL; @@ -2246,7 +2254,7 @@ mta_source(const struct sockaddr *sa) if (s == NULL) { s = xcalloc(1, sizeof(*s)); if (sa) - s->sa = xmemdup(sa, sa->sa_len); + s->sa = xmemdup(sa, SA_LEN(sa)); SPLAY_INSERT(mta_source_tree, &sources, s); stat_increment("mta.source", 1); } @@ -2291,11 +2299,11 @@ mta_source_cmp(const struct mta_source *a, const struct mta_source *b) return ((b->sa == NULL) ? 0 : -1); if (b->sa == NULL) return (1); - if (a->sa->sa_len < b->sa->sa_len) + if (SA_LEN(a->sa) < SA_LEN(b->sa)) return (-1); - if (a->sa->sa_len > b->sa->sa_len) + if (SA_LEN(a->sa) > SA_LEN(b->sa)) return (1); - return (memcmp(a->sa, b->sa, a->sa->sa_len)); + return (memcmp(a->sa, b->sa, SA_LEN(a->sa))); } SPLAY_GENERATE(mta_source_tree, mta_source, entry, mta_source_cmp); diff --git a/smtpd/mta_session.c b/usr.sbin/smtpd/mta_session.c index 72969e0a..ad1c3c84 100644 --- a/smtpd/mta_session.c +++ b/usr.sbin/smtpd/mta_session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: mta_session.c,v 1.122 2019/09/20 17:46:05 gilles Exp $ */ +/* $OpenBSD: mta_session.c,v 1.136 2020/05/21 15:38:05 millert Exp $ */ /* * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> @@ -19,6 +19,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -105,6 +107,8 @@ struct mta_session { char *helo; char *mxname; + char *username; + int flags; int attempt; @@ -128,6 +132,7 @@ struct mta_session { struct mta_task *task; struct mta_envelope *currevp; FILE *datafp; + size_t datalen; size_t failures; @@ -163,6 +168,36 @@ void mta_hoststat_reschedule(const char *); void mta_hoststat_cache(const char *, uint64_t); void mta_hoststat_uncache(const char *, uint64_t); + +static void mta_filter_begin(struct mta_session *); +static void mta_filter_end(struct mta_session *); +static void mta_connected(struct mta_session *); +static void mta_disconnected(struct mta_session *); + +static void mta_report_link_connect(struct mta_session *, const char *, int, + const struct sockaddr_storage *, + const struct sockaddr_storage *); +static void mta_report_link_greeting(struct mta_session *, const char *); +static void mta_report_link_identify(struct mta_session *, const char *, const char *); +static void mta_report_link_tls(struct mta_session *, const char *); +static void mta_report_link_disconnect(struct mta_session *); +static void mta_report_link_auth(struct mta_session *, const char *, const char *); +static void mta_report_tx_reset(struct mta_session *, uint32_t); +static void mta_report_tx_begin(struct mta_session *, uint32_t); +static void mta_report_tx_mail(struct mta_session *, uint32_t, const char *, int); +static void mta_report_tx_rcpt(struct mta_session *, uint32_t, const char *, int); +static void mta_report_tx_envelope(struct mta_session *, uint32_t, uint64_t); +static void mta_report_tx_data(struct mta_session *, uint32_t, int); +static void mta_report_tx_commit(struct mta_session *, uint32_t, size_t); +static void mta_report_tx_rollback(struct mta_session *, uint32_t); +static void mta_report_protocol_client(struct mta_session *, const char *); +static void mta_report_protocol_server(struct mta_session *, const char *); +#if 0 +static void mta_report_filter_response(struct mta_session *, int, int, const char *); +#endif +static void mta_report_timeout(struct mta_session *); + + static struct tree wait_helo; static struct tree wait_ptr; static struct tree wait_fd; @@ -171,6 +206,9 @@ static struct tree wait_tls_verify; static struct runq *hangon; +#define SESSION_FILTERED(s) \ + ((s)->relay->dispatcher->u.remote.filtername) + static void mta_session_init(void) { @@ -201,6 +239,8 @@ mta_session(struct mta_relay *relay, struct mta_route *route, const char *mxname s->route = route; s->mxname = xstrdup(mxname); + mta_filter_begin(s); + if (relay->flags & RELAY_LMTP) s->flags |= MTA_LMTP; switch (relay->tls) { @@ -349,6 +389,8 @@ mta_free(struct mta_session *s) log_debug("debug: mta: %p: session done", s); + mta_disconnected(s); + if (s->ready) s->relay->nconn_ready -= 1; @@ -362,12 +404,15 @@ mta_free(struct mta_session *s) if (s->task) fatalx("current task should have been deleted already"); - if (s->datafp) + if (s->datafp) { fclose(s->datafp); + s->datalen = 0; + } free(s->helo); relay = s->relay; route = s->route; + free(s->username); free(s->mxname); free(s); stat_decrement("mta.session", 1); @@ -476,7 +521,7 @@ mta_connect(struct mta_session *s) if (s->relay->port) portno = s->relay->port; - memmove(&ss, s->route->dst->sa, s->route->dst->sa->sa_len); + memmove(&ss, s->route->dst->sa, SA_LEN(s->route->dst->sa)); sa = (struct sockaddr *)&ss; if (sa->sa_family == AF_INET) @@ -553,16 +598,19 @@ again: case MTA_EHLO: s->ext = 0; mta_send(s, "EHLO %s", s->helo); + mta_report_link_identify(s, "EHLO", s->helo); break; case MTA_HELO: s->ext = 0; mta_send(s, "HELO %s", s->helo); + mta_report_link_identify(s, "HELO", s->helo); break; case MTA_LHLO: s->ext = 0; mta_send(s, "LHLO %s", s->helo); + mta_report_link_identify(s, "LHLO", s->helo); break; case MTA_STARTTLS: @@ -613,6 +661,14 @@ again: break; case MTA_AUTH_PLAIN: + memset(ibuf, 0, sizeof ibuf); + if (base64_decode(s->relay->secret, (unsigned char *)ibuf, + sizeof(ibuf)-1) == -1) { + log_debug("debug: mta: %p: credentials too large on session", s); + mta_error(s, "Credentials too large"); + break; + } + s->username = xstrdup(ibuf+1); mta_send(s, "AUTH PLAIN %s", s->relay->secret); break; @@ -628,6 +684,7 @@ again: mta_error(s, "Credentials too large"); break; } + s->username = xstrdup(ibuf+1); memset(obuf, 0, sizeof obuf); base64_encode((unsigned char *)ibuf + 1, strlen(ibuf + 1), obuf, sizeof obuf); @@ -769,11 +826,12 @@ again: e->dest, e->dsn_notify ? " NOTIFY=" : "", e->dsn_notify ? dsn_strnotify(e->dsn_notify) : "", - e->dsn_orcpt ? " ORCPT=" : "", + e->dsn_orcpt ? " ORCPT=rfc822;" : "", e->dsn_orcpt ? e->dsn_orcpt : ""); } else mta_send(s, "RCPT TO:<%s>", e->dest); + mta_report_tx_envelope(s, s->task->msgid, e->id); s->rcptcount++; break; @@ -814,6 +872,7 @@ again: if (s->datafp) { fclose(s->datafp); s->datafp = NULL; + s->datalen = 0; } mta_send(s, "RSET"); break; @@ -838,6 +897,7 @@ mta_response(struct mta_session *s, char *line) struct sockaddr_storage ss; struct sockaddr *sa; const char *domain; + char *pbuf; socklen_t sa_len; char buf[LINE_MAX]; int delivery; @@ -850,6 +910,16 @@ mta_response(struct mta_session *s, char *line) s->flags |= MTA_FREE; return; } + + pbuf = ""; + if (strlen(line) > 4) { + (void)strlcpy(buf, line + 4, sizeof buf); + if ((pbuf = strchr(buf, ' '))) + *pbuf = '\0'; + pbuf = valid_domainpart(buf) ? buf : ""; + } + mta_report_link_greeting(s, pbuf); + if (s->flags & MTA_LMTP) mta_enter_state(s, MTA_LHLO); else @@ -910,15 +980,18 @@ mta_response(struct mta_session *s, char *line) case MTA_AUTH_PLAIN: if (line[0] != '2') { mta_error(s, "AUTH rejected: %s", line); + mta_report_link_auth(s, s->username, "fail"); s->flags |= MTA_FREE; return; } + mta_report_link_auth(s, s->username, "pass"); mta_enter_state(s, MTA_READY); break; case MTA_AUTH_LOGIN: if (strncmp(line, "334 ", 4) != 0) { mta_error(s, "AUTH rejected: %s", line); + mta_report_link_auth(s, s->username, "fail"); s->flags |= MTA_FREE; return; } @@ -928,6 +1001,7 @@ mta_response(struct mta_session *s, char *line) case MTA_AUTH_LOGIN_USER: if (strncmp(line, "334 ", 4) != 0) { mta_error(s, "AUTH rejected: %s", line); + mta_report_link_auth(s, s->username, "fail"); s->flags |= MTA_FREE; return; } @@ -937,9 +1011,11 @@ mta_response(struct mta_session *s, char *line) case MTA_AUTH_LOGIN_PASS: if (line[0] != '2') { mta_error(s, "AUTH rejected: %s", line); + mta_report_link_auth(s, s->username, "fail"); s->flags |= MTA_FREE; return; } + mta_report_link_auth(s, s->username, "pass"); mta_enter_state(s, MTA_READY); break; @@ -949,10 +1025,13 @@ mta_response(struct mta_session *s, char *line) delivery = IMSG_MTA_DELIVERY_PERMFAIL; else delivery = IMSG_MTA_DELIVERY_TEMPFAIL; + mta_flush_task(s, delivery, line, 0, 0); mta_enter_state(s, MTA_RSET); return; } + mta_report_tx_begin(s, s->task->msgid); + mta_report_tx_mail(s, s->task->msgid, s->task->sender, 1); mta_enter_state(s, MTA_RCPT); break; @@ -976,6 +1055,8 @@ mta_response(struct mta_session *s, char *line) mta_hoststat_reschedule(domain); } else { + mta_report_tx_rollback(s, s->task->msgid); + mta_report_tx_reset(s, s->task->msgid); if (line[0] == '5') delivery = IMSG_MTA_DELIVERY_PERMFAIL; else @@ -1027,6 +1108,21 @@ mta_response(struct mta_session *s, char *line) } } + switch (line[0]) { + case '2': + mta_report_tx_rcpt(s, + s->task->msgid, e->dest, 1); + break; + case '4': + mta_report_tx_rcpt(s, + s->task->msgid, e->dest, -1); + break; + case '5': + mta_report_tx_rcpt(s, + s->task->msgid, e->dest, 0); + break; + } + if (s->currevp == NULL) mta_enter_state(s, MTA_DATA); else @@ -1035,13 +1131,19 @@ mta_response(struct mta_session *s, char *line) case MTA_DATA: if (line[0] == '2' || line[0] == '3') { + mta_report_tx_data(s, s->task->msgid, 1); mta_enter_state(s, MTA_BODY); break; } + if (line[0] == '5') delivery = IMSG_MTA_DELIVERY_PERMFAIL; else delivery = IMSG_MTA_DELIVERY_TEMPFAIL; + mta_report_tx_data(s, s->task->msgid, + delivery == IMSG_MTA_DELIVERY_TEMPFAIL ? -1 : 0); + mta_report_tx_rollback(s, s->task->msgid); + mta_report_tx_reset(s, s->task->msgid); mta_flush_task(s, delivery, line, 0, 0); mta_enter_state(s, MTA_RSET); break; @@ -1057,6 +1159,14 @@ mta_response(struct mta_session *s, char *line) delivery = IMSG_MTA_DELIVERY_PERMFAIL; else delivery = IMSG_MTA_DELIVERY_TEMPFAIL; + if (delivery != IMSG_MTA_DELIVERY_OK) { + mta_report_tx_rollback(s, s->task->msgid); + mta_report_tx_reset(s, s->task->msgid); + } + else { + mta_report_tx_commit(s, s->task->msgid, s->datalen); + mta_report_tx_reset(s, s->task->msgid); + } mta_flush_task(s, delivery, line, (s->flags & MTA_LMTP) ? 1 : 0, 0); if (s->task) { s->rcptcount--; @@ -1078,6 +1188,11 @@ mta_response(struct mta_session *s, char *line) case MTA_RSET: s->rcptcount = 0; + + if (s->task) { + mta_report_tx_rollback(s, s->task->msgid); + mta_report_tx_reset(s, s->task->msgid); + } if (s->relay->limits->sessdelay_transaction) { log_debug("debug: mta: waiting for %llds after reset", (long long int)s->relay->limits->sessdelay_transaction); @@ -1110,7 +1225,7 @@ mta_io(struct io *io, int evt, void *arg) switch (evt) { case IO_CONNECTED: - log_info("%016"PRIx64" mta connected", s->id); + mta_connected(s); if (s->use_smtps) { io_set_write(io); @@ -1127,6 +1242,9 @@ mta_io(struct io *io, int evt, void *arg) s->id, ssl_to_text(io_tls(s->io))); s->flags |= MTA_TLS; + mta_report_link_tls(s, + ssl_to_text(io_tls(s->io))); + mta_cert_verify(s); break; @@ -1141,7 +1259,12 @@ mta_io(struct io *io, int evt, void *arg) return; } + /* Strip trailing '\r' */ + if (len && line[len - 1] == '\r') + line[--len] = '\0'; + log_trace(TRACE_MTA, "mta: %p: <<< %s", s, line); + mta_report_protocol_server(s, line); if ((error = parse_smtp_response(line, len, &msg, &cont))) { mta_error(s, "Bad response: %s", error); @@ -1177,14 +1300,13 @@ mta_io(struct io *io, int evt, void *arg) if (cont) { if (s->replybuf[0] == '\0') (void)strlcat(s->replybuf, line, sizeof s->replybuf); - else { - line = line + 4; - if (isdigit((int)*line) && *(line + 1) == '.' && - isdigit((int)*line+2) && *(line + 3) == '.' && - isdigit((int)*line+4) && isspace((int)*(line + 5))) - (void)strlcat(s->replybuf, line+5, sizeof s->replybuf); - else - (void)strlcat(s->replybuf, line, sizeof s->replybuf); + else if (len > 4) { + p = line + 4; + if (isdigit((unsigned char)p[0]) && p[1] == '.' && + isdigit((unsigned char)p[2]) && p[3] == '.' && + isdigit((unsigned char)p[4]) && isspace((unsigned char)p[5])) + p += 5; + (void)strlcat(s->replybuf, p, sizeof s->replybuf); } goto nextline; } @@ -1192,17 +1314,17 @@ mta_io(struct io *io, int evt, void *arg) /* last line of a reply, check if we're on a continuation to parse out status and ESC. * if we overflow reply buffer or are not on continuation, log entire last line. */ - if (s->replybuf[0] != '\0') { + if (s->replybuf[0] == '\0') + (void)strlcat(s->replybuf, line, sizeof s->replybuf); + else if (len > 4) { p = line + 4; - if (isdigit((int)*p) && *(p + 1) == '.' && - isdigit((int)*p+2) && *(p + 3) == '.' && - isdigit((int)*p+4) && isspace((int)*(p + 5))) + if (isdigit((unsigned char)p[0]) && p[1] == '.' && + isdigit((unsigned char)p[2]) && p[3] == '.' && + isdigit((unsigned char)p[4]) && isspace((unsigned char)p[5])) p += 5; if (strlcat(s->replybuf, p, sizeof s->replybuf) >= sizeof s->replybuf) (void)strlcpy(s->replybuf, line, sizeof s->replybuf); } - else - (void)strlcpy(s->replybuf, line, sizeof s->replybuf); if (s->state == MTA_QUIT) { log_info("%016"PRIx64" mta disconnected reason=quit messages=%zu", @@ -1248,6 +1370,7 @@ mta_io(struct io *io, int evt, void *arg) case IO_TIMEOUT: log_debug("debug: mta: %p: connection timeout", s); mta_error(s, "Connection timeout"); + mta_report_timeout(s); if (!s->ready) mta_connect(s); else @@ -1255,40 +1378,20 @@ mta_io(struct io *io, int evt, void *arg) break; case IO_ERROR: + case IO_TLSERROR: log_debug("debug: mta: %p: IO error: %s", s, io_error(io)); - if (!s->ready) { - mta_error(s, "IO Error: %s", io_error(io)); - mta_connect(s); - break; - } - else if (!(s->flags & (MTA_FORCE_TLS|MTA_FORCE_SMTPS|MTA_FORCE_ANYSSL))) { - /* error in non-strict SSL negotiation, downgrade to plain */ - if (s->flags & MTA_TLS) { - log_info("smtp-out: Error on session %016"PRIx64 - ": opportunistic TLS failed, " - "downgrading to plain", s->id); - s->flags &= ~MTA_TLS; - s->flags |= MTA_DOWNGRADE_PLAIN; - mta_connect(s); - break; - } - } - mta_error(s, "IO Error: %s", io_error(io)); - mta_free(s); - break; - case IO_TLSERROR: - log_debug("debug: mta: %p: TLS IO error: %s", s, io_error(io)); - if (!(s->flags & (MTA_FORCE_TLS|MTA_FORCE_SMTPS|MTA_FORCE_ANYSSL))) { + if (s->state == MTA_STARTTLS && s->use_smtp_tls) { /* error in non-strict SSL negotiation, downgrade to plain */ - log_info("smtp-out: TLS Error on session %016"PRIx64 - ": TLS failed, " + log_info("smtp-out: Error on session %016"PRIx64 + ": opportunistic TLS failed, " "downgrading to plain", s->id); s->flags &= ~MTA_TLS; s->flags |= MTA_DOWNGRADE_PLAIN; mta_connect(s); break; } + mta_error(s, "IO Error: %s", io_error(io)); mta_free(s); break; @@ -1322,6 +1425,13 @@ mta_send(struct mta_session *s, char *fmt, ...) log_trace(TRACE_MTA, "mta: %p: >>> %s", s, p); + if (strncasecmp(p, "AUTH PLAIN ", 11) == 0) + mta_report_protocol_client(s, "AUTH PLAIN ********"); + else if (s->state == MTA_AUTH_LOGIN_USER || s->state == MTA_AUTH_LOGIN_PASS) + mta_report_protocol_client(s, "********"); + else + mta_report_protocol_client(s, p); + io_xprintf(s->io, "%s\r\n", p); free(p); @@ -1344,7 +1454,7 @@ mta_queue_data(struct mta_session *s) break; if (ln[len - 1] == '\n') ln[len - 1] = '\0'; - io_xprintf(s->io, "%s%s\r\n", *ln == '.' ? "." : "", ln); + s->datalen += io_xprintf(s->io, "%s%s\r\n", *ln == '.' ? "." : "", ln); } free(ln); @@ -1560,8 +1670,12 @@ mta_cert_verify_cb(void *arg, int status) match = 0; (void)ssl_check_name(cert, s->mxname, &match); X509_free(cert); - if (!match) + if (!match) { + log_info("%016"PRIx64" mta " + "ssl_check_name: no match for '%s' in cert", + s->id, s->mxname); status = CERT_INVALID; + } } } @@ -1670,3 +1784,225 @@ mta_strstate(int state) return "MTA_???"; } } + +static void +mta_filter_begin(struct mta_session *s) +{ + if (!SESSION_FILTERED(s)) + return; + + m_create(p_lka, IMSG_FILTER_SMTP_BEGIN, 0, 0, -1); + m_add_id(p_lka, s->id); + m_add_string(p_lka, s->relay->dispatcher->u.remote.filtername); + m_close(p_lka); +} + +static void +mta_filter_end(struct mta_session *s) +{ + if (!SESSION_FILTERED(s)) + return; + + m_create(p_lka, IMSG_FILTER_SMTP_END, 0, 0, -1); + m_add_id(p_lka, s->id); + m_close(p_lka); +} + +static void +mta_connected(struct mta_session *s) +{ + struct sockaddr_storage sa_src; + struct sockaddr_storage sa_dest; + int sa_len; + + log_info("%016"PRIx64" mta connected", s->id); + + sa_len = sizeof sa_src; + if (getsockname(io_fileno(s->io), + (struct sockaddr *)&sa_src, &sa_len) == -1) + bzero(&sa_src, sizeof sa_src); + sa_len = sizeof sa_dest; + if (getpeername(io_fileno(s->io), + (struct sockaddr *)&sa_dest, &sa_len) == -1) + bzero(&sa_dest, sizeof sa_dest); + + mta_report_link_connect(s, + s->route->dst->ptrname, 1, + &sa_src, + &sa_dest); +} + +static void +mta_disconnected(struct mta_session *s) +{ + mta_report_link_disconnect(s); + mta_filter_end(s); +} + + +static void +mta_report_link_connect(struct mta_session *s, const char *rdns, int fcrdns, + const struct sockaddr_storage *ss_src, + const struct sockaddr_storage *ss_dest) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_link_connect("smtp-out", s->id, rdns, fcrdns, ss_src, ss_dest); +} + +static void +mta_report_link_greeting(struct mta_session *s, + const char *domain) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_link_greeting("smtp-out", s->id, domain); +} + +static void +mta_report_link_identify(struct mta_session *s, const char *method, const char *identity) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_link_identify("smtp-out", s->id, method, identity); +} + +static void +mta_report_link_tls(struct mta_session *s, const char *ssl) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_link_tls("smtp-out", s->id, ssl); +} + +static void +mta_report_link_disconnect(struct mta_session *s) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_link_disconnect("smtp-out", s->id); +} + +static void +mta_report_link_auth(struct mta_session *s, const char *user, const char *result) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_link_auth("smtp-out", s->id, user, result); +} + +static void +mta_report_tx_reset(struct mta_session *s, uint32_t msgid) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_tx_reset("smtp-out", s->id, msgid); +} + +static void +mta_report_tx_begin(struct mta_session *s, uint32_t msgid) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_tx_begin("smtp-out", s->id, msgid); +} + +static void +mta_report_tx_mail(struct mta_session *s, uint32_t msgid, const char *address, int ok) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_tx_mail("smtp-out", s->id, msgid, address, ok); +} + +static void +mta_report_tx_rcpt(struct mta_session *s, uint32_t msgid, const char *address, int ok) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_tx_rcpt("smtp-out", s->id, msgid, address, ok); +} + +static void +mta_report_tx_envelope(struct mta_session *s, uint32_t msgid, uint64_t evpid) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_tx_envelope("smtp-out", s->id, msgid, evpid); +} + +static void +mta_report_tx_data(struct mta_session *s, uint32_t msgid, int ok) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_tx_data("smtp-out", s->id, msgid, ok); +} + +static void +mta_report_tx_commit(struct mta_session *s, uint32_t msgid, size_t msgsz) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_tx_commit("smtp-out", s->id, msgid, msgsz); +} + +static void +mta_report_tx_rollback(struct mta_session *s, uint32_t msgid) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_tx_rollback("smtp-out", s->id, msgid); +} + +static void +mta_report_protocol_client(struct mta_session *s, const char *command) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_protocol_client("smtp-out", s->id, command); +} + +static void +mta_report_protocol_server(struct mta_session *s, const char *response) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_protocol_server("smtp-out", s->id, response); +} + +#if 0 +static void +mta_report_filter_response(struct mta_session *s, int phase, int response, const char *param) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_filter_response("smtp-out", s->id, phase, response, param); +} +#endif + +static void +mta_report_timeout(struct mta_session *s) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_timeout("smtp-out", s->id); +} diff --git a/smtpd/newaliases.8 b/usr.sbin/smtpd/newaliases.8 index b82e4515..b82e4515 100644 --- a/smtpd/newaliases.8 +++ b/usr.sbin/smtpd/newaliases.8 diff --git a/smtpd/parse.y b/usr.sbin/smtpd/parse.y index 2fff100e..a82f8206 100644 --- a/smtpd/parse.y +++ b/usr.sbin/smtpd/parse.y @@ -1,4 +1,4 @@ -/* $OpenBSD: parse.y,v 1.263 2019/09/22 11:49:53 semarie Exp $ */ +/* $OpenBSD: parse.y,v 1.281 2020/09/23 19:11:50 martijn Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> @@ -22,7 +22,10 @@ */ %{ +#include "includes.h" + #include <sys/types.h> +#include <sys/time.h> #include <sys/queue.h> #include <sys/tree.h> #include <sys/socket.h> @@ -43,13 +46,16 @@ #include <limits.h> #include <netdb.h> #include <pwd.h> +#include <stdarg.h> #include <resolv.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <syslog.h> #include <unistd.h> +#ifdef HAVE_UTIL_H #include <util.h> +#endif #include <openssl/ssl.h> @@ -105,7 +111,7 @@ static struct ca *sca; struct dispatcher *dispatcher; struct rule *rule; -struct processor *processor; +struct filter_proc *processor; struct filter_config *filter_config; static uint32_t last_dynchain_id = 1; @@ -173,8 +179,8 @@ typedef struct { %} -%token ACTION ALIAS ANY ARROW AUTH AUTH_OPTIONAL -%token BACKUP BOUNCE +%token ACTION ADMD ALIAS ANY ARROW AUTH AUTH_OPTIONAL +%token BACKUP BOUNCE BYPASS %token CA CERT CHAIN CHROOT CIPHERS COMMIT COMPRESSION CONNECT %token DATA DATA_LINE DHE DISCONNECT DOMAIN %token EHLO ENABLE ENCRYPTION ERROR EXPAND_ONLY @@ -209,6 +215,7 @@ grammar : /* empty */ | grammar include '\n' | grammar varset '\n' | grammar bounce '\n' + | grammar admd '\n' | grammar ca '\n' | grammar mda '\n' | grammar mta '\n' @@ -310,6 +317,21 @@ BOUNCE WARN_INTERVAL { ; +admd: +ADMD STRING { + size_t i; + + for (i = 0; $2[i] != '\0'; i++) { + if (!isprint($2[i])) { + yyerror("not a valid admd"); + free($2); + YYERROR; + } + } + conf->sc_admd = $2; +}; + + ca: CA STRING { char buf[HOST_NAME_MAX+1]; @@ -434,7 +456,7 @@ pki_params_opt pki_params proc: PROC STRING STRING { - if (dict_get(conf->sc_processors_dict, $2)) { + if (dict_get(conf->sc_filter_processes_dict, $2)) { yyerror("processor already exists with that name: %s", $2); free($2); free($3); @@ -443,7 +465,7 @@ PROC STRING STRING { processor = xcalloc(1, sizeof *processor); processor->command = $3; } proc_params { - dict_set(conf->sc_processors_dict, $2, processor); + dict_set(conf->sc_filter_processes_dict, $2, processor); processor = NULL; } ; @@ -529,7 +551,7 @@ SMTP LIMIT limits_smtp free($3); YYERROR; } - if (isspace((int)*$3) || !isprint((int)*$3) || *$3== '@') { + if (isspace((unsigned char)*$3) || !isprint((unsigned char)*$3) || *$3 == '@') { yyerror("sub-addr-delim uses invalid character"); free($3); YYERROR; @@ -566,7 +588,7 @@ SRS KEY STRING { dispatcher_local_option: USER STRING { - if (dispatcher->u.local.requires_root) { + if (dispatcher->u.local.is_mbox) { yyerror("user may not be specified for this dispatcher"); YYERROR; } @@ -662,43 +684,42 @@ dispatcher_local_option dispatcher_local_options dispatcher_local: MBOX { - dispatcher->u.local.requires_root = 1; - dispatcher->u.local.user = xstrdup("root"); - asprintf(&dispatcher->u.local.command, "/usr/libexec/mail.local -f %%{mbox.from} %%{user.username}"); + dispatcher->u.local.is_mbox = 1; + asprintf(&dispatcher->u.local.command, PATH_LIBEXEC"/mail.local -f %%{mbox.from} -- %%{user.username}"); } dispatcher_local_options | MAILDIR { - asprintf(&dispatcher->u.local.command, "/usr/libexec/mail.maildir"); + asprintf(&dispatcher->u.local.command, PATH_LIBEXEC"/mail.maildir"); } dispatcher_local_options | MAILDIR JUNK { - asprintf(&dispatcher->u.local.command, "/usr/libexec/mail.maildir -j"); + asprintf(&dispatcher->u.local.command, PATH_LIBEXEC"/mail.maildir -j"); } dispatcher_local_options | MAILDIR STRING { if (strncmp($2, "~/", 2) == 0) asprintf(&dispatcher->u.local.command, - "/usr/libexec/mail.maildir \"%%{user.directory}/%s\"", $2+2); + PATH_LIBEXEC"/mail.maildir \"%%{user.directory}/%s\"", $2+2); else asprintf(&dispatcher->u.local.command, - "/usr/libexec/mail.maildir \"%s\"", $2); + PATH_LIBEXEC"/mail.maildir \"%s\"", $2); } dispatcher_local_options | MAILDIR STRING JUNK { if (strncmp($2, "~/", 2) == 0) asprintf(&dispatcher->u.local.command, - "/usr/libexec/mail.maildir -j \"%%{user.directory}/%s\"", $2+2); + PATH_LIBEXEC"/mail.maildir -j \"%%{user.directory}/%s\"", $2+2); else asprintf(&dispatcher->u.local.command, - "/usr/libexec/mail.maildir -j \"%s\"", $2); + PATH_LIBEXEC"/mail.maildir -j \"%s\"", $2); } dispatcher_local_options | LMTP STRING { asprintf(&dispatcher->u.local.command, - "/usr/libexec/mail.lmtp -f \"%%{sender}\" -d %s %%{user.username}", $2); + PATH_LIBEXEC"/mail.lmtp -d \"%s\" -u", $2); } dispatcher_local_options | LMTP STRING RCPT_TO { asprintf(&dispatcher->u.local.command, - "/usr/libexec/mail.lmtp -f \"%%{sender}\" -d %s %%{dest}", $2); + PATH_LIBEXEC"/mail.lmtp -d \"%s\" -r", $2); } dispatcher_local_options | MDA STRING { asprintf(&dispatcher->u.local.command, - "/usr/libexec/mail.mda \"%s\"", $2); + PATH_LIBEXEC"/mail.mda \"%s\"", $2); } dispatcher_local_options | FORWARD_ONLY { dispatcher->u.local.forward_only = 1; @@ -818,6 +839,27 @@ HELO STRING { dispatcher->u.remote.smarthost = strdup(t->t_name); } +| DOMAIN tables { + struct table *t = $2; + + if (dispatcher->u.remote.smarthost) { + yyerror("host mapping already specified for this dispatcher"); + YYERROR; + } + if (dispatcher->u.remote.backup) { + yyerror("backup and domain are mutually exclusive"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_HASH, K_RELAYHOST)) { + yyerror("table \"%s\" may not be used for host lookups", + t->t_name); + YYERROR; + } + + dispatcher->u.remote.smarthost = strdup(t->t_name); + dispatcher->u.remote.smarthost_domain = 1; +} | TLS { if (dispatcher->u.remote.tls_required == 1) { yyerror("tls already specified for this dispatcher"); @@ -856,6 +898,45 @@ HELO STRING { dispatcher->u.remote.auth = strdup(t->t_name); } +| FILTER STRING { + struct filter_config *fc; + + if (dispatcher->u.remote.filtername) { + yyerror("filter already specified for this dispatcher"); + YYERROR; + } + + if ((fc = dict_get(conf->sc_filters_dict, $2)) == NULL) { + yyerror("no filter exist with that name: %s", $2); + free($2); + YYERROR; + } + fc->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_OUT; + dispatcher->u.remote.filtername = $2; +} +| FILTER { + char buffer[128]; + char *filtername; + + if (dispatcher->u.remote.filtername) { + yyerror("filter already specified for this dispatcher"); + YYERROR; + } + + do { + (void)snprintf(buffer, sizeof buffer, "<dynchain:%08x>", last_dynchain_id++); + } while (dict_check(conf->sc_filters_dict, buffer)); + + filtername = xstrdup(buffer); + filter_config = xcalloc(1, sizeof *filter_config); + filter_config->filter_type = FILTER_TYPE_CHAIN; + filter_config->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_OUT; + dict_init(&filter_config->chain_procs); + dispatcher->u.remote.filtername = filtername; +} '{' filter_list '}' { + dict_set(conf->sc_filters_dict, dispatcher->u.remote.filtername, filter_config); + filter_config = NULL; +} | SRS { if (conf->sc_srs_key == NULL) { yyerror("an srs key is required for srs to be specified in an action"); @@ -1021,7 +1102,7 @@ negation TAG REGEX tables { YYERROR; } - if (!table_check_use(t, T_DYNAMIC|T_LIST, K_CREDENTIALS)) { + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_STRING|K_CREDENTIALS)) { yyerror("table \"%s\" may not be used for auth lookups", t->t_name); YYERROR; @@ -1228,6 +1309,101 @@ negation TAG REGEX tables { rule->table_from = strdup(t->t_name); } +| negation FROM AUTH { + struct table *anyhost = table_find(conf, "<anyhost>"); + + if (rule->flag_from) { + yyerror("from already specified for this rule"); + YYERROR; + } + + rule->flag_from = 1; + rule->table_from = strdup(anyhost->t_name); + rule->flag_smtp_auth = $1 ? -1 : 1; +} +| negation FROM AUTH tables { + struct table *anyhost = table_find(conf, "<anyhost>"); + struct table *t = $4; + + if (rule->flag_from) { + yyerror("from already specified for this rule"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_STRING|K_CREDENTIALS)) { + yyerror("table \"%s\" may not be used for from lookups", + t->t_name); + YYERROR; + } + + rule->flag_from = 1; + rule->table_from = strdup(anyhost->t_name); + rule->flag_smtp_auth = $1 ? -1 : 1; + rule->table_smtp_auth = strdup(t->t_name); +} +| negation FROM AUTH REGEX tables { + struct table *anyhost = table_find(conf, "<anyhost>"); + struct table *t = $5; + + if (rule->flag_from) { + yyerror("from already specified for this rule"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) { + yyerror("table \"%s\" may not be used for from lookups", + t->t_name); + YYERROR; + } + + rule->flag_from = 1; + rule->table_from = strdup(anyhost->t_name); + rule->flag_smtp_auth = $1 ? -1 : 1; + rule->flag_smtp_auth_regex = 1; + rule->table_smtp_auth = strdup(t->t_name); +} + +| negation FROM MAIL_FROM tables { + struct table *anyhost = table_find(conf, "<anyhost>"); + struct table *t = $4; + + if (rule->flag_from) { + yyerror("from already specified for this rule"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) { + yyerror("table \"%s\" may not be used for from lookups", + t->t_name); + YYERROR; + } + + rule->flag_from = 1; + rule->table_from = strdup(anyhost->t_name); + rule->flag_smtp_mail_from = $1 ? -1 : 1; + rule->table_smtp_mail_from = strdup(t->t_name); +} +| negation FROM MAIL_FROM REGEX tables { + struct table *anyhost = table_find(conf, "<anyhost>"); + struct table *t = $5; + + if (rule->flag_from) { + yyerror("from already specified for this rule"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) { + yyerror("table \"%s\" may not be used for from lookups", + t->t_name); + YYERROR; + } + + rule->flag_from = 1; + rule->table_from = strdup(anyhost->t_name); + rule->flag_smtp_mail_from = $1 ? -1 : 1; + rule->flag_smtp_mail_from_regex = 1; + rule->table_smtp_mail_from = strdup(t->t_name); +} | negation FOR LOCAL { struct table *t = table_find(conf, "<localnames>"); @@ -1284,6 +1460,47 @@ negation TAG REGEX tables { rule->flag_for_regex = 1; rule->table_for = strdup(t->t_name); } +| negation FOR RCPT_TO tables { + struct table *anyhost = table_find(conf, "<anydestination>"); + struct table *t = $4; + + if (rule->flag_for) { + yyerror("for already specified for this rule"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) { + yyerror("table \"%s\" may not be used for for lookups", + t->t_name); + YYERROR; + } + + rule->flag_for = 1; + rule->table_for = strdup(anyhost->t_name); + rule->flag_smtp_rcpt_to = $1 ? -1 : 1; + rule->table_smtp_rcpt_to = strdup(t->t_name); +} +| negation FOR RCPT_TO REGEX tables { + struct table *anyhost = table_find(conf, "<anydestination>"); + struct table *t = $5; + + if (rule->flag_for) { + yyerror("for already specified for this rule"); + YYERROR; + } + + if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) { + yyerror("table \"%s\" may not be used for for lookups", + t->t_name); + YYERROR; + } + + rule->flag_for = 1; + rule->table_for = strdup(anyhost->t_name); + rule->flag_smtp_rcpt_to = $1 ? -1 : 1; + rule->flag_smtp_rcpt_to_regex = 1; + rule->table_smtp_rcpt_to = strdup(t->t_name); +} ; match_options: @@ -1330,6 +1547,9 @@ filter_action_builtin_nojunk | JUNK { filter_config->junk = 1; } +| BYPASS { + filter_config->bypass = 1; +} ; filter_action_builtin_nojunk: @@ -1400,6 +1620,25 @@ negation HELO REGEX tables { } ; +filter_phase_check_auth: +negation AUTH { + filter_config->not_auth = $1 ? -1 : 1; + filter_config->auth = 1; +} +; +filter_phase_check_auth_table: +negation AUTH tables { + filter_config->not_auth_table = $1 ? -1 : 1; + filter_config->auth_table = $3; +} +; +filter_phase_check_auth_regex: +negation AUTH REGEX tables { + filter_config->not_auth_regex = $1 ? -1 : 1; + filter_config->auth_regex = $4; +} +; + filter_phase_check_mail_from_table: negation MAIL_FROM tables { filter_config->not_mail_from_table = $1 ? -1 : 1; @@ -1442,9 +1681,20 @@ filter_phase_check_helo_table | filter_phase_check_helo_regex | filter_phase_global_options; +filter_phase_auth_options: +filter_phase_check_helo_table | +filter_phase_check_helo_regex | +filter_phase_check_auth | +filter_phase_check_auth_table | +filter_phase_check_auth_regex | +filter_phase_global_options; + filter_phase_mail_from_options: filter_phase_check_helo_table | filter_phase_check_helo_regex | +filter_phase_check_auth | +filter_phase_check_auth_table | +filter_phase_check_auth_regex | filter_phase_check_mail_from_table | filter_phase_check_mail_from_regex | filter_phase_global_options; @@ -1452,6 +1702,9 @@ filter_phase_global_options; filter_phase_rcpt_to_options: filter_phase_check_helo_table | filter_phase_check_helo_regex | +filter_phase_check_auth | +filter_phase_check_auth_table | +filter_phase_check_auth_regex | filter_phase_check_mail_from_table | filter_phase_check_mail_from_regex | filter_phase_check_rcpt_to_table | @@ -1461,6 +1714,9 @@ filter_phase_global_options; filter_phase_data_options: filter_phase_check_helo_table | filter_phase_check_helo_regex | +filter_phase_check_auth | +filter_phase_check_auth_table | +filter_phase_check_auth_regex | filter_phase_check_mail_from_table | filter_phase_check_mail_from_regex | filter_phase_global_options; @@ -1485,6 +1741,9 @@ filter_phase_global_options; filter_phase_commit_options: filter_phase_check_helo_table | filter_phase_check_helo_regex | +filter_phase_check_auth | +filter_phase_check_auth_table | +filter_phase_check_auth_regex | filter_phase_check_mail_from_table | filter_phase_check_mail_from_regex | filter_phase_global_options; @@ -1509,6 +1768,11 @@ EHLO { } MATCH filter_phase_helo_options filter_action_builtin ; +filter_phase_auth: +AUTH { +} MATCH filter_phase_auth_options filter_action_builtin +; + filter_phase_mail_from: MAIL_FROM { filter_config->phase = FILTER_MAIL_FROM; @@ -1565,6 +1829,7 @@ filter_phase: filter_phase_connect | filter_phase_helo | filter_phase_ehlo +| filter_phase_auth | filter_phase_mail_from | filter_phase_rcpt_to | filter_phase_data @@ -1579,6 +1844,7 @@ filter_phase_connect filterel: STRING { struct filter_config *fr; + struct filter_proc *fp; size_t i; if ((fr = dict_get(conf->sc_filters_dict, $1)) == NULL) { @@ -1601,7 +1867,7 @@ STRING { } if (fr->proc) { - if (dict_check(&filter_config->chain_procs, fr->proc)) { + if ((fp = dict_get(&filter_config->chain_procs, fr->proc))) { yyerror("no proc allowed twice within a filter chain: %s", fr->proc); free($1); YYERROR; @@ -1609,6 +1875,7 @@ STRING { dict_set(&filter_config->chain_procs, fr->proc, NULL); } + fr->filter_subsystem |= filter_config->filter_subsystem; filter_config->chain_size += 1; filter_config->chain = reallocarray(filter_config->chain, filter_config->chain_size, sizeof(char *)); if (filter_config->chain == NULL) @@ -1624,13 +1891,15 @@ filterel filter: FILTER STRING PROC STRING { + struct filter_proc *fp; + if (dict_get(conf->sc_filters_dict, $2)) { yyerror("filter already exists with that name: %s", $2); free($2); free($4); YYERROR; } - if (! dict_get(conf->sc_processors_dict, $4)) { + if ((fp = dict_get(conf->sc_filter_processes_dict, $4)) == NULL) { yyerror("no processor exist with that name: %s", $4); free($4); YYERROR; @@ -1661,7 +1930,7 @@ FILTER STRING PROC_EXEC STRING { filter_config->proc = xstrdup($2); dict_set(conf->sc_filters_dict, $2, filter_config); } proc_params { - dict_set(conf->sc_processors_dict, filter_config->proc, processor); + dict_set(conf->sc_filter_processes_dict, filter_config->proc, processor); processor = NULL; filter_config = NULL; } @@ -1836,16 +2105,19 @@ limits_scheduler: opt_limit_scheduler limits_scheduler opt_sock_listen : FILTER STRING { + struct filter_config *fc; + if (listen_opts.options & LO_FILTER) { yyerror("filter already specified"); free($2); YYERROR; } - if (dict_get(conf->sc_filters_dict, $2) == NULL) { + if ((fc = dict_get(conf->sc_filters_dict, $2)) == NULL) { yyerror("no filter exist with that name: %s", $2); free($2); YYERROR; } + fc->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_IN; listen_opts.options |= LO_FILTER; listen_opts.filtername = $2; } @@ -1865,6 +2137,7 @@ opt_sock_listen : FILTER STRING { listen_opts.filtername = xstrdup(buffer); filter_config = xcalloc(1, sizeof *filter_config); filter_config->filter_type = FILTER_TYPE_CHAIN; + filter_config->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_IN; dict_init(&filter_config->chain_procs); } '{' filter_list '}' { dict_set(conf->sc_filters_dict, listen_opts.filtername, filter_config); @@ -1875,6 +2148,20 @@ opt_sock_listen : FILTER STRING { YYERROR; } } + | TAG STRING { + if (listen_opts.options & LO_TAG) { + yyerror("tag already specified"); + YYERROR; + } + listen_opts.options |= LO_TAG; + + if (strlen($2) >= SMTPD_TAG_SIZE) { + yyerror("tag name too long"); + free($2); + YYERROR; + } + listen_opts.tag = $2; + } ; opt_if_listen : INET4 { @@ -1957,15 +2244,18 @@ opt_if_listen : INET4 { listen_opts.port = $2; } | FILTER STRING { + struct filter_config *fc; + if (listen_opts.options & LO_FILTER) { yyerror("filter already specified"); YYERROR; } - if (dict_get(conf->sc_filters_dict, $2) == NULL) { + if ((fc = dict_get(conf->sc_filters_dict, $2)) == NULL) { yyerror("no filter exist with that name: %s", $2); free($2); YYERROR; } + fc->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_IN; listen_opts.options |= LO_FILTER; listen_opts.filtername = $2; } @@ -1985,6 +2275,7 @@ opt_if_listen : INET4 { listen_opts.filtername = xstrdup(buffer); filter_config = xcalloc(1, sizeof *filter_config); filter_config->filter_type = FILTER_TYPE_CHAIN; + filter_config->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_IN; dict_init(&filter_config->chain_procs); } '{' filter_list '}' { dict_set(conf->sc_filters_dict, listen_opts.filtername, filter_config); @@ -2332,12 +2623,14 @@ lookup(char *s) /* this has to be sorted always */ static const struct keywords keywords[] = { { "action", ACTION }, + { "admd", ADMD }, { "alias", ALIAS }, { "any", ANY }, { "auth", AUTH }, { "auth-optional", AUTH_OPTIONAL }, { "backup", BACKUP }, { "bounce", BOUNCE }, + { "bypass", BYPASS }, { "ca", CA }, { "cert", CERT }, { "chain", CHAIN }, @@ -2915,10 +3208,11 @@ static void create_sock_listener(struct listen_opts *lo) { struct listener *l = xcalloc(1, sizeof(*l)); - lo->tag = "local"; lo->hostname = conf->sc_hostname; l->ss.ss_family = AF_LOCAL; +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN l->ss.ss_len = sizeof(struct sockaddr *); +#endif l->local = 1; conf->sc_sock_listener = l; config_listener(l, lo); @@ -3050,7 +3344,9 @@ host_v4(struct listen_opts *lo) h = xcalloc(1, sizeof(*h)); sain = (struct sockaddr_in *)&h->ss; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sain->sin_len = sizeof(struct sockaddr_in); +#endif sain->sin_family = AF_INET; sain->sin_addr.s_addr = ina.s_addr; sain->sin_port = lo->port; @@ -3078,7 +3374,9 @@ host_v6(struct listen_opts *lo) h = xcalloc(1, sizeof(*h)); sin6 = (struct sockaddr_in6 *)&h->ss; +#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN sin6->sin6_len = sizeof(struct sockaddr_in6); +#endif sin6->sin6_family = AF_INET6; sin6->sin6_port = lo->port; memcpy(&sin6->sin6_addr, &ina6, sizeof(ina6)); @@ -3121,7 +3419,9 @@ host_dns(struct listen_opts *lo) h->ss.ss_family = res->ai_family; if (res->ai_family == AF_INET) { sain = (struct sockaddr_in *)&h->ss; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sain->sin_len = sizeof(struct sockaddr_in); +#endif sain->sin_addr.s_addr = ((struct sockaddr_in *) res->ai_addr)->sin_addr.s_addr; sain->sin_port = lo->port; @@ -3129,7 +3429,9 @@ host_dns(struct listen_opts *lo) h->local = 1; } else { sin6 = (struct sockaddr_in6 *)&h->ss; +#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN sin6->sin6_len = sizeof(struct sockaddr_in6); +#endif memcpy(&sin6->sin6_addr, &((struct sockaddr_in6 *) res->ai_addr)->sin6_addr, sizeof(struct in6_addr)); sin6->sin6_port = lo->port; @@ -3173,7 +3475,9 @@ interface(struct listen_opts *lo) case AF_INET: sain = (struct sockaddr_in *)&h->ss; *sain = *(struct sockaddr_in *)p->ifa_addr; +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sain->sin_len = sizeof(struct sockaddr_in); +#endif sain->sin_port = lo->port; if (sain->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) h->local = 1; @@ -3182,7 +3486,9 @@ interface(struct listen_opts *lo) case AF_INET6: sin6 = (struct sockaddr_in6 *)&h->ss; *sin6 = *(struct sockaddr_in6 *)p->ifa_addr; +#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN sin6->sin6_len = sizeof(struct sockaddr_in6); +#endif sin6->sin6_port = lo->port; if (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) h->local = 1; @@ -3251,6 +3557,7 @@ bad: int is_if_in_group(const char *ifname, const char *groupname) { +#ifdef HAVE_STRUCT_IFGROUPREQ unsigned int len; struct ifgroupreq ifgr; struct ifg_req *ifg; @@ -3289,6 +3596,9 @@ is_if_in_group(const char *ifname, const char *groupname) end: close(s); return ret; +#else + return (0); +#endif } static int diff --git a/smtpd/parser.c b/usr.sbin/smtpd/parser.c index df90e508..4c2321ec 100644 --- a/smtpd/parser.c +++ b/usr.sbin/smtpd/parser.c @@ -1,4 +1,4 @@ -/* $OpenBSD: parser.c,v 1.41 2017/07/31 16:38:33 gilles Exp $ */ +/* $OpenBSD: parser.c,v 1.42 2020/01/06 11:02:38 gilles Exp $ */ /* * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/socket.h> @@ -219,6 +221,11 @@ cmd_run(int argc, char **argv) return (node->cmd(np, np ? param : NULL)); fail: + if (TAILQ_FIRST(&node->children) == NULL) { + fprintf(stderr, "invalid command\n"); + return (-1); + } + fprintf(stderr, "possibilities are:\n"); TAILQ_FOREACH(tmp, &node->children, entry) { for (j = 0; j < i; j++) @@ -281,7 +288,9 @@ text_to_sockaddr(struct sockaddr *sa, int family, const char *str) in = (struct sockaddr_in *)sa; memset(in, 0, sizeof *in); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN in->sin_len = sizeof(struct sockaddr_in); +#endif in->sin_family = PF_INET; in->sin_addr.s_addr = ina.s_addr; return (0); @@ -304,7 +313,9 @@ text_to_sockaddr(struct sockaddr *sa, int family, const char *str) in6 = (struct sockaddr_in6 *)sa; memset(in6, 0, sizeof *in6); +#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN in6->sin6_len = sizeof(struct sockaddr_in6); +#endif in6->sin6_family = PF_INET6; in6->sin6_addr = in6a; @@ -313,7 +324,7 @@ text_to_sockaddr(struct sockaddr *sa, int family, const char *str) if (IN6_IS_ADDR_LINKLOCAL(&in6a) || IN6_IS_ADDR_MC_LINKLOCAL(&in6a) || - IN6_IS_ADDR_MC_INTFACELOCAL(&in6a)) + IN6_IS_ADDR_MC_NODELOCAL(&in6a)) if ((in6->sin6_scope_id = if_nametoindex(cp))) return (0); diff --git a/smtpd/parser.h b/usr.sbin/smtpd/parser.h index f0114e9e..f0114e9e 100644 --- a/smtpd/parser.h +++ b/usr.sbin/smtpd/parser.h diff --git a/smtpd/pony.c b/usr.sbin/smtpd/pony.c index 026d6473..1865b339 100644 --- a/smtpd/pony.c +++ b/usr.sbin/smtpd/pony.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -198,8 +200,10 @@ pony(void) ca_engine_init(); +#if HAVE_PLEDGE if (pledge("stdio inet unix recvfd sendfd", NULL) == -1) err(1, "pledge"); +#endif event_dispatch(); fatalx("exited event loop"); diff --git a/smtpd/proxy.c b/usr.sbin/smtpd/proxy.c index 7bf976d8..fdaf4f27 100644 --- a/smtpd/proxy.c +++ b/usr.sbin/smtpd/proxy.c @@ -14,6 +14,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/socket.h> #include <sys/tree.h> diff --git a/smtpd/queue.c b/usr.sbin/smtpd/queue.c index 8380c7b5..434e3647 100644 --- a/smtpd/queue.c +++ b/usr.sbin/smtpd/queue.c @@ -1,4 +1,4 @@ -/* $OpenBSD: queue.c,v 1.189 2018/12/30 23:09:58 guenther Exp $ */ +/* $OpenBSD: queue.c,v 1.190 2020/04/22 11:35:34 eric Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> @@ -18,6 +18,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -26,6 +28,8 @@ #include <err.h> #include <event.h> +#include <fcntl.h> +#include <grp.h> /* needed for setgroups */ #include <imsg.h> #include <inttypes.h> #include <pwd.h> @@ -673,8 +677,10 @@ queue(void) tv.tv_usec = 10; evtimer_add(&ev_qload, &tv); +#if HAVE_PLEDGE if (pledge("stdio rpath wpath cpath flock recvfd sendfd", NULL) == -1) err(1, "pledge"); +#endif event_dispatch(); fatalx("exited event loop"); @@ -686,7 +692,6 @@ static void queue_timeout(int fd, short event, void *p) { static uint32_t msgid = 0; - struct dispatcher *dsp; struct envelope evp; struct event *ev = p; struct timeval tv; @@ -705,13 +710,6 @@ queue_timeout(int fd, short event, void *p) } if (r) { - dsp = dict_get(env->sc_dispatchers, evp.dispatcher); - if (dsp == NULL) { - log_warnx("warn: queue: missing dispatcher \"%s\"" - " for envelope %016"PRIx64", ignoring", - evp.dispatcher, evp.id); - goto reset; - } if (msgid && evpid_to_msgid(evp.id) != msgid) { m_create(p_scheduler, IMSG_QUEUE_MESSAGE_COMMIT, 0, 0, -1); @@ -724,7 +722,6 @@ queue_timeout(int fd, short event, void *p) m_close(p_scheduler); } -reset: tv.tv_sec = 0; tv.tv_usec = 10; evtimer_add(ev, &tv); diff --git a/smtpd/queue_backend.c b/usr.sbin/smtpd/queue_backend.c index 5e7c38fa..fa945f47 100644 --- a/smtpd/queue_backend.c +++ b/usr.sbin/smtpd/queue_backend.c @@ -1,4 +1,4 @@ -/* $OpenBSD: queue_backend.c,v 1.65 2018/12/30 23:09:58 guenther Exp $ */ +/* $OpenBSD: queue_backend.c,v 1.66 2020/04/22 11:35:34 eric Exp $ */ /* * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org> @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -730,6 +732,9 @@ envelope_validate(struct envelope *ep) if (memchr(ep->errorline, '\0', sizeof(ep->errorline)) == NULL) return "invalid error line"; + if (dict_get(env->sc_dispatchers, ep->dispatcher) == NULL) + return "unknown dispatcher"; + return NULL; } diff --git a/smtpd/queue_fs.c b/usr.sbin/smtpd/queue_fs.c index d1cd4800..097ba1e2 100644 --- a/smtpd/queue_fs.c +++ b/usr.sbin/smtpd/queue_fs.c @@ -1,4 +1,4 @@ -/* $OpenBSD: queue_fs.c,v 1.19 2019/06/28 13:32:51 deraadt Exp $ */ +/* $OpenBSD: queue_fs.c,v 1.20 2020/02/25 17:03:13 millert Exp $ */ /* * Copyright (c) 2011 Gilles Chehade <gilles@poolp.org> @@ -16,12 +16,19 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> +#if HAVE_SYS_MOUNT_H #include <sys/mount.h> +#endif #include <sys/queue.h> #include <sys/tree.h> #include <sys/socket.h> #include <sys/stat.h> +#ifdef HAVE_SYS_STATFS_H +#include <sys/statfs.h> +#endif #include <ctype.h> #include <dirent.h> @@ -228,8 +235,7 @@ queue_fs_envelope_create(uint32_t msgid, const char *buf, size_t len, fsqueue_envelope_incoming_path(*evpid, path, sizeof(path)); - r = fsqueue_envelope_dump(path, buf, len, 0, 0); - if (r >= 0) + if ((r = fsqueue_envelope_dump(path, buf, len, 0, 0)) != 0) goto done; } r = 0; @@ -340,8 +346,10 @@ queue_fs_message_walk(uint64_t *evpid, char *buf, size_t len, (void)snprintf(msgid_str, sizeof msgid_str, "%08" PRIx32, msgid); while ((dp = readdir(dir)) != NULL) { +#if defined(HAVE_STRUCT_DIR_D_TYPE) if (dp->d_type != DT_REG) continue; +#endif /* ignore files other than envelopes */ if (strlen(dp->d_name) != 16 || @@ -410,6 +418,7 @@ queue_fs_envelope_walk(uint64_t *evpid, char *buf, size_t len) static int fsqueue_check_space(void) { +#ifdef __OpenBSD__ struct statfs buf; uint64_t used; uint64_t total; @@ -453,7 +462,7 @@ fsqueue_check_space(void) log_warnx("warn: temporarily rejecting messages"); return 0; } - +#endif return 1; } @@ -616,7 +625,12 @@ fsqueue_qwalk(void *hdl, uint64_t *evpid) break; if (e->fts_namelen != 16) break; +#if HAVE_STRUCT_STAT_ST_MTIM if (timespeccmp(&e->fts_statp->st_mtim, &startup, >)) +#endif +#if HAVE_STRUCT_STAT_ST_MTIMSPEC + if (timespeccmp(&e->fts_statp->st_mtimspec, &startup, >)) +#endif break; tmp = NULL; *evpid = strtoull(e->fts_name, &tmp, 16); diff --git a/smtpd/queue_null.c b/usr.sbin/smtpd/queue_null.c index d92f98a7..1e608be8 100644 --- a/smtpd/queue_null.c +++ b/usr.sbin/smtpd/queue_null.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> diff --git a/smtpd/queue_proc.c b/usr.sbin/smtpd/queue_proc.c index 753271c1..d6e0f409 100644 --- a/smtpd/queue_proc.c +++ b/usr.sbin/smtpd/queue_proc.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> diff --git a/smtpd/queue_ram.c b/usr.sbin/smtpd/queue_ram.c index 56b9fa5b..50ce17e1 100644 --- a/smtpd/queue_ram.c +++ b/usr.sbin/smtpd/queue_ram.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> diff --git a/smtpd/report_smtp.c b/usr.sbin/smtpd/report_smtp.c index ab17901f..7802eaae 100644 --- a/smtpd/report_smtp.c +++ b/usr.sbin/smtpd/report_smtp.c @@ -1,4 +1,4 @@ -/* $OpenBSD: report_smtp.c,v 1.10 2019/09/19 14:40:53 gilles Exp $ */ +/* $OpenBSD: report_smtp.c,v 1.11 2020/01/07 23:03:37 gilles Exp $ */ /* * Copyright (c) 2018 Gilles Chehade <gilles@poolp.org> @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -36,7 +38,11 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#if defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) #include <vis.h> +#else +#include "bsd-vis.h" +#endif #include "smtpd.h" #include "log.h" @@ -174,15 +180,6 @@ void report_smtp_tx_mail(const char *direction, uint64_t qid, uint32_t msgid, const char *address, int ok) { struct timeval tv; - char buffer[SMTPD_MAXMAILADDRSIZE]; - char *p; - - if ((p = strchr(address, '<')) == NULL) - return; - (void)strlcpy(buffer, p + 1, sizeof buffer); - if ((p = strchr(buffer, '>')) == NULL) - return; - *p = '\0'; gettimeofday(&tv, NULL); @@ -191,7 +188,7 @@ report_smtp_tx_mail(const char *direction, uint64_t qid, uint32_t msgid, const c m_add_timeval(p_lka, &tv); m_add_id(p_lka, qid); m_add_u32(p_lka, msgid); - m_add_string(p_lka, buffer); + m_add_string(p_lka, address); m_add_int(p_lka, ok); m_close(p_lka); } @@ -200,15 +197,6 @@ void report_smtp_tx_rcpt(const char *direction, uint64_t qid, uint32_t msgid, const char *address, int ok) { struct timeval tv; - char buffer[SMTPD_MAXMAILADDRSIZE]; - char *p; - - if ((p = strchr(address, '<')) == NULL) - return; - (void)strlcpy(buffer, p + 1, sizeof buffer); - if ((p = strchr(buffer, '>')) == NULL) - return; - *p = '\0'; gettimeofday(&tv, NULL); @@ -217,7 +205,7 @@ report_smtp_tx_rcpt(const char *direction, uint64_t qid, uint32_t msgid, const c m_add_timeval(p_lka, &tv); m_add_id(p_lka, qid); m_add_u32(p_lka, msgid); - m_add_string(p_lka, buffer); + m_add_string(p_lka, address); m_add_int(p_lka, ok); m_close(p_lka); } diff --git a/smtpd/resolver.c b/usr.sbin/smtpd/resolver.c index ce047b24..f0f0f8ea 100644 --- a/smtpd/resolver.c +++ b/usr.sbin/smtpd/resolver.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/socket.h> #include <sys/tree.h> @@ -65,6 +67,9 @@ static void resolver_res_query_cb(struct asr_result *, void *); static int request_cmp(struct request *, struct request *); SPLAY_PROTOTYPE(reqtree, request, entry, request_cmp); +/* musl work-around */ +void portable_freeaddrinfo(struct addrinfo *); + static struct reqtree reqs; void @@ -216,7 +221,7 @@ resolver_dispatch_request(struct mproc *proc, struct imsg *imsg) if ((s = calloc(1, sizeof(*s))) && (s->host = malloc(NI_MAXHOST)) && (s->serv = malloc(NI_MAXSERV)) && - (q = getnameinfo_async(sa, sa->sa_len, s->host, NI_MAXHOST, + (q = getnameinfo_async(sa, SA_LEN(sa), s->host, NI_MAXHOST, s->serv, NI_MAXSERV, flags, NULL)) && (event_asr_run(q, resolver_getnameinfo_cb, s))) { s->reqid = reqid; @@ -312,14 +317,14 @@ resolver_dispatch_result(struct mproc *proc, struct imsg *imsg) m_get_string(&m, &cname); m_end(&m); - ai->ai_addr = malloc(ss.ss_len); + ai->ai_addr = malloc(SS_LEN(&ss)); if (ai->ai_addr == NULL) { log_warn("%s: malloc", __func__); free(ai); break; } - memmove(ai->ai_addr, &ss, ss.ss_len); + memmove(ai->ai_addr, &ss, SS_LEN(&ss)); if (cname) { ai->ai_canonname = strdup(cname); @@ -406,7 +411,7 @@ resolver_getaddrinfo_cb(struct asr_result *ar, void *arg) m_close(s->proc); if (ar->ar_addrinfo) - freeaddrinfo(ar->ar_addrinfo); + portable_freeaddrinfo(ar->ar_addrinfo); free(s); } diff --git a/smtpd/rfc5322.c b/usr.sbin/smtpd/rfc5322.c index 02ea7976..0af66772 100644 --- a/smtpd/rfc5322.c +++ b/usr.sbin/smtpd/rfc5322.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <ctype.h> #include <errno.h> #include <limits.h> diff --git a/smtpd/rfc5322.h b/usr.sbin/smtpd/rfc5322.h index 0979bd4c..0979bd4c 100644 --- a/smtpd/rfc5322.h +++ b/usr.sbin/smtpd/rfc5322.h diff --git a/smtpd/ruleset.c b/usr.sbin/smtpd/ruleset.c index db9d1aa5..719a2913 100644 --- a/smtpd/ruleset.c +++ b/usr.sbin/smtpd/ruleset.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ruleset.c,v 1.46 2019/11/12 20:21:46 gilles Exp $ */ +/* $OpenBSD: ruleset.c,v 1.47 2019/11/25 14:18:33 gilles Exp $ */ /* * Copyright (c) 2009 Gilles Chehade <gilles@poolp.org> @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -152,6 +154,8 @@ static int ruleset_match_smtp_auth(struct rule *r, const struct envelope *evp) { int ret; + struct table *table; + enum table_service service; if (!r->flag_smtp_auth) return 1; @@ -159,14 +163,14 @@ ruleset_match_smtp_auth(struct rule *r, const struct envelope *evp) if (!(evp->flags & EF_AUTHENTICATED)) ret = 0; else if (r->table_smtp_auth) { - /* XXX - not until smtp_session->username is added to envelope */ - /* - * table = table_find(m->from_table); - * key = evp->username; - * return table_match(table, K_CREDENTIALS, key); - */ - return -1; + if (r->flag_smtp_auth_regex) + service = K_REGEX; + else + service = strchr(evp->username, '@') ? + K_MAILADDR : K_STRING; + table = table_find(env, r->table_smtp_auth); + ret = table_match(table, service, evp->username); } else ret = 1; diff --git a/smtpd/runq.c b/usr.sbin/smtpd/runq.c index bbb9affa..786d36fb 100644 --- a/smtpd/runq.c +++ b/usr.sbin/smtpd/runq.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/socket.h> #include <sys/queue.h> diff --git a/smtpd/scheduler.c b/usr.sbin/smtpd/scheduler.c index b3bda175..ea70a83d 100644 --- a/smtpd/scheduler.c +++ b/usr.sbin/smtpd/scheduler.c @@ -19,6 +19,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -30,6 +32,7 @@ #include <err.h> #include <errno.h> #include <event.h> +#include <grp.h> /* needed for setgroups */ #include <imsg.h> #include <inttypes.h> #include <pwd.h> @@ -470,8 +473,10 @@ scheduler(void) evtimer_set(&ev, scheduler_timeout, NULL); scheduler_reset_events(); +#if HAVE_PLEDGE if (pledge("stdio", NULL) == -1) err(1, "pledge"); +#endif event_dispatch(); fatalx("exited event loop"); diff --git a/smtpd/scheduler_backend.c b/usr.sbin/smtpd/scheduler_backend.c index 061f1129..ad2b4cab 100644 --- a/smtpd/scheduler_backend.c +++ b/usr.sbin/smtpd/scheduler_backend.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> diff --git a/smtpd/scheduler_null.c b/usr.sbin/smtpd/scheduler_null.c index a8c43331..40db6205 100644 --- a/smtpd/scheduler_null.c +++ b/usr.sbin/smtpd/scheduler_null.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> diff --git a/smtpd/scheduler_proc.c b/usr.sbin/smtpd/scheduler_proc.c index 0f8c44b9..5f4e8b70 100644 --- a/smtpd/scheduler_proc.c +++ b/usr.sbin/smtpd/scheduler_proc.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> diff --git a/smtpd/scheduler_ramqueue.c b/usr.sbin/smtpd/scheduler_ramqueue.c index 8d5efc10..0c04fc0b 100644 --- a/smtpd/scheduler_ramqueue.c +++ b/usr.sbin/smtpd/scheduler_ramqueue.c @@ -17,6 +17,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> diff --git a/smtpd/sendmail.8 b/usr.sbin/smtpd/sendmail.8 index 1696a861..1696a861 100644 --- a/smtpd/sendmail.8 +++ b/usr.sbin/smtpd/sendmail.8 diff --git a/smtpd/smtp.1 b/usr.sbin/smtpd/smtp.1 index 3cc03844..3cc03844 100644 --- a/smtpd/smtp.1 +++ b/usr.sbin/smtpd/smtp.1 diff --git a/smtpd/smtp.c b/usr.sbin/smtpd/smtp.c index 35776c4f..602fd0d6 100644 --- a/smtpd/smtp.c +++ b/usr.sbin/smtpd/smtp.c @@ -18,6 +18,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -26,6 +28,7 @@ #include <err.h> #include <errno.h> #include <event.h> +#include <grp.h> /* needed for setgroups */ #include <imsg.h> #include <netdb.h> #include <pwd.h> @@ -64,6 +67,8 @@ static void smtp_accepted(struct listener *, int, const struct sockaddr_storage #define SMTP_FD_RESERVE 5 +#define getdtablecount() 0 + static size_t sessions; static size_t maxsessions; @@ -146,10 +151,26 @@ smtp_setup_listeners(void) fatal("smtpd: socket"); } opt = 1; +#ifdef SO_REUSEADDR if (setsockopt(l->fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == -1) fatal("smtpd: setsockopt"); - if (bind(l->fd, (struct sockaddr *)&l->ss, l->ss.ss_len) == -1) +#else + if (setsockopt(l->fd, SOL_SOCKET, SO_REUSEPORT, &opt, + sizeof(opt)) < 0) + fatal("smtpd: setsockopt"); +#endif +#ifdef IPV6_V6ONLY + /* + * If using IPv6, bind only to IPv6 if possible. + * This avoids ambiguities with IPv4-mapped IPv6 addresses. + */ + if (l->ss.ss_family == AF_INET6) + if (setsockopt(l->fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, + sizeof(opt)) < 0) + fatal("smtpd: setsockopt"); +#endif + if (bind(l->fd, (struct sockaddr *)&l->ss, SS_LEN(&l->ss)) == -1) fatal("smtpd: bind"); } } diff --git a/smtpd/smtp.h b/usr.sbin/smtpd/smtp.h index dc91d878..dc91d878 100644 --- a/smtpd/smtp.h +++ b/usr.sbin/smtpd/smtp.h diff --git a/smtpd/smtp/Makefile b/usr.sbin/smtpd/smtp/Makefile index 380e3ad6..380e3ad6 100644 --- a/smtpd/smtp/Makefile +++ b/usr.sbin/smtpd/smtp/Makefile diff --git a/smtpd/smtp_client.c b/usr.sbin/smtpd/smtp_client.c index 22e79890..8e146e1b 100644 --- a/smtpd/smtp_client.c +++ b/usr.sbin/smtpd/smtp_client.c @@ -1,4 +1,4 @@ -/* $OpenBSD: smtp_client.c,v 1.12 2019/09/10 12:08:26 eric Exp $ */ +/* $OpenBSD: smtp_client.c,v 1.14 2020/04/24 11:34:07 eric Exp $ */ /* * Copyright (c) 2018 Eric Faurot <eric@openbsd.org> @@ -680,6 +680,10 @@ smtp_client_readline(struct smtp_client *proto) return 0; } + /* Strip trailing '\r' */ + if (len && line[len - 1] == '\r') + line[--len] = '\0'; + log_trace(TRACE_SMTPCLT, "%p: <<< %s", proto, line); /* Validate SMTP */ @@ -779,9 +783,10 @@ smtp_client_replycat(struct smtp_client *proto, const char *line) line += 3; if (line[0]) { line += 1; - if (isdigit((int)line[0]) && line[1] == '.' && - isdigit((int)line[2]) && line[3] == '.' && - isdigit((int)line[4]) && isspace((int)line[5])) + if (isdigit((unsigned char)line[0]) && line[1] == '.' && + isdigit((unsigned char)line[2]) && line[3] == '.' && + isdigit((unsigned char)line[4]) && + isspace((unsigned char)line[5])) line += 5; } } else diff --git a/smtpd/smtp_session.c b/usr.sbin/smtpd/smtp_session.c index c6a6d52d..c103fa19 100644 --- a/smtpd/smtp_session.c +++ b/usr.sbin/smtpd/smtp_session.c @@ -1,4 +1,4 @@ -/* $OpenBSD: smtp_session.c,v 1.415 2019/10/04 08:34:29 gilles Exp $ */ +/* $OpenBSD: smtp_session.c,v 1.427 2020/11/20 20:37:56 jung Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> @@ -19,6 +19,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -39,7 +41,11 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#if defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) #include <vis.h> +#else +#include "bsd-vis.h" +#endif #include "smtpd.h" #include "log.h" @@ -253,6 +259,32 @@ static void smtp_filter_end(struct smtp_session *); static void smtp_filter_data_begin(struct smtp_session *); static void smtp_filter_data_end(struct smtp_session *); +static void smtp_report_link_connect(struct smtp_session *, const char *, int, + const struct sockaddr_storage *, + const struct sockaddr_storage *); +static void smtp_report_link_greeting(struct smtp_session *, const char *); +static void smtp_report_link_identify(struct smtp_session *, const char *, const char *); +static void smtp_report_link_tls(struct smtp_session *, const char *); +static void smtp_report_link_disconnect(struct smtp_session *); +static void smtp_report_link_auth(struct smtp_session *, const char *, const char *); +static void smtp_report_tx_reset(struct smtp_session *, uint32_t); +static void smtp_report_tx_begin(struct smtp_session *, uint32_t); +static void smtp_report_tx_mail(struct smtp_session *, uint32_t, const char *, int); +static void smtp_report_tx_rcpt(struct smtp_session *, uint32_t, const char *, int); +static void smtp_report_tx_envelope(struct smtp_session *, uint32_t, uint64_t); +static void smtp_report_tx_data(struct smtp_session *, uint32_t, int); +static void smtp_report_tx_commit(struct smtp_session *, uint32_t, size_t); +static void smtp_report_tx_rollback(struct smtp_session *, uint32_t); +static void smtp_report_protocol_client(struct smtp_session *, const char *); +static void smtp_report_protocol_server(struct smtp_session *, const char *); +static void smtp_report_filter_response(struct smtp_session *, int, int, const char *); +static void smtp_report_timeout(struct smtp_session *); + + +/* musl work-around */ +void portable_freeaddrinfo(struct addrinfo *); + + static struct { int code; enum filter_phase filter_phase; @@ -430,7 +462,7 @@ header_address_rewrite_buffer(char *buffer, const char *address, size_t len) pos_component_beg = 0; else { for (pos_component_beg = pos_component_end; pos_component_beg >= 0; --pos_component_beg) - if (buffer[pos_component_beg] == ')' || isspace(buffer[pos_component_beg])) + if (buffer[pos_component_beg] == ')' || isspace((unsigned char)buffer[pos_component_beg])) break; pos_component_beg += 1; pos_component_end += 1; @@ -678,7 +710,7 @@ smtp_getaddrinfo_cb(void *arg, int gaierrno, struct addrinfo *ai0) break; } } - freeaddrinfo(ai0); + portable_freeaddrinfo(ai0); } smtp_lookup_servername(s); @@ -689,7 +721,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg) { struct smtp_session *s; struct smtp_rcpt *rcpt; - char user[LOGIN_NAME_MAX]; + char user[SMTPD_MAXMAILADDRSIZE]; char tmp[SMTP_LINE_MAX]; struct msg m; const char *line, *helo; @@ -778,7 +810,6 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg) s->tx->msgid = msgid; s->tx->evp.id = msgid_to_evpid(msgid); s->tx->rcptcount = 0; - report_smtp_tx_begin("smtp-in", s->id, s->tx->msgid); smtp_reply(s, "250 %s Ok", esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); } else { @@ -847,7 +878,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg) m_get_evpid(&m, &evpid); s->tx->evp.id = evpid; s->tx->destcount++; - report_smtp_tx_envelope("smtp-in", s->id, s->tx->msgid, evpid); + smtp_report_tx_envelope(s, s->tx->msgid, evpid); } else s->tx->error = TX_ERROR_ENVELOPE; @@ -894,9 +925,9 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg) m_end(&m); s = tree_xpop(&wait_queue_commit, reqid); if (!success) { - smtp_tx_free(s->tx); smtp_reply(s, "421 %s Temporary failure", esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); + smtp_tx_free(s->tx); smtp_enter_state(s, STATE_QUIT); return; } @@ -904,6 +935,8 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg) smtp_reply(s, "250 %s %08x Message accepted for delivery", esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS), s->tx->msgid); + smtp_report_tx_commit(s, s->tx->msgid, s->tx->odatalen); + smtp_report_tx_reset(s, s->tx->msgid); log_info("%016"PRIx64" smtp message " "msgid=%08x size=%zu nrcpt=%zu proto=%s", @@ -943,7 +976,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg) "result=ok", s->id, user); s->flags |= SF_AUTHENTICATED; - report_smtp_link_auth("smtp-in", s->id, user, "pass"); + smtp_report_link_auth(s, user, "pass"); smtp_reply(s, "235 %s Authentication succeeded", esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); } @@ -952,7 +985,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg) "authentication user=%s " "result=permfail", s->id, user); - report_smtp_link_auth("smtp-in", s->id, user, "fail"); + smtp_report_link_auth(s, user, "fail"); smtp_auth_failure_pause(s); return; } @@ -961,7 +994,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg) "authentication user=%s " "result=tempfail", s->id, user); - report_smtp_link_auth("smtp-in", s->id, user, "error"); + smtp_report_link_auth(s, user, "error"); smtp_reply(s, "421 %s Temporary failure", esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS)); } @@ -993,7 +1026,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg) if (!strncmp(filter_param, "421", 3)) filter_response = FILTER_DISCONNECT; - report_smtp_filter_response("smtp-in", s->id, s->filter_phase, + smtp_report_filter_response(s, s->filter_phase, filter_response, filter_param); smtp_reply(s, "%s", filter_param); @@ -1017,7 +1050,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg) /* fallthrough */ case FILTER_REWRITE: - report_smtp_filter_response("smtp-in", s->id, s->filter_phase, + smtp_report_filter_response(s, s->filter_phase, filter_response, filter_param == s->filter_param ? NULL : filter_param); if (s->filter_phase == FILTER_CONNECT) { @@ -1084,7 +1117,7 @@ smtp_io(struct io *io, int evt, void *arg) log_info("%016"PRIx64" smtp tls ciphers=%s", s->id, ssl_to_text(io_tls(s->io))); - report_smtp_link_tls("smtp-in", s->id, ssl_to_text(io_tls(s->io))); + smtp_report_link_tls(s, ssl_to_text(io_tls(s->io))); s->flags |= SF_SECURE; s->helo[0] = '\0'; @@ -1109,6 +1142,10 @@ smtp_io(struct io *io, int evt, void *arg) if (line == NULL) return; + /* Strip trailing '\r' */ + if (len && line[len - 1] == '\r') + line[--len] = '\0'; + /* Message body */ eom = 0; if (s->state == STATE_BODY) { @@ -1177,7 +1214,7 @@ smtp_io(struct io *io, int evt, void *arg) log_info("%016"PRIx64" smtp disconnected " "reason=timeout", s->id); - report_smtp_timeout("smtp-in", s->id); + smtp_report_timeout(s); smtp_free(s, "timeout"); break; @@ -1212,20 +1249,20 @@ smtp_command(struct smtp_session *s, char *line) * These states are special. */ if (s->state == STATE_AUTH_INIT) { - report_smtp_protocol_client("smtp-in", s->id, "********"); + smtp_report_protocol_client(s, "********"); smtp_rfc4954_auth_plain(s, line); return; } if (s->state == STATE_AUTH_USERNAME || s->state == STATE_AUTH_PASSWORD) { - report_smtp_protocol_client("smtp-in", s->id, "********"); + smtp_report_protocol_client(s, "********"); smtp_rfc4954_auth_login(s, line); return; } if (s->state == STATE_HELO && strncasecmp(line, "AUTH PLAIN ", 11) == 0) - report_smtp_protocol_client("smtp-in", s->id, "AUTH PLAIN ********"); + smtp_report_protocol_client(s, "AUTH PLAIN ********"); else - report_smtp_protocol_client("smtp-in", s->id, line); + smtp_report_protocol_client(s, line); /* @@ -1640,10 +1677,6 @@ smtp_filter_begin(struct smtp_session *s) m_create(p_lka, IMSG_FILTER_SMTP_BEGIN, 0, 0, -1); m_add_id(p_lka, s->id); m_add_string(p_lka, s->listener->filter_name); - m_add_sockaddr(p_lka, (struct sockaddr *)&s->ss); - m_add_sockaddr(p_lka, (struct sockaddr *)&s->listener->ss); - m_add_string(p_lka, s->rdns); - m_add_int(p_lka, s->fcrdns); m_close(p_lka); } @@ -1715,14 +1748,14 @@ smtp_filter_phase(enum filter_phase phase, struct smtp_session *s, const char *p static void smtp_proceed_rset(struct smtp_session *s, const char *args) { + smtp_reply(s, "250 %s Reset state", + esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); + if (s->tx) { if (s->tx->msgid) smtp_tx_rollback(s->tx); smtp_tx_free(s->tx); } - - smtp_reply(s, "250 %s Reset state", - esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS)); } static void @@ -1731,7 +1764,7 @@ smtp_proceed_helo(struct smtp_session *s, const char *args) (void)strlcpy(s->helo, args, sizeof(s->helo)); s->flags &= SF_SECURE | SF_AUTHENTICATED | SF_VERIFIED; - report_smtp_link_identify("smtp-in", s->id, "HELO", s->helo); + smtp_report_link_identify(s, "HELO", s->helo); smtp_enter_state(s, STATE_HELO); @@ -1751,7 +1784,7 @@ smtp_proceed_ehlo(struct smtp_session *s, const char *args) s->flags |= SF_EHLO; s->flags |= SF_8BITMIME; - report_smtp_link_identify("smtp-in", s->id, "EHLO", s->helo); + smtp_report_link_identify(s, "EHLO", s->helo); smtp_enter_state(s, STATE_HELO); smtp_reply(s, "250-%s Hello %s %s%s%s, pleased to meet you", @@ -2038,7 +2071,7 @@ smtp_connected(struct smtp_session *s) smtp_filter_begin(s); - report_smtp_link_connect("smtp-in", s->id, s->rdns, s->fcrdns, &s->ss, + smtp_report_link_connect(s, s->rdns, s->fcrdns, &s->ss, &s->listener->ss); smtp_filter_phase(FILTER_CONNECT, s, ss_to_text(&s->ss)); @@ -2058,7 +2091,7 @@ smtp_send_banner(struct smtp_session *s) { smtp_reply(s, "220 %s ESMTP %s", s->smtpname, SMTPD_NAME); s->banner_sent = 1; - report_smtp_link_greeting("smtp-in", s->id, s->smtpname); + smtp_report_link_greeting(s, s->smtpname); } void @@ -2093,20 +2126,23 @@ smtp_reply(struct smtp_session *s, char *fmt, ...) } log_trace(TRACE_SMTP, "smtp: %p: >>> %s", s, buf); + smtp_report_protocol_server(s, buf); switch (buf[0]) { case '2': if (s->tx) { - if (s->last_cmd == CMD_MAIL_FROM) - report_smtp_tx_mail("smtp-in", s->id, s->tx->msgid, s->cmd + 10, 1); + if (s->last_cmd == CMD_MAIL_FROM) { + smtp_report_tx_begin(s, s->tx->msgid); + smtp_report_tx_mail(s, s->tx->msgid, s->cmd + 10, 1); + } else if (s->last_cmd == CMD_RCPT_TO) - report_smtp_tx_rcpt("smtp-in", s->id, s->tx->msgid, s->cmd + 8, 1); + smtp_report_tx_rcpt(s, s->tx->msgid, s->cmd + 8, 1); } break; case '3': if (s->tx) { if (s->last_cmd == CMD_DATA) - report_smtp_tx_data("smtp-in", s->id, s->tx->msgid, 1); + smtp_report_tx_data(s, s->tx->msgid, 1); } break; case '5': @@ -2116,13 +2152,13 @@ smtp_reply(struct smtp_session *s, char *fmt, ...) */ if (s->tx) { if (s->last_cmd == CMD_MAIL_FROM) - report_smtp_tx_mail("smtp-in", s->id, s->tx->msgid, + smtp_report_tx_mail(s, s->tx->msgid, s->cmd + 10, buf[0] == '4' ? -1 : 0); else if (s->last_cmd == CMD_RCPT_TO) - report_smtp_tx_rcpt("smtp-in", s->id, + smtp_report_tx_rcpt(s, s->tx->msgid, s->cmd + 8, buf[0] == '4' ? -1 : 0); else if (s->last_cmd == CMD_DATA && s->tx->rcptcount) - report_smtp_tx_data("smtp-in", s->id, s->tx->msgid, + smtp_report_tx_data(s, s->tx->msgid, buf[0] == '4' ? -1 : 0); } @@ -2160,7 +2196,6 @@ smtp_reply(struct smtp_session *s, char *fmt, ...) } io_xprintf(s->io, "%s\r\n", buf); - report_smtp_protocol_server("smtp-in", s->id, buf); } static void @@ -2172,7 +2207,7 @@ smtp_free(struct smtp_session *s, const char * reason) smtp_tx_free(s->tx); } - report_smtp_link_disconnect("smtp-in", s->id); + smtp_report_link_disconnect(s); smtp_filter_end(s); if (s->flags & SF_SECURE && s->listener->flags & F_SMTPS) @@ -2215,25 +2250,23 @@ smtp_mailaddr(struct mailaddr *maddr, char *line, int mailfrom, char **args, memmove(maddr->user, p, strlen(p) + 1); } - if (!valid_localpart(maddr->user) || - !valid_domainpart(maddr->domain)) { - /* accept empty return-path in MAIL FROM, required for bounces */ - if (mailfrom && maddr->user[0] == '\0' && maddr->domain[0] == '\0') - return (1); - - /* no user-part, reject */ - if (maddr->user[0] == '\0') - return (0); + /* accept empty return-path in MAIL FROM, required for bounces */ + if (mailfrom && maddr->user[0] == '\0' && maddr->domain[0] == '\0') + return (1); - /* no domain, local user */ - if (maddr->domain[0] == '\0') { - (void)strlcpy(maddr->domain, domain, - sizeof(maddr->domain)); - return (1); - } + /* no or invalid user-part, reject */ + if (maddr->user[0] == '\0' || !valid_localpart(maddr->user)) return (0); + + /* no domain part, local user */ + if (maddr->domain[0] == '\0') { + (void)strlcpy(maddr->domain, domain, + sizeof(maddr->domain)); } + if (!valid_domainpart(maddr->domain)) + return (0); + return (1); } @@ -2385,6 +2418,7 @@ smtp_tx(struct smtp_session *s) (void)strlcpy(tx->evp.smtpname, s->smtpname, sizeof(tx->evp.smtpname)); (void)strlcpy(tx->evp.hostname, s->rdns, sizeof tx->evp.hostname); (void)strlcpy(tx->evp.helo, s->helo, sizeof(tx->evp.helo)); + (void)strlcpy(tx->evp.username, s->username, sizeof(tx->evp.username)); if (s->flags & SF_BOUNCE) tx->evp.flags |= EF_BOUNCE; @@ -2553,7 +2587,14 @@ smtp_tx_rcpt_to(struct smtp_tx *tx, const char *line) } } else if (ADVERTISE_EXT_DSN(tx->session) && strncasecmp(opt, "ORCPT=", 6) == 0) { opt += 6; - if (!text_to_mailaddr(&tx->evp.dsn_orcpt, opt)) { + + if (strncasecmp(opt, "rfc822;", 7) == 0) + opt += 7; + + if (!text_to_mailaddr(&tx->evp.dsn_orcpt, opt) || + !valid_localpart(tx->evp.dsn_orcpt.user) || + (strlen(tx->evp.dsn_orcpt.domain) != 0 && + !valid_domainpart(tx->evp.dsn_orcpt.domain))) { smtp_reply(tx->session, "553 ORCPT address syntax error"); return; @@ -2589,8 +2630,6 @@ smtp_tx_commit(struct smtp_tx *tx) m_add_msgid(p_queue, tx->msgid); m_close(p_queue); tree_xset(&wait_queue_commit, tx->session->id, tx->session); - report_smtp_tx_commit("smtp-in", tx->session->id, tx->msgid, tx->odatalen); - report_smtp_tx_reset("smtp-in", tx->session->id, tx->msgid); smtp_filter_data_end(tx->session); } @@ -2600,8 +2639,8 @@ smtp_tx_rollback(struct smtp_tx *tx) m_create(p_queue, IMSG_SMTP_MESSAGE_ROLLBACK, 0, 0, -1); m_add_msgid(p_queue, tx->msgid); m_close(p_queue); - report_smtp_tx_rollback("smtp-in", tx->session->id, tx->msgid); - report_smtp_tx_reset("smtp-in", tx->session->id, tx->msgid); + smtp_report_tx_rollback(tx->session, tx->msgid); + smtp_report_tx_reset(tx->session, tx->msgid); smtp_filter_data_end(tx->session); } @@ -2614,7 +2653,7 @@ smtp_tx_dataline(struct smtp_tx *tx, const char *line) log_trace(TRACE_SMTP, "<<< [MSG] %s", line); if (!strcmp(line, ".")) { - report_smtp_protocol_client("smtp-in", tx->session->id, "."); + smtp_report_protocol_client(tx->session, "."); log_trace(TRACE_SMTP, "<<< [EOM]"); if (tx->error) return 1; @@ -2997,3 +3036,189 @@ smtp_strstate(int state) return (buf); } } + + +static void +smtp_report_link_connect(struct smtp_session *s, const char *rdns, int fcrdns, + const struct sockaddr_storage *ss_src, + const struct sockaddr_storage *ss_dest) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_link_connect("smtp-in", s->id, rdns, fcrdns, ss_src, ss_dest); +} + +static void +smtp_report_link_greeting(struct smtp_session *s, + const char *domain) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_link_greeting("smtp-in", s->id, domain); +} + +static void +smtp_report_link_identify(struct smtp_session *s, const char *method, const char *identity) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_link_identify("smtp-in", s->id, method, identity); +} + +static void +smtp_report_link_tls(struct smtp_session *s, const char *ssl) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_link_tls("smtp-in", s->id, ssl); +} + +static void +smtp_report_link_disconnect(struct smtp_session *s) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_link_disconnect("smtp-in", s->id); +} + +static void +smtp_report_link_auth(struct smtp_session *s, const char *user, const char *result) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_link_auth("smtp-in", s->id, user, result); +} + +static void +smtp_report_tx_reset(struct smtp_session *s, uint32_t msgid) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_tx_reset("smtp-in", s->id, msgid); +} + +static void +smtp_report_tx_begin(struct smtp_session *s, uint32_t msgid) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_tx_begin("smtp-in", s->id, msgid); +} + +static void +smtp_report_tx_mail(struct smtp_session *s, uint32_t msgid, const char *address, int ok) +{ + char mailaddr[SMTPD_MAXMAILADDRSIZE]; + char *p; + + if (! SESSION_FILTERED(s)) + return; + + if ((p = strchr(address, '<')) == NULL) + return; + (void)strlcpy(mailaddr, p + 1, sizeof mailaddr); + if ((p = strchr(mailaddr, '>')) == NULL) + return; + *p = '\0'; + + report_smtp_tx_mail("smtp-in", s->id, msgid, mailaddr, ok); +} + +static void +smtp_report_tx_rcpt(struct smtp_session *s, uint32_t msgid, const char *address, int ok) +{ + char mailaddr[SMTPD_MAXMAILADDRSIZE]; + char *p; + + if (! SESSION_FILTERED(s)) + return; + + if ((p = strchr(address, '<')) == NULL) + return; + (void)strlcpy(mailaddr, p + 1, sizeof mailaddr); + if ((p = strchr(mailaddr, '>')) == NULL) + return; + *p = '\0'; + + report_smtp_tx_rcpt("smtp-in", s->id, msgid, mailaddr, ok); +} + +static void +smtp_report_tx_envelope(struct smtp_session *s, uint32_t msgid, uint64_t evpid) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_tx_envelope("smtp-in", s->id, msgid, evpid); +} + +static void +smtp_report_tx_data(struct smtp_session *s, uint32_t msgid, int ok) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_tx_data("smtp-in", s->id, msgid, ok); +} + +static void +smtp_report_tx_commit(struct smtp_session *s, uint32_t msgid, size_t msgsz) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_tx_commit("smtp-in", s->id, msgid, msgsz); +} + +static void +smtp_report_tx_rollback(struct smtp_session *s, uint32_t msgid) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_tx_rollback("smtp-in", s->id, msgid); +} + +static void +smtp_report_protocol_client(struct smtp_session *s, const char *command) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_protocol_client("smtp-in", s->id, command); +} + +static void +smtp_report_protocol_server(struct smtp_session *s, const char *response) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_protocol_server("smtp-in", s->id, response); +} + +static void +smtp_report_filter_response(struct smtp_session *s, int phase, int response, const char *param) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_filter_response("smtp-in", s->id, phase, response, param); +} + +static void +smtp_report_timeout(struct smtp_session *s) +{ + if (! SESSION_FILTERED(s)) + return; + + report_smtp_timeout("smtp-in", s->id); +} diff --git a/smtpd/smtpc.c b/usr.sbin/smtpd/smtpc.c index deb99c63..c0f5d992 100644 --- a/smtpd/smtpc.c +++ b/usr.sbin/smtpd/smtpc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpc.c,v 1.10 2019/09/21 09:04:08 semarie Exp $ */ +/* $OpenBSD: smtpc.c,v 1.11 2020/09/14 18:32:11 millert Exp $ */ /* * Copyright (c) 2018 Eric Faurot <eric@openbsd.org> @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/socket.h> @@ -147,19 +149,25 @@ main(int argc, char **argv) fatal("SSL_CTX_set_ssl_version"); SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE , NULL); +#if HAVE_PLEDGE if (pledge("stdio inet dns tmppath", NULL) == -1) fatal("pledge"); +#endif if (!noaction) parse_message(stdin); +#if HAVE_PLEDGE if (pledge("stdio inet dns", NULL) == -1) fatal("pledge"); +#endif parse_server(server); +#if HAVE_PLEDGE if (pledge("stdio inet", NULL) == -1) fatal("pledge"); +#endif resume(); @@ -185,7 +193,7 @@ parse_server(char *server) *p = '\0'; p += 3; /* check for credentials */ - c = strchr(p, '@'); + c = strrchr(p, '@'); if (c) { creds = p; *c = '\0'; @@ -292,7 +300,7 @@ parse_message(FILE *ifp) if (len >= 2 && line[len - 2] == '\r' && line[len - 1] == '\n') line[--len - 1] = '\n'; - if (fwrite(line, 1, len, mail.fp) != len) + if (fwrite(line, 1, len, mail.fp) != (size_t)len) fatal("fwrite"); if (line[len - 1] != '\n' && fputc('\n', mail.fp) == EOF) @@ -318,7 +326,7 @@ resume(void) if (ai == NULL) fatalx("no more host"); - getnameinfo(ai->ai_addr, ai->ai_addr->sa_len, + getnameinfo(ai->ai_addr, SA_LEN(ai->ai_addr), host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV); log_debug("trying host %s port %s...", host, serv); diff --git a/smtpd/smtpctl.8 b/usr.sbin/smtpd/smtpctl.8 index 1efcff63..b29f2063 100644 --- a/smtpd/smtpctl.8 +++ b/usr.sbin/smtpd/smtpctl.8 @@ -1,4 +1,4 @@ -.\" $OpenBSD: smtpctl.8,v 1.64 2018/09/18 06:21:45 miko Exp $ +.\" $OpenBSD: smtpctl.8,v 1.65 2020/09/14 09:48:08 martijn Exp $ .\" .\" Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> .\" Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> @@ -15,7 +15,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: September 18 2018 $ +.Dd $Mdocdate: September 14 2020 $ .Dt SMTPCTL 8 .Os .Sh NAME @@ -247,8 +247,13 @@ Shows if MTA, MDA and SMTP systems are currently running or paused. Recursively look up SPF records for the domains read from stdin. For example: .Bd -literal -offset indent -# smtpctl spf walk < domains.txt +$ smtpctl spf walk < domains.txt .Ed +.Pp +SPF records may contain macros which cannot be included in a static list and +must be resolved dynamically at connection time. +.Cm spf walk +cannot provide full results in these cases. .It Cm trace Ar subsystem Enables real-time tracing of .Ar subsystem . diff --git a/smtpd/smtpctl.c b/usr.sbin/smtpd/smtpctl.c index a985172a..1cc37994 100644 --- a/smtpd/smtpctl.c +++ b/usr.sbin/smtpd/smtpctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpctl.c,v 1.165 2019/07/23 08:11:10 gilles Exp $ */ +/* $OpenBSD: smtpctl.c,v 1.167 2020/02/24 16:16:07 millert Exp $ */ /* * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> @@ -21,6 +21,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/socket.h> #include <sys/queue.h> @@ -29,6 +31,12 @@ #include <sys/wait.h> #include <sys/stat.h> +#include <net/if.h> +/* #include <net/if_media.h> */ +/* #include <net/if_types.h> */ +#include <netinet/in.h> +#include <arpa/inet.h> + #include <err.h> #include <errno.h> #include <event.h> @@ -43,17 +51,30 @@ #include <syslog.h> #include <time.h> #include <unistd.h> +#if defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) #include <vis.h> +#else +#include "bsd-vis.h" +#endif #include <limits.h> #include "smtpd.h" #include "parser.h" #include "log.h" +#ifndef PATH_GZCAT #define PATH_GZCAT "/usr/bin/gzcat" +#endif #define PATH_CAT "/bin/cat" #define PATH_QUEUE "/queue" +#ifndef PATH_ENCRYPT #define PATH_ENCRYPT "/usr/bin/encrypt" +#endif + +#ifndef HAVE_DB_API +#define makemap(x, y, z) 1 +#endif + int srv_connect(void); int srv_connected(void); @@ -1041,9 +1062,20 @@ int main(int argc, char **argv) { gid_t gid; + struct group *gr; int privileged; char *argv_mailq[] = { "show", "queue", NULL }; +#ifdef NEED_PROGNAME + __progname = get_progname(argv[0]); +#endif + + /* check that smtpctl was installed setgid */ + if ((gr = getgrnam(SMTPD_QUEUE_GROUP)) == NULL) + errx(1, "unknown group %s", SMTPD_QUEUE_GROUP); + else if (gr->gr_gid != getegid()) + errx(1, "this program must be setgid %s", SMTPD_QUEUE_GROUP); + sendmail_compat(argc, argv); privileged = geteuid() == 0; @@ -1125,7 +1157,7 @@ sendmail_compat(int argc, char **argv) */ for (i = 1; i < argc; i++) if (strncmp(argv[i], "-bi", 3) == 0) - exit(makemap(P_NEWALIASES, argc, argv)); + exit(makemap(P_SENDMAIL, argc, argv)); if (!srv_connect()) offlinefp = offline_file(); @@ -1134,10 +1166,12 @@ sendmail_compat(int argc, char **argv) if (setresgid(gid, gid, gid) == -1) err(1, "setresgid"); +#if HAVE_PLEDGE /* we'll reduce further down the road */ if (pledge("stdio rpath wpath cpath tmppath flock " "dns getpw recvfd", NULL) == -1) err(1, "pledge"); +#endif sendmail = 1; exit(enqueue(argc, argv, offlinefp)); diff --git a/smtpd/smtpctl/Makefile b/usr.sbin/smtpd/smtpctl/Makefile index ef8148be..ef8148be 100644 --- a/smtpd/smtpctl/Makefile +++ b/usr.sbin/smtpd/smtpctl/Makefile diff --git a/smtpd/smtpd-api.h b/usr.sbin/smtpd/smtpd-api.h index f83edd05..f83edd05 100644 --- a/smtpd/smtpd-api.h +++ b/usr.sbin/smtpd/smtpd-api.h diff --git a/smtpd/smtpd-defines.h b/usr.sbin/smtpd/smtpd-defines.h index 3b9038cd..f22a546f 100644 --- a/smtpd/smtpd-defines.h +++ b/usr.sbin/smtpd/smtpd-defines.h @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd-defines.h,v 1.10 2018/12/27 15:41:50 gilles Exp $ */ +/* $OpenBSD: smtpd-defines.h,v 1.12 2020/02/24 16:16:08 millert Exp $ */ /* * Copyright (c) 2013 Gilles Chehade <gilles@poolp.org> @@ -32,11 +32,29 @@ #define SMTPD_VUSERNAME_SIZE (255 + 1) #define SMTPD_SUBADDRESS_SIZE (255 + 1) +#ifndef SMTPD_USER #define SMTPD_USER "_smtpd" +#endif +#ifndef PATH_CHROOT #define PATH_CHROOT "/var/empty" +#endif +#ifndef SMTPD_QUEUE_USER #define SMTPD_QUEUE_USER "_smtpq" +#endif +#ifndef SMTPD_QUEUE_GROUP #define SMTPD_QUEUE_GROUP "_smtpq" +#endif +#ifndef PATH_SPOOL #define PATH_SPOOL "/var/spool/smtpd" +#endif + +#ifndef PATH_MAILLOCAL +#define PATH_MAILLOCAL PATH_LIBEXEC "/mail.local" +#endif + +#ifndef PATH_MAKEMAP +#define PATH_MAKEMAP "/usr/sbin/makemap" +#endif #define SUBADDRESSING_DELIMITER "+" diff --git a/usr.sbin/smtpd/smtpd-filters.7 b/usr.sbin/smtpd/smtpd-filters.7 new file mode 100644 index 00000000..5af7008e --- /dev/null +++ b/usr.sbin/smtpd/smtpd-filters.7 @@ -0,0 +1,653 @@ +.\" $OpenBSD: smtpd-filters.7,v 1.6 2020/04/25 09:44:02 eric Exp $ +.\" +.\" Copyright (c) 2008 Janne Johansson <jj@openbsd.org> +.\" Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> +.\" Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> +.\" +.\" 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. +.\" +.\" +.Dd $Mdocdate: April 25 2020 $ +.Dt FILTERS 7 +.Os +.Sh NAME +.Nm filters +.Nd filtering API for the +.Xr smtpd 8 +daemon +.Sh DESCRIPTION +The +.Xr smtpd 8 +daemon provides a Simple Mail Transfer Protocol (SMTP) implementation +that allows an ordinary machine to become Mail eXchangers (MX). +Many features that are commonly used by MX, +such as delivery reporting or Spam filtering, +are outside the scope of SMTP and too complex to fit in +.Xr smtpd 8 . +.Pp +Because an MX needs to provide these features, +.Xr smtpd 8 +provides an API to extend its behavior through pluggable +.Nm . +.Pp +At runtime, +.Xr smtpd 8 +can report events to +.Nm +and query what it should answer to these events. +This allows the decision logic to rely on third-party programs. +.Sh DESIGN +The +.Nm +are programs that run as unique standalone processes, +they do not share +.Xr smtpd 8 +memory space. +They are executed by +.Xr smtpd 8 +at startup and expected to run in an infinite loop, +reading events and filtering requests from +.Xr stdin 4 , +writing responses to +.Xr stdout 4 +and logging to +.Xr stderr 4 . +They are not allowed to terminate. +.Pp +Because +.Nm +are standalone programs that communicate with +.Xr smtpd 8 +through +.Xr fd 4 , +they may run as different users than +.Xr smtpd 8 +and may be written in any language. +The +.Nm +must not use blocking I/O, +they must support answering asynchronously to +.Xr smtpd 8 . +.Sh REPORT AND FILTER +The API relies on two streams, +report and filter. +.Pp +The report stream is a one-way stream which allows +.Xr smtpd 8 +to inform +.Nm +in real-time about events that are occurring in the daemon. +The report events do not expect an answer from +.Nm , +it is just meant to provide them with informations. +A filter should be able to replicate the +.Xr smtpd 8 +state for a session by gathering informations coming from report events. +No decision is ever taken by the report stream. +.Pp +The filter stream is a two-way stream which allows +.Xr smtpd 8 +to query +.Nm +about what it should do with a session at a given phase. +The filter requests expects an answer from +.Nm , +.Xr smtpd 8 +will not let the session move forward until then. +A decision must always be taken by the filter stream. +.Pp +It is sometimes possible to rely on filter requests to gather information, +but because a reponse is expected by +.Xr smtpd 8 , +this is more costly than using report events. +The correct pattern for writing filters is to use the report events to +create a local state for a session, +then use filter requests to take decisions based on this state. +The only case when using filter request instead of report events is correct, +is when a decision is required for the filter request and there is no need for +more information than that of the event. +.Sh PROTOCOL +The protocol is straightforward, +it consists of a human-readable line exchanges between +.Nm +and +.Xr smtpd 8 +through +.Xr fd 4 . +.Pp +The protocol begins with a handshake. +First, +.Xr smtpd 8 +provides +.Nm +with general configuration information in the form of key-value lines: +.Bd -literal -offset indent +config|smtpd-version|6.6.1 +config|smtp-session-timeout|300 +config|subsystem|smtp-in +config|ready +.Ed +.Pp +Then, +.Nm +register the stream, +subsystem and event they want to handle: +.Bd -literal -offset indent +register|report|smtp-in|link-connect +register|ready +.Ed +.Pp +Finally, +.Xr smtpd 8 +will emit report events and filter requests, +expecting +.Nm +to react accordingly either by responding or not depending on the stream: +.Bd -literal -offset indent +report|0.5|1576146008.006099|smtp-in|link-connect|7641df9771b4ed00|mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25 +report|0.5|1576147242.200225|smtp-in|link-connect|7641dfb3798eb5bf|mail.openbsd.org|pass|199.185.178.25:31205|45.77.67.80:25 +report|0.5|1576148447.982572|smtp-in|link-connect|7641dfc063102cbd|mail.openbsd.org|pass|199.185.178.25:24786|45.77.67.80:25 +.Ed +.Pp +The char +.Dq | +may only appear in the last field of a payload, +in which case it should be considered a regular char and not a separator. +Other fields have strict formatting excluding the possibility of having a +.Dq | . +.Pp +The list of subsystems and events, +as well as the format of requests and reponses, +will be documented in the sections below. +.Sh CONFIGURATION +During the initial handshake, +.Xr smtpd 8 +will emit a serie of configuration keys and values. +The list is meant to be ignored by +.Nm +that do not require it and consumed gracefully by filters that do. +.Pp +There are currently three keys: +.Bd -literal -offset indent +config|smtpd-version|6.6.1 +config|smtp-session-timeout|300 +config|subsystem|smtp-in +.Ed +.Pp +When +.Xr smtpd 8 +has sent all configuration keys it emits the following line: +.Bd -literal -offset indent +config|ready +.Ed +.Sh REPORT EVENTS +There is currently only one subsystem supported in the API: +smtp-in. +.Pp +Each report event is generated by +.Xr smtpd 8 +as a single line similar to the one below: +.Bd -literal -offset indent +report|0.5|1576146008.006099|smtp-in|link-connect|7641df9771b4ed00|mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25 +.Ed +.Pp +The format consists of a protocol prefix containing the stream, +the protocol version, +the timestamp, +the subsystem, +the event and the unique session identifier separated by +.Dq | : +.Bd -literal -offset indent +report|0.5|1576146008.006099|smtp-in|link-connect|7641df9771b4ed00 +.Ed +.Pp +It is followed by a suffix containing the event-specific parameters, +also separated by +.Dq | : +.Bd -literal -offset indent +mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25 +.Ed +.Pp +The list of events and event-specific parameters are provided here for smtp-in: +.Bl -tag -width Ds +.It Ic link-connect : Ar rdns fcrdns src dest +This event is generated upon connection. +.Pp +.Ar rdns +contains the reverse DNS hostname for the remote end or an empty string if none. +.Pp +.Ar fcrdns +contains the string +.Dq pass +or +.Dq fail +depending on if the remote end validates FCrDNS. +.Pp +.Ar src +holds either the IP address and port from source address, +in the format +.Dq address:port +or the path to a UNIX socket in the format +.Dq unix:/path . +.Pp +.Ar dest +holds either the IP address and port from destination address, +in the format +.Dq address:port +or the path to a UNIX socket in the format +.Dq unix:/path . +.It Ic link-greeting : Ar hostname +This event is generated upon display of the server banner. +.Pp +.Ar hostname +contains the hostname displayed in the banner. +.It Ic link-identify : Ar method identity +This event is generated upon +.Dq HELO +or +.Dq EHLO +command from the client. +.Pp +.Ar method +contains the string +.Dq HELO +or +.Dq EHLO +indicating the method used by the client. +.Pp +.Ar identity +contains the identity provided by the client. +.It Ic link-tls : Ar tls-string +This event is generated upon successful negotiation of TLS. +.Pp +.Ar tls-string +contains a colon-separated list of TLS properties including the TLS version, +the cipher suite used by the session and the cipher strenght in bits. +.It Ic link-disconnect +This event is generated upon disconnection of the client. +.It Ic link-auth : Ar username result +This event is generated upon authentication attempt of the client. +.Pp +.Ar username +contains the username used for the authentication attempt. +.Pp +.Ar result +contains the string +.Dq pass , +.Dq fail +or +.Dq error +depending on the result of the authentication attempt. +.It Ic tx-reset : Op message-id +This event is generated when a transaction is reset. +.Pp +If reset happend while in a transaction, +.Ar message-id +contains the identifier of the transaction being reset. +.It Ic tx-begin : Ar message-id +This event is generated when a transaction is initiated. +.Pp +.Ar message-id +contains the identifier for the transaction. +.It Ic tx-mail : Ar message-id Ar result address +This event is generated when client emits +.Dq MAIL FROM . +.Pp +.Ar message-id +contains the identifier for the transaction. +.Pp +.Ar result +contains +.Dq ok +if the sender was accepted, +.Dq permfail +if it was rejected +or +.Dq tempfail +if it was rejected for a transient error. +.Pp +.Ar address +contains the e-mail address of the sender. +The address is normalized and sanitized, +the protocol +.Dq < +and +.Dq > +are removed and so are parameters to +.Dq MAIL FROM . +.It Ic tx-rcpt : Ar message-id Ar result address +This event is generated when client emits +.Dq RCPT TO . +.Pp +.Ar message-id +contains the identifier for the transaction. +.Pp +.Ar result +contains +.Dq ok +if the recipient was accepted, +.Dq permfail +if it was rejected +or +.Dq tempfail +if it was rejected for a transient error. +.Pp +.Ar address +contains the e-mail address of the recipient. +The address is normalized and sanitized, +the protocol +.Dq < +and +.Dq > +are removed and so are parameters to +.Dq RCPT TO . +.It Ic tx-envelope : Ar message-id Ar envelope-id +This event is generated when an envelope is accepted. +.Pp +.Ar envelope-id +contains the unique identifier for the envelope. +.It Ic tx-data : Ar message-id Ar result +This event is generated when client has emitted +.Dq DATA . +.Pp +.Ar message-id +contains the unique identifier for the transaction. +.Pp +.Ar result +contains +.Dq ok +if server accepted to process the message, +.Dq permfail +if it has not accepted and +.Dq tempfail +if a transient error is preventing the processing of message. +.It Ic tx-commit : Ar message-id Ar message-size +This event is generated when a transaction has been accepted by the server. +.Pp +.Ar message-id +contains the unique identifier for the SMTP transaction. +.Pp +.Ar message-size +contains the size of the message submitted in the +.Dq DATA +phase of the SMTP transaction. +.It Ic tx-rollback : Ar message-id +This event is generated when a transaction has been rejected by the server. +.Pp +.Ar message-id +contains the unique identifier for the SMTP transaction. +.It Ic protocol-client : Ar command +This event is generated for every command submitted by the client. +It contains the raw command as received by the server. +.Pp +.Ar command +contains the command emitted by the client to the server. +.It Ic protocol-server : Ar response +This event is generated for every response emitted by the server. +It contains the raw response as emitted by the server. +.Pp +.Ar response +contains the response emitted by the server to the client. +.It Ic filter-report : Ar filter-kind Ar name message +This event is generated when a filter emits a report. +.Pp +.Ar filter-kind may be either +.Dq builtin +or +.Dq proc +depending on if the filter is an +.Xr smtpd 8 +builtin filter or a proc filter implementing the API. +.Pp +.Ar name +is the name of the filter that generated the report. +.Pp +.Ar message +is a filter-specific message. +.It Ic filter-response : Ar phase response Op param +This event is generated when a filter responds to a filtering request. +.Pp +.Ar phase +contains the phase name for the request. +The phases are documented in the next section. +.Pp +.Ar response +contains the response of the filter to the request, +it is either one of +.Dq proceed , +.Dq report , +.Dq reject , +.Dq disconnect , +.Dq junk or +.Dq rewrite . +.Pp +If specified, +.Ar param +is the parameter to the response. +.It Ic timeout +This event is generated when a timeout happens for a session. +.El +.Sh FILTER REQUESTS +There is currently only one subsystem supported in the API: +smtp-in. +.Pp +The filter requests allow +.Xr smtpd 8 +to query +.Nm +about what to do with a session at a particular phase. +In addition, +they allow +.Nm +to alter the content of a message by adding, +modifying, +or suppressing lines of input in a way that is similar to what program like +.Xr sed 1 +or +.Xr grep 1 +would do. +.Pp +Each filter request is generated by +.Xr smtpd 8 +as a single line similar to the one below: +.Bd -literal -offset indent +filter|0.5|1576146008.006099|smtp-in|connect|7641df9771b4ed00|1ef1c203cc576e5d|mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25 +.Ed +.Pp +The format consists of a protocol prefix containing the stream, +the protocol version, +the timestamp, +the subsystem, +the filtering phase, +the unique session identifier and an opaque token separated by +.Dq | +that the filter should provide in its response: +.Bd -literal -offset indent +filter|0.5|1576146008.006099|smtp-in|connect|7641df9771b4ed00|1ef1c203cc576e5d +.Ed +.Pp +It is followed by a suffix containing the phase-specific parameters to the +filter request, +also separated by +.Dq | : +.Bd -literal -offset indent +mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25 +.Ed +.Pp +Unlike with report events, +.Xr smtpd 8 +expects answers from filter requests and will not allow a session to move +forward before the filter has instructed +.Xr smtpd 8 +what to do with it. +.Pp +For all phases, +excepted +.Dq data-line , +the responses must follow the same construct, +a message type +.Dq filter-result , +followed by the unique session id, +the opaque token, +a decision and optional decision-specific parameters: +.Bd -literal -offset indent +filter-result|7641df9771b4ed00|1ef1c203cc576e5d|proceed +filter-result|7641df9771b4ed00|1ef1c203cc576e5d|reject|550 nope +.Ed +.Pp +The possible decisions to a +.Dq filter-result +message will be described below. +.Pp +For the +.Dq data-line +phase, +.Nm +are fed with a stream of lines corresponding to the message to filter, +and terminated by a single dot: +.Bd -literal -offset indent +filter|0.5|1576146008.006099|smtp-in|data-line|7641df9771b4ed00|1ef1c203cc576e5d|line 1 +filter|0.5|1576146008.006103|smtp-in|data-line|7641df9771b4ed00|1ef1c203cc576e5d|line 2 +filter|0.5|1576146008.006105|smtp-in|data-line|7641df9771b4ed00|1ef1c203cc576e5d|. +.Ed +.Pp +They are expected to produce an output stream similarly terminate by a single +dot. +A filter may inject, +suppress, +modify or echo back the lines it receives. +Ultimately, +.Xr smtpd 8 +will assume that the message consists of the output from +.Nm . +.Pp +Note that filters may be chained and the lines that are input into a filter +are the lines that are output from previous filter. +.Pp +The response to +.Dq data-line +requests use their own construct. +A +.Dq filter-dataline +prefix, +followed by the unique session identifier, +the opaque token and the output line as follows: +.Bd -literal -offset indent +filter-dataline|7641df9771b4ed00|1ef1c203cc576e5d|line 1 +filter-dataline|7641df9771b4ed00|1ef1c203cc576e5d|line 2 +filter-dataline|7641df9771b4ed00|1ef1c203cc576e5d|. +.Ed +.Pp +The list of events and event-specific parameters are provided here for smtp-in: +.Bl -tag -width Ds +.It Ic connect : Ar rdns fcrdns src dest +This request is emitted after connection, +before the banner is displayed. +.It Ic helo : Ar identity +This request is emitted after the client has emitted +.Dq HELO . +.It Ic ehlo : Ar identity +This request is emitted after the client has emitted +.Dq EHLO . +.It Ic starttls : Ar tls-string +This request is emitted after the client has requested +.Dq STARTTLS . +.It Ic auth : Ar auth +This request is emitted after the client has requested +.Dq AUTH . +.It Ic mail-from : Ar address +This request is emitted after the client has requested +.Dq MAIL FROM . +.It Ic rcpt-to : Ar address +This request is emitted after the client has requested +.Dq RCPT TO . +.It Ic data +This request is emitted after the client has requested +.Dq DATA . +.It Ic data-line : Ar line +This request is emitted for each line of input in the +.Dq DATA +phase. +The lines are raw dot-escaped SMTP DATA input, +terminated with a single dot. +.It Ic commit +This request is emitted after the final single dot is received. +.El +.Pp +For every filtering phase, +excepted +.Dq data-line , +the following decisions may be taken by a filter: +.Bl -tag -width Ds +.It Ic proceed +No action is taken, +session or transaction may be passed to the next filter. +.It Ic junk +The session or transaction is marked as Spam. +.Xr smtpd 8 +will prepend a +.Dq X-Spam +header to the message. +.It Ic reject Ar error +The command is rejected with the message +.Ar error . +The message must be a valid SMTP message including status code, +5xx or 4xx. +.Pp +Messages starting with a 5xx status result in a permanent failure, +those starting with a 4xx status result in a temporary failure. +.Pp +Messages starting with a 421 status will result in a client disconnect. +.It Ic disconnect Ar error +The client is disconnected with the message +.Ar error . +The message must be a valid SMTP message including status code, +5xx or 4xx. +.Pp +Messages starting with a 5xx status result in a permanent failure, +those starting with a 4xx status result in a temporary failure. +.It Ic rewrite Ar parameter +The command parameter is rewritten. +.Pp +This decision allows a filter to perform a rewrite of client-submitted +commands before they are processed by the SMTP engine. +.Ar parameter +is expected to be a valid SMTP parameter for the command. +.It Ic report Ar parameter +Generates a report with +.Ar parameter +for this filter. +.El +.\".Sh EXAMPLES +.\"This example filter written in +.\".Xr sh 1 +.\"will echo back... +.\".Bd -literal -offset indent +.\"XXX +.\".Ed +.\".Pp +.\"This example filter will filter... +.\".Bd -literal -offset indent +.\"XXX +.\".Ed +.\".Pp +.\"Note that libraries may provide a simpler interface to +.\".Nm +.\"that does not require implementing the protocol itself. +.\".Ed +.Sh SEE ALSO +.Xr smtpd 8 +.Sh HISTORY +.Nm +first appeared in +.Ox 6.6 . diff --git a/smtpd/smtpd.8 b/usr.sbin/smtpd/smtpd.8 index e3429f07..e3429f07 100644 --- a/smtpd/smtpd.8 +++ b/usr.sbin/smtpd/smtpd.8 diff --git a/smtpd/smtpd.c b/usr.sbin/smtpd/smtpd.c index 1ec261e4..9307fc3b 100644 --- a/smtpd/smtpd.c +++ b/usr.sbin/smtpd/smtpd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd.c,v 1.325 2019/09/03 04:48:20 martijn Exp $ */ +/* $OpenBSD: smtpd.c,v 1.335 2020/09/23 19:11:50 martijn Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> @@ -18,6 +18,9 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + +#include <sys/file.h> /* Needed for flock */ #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -27,21 +30,44 @@ #include <sys/uio.h> #include <sys/mman.h> +#ifdef BSD_AUTH #include <bsd_auth.h> +#endif + +#ifdef USE_PAM +#if defined(HAVE_SECURITY_PAM_APPL_H) +#include <security/pam_appl.h> +#elif defined (HAVE_PAM_PAM_APPL_H) +#include <pam/pam_appl.h> +#endif +#endif + +#ifdef HAVE_CRYPT_H +#include <crypt.h> /* needed for crypt() */ +#endif #include <dirent.h> #include <err.h> #include <errno.h> #include <event.h> #include <fcntl.h> +#include <grp.h> /* needed for setgroups */ #include <fts.h> #include <grp.h> #include <imsg.h> #include <inttypes.h> +#include <libgen.h> +#ifdef HAVE_LOGIN_CAP_H #include <login_cap.h> +#endif +#ifdef HAVE_PATHS_H #include <paths.h> +#endif #include <poll.h> #include <pwd.h> #include <signal.h> +#ifdef HAVE_SHADOW_H +#include <shadow.h> /* needed for getspnam() */ +#endif #include <stdio.h> #include <syslog.h> #include <limits.h> @@ -50,7 +76,11 @@ #include <sysexits.h> #include <time.h> #include <unistd.h> +#ifdef HAVE_UTIL_H +#include <util.h> +#endif +#include <openssl/rand.h> #include <openssl/ssl.h> #include <openssl/evp.h> @@ -58,6 +88,8 @@ #include "log.h" #include "ssl.h" +extern char *__progname; + #define SMTPD_MAXARG 32 static void parent_imsg(struct mproc *, struct imsg *); @@ -81,17 +113,17 @@ static struct mproc *setup_peer(enum smtp_proc_type, pid_t, int); static int imsg_wait(struct imsgbuf *, struct imsg *, int); static void offline_scan(int, short, void *); -static int offline_add(char *); +static int offline_add(char *, uid_t, gid_t); static void offline_done(void); -static int offline_enqueue(char *); +static int offline_enqueue(char *, uid_t, gid_t); static void purge_task(void); static int parent_auth_user(const char *, const char *); static void load_pki_tree(void); static void load_pki_keys(void); -static void fork_processors(void); -static void fork_processor(const char *, const char *, const char *, const char *, const char *); +static void fork_filter_processes(void); +static void fork_filter_process(const char *, const char *, const char *, const char *, const char *, uint32_t); enum child_type { CHILD_DAEMON, @@ -112,6 +144,8 @@ struct child { struct offline { TAILQ_ENTRY(offline) entry; + uid_t uid; + gid_t gid; char *path; }; @@ -152,11 +186,15 @@ int control_socket = -1; struct tree children; +/* Saved arguments to main(). */ +char **saved_argv; +int saved_argc; + static void parent_imsg(struct mproc *p, struct imsg *imsg) { struct forward_req *fwreq; - struct processor *processor; + struct filter_proc *processor; struct deliver deliver; struct child *c; struct msg m; @@ -260,7 +298,7 @@ parent_imsg(struct mproc *p, struct imsg *imsg) m_get_string(&m, &procname); m_end(&m); - processor = dict_xget(env->sc_processors_dict, procname); + processor = dict_xget(env->sc_filter_processes_dict, procname); m_create(p_lka, IMSG_LKA_PROCESSOR_ERRFD, 0, 0, processor->errfd); m_add_string(p_lka, procname); m_close(p_lka); @@ -482,6 +520,28 @@ main(int argc, char *argv[]) char *rexec = NULL; struct smtpd *conf; +#ifdef NEED_PROGNAME + __progname = get_progname(argv[0]); +#endif + __progname = xstrdup(__progname); + + /* Save argv. Duplicate so setproctitle emulation doesn't clobber it */ + saved_argc = argc; + saved_argv = xcalloc(argc + 1, sizeof(*saved_argv)); + for (i = 0; i < argc; i++) + saved_argv[i] = xstrdup(argv[i]); + saved_argv[i] = NULL; + +#ifdef NEED_SETPROCTITLE + /* Prepare for later setproctitle emulation */ + compat_init_setproctitle(argc, argv); + argv = saved_argv; +#endif + + /* this is to work around GNU getopt + portable setproctitle() fuckery */ + save_argc = saved_argc; + save_argv = saved_argv; + if ((conf = config_default()) == NULL) err(1, NULL); @@ -612,6 +672,9 @@ main(int argc, char *argv[]) if (parse_config(conf, conffile, opts)) exit(1); + if (RAND_status() != 1) + errx(1, "PRNG is not seeded"); + if (strlcpy(env->sc_conffile, conffile, PATH_MAX) >= PATH_MAX) errx(1, "config file exceeds PATH_MAX"); @@ -845,8 +908,7 @@ start_child(int save_argc, char **save_argv, char *rexec) } else if (fcntl(sp[0], F_SETFD, 0) == -1) fatal("%s: fcntl", rexec); - if (closefrom(4) == -1) - fatal("%s: closefrom", rexec); + xclosefrom(4); for (argc = 0; argc < save_argc; argc++) argv[argc] = save_argv[argc]; @@ -1079,13 +1141,18 @@ smtpd(void) { offline_timeout.tv_usec = 0; evtimer_add(&offline_ev, &offline_timeout); - fork_processors(); + if (pidfile(NULL) < 0) + err(1, "pidfile"); + + fork_filter_processes(); purge_task(); +#if HAVE_PLEDGE if (pledge("stdio rpath wpath cpath fattr tmppath " - "getpw sendfd proc exec id inet unix", NULL) == -1) + "getpw sendfd proc exec id inet chown unix", NULL) == -1) err(1, "pledge"); +#endif event_dispatch(); fatalx("exited event loop"); @@ -1179,8 +1246,7 @@ fork_proc_backend(const char *key, const char *conf, const char *procname) if (pid == 0) { /* child process */ dup2(sp[0], STDIN_FILENO); - if (closefrom(STDERR_FILENO + 1) == -1) - exit(1); + closefrom(STDERR_FILENO + 1); if (procname == NULL) procname = name; @@ -1257,22 +1323,46 @@ purge_task(void) } static void -fork_processors(void) +fork_filter_processes(void) { const char *name; - struct processor *processor; void *iter; + const char *fn; + struct filter_config *fc; + struct filter_config *fcs; + struct filter_proc *fp; + size_t i; + /* For each filter chain, assign the registered subsystem to subfilters */ iter = NULL; - while (dict_iter(env->sc_processors_dict, &iter, &name, (void **)&processor)) - fork_processor(name, processor->command, processor->user, processor->group, processor->chroot); + while (dict_iter(env->sc_filters_dict, &iter, (const char **)&fn, (void **)&fc)) { + if (fc->chain) { + for (i = 0; i < fc->chain_size; ++i) { + fcs = dict_xget(env->sc_filters_dict, fc->chain[i]); + fcs->filter_subsystem |= fc->filter_subsystem; + } + } + } + + /* For each filter, assign the registered subsystem to underlying proc */ + iter = NULL; + while (dict_iter(env->sc_filters_dict, &iter, (const char **)&fn, (void **)&fc)) { + if (fc->proc) { + fp = dict_xget(env->sc_filter_processes_dict, fc->proc); + fp->filter_subsystem |= fc->filter_subsystem; + } + } + + iter = NULL; + while (dict_iter(env->sc_filter_processes_dict, &iter, &name, (void **)&fp)) + fork_filter_process(name, fp->command, fp->user, fp->group, fp->chroot, fp->filter_subsystem); } static void -fork_processor(const char *name, const char *command, const char *user, const char *group, const char *chroot_path) +fork_filter_process(const char *name, const char *command, const char *user, const char *group, const char *chroot_path, uint32_t subsystems) { pid_t pid; - struct processor *processor; + struct filter_proc *processor; char buf; int sp[2], errfd[2]; struct passwd *pw; @@ -1304,13 +1394,14 @@ fork_processor(const char *name, const char *command, const char *user, const ch /* parent passes the child fd over to lka */ if (pid > 0) { - processor = dict_xget(env->sc_processors_dict, name); + processor = dict_xget(env->sc_filter_processes_dict, name); processor->errfd = errfd[1]; child_add(pid, CHILD_PROCESSOR, name); close(sp[0]); close(errfd[0]); m_create(p_lka, IMSG_LKA_PROCESSOR_FORK, 0, 0, sp[1]); m_add_string(p_lka, name); + m_add_u32(p_lka, (uint32_t)subsystems); m_close(p_lka); return; } @@ -1329,11 +1420,11 @@ fork_processor(const char *name, const char *command, const char *user, const ch if (setgroups(1, &gr->gr_gid) || setresgid(gr->gr_gid, gr->gr_gid, gr->gr_gid) || setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) - err(1, "fork_processor: cannot drop privileges"); + err(1, "fork_filter_process: cannot drop privileges"); + + xclosefrom(STDERR_FILENO + 1); - if (closefrom(STDERR_FILENO + 1) == -1) - err(1, "closefrom"); - if (setsid() == -1) + if (setsid() < 0) err(1, "setsid"); if (signal(SIGPIPE, SIG_DFL) == SIG_ERR || signal(SIGINT, SIG_DFL) == SIG_ERR || @@ -1382,6 +1473,8 @@ forkmda(struct mproc *p, uint64_t id, struct deliver *deliver) const char *pw_dir; dsp = dict_xget(env->sc_dispatchers, deliver->dispatcher); + if (dsp->type != DISPATCHER_LOCAL) + fatalx("non-local dispatcher called from forkmda()"); log_debug("debug: smtpd: forking mda for session %016"PRIx64 ": %s as %s", id, deliver->userinfo.username, @@ -1419,7 +1512,7 @@ forkmda(struct mproc *p, uint64_t id, struct deliver *deliver) pw_dir = deliver->userinfo.directory; } - if (pw_uid == 0 && !dsp->u.local.requires_root) { + if (pw_uid == 0 && !dsp->u.local.is_mbox) { (void)snprintf(ebuf, sizeof ebuf, "not allowed to deliver to: %s", deliver->userinfo.username); m_create(p_pony, IMSG_MDA_DONE, 0, 0, -1); @@ -1485,6 +1578,11 @@ forkmda(struct mproc *p, uint64_t id, struct deliver *deliver) m_close(p); return; } + + /* mbox helper, create mailbox before privdrop if it doesn't exist */ + if (dsp->u.local.is_mbox) + mda_mbox_init(deliver); + if (chdir(pw_dir) == -1 && chdir("/") == -1) err(1, "chdir"); if (setgroups(1, &pw_gid) || @@ -1495,9 +1593,8 @@ forkmda(struct mproc *p, uint64_t id, struct deliver *deliver) dup2(allout, STDOUT_FILENO) == -1 || dup2(allout, STDERR_FILENO) == -1) err(1, "forkmda: dup2"); - if (closefrom(STDERR_FILENO + 1) == -1) - err(1, "closefrom"); - if (setsid() == -1) + closefrom(STDERR_FILENO + 1); + if (setsid() < 0) err(1, "setsid"); if (signal(SIGPIPE, SIG_DFL) == SIG_ERR || signal(SIGINT, SIG_DFL) == SIG_ERR || @@ -1509,7 +1606,12 @@ forkmda(struct mproc *p, uint64_t id, struct deliver *deliver) /* avoid hangs by setting 5m timeout */ alarm(300); - mda_unpriv(dsp, deliver, pw_name, pw_dir); + if (dsp->u.local.is_mbox && + dsp->u.local.mda_wrapper == NULL && + deliver->mda_exec[0] == '\0') + mda_mbox(deliver); + else + mda_unpriv(dsp, deliver, pw_name, pw_dir); } static void @@ -1550,7 +1652,8 @@ offline_scan(int fd, short ev, void *arg) continue; } - if (offline_add(e->fts_name)) { + if (offline_add(e->fts_name, e->fts_statp->st_uid, + e->fts_statp->st_gid)) { log_warnx("warn: smtpd: " "could not add offline message %s", e->fts_name); continue; @@ -1570,7 +1673,7 @@ offline_scan(int fd, short ev, void *arg) } static int -offline_enqueue(char *name) +offline_enqueue(char *name, uid_t uid, gid_t gid) { char *path; struct stat sb; @@ -1607,8 +1710,7 @@ offline_enqueue(char *name) ssize_t len; arglist args; - if (closefrom(STDERR_FILENO + 1) == -1) - _exit(1); + closefrom(STDERR_FILENO + 1); memset(&args, 0, sizeof(args)); @@ -1633,6 +1735,18 @@ offline_enqueue(char *name) _exit(1); } + if (sb.st_uid != uid) { + log_warnx("warn: smtpd: file %s has bad uid %d", + path, sb.st_uid); + _exit(1); + } + + if (sb.st_gid != gid) { + log_warnx("warn: smtpd: file %s has bad gid %d", + path, sb.st_gid); + _exit(1); + } + pw = getpwuid(sb.st_uid); if (pw == NULL) { log_warnx("warn: smtpd: getpwuid for uid %d failed", @@ -1689,17 +1803,19 @@ offline_enqueue(char *name) } static int -offline_add(char *path) +offline_add(char *path, uid_t uid, gid_t gid) { struct offline *q; if (offline_running < OFFLINE_QUEUEMAX) /* skip queue */ - return offline_enqueue(path); + return offline_enqueue(path, uid, gid); q = malloc(sizeof(*q) + strlen(path) + 1); if (q == NULL) return (-1); + q->uid = uid; + q->gid = gid; q->path = (char *)q + sizeof(*q); memmove(q->path, path, strlen(path) + 1); TAILQ_INSERT_TAIL(&offline_q, q, entry); @@ -1718,7 +1834,7 @@ offline_done(void) if ((q = TAILQ_FIRST(&offline_q)) == NULL) break; /* all done */ TAILQ_REMOVE(&offline_q, q, entry); - offline_enqueue(q->path); + offline_enqueue(q->path, q->uid, q->gid); free(q); } } @@ -2049,6 +2165,9 @@ imsg_to_str(int type) CASE(IMSG_REPORT_SMTP_LINK_CONNECT); CASE(IMSG_REPORT_SMTP_LINK_DISCONNECT); CASE(IMSG_REPORT_SMTP_LINK_TLS); + CASE(IMSG_REPORT_SMTP_LINK_GREETING); + CASE(IMSG_REPORT_SMTP_LINK_IDENTIFY); + CASE(IMSG_REPORT_SMTP_LINK_AUTH); CASE(IMSG_REPORT_SMTP_TX_RESET); CASE(IMSG_REPORT_SMTP_TX_BEGIN); @@ -2075,8 +2194,9 @@ imsg_to_str(int type) } } +#ifdef BSD_AUTH int -parent_auth_user(const char *username, const char *password) +parent_auth_bsd(const char *username, const char *password) { char user[LOGIN_NAME_MAX]; char pass[LINE_MAX]; @@ -2090,3 +2210,121 @@ parent_auth_user(const char *username, const char *password) return LKA_OK; return LKA_PERMFAIL; } +#endif + +#ifdef USE_PAM +int +pam_conv_password(int num_msg, const struct pam_message **msg, + struct pam_response **respp, void *password) +{ + struct pam_response *response; + + if (num_msg != 1) + return PAM_CONV_ERR; + + response = calloc(1, sizeof(struct pam_response)); + if (response == NULL || (response->resp = strdup(password)) == NULL) { + free(response); + return PAM_BUF_ERR; + } + + *respp = response; + return PAM_SUCCESS; +} +int +parent_auth_pam(const char *username, const char *password) +{ + int rc; + pam_handle_t *pamh = NULL; + struct pam_conv conv = { pam_conv_password, (char *)password }; + + if ((rc = pam_start(USE_PAM_SERVICE, username, &conv, &pamh)) != PAM_SUCCESS) + goto end; + if ((rc = pam_authenticate(pamh, 0)) != PAM_SUCCESS) + goto end; + if ((rc = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS) + goto end; + +end: + pam_end(pamh, rc); + + switch (rc) { + case PAM_SUCCESS: + return LKA_OK; + case PAM_SYSTEM_ERR: + case PAM_ABORT: + case PAM_AUTHINFO_UNAVAIL: + return LKA_TEMPFAIL; + default: + return LKA_PERMFAIL; + } +} +#endif + +#ifdef HAVE_GETSPNAM +int +parent_auth_getspnam(const char *username, const char *password) +{ + struct spwd *pw; + char *ep; + + errno = 0; + do { + pw = getspnam(username); + } while (pw == NULL && errno == EINTR); + + if (pw == NULL) { + if (errno) + return LKA_TEMPFAIL; + return LKA_PERMFAIL; + } + + if ((ep = crypt(password, pw->sp_pwdp)) == NULL) + return LKA_PERMFAIL; + + if (strcmp(pw->sp_pwdp, ep) == 0) + return LKA_OK; + + return LKA_PERMFAIL; +} +#endif + +int +parent_auth_pwd(const char *username, const char *password) +{ + struct passwd *pw; + char *ep; + + errno = 0; + do { + pw = getpwnam(username); + } while (pw == NULL && errno == EINTR); + + if (pw == NULL) { + if (errno) + return LKA_TEMPFAIL; + return LKA_PERMFAIL; + } + + if ((ep = crypt(password, pw->pw_passwd)) == NULL) + return LKA_PERMFAIL; + + if (strcmp(pw->pw_passwd, ep) == 0) + return LKA_OK; + + return LKA_PERMFAIL; +} + +int +parent_auth_user(const char *username, const char *password) +{ +#if defined(BSD_AUTH) + return (parent_auth_bsd(username, password)); +#elif defined(USE_PAM) + return (parent_auth_pam(username, password)); +#elif defined(HAVE_GETSPNAM) + return (parent_auth_getspnam(username, password)); +#else + return (parent_auth_pwd(username, password)); +#endif +} diff --git a/usr.sbin/smtpd/smtpd.conf b/usr.sbin/smtpd/smtpd.conf new file mode 100644 index 00000000..a7ba6c64 --- /dev/null +++ b/usr.sbin/smtpd/smtpd.conf @@ -0,0 +1,19 @@ +# $OpenBSD: smtpd.conf,v 1.10 2018/05/24 11:40:17 gilles Exp $ + +# This is the smtpd server system-wide configuration file. +# See smtpd.conf(5) for more information. + +table aliases file:/etc/mail/aliases + +# To accept external mail, replace with: listen on all +# +listen on localhost + +action "local" maildir alias <aliases> +action "relay" relay + +# Uncomment the following to accept external mail for domain "example.org" +# +# match from any for domain "example.org" action "local" +match for local action "local" +match from local for any action "relay" diff --git a/smtpd/smtpd.conf.5 b/usr.sbin/smtpd/smtpd.conf.5 index ca4087c3..36207c39 100644 --- a/smtpd/smtpd.conf.5 +++ b/usr.sbin/smtpd/smtpd.conf.5 @@ -1,4 +1,4 @@ -.\" $OpenBSD: smtpd.conf.5,v 1.228 2019/11/19 22:04:04 gilles Exp $ +.\" $OpenBSD: smtpd.conf.5,v 1.255 2020/09/23 19:11:50 martijn Exp $ .\" .\" Copyright (c) 2008 Janne Johansson <jj@openbsd.org> .\" Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net> @@ -17,7 +17,7 @@ .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" .\" -.Dd $Mdocdate: November 19 2019 $ +.Dd $Mdocdate: September 23 2020 $ .Dt SMTPD.CONF 5 .Os .Sh NAME @@ -141,8 +141,14 @@ may contain format specifiers that are expanded before use .Pp If the .Cm junk -argument is provided, the message will be moved to the Junk -folder if it contains a positive X-Spam header. +argument is provided, the message will be moved to the +.Ql Junk +folder if it contains a positive +.Ql X-Spam +header. +This folder will be created under +.Ar pathname +if it does not yet exist. .It Cm mbox Deliver the message to the user's mbox with .Xr mail.local 8 . @@ -228,6 +234,10 @@ Use the mapping .Ar table to look up a hostname matching the source address, to advertise during the HELO phase. +.It Cm domain Pf < Ar domains Ns > +Do not perform MX lookups but look up destination domain in +.Ar domains +and use matching relay url as relay host. .It Cm host Ar relay-url Do not perform MX lookups but relay messages to the relay host described by .Ar relay-url . @@ -270,6 +280,14 @@ and .Dq smtps protocols for authentication. Server certificates for those protocols are verified by default. +.It Cm pki Ar pkiname +For secure connections, +use the certificate associated with +.Ar pkiname +(declared in a +.Ic pki +directive) +to prove the client's identity to the remote mail server. .It Cm srs When relaying a mail resulting from a forward, use the Sender Rewriting Scheme to rewrite sender address. @@ -295,13 +313,19 @@ The credential table format is described in Use .Ar mailaddr as the MAIL FROM address within the SMTP transaction. -.It Cm src Ar address | Pf < Ar address Ns > +.It Cm src Ar sourceaddr | Pf < Ar sourceaddr Ns > Use the string or list table -.Ar address -for the source IP address. +.Ar sourceaddr +for the source IP address, +which is useful on machines with multiple interfaces. If the list contains more than one address, all of them are used in such a way that traffic is routed as efficiently as possible. .El +.It Ic admd Ar authservid +The Administrative Management Domain this mailserver belongs to. +The authservid will be forwarded to filters using it to identify or mark +authentication-results headers. +If omitted it defaults to the server name. .It Ic bounce Cm warn-interval Ar delay Op , Ar delay ... Send warning messages to the envelope sender when temporary delivery failures cause a message to remain on the queue for longer than @@ -333,64 +357,26 @@ or using the .Ic hostname directive. .It Ic filter Ar chain-name Ic chain Brq Ar filter-name Op , Ar ... -Register a chain of filters named -.Ar chain-name -and consisting of the filters listed from +Register a chain of filters +.Ar chain-name , +consisting of the filters listed from .Ar filter-name . Filters part of a filter chain are executed in order of declaration for each phase that they are registered for. A filter chain may be used in place of a filter for any directive but filter chains themselves. -.It Ic filter Ar filter-name Ic phase Ar phase-name Ic match Ar conditions Ic disconnect Ar message -Register builtin filter -.Ar filter-name -matching -.Ar conditions -to disconnect session with -.Ar message . -Phase and matching conditions are documented in a specific section, -see -.Sx BUILTIN FILTERING . -.It Ic filter Ar filter-name Ic phase Ar phase-name Ic match Ar conditions Ic junk -Register builtin filter -.Ar filter-name -matching -.Ar conditions -to mark a session or a transaction as junk. -Phase and matching conditions are documented in a specific section, -see -.Sx BUILTIN FILTERING . -.It Ic filter Ar filter-name Ic phase Ar phase-name Ic match Ar conditions Ic reject Ar message -Register builtin filter -.Ar filter-name -matching -.Ar conditions -to reject session with -.Ar message . -Phase and matching conditions are documented in a specific section, -see -.Sx BUILTIN FILTERING . -.It Ic filter Ar filter-name Ic phase Ar phase-name Ic match Ar conditions Ic report Ar message -Register builtin filter -.Ar filter-name -matching -.Ar conditions -to report on session with -.Ar message -and proceed with the transaction. -Phase and matching conditions are documented in a specific section, -see -.Sx BUILTIN FILTERING . -.It Ic filter Ar filter-name Ic phase Ar phase-name Ic match Ar conditions Ic rewrite Ar value -Register builtin filter -.Ar filter-name -matching -.Ar conditions -to rewrite phase parameter with new -.Ar value . -Phase and matching conditions are documented in a specific section, -see -.Sx BUILTIN FILTERING . +.It Ic filter Ar filter-name Ic phase Ar phase-name Ic match Ar conditions decision +Register a filter +.Ar filter-name . +A +.Ar decision +about what to do with the mail is taken at phase +.Ar phase-name +when matching +.Ar conditions . +Phases, matching conditions, and decisions are described in +.Sx MAIL FILTERING , +below. .It Ic filter Ar filter-name Ic proc Ar proc-name Register .Qq proc @@ -536,19 +522,29 @@ With the option, clients must also provide a valid certificate to establish an SMTP session. .El -.It Ic listen on Cm socket Oo Ar mask-src Oc Op Cm tag Ar tag +.It Ic listen on Cm socket Op Ar options Listen for incoming SMTP connections on the Unix domain socket .Pa /var/run/smtpd.sock . This is done by default, even if the directive is absent. -If the -.Cm mask-src -option is specified, printing of the HELO name, hostname, and IP -address of the originating host is suppressed in Received: header lines. -If the -.Cm tag -option is specified, -sessions initiated from the socket will be tagged +.Pp +The +.Ar options +are as follows: +.Bl -tag -width Ds +.It Ic filter Ar name +Apply filter +.Ar name +on connections handled by this listener. +.It Cm mask-src +Omit the +.Sy from +part when prepending +.Dq Received +headers. +.It Cm tag Ar tag +Clients connecting to the listener are tagged with the given .Ar tag . +.El .It Ic match Ar options Cm action Ar name If at least one mail envelope matches the .Ar options @@ -588,11 +584,47 @@ Specify that session may address the regex or regex table .Ar domain . .It Xo .Op Ic \&! +.Cm for rcpt-to +.Ar recipient | Pf < Ar recipient Ns > +.Xc +Specify that session may address the string or list table +.Ar recipient . +.It Xo +.Op Ic \&! +.Cm for rcpt-to regex +.Ar recipient | Pf < Ar recipient Ns > +.Xc +Specify that session may address the regex or regex table +.Ar recipient . +.It Xo +.Op Ic \&! .Cm from any .Xc Specify that session may originate from any source. .It Xo .Op Ic \&! +.Cm from auth +.Xc +Specify that session may originate from any authenticated user, +no matter the source IP address. +.It Xo +.Op Ic \&! +.Cm from auth +.Ar user | Pf < Ar user Ns > +.Xc +Specify that session may originate from authenticated user or user list +.Ar user , +no matter the source IP address. +.It Xo +.Op Ic \&! +.Cm from auth regex +.Ar user | Pf < Ar user Ns > +.Xc +Specify that session may originate from authenticated regex or regex list +.Ar user , +no matter the source IP address. +.It Xo +.Op Ic \&! .Cm from local .Xc Specify that session may only originate from a local IP address, @@ -600,6 +632,22 @@ or from the local enqueuer. This is the default, and may be omitted. .It Xo .Op Ic \&! +.Cm from mail-from +.Ar sender | Pf < Ar sender Ns > +.Xc +Specify that session may originate from sender or sender list +.Ar sender , +no matter the source IP address. +.It Xo +.Op Ic \&! +.Cm from mail-from regex +.Ar sender | Pf < Ar sender Ns > +.Xc +Specify that session may originate from regex or regex list +.Ar sender , +no matter the source IP address. +.It Xo +.Op Ic \&! .Cm from rdns .Xc Specify that session may only originate from an IP address that @@ -652,6 +700,20 @@ In addition, the following transaction options: Matches transactions which have been authenticated. .It Xo .Op Ic \&! +.Cm auth +.Ar username | Pf < Ar username Ns > +.Xc +Matches transactions which have been authenticated for user or user list +.Ar username . +.It Xo +.Op Ic \&! +.Cm auth regex +.Ar username | Pf < Ar username Ns > +.Xc +Matches transactions which have been authenticated for regex or regex list +.Ar username . +.It Xo +.Op Ic \&! .Cm helo .Ar helo-name | Pf < Ar helo-name Ns > .Xc @@ -899,7 +961,7 @@ pairs. The table must contain at least one key-value pair and may declare multiple pairs as a comma-separated (whitespace optional) list. .El -.Ss BUILTIN FILTERING +.Ss MAIL FILTERING In a regular workflow, .Xr smtpd 8 may accept or reject a message based only on the content of envelopes. @@ -915,7 +977,7 @@ then decide if a session is allowed to move forward. With filtering, a session may be interrupted at any phase before an envelope is complete. A message may also be rejected after being submitted, -disregarding if the envelope was accepted or not. +regardless of whether the envelope was accepted or not. .Pp The following phases are currently supported: .Bl -column mail-from -offset indent @@ -928,37 +990,39 @@ The following phases are currently supported: .It commit Ta after message is fully is submitted .El .Pp -At each phase, -multiple criteria may be checked: +At each phase, various conditions may be matched. +The fcrdns, rdns, and src data are available in all phases, +but other data must have been already submitted before they are available. .Bl -column XXXXXXXXXXXXXXXXXXXXX -offset indent .It fcrdns Ta forward-confirmed reverse DNS is valid .It rdns Ta session has a reverse DNS .It rdns Pf < Ar table Ns > Ta session has a reverse DNS in table .It src Pf < Ar table Ns > Ta source address is in table .It helo Pf < Ar table Ns > Ta helo name is in table -.It mail-from Pf < Ar table Ns > Ta sender address is in table -.It rcpt-to Pf < Ar table Ns > Ta recipient address is in table +.It auth Ta session is authenticated +.It auth Pf < Ar table Ns > Ta session username is in table +.It mail-from Pf < Ar table Ns > Ta sender address is in table +.It rcpt-to Pf < Ar table Ns > Ta recipient address is in table .El .Pp -All criteria from previous phases are available to subsequent phases, -so while the helo criteria is not available before the helo or ehlo phase, -the fcrdns criteria is available in all phases. -.Pp -Criteria may all be negated by prefixing them with an exclamation mark: +These conditions may all be negated by prefixing them with an exclamation mark: .Bl -column XXXXXXXXXXXXXXXXXXXXX -offset indent -.It ! fcrdns Ta forward-confirmed reverse DNS is invalid +.It !fcrdns Ta forward-confirmed reverse DNS is invalid .El .Pp -Any criteria using a table may indicate that tables hold regex by +Any conditions using a table may indicate that tables hold regex by prefixing the table name with the keyword regex. .Bl -column XXXXXXXXXXXXXXXXXXXXX -offset indent .It helo regex Pf < Ar table Ns > Ta helo name matches a regex in table .El -Finally, -four decisions may be taken: +.Pp +Finally, a number of decisions may be taken: .Bl -column XXXXXXXXXXXXXXXXXXXXX -offset indent +.It bypass Ta the session or transaction bypasses filters .It disconnect Ar message Ta the session is disconnected with message -.It junk Ta the session or transaction is junked +.It junk Ta the session or transaction is junked, i.e., an +.Ql X-Spam: yes +header is added to any messages .It reject Ar message Ta the command is rejected with message .It rewrite Ar value Ta the command parameter is rewritten with value .El @@ -966,7 +1030,7 @@ four decisions may be taken: Decisions that involve a message require that the message be RFC valid, meaning that they should either start with a 4xx or 5xx status code. Descisions can be taken at any phase, -however junking can only happen before a message is committed. +though junking can only happen before a message is committed. .Ss FORMAT SPECIFIERS Some configuration directives support expansion of their parameters at runtime. Such directives (for example @@ -1078,8 +1142,8 @@ action "local_mail" mbox alias <aliases> action "outbound" relay host smtp+tls://bob@smtp.example.com \e auth <secrets> -match for local action "local_mail" -match for any action "outbound" +match from local for local action "local_mail" +match from local for any action "outbound" .Ed .Pp In this second example, @@ -1119,26 +1183,40 @@ match for any action "outbound" match auth from any for any action "outbound" .Ed .Pp -For sites that wish to sign messages using DKIM, the -.Sy dkimproxy -package may be used as a filter. -The following example is the same as the default configuration, -but all outgoing mail is passed to dkimproxy_out on port 10027 -for signing. -The signed messages are received on port 10028 and tagged for relaying. +For sites that wish to sign messages using DKIM, +the following example uses +.Sy opensmtpd-filter-dkimsign +for DKIM signing: .Bd -literal -offset indent table aliases file:/etc/mail/aliases -listen on lo0 -listen on lo0 port 10028 tag DKIM +filter "dkimsign" proc-exec "filter-dkimsign -d <domain> -s <selector> \e + -k /etc/mail/dkim/private.key" user _dkimsign group _dkimsign + +listen on socket filter "dkimsign" +listen on lo0 filter "dkimsign" action "local_mail" mbox alias <aliases> action "outbound" relay -action "relay_dkim" relay host smtp://127.0.0.1:10027 match for local action "local_mail" -match tag DKIM for any action "outbound" -match for any action "relay_dkim" +match for any action "outbound" +.Ed +.Pp +Alternatively, the +.Sy opensmtpd-filter-rspamd +package may be used to provide integration with +.Sy rspamd , +a third-party daemon which provides multiple antispam features +as well as DKIM signing. +As well as configuring +.Sy rspamd +itself, +it requires use of the +.Cm proc-exec +keyword: +.Bd -literal -offset indent +filter "rspamd" proc-exec "filter-rspamd" .Ed .Pp Sites that accept non-local messages may be able to cut down on the diff --git a/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h index b1c8fe37..beb1d7b4 100644 --- a/smtpd/smtpd.h +++ b/usr.sbin/smtpd/smtpd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: smtpd.h,v 1.642 2019/11/03 23:58:51 gilles Exp $ */ +/* $OpenBSD: smtpd.h,v 1.659 2020/09/23 19:11:50 martijn Exp $ */ /* * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> @@ -18,6 +18,12 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <event.h> + +#include <imsg.h> + +#include "openbsd-compat.h" + #ifndef nitems #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) #endif @@ -37,9 +43,14 @@ (expected_sz), (imsg)->hdr.len - IMSG_HEADER_SIZE); \ } while (0) -#define CONF_FILE "/etc/mail/smtpd.conf" -#define MAILNAME_FILE "/etc/mail/mailname" -#define CA_FILE "/etc/ssl/cert.pem" +#ifndef SMTPD_CONFDIR +#define SMTPD_CONFDIR "/etc" +#endif +#define CONF_FILE SMTPD_CONFDIR "/smtpd.conf" +#define MAILNAME_FILE SMTPD_CONFDIR "/mailname" +#ifndef CA_FILE +#define CA_FILE "/etc/ssl/cert.pem" +#endif #define PROC_COUNT 7 @@ -49,19 +60,34 @@ #define EXPAND_BUFFER 1024 #define SMTPD_QUEUE_EXPIRY (4 * 24 * 60 * 60) -#define SMTPD_SOCKET "/var/run/smtpd.sock" +#ifndef SMTPD_USER +#define SMTPD_USER "_smtpd" +#endif +#ifndef SMTPD_QUEUE_USER +#define SMTPD_QUEUE_USER "_smtpq" +#endif +#ifndef SMTPD_SOCKDIR +#define SMTPD_SOCKDIR "/var/run" +#endif +#define SMTPD_SOCKET SMTPD_SOCKDIR "/smtpd.sock" +#ifndef SMTPD_NAME #define SMTPD_NAME "OpenSMTPD" -#define SMTPD_VERSION "6.6.1" +#endif +#define SMTPD_VERSION "6.7.0-portable" #define SMTPD_SESSION_TIMEOUT 300 #define SMTPD_BACKLOG 5 +#ifndef PATH_SMTPCTL #define PATH_SMTPCTL "/usr/sbin/smtpctl" +#endif #define PATH_OFFLINE "/offline" #define PATH_PURGE "/purge" #define PATH_TEMPORARY "/temporary" +#ifndef PATH_LIBEXEC #define PATH_LIBEXEC "/usr/local/libexec/smtpd" +#endif /* @@ -98,8 +124,9 @@ #define MTA_EXT_DSN 0x400 -#define P_NEWALIASES 0 -#define P_MAKEMAP 1 +#define P_SENDMAIL 0 +#define P_NEWALIASES 1 +#define P_MAKEMAP 2 #define CERT_ERROR -1 #define CERT_OK 0 @@ -489,6 +516,7 @@ struct envelope { char smtpname[HOST_NAME_MAX+1]; char helo[HOST_NAME_MAX+1]; char hostname[HOST_NAME_MAX+1]; + char username[SMTPD_MAXMAILADDRSIZE]; char errorline[LINE_MAX]; struct sockaddr_storage ss; @@ -584,7 +612,7 @@ struct smtpd { size_t sc_scheduler_max_msg_batch_size; size_t sc_scheduler_max_schedule; - struct dict *sc_processors_dict; + struct dict *sc_filter_processes_dict; int sc_ttl; #define MAX_BOUNCE_WARN 4 @@ -622,6 +650,8 @@ struct smtpd { char *sc_srs_key; char *sc_srs_key_backup; int sc_srs_ttl; + + char *sc_admd; }; #define TRACE_DEBUG 0x0001 @@ -1031,22 +1061,29 @@ enum lka_resp_status { LKA_PERMFAIL }; -struct processor { +enum filter_type { + FILTER_TYPE_BUILTIN, + FILTER_TYPE_PROC, + FILTER_TYPE_CHAIN, +}; + +enum filter_subsystem { + FILTER_SUBSYSTEM_SMTP_IN = 1<<0, + FILTER_SUBSYSTEM_SMTP_OUT = 1<<1, +}; + +struct filter_proc { const char *command; const char *user; const char *group; const char *chroot; int errfd; -}; - -enum filter_type { - FILTER_TYPE_BUILTIN, - FILTER_TYPE_PROC, - FILTER_TYPE_CHAIN, + enum filter_subsystem filter_subsystem; }; struct filter_config { char *name; + enum filter_subsystem filter_subsystem; enum filter_type filter_type; enum filter_phase phase; char *reject; @@ -1054,6 +1091,7 @@ struct filter_config { char *rewrite; char *report; uint8_t junk; + uint8_t bypass; char *proc; const char **chain; @@ -1084,6 +1122,15 @@ struct filter_config { int8_t not_helo_regex; struct table *helo_regex; + int8_t not_auth; + int8_t auth; + + int8_t not_auth_table; + struct table *auth_table; + + int8_t not_auth_regex; + struct table *auth_regex; + int8_t not_mail_from_table; struct table *mail_from_table; @@ -1134,7 +1181,7 @@ enum dispatcher_type { }; struct dispatcher_local { - uint8_t requires_root; /* only for MBOX */ + uint8_t is_mbox; /* only for MBOX */ uint8_t expand_only; uint8_t forward_only; @@ -1161,6 +1208,8 @@ struct dispatcher_remote { char *mail_from; char *smarthost; + int smarthost_domain; + char *auth; int tls_required; int tls_noverify; @@ -1168,6 +1217,8 @@ struct dispatcher_remote { int backup; char *backupmx; + char *filtername; + int srs; }; @@ -1333,7 +1384,7 @@ int lka(void); /* lka_proc.c */ int lka_proc_ready(void); -void lka_proc_forked(const char *, int); +void lka_proc_forked(const char *, uint32_t, int); void lka_proc_errfd(const char *, int); struct io *lka_proc_get_io(const char *); @@ -1372,7 +1423,7 @@ void lka_filter_init(void); void lka_filter_register_hook(const char *, const char *); void lka_filter_ready(void); int lka_filter_proc_in_session(uint64_t, const char *); -void lka_filter_begin(uint64_t, const char *, const struct sockaddr_storage *, const struct sockaddr_storage *, const char *, int); +void lka_filter_begin(uint64_t, const char *); void lka_filter_end(uint64_t); void lka_filter_protocol(uint64_t, enum filter_phase, const char *); void lka_filter_data_begin(uint64_t); @@ -1396,6 +1447,11 @@ void mda_postprivdrop(void); void mda_imsg(struct mproc *, struct imsg *); +/* mda_mbox.c */ +void mda_mbox_init(struct deliver *); +void mda_mbox(struct deliver *); + + /* mda_unpriv.c */ void mda_unpriv(struct dispatcher *, struct deliver *, const char *, const char *); @@ -1709,6 +1765,7 @@ int base64_encode(unsigned char const *, size_t, char *, size_t); int base64_decode(char const *, unsigned char *, size_t); int base64_encode_rfc3548(unsigned char const *, size_t, char *, size_t); +void xclosefrom(int); void log_trace_verbose(int); void log_trace(int, const char *, ...) diff --git a/smtpd/smtpd/Makefile b/usr.sbin/smtpd/smtpd/Makefile index 00c7951f..858bfeda 100644 --- a/smtpd/smtpd/Makefile +++ b/usr.sbin/smtpd/smtpd/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.91 2018/06/03 14:04:06 gilles Exp $ +# $OpenBSD: Makefile,v 1.109 2020/09/23 19:11:50 martijn Exp $ .PATH: ${.CURDIR}/.. @@ -24,12 +24,11 @@ SRCS+= ioev.c SRCS+= limit.c SRCS+= lka.c SRCS+= lka_filter.c -SRCS+= lka_proc.c -SRCS+= lka_report.c SRCS+= lka_session.c SRCS+= log.c SRCS+= mailaddr.c SRCS+= mda.c +SRCS+= mda_mbox.c SRCS+= mda_unpriv.c SRCS+= mda_variables.c SRCS+= mproc.c diff --git a/smtpd/spfwalk.c b/usr.sbin/smtpd/spfwalk.c index d28fa58c..46bf3129 100644 --- a/smtpd/spfwalk.c +++ b/usr.sbin/smtpd/spfwalk.c @@ -14,10 +14,15 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/socket.h> #include <sys/tree.h> +#ifdef HAVE_ARPA_NAMESER_COMPAT_H +#include <arpa/nameser_compat.h> +#endif #include <arpa/inet.h> #include <arpa/nameser.h> #include <netinet/in.h> @@ -28,27 +33,36 @@ #include <err.h> #include <errno.h> #include <event.h> +#include <imsg.h> +#include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <strings.h> #include <unistd.h> -#define LINE_MAX 1024 #include "smtpd-defines.h" #include "smtpd-api.h" #include "unpack_dns.h" #include "parser.h" +struct target { + void (*dispatch)(struct dns_rr *, struct target *); + int cidr4; + int cidr6; +}; + int spfwalk(int, struct parameter *); -static void dispatch_txt(struct dns_rr *); -static void dispatch_mx(struct dns_rr *); -static void dispatch_a(struct dns_rr *); -static void dispatch_aaaa(struct dns_rr *); -static void lookup_record(int, const char *, void (*)(struct dns_rr *)); +static void dispatch_txt(struct dns_rr *, struct target *); +static void dispatch_mx(struct dns_rr *, struct target *); +static void dispatch_a(struct dns_rr *, struct target *); +static void dispatch_aaaa(struct dns_rr *, struct target *); +static void lookup_record(int, char *, struct target *); static void dispatch_record(struct asr_result *, void *); static ssize_t parse_txt(const char *, size_t, char *, size_t); +static int parse_target(char *, struct target *); +void *xmalloc(size_t size); int ip_v4 = 0; int ip_v6 = 0; @@ -59,6 +73,7 @@ struct dict seen; int spfwalk(int argc, struct parameter *argv) { + struct target tgt; const char *ip_family = NULL; char *line = NULL; size_t linesize = 0; @@ -81,18 +96,23 @@ spfwalk(int argc, struct parameter *argv) dict_init(&seen); event_init(); + tgt.cidr4 = tgt.cidr6 = -1; + tgt.dispatch = dispatch_txt; + while ((linelen = getline(&line, &linesize, stdin)) != -1) { - while (linelen-- > 0 && isspace(line[linelen])) + while (linelen-- > 0 && isspace((unsigned char)line[linelen])) line[linelen] = '\0'; if (linelen > 0) - lookup_record(T_TXT, line, dispatch_txt); + lookup_record(T_TXT, line, &tgt); } free(line); +#if HAVE_PLEDGE if (pledge("dns stdio", NULL) == -1) err(1, "pledge"); +#endif event_dispatch(); @@ -100,20 +120,33 @@ spfwalk(int argc, struct parameter *argv) } void -lookup_record(int type, const char *record, void (*cb)(struct dns_rr *)) +lookup_record(int type, char *record, struct target *tgt) { struct asr_query *as; + struct target *ntgt; + size_t i; + if (strchr(record, '%') != NULL) { + for (i = 0; record[i] != '\0'; i++) { + if (!isprint(record[i])) + record[i] = '?'; + } + warnx("%s: %s contains macros and can't be resolved", __func__, + record); + return; + } as = res_query_async(record, C_IN, type, NULL); if (as == NULL) err(1, "res_query_async"); - event_asr_run(as, dispatch_record, cb); + ntgt = xmalloc(sizeof(*ntgt)); + *ntgt = *tgt; + event_asr_run(as, dispatch_record, (void *)ntgt); } void dispatch_record(struct asr_result *ar, void *arg) { - void (*cb)(struct dns_rr *) = arg; + struct target *tgt = arg; struct unpack pack; struct dns_header h; struct dns_query q; @@ -121,7 +154,7 @@ dispatch_record(struct asr_result *ar, void *arg) /* best effort */ if (ar->ar_h_errno && ar->ar_h_errno != NO_DATA) - return; + goto end; unpack_init(&pack, ar->ar_data, ar->ar_datalen); unpack_header(&pack, &h); @@ -130,20 +163,23 @@ dispatch_record(struct asr_result *ar, void *arg) for (; h.ancount; h.ancount--) { unpack_rr(&pack, &rr); /**/ - cb(&rr); + tgt->dispatch(&rr, tgt); } +end: + free(tgt); } void -dispatch_txt(struct dns_rr *rr) +dispatch_txt(struct dns_rr *rr, struct target *tgt) { + char buf[4096]; + char *argv[512]; + char buf2[512]; + struct target ltgt; struct in6_addr ina; - char buf[4096]; - char buf2[512]; - char *in = buf; - char *argv[512]; - char **ap = argv; - char *end; + char **ap = argv; + char *in = buf; + char *record, *end; ssize_t n; if (rr->rr_type != T_TXT) @@ -173,6 +209,8 @@ dispatch_txt(struct dns_rr *rr) if (**ap == '+' || **ap == '?') (*ap)++; + ltgt.cidr4 = ltgt.cidr6 = -1; + if (strncasecmp("ip4:", *ap, 4) == 0) { if ((ip_v4 == 1 || ip_both == 1) && inet_net_pton(AF_INET, *(ap) + 4, @@ -190,35 +228,50 @@ dispatch_txt(struct dns_rr *rr) if (strcasecmp("a", *ap) == 0) { print_dname(rr->rr_dname, buf2, sizeof(buf2)); buf2[strlen(buf2) - 1] = '\0'; - lookup_record(T_A, buf2, dispatch_a); - lookup_record(T_AAAA, buf2, dispatch_aaaa); + ltgt.dispatch = dispatch_a; + lookup_record(T_A, buf2, <gt); + ltgt.dispatch = dispatch_aaaa; + lookup_record(T_AAAA, buf2, <gt); continue; } if (strncasecmp("a:", *ap, 2) == 0) { - lookup_record(T_A, *(ap) + 2, dispatch_a); - lookup_record(T_AAAA, *(ap) + 2, dispatch_aaaa); + record = *(ap) + 2; + if (parse_target(record, <gt) < 0) + continue; + ltgt.dispatch = dispatch_a; + lookup_record(T_A, record, <gt); + ltgt.dispatch = dispatch_aaaa; + lookup_record(T_AAAA, record, <gt); continue; } if (strncasecmp("exists:", *ap, 7) == 0) { - lookup_record(T_A, *(ap) + 7, dispatch_a); + ltgt.dispatch = dispatch_a; + lookup_record(T_A, *(ap) + 7, <gt); continue; } if (strncasecmp("include:", *ap, 8) == 0) { - lookup_record(T_TXT, *(ap) + 8, dispatch_txt); + ltgt.dispatch = dispatch_txt; + lookup_record(T_TXT, *(ap) + 8, <gt); continue; } if (strncasecmp("redirect=", *ap, 9) == 0) { - lookup_record(T_TXT, *(ap) + 9, dispatch_txt); + ltgt.dispatch = dispatch_txt; + lookup_record(T_TXT, *(ap) + 9, <gt); continue; } if (strcasecmp("mx", *ap) == 0) { print_dname(rr->rr_dname, buf2, sizeof(buf2)); buf2[strlen(buf2) - 1] = '\0'; - lookup_record(T_MX, buf2, dispatch_mx); + ltgt.dispatch = dispatch_mx; + lookup_record(T_MX, buf2, <gt); continue; } if (strncasecmp("mx:", *ap, 3) == 0) { - lookup_record(T_MX, *(ap) + 3, dispatch_mx); + record = *(ap) + 3; + if (parse_target(record, <gt) < 0) + continue; + ltgt.dispatch = dispatch_mx; + lookup_record(T_MX, record, <gt); continue; } } @@ -226,9 +279,10 @@ dispatch_txt(struct dns_rr *rr) } void -dispatch_mx(struct dns_rr *rr) +dispatch_mx(struct dns_rr *rr, struct target *tgt) { char buf[512]; + struct target ltgt; if (rr->rr_type != T_MX) return; @@ -237,12 +291,16 @@ dispatch_mx(struct dns_rr *rr) buf[strlen(buf) - 1] = '\0'; if (buf[strlen(buf) - 1] == '.') buf[strlen(buf) - 1] = '\0'; - lookup_record(T_A, buf, dispatch_a); - lookup_record(T_AAAA, buf, dispatch_aaaa); + + ltgt = *tgt; + ltgt.dispatch = dispatch_a; + lookup_record(T_A, buf, <gt); + ltgt.dispatch = dispatch_aaaa; + lookup_record(T_AAAA, buf, <gt); } void -dispatch_a(struct dns_rr *rr) +dispatch_a(struct dns_rr *rr, struct target *tgt) { char buffer[512]; const char *ptr; @@ -251,12 +309,16 @@ dispatch_a(struct dns_rr *rr) return; if ((ptr = inet_ntop(AF_INET, &rr->rr.in_a.addr, - buffer, sizeof buffer))) - printf("%s\n", ptr); + buffer, sizeof buffer))) { + if (tgt->cidr4 >= 0) + printf("%s/%d\n", ptr, tgt->cidr4); + else + printf("%s\n", ptr); + } } void -dispatch_aaaa(struct dns_rr *rr) +dispatch_aaaa(struct dns_rr *rr, struct target *tgt) { char buffer[512]; const char *ptr; @@ -265,11 +327,15 @@ dispatch_aaaa(struct dns_rr *rr) return; if ((ptr = inet_ntop(AF_INET6, &rr->rr.in_aaaa.addr6, - buffer, sizeof buffer))) - printf("%s\n", ptr); + buffer, sizeof buffer))) { + if (tgt->cidr6 >= 0) + printf("%s/%d\n", ptr, tgt->cidr6); + else + printf("%s\n", ptr); + } } -static ssize_t +ssize_t parse_txt(const char *rdata, size_t rdatalen, char *dst, size_t dstsz) { size_t len; @@ -303,3 +369,33 @@ parse_txt(const char *rdata, size_t rdatalen, char *dst, size_t dstsz) return r; } + +int +parse_target(char *record, struct target *tgt) +{ + const char *err; + char *m4, *m6; + + m4 = record; + strsep(&m4, "/"); + if (m4 == NULL) + return 0; + + m6 = m4; + strsep(&m6, "/"); + + if (*m4) { + tgt->cidr4 = strtonum(m4, 0, 32, &err); + if (err) + return tgt->cidr4 = -1; + } + + if (m6 == NULL) + return 0; + + tgt->cidr6 = strtonum(m6, 0, 128, &err); + if (err) + return tgt->cidr6 = -1; + + return 0; +} diff --git a/smtpd/srs.c b/usr.sbin/smtpd/srs.c index 05737d8d..bb4f4d9e 100644 --- a/smtpd/srs.c +++ b/usr.sbin/smtpd/srs.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> diff --git a/smtpd/ssl.c b/usr.sbin/smtpd/ssl.c index bd18ad61..97f7b1df 100644 --- a/smtpd/ssl.c +++ b/usr.sbin/smtpd/ssl.c @@ -18,6 +18,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -86,7 +88,7 @@ ssl_setup(SSL_CTX **ctxp, struct pki *pki, if (sni_cb) SSL_CTX_set_tlsext_servername_callback(ctx, sni_cb); - SSL_CTX_set_dh_auto(ctx, pki->pki_dhe); + SSL_CTX_set_dh_auto(ctx, 0); SSL_CTX_set_ecdh_auto(ctx, 1); @@ -395,8 +397,13 @@ ssl_load_pkey(const void *data, size_t datalen, char *buf, off_t len, memcpy(exdata, data, datalen); if (rsa) RSA_set_ex_data(rsa, 0, exdata); +#if defined(SUPPORT_ECDSA) if (eckey) ECDSA_set_ex_data(eckey, 0, exdata); +#else + if (eckey) + EC_KEY_set_ex_data(eckey, 0, exdata); +#endif RSA_free(rsa); /* dereference, will be cleaned up with pkey */ EC_KEY_free(eckey); /* dereference, will be cleaned up with pkey */ } diff --git a/smtpd/ssl.h b/usr.sbin/smtpd/ssl.h index 1c0dc072..11c80c68 100644 --- a/smtpd/ssl.h +++ b/usr.sbin/smtpd/ssl.h @@ -65,6 +65,7 @@ int ssl_ctx_fake_private_key(SSL_CTX *, const void *, size_t, /* ssl_privsep.c */ int ssl_by_mem_ctrl(X509_LOOKUP *, int, const char *, long, char **); +int SSL_CTX_use_certificate_chain_mem(SSL_CTX *, void *, int); /* ssl_verify.c */ int ssl_check_name(X509 *, const char *, int *); diff --git a/smtpd/ssl_smtpd.c b/usr.sbin/smtpd/ssl_smtpd.c index 1f1e62d2..4e5b7e75 100644 --- a/smtpd/ssl_smtpd.c +++ b/usr.sbin/smtpd/ssl_smtpd.c @@ -18,6 +18,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> diff --git a/smtpd/ssl_verify.c b/usr.sbin/smtpd/ssl_verify.c index 02148862..2e784b97 100644 --- a/smtpd/ssl_verify.c +++ b/usr.sbin/smtpd/ssl_verify.c @@ -17,6 +17,7 @@ /* Adapted from lib/libtls/tls_verify.c */ +#include "includes.h" #include <sys/socket.h> diff --git a/smtpd/stat_backend.c b/usr.sbin/smtpd/stat_backend.c index 027f37a5..30cb299b 100644 --- a/smtpd/stat_backend.c +++ b/usr.sbin/smtpd/stat_backend.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/socket.h> #include <sys/queue.h> diff --git a/smtpd/stat_ramstat.c b/usr.sbin/smtpd/stat_ramstat.c index ede2e130..bbf1541a 100644 --- a/smtpd/stat_ramstat.c +++ b/usr.sbin/smtpd/stat_ramstat.c @@ -15,6 +15,7 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" #include <sys/types.h> #include <sys/socket.h> diff --git a/smtpd/table.5 b/usr.sbin/smtpd/table.5 index e9d4fa4b..e9d4fa4b 100644 --- a/smtpd/table.5 +++ b/usr.sbin/smtpd/table.5 diff --git a/smtpd/table.c b/usr.sbin/smtpd/table.c index b79451ca..469eeee1 100644 --- a/smtpd/table.c +++ b/usr.sbin/smtpd/table.c @@ -17,6 +17,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -43,7 +45,9 @@ struct table_backend *table_backend_lookup(const char *); extern struct table_backend table_backend_static; +#ifdef HAVE_DB_API extern struct table_backend table_backend_db; +#endif extern struct table_backend table_backend_getpwnam; extern struct table_backend table_backend_proc; @@ -56,7 +60,9 @@ static unsigned int last_table_id = 0; static struct table_backend *backends[] = { &table_backend_static, +#ifdef HAVE_DB_API &table_backend_db, +#endif &table_backend_getpwnam, &table_backend_proc, NULL @@ -395,7 +401,7 @@ table_netaddr_match(const char *s1, const char *s2) return 0; if (n1.ss.ss_family != n2.ss.ss_family) return 0; - if (n1.ss.ss_len != n2.ss.ss_len) + if (SS_LEN(&n1.ss) != SS_LEN(&n2.ss)) return 0; return table_match_mask(&n1.ss, &n2); } @@ -648,7 +654,9 @@ parse_sockaddr(struct sockaddr *sa, int family, const char *str) sin = (struct sockaddr_in *)sa; memset(sin, 0, sizeof *sin); +#ifdef HAVE_STRUCT_SOCKADDR_IN_SIN_LEN sin->sin_len = sizeof(struct sockaddr_in); +#endif sin->sin_family = PF_INET; sin->sin_addr.s_addr = ina.s_addr; return (0); @@ -673,7 +681,9 @@ parse_sockaddr(struct sockaddr *sa, int family, const char *str) sin6 = (struct sockaddr_in6 *)sa; memset(sin6, 0, sizeof *sin6); +#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN sin6->sin6_len = sizeof(struct sockaddr_in6); +#endif sin6->sin6_family = PF_INET6; sin6->sin6_addr = in6a; @@ -682,7 +692,7 @@ parse_sockaddr(struct sockaddr *sa, int family, const char *str) if (IN6_IS_ADDR_LINKLOCAL(&in6a) || IN6_IS_ADDR_MC_LINKLOCAL(&in6a) || - IN6_IS_ADDR_MC_INTFACELOCAL(&in6a)) + IN6_IS_ADDR_MC_NODELOCAL(&in6a)) if ((sin6->sin6_scope_id = if_nametoindex(cp))) return (0); diff --git a/smtpd/table_db.c b/usr.sbin/smtpd/table_db.c index daa6a3f8..f7d766dd 100644 --- a/smtpd/table_db.c +++ b/usr.sbin/smtpd/table_db.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/stat.h> #include <sys/queue.h> @@ -24,8 +26,13 @@ #include <netinet/in.h> #include <arpa/inet.h> - +#ifdef HAVE_DB_H #include <db.h> +#elif defined(HAVE_DB1_DB_H) +#include <db1/db.h> +#elif defined(HAVE_DB_185_H) +#include <db_185.h> +#endif #include <ctype.h> #include <err.h> #include <event.h> diff --git a/smtpd/table_getpwnam.c b/usr.sbin/smtpd/table_getpwnam.c index 78e6edc2..ccf889be 100644 --- a/smtpd/table_getpwnam.c +++ b/usr.sbin/smtpd/table_getpwnam.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> diff --git a/smtpd/table_proc.c b/usr.sbin/smtpd/table_proc.c index dad041a4..44589bd7 100644 --- a/smtpd/table_proc.c +++ b/usr.sbin/smtpd/table_proc.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -26,7 +28,9 @@ #include <event.h> #include <fcntl.h> #include <imsg.h> +#ifdef HAVE_PATHS_H #include <paths.h> +#endif #include <stdio.h> #include <stdlib.h> #include <string.h> diff --git a/smtpd/table_static.c b/usr.sbin/smtpd/table_static.c index f9519cb4..8f78ae11 100644 --- a/smtpd/table_static.c +++ b/usr.sbin/smtpd/table_static.c @@ -17,6 +17,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> diff --git a/smtpd/to.c b/usr.sbin/smtpd/to.c index 052a2354..81a1bb54 100644 --- a/smtpd/to.c +++ b/usr.sbin/smtpd/to.c @@ -18,6 +18,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -56,12 +58,14 @@ static int alias_is_filename(struct expandnode *, const char *, size_t); static int alias_is_include(struct expandnode *, const char *, size_t); static int alias_is_error(struct expandnode *, const char *, size_t); +static int broken_inet_net_pton_ipv6(const char *, void *, size_t); + const char * sockaddr_to_text(struct sockaddr *sa) { static char buf[NI_MAXHOST]; - if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf), NULL, 0, + if (getnameinfo(sa, SA_LEN(sa), buf, sizeof(buf), NULL, 0, NI_NUMERICHOST)) return ("(unknown)"); else @@ -75,7 +79,9 @@ in6addr_to_text(const struct in6_addr *addr) uint16_t tmp16; memset(&sa_in6, 0, sizeof(sa_in6)); +#ifdef HAVE_STRUCT_SOCKADDR_IN6_SIN6_LEN sa_in6.sin6_len = sizeof(sa_in6); +#endif sa_in6.sin6_family = AF_INET6; memcpy(&sa_in6.sin6_addr, addr, sizeof(sa_in6.sin6_addr)); @@ -191,15 +197,20 @@ time_to_text(time_t when) char *day[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; char *month[] = {"Jan","Feb","Mar","Apr","May","Jun", "Jul","Aug","Sep","Oct","Nov","Dec"}; - char *tz; + const char *tz; long offset; lt = localtime(&when); if (lt == NULL || when == 0) fatalx("time_to_text: localtime"); +#if HAVE_STRUCT_TM_TM_GMTOFF offset = lt->tm_gmtoff; tz = lt->tm_zone; +#elif defined HAVE_DECL_ALTZONE && defined HAVE_DECL_TIMEZONE + offset = lt->tm_isdst > 0 ? altzone : timezone; + tz = lt->tm_isdst > 0 ? tzname[1] : tzname[0]; +#endif /* We do not use strftime because it is subject to locale substitution*/ if (!bsnprintf(buf, sizeof(buf), @@ -283,7 +294,9 @@ text_to_netaddr(struct netaddr *netaddr, const char *s) if (bits != -1) { ssin.sin_family = AF_INET; memcpy(&ss, &ssin, sizeof(ssin)); +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN ss.ss_len = sizeof(struct sockaddr_in); +#endif } else { if (s[0] != '[') { if ((len = strlcpy(buf, s, sizeof buf)) >= sizeof buf) @@ -301,11 +314,19 @@ text_to_netaddr(struct netaddr *netaddr, const char *s) } bits = inet_net_pton(AF_INET6, buf, &ssin6.sin6_addr, sizeof(struct in6_addr)); - if (bits == -1) - return 0; + if (bits == -1) { + if (errno != EAFNOSUPPORT) + return 0; + bits = broken_inet_net_pton_ipv6(buf, &ssin6.sin6_addr, + sizeof(struct in6_addr)); + if (bits == -1) + return 0; + } ssin6.sin6_family = AF_INET6; memcpy(&ss, &ssin6, sizeof(ssin6)); +#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_LEN ss.ss_len = sizeof(struct sockaddr_in6); +#endif } netaddr->ss = ss; @@ -825,3 +846,35 @@ alias_is_error(struct expandnode *alias, const char *line, size_t len) alias->type = EXPAND_ERROR; return 1; } + +static int +broken_inet_net_pton_ipv6(const char *src, void *dst, size_t size) +{ + int ret; + int bits; + char buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255:255:255:255/128")]; + char *sep; + const char *errstr; + + if (strlcpy(buf, src, sizeof buf) >= sizeof buf) { + errno = EMSGSIZE; + return (-1); + } + + sep = strchr(buf, '/'); + if (sep != NULL) + *sep++ = '\0'; + + ret = inet_pton(AF_INET6, buf, dst); + if (ret != 1) + return (-1); + + if (sep == NULL) + return 128; + + bits = strtonum(sep, 0, 128, &errstr); + if (errstr) + return (-1); + + return bits; +} diff --git a/smtpd/tree.c b/usr.sbin/smtpd/tree.c index 70aef047..1d720a59 100644 --- a/smtpd/tree.c +++ b/usr.sbin/smtpd/tree.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/tree.h> diff --git a/smtpd/tree.h b/usr.sbin/smtpd/tree.h index 3d719f09..3d719f09 100644 --- a/smtpd/tree.h +++ b/usr.sbin/smtpd/tree.h diff --git a/smtpd/unpack_dns.c b/usr.sbin/smtpd/unpack_dns.c index fe50b026..974d5727 100644 --- a/smtpd/unpack_dns.c +++ b/usr.sbin/smtpd/unpack_dns.c @@ -16,6 +16,11 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + +#ifdef HAVE_ARPA_NAMESER_COMPAT_H +#include <arpa/nameser_compat.h> +#endif #include <arpa/inet.h> #include <string.h> diff --git a/smtpd/unpack_dns.h b/usr.sbin/smtpd/unpack_dns.h index 2318a0c5..2318a0c5 100644 --- a/smtpd/unpack_dns.h +++ b/usr.sbin/smtpd/unpack_dns.h diff --git a/smtpd/util.c b/usr.sbin/smtpd/util.c index f59ad1e4..5f9d2113 100644 --- a/smtpd/util.c +++ b/usr.sbin/smtpd/util.c @@ -1,4 +1,4 @@ -/* $OpenBSD: util.c,v 1.150 2019/10/03 04:49:12 gilles Exp $ */ +/* $OpenBSD: util.c,v 1.152 2020/11/29 20:07:38 tb Exp $ */ /* * Copyright (c) 2000,2001 Markus Friedl. All rights reserved. @@ -19,6 +19,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/queue.h> #include <sys/tree.h> @@ -176,7 +178,7 @@ bsnprintf(char *str, size_t size, const char *format, ...) va_start(ap, format); ret = vsnprintf(str, size, format, ap); va_end(ap); - if (ret < 0 || ret >= (int)size) + if (ret < 0 || (size_t)ret >= size) return 0; return 1; @@ -462,7 +464,7 @@ valid_domainpart(const char *s) if (strlcpy(domain, p, sizeof domain) >= sizeof domain) return 0; - c = strchr(domain, (int)']'); + c = strchr(domain, ']'); if (!c || c[1] != '\0') return 0; @@ -489,7 +491,7 @@ valid_domainpart(const char *s) return res_hnok(s); } -#define LABELCHR(c) ((c) == '-' || (c) == '_' || isalpha((int)(c)) || isdigit((int)(c))) +#define LABELCHR(c) ((c) == '-' || (c) == '_' || isalpha((unsigned char)(c)) || isdigit((unsigned char)(c))) #define LABELMAX 63 #define DNAMEMAX 253 @@ -842,3 +844,27 @@ log_trace_verbose(int v) /* Set debug logging in log.c */ log_setverbose(v & TRACE_DEBUG ? 2 : foreground_log); } + +void +xclosefrom(int lowfd) +{ +#if defined HAVE_CLOSEFROM_INT + if (closefrom(lowfd) == -1) + err(1, "closefrom"); +#else + closefrom(lowfd); +#endif +} + +void +portable_freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *p; + + do { + p = ai; + ai = ai->ai_next; + free(p->ai_canonname); + free(p); + } while (ai); +} diff --git a/smtpd/waitq.c b/usr.sbin/smtpd/waitq.c index dc459372..082a1e51 100644 --- a/smtpd/waitq.c +++ b/usr.sbin/smtpd/waitq.c @@ -16,6 +16,8 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" + #include <sys/types.h> #include <sys/socket.h> #include <sys/queue.h> |