pax_global_header00006660000000000000000000000064152535522610014520gustar00rootroot0000000000000052 comment=04cd8c2b6e72ed490b814dfc8e9ced7e1a2906de opensmtpd-7.9.0p0/000077500000000000000000000000001525355226100140065ustar00rootroot00000000000000opensmtpd-7.9.0p0/.builds/000077500000000000000000000000001525355226100153465ustar00rootroot00000000000000opensmtpd-7.9.0p0/.builds/freebsd.yml000066400000000000000000000006641525355226100175110ustar00rootroot00000000000000image: freebsd/latest packages: - autoconf - automake - bison - libevent - libtool - libressl - libasr - py37-ansible - db5 - 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" opensmtpd-7.9.0p0/.builds/openbsd.yml000066400000000000000000000004141525355226100175220ustar00rootroot00000000000000image: 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 opensmtpd-7.9.0p0/.gitattributes000066400000000000000000000000241525355226100166750ustar00rootroot00000000000000*/CVS export-ignore opensmtpd-7.9.0p0/.github/000077500000000000000000000000001525355226100153465ustar00rootroot00000000000000opensmtpd-7.9.0p0/.github/FUNDING.yml000066400000000000000000000000441525355226100171610ustar00rootroot00000000000000github: [poolpOrg] patreon: gilles opensmtpd-7.9.0p0/.github/workflows/000077500000000000000000000000001525355226100174035ustar00rootroot00000000000000opensmtpd-7.9.0p0/.github/workflows/alpine-release.yml000066400000000000000000000023051525355226100230140ustar00rootroot00000000000000name: Alpine Linux release on: push: tags: env: IMAGE_NAME: "opensmtpd" jobs: build: permissions: write-all runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: Build the Dockerfile run: docker build . --file ci/docker/Dockerfile.alpine-release --tag opensmtpd:alpine - name: Login to ghcr.io uses: docker/login-action@v2 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} # run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin - name: Push Image run: | IMAGE_ID=ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME # Change all uppercase to lowercase IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]') # Strip git ref prefix from version VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,') # Strip "v" prefix from tag name # [[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//') echo IMAGE_ID=$IMAGE_ID echo VERSION=$VERSION docker tag opensmtpd:alpine $IMAGE_ID:$VERSION docker push $IMAGE_ID:$VERSION opensmtpd-7.9.0p0/.github/workflows/alpine.yml000066400000000000000000000005261525355226100214010ustar00rootroot00000000000000name: 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 opensmtpd-7.9.0p0/.github/workflows/arch.yml000066400000000000000000000005321525355226100210430ustar00rootroot00000000000000name: 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 opensmtpd-7.9.0p0/.github/workflows/clang.yml000066400000000000000000000017371525355226100212220ustar00rootroot00000000000000name: Clang Scan on: schedule: - cron: '0 23 * * *' # Daily at 23:00 UTC jobs: scan: runs-on: ubuntu-latest steps: - name: install dependencies run: | wget https://apt.llvm.org/llvm.sh chmod +x llvm.sh sudo ./llvm.sh 16 sudo apt install \ clang-tools-16 \ libevent-dev \ libssl-dev \ bison - uses: actions/checkout@v1 - name: run scan-build run: | export PATH=$PATH:/usr/lib/llvm-16/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 }} opensmtpd-7.9.0p0/.github/workflows/coverity.yml000066400000000000000000000007111525355226100217710ustar00rootroot00000000000000name: Coverity Scan on: schedule: - cron: '0 23 * * *' # Daily at 23:00 UTC jobs: scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: dependencies run: sudo apt install autoconf automake bison build-essential \ libevent-dev libssl-dev libtool pkgconf libpam0g-dev zlib1g-dev - name: scan run: sh ci/scripts/coverity_scan.sh env: token: ${{ secrets.COVERITY_SCAN_TOKEN }} opensmtpd-7.9.0p0/.github/workflows/fedora-db.yml000066400000000000000000000005271525355226100217550ustar00rootroot00000000000000name: Fedora (amd64 glibc openssl db) on: push: branches: - master pull_request: branches: - master jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: Fedora (amd64 glibc openssl db) run: docker build . --file ci/docker/Dockerfile.fedora-db --tag opensmtpd:fedora-db opensmtpd-7.9.0p0/.github/workflows/fedora.yml000066400000000000000000000005141525355226100213660ustar00rootroot00000000000000name: 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 opensmtpd-7.9.0p0/.github/workflows/macos-latest.yml000066400000000000000000000013771525355226100225320ustar00rootroot00000000000000name: macOS Catalina 10.15 on: # TODO: Fix build on macOS, remove the next line and uncomment the following lines. - workflow_dispatch # allow manual trigger #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 opensmtpd-7.9.0p0/.github/workflows/opensuse-tumbleweed.yml000066400000000000000000000006001525355226100241160ustar00rootroot00000000000000name: openSUSE Tumbleweed (amd64 glibc openssl) on: push: branches: - master pull_request: branches: - master jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: openSUSE Tumbleweed (amd64 glibc openssl) run: docker build . --file ci/docker/Dockerfile.opensuse-tumbleweed --tag opensmtpd:opensuse-tumbleweed opensmtpd-7.9.0p0/.github/workflows/ubuntu-gcc16.yml000066400000000000000000000005441525355226100223540ustar00rootroot00000000000000name: Ubuntu (amd64 glibc openssl gcc16) on: push: branches: - master pull_request: branches: - master jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: Ubuntu (amd64 glibc openssl gcc16) run: docker build . --file ci/docker/Dockerfile.ubuntu-gcc16 --tag opensmtpd:ubuntu-gcc16 opensmtpd-7.9.0p0/.github/workflows/ubuntu.yml000066400000000000000000000005141525355226100214500ustar00rootroot00000000000000name: 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 opensmtpd-7.9.0p0/.gitignore000066400000000000000000000017661525355226100160100ustar00rootroot00000000000000**/*.a **/*.d **/*.in **/*.log **/*.o **/*.out **/.#* **/.deps **/.dirstamp **/parse.c aclocal.m4 ar-lib autom4te.cache compile config.guess config.h config.status config.sub configure depcomp install-sh libtool ltmain.sh m4 missing obj stamp-h1 tags ylwrap #auto-generated makefiles Makefile contrib/Makefile contrib/**/Makefile mk/Makefile mk/**/Makefile openbsd-compat/Makefile #binaries contrib/libexec/encrypt/encrypt contrib/libexec/lockspool/lockspool contrib/libexec/mail.local/mail.local mk/mail/mail.lmtp/mail.lmtp mk/mail/mail.maildir/mail.maildir mk/mail/mail.mboxfile/mail.mboxfile mk/mail/mail.mda/mail.mda mk/smtp/smtp mk/smtpctl/smtpctl mk/smtpd/smtpd #Other VCS files CVS/ smtpd/CVS smtpd/smtpctl/CVS smtpd/smtpd/CVS 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 opensmtpd-7.9.0p0/CHANGES.md000066400000000000000000000373311525355226100154070ustar00rootroot00000000000000# Release 7.9.0p0 (2026-09-13) This includes also the OpenBSD 7.9 errata `002` and `015` (all the ones released as of today.) - Restricted the characters allowed through when expanding variables with the raw modifier, both in the MDA command line and in its environment. - Don't lowercase K_AUTH lookups. The lookup key is "user:password", so passwords were being case-flattened before reaching the table. - Reject CR and LF in the sender, recipients and DSN options taken from the sendmail(8) command line, which could otherwise inject additional SMTP commands into the local dialogue. - Reject empty or slash-bearing usernames before building the mbox path. - Fixed a possible out of bounds access in dname_expand(). - Fixed a possible out of bounds read in text_to_netaddr(). - Stop parsing DNS answers once unpack_rr() reports a failure, instead of looping on the attacker-controlled answer count. - Validate encrypted queue buffer sizes before processing the auth tag and IV data. - Hardened the privsep environment: reject oversized sockaddr payloads received over IPC, clear userinfo before sending it over imsg, zero the temporary envelope parsing buffers, and drop pending asynchronous lookup references when a session is torn down. - EHLO now resets the transaction, as required by RFC 5321. - Return 452 instead of 451 when there are too many recipients. - "smtp limit max-rcpt" now requires a value of at least 100, per RFC 5321. - Removed "support" for the WIZ command. - Fixed using modifiers together with partial expansion in format specifiers, for example %{sender.user[6:]:strip}. - Fixed a leak of the inflate stream on every read from a compressed queue. - Fixed leaks of mda_subaddress and dispatcher in mda_envelope_free(), and of the copied envelope on the smarthost lookup failure path. - Unlink the transformed temporary file on every queue commit failure, instead of leaving orphaned .comp/.enc files behind. - Updated the bundled imsg and switched smtpd over to imsgbuf_get(). - Various documentation improvements. Two changes in this release can affect existing installations: * A configuration setting "smtp limit max-rcpt" below 100 is now rejected at parse time. * The set of characters accepted in envelope addresses no longer includes $ ` { | } , which are never present in legitimate addresses and are commonly seen in exploit attempts. ## bundled libtls - Added X25519MLKEM768 to the front of the default ECDHE curve list. Curves the installed libcrypto does not know about are skipped when expanding the default list, so this is a no-op before OpenSSL 3.5; a curve requested explicitly is still an error. - Consistently use the "x" variants of the error setting functions where errno carries no useful information, and report TLS_ERROR_OUT_OF_MEMORY rather than TLS_ERROR_UNKNOWN after allocation failures. - Disallow wildcard matching of a TLD specified as a FQDN. smtpd is not believed to be affected. - Added missing length checks before BIO_new_mem_buf(): certificates and keys larger than INT_MAX no longer have their length truncated. - The subject of a certificate is no longer treated as optional. - Abort the handshake when there is no ALPN protocol overlap, as required by RFC 7301. Not reachable from smtpd. - const correctness for X509_NAME, in preparation for OpenSSL 4. - Open certificate and key files with O_CLOEXEC. # Release 7.8.0p1 (2026-03-27) - table_proc: ensure the request does not contain newlines # Release 7.8.0p0 (2025-10-30) - Don't reject single character AUTH PLAIN passwords. - Fix address family typo (PF_INET->PF_INET6). - Various documentation improvements. - Removed support for world-writable mail spools. - Updated contrib mail.local and lockspool. - Don't die if garbage is being sent on the local socket. This release includes the OpenBSD errata 005 which fixes CVE-2025-62875. # Release 7.7.0p0 (2025-05-12) - mail.lmtp: Correctly propagate LMTP permanent failures to smtpd. - Fixed connect filter request documentation in smtpd-filters.7. - Updated to new imsg APIs. # Release 7.6.0p1 (2024-10-20) - fixed distribution tarball - added missing forward(5) documentation fix # Release 7.6.0p0 (2024-10-13) - Introduced a new K_AUTH service to allow offloading the credentials to a proc table for non-crypt(3) authentication. Helps with use cases like LDAP or custom auth. - Implement report responses for proc-filters too. - Changed the table protocol to a simpler text-based one. Existing proc tables needs to be updated since old ones won't work. The new protocol is documented in smtpd-tables(7). - Fixed the parsing of IPv6 addresses in file-backed table(5) - Document expected MDA behavior and the environment set by OpenSMTPD. - Set ORIGINAL_RECIPIENT in the environment of MDA scripts for compatibility with postfix. - Updated the bundled libtls. # Release 7.5.0p0 (2024-04-10) - Added support for RFC 7505 "Null MX" handling and treat an MX of "localhost" as it were a "Null MX". - Allow inline tables and filter listings in smtpd.conf(5) to span over multiple lines. - Enabled DSN for the implicit socket too. - Added the `no-dsn' option for listen on socket too. - Reject headers that start with a space or a tab. - Fixed parsing of the ORCPT parameter. - Fixed table lookups of IPv6 addresses. - Fixed handling of escape characters in To, From and Cc headers. - Run LMTP deliveries as the recipient user again. - Disallow custom commands and file reading in root's .forward file. - Do not process other users .forward files when an alternate delivery user is provided in a dispatcher. - Unify the table(5) parser used in smtpd(8) and makemap(8). - Allow to use table(5) mappings on various match constraints. Portability fixes: - re-add ASR_IPV4_BEFORE_IPV6 compile-time knob to prefer connecting to IPv6 instead of IPv4. - update asr(3) and imsg with OpenBSD. - fixed rpath handling on NetBSD in the configure. # Release 7.4.0p1 (2023-11-16) - Fixed potential crash with LibreSSL versions prior 3.8 due to arc4random_buf() symbol clash. - Fixed manpage install path; reintroduced --with-mantype - Fixed typo in the configure help string: it's --without-libbsd - Fixed a couple of issues on MacOS: - Fixed typo that resulted in the re-declaration of strlcpy() and strlcat() - Cast suseconds_t to long for *printf - Fixed res_hnok() and b64_{pton,ntop}() discovery # Release 7.4.0p0 (2023-10-25) - Avoid truncation of filtered data lines. Lines in the email body passed through a filter were truncated to roughly LINE_MAX bytes. - Allow arguments on NOOP. - Swap link-auth filter arguments and bump filter protocol version. It was ambiguous in the case the user name would contain a '|' character. - Add Message-ID as needed for messages received on the submission port. This was dropped during the incoming message parser refactor in 2018. - Drop ENGINE support. - Updated the bundled copy of libtls. This includes the removal of the support for TLS v1.0 and 1.1 as they were "MUST NOT use" for more than two years already. The neverending cleanup of the -portable layer continued. This includes the complete rework of some parts: - Rework of the configure script: + use AC_SYSTEM_EXTENSIONS + better checks for libraries using AC_SEARCH_LIBS + dropped some useless and/or redundant checks + better checks for functions, shouldn't yield false-positives + various simplification to the -portable layer thanks to these changes - Simplified the `bootstrap` script. # Release 7.3.0p2 (2023-09-20) - avoid potential use of uninitialized in ASN1_time_parse - backport ENGINE removal fix the build with newer LibreSSL # Release 7.3.0p1 (2023-06-30) - add missing include of stdio.h for fparseln(3) on FreeBSD - fix a typo in the configure - use fatal() instead of err(3) in xclosefrom() - don't add "-lcrypto -lssl" thrice - fix the build of the bundled libtls with LibreSSL - force the use of the bundled libtls and libasr - append, not prepend, to LIBS during automatic configuration - do not add -L/usr/local/lib or -L/usr/lib, nor -I/usr/local/include or -I/usr/include as consequence of missing --with-libevent - optionally link libbsd-ctor too # Release 7.3.0p0 (2023-06-17) Includes the following security fixes: - OpenBSD 7.2 errata 20 "smtpd(8) could abort due to a connection from a local, scoped ipv6 address" - OpenBSD 7.2 errata 22 "Out of bounds accesses in libc resolver" ## Configuration changes - The certificate to use is now selected by looking at the names found in the certificates themselves rather than the `pki` name. The set of certificates for a TLS listener must be defined explicitly by using the `pki` listener option multiple times. ## Synced with OpenBSD 7.3 - OpenBSD 6.8: * Run LMTP deliveries as the smtpd user instead of the recipient user. - OpenBSD 6.9: * Introduced smtp(1) `-a` to perform authentication before sending a message. * Fixed a memory leak in smtpd(8) resolver. * Prevented a crash due to premature release of resources by the smtpd(8) filter state machine. * Switch to libtls internally. * Change the way SNI works in smtpd.conf(5). TLS listeners may be configured with multiple certificates. The matching is based on the names included in the certificates. * Allow to specify TLS protocols and ciphers per listener and relay action. - OpenBSD 7.0: * Fixed incorrect status code for expired mails resulting in misleading bounce report in smtpd(8). * Added TLS options `cafile=(path)`, `nosni`, `noverify` and `servername=(name)` to smtp(1). * Allowed specification of TLS ciphers and protocols in smtp(1). - OpenBSD 7.1: * Stop verifying the cert or CA for a relay using opportunistic TLS. * Enabled TLS verify by default for outbound "smtps://" and "smtp+tls://", restoring documented smtpd(8) behavior. - OpenBSD 7.3: * Prevented smtpd(8) abort due to a connection from a local, scoped ipv6 address. ## Portable layer changes - libbsd and libtls are now optionally used if found. + Added `--with-libbsd`/`--without-libbsd` configure flag to enable linking to libbsd-overlay. + Added `--with-bundled-libtls` to force the usage of the bundled libtls. LibreTLS 3.7.0 (last version at the time of writing) and previous have a regression with OpenSSL 3+, so please use the bundled one. See the GitHub issue #1171 for more info. - Updated and cleanup of the OpenBSD compats. + Ported `res_randomid()` from OpenBSD. - The configure option `--with-path-CAfile` shouldn't be required anymore in most systems but it is retained since it could be useful in some configuration when using the bundled libtls. - Various minor portability fixes. # Release 6.8.0p2 (2020-12-24) - Fixed an uninitialized variable and potential stack overflow with IPv6 connections in smtpd(8). - Fixed smtpd(8) handling of user names containing "@" symbols. - Allowed handling of long lines in an smtpd(8) aliases table. - Removed mail.local(8) support for world-writable mail spools. # Release 6.7.1p1 (2020-05-21) - fixes a packaging issue causing asr.h to be installed in target system - fixes a possible crash in the MTA when establishing IPv6 connections # Release 6.7.0p1 (2020-05-21) ## New Features: - Allowed use of the smtpd(8) session username in built-in filters when available. - Introduced a `bypass` keyword to smtpd(8) so that built-in filters can bypass processing when a condition is met. - Allowed use of 'auth' as an origin in smtpd.conf(5). - Allowed use of mail-from and rctp-to as for and from parameters in smtpd.conf(5). ## Bug fixes: - Ensured legacy ssl(8) session ID is persistent during a client TLS session, fixing an issue using TLSv1.3 with smtp.mail.yahoo.com. - Fixed security vulnerabilities in smtpd(8). Corrected an out-of-bounds read in smtpd allowing an attacker to inject arbitrary commands into the envelope file to be executed as root, and ensured privilege revocation in smtpctl(8) to prevent arbitrary commands from being run with the _smtpq group. - Allowed mail.local(8) to be run as non-root, opening a pipe to lockspool(1) for file locking. - Fixed a security vulnerability in smtpd(8) which could lead to a privilege escalation on mbox deliveries and unprivileged code execution on lmtp deliveries. - Added support for CIDR in a: spf atoms in smtpd(8). - Fixed a possible crash in smtpd(8) when combining "from rdns" with nested virtual aliases under a particular configuration. ## Experimental Features: - Introduced smtp-out event reporting. - Improved filtering protocol. # Release 6.6.4p1 (2020-02-24) An out of bounds read in smtpd allows an attacker to inject arbitrary commands into the envelope file which are then executed as root. Separately, missing privilege revocation in smtpctl allows arbitrary commands to be run with the _smtpq group. # Release 6.6.3p1 (2020-02-10) 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. - Fixed crash on recipient expansion [#968](https://github.com/OpenSMTPD/OpenSMTPD/issues/968) - Fixed broken build with LibreSSL [#944](https://github.com/OpenSMTPD/OpenSMTPD/issues/944) - 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) [#967](https://github.com/OpenSMTPD/OpenSMTPD/issues/967) [#978](https://github.com/OpenSMTPD/OpenSMTPD/issues/978) [#977](https://github.com/OpenSMTPD/OpenSMTPD/issues/977) [#975](https://github.com/OpenSMTPD/OpenSMTPD/issues/975) # Release 6.6.0p1 (2019-10-26) ## Dependencies note: This release builds with LibreSSL > 3.0.2 or OpenSSL > 1.1.0. It's preferable to depend on LibreSSL as OpenSMTPD is written and tested with that dependency. In addition, the features parity is not respected, some features will not be available with OpenSSL, like ECDSA server-side certificates support in this release. OpenSSL library is considered as a best effort target TLS library and provided as a commodity, LibreSSL has become our target TLS library. ## Changes in this release (since 6.4.0): - various improvements to documentation and code - reverse dns session matching criteria added to smtpd.conf(5) - regex table lookup support added to smtpd.conf(5) - introduced support for ECDSA certificates with an ECDSA privsep engine - introduced builtin filters for basic filtering of incoming sessions - introduced option to deliver junk to a Junk folder in mail.maildir(8) - fixed the smtp(1) client so it uses correct default port for SMTPS - fixed an smtpd(8) crash on excessively large input - ensured mail rejected by an LMTP server stay queued ## Experimental features: - introduced a filters API to allow writing standalone filters for smtpd - introduced proxy-v2 support allowing smtpd to operate behind a proxy opensmtpd-7.9.0p0/INSTALL000066400000000000000000000224501525355226100150420ustar00rootroot00000000000000Installation 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. opensmtpd-7.9.0p0/LICENSE000066400000000000000000000356671525355226100150340ustar00rootroot00000000000000This 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 , 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 # # 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 opensmtpd-7.9.0p0/Makefile.am000066400000000000000000000002301525355226100160350ustar00rootroot00000000000000SUBDIRS = mk contrib openbsd-compat EXTRA_DIST = CHANGES.md \ LICENSE \ bootstrap \ etc/README.md \ etc/aliases ACLOCAL_AMFLAGS = -I m4 opensmtpd-7.9.0p0/README.md000066400000000000000000000202521525355226100152660ustar00rootroot00000000000000# OpenSMTPD [![Version](https://img.shields.io/badge/Version-7.9.0p0-brihtgreen.svg)](https://github.com/OpenSMTPD/OpenSMTPD/releases/tag/7.9.0p0) [![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/) > **Warning** > This repository may be out of date compared to [OpenBSD smtpd source code](https://cvsweb.openbsd.org/src/usr.sbin/smtpd/). Downstream package maintainers should be aware of this when backporting security fixes and other changes. 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 various [Unix](https://en.wikipedia.org/wiki/Unix) and Unix-like operating systems including: - Linux - [FreeBSD](https://www.freebsd.org) - [OpenBSD](https://www.openbsd.org) - [NetBSD](https://www.netbsd.org) - [DragonFlyBSD](https://www.dragonflybsd.org) - [macOS](https://en.wikipedia.org/wiki/MacOS) For more information: http://www.opensmtpd.org/portable.html If you are looking for a comprehensive manual on how to build your own mail server visit our [wiki](https://github.com/OpenSMTPD/OpenSMTPD/wiki). ## Get in touch If you want to stay up to day with most recent developments or chat about OpenSMTPD you can: - subscribe to our [mailing list](http://www.opensmtpd.org/list.html) - join the IRC channel: `#opensmtpd` @ [irc.libera.chat](https://libera.chat) - submit a bug report or a feature request here on [GitHub](https://github.com/OpenSMTPD/OpenSMTPD) - visit GitHub's [discussions page](https://github.com/OpenSMTPD/OpenSMTPD/discussions) ## Documentation The manual pages are available [online](https://www.opensmtpd.org/manual.html), which you are encouraged to contribute to. ## Install via package manager Many distributions already provide a packaged version of opensmtpd. All you need to do is install it via your package manager. > **Warning** Some distributions might ship old versions of OpenSMTPD, and some distributions may selectively backport security fixes and other code changes. ### Debian/Ubuntu sudo apt install opensmtpd ### Archlinux Has a [dedicated wiki page](https://wiki.archlinux.org/index.php/OpenSMTPD#Installation) ### Alpine Linux apk install opensmtpd ### Fedora yum install opensmtpd ### openSUSE Tumbleweed zypper install OpenSMTPD ### macOS OpenSMTPD is available from [MacPorts](https://www.macports.org): port install opensmtpd ## Install via container Container images available at [this repo's packages page.](https://github.com/orgs/OpenSMTPD/packages) ## Install from source ### Install dependencies OpenSMTPD relies on: - [pkgconf](https://github.com/pkgconf/pkgconf) or [pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config/) - [libevent](http://libevent.org/) - [libressl](https://www.libressl.org/) or [OpenSSL](https://www.openssl.org/) When not building from a release tarball (e.g. from the git repository), the following dependencies are needed too: - [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) - [libtool](http://www.gnu.org/software/libtool/) 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 code Clone from github: git clone https://github.com/OpenSMTPD/OpenSMTPD.git [Download tarball](https://github.com/OpenSMTPD/OpenSMTPD/archive/7.6.0p1.tar.gz) Latest release can always be found [here](https://github.com/OpenSMTPD/OpenSMTPD/releases/latest) ### Compile cd opensmtpd* ./bootstrap # Only if you build from git sources ./configure make sudo make install #### Special notes for macOS Please launch configure with special directive about libevent directory: ./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 accommodate 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 accommodate all systems with the preferred 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 #### macOS 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 opensmtpd-7.9.0p0/bootstrap000077500000000000000000000000411525355226100157440ustar00rootroot00000000000000#! /bin/sh exec autoreconf -vfi opensmtpd-7.9.0p0/ci/000077500000000000000000000000001525355226100144015ustar00rootroot00000000000000opensmtpd-7.9.0p0/ci/COVERITY.MD5SUM000066400000000000000000000000751525355226100165430ustar00rootroot00000000000000d0d7d7df9d6609e578f85096a755fb8f ./cov-analysis-linux64.tgz opensmtpd-7.9.0p0/ci/README.md000066400000000000000000000046251525355226100156670ustar00rootroot00000000000000# 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: libressl, 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 structure - [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 resources 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) opensmtpd-7.9.0p0/ci/docker/000077500000000000000000000000001525355226100156505ustar00rootroot00000000000000opensmtpd-7.9.0p0/ci/docker/Dockerfile.alpine000066400000000000000000000023441525355226100211140ustar00rootroot00000000000000FROM alpine:3.15 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 \ openssl \ openssl-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 opensmtpd-7.9.0p0/ci/docker/Dockerfile.alpine-release000066400000000000000000000032471525355226100225350ustar00rootroot00000000000000FROM alpine:3.22 as build WORKDIR /opensmtpd RUN apk add --no-cache \ autoconf \ automake \ bison \ ca-certificates \ fts-dev \ gcc \ fts \ libevent-dev \ libtool \ linux-pam-dev \ make \ musl-dev \ openssl \ openssl-dev \ zlib-dev 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 \ && mkdir ./install COPY . /opensmtpd RUN ./bootstrap \ && ./configure \ --with-gnu-ld \ --sysconfdir=/etc/mail \ --with-auth-pam \ && make \ && make install # --- BEGIN Production deployment dockerfile --- # FROM alpine:3.22 AS deploy 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 RUN apk add --no-cache libevent \ openssl \ linux-pam \ fts # Trailing slashes are important! RUN mkdir -p /usr/local/libexec/opensmtpd/ COPY --from=build /usr/local/libexec/opensmtpd/* /usr/local/libexec/opensmtpd/ COPY --from=build /usr/local/sbin/smtpctl /usr/local/sbin/ COPY --from=build /usr/local/sbin/smtpd /usr/local/sbin/ COPY --from=build /usr/local/bin/smtp /usr/local/bin/ COPY --from=build /etc/mail/smtpd.conf /etc/mail/ COPY --from=build /opensmtpd/etc/aliases /etc/mail/aliases ENTRYPOINT ["smtpd", "-d"] opensmtpd-7.9.0p0/ci/docker/Dockerfile.archlinux000066400000000000000000000024511525355226100216400ustar00rootroot00000000000000FROM 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 \ pkgconf \ bison \ gettext \ libevent \ openssl \ 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-path-empty=/var/lib/opensmtpd/empty \ --with-auth-pam \ && make \ && make install \ && cp etc/aliases /etc/mail/aliases opensmtpd-7.9.0p0/ci/docker/Dockerfile.fedora000066400000000000000000000023451525355226100211050ustar00rootroot00000000000000FROM 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 \ gawk \ @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 opensmtpd-7.9.0p0/ci/docker/Dockerfile.fedora-db000066400000000000000000000024151525355226100214660ustar00rootroot00000000000000FROM 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 \ gawk \ @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 ./bootstrap \ && ./configure \ --with-gnu-ld \ --sysconfdir=/etc/mail \ --with-auth-pam \ --with-table-db \ && make \ && make install \ && cp etc/aliases /etc/mail/aliases opensmtpd-7.9.0p0/ci/docker/Dockerfile.opensuse-tumbleweed000066400000000000000000000023761525355226100236450ustar00rootroot00000000000000FROM opensuse/tumbleweed: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 zypper --non-interactive install \ autoconf \ automake \ bison \ gawk \ make \ gcc \ 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 opensmtpd-7.9.0p0/ci/docker/Dockerfile.ubuntu000066400000000000000000000024141525355226100211640ustar00rootroot00000000000000FROM 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 \ pkgconf \ 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 opensmtpd-7.9.0p0/ci/docker/Dockerfile.ubuntu-gcc16000066400000000000000000000025001525355226100220610ustar00rootroot00000000000000FROM 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 \ pkgconf \ libpam0g-dev \ zlib1g-dev \ gcc-16 \ g++-16 # 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 ENV CC=gcc-16 ENV CXX=g++-16 RUN ./bootstrap \ && ./configure \ --with-gnu-ld \ --sysconfdir=/etc/mail \ --with-auth-pam \ && make \ && make install \ && cp etc/aliases /etc/mail/aliases opensmtpd-7.9.0p0/ci/scripts/000077500000000000000000000000001525355226100160705ustar00rootroot00000000000000opensmtpd-7.9.0p0/ci/scripts/clang_scan.sh000077500000000000000000000030041525355226100205140ustar00rootroot00000000000000#!/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 opensmtpd-7.9.0p0/ci/scripts/coverity_scan.sh000077500000000000000000000037761525355226100213140ustar00rootroot00000000000000#!/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 opensmtpd-7.9.0p0/ci/scripts/generate_certs.sh000077500000000000000000000010711525355226100214200ustar00rootroot00000000000000#!/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 opensmtpd-7.9.0p0/ci/scripts/imports/000077500000000000000000000000001525355226100175655ustar00rootroot00000000000000opensmtpd-7.9.0p0/ci/scripts/imports/badge.sh000066400000000000000000000046261525355226100211730ustar00rootroot00000000000000#!/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 } opensmtpd-7.9.0p0/ci/scripts/ssl.conf000066400000000000000000000005711525355226100175430ustar00rootroot00000000000000[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 opensmtpd-7.9.0p0/configure.ac000066400000000000000000001132071525355226100163000ustar00rootroot00000000000000# Copyright (c) 2023 Omar Polo # Copyright (c) 2016 Gilles Chehade # 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. AC_INIT([OpenSMTPD-portable], [7.9.0p0], [bugs@opensmtpd.org], [opensmtpd], [https://www.opensmtpd.org]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_LIBOBJ_DIR([openbsd-compat]) AC_CANONICAL_HOST AM_INIT_AUTOMAKE([-Wall foreign subdir-objects]) AC_USE_SYSTEM_EXTENSIONS AM_PROG_AR AC_PROG_CC AC_C_INLINE AC_PROG_INSTALL AC_PROG_EGREP PKG_PROG_PKG_CONFIG AC_PROG_YACC LT_INIT 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]) # use libbsd by default; these needs to precede any other check, as we # might not need a compat function when using libbsd. AC_ARG_WITH([libbsd], AS_HELP_STRING([--without-libbsd], [Disable libbsd usage])) AS_IF([test "x$with_libbsd" != xno], [ PKG_CHECK_MODULES([LIBBSD], [libbsd-overlay libbsd-ctor], [ CFLAGS="$LIBBSD_CFLAGS $CFLAGS" LDFLAGS="$LIBBSD_LIBS $LDFLAGS" ], [AS_IF([test "x$with_libbsd" = "xyes"], [ AC_MSG_ERROR([libbsd requested but not found]) ])]) ]) need_dash_r= need_libasr=yes pkglibexecdir="$libexecdir/opensmtpd" AS_CASE(["$host"], [*-*-darwin*], [ use_pie=auto 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([SPT_TYPE], [SPT_REUSEARGV], [Define to a Set Process Title type if your system is supported by bsd-setproctitle.c]) ], [*-*-linux* | *-gnu* | *-k*bsd*-gnu*], [ use_pie=auto AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV]) ], [*-*-netbsd*], [ need_dash_r=yes AM_CFLAGS="$AM_CFLAGS -D_OPENBSD_SOURCE" ], [*-*-openbsd*], [ use_pie=auto need_libasr=no pkglibexecdir="$libexecdir/smtpd" AC_DEFINE([HAVE_ATTRIBUTE__SENTINEL__], [1], [OpenBSD's gcc has sentinel]) AC_DEFINE([HAVE_ATTRIBUTE__BOUNDED__], [1], [OpenBSD's gcc has bounded]) 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]) ]) AC_SUBST([pkglibexecdir]) # XXX arc4random is checked later since some old (pre 3.8) versions of # LibreSSL provide this symbol mistakenly. AC_REPLACE_FUNCS([ \ basename \ clock_gettime \ closefrom \ crypt_checkpass \ daemon \ dirname \ errc \ explicit_bzero \ fgetln \ freezero \ getdtablecount \ getdtablesize \ getopt \ getpeereid \ nanosleep \ pipe2 \ reallocarray \ recallocarray \ res_hnok \ seteuid \ setegid \ setproctitle \ setresuid \ setresgid \ signal \ strlcpy \ strlcat \ strmode \ strndup \ strnlen \ strsep \ strtonum \ timingsafe_memcmp \ usleep \ ]) # TODO: add vasprintf to the above AC_CHECK_FUNCS([ \ dirfd \ getpeerucred \ getspnam \ malloc_conceal \ pledge \ setreuid \ setsid \ sigaction \ strnvis \ sysconf \ unveil \ ]) AC_CHECK_HEADERS([ \ arpa/nameser_compat.h \ crypt.h \ dirent.h \ endian.h \ fcntl.h \ libkern/OSByteOrder.h \ libutil.h \ limits.h \ login_cap.h \ mach/mach_time.h \ maillock.h \ ndir.h \ netdb.h \ pam/pam_appl.h \ paths.h \ security/pam_appl.h \ shadow.h \ sys/bitypes.h \ sys/dir.h \ sys/endian.h \ sys/file.h \ sys/mount.h \ sys/ndir.h \ sys/pstat.h \ sys/statfs.h \ sys/statvfs.h \ sys/time.h \ sys/un.h \ ucred.h \ util.h \ vis.h \ ]) AS_IF([test "x$need_libasr" = xyes], [ AM_CFLAGS="$AM_CFLAGS -I\${top_srcdir}/openbsd-compat/libasr" ]) AM_CONDITIONAL([NEED_LIBASR], [test "x$need_libasr" = xyes]) AS_IF([test "x$ac_cv_func_errc" = xno], [ AM_CFLAGS="$AM_CFLAGS -I\${top_srcdir}/openbsd-compat/err_h" ]) AS_IF([test "x$ac_cv_header_paths_h" = xno], [ AM_CFLAGS="$AM_CFLAGS -I\${top_srcdir}/openbsd-compat/paths_h" ]) # check for functions that may be provided by a library; need to do # this *after* the libc/libbsd things to avoid picking up unwanted # symbols from libraries. AC_SEARCH_LIBS([basename], [gen], [AC_DEFINE([HAVE_BASENAME], 1, [1 if have basename])], [AC_LIBOBJ([basename])]) AC_SEARCH_LIBS([clock_gettime], [rt], [AC_DEFINE([HAVE_CLOCK_GETTIME], 1, [1 if have clock_gettime])], [AC_LIBOBJ([clock_gettime])]) AC_SEARCH_LIBS([closefrom], [gen], [ AC_DEFINE([HAVE_CLOSEFROM], 1, [1 if have closefrom]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[int r = closefrom(0);]])], [ AC_DEFINE([HAVE_CLOSEFROM_INT], 1, [closefrom returns an int]) ]) ], [AC_LIBOBJ([closefrom])]) AC_SEARCH_LIBS([crypt], [crypt], [AC_DEFINE([HAVE_CRYPT], 1, [1 if have crypt])]) AC_SEARCH_LIBS([crypt_gensalt], [crypt], [AC_DEFINE([HAVE_CRYPT_GENSALT], 1, [1 if have crypt_gensalt])]) AC_SEARCH_LIBS([dirname], [gen], [AC_DEFINE([HAVE_DIRNAME], 1, [1 if have dirname])], [AC_LIBOBJ([dirname])]) AC_SEARCH_LIBS([fmt_scaled], [util], [AC_DEFINE([HAVE_FMT_SCALED], 1, [1 if have fmt_scaled])], [AC_LIBOBJ([fmt_scaled])]) AC_SEARCH_LIBS([fparseln], [util], [AC_DEFINE([HAVE_FPARSELN], 1, [1 if have fparseln])], [AC_LIBOBJ([fparseln])]) AC_SEARCH_LIBS([fts_open], [fts], [AC_DEFINE([HAVE_FTS_OPEN], 1, [1 if have fts_open])], [AC_MSG_ERROR([can't find fts_open(); please install libfts])]) AC_SEARCH_LIBS([inet_net_pton], [resorv], [AC_DEFINE([HAVE_INET_NET_PTON], 1, [1 if have inet_net_pton])], [AC_LIBOBJ([inet_net_pton])]) AC_SEARCH_LIBS([imsg_init], [util imsg], [AC_DEFINE([HAVE_IMSG_INIT], 1, [1 if have imsg_init])], [ AC_LIBOBJ([imsg]) AC_LIBOBJ([imsg-buffer]) ]) AC_SEARCH_LIBS([pidfile], [util], [AC_DEFINE([HAVE_PIDFILE], 1, [1 if have pidfile])], [AC_LIBOBJ([pidfile])]) AC_SEARCH_LIBS([res_hnok], [resolv], [AC_DEFINE([HAVE_RES_HNOK], 1, [1 if have res_hnok])], [ dnl darwin (at least) has res_9_hnok plus #defines dnl to "rename" it to res_hnok. AC_SEARCH_LIBS([res_9_hnok], [resolv], [ AC_DEFINE([HAVE_RES_HNOK], 1, [1 if have res_hnok]) AC_LIBOBJ([res_hnok]) ]) ]) AC_SEARCH_LIBS([res_randomid], [resolv], [AC_DEFINE([HAVE_RES_RANDOMID], 1, [1 if have res_randomid])], [AC_LIBOBJ([res_random])]) AC_SEARCH_LIBS([setsockopt], [socket], [:], [AC_MSG_ERROR([can't find setsockopt])]) AC_SEARCH_LIBS([__b64_pton], [resolv], [AC_DEFINE([HAVE___B64_PTON], 1, [1 if have __b64_pton])]) AC_SEARCH_LIBS([b64_pton], [resolv], [AC_DEFINE([HAVE_b64_PTON], 1, [1 if have b64_pton])]) AC_SEARCH_LIBS([__b64_ntop], [resolv], [AC_DEFINE([HAVE___B64_NTOP], 1, [1 if have __b64_ntop])]) AC_SEARCH_LIBS([b64_ntop], [resolv], [AC_DEFINE([HAVE_b64_NTOP], 1, [1 if have b64_ntop])]) dnl darwin (at least) has these plus #defines in resolv.h AC_SEARCH_LIBS([res_9_b64_pton], [resolv], [AC_DEFINE([HAVE_RES_9_B64_PTON], 1, [1 if have res_9_b64_pton])]) AC_SEARCH_LIBS([res_9_b64_ntop], [resolv], [AC_DEFINE([HAVE_RES_9_B64_NTOP], 1, [1 if have res_9_b64_ntop])]) AS_IF([test "x$ac_cv_search_b64_ntop" = "xno" -a \ "x$ac_cv_search___b64_ntop" = "xno" -a \ "x$ac_cv_search_res_9_b64_pton" = "xno" -a \ "x$ac_cv_search_res_9_b64_ntop" = "xno"], [ AC_LIBOBJ([base64]) ]) dnl NetBSD added an strnvis and unfortunately made it incompatible with the dnl existing one in OpenBSD and Linux's libbsd (the former having existed dnl for over ten years). Despite this incompatibility being reported during dnl development (see http://gnats.netbsd.org/44977) they still shipped it. dnl Even more unfortunately FreeBSD and later MacOS picked up this incompatible dnl implementation. dnl dnl OpenBSD, 2001: strnvis(char *dst, const char *src, size_t dlen, int flag); dnl NetBSD, 2012: strnvis(char *dst, size_t dlen, const char *src, int flag); AS_IF([test "x$ac_cv_func_strnvis" = xyes], [ AC_MSG_CHECKING([for broken strnvis]) saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Wall -Werror" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #include ]], [[ char buf[128]; return (strnvis(buf, "Hello, world!\n", sizeof(buf), 0)); ]])], [ AC_MSG_RESULT([no]) ], [ AC_MSG_RESULT([yes]) AC_DEFINE([BROKEN_STRNVIS], [1],[argument order is swapped]) AC_LIBOBJ([vis]) ]) CFLAGS="$saved_CFLAGS" ]) AS_IF([test "x$ac_cv_func_strnvis" = xno], [AC_LIBOBJ([vis])]) AC_MSG_CHECKING([for /proc/pid/fd directory]) AS_IF([test -d "/proc/$$/fd"], [ AC_DEFINE([HAVE_PROC_PID], [1], [Define if you have /proc/pid/fd]) AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ]) 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 ]) AC_CHECK_TYPES([fsblkcnt_t, fsfilcnt_t], [], [], [ #include #ifdef HAVE_SYS_BITYPES_H #include #endif #ifdef HAVE_SYS_STATFS_H #include #endif #ifdef HAVE_SYS_STATVFS_H #include #endif ]) AC_CHECK_TYPES([in_addr_t, in_port_t], [], [], [ #include #include ]) AC_CHECK_TYPES([sa_family_t], [], [], [ #include #include #include ]) AC_CHECK_TYPES([struct timespec]) AC_CHECK_TYPES([struct ifgroupreq]) AC_CHECK_TYPES([struct sockaddr_storage], [], [], [ #include #include ]) AC_CHECK_TYPES([struct sockaddr_in6], [], [], [ #include #include ]) AC_CHECK_TYPES([struct in6_addr], [], [], [ #include #include ]) AC_CHECK_TYPES([struct addrinfo], [], [], [ #include #include #include ]) AC_CHECK_TYPES([struct timeval], [], [], [ #include ]) AC_CHECK_MEMBERS([struct sockaddr_in6.sin6_scope_id], [], [], [ #ifdef HAVE_SYS_TYPES_H #include #endif #include ]) AC_CHECK_MEMBERS([struct passwd.pw_gecos, struct passwd.pw_class, struct passwd.pw_change, struct passwd.pw_expire], [], [], [ #include #include ]) AC_CHECK_MEMBERS([struct stat.st_flags], , , [ #include #include ] ) AC_CHECK_MEMBERS([struct stat.st_mtim], , , [ #include #include ] ) AC_CHECK_MEMBERS([struct stat.st_mtimespec], , , [ #include #include ] ) AC_CHECK_MEMBERS([struct sockaddr.sa_len], , , [ #include #include #include ] ) AC_CHECK_MEMBERS([struct sockaddr_storage.ss_len], , , [ #include #include #include ] ) AC_CHECK_MEMBERS([struct sockaddr_in.sin_len], , , [ #include #include #include ] ) AC_CHECK_MEMBERS([struct sockaddr_in6.sin6_len], , , [ #include #include #include ] ) AC_CHECK_MEMBERS([struct statfs.f_favail], , , [ #include #include ] ) AC_CHECK_MEMBERS([struct sockaddr_storage.ss_family], [], [], [ #include #include ]) AC_CHECK_MEMBERS([struct sockaddr_storage.__ss_family], [], [], [ #include #include ]) AC_CHECK_MEMBERS([struct tm.tm_gmtoff, struct tm.tm_zone], [], [ AC_CHECK_DECLS([timezone, altzone, tzname], [], [ AC_MSG_ERROR([cannot find timezone])], [ #include ] ) ], [ #include ] ) AC_CHECK_MEMBERS([struct DIR.d_type], [], [], [ #include #include ]) AC_CHECK_DECLS([O_NONBLOCK], [], [], [ #include #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_FCNTL_H # include #endif ]) AC_CHECK_DECLS([AF_LOCAL, PF_LOCAL], [], [], [ #include ]) AC_CHECK_DECLS([IPPORT_HILASTAUTO], [], [], [ #include ]) AC_CHECK_DECLS([IPV6_ADDR_SCOPE_INTFACELOCAL], [], [], [ #include #include ]) AC_CHECK_DECLS([WAIT_MYPGRP], [], [], [ #include ]) AC_CHECK_DECLS([LOG_PERROR], [], [], [ #include ]) ### ### 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 ]], [[ 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 ]], [[ 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 ]], [[ 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 ] ) fi fi AC_MSG_CHECKING([if compiler allows __attribute__ on return types]) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[ #include __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], AS_CASE(["$withval"], [yes], [need_dash_r=yes], [no], [need_dash_r=''] )) 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 ] ) werror_flags= 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 # Man pages defaults to mandoc MANTYPE=doc 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 ] ) AC_SUBST([MANTYPE]) if test "$MANTYPE" = "doc"; then mansubdir=man; else mansubdir=$MANTYPE; fi AC_SUBST([mansubdir]) # Whether to enable BSD auth support 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 ] ) 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" && \ 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 #if defined(HAVE_SECURITY_PAM_APPL_H) #include #elif defined (HAVE_PAM_PAM_APPL_H) #include #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 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]) 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 #include #include #ifdef HAVE_PATHS_H #include #endif #ifdef HAVE_MAILLOCK_H #include #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 # 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 #include #ifdef HAVE_PATHS_H #include #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]) AC_ARG_WITH([path-CAfile], [ --with-path-CAfile=FILE Specify path to CA certificate], ) AS_IF([test -n "$with_path_CAfile" -a "$with_path_CAfile" != no -a \ "$with_path_CAfile" != yes], [ AC_DEFINE_UNQUOTED([OPENSMTPD_CA_FILE], ["$with_path_CAfile"], [Custom path to a CA file]) ]) 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 #include #include #include ]], [[ #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 #include #include #include ]], [[ #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 ]], [[ 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 ]], [[ 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 ]], [[ 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 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 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 ]], [[ 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 AC_CHECK_DECL([suboptarg], [ AC_DEFINE([HAVE_SUBOPTARG], 1, [1 if have suboptarg]) ], [ AC_LIBOBJ([getsubopt]) ]) # 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="$LIBS -lfts" fi ] ) # 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 fi ] ) AC_CHECK_HEADER([event.h], ,[AC_MSG_ERROR([*** event.h missing - please install libevent ***])], [#include ]) LIBS="$LIBS -levent" AC_MSG_CHECKING([if programs using libevent functions will link]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include ]], [[ event_base_new(); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ] ) # 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 ] ) AC_SEARCH_LIBS([RAND_add], [crypto], [:], [ AC_MSG_ERROR([can't find libcrypto]) ]) AC_SEARCH_LIBS([SSL_CTX_new], [ssl], [:], [ AC_MSG_ERROR([can't find libssl]) ]) # Check arc4random only after libcrypto to avoid issues with old # (pre 3.8) versions of LibreSSL. AC_REPLACE_FUNCS([arc4random]) AC_CHECK_FUNCS([ \ SSL_CTX_use_certificate_chain_mem \ SSL_CTX_load_verify_mem \ X509_LOOKUP_mem \ OPENSSL_posix_to_tm \ OPENSSL_gmtime \ ]) AS_IF([test "x$ac_cv_func_OPENSSL_posix_to_tm" = xno], [ AM_CFLAGS="${AM_CFLAGS} -I\${top_srcdir}/openbsd-compat/openssl/" ]) # Sanity check OpenSSL headers AC_MSG_CHECKING([whether LibreSSL's headers match the library]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include #include ]], [[ 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_CHECK_DECL([LIBRESSL_VERSION_NUMBER], , , [#include ]) 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_SEARCH_LIBS([deflate], [z], [:], [ AC_MSG_ERROR([can't find zlib]) ]) 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 header file]) ] , [ AC_CHECK_HEADER(db.h, [AC_DEFINE([HAVE_DB_H], [], [if you have the header file]) ] , [ AC_CHECK_HEADER(db1/db.h, [AC_DEFINE([HAVE_DB1_DB_H], [], [if you have the 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])]) LIBS="$LIBS ${SMTPDLIBS}" dnl Adding -Werror to CFLAGS early prevents configure tests from running. dnl Add now. AM_CFLAGS="$AM_CFLAGS $werror_flags" AC_SUBST([AM_CFLAGS]) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([ 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 openbsd-compat/Makefile ]) AC_OUTPUT opensmtpd-7.9.0p0/contrib/000077500000000000000000000000001525355226100154465ustar00rootroot00000000000000opensmtpd-7.9.0p0/contrib/Makefile.am000066400000000000000000000000221525355226100174740ustar00rootroot00000000000000SUBDIRS = libexec opensmtpd-7.9.0p0/contrib/libexec/000077500000000000000000000000001525355226100170615ustar00rootroot00000000000000opensmtpd-7.9.0p0/contrib/libexec/Makefile.am000066400000000000000000000000501525355226100211100ustar00rootroot00000000000000SUBDIRS = mail.local lockspool encrypt opensmtpd-7.9.0p0/contrib/libexec/encrypt/000077500000000000000000000000001525355226100205455ustar00rootroot00000000000000opensmtpd-7.9.0p0/contrib/libexec/encrypt/Makefile.am000066400000000000000000000003751525355226100226060ustar00rootroot00000000000000pkglibexec_PROGRAMS = encrypt encrypt_SOURCES = encrypt.c encrypt_SOURCES += $(top_srcdir)/usr.sbin/smtpd/log.c AM_CPPFLAGS = -I$(top_srcdir)/openbsd-compat/ LDADD = $(LIBOBJS) uninstall-hook: rmdir $(DESTDIR)$(pkglibexecdir) 2> /dev/null || true opensmtpd-7.9.0p0/contrib/libexec/encrypt/encrypt.c000066400000000000000000000056531525355226100224060ustar00rootroot00000000000000/* * Copyright (c) 2013 Sunil Nimmagadda * Copyright (c) 2013 Gilles Chehade * * 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 /* needed for crypt() */ #endif #include #include #include #include #ifndef CRYPT_GENSALT_IMPLEMENTS_DEFAULT_PREFIX #define PASSWORD_LEN 128 #endif #define SALT_LEN 16 struct hashing_method { char *prefix; unsigned long count; }; static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; static struct hashing_method ids[] = { {"$y$", 8}, {"$gy$", 8}, {"$7$", 8}, {"$2a$", 10}, {"$2b$", 10}, {"$2y$", 10}, {"$6$", 5000}, {"$5$", 5000}, {"$3$", 1}, {"$2$", 10}, {"$1$", 1000} }; 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 \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 *id; char salt[SALT_LEN+1]; #ifndef CRYPT_GENSALT_IMPLEMENTS_DEFAULT_PREFIX char buffer[PASSWORD_LEN]; #else char *buffer; #endif int n; const char *p; int nb_ids = sizeof(ids) / sizeof(ids[0]); for (n = 0; n < SALT_LEN; ++n) to64(&salt[n], arc4random_uniform(0xff), 1); salt[SALT_LEN] = '\0'; for (n = 0; n < nb_ids; n++) { id = ids[n].prefix; #ifndef CRYPT_GENSALT_IMPLEMENTS_DEFAULT_PREFIX (void)snprintf(buffer, sizeof buffer, "%s%s$", id, salt); #else buffer = crypt_gensalt(id, ids[n].count, NULL, 0); #endif 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; } } opensmtpd-7.9.0p0/contrib/libexec/lockspool/000077500000000000000000000000001525355226100210665ustar00rootroot00000000000000opensmtpd-7.9.0p0/contrib/libexec/lockspool/Makefile.am000066400000000000000000000010151525355226100231170ustar00rootroot00000000000000pkglibexec_PROGRAMS = lockspool lockspool_SOURCES = lockspool.c lockspool_SOURCES += locking.c lockspool_SOURCES += $(top_srcdir)/usr.sbin/smtpd/log.c dist_man1_MANS = lockspool.1 EXTRA_DIST = mail.local.h pathnames.h AM_CPPFLAGS = -I$(top_srcdir)/openbsd-compat/ LDADD = $(LIBOBJS) 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 || true opensmtpd-7.9.0p0/contrib/libexec/lockspool/locking.c000066400000000000000000000055371525355226100226720ustar00rootroot00000000000000/* $OpenBSD: locking.c,v 1.15 2020/05/27 03:12:06 millert Exp $ */ /* * Copyright (c) 1996-1998 Theo de Raadt * Copyright (c) 1996-1998 David Mazieres * 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 #include #include #include #include #include #include #include #include #include #include #include #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; int lfd=-1; 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) { mwarn("%s: will not deliver to world-writable spool", _PATH_MAILDIR); } 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 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); } opensmtpd-7.9.0p0/contrib/libexec/lockspool/lockspool.1000066400000000000000000000040461525355226100231610ustar00rootroot00000000000000.\" $OpenBSD: lockspool.1,v 1.14 2019/01/25 00:19:26 millert Exp $ .\" .\" Copyright (c) 1998 Todd C. 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. .\" .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 . opensmtpd-7.9.0p0/contrib/libexec/lockspool/lockspool.c000066400000000000000000000060631525355226100232440ustar00rootroot00000000000000/* $OpenBSD: lockspool.c,v 1.23 2023/03/08 04:43:05 guenther Exp $ */ /* * Copyright (c) 1998 Theo de Raadt * Copyright (c) 1998 Todd C. 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 ``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 #include #include #include #include #include #include #include #include #include #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 %s", _PATH_MAILDIR); #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); } void unhold(int signo) { rellock(); _exit(0); } void usage(void) { merr(1, "usage: %s [username]", __progname); } opensmtpd-7.9.0p0/contrib/libexec/lockspool/mail.local.h000066400000000000000000000036721525355226100232620ustar00rootroot00000000000000/* $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); opensmtpd-7.9.0p0/contrib/libexec/lockspool/pathnames.h000066400000000000000000000034351525355226100232240ustar00rootroot00000000000000/* $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 */ #include #define _PATH_LOCTMP "/tmp/local.XXXXXXXXXX" #define _PATH_LOCKSPOOL PATH_LIBEXEC"/lockspool" opensmtpd-7.9.0p0/contrib/libexec/mail.local/000077500000000000000000000000001525355226100210745ustar00rootroot00000000000000opensmtpd-7.9.0p0/contrib/libexec/mail.local/Makefile.am000066400000000000000000000006331525355226100231320ustar00rootroot00000000000000pkglibexec_PROGRAMS = mail.local mail_local_SOURCES = mail.local.c mail_local_SOURCES += locking.c mail_local_SOURCES += $(top_srcdir)/usr.sbin/smtpd/log.c dist_man8_MANS = mail.local.8 EXTRA_DIST = mail.local.h pathnames.h AM_CPPFLAGS = -I$(top_srcdir)/openbsd-compat/ \ -DPATH_LIBEXEC=\"$(pkglibexecdir)\" LDADD = $(LIBOBJS) uninstall-hook: rmdir $(DESTDIR)$(pkglibexecdir) 2> /dev/null || true opensmtpd-7.9.0p0/contrib/libexec/mail.local/locking.c000066400000000000000000000055371525355226100227000ustar00rootroot00000000000000/* $OpenBSD: locking.c,v 1.15 2020/05/27 03:12:06 millert Exp $ */ /* * Copyright (c) 1996-1998 Theo de Raadt * Copyright (c) 1996-1998 David Mazieres * 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 #include #include #include #include #include #include #include #include #include #include #include #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; int lfd=-1; 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) { mwarn("%s: will not deliver to world-writable spool", _PATH_MAILDIR); } 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 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); } opensmtpd-7.9.0p0/contrib/libexec/mail.local/mail.local.8000066400000000000000000000123621525355226100232040ustar00rootroot00000000000000.\" $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 ) . opensmtpd-7.9.0p0/contrib/libexec/mail.local/mail.local.c000066400000000000000000000217761525355226100232700ustar00rootroot00000000000000/* $OpenBSD: mail.local.c,v 1.42 2023/06/05 08:07:18 op Exp $ */ /*- * Copyright (c) 1996-1998 Theo de Raadt * Copyright (c) 1996-1998 David Mazieres * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 = 1; char *tbuf, *line = NULL; size_t linesize = 0; ssize_t linelen; 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)); while ((linelen = getline(&line, &linesize, stdin)) != -1) { if (line[linelen - 1] == '\n') line[linelen - 1] = '\0'; if (line[0] == '\0') eline = 1; else { if (eline && !strncmp(line, "From ", 5)) (void)putc('>', fp); eline = 0; } (void)fprintf(fp, "%s\n", line); if (ferror(fp)) break; } free(line); /* 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, (long long)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 ..."); } opensmtpd-7.9.0p0/contrib/libexec/mail.local/mail.local.h000066400000000000000000000036721525355226100232700ustar00rootroot00000000000000/* $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); opensmtpd-7.9.0p0/contrib/libexec/mail.local/pathnames.h000066400000000000000000000034351525355226100232320ustar00rootroot00000000000000/* $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 */ #include #define _PATH_LOCTMP "/tmp/local.XXXXXXXXXX" #define _PATH_LOCKSPOOL PATH_LIBEXEC"/lockspool" opensmtpd-7.9.0p0/etc/000077500000000000000000000000001525355226100145615ustar00rootroot00000000000000opensmtpd-7.9.0p0/etc/README.md000066400000000000000000000004621525355226100160420ustar00rootroot00000000000000This 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. opensmtpd-7.9.0p0/etc/aliases000066400000000000000000000040221525355226100161230ustar00rootroot00000000000000# # $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 opensmtpd-7.9.0p0/m4/000077500000000000000000000000001525355226100143265ustar00rootroot00000000000000opensmtpd-7.9.0p0/m4/aclocal-openssh.m4000066400000000000000000000137431525355226100176530ustar00rootroot00000000000000dnl 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 #include /* Trivial function to help test for -fzero-call-used-regs */ void f(int n) {} int main(int argc, char **argv) { (void)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; f(0); printf("%d %d %d %f %f %lld %lld\n", i, j, k, l, m, n, o); /* * Test fallthrough behaviour. clang 10's -Wimplicit-fallthrough does * not understand comments and we don't use the "fallthrough" attribute * that it's looking for. */ switch(i){ case 0: j += i; /* FALLTHROUGH */ default: j += k; } exit(0); } ]])], [ if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" 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 #include int main(int argc, char **argv) { (void)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; long long int p = n * o; printf("%d %d %d %f %f %lld %lld %lld\n", i, j, k, l, m, n, o, p); exit(0); } ]])], [ if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" 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 #include int main(int argc, char **argv) { (void)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; long long p = n * o; printf("%d %d %d %f %f %lld %lld %lld\n", i, j, k, l, m, n, o, p); exit(0); } ]])], [ if $ac_cv_path_EGREP -i "unrecognized option|warning.*ignored" conftest.err >/dev/null then AC_MSG_RESULT([no]) LDFLAGS="$saved_LDFLAGS" else AC_MSG_RESULT([yes]) LDFLAGS="$saved_LDFLAGS $_define_flag" fi ], [ 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_COMPILE_IFELSE([ AC_LANG_PROGRAM([[ #include #include 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 #include ]) ]) opensmtpd-7.9.0p0/mk/000077500000000000000000000000001525355226100144155ustar00rootroot00000000000000opensmtpd-7.9.0p0/mk/Makefile.am000066400000000000000000000001361525355226100164510ustar00rootroot00000000000000SUBDIRS = smtpd SUBDIRS += smtpctl SUBDIRS += mail SUBDIRS += smtp EXTRA_DIST = mdoc2man.awk opensmtpd-7.9.0p0/mk/mail/000077500000000000000000000000001525355226100153375ustar00rootroot00000000000000opensmtpd-7.9.0p0/mk/mail/Makefile.am000066400000000000000000000001321525355226100173670ustar00rootroot00000000000000SUBDIRS = mail.lmtp SUBDIRS += mail.maildir SUBDIRS += mail.mboxfile SUBDIRS += mail.mda opensmtpd-7.9.0p0/mk/mail/mail.lmtp/000077500000000000000000000000001525355226100172345ustar00rootroot00000000000000opensmtpd-7.9.0p0/mk/mail/mail.lmtp/Makefile.am000066400000000000000000000006441525355226100212740ustar00rootroot00000000000000include $(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 dist_man8_MANS = $(top_srcdir)/usr.sbin/smtpd/mail.lmtp.8 AM_CPPFLAGS= -I$(top_srcdir)/usr.sbin/smtpd \ -I$(top_srcdir)/openbsd-compat LDADD = $(LIBOBJS) uninstall-hook: rmdir $(DESTDIR)$(pkglibexecdir) 2> /dev/null || true opensmtpd-7.9.0p0/mk/mail/mail.maildir/000077500000000000000000000000001525355226100177015ustar00rootroot00000000000000opensmtpd-7.9.0p0/mk/mail/mail.maildir/Makefile.am000066400000000000000000000006631525355226100217420ustar00rootroot00000000000000include $(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 dist_man8_MANS = $(top_srcdir)/usr.sbin/smtpd/mail.maildir.8 AM_CPPFLAGS= -I$(top_srcdir)/usr.sbin/smtpd \ -I$(top_srcdir)/openbsd-compat LDADD = $(LIBOBJS) uninstall-hook: rmdir $(DESTDIR)$(pkglibexecdir) 2> /dev/null || true opensmtpd-7.9.0p0/mk/mail/mail.mboxfile/000077500000000000000000000000001525355226100200655ustar00rootroot00000000000000opensmtpd-7.9.0p0/mk/mail/mail.mboxfile/Makefile.am000066400000000000000000000006701525355226100221240ustar00rootroot00000000000000include $(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 dist_man8_MANS = $(top_srcdir)/usr.sbin/smtpd/mail.mboxfile.8 AM_CPPFLAGS= -I$(top_srcdir)/usr.sbin/smtpd \ -I$(top_srcdir)/openbsd-compat LDADD = $(LIBOBJS) uninstall-hook: rmdir $(DESTDIR)$(pkglibexecdir) 2> /dev/null || true opensmtpd-7.9.0p0/mk/mail/mail.mda/000077500000000000000000000000001525355226100170215ustar00rootroot00000000000000opensmtpd-7.9.0p0/mk/mail/mail.mda/Makefile.am000066400000000000000000000006371525355226100210630ustar00rootroot00000000000000include $(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 dist_man8_MANS = $(top_srcdir)/usr.sbin/smtpd/mail.mda.8 AM_CPPFLAGS= -I$(top_srcdir)/usr.sbin/smtpd \ -I$(top_srcdir)/openbsd-compat LDADD = $(LIBOBJS) uninstall-hook: rmdir $(DESTDIR)$(pkglibexecdir) 2> /dev/null || true opensmtpd-7.9.0p0/mk/mdoc2man.awk000066400000000000000000000232511525355226100166240ustar00rootroot00000000000000#!/usr/bin/awk # # Copyright (c) 2003 Peter Stuge # # 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 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") } 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\\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() } opensmtpd-7.9.0p0/mk/pathnames000066400000000000000000000006161525355226100163230ustar00rootroot00000000000000smtpd_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)\" opensmtpd-7.9.0p0/mk/smtp/000077500000000000000000000000001525355226100154005ustar00rootroot00000000000000opensmtpd-7.9.0p0/mk/smtp/Makefile.am000066400000000000000000000045141525355226100174400ustar00rootroot00000000000000include $(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 # libtls smtp_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/a_time_posix.c smtp_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/tls.c smtp_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/tls_client.c smtp_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/tls_bio_cb.c smtp_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/tls_config.c smtp_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/tls_conninfo.c smtp_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/tls_keypair.c smtp_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/tls_server.c smtp_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/tls_ocsp.c smtp_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/tls_peer.c smtp_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/tls_signer.c smtp_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/tls_util.c smtp_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/tls_verify.c smtp_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/by_mem.c smtp_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/openssl.c AM_CPPFLAGS= -DIO_TLS \ -I$(top_srcdir)/usr.sbin/smtpd \ -I$(top_srcdir)/openbsd-compat \ -I$(top_srcdir)/openbsd-compat/libtls \ -I$(srcdir) @CPPFLAGS@ $(PATHS) @DEFS@ LDADD= $(LIBOBJS) 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 || true opensmtpd-7.9.0p0/mk/smtpctl/000077500000000000000000000000001525355226100161035ustar00rootroot00000000000000opensmtpd-7.9.0p0/mk/smtpctl/Makefile.am000066400000000000000000000103441525355226100201410ustar00rootroot00000000000000include $(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 # libasr if NEED_LIBASR smtpctl_SOURCES+= $(top_srcdir)/openbsd-compat/event_asr_run.c smtpctl_SOURCES+= $(top_srcdir)/openbsd-compat/libasr/asr.c smtpctl_SOURCES+= $(top_srcdir)/openbsd-compat/libasr/asr_compat.c smtpctl_SOURCES+= $(top_srcdir)/openbsd-compat/libasr/asr_debug.c smtpctl_SOURCES+= $(top_srcdir)/openbsd-compat/libasr/asr_utils.c smtpctl_SOURCES+= $(top_srcdir)/openbsd-compat/libasr/getaddrinfo_async.c smtpctl_SOURCES+= $(top_srcdir)/openbsd-compat/libasr/gethostnamadr_async.c smtpctl_SOURCES+= $(top_srcdir)/openbsd-compat/libasr/getnameinfo_async.c smtpctl_SOURCES+= $(top_srcdir)/openbsd-compat/libasr/getnetnamadr_async.c smtpctl_SOURCES+= $(top_srcdir)/openbsd-compat/libasr/res_search_async.c smtpctl_SOURCES+= $(top_srcdir)/openbsd-compat/libasr/res_send_async.c endif smtpctl_CPPFLAGS= -DNO_IO -DCONFIG_MINIMUM \ -DPATH_GZCAT=\"$(ZCAT)\" \ -DPATH_ENCRYPT=\"$(pkglibexecdir)/encrypt\" \ -I$(top_srcdir)/usr.sbin/smtpd \ -I$(top_srcdir)/openbsd-compat \ -I$(srcdir) @CPPFLAGS@ $(PATHS) @DEFS@ LDADD= $(LIBOBJS) if HAVE_DB_API LDADD+= $(DB_LIB) endif 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) $(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 || true opensmtpd-7.9.0p0/mk/smtpd/000077500000000000000000000000001525355226100155445ustar00rootroot00000000000000opensmtpd-7.9.0p0/mk/smtpd/Makefile.am000066400000000000000000000231061525355226100176020ustar00rootroot00000000000000# 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/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/dispatcher.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/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/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 # libasr if NEED_LIBASR smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/event_asr_run.c smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libasr/asr.c smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libasr/asr_compat.c smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libasr/asr_debug.c smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libasr/asr_utils.c smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libasr/getaddrinfo_async.c smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libasr/gethostnamadr_async.c smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libasr/getnameinfo_async.c smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libasr/getnetnamadr_async.c smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libasr/res_search_async.c smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libasr/res_send_async.c endif # libtls smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/a_time_posix.c smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/tls.c smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/tls_client.c smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/tls_bio_cb.c smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/tls_config.c smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/tls_conninfo.c smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/tls_keypair.c smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/tls_server.c smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/tls_ocsp.c smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/tls_peer.c smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/tls_signer.c smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/tls_util.c smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/tls_verify.c smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/by_mem.c smtpd_SOURCES+= $(top_srcdir)/openbsd-compat/libtls/openssl.c AM_CPPFLAGS= -DIO_TLS \ -I$(top_srcdir)/usr.sbin/smtpd \ -I$(top_srcdir)/openbsd-compat \ -I$(top_srcdir)/openbsd-compat/libtls \ -I$(srcdir) $(PATHS) @DEFS@ LDADD= $(LIBOBJS) $(DB_LIB) MANPAGES= aliases.5.out forward.5.out smtpd.8.out \ smtpd.conf.5.out smtpd-filters.7.out \ smtpd-tables.7.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/smtpd-filters.7 MANPAGES_IN+= $(top_srcdir)/usr.sbin/smtpd/smtpd-tables.7 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' \ -e 's|_smtpd|$(SMTPD_USER)|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)7 $(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 $(INSTALL) -m 644 smtpd-filters.7.out $(DESTDIR)$(mandir)/$(mansubdir)7/smtpd-filters.7 $(INSTALL) -m 644 smtpd-tables.7.out $(DESTDIR)$(mandir)/$(mansubdir)7/smtpd-tables.7 rm aliases.5.out forward.5.out table.5.out \ smtpd.8.out smtpd.conf.5.out smtpd-filters.7.out smtpd-tables.7.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 \ $(DESTDIR)$(mandir)/$(mansubdir)7/smtpd-filters.7 \ $(DESTDIR)$(mandir)/$(mansubdir)7/smtpd-tables.7 rmdir $(DESTDIR)$(mandir)/$(mansubdir)5 \ $(DESTDIR)$(mandir)/$(mansubdir)7 \ $(DESTDIR)$(mandir)/$(mansubdir)8 2> /dev/null || true opensmtpd-7.9.0p0/openbsd-compat/000077500000000000000000000000001525355226100167215ustar00rootroot00000000000000opensmtpd-7.9.0p0/openbsd-compat/Makefile.am000066400000000000000000000006241525355226100207570ustar00rootroot00000000000000EXTRA_DIST = NOTES \ base64.h \ bsd-vis.h \ chacha_private.h \ defines.h \ err_h/err.h \ imsg.h \ includes.h \ libasr/asr.h \ libasr/asr_compat.h \ libasr/asr_private.h \ libasr/thread_private.h \ libtls/tls.h \ libtls/tls_internal.h \ openbsd-compat.h \ openssl/posix_time.h \ paths_h/paths.h \ sys/queue.h \ sys/tree.h \ waitpid.h opensmtpd-7.9.0p0/openbsd-compat/NOTES000066400000000000000000000034441525355226100175410ustar00rootroot00000000000000List 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-err.{c,h} portable openssh clock_gettime.c handmade # crypt_checkpass daemon.c portable openssh defines.h portable openssh dirname.c 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 freezero.c part of /usr/src/lib/libc/stdlib/malloc.c getdtablecount.c tmux getdtablesize.c tmux getopt.c portable openssh getpeereid.c portable openssh getsubopt.c /usr/src/lib/libc/stdlib/getsubopt.c 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 inet_net_pton.c /usr/src/lib/libc/net/inet_net_pton.c # nanosleep.c openbsd-compat.h portable openssh pidfile.c /usr/src/lib/libutil/pidfile.c # pipe2.c # progname.c reallocarray.c /usr/src/lib/libc/stdlib/reallocarray.c recallocarray.c /usr/src/lib/libc/stdlib/recallocarray.c res_hok.c part of /usr/src/lib/libc/net/res_comp.c res_random.c /usr/src/lib/libc/net/res_random.c setegid.c portable openssh bsd-misc.c seteuid.c portable openssh bsd-misc.c setproctitle.c portable openssh # setresgid.c # setresuid.c # signal.c strlcat.c portable openssh strlcpy.c portable openssh strmode.c portable openssh strndup.c /usr/src/lib/libc/string/strndup.c strnlen.c /usr/src/lib/libc/string/strnlen.c strsep.c portable openssh strtonum.c portable openssh sys/queue.h portable openssh sys/tree.h portable openssh timingsafe_memcmp.c /usr/src/lib/libc/string/timingsafe_memcmp.c vis.{c,h} portable openssh waitpid.{c,h} portable openssh xmalloc.{c,h} portable openssh opensmtpd-7.9.0p0/openbsd-compat/arc4random.c000066400000000000000000000122651525355226100211250ustar00rootroot00000000000000/* 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 * Copyright (c) 2008, Damien Miller * Copyright (c) 2013, Markus Friedl * * 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 #include #include #include #include #include #include "log.h" #define KEYSTREAM_ONLY #include "chacha_private.h" /* 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; } opensmtpd-7.9.0p0/openbsd-compat/base64.c000066400000000000000000000235571525355226100201650ustar00rootroot00000000000000/* $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 #include #include #include #include #include #include #include #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); } opensmtpd-7.9.0p0/openbsd-compat/base64.h000066400000000000000000000053071525355226100201630ustar00rootroot00000000000000/* * 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 */ opensmtpd-7.9.0p0/openbsd-compat/basename.c000066400000000000000000000034341525355226100206440ustar00rootroot00000000000000/* $OpenBSD: basename.c,v 1.14 2005/08/08 08:05:33 espie Exp $ */ /* * Copyright (c) 1997, 2004 Todd C. 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. */ /* OPENBSD ORIGINAL: lib/libc/gen/basename.c */ #include "includes.h" #ifndef HAVE_BASENAME #include #include 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) */ opensmtpd-7.9.0p0/openbsd-compat/bsd-vis.h000066400000000000000000000071131525355226100204430ustar00rootroot00000000000000/* $OpenBSD: vis.h,v 1.15 2015/07/20 01:52:27 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 #include /* * 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 */ #define VIS_DQ 0x200 /* backslash-escape double quotes */ #define VIS_ALL 0x400 /* encode all 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 stravis(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 */ opensmtpd-7.9.0p0/openbsd-compat/chacha_private.h000066400000000000000000000124451525355226100220410ustar00rootroot00000000000000/* 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 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 } } opensmtpd-7.9.0p0/openbsd-compat/clock_gettime.c000066400000000000000000000030151525355226100216750ustar00rootroot00000000000000/* * Copyright (c) 2012 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. */ #include "includes.h" #ifdef HAVE_MACH_MACH_TIME_H #include #endif #include #include 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) return (-1); scaling_factor = info.numer/info.denom; } time *= scaling_factor; tp->tv_sec = time / 1000000000; tp->tv_nsec = time % 1000000000; return (ret); } opensmtpd-7.9.0p0/openbsd-compat/closefrom.c000066400000000000000000000054171525355226100210650ustar00rootroot00000000000000/* * Copyright (c) 2004-2005 Todd C. 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. */ #include "includes.h" #include #include #include #ifdef HAVE_FCNTL_H # include #endif #include #include #include #include #include #ifdef HAVE_DIRENT_H # include # define NAMLEN(dirent) strlen((dirent)->d_name) #else # define dirent direct # define NAMLEN(dirent) (dirent)->d_namlen # ifdef HAVE_SYS_NDIR_H # include # endif # ifdef HAVE_SYS_DIR_H # include # endif # ifdef HAVE_NDIR_H # include # 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 */ opensmtpd-7.9.0p0/openbsd-compat/crypt_checkpass.c000066400000000000000000000010011525355226100222420ustar00rootroot00000000000000/* OPENBSD ORIGINAL: lib/libc/crypt/cryptutil.c */ #include "includes.h" #include #ifdef HAVE_CRYPT_H #include #endif #include #include 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; } opensmtpd-7.9.0p0/openbsd-compat/daemon.c000066400000000000000000000045111525355226100203310ustar00rootroot00000000000000/* $OpenBSD: daemon.c,v 1.8 2021/10/24 21:24:20 deraadt 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 #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_FCNTL_H # include #endif #ifdef HAVE_UNISTD_H # include #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)) != -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 */ opensmtpd-7.9.0p0/openbsd-compat/defines.h000066400000000000000000000271121525355226100205120ustar00rootroot00000000000000/* * Copyright (c) 2016 Gilles Chehade . 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 $ */ #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 */ #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 EAI_OVERFLOW #define EAI_OVERFLOW EAI_FAIL #endif #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 #if !HAVE_DECL_LOG_PERROR #define LOG_PERROR 0 #endif #ifndef MAXDNAME #define MAXDNAME 1025 #endif #endif /* _DEFINES_H */ opensmtpd-7.9.0p0/openbsd-compat/dirname.c000066400000000000000000000035411525355226100205070ustar00rootroot00000000000000/* $OpenBSD: dirname.c,v 1.13 2005/08/08 08:05:33 espie Exp $ */ /* * Copyright (c) 1997, 2004 Todd C. 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. */ /* OPENBSD ORIGINAL: lib/libc/gen/dirname.c */ #include "includes.h" #ifndef HAVE_DIRNAME #include #include 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 opensmtpd-7.9.0p0/openbsd-compat/err_h/000077500000000000000000000000001525355226100200205ustar00rootroot00000000000000opensmtpd-7.9.0p0/openbsd-compat/err_h/err.h000066400000000000000000000010601525355226100207560ustar00rootroot00000000000000#ifndef ERR_H #define ERR_H # include __dead void err(int, const char *, ...); __dead void errc(int, int, const char *, ...); __dead void errx(int, const char *, ...); void warn(const char *, ...); void warnc(int, const char *, ...); void warnx(const char *, ...); __dead void verr(int, const char *, va_list); __dead void verrc(int, int, const char *, va_list); __dead void verrx(int, const char *, va_list); void vwarn(const char *, va_list); void vwarnc(int, const char *, va_list); void vwarnx(const char *, va_list); #endif opensmtpd-7.9.0p0/openbsd-compat/errc.c000066400000000000000000000047141525355226100200260ustar00rootroot00000000000000/* * Copyright (c) 2023 Omar Polo * * 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 #include #include #include #include extern char *__progname; void vwarn(const char *fmt, va_list ap) { int save_errno; save_errno = errno; fprintf(stderr, "%s: ", __progname); if (fmt != NULL) { vfprintf(stderr, fmt, ap); fprintf(stderr, ": "); } fprintf(stderr, "%s\n", strerror(save_errno)); errno = save_errno; } void vwarnc(int code, const char *fmt, va_list ap) { errno = code; vwarn(fmt, ap); } void vwarnx(const char *fmt, va_list ap) { int save_errno; save_errno = errno; fprintf(stderr, "%s: ", __progname); if (fmt != NULL) vfprintf(stderr, fmt, ap); fputc('\n', stderr); errno = save_errno; } __dead void verr(int eval, const char *fmt, va_list ap) { vwarn(fmt, ap); exit(eval); } __dead void verrc(int eval, int code, const char *fmt, va_list ap) { vwarnc(code, fmt, ap); exit(eval); } __dead void verrx(int eval, const char *fmt, va_list ap) { vwarnx(fmt, ap); exit(eval); } __dead void err(int eval, const char *fmt, ...) { va_list ap; va_start(ap, fmt); verr(eval, fmt, ap); va_end(ap); } __dead void errc(int eval, int code, const char *fmt, ...) { va_list ap; va_start(ap, fmt); verrc(eval, code, fmt, ap); va_end(ap); } __dead void errx(int eval, const char *fmt, ...) { va_list ap; va_start(ap, fmt); verrx(eval, fmt, ap); va_end(ap); } void warn(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vwarn(fmt, ap); va_end(ap); } void warnc(int code, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vwarnc(code, fmt, ap); va_end(ap); } void warnx(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vwarnx(fmt, ap); va_end(ap); } opensmtpd-7.9.0p0/openbsd-compat/event_asr_run.c000066400000000000000000000043501525355226100217410ustar00rootroot00000000000000/* $OpenBSD$ */ /* * Copyright (c) 2012 Eric Faurot * * 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 #include #include #include #include #include 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); } opensmtpd-7.9.0p0/openbsd-compat/explicit_bzero.c000066400000000000000000000003651525355226100221130ustar00rootroot00000000000000/* $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 void explicit_bzero(void *buf, size_t len) { memset(buf, 0, len); } opensmtpd-7.9.0p0/openbsd-compat/fgetln.c000066400000000000000000000027401525355226100203470ustar00rootroot00000000000000/* * Copyright (c) 2015 Joerg Jung * * 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 #include #include 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; } opensmtpd-7.9.0p0/openbsd-compat/fmt_scaled.c000066400000000000000000000175221525355226100211750ustar00rootroot00000000000000/* $OpenBSD: fmt_scaled.c,v 1.21 2022/03/11 07:29:53 dtucker 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 #include #include #include #include #include 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 const unit_type units[] = { NONE, KILO, MEGA, GIGA, TERA, PETA, EXA }; static const char scale_chars[] = "BKMGTPE"; static const 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((unsigned char)*p) && isspace((unsigned char)*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((unsigned char)*p) && (isdigit((unsigned char)*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 */ if (fpart > LLONG_MAX / 10) { errno = ERANGE; return -1; } fpart *= 10; if (i > LLONG_MAX - fpart) { errno = ERANGE; return -1; } fpart += i; } else { /* normal digit */ if (++ndigits >= MAX_DIGITS) { errno = ERANGE; return -1; } if (whole > LLONG_MAX / 10) { errno = ERANGE; return -1; } whole *= 10; if (i > LLONG_MAX - whole) { errno = ERANGE; return -1; } whole += i; } } if (sign) whole *= 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((unsigned char)scale_chars[i])) { /* If it ends with alphanumerics after the scale char, bad. */ if (isalnum((unsigned char)*(p+1))) { errno = EINVAL; return -1; } scale_fact = scale_factors[i]; /* check for overflow and underflow after scaling */ if (whole > LLONG_MAX / scale_fact || whole < LLONG_MIN / scale_fact) { errno = ERANGE; return -1; } /* scale whole part */ whole *= scale_fact; /* truncate fpart so it doesn't overflow. * then scale fractional part. */ while (fpart >= LLONG_MAX / scale_fact || fpart <= LLONG_MIN / scale_fact) { fpart /= 10; fract_digits--; } fpart *= scale_fact; if (fract_digits > 0) { for (i = 0; i < fract_digits -1; i++) fpart /= 10; } if (sign == -1) whole -= fpart; else whole += fpart; *result = whole; return 0; } } /* Invalid unit or character */ errno = EINVAL; 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; /* Not every negative long long has a positive representation. */ if (number == LLONG_MIN) { errno = ERANGE; return -1; } abval = llabs(number); /* Also check for numbers that are just too darned big to format. */ if (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; } else if (fract < 0) { /* shouldn't happen */ 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 */ opensmtpd-7.9.0p0/openbsd-compat/fparseln.c000066400000000000000000000103661525355226100207050ustar00rootroot00000000000000/* $OpenBSD: fparseln.c,v 1.8 2023/01/04 13:00:11 jsg 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. * * 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" #include #include #include 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; } opensmtpd-7.9.0p0/openbsd-compat/freezero.c000066400000000000000000000025761525355226100207200ustar00rootroot00000000000000/* $OpenBSD: malloc.c,v 1.289 2023/06/30 06:24:58 otto Exp $ */ /* * Copyright (c) 2008, 2010, 2011, 2016, 2023 Otto Moerbeek * Copyright (c) 2012 Matthew Dempsky * Copyright (c) 2008 Damien Miller * Copyright (c) 2000 Poul-Henning Kamp * * 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. */ /* * If we meet some day, and you think this stuff is worth it, you * can buy me a beer in return. Poul-Henning Kamp */ /* OPENBSD ORIGINAL: lib/libc/stdlib/malloc.c */ #include "includes.h" #include #include void freezero(void *ptr, size_t sz) { if (ptr == NULL) return; explicit_bzero(ptr, sz); free(ptr); } opensmtpd-7.9.0p0/openbsd-compat/getdtablecount.c000066400000000000000000000024441525355226100220750ustar00rootroot00000000000000/* * Copyright (c) 2017 Nicholas Marriott * * 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 MIND, 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 #include #include #include void fatal(const char *, ...); void fatalx(const char *, ...); #ifdef HAVE_PROC_PID int getdtablecount(void) { char path[PATH_MAX]; glob_t g; int n = 0; if (snprintf(path, sizeof path, "/proc/%ld/fd/*", (long)getpid()) < 0) fatal("snprintf overflow"); if (glob(path, 0, NULL, &g) == 0) n = g.gl_pathc; globfree(&g); return (n); } #else int getdtablecount(void) { return (0); } #endif opensmtpd-7.9.0p0/openbsd-compat/getdtablesize.c000066400000000000000000000017111525355226100217130ustar00rootroot00000000000000/* * Copyright (c) 2020 Nicholas Marriott * * 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 MIND, 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 #include #ifdef HAVE_SYSCONF int getdtablesize(void) { return (sysconf(_SC_OPEN_MAX)); } #endif opensmtpd-7.9.0p0/openbsd-compat/getopt.c000066400000000000000000000076031525355226100203750ustar00rootroot00000000000000/* * 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 #include #include 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) */ opensmtpd-7.9.0p0/openbsd-compat/getpeereid.c000066400000000000000000000031561525355226100212070ustar00rootroot00000000000000/* * Copyright (c) 2002,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. */ #include "includes.h" #include #include #include #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 #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) */ opensmtpd-7.9.0p0/openbsd-compat/getsubopt.c000066400000000000000000000060011525355226100210760ustar00rootroot00000000000000/* $OpenBSD: getsubopt.c,v 1.4 2005/08/08 08:05:36 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. */ #include #include #include /* * The SVID interface to getsubopt provides no way of figuring out which * part of the suboptions list wasn't matched. This makes error messages * tricky... The extern variable suboptarg is a pointer to the token * which didn't match. */ char *suboptarg; int openbsd_getsubopt(char **optionp, char * const *tokens, char **valuep) { int cnt; char *p; suboptarg = *valuep = NULL; if (!optionp || !*optionp) return(-1); /* skip leading white-space, commas */ for (p = *optionp; *p && (*p == ',' || *p == ' ' || *p == '\t'); ++p); if (!*p) { *optionp = p; return(-1); } /* save the start of the token, and skip the rest of the token. */ for (suboptarg = p; *++p && *p != ',' && *p != '=' && *p != ' ' && *p != '\t';); if (*p) { /* * If there's an equals sign, set the value pointer, and * skip over the value part of the token. Terminate the * token. */ if (*p == '=') { *p = '\0'; for (*valuep = ++p; *p && *p != ',' && *p != ' ' && *p != '\t'; ++p); if (*p) *p++ = '\0'; } else *p++ = '\0'; /* Skip any whitespace or commas after this token. */ for (; *p && (*p == ',' || *p == ' ' || *p == '\t'); ++p); } /* set optionp for next round. */ *optionp = p; for (cnt = 0; *tokens; ++tokens, ++cnt) if (!strcmp(suboptarg, *tokens)) return(cnt); return(-1); } opensmtpd-7.9.0p0/openbsd-compat/imsg-buffer.c000066400000000000000000000464141525355226100213040ustar00rootroot00000000000000/* $OpenBSD: imsg-buffer.c,v 1.42 2026/09/04 18:47:43 claudio Exp $ */ /* * Copyright (c) 2023 Claudio Jeker * Copyright (c) 2003, 2004 Henning Brauer * * 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 #include #include #include #include #include #include #include #include #include #include "imsg.h" struct ibufqueue { TAILQ_HEAD(, ibuf) bufs; uint32_t queued; }; struct msgbuf { struct ibufqueue bufs; struct ibufqueue rbufs; char *rbuf; struct ibuf *rpmsg; struct ibuf *(*readhdr)(struct ibuf *, void *, int *); void *rarg; size_t roff; size_t hdrsize; }; static void msgbuf_drain(struct msgbuf *, size_t); static void ibufq_init(struct ibufqueue *); #define IBUF_FD_MARK_ON_STACK -2 struct ibuf * ibuf_open(size_t len) { struct ibuf *buf; if ((buf = calloc(1, sizeof(struct ibuf))) == NULL) return (NULL); if (len > 0) { if ((buf->buf = calloc(len, 1)) == 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 == 0 || max < len) { errno = EINVAL; return (NULL); } if ((buf = calloc(1, sizeof(struct ibuf))) == NULL) return (NULL); if (len > 0) { if ((buf->buf = calloc(len, 1)) == NULL) { free(buf); return (NULL); } } buf->size = len; buf->max = max; buf->fd = -1; return (buf); } void * ibuf_reserve(struct ibuf *buf, size_t len) { void *b; if (len > SIZE_MAX - buf->wpos) { errno = ERANGE; return (NULL); } if (buf->fd == IBUF_FD_MARK_ON_STACK) { /* can not grow stack buffers */ errno = EINVAL; return (NULL); } if (buf->wpos + len > buf->size) { unsigned char *nb; /* check if buffer is allowed to grow */ if (buf->wpos + len > buf->max) { errno = ERANGE; return (NULL); } nb = realloc(buf->buf, buf->wpos + len); if (nb == NULL) return (NULL); memset(nb + buf->size, 0, buf->wpos + len - buf->size); buf->buf = nb; buf->size = buf->wpos + len; } b = buf->buf + buf->wpos; buf->wpos += len; return (b); } int ibuf_add(struct ibuf *buf, const void *data, size_t len) { void *b; if (len == 0) return (0); if ((b = ibuf_reserve(buf, len)) == NULL) return (-1); memcpy(b, data, len); return (0); } int ibuf_add_ibuf(struct ibuf *buf, const struct ibuf *from) { return ibuf_add(buf, ibuf_data(from), ibuf_size(from)); } int ibuf_add_n8(struct ibuf *buf, uint64_t value) { uint8_t v; if (value > UINT8_MAX) { errno = EINVAL; return (-1); } v = value; return ibuf_add(buf, &v, sizeof(v)); } int ibuf_add_n16(struct ibuf *buf, uint64_t value) { uint16_t v; if (value > UINT16_MAX) { errno = EINVAL; return (-1); } v = htobe16(value); return ibuf_add(buf, &v, sizeof(v)); } int ibuf_add_n32(struct ibuf *buf, uint64_t value) { uint32_t v; if (value > UINT32_MAX) { errno = EINVAL; return (-1); } v = htobe32(value); return ibuf_add(buf, &v, sizeof(v)); } int ibuf_add_n64(struct ibuf *buf, uint64_t value) { value = htobe64(value); return ibuf_add(buf, &value, sizeof(value)); } int ibuf_add_h16(struct ibuf *buf, uint64_t value) { uint16_t v; if (value > UINT16_MAX) { errno = EINVAL; return (-1); } v = value; return ibuf_add(buf, &v, sizeof(v)); } int ibuf_add_h32(struct ibuf *buf, uint64_t value) { uint32_t v; if (value > UINT32_MAX) { errno = EINVAL; return (-1); } v = value; return ibuf_add(buf, &v, sizeof(v)); } int ibuf_add_h64(struct ibuf *buf, uint64_t value) { return ibuf_add(buf, &value, sizeof(value)); } int ibuf_add_zero(struct ibuf *buf, size_t len) { void *b; if (len == 0) return (0); if ((b = ibuf_reserve(buf, len)) == NULL) return (-1); memset(b, 0, len); return (0); } int ibuf_add_strbuf(struct ibuf *buf, const char *str, size_t len) { char *b; size_t n; if ((b = ibuf_reserve(buf, len)) == NULL) return (-1); n = strlcpy(b, str, len); if (n >= len) { /* also covers the case where len == 0 */ errno = EOVERFLOW; return (-1); } memset(b + n, 0, len - n); return (0); } void * ibuf_seek(struct ibuf *buf, size_t pos, size_t len) { /* only allow seeking between rpos and wpos */ if (ibuf_size(buf) < pos || SIZE_MAX - pos < len || ibuf_size(buf) < pos + len) { errno = ERANGE; return (NULL); } return (buf->buf + buf->rpos + pos); } int ibuf_set(struct ibuf *buf, size_t pos, const void *data, size_t len) { void *b; if ((b = ibuf_seek(buf, pos, len)) == NULL) return (-1); if (len == 0) return (0); memcpy(b, data, len); return (0); } int ibuf_set_n8(struct ibuf *buf, size_t pos, uint64_t value) { uint8_t v; if (value > UINT8_MAX) { errno = EINVAL; return (-1); } v = value; return (ibuf_set(buf, pos, &v, sizeof(v))); } int ibuf_set_n16(struct ibuf *buf, size_t pos, uint64_t value) { uint16_t v; if (value > UINT16_MAX) { errno = EINVAL; return (-1); } v = htobe16(value); return (ibuf_set(buf, pos, &v, sizeof(v))); } int ibuf_set_n32(struct ibuf *buf, size_t pos, uint64_t value) { uint32_t v; if (value > UINT32_MAX) { errno = EINVAL; return (-1); } v = htobe32(value); return (ibuf_set(buf, pos, &v, sizeof(v))); } int ibuf_set_n64(struct ibuf *buf, size_t pos, uint64_t value) { value = htobe64(value); return (ibuf_set(buf, pos, &value, sizeof(value))); } int ibuf_set_h16(struct ibuf *buf, size_t pos, uint64_t value) { uint16_t v; if (value > UINT16_MAX) { errno = EINVAL; return (-1); } v = value; return (ibuf_set(buf, pos, &v, sizeof(v))); } int ibuf_set_h32(struct ibuf *buf, size_t pos, uint64_t value) { uint32_t v; if (value > UINT32_MAX) { errno = EINVAL; return (-1); } v = value; return (ibuf_set(buf, pos, &v, sizeof(v))); } int ibuf_set_h64(struct ibuf *buf, size_t pos, uint64_t value) { return (ibuf_set(buf, pos, &value, sizeof(value))); } int ibuf_set_maxsize(struct ibuf *buf, size_t max) { if (buf->fd == IBUF_FD_MARK_ON_STACK) { /* can't fiddle with stack buffers */ errno = EINVAL; return (-1); } if (max > buf->max) { /* can only shrink the buffer */ errno = ERANGE; return (-1); } if (buf->wpos > max) { /* cannot shrink below current wpos */ errno = ERANGE; return (-1); } if (buf->size > max) { /* clear excess memory */ explicit_bzero(buf->buf + max, buf->size - max); buf->size = max; } buf->max = max; return (0); } void * ibuf_data(const struct ibuf *buf) { return (buf->buf + buf->rpos); } size_t ibuf_size(const struct ibuf *buf) { return (buf->wpos - buf->rpos); } size_t ibuf_left(const struct ibuf *buf) { /* on stack buffers have no space left */ if (buf->fd == IBUF_FD_MARK_ON_STACK) return (0); return (buf->max - buf->wpos); } int ibuf_truncate(struct ibuf *buf, size_t len) { if (ibuf_size(buf) >= len) { buf->wpos = buf->rpos + len; return (0); } if (buf->fd == IBUF_FD_MARK_ON_STACK) { /* only allow to truncate down for stack buffers */ errno = ERANGE; return (-1); } return ibuf_add_zero(buf, len - ibuf_size(buf)); } void ibuf_rewind(struct ibuf *buf) { buf->rpos = 0; } void ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf) { ibufq_push(&msgbuf->bufs, buf); } void ibuf_from_buffer(struct ibuf *buf, void *data, size_t len) { memset(buf, 0, sizeof(*buf)); buf->buf = data; buf->size = buf->wpos = len; buf->fd = IBUF_FD_MARK_ON_STACK; } void ibuf_from_ibuf(struct ibuf *buf, const struct ibuf *from) { ibuf_from_buffer(buf, ibuf_data(from), ibuf_size(from)); } int ibuf_get(struct ibuf *buf, void *data, size_t len) { if (ibuf_size(buf) < len) { errno = EBADMSG; return (-1); } memcpy(data, ibuf_data(buf), len); buf->rpos += len; return (0); } int ibuf_get_ibuf(struct ibuf *buf, size_t len, struct ibuf *new) { if (ibuf_size(buf) < len) { errno = EBADMSG; return (-1); } ibuf_from_buffer(new, ibuf_data(buf), len); buf->rpos += len; return (0); } int ibuf_get_h16(struct ibuf *buf, uint16_t *value) { return ibuf_get(buf, value, sizeof(*value)); } int ibuf_get_h32(struct ibuf *buf, uint32_t *value) { return ibuf_get(buf, value, sizeof(*value)); } int ibuf_get_h64(struct ibuf *buf, uint64_t *value) { return ibuf_get(buf, value, sizeof(*value)); } int ibuf_get_n8(struct ibuf *buf, uint8_t *value) { return ibuf_get(buf, value, sizeof(*value)); } int ibuf_get_n16(struct ibuf *buf, uint16_t *value) { int rv; rv = ibuf_get(buf, value, sizeof(*value)); *value = be16toh(*value); return (rv); } int ibuf_get_n32(struct ibuf *buf, uint32_t *value) { int rv; rv = ibuf_get(buf, value, sizeof(*value)); *value = be32toh(*value); return (rv); } int ibuf_get_n64(struct ibuf *buf, uint64_t *value) { int rv; rv = ibuf_get(buf, value, sizeof(*value)); *value = be64toh(*value); return (rv); } char * ibuf_get_string(struct ibuf *buf, size_t len) { char *str; if (ibuf_size(buf) < len) { errno = EBADMSG; return (NULL); } str = strndup(ibuf_data(buf), len); if (str == NULL) return (NULL); buf->rpos += len; return (str); } int ibuf_get_strbuf(struct ibuf *buf, char *str, size_t len) { size_t n; if (len == 0) { errno = EINVAL; return (-1); } if (ibuf_get(buf, str, len) == -1) return -1; if ((n = strnlen(str, len)) == len) { str[len - 1] = '\0'; /* just to be safe */ errno = EOVERFLOW; return -1; } /* clear rest of the buffer */ memset(str + n, 0, len - n); return 0; } int ibuf_skip(struct ibuf *buf, size_t len) { if (ibuf_size(buf) < len) { errno = EBADMSG; return (-1); } buf->rpos += len; return (0); } void ibuf_free(struct ibuf *buf) { int save_errno = errno; if (buf == NULL) return; /* if buf lives on the stack abort before causing more harm */ if (buf->fd == IBUF_FD_MARK_ON_STACK) abort(); if (buf->fd >= 0) close(buf->fd); freezero(buf->buf, buf->size); free(buf); errno = save_errno; } int ibuf_fd_avail(struct ibuf *buf) { return (buf->fd >= 0); } int ibuf_fd_get(struct ibuf *buf) { int fd; /* negative fds are internal use and equivalent to -1 */ if (buf->fd < 0) return (-1); fd = buf->fd; buf->fd = -1; return (fd); } void ibuf_fd_set(struct ibuf *buf, int fd) { /* if buf lives on the stack abort before causing more harm */ if (buf->fd == IBUF_FD_MARK_ON_STACK) abort(); if (buf->fd >= 0) close(buf->fd); buf->fd = -1; if (fd >= 0) buf->fd = fd; } struct msgbuf * msgbuf_new(void) { struct msgbuf *msgbuf; if ((msgbuf = calloc(1, sizeof(*msgbuf))) == NULL) return (NULL); ibufq_init(&msgbuf->bufs); ibufq_init(&msgbuf->rbufs); return msgbuf; } struct msgbuf * msgbuf_new_reader(size_t hdrsz, struct ibuf *(*readhdr)(struct ibuf *, void *, int *), void *arg) { struct msgbuf *msgbuf; char *buf; if (hdrsz == 0 || hdrsz > IBUF_READ_SIZE / 2) { errno = EINVAL; return (NULL); } if ((buf = malloc(IBUF_READ_SIZE)) == NULL) return (NULL); msgbuf = msgbuf_new(); if (msgbuf == NULL) { free(buf); return (NULL); } msgbuf->rbuf = buf; msgbuf->hdrsize = hdrsz; msgbuf->readhdr = readhdr; msgbuf->rarg = arg; return (msgbuf); } void msgbuf_free(struct msgbuf *msgbuf) { if (msgbuf == NULL) return; msgbuf_clear(msgbuf); free(msgbuf->rbuf); free(msgbuf); } uint32_t msgbuf_queuelen(struct msgbuf *msgbuf) { return ibufq_queuelen(&msgbuf->bufs); } uint32_t msgbuf_readlen(struct msgbuf *msgbuf) { return ibufq_queuelen(&msgbuf->rbufs); } void msgbuf_clear(struct msgbuf *msgbuf) { /* write side */ ibufq_flush(&msgbuf->bufs); /* read side */ ibufq_flush(&msgbuf->rbufs); msgbuf->roff = 0; ibuf_free(msgbuf->rpmsg); msgbuf->rpmsg = NULL; } struct ibuf * msgbuf_get(struct msgbuf *msgbuf) { return ibufq_pop(&msgbuf->rbufs); } void msgbuf_concat(struct msgbuf *msgbuf, struct ibufqueue *from) { ibufq_concat(&msgbuf->bufs, from); } int ibuf_write(int fd, struct msgbuf *msgbuf) { struct iovec iov[IOV_MAX]; struct ibuf *buf; unsigned int i = 0; ssize_t n; memset(&iov, 0, sizeof(iov)); TAILQ_FOREACH(buf, &msgbuf->bufs.bufs, entry) { if (i >= IOV_MAX) break; iov[i].iov_base = ibuf_data(buf); iov[i].iov_len = ibuf_size(buf); i++; } if (i == 0) return (0); /* nothing queued */ again: if ((n = writev(fd, iov, i)) == -1) { if (errno == EINTR) goto again; if (errno == EAGAIN || errno == ENOBUFS) /* let's retry later again */ return (0); return (-1); } msgbuf_drain(msgbuf, n); return (0); } int msgbuf_write(int fd, struct msgbuf *msgbuf) { struct iovec iov[IOV_MAX]; struct ibuf *buf, *buf0 = NULL; unsigned int i = 0; ssize_t n; struct msghdr msg; struct cmsghdr *cmsg; union { struct cmsghdr hdr; char buf[CMSG_SPACE(sizeof(int))]; } cmsgbuf; memset(&iov, 0, sizeof(iov)); memset(&msg, 0, sizeof(msg)); memset(&cmsgbuf, 0, sizeof(cmsgbuf)); TAILQ_FOREACH(buf, &msgbuf->bufs.bufs, entry) { if (i >= IOV_MAX) break; if (i > 0 && buf->fd != -1) break; iov[i].iov_base = ibuf_data(buf); iov[i].iov_len = ibuf_size(buf); i++; if (buf->fd != -1) buf0 = buf; } if (i == 0) return (0); /* nothing queued */ msg.msg_iov = iov; msg.msg_iovlen = i; if (buf0 != NULL) { 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) = buf0->fd; } again: if ((n = sendmsg(fd, &msg, 0)) == -1) { if (errno == EINTR) goto again; if (errno == EAGAIN || errno == ENOBUFS) /* let's retry later again */ return (0); return (-1); } /* * assumption: fd got sent if sendmsg sent anything * this works because fds are passed one at a time */ if (buf0 != NULL) { close(buf0->fd); buf0->fd = -1; } msgbuf_drain(msgbuf, n); return (0); } static int ibuf_read_process(struct msgbuf *msgbuf, int fd) { struct ibuf rbuf, msg; ssize_t sz; ibuf_from_buffer(&rbuf, msgbuf->rbuf, msgbuf->roff); do { if (msgbuf->rpmsg == NULL) { if (ibuf_size(&rbuf) < msgbuf->hdrsize) break; /* get size from header */ ibuf_from_buffer(&msg, ibuf_data(&rbuf), msgbuf->hdrsize); if ((msgbuf->rpmsg = msgbuf->readhdr(&msg, msgbuf->rarg, &fd)) == NULL) goto fail; } if (ibuf_left(msgbuf->rpmsg) <= ibuf_size(&rbuf)) sz = ibuf_left(msgbuf->rpmsg); else sz = ibuf_size(&rbuf); /* neither call below can fail */ if (ibuf_get_ibuf(&rbuf, sz, &msg) == -1 || ibuf_add_ibuf(msgbuf->rpmsg, &msg) == -1) goto fail; if (ibuf_left(msgbuf->rpmsg) == 0) { ibufq_push(&msgbuf->rbufs, msgbuf->rpmsg); msgbuf->rpmsg = NULL; } } while (ibuf_size(&rbuf) > 0); if (ibuf_size(&rbuf) > 0) memmove(msgbuf->rbuf, ibuf_data(&rbuf), ibuf_size(&rbuf)); msgbuf->roff = ibuf_size(&rbuf); if (fd != -1) close(fd); return (1); fail: /* XXX how to properly clean up is unclear */ if (fd != -1) close(fd); return (-1); } int ibuf_read(int fd, struct msgbuf *msgbuf) { struct iovec iov; ssize_t n; if (msgbuf->rbuf == NULL) { errno = EINVAL; return (-1); } iov.iov_base = msgbuf->rbuf + msgbuf->roff; iov.iov_len = IBUF_READ_SIZE - msgbuf->roff; again: if ((n = readv(fd, &iov, 1)) == -1) { if (errno == EINTR) goto again; if (errno == EAGAIN) /* let's retry later again */ return (1); return (-1); } if (n == 0) /* connection closed */ return (0); msgbuf->roff += n; /* new data arrived, try to process it */ return (ibuf_read_process(msgbuf, -1)); } int msgbuf_read(int fd, struct msgbuf *msgbuf) { struct msghdr msg; struct cmsghdr *cmsg; union { struct cmsghdr hdr; char buf[CMSG_SPACE(sizeof(int) * 1)]; } cmsgbuf; struct iovec iov; ssize_t n; int fdpass = -1; if (msgbuf->rbuf == NULL) { errno = EINVAL; return (-1); } memset(&msg, 0, sizeof(msg)); memset(&cmsgbuf, 0, sizeof(cmsgbuf)); iov.iov_base = msgbuf->rbuf + msgbuf->roff; iov.iov_len = IBUF_READ_SIZE - msgbuf->roff; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = &cmsgbuf.buf; msg.msg_controllen = sizeof(cmsgbuf.buf); again: if ((n = recvmsg(fd, &msg, 0)) == -1) { if (errno == EINTR) goto again; if (errno == EMSGSIZE) { /* * Not enough fd slots: fd passing failed, retry * to receive the message without fd. * imsg_get_fd() will return -1 in that case. */ msg.msg_control = NULL; msg.msg_controllen = 0; goto again; } if (errno == EAGAIN) { /* let's retry later again */ return (1); } return (-1); } if (n == 0) /* connection closed */ return (0); msgbuf->roff += 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, j, f; /* * 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++) { f = ((int *)CMSG_DATA(cmsg))[i]; if (i == 0) fdpass = f; else close(f); } } /* we do not handle other ctl data level */ } /* new data arrived, try to process it */ return (ibuf_read_process(msgbuf, fdpass)); } static void msgbuf_drain(struct msgbuf *msgbuf, size_t n) { struct ibuf *buf; while ((buf = TAILQ_FIRST(&msgbuf->bufs.bufs)) != NULL) { if (n >= ibuf_size(buf)) { n -= ibuf_size(buf); TAILQ_REMOVE(&msgbuf->bufs.bufs, buf, entry); msgbuf->bufs.queued--; ibuf_free(buf); } else { buf->rpos += n; return; } } } static void ibufq_init(struct ibufqueue *bufq) { TAILQ_INIT(&bufq->bufs); bufq->queued = 0; } struct ibufqueue * ibufq_new(void) { struct ibufqueue *bufq; if ((bufq = calloc(1, sizeof(*bufq))) == NULL) return NULL; ibufq_init(bufq); return bufq; } void ibufq_free(struct ibufqueue *bufq) { if (bufq == NULL) return; ibufq_flush(bufq); free(bufq); } struct ibuf * ibufq_pop(struct ibufqueue *bufq) { struct ibuf *buf; if ((buf = TAILQ_FIRST(&bufq->bufs)) == NULL) return NULL; TAILQ_REMOVE(&bufq->bufs, buf, entry); bufq->queued--; return buf; } void ibufq_push(struct ibufqueue *bufq, struct ibuf *buf) { /* if buf lives on the stack abort before causing more harm */ if (buf->fd == IBUF_FD_MARK_ON_STACK) abort(); TAILQ_INSERT_TAIL(&bufq->bufs, buf, entry); bufq->queued++; } uint32_t ibufq_queuelen(struct ibufqueue *bufq) { return (bufq->queued); } void ibufq_concat(struct ibufqueue *to, struct ibufqueue *from) { to->queued += from->queued; TAILQ_CONCAT(&to->bufs, &from->bufs, entry); from->queued = 0; } void ibufq_flush(struct ibufqueue *bufq) { struct ibuf *buf; while ((buf = TAILQ_FIRST(&bufq->bufs)) != NULL) { TAILQ_REMOVE(&bufq->bufs, buf, entry); ibuf_free(buf); } bufq->queued = 0; } opensmtpd-7.9.0p0/openbsd-compat/imsg.c000066400000000000000000000203121525355226100200220ustar00rootroot00000000000000/* $OpenBSD: imsg.c,v 1.44 2026/09/04 18:47:43 claudio Exp $ */ /* * Copyright (c) 2023 Claudio Jeker * Copyright (c) 2003, 2004 Henning Brauer * * 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 #include #include #include #include #include #include #include #include #include #include "imsg.h" #define IMSG_ALLOW_FDPASS 0x01 #define IMSG_FD_MARK 0x80000000U static struct ibuf *imsg_parse_hdr(struct ibuf *, void *, int *); int imsgbuf_init(struct imsgbuf *imsgbuf, int fd) { memset(imsgbuf, 0, sizeof(*imsgbuf)); imsgbuf->w = msgbuf_new_reader(IMSG_HEADER_SIZE, imsg_parse_hdr, imsgbuf); if (imsgbuf->w == NULL) return (-1); imsgbuf->pid = getpid(); imsgbuf->maxsize = MAX_IMSGSIZE; imsgbuf->fd = fd; return (0); } void imsgbuf_allow_fdpass(struct imsgbuf *imsgbuf) { imsgbuf->flags |= IMSG_ALLOW_FDPASS; } void imsgbuf_set_close_callback(struct imsgbuf *imsgbuf, void (*cb)(struct imsgbuf *, void *)) { imsgbuf->cb = cb; } int imsgbuf_set_maxsize(struct imsgbuf *imsgbuf, uint32_t max) { if (max > UINT32_MAX - IMSG_HEADER_SIZE) { errno = ERANGE; return (-1); } max += IMSG_HEADER_SIZE; if (max & IMSG_FD_MARK) { errno = EINVAL; return (-1); } imsgbuf->maxsize = max; return (0); } void * imsgbuf_set_userdata(struct imsgbuf *imsgbuf, void *data) { void *old = imsgbuf->userdata; imsgbuf->userdata = data; return old; } void * imsgbuf_get_userdata(struct imsgbuf *imsgbuf) { return imsgbuf->userdata; } int imsgbuf_read(struct imsgbuf *imsgbuf) { if (imsgbuf->flags & IMSG_ALLOW_FDPASS) return msgbuf_read(imsgbuf->fd, imsgbuf->w); else return ibuf_read(imsgbuf->fd, imsgbuf->w); } int imsgbuf_write(struct imsgbuf *imsgbuf) { if (imsgbuf->flags & IMSG_ALLOW_FDPASS) return msgbuf_write(imsgbuf->fd, imsgbuf->w); else return ibuf_write(imsgbuf->fd, imsgbuf->w); } int imsgbuf_flush(struct imsgbuf *imsgbuf) { while (imsgbuf_queuelen(imsgbuf) > 0) { if (imsgbuf_write(imsgbuf) == -1) return (-1); } return (0); } void imsgbuf_clear(struct imsgbuf *imsgbuf) { msgbuf_free(imsgbuf->w); imsgbuf->w = NULL; } uint32_t imsgbuf_queuelen(struct imsgbuf *imsgbuf) { return msgbuf_queuelen(imsgbuf->w); } int imsgbuf_get(struct imsgbuf *imsgbuf, struct imsg *imsg) { struct imsg m; struct ibuf *buf; if ((buf = msgbuf_get(imsgbuf->w)) == NULL) return (0); if (ibuf_get(buf, &m.hdr, sizeof(m.hdr)) == -1) return (-1); if (ibuf_size(buf)) m.data = ibuf_data(buf); else m.data = NULL; m.buf = buf; m.hdr.len &= ~IMSG_FD_MARK; *imsg = m; return (1); } int imsg_ibufq_pop(struct ibufqueue *bufq, struct imsg *imsg) { struct imsg m; struct ibuf *buf; if ((buf = ibufq_pop(bufq)) == NULL) return (0); if (ibuf_get(buf, &m.hdr, sizeof(m.hdr)) == -1) return (-1); if (ibuf_size(buf)) m.data = ibuf_data(buf); else m.data = NULL; m.buf = buf; m.hdr.len &= ~IMSG_FD_MARK; *imsg = m; return (1); } void imsg_ibufq_push(struct ibufqueue *bufq, struct imsg *imsg) { ibuf_rewind(imsg->buf); ibufq_push(bufq, imsg->buf); memset(imsg, 0, sizeof(*imsg)); } int imsg_get_ibuf(struct imsg *imsg, struct ibuf *ibuf) { if (ibuf_size(imsg->buf) == 0) { errno = EBADMSG; return (-1); } return ibuf_get_ibuf(imsg->buf, ibuf_size(imsg->buf), ibuf); } int imsg_get_data(struct imsg *imsg, void *data, size_t len) { if (len == 0) { errno = EINVAL; return (-1); } if (ibuf_size(imsg->buf) != len) { errno = EBADMSG; return (-1); } return ibuf_get(imsg->buf, data, len); } int imsg_get_buf(struct imsg *imsg, void *data, size_t len) { return ibuf_get(imsg->buf, data, len); } int imsg_get_strbuf(struct imsg *imsg, char *str, size_t len) { return ibuf_get_strbuf(imsg->buf, str, len); } int imsg_get_fd(struct imsg *imsg) { return ibuf_fd_get(imsg->buf); } uint32_t imsg_get_id(struct imsg *imsg) { return (imsg->hdr.peerid); } size_t imsg_get_len(struct imsg *imsg) { return ibuf_size(imsg->buf); } pid_t imsg_get_pid(struct imsg *imsg) { return (imsg->hdr.pid); } uint32_t imsg_get_type(struct imsg *imsg) { return (imsg->hdr.type); } int imsg_compose(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid, int fd, const void *data, size_t datalen) { struct ibuf *wbuf; if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL) goto fail; if (ibuf_add(wbuf, data, datalen) == -1) goto fail; ibuf_fd_set(wbuf, fd); imsg_close(imsgbuf, wbuf); return (1); fail: ibuf_free(wbuf); return (-1); } int imsg_composev(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid, int fd, const struct iovec *iov, int iovcnt) { struct ibuf *wbuf; int i; size_t datalen = 0; for (i = 0; i < iovcnt; i++) datalen += iov[i].iov_len; if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL) goto fail; for (i = 0; i < iovcnt; i++) if (ibuf_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1) goto fail; ibuf_fd_set(wbuf, fd); imsg_close(imsgbuf, wbuf); return (1); fail: ibuf_free(wbuf); return (-1); } /* * Forward imsg to another channel. Any attached fd is closed. */ int imsg_forward(struct imsgbuf *imsgbuf, struct imsg *msg) { struct ibuf *wbuf; size_t len; ibuf_rewind(msg->buf); if (ibuf_skip(msg->buf, sizeof(msg->hdr)) == -1) return (-1); len = ibuf_size(msg->buf); if ((wbuf = imsg_create(imsgbuf, msg->hdr.type, msg->hdr.peerid, msg->hdr.pid, len)) == NULL) return (-1); if (len != 0) { if (ibuf_add_ibuf(wbuf, msg->buf) == -1) { ibuf_free(wbuf); return (-1); } } imsg_close(imsgbuf, wbuf); return (1); } struct ibuf * imsg_create(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid, size_t datalen) { struct ibuf *wbuf; struct imsg_hdr hdr; datalen += IMSG_HEADER_SIZE; if (datalen > imsgbuf->maxsize) { errno = ERANGE; return (NULL); } hdr.len = 0; hdr.type = type; hdr.peerid = id; if ((hdr.pid = pid) == 0) hdr.pid = imsgbuf->pid; if ((wbuf = ibuf_dynamic(datalen, imsgbuf->maxsize)) == NULL) goto fail; if (ibuf_add(wbuf, &hdr, sizeof(hdr)) == -1) goto fail; return (wbuf); fail: ibuf_free(wbuf); return (NULL); } int imsg_add(struct ibuf *msg, const void *data, size_t datalen) { if (ibuf_add(msg, data, datalen) == -1) { ibuf_free(msg); return (-1); } return (0); } int imsg_add_strbuf(struct ibuf *msg, const char *str, size_t datalen) { if (ibuf_add_strbuf(msg, str, datalen) == -1) { ibuf_free(msg); return (-1); } return (0); } void imsg_close(struct imsgbuf *imsgbuf, struct ibuf *msg) { uint32_t len; len = ibuf_size(msg); if (ibuf_fd_avail(msg)) len |= IMSG_FD_MARK; (void)ibuf_set_h32(msg, offsetof(struct imsg_hdr, len), len); ibuf_close(imsgbuf->w, msg); if (imsgbuf->cb != NULL) imsgbuf->cb(imsgbuf, imsgbuf->userdata); } void imsg_free(struct imsg *imsg) { ibuf_free(imsg->buf); } int imsg_set_maxsize(struct ibuf *msg, size_t max) { if (max > UINT32_MAX - IMSG_HEADER_SIZE) { errno = ERANGE; return (-1); } return ibuf_set_maxsize(msg, max + IMSG_HEADER_SIZE); } static struct ibuf * imsg_parse_hdr(struct ibuf *buf, void *arg, int *fd) { struct imsgbuf *imsgbuf = arg; struct imsg_hdr hdr; struct ibuf *b; uint32_t len; if (ibuf_get(buf, &hdr, sizeof(hdr)) == -1) return (NULL); len = hdr.len & ~IMSG_FD_MARK; if (len < IMSG_HEADER_SIZE || len > imsgbuf->maxsize) { errno = ERANGE; return (NULL); } if ((b = ibuf_open(len)) == NULL) return (NULL); if (hdr.len & IMSG_FD_MARK) { ibuf_fd_set(b, *fd); *fd = -1; } return b; } opensmtpd-7.9.0p0/openbsd-compat/imsg.h000066400000000000000000000150011525355226100200260ustar00rootroot00000000000000/* $OpenBSD: imsg.h,v 1.25 2026/09/04 18:47:43 claudio Exp $ */ /* * Copyright (c) 2023 Claudio Jeker * Copyright (c) 2006, 2007 Pierre-Yves Ritschard * Copyright (c) 2006, 2007, 2008 Reyk Floeter * Copyright (c) 2003, 2004 Henning Brauer * * 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_ #include #include #include #include #define IBUF_READ_SIZE 65535 #define IMSG_HEADER_SIZE sizeof(struct imsg_hdr) #define MAX_IMSGSIZE 16384 struct ibuf { TAILQ_ENTRY(ibuf) entry; unsigned char *buf; size_t size; size_t max; size_t wpos; size_t rpos; int fd; }; struct ibufqueue; struct msgbuf; struct imsgbuf { struct msgbuf *w; void (*cb)(struct imsgbuf *, void *); void *userdata; pid_t pid; uint32_t maxsize; int fd; int flags; }; struct imsg_hdr { uint32_t type; uint32_t len; uint32_t peerid; uint32_t pid; }; struct imsg { struct imsg_hdr hdr; void *data; struct ibuf *buf; }; struct iovec; /* imsg-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); int ibuf_add_ibuf(struct ibuf *, const struct ibuf *); int ibuf_add_zero(struct ibuf *, size_t); int ibuf_add_n8(struct ibuf *, uint64_t); int ibuf_add_n16(struct ibuf *, uint64_t); int ibuf_add_n32(struct ibuf *, uint64_t); int ibuf_add_n64(struct ibuf *, uint64_t); int ibuf_add_h16(struct ibuf *, uint64_t); int ibuf_add_h32(struct ibuf *, uint64_t); int ibuf_add_h64(struct ibuf *, uint64_t); int ibuf_add_strbuf(struct ibuf *, const char *, size_t); void *ibuf_reserve(struct ibuf *, size_t); void *ibuf_seek(struct ibuf *, size_t, size_t); int ibuf_set(struct ibuf *, size_t, const void *, size_t); int ibuf_set_n8(struct ibuf *, size_t, uint64_t); int ibuf_set_n16(struct ibuf *, size_t, uint64_t); int ibuf_set_n32(struct ibuf *, size_t, uint64_t); int ibuf_set_n64(struct ibuf *, size_t, uint64_t); int ibuf_set_h16(struct ibuf *, size_t, uint64_t); int ibuf_set_h32(struct ibuf *, size_t, uint64_t); int ibuf_set_h64(struct ibuf *, size_t, uint64_t); int ibuf_set_maxsize(struct ibuf *, size_t); void *ibuf_data(const struct ibuf *); size_t ibuf_size(const struct ibuf *); size_t ibuf_left(const struct ibuf *); int ibuf_truncate(struct ibuf *, size_t); void ibuf_rewind(struct ibuf *); void ibuf_close(struct msgbuf *, struct ibuf *); void ibuf_from_buffer(struct ibuf *, void *, size_t); void ibuf_from_ibuf(struct ibuf *, const struct ibuf *); int ibuf_get(struct ibuf *, void *, size_t); int ibuf_get_ibuf(struct ibuf *, size_t, struct ibuf *); int ibuf_get_n8(struct ibuf *, uint8_t *); int ibuf_get_n16(struct ibuf *, uint16_t *); int ibuf_get_n32(struct ibuf *, uint32_t *); int ibuf_get_n64(struct ibuf *, uint64_t *); int ibuf_get_h16(struct ibuf *, uint16_t *); int ibuf_get_h32(struct ibuf *, uint32_t *); int ibuf_get_h64(struct ibuf *, uint64_t *); char *ibuf_get_string(struct ibuf *, size_t); int ibuf_get_strbuf(struct ibuf *, char *, size_t); int ibuf_skip(struct ibuf *, size_t); void ibuf_free(struct ibuf *); int ibuf_fd_avail(struct ibuf *); int ibuf_fd_get(struct ibuf *); void ibuf_fd_set(struct ibuf *, int); struct msgbuf *msgbuf_new(void); struct msgbuf *msgbuf_new_reader(size_t, struct ibuf *(*)(struct ibuf *, void *, int *), void *); void msgbuf_free(struct msgbuf *); void msgbuf_clear(struct msgbuf *); void msgbuf_concat(struct msgbuf *, struct ibufqueue *); uint32_t msgbuf_queuelen(struct msgbuf *); int ibuf_write(int, struct msgbuf *); int msgbuf_write(int, struct msgbuf *); int ibuf_read(int, struct msgbuf *); int msgbuf_read(int, struct msgbuf *); uint32_t msgbuf_readlen(struct msgbuf *); struct ibuf *msgbuf_get(struct msgbuf *); struct ibufqueue *ibufq_new(void); void ibufq_free(struct ibufqueue *); struct ibuf *ibufq_pop(struct ibufqueue *bufq); void ibufq_push(struct ibufqueue *, struct ibuf *); uint32_t ibufq_queuelen(struct ibufqueue *); void ibufq_concat(struct ibufqueue *, struct ibufqueue *); void ibufq_flush(struct ibufqueue *); /* imsg.c */ int imsgbuf_init(struct imsgbuf *, int); void imsgbuf_allow_fdpass(struct imsgbuf *imsgbuf); void imsgbuf_set_close_callback(struct imsgbuf *, void (*)(struct imsgbuf *, void *)); int imsgbuf_set_maxsize(struct imsgbuf *, uint32_t); void *imsgbuf_set_userdata(struct imsgbuf *, void *); void *imsgbuf_get_userdata(struct imsgbuf *); int imsgbuf_read(struct imsgbuf *); int imsgbuf_write(struct imsgbuf *); int imsgbuf_flush(struct imsgbuf *); void imsgbuf_clear(struct imsgbuf *); uint32_t imsgbuf_queuelen(struct imsgbuf *); int imsgbuf_get(struct imsgbuf *, struct imsg *); int imsg_ibufq_pop(struct ibufqueue *, struct imsg *); void imsg_ibufq_push(struct ibufqueue *, struct imsg *); int imsg_get_ibuf(struct imsg *, struct ibuf *); int imsg_get_data(struct imsg *, void *, size_t); int imsg_get_buf(struct imsg *, void *, size_t); int imsg_get_strbuf(struct imsg *, char *, size_t); int imsg_get_fd(struct imsg *); uint32_t imsg_get_id(struct imsg *); size_t imsg_get_len(struct imsg *); pid_t imsg_get_pid(struct imsg *); uint32_t imsg_get_type(struct imsg *); int imsg_forward(struct imsgbuf *, struct imsg *); int imsg_compose(struct imsgbuf *, uint32_t, uint32_t, pid_t, int, const void *, size_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, size_t); int imsg_add(struct ibuf *, const void *, size_t); int imsg_add_strbuf(struct ibuf *, const char *, size_t); void imsg_close(struct imsgbuf *, struct ibuf *); void imsg_free(struct imsg *); int imsg_set_maxsize(struct ibuf *, size_t); #endif opensmtpd-7.9.0p0/openbsd-compat/includes.h000066400000000000000000000043441525355226100207050ustar00rootroot00000000000000/* $OpenBSD: includes.h,v 1.54 2006/07/22 20:48:23 stevesk Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , 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" #include #include #include /* For CMSG_* */ #ifdef HAVE_LIMITS_H # include /* For PATH_MAX */ #endif #ifdef HAVE_BSTRING_H # include #endif #if defined(HAVE_ENDIAN_H) # include #elif defined(HAVE_SYS_ENDIAN_H) # include #elif defined(HAVE_LIBKERN_OSBYTEORDER_H) # include # include # define htobe16(x) OSSwapHostToBigInt16(x) # define htole16(x) OSSwapHostToLittleInt16(x) # define be16toh(x) OSSwapBigToHostInt16(x) # define le16toh(x) OSSwapLittleToHostInt16(x) # define htobe32(x) OSSwapHostToBigInt32(x) # define htole32(x) OSSwapHostToLittleInt32(x) # define be32toh(x) OSSwapBigToHostInt32(x) # define le32toh(x) OSSwapLittleToHostInt32(x) # define htobe64(x) OSSwapHostToBigInt64(x) # define htole64(x) OSSwapHostToLittleInt64(x) # define be64toh(x) OSSwapBigToHostInt64(x) # define le64toh(x) OSSwapLittleToHostInt64(x) #else # error no compatible endian.h header found #endif #ifdef HAVE_MAILLOCK_H # include /* For _PATH_MAILDIR */ #endif #ifdef HAVE_RPC_TYPES_H # include /* For INADDR_LOOPBACK */ #endif #ifdef USE_PAM #if defined(HAVE_SECURITY_PAM_APPL_H) # include #elif defined (HAVE_PAM_PAM_APPL_H) # include #endif #endif #include /* chl */ #ifdef HAVE_NETDB_H # include #endif /* end of chl*/ #if !defined(NETDB_INTERNAL) # define NETDB_INTERNAL (-1) #endif #include "defines.h" #include "openbsd-compat.h" #endif /* INCLUDES_H */ opensmtpd-7.9.0p0/openbsd-compat/inet_net_pton.c000066400000000000000000000143631525355226100217410ustar00rootroot00000000000000/* $OpenBSD: inet_net_pton.c,v 1.14 2022/12/27 17:10:06 jmc Exp $ */ /* * Copyright (c) 2012 by Gilles Chehade * Copyright (c) 1996,1999 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" #if !defined(HAVE_INET_NET_PTON) || defined(HAVE_LIBBSD) #include #include #include #include #include #include #include #include #include #include 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; tmp = 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); if (dirty == 0) tmp = n; else tmp = (tmp << 4) | n; if (++dirty == 2) { if (size-- == 0) goto emsgsize; *dst++ = (u_char) tmp; dirty = 0; } } if (dirty) { /* Odd trailing nybble? */ if (size-- == 0) goto emsgsize; *dst++ = (u_char) (tmp << 4); } } 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; } /* Fiery 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) { struct in6_addr in6; int ret; int bits; size_t bytes; char buf[INET6_ADDRSTRLEN + sizeof("/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, &in6); if (ret != 1) return (-1); if (sep == NULL) bits = 128; else { bits = strtonum(sep, 0, 128, &errstr); if (errstr) { errno = EINVAL; return (-1); } } bytes = (bits + 7) / 8; if (bytes > size) { errno = EMSGSIZE; return (-1); } memcpy(dst, &in6.s6_addr, bytes); return (bits); } #endif opensmtpd-7.9.0p0/openbsd-compat/libasr/000077500000000000000000000000001525355226100201755ustar00rootroot00000000000000opensmtpd-7.9.0p0/openbsd-compat/libasr/asr.c000066400000000000000000000473511525355226100211400ustar00rootroot00000000000000/* $OpenBSD: asr.c,v 1.68 2022/01/20 14:18:10 naddy Exp $ */ /* * Copyright (c) 2010-2012 Eric Faurot * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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_ctx *ac = arg; struct asr *asr; struct asr **priv; if (ac) { _asr_ctx_unref(ac); return; } else { priv = _THREAD_PRIVATE_DT(_asr, _asr, NULL, &_asr); if (*priv == NULL) return; asr = *priv; *priv = NULL; } _asr_ctx_unref(asr->a_ctx); free(asr); } static void _asr_resolver_done_tp(void *arg) { struct asr **priv = arg; struct asr *asr; if (*priv == NULL) return; asr = *priv; _asr_ctx_unref(asr->a_ctx); free(asr); free(priv); } void * asr_resolver_from_string(const char *str) { struct asr_ctx *ac; if ((ac = asr_ctx_create()) == NULL) return NULL; if (asr_ctx_from_string(ac, str) == -1) { asr_ctx_free(ac); return NULL; } return ac; } DEF_WEAK(asr_resolver_from_string); void asr_resolver_free(void *arg) { _asr_ctx_unref(arg); } DEF_WEAK(asr_resolver_free); /* * 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_ctx *ac = arg; struct asr *asr; struct asr **priv; if (ac) { asr_ctx_ref(ac); return ac; } else { DPRINT("using thread-local resolver\n"); priv = _THREAD_PRIVATE_DT(_asr, _asr, _asr_resolver_done_tp, &_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; /* * opensmtpd-portable knob to prefer IPv6 over IPv4. * * See: https://github.com/poolpOrg/OpenSMTPD/issues/182 */ #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; } else if (!strcmp(tok[i], "trust-ad")) #ifdef RES_TRUSTAD ac->ac_options |= RES_TRUSTAD; #else /* nop */ ; #endif } } } /* * 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) { struct sockaddr_in6 *sin6; struct sockaddr_in *sin; int i, trustad; 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; } trustad = 1; for (i = 0; i < ac->ac_nscount && trustad; i++) { switch (ac->ac_ns[i]->sa_family) { case AF_INET: sin = (struct sockaddr_in *)ac->ac_ns[i]; if (sin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) trustad = 0; break; case AF_INET6: sin6 = (struct sockaddr_in6 *)ac->ac_ns[i]; if (!IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr)) trustad = 0; break; default: trustad = 0; break; } } if (trustad) #ifdef RES_TRUSTAD ac->ac_options |= RES_TRUSTAD; #else /* nop */ ; #endif 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, count; if (_dname[0] == 0) { strlcpy(buf, ".", max); return buf; } res = buf; left = max - 1; while (dname[0] && left) { 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); } opensmtpd-7.9.0p0/openbsd-compat/libasr/asr.h000066400000000000000000000066721525355226100211460ustar00rootroot00000000000000/* $OpenBSD: asr.h,v 1.1 2014/03/26 18:13:15 eric Exp $ */ /* * Copyright (c) 2012-2014 Eric Faurot * * 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 *); opensmtpd-7.9.0p0/openbsd-compat/libasr/asr_compat.c000066400000000000000000000052301525355226100224710ustar00rootroot00000000000000/* $OpenBSD: asr_debug.c,v 1.25 2018/04/28 15:16:49 schwarze Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * 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 #ifdef HAVE_ARPA_NAMESER_COMPAT_H #include #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 */ opensmtpd-7.9.0p0/openbsd-compat/libasr/asr_compat.h000066400000000000000000000040101525355226100224710ustar00rootroot00000000000000/* * Copyright (c) 2018 Eric Faurot * * 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 /* * sys/socket.h */ #ifndef SOCK_DNS #define SOCK_DNS 0 #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 #ifndef EAI_BADHINTS #define EAI_BADHINTS EAI_FAIL #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 /* * netinet6/in6.h */ #ifndef IN6_IS_ADDR_MC_INTFACELOCAL #define IN6_IS_ADDR_MC_INTFACELOCAL(x) IN6_IS_ADDR_MC_NODELOCAL(x) #endif opensmtpd-7.9.0p0/openbsd-compat/libasr/asr_debug.c000066400000000000000000000211621525355226100222760ustar00rootroot00000000000000/* $OpenBSD: asr_debug.c,v 1.28 2021/11/22 20:18:27 jca Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * 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 #include #include #include #ifdef HAVE_ARPA_NAMESER_COMPAT_H #include #endif #include #include #include #include #include #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.mname, tmp, sizeof tmp), print_dname(rr->rr.soa.rname, 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"); #ifdef RES_USE_CD PRINTOPT(RES_USE_CD, "USE_CD"); #endif #ifdef RES_TRUSTAD PRINTOPT(RES_TRUSTAD, "TRUSTAD"); #endif 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 "?"; } } opensmtpd-7.9.0p0/openbsd-compat/libasr/asr_private.h000066400000000000000000000177311525355226100226760ustar00rootroot00000000000000/* $OpenBSD: asr_private.h,v 1.49 2023/11/20 12:15:16 florian Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * 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 #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); int hnok_lenient(const char *); int _asr_is_localhost(const char*); /* 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 opensmtpd-7.9.0p0/openbsd-compat/libasr/asr_run.3000066400000000000000000000250311525355226100217330ustar00rootroot00000000000000.\" $OpenBSD: asr_run.3,v 1.5 2022/03/31 17:27:15 naddy Exp $ .\" .\" Copyright (c) 2012-2014, Eric Faurot .\" .\" 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: March 31 2022 $ .Dt ASR_RUN 3 .Os .Sh NAME .Nm asr_run , .Nm asr_run_sync , .Nm asr_abort , .Nm asr_resolver_from_string , .Nm asr_resolver_free , .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 void * .Fn asr_resolver_from_string "const char *str" .Ft void .Fn asr_resolver_free "void *asr" .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 .Fn asr_resolver_from_string function constructs an asr context from a string that conforms to the .Xr resolv.conf 5 file format. .Fn asr_resolver_free frees an asr context obtained from .Fn asr_resolver_from_string . .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. An .Ar asr argument of NULL will use the default context for the current thread. This is constructed from .Pa /etc/resolv.conf and takes care of reloading the file when it changes. .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 opensmtpd-7.9.0p0/openbsd-compat/libasr/asr_utils.c000066400000000000000000000321141525355226100223470ustar00rootroot00000000000000/* $OpenBSD: asr_utils.c,v 1.22 2023/11/20 12:15:16 florian Exp $ */ /* * Copyright (c) 2009-2012 Eric Faurot * * 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 #include #include #include #include #include #ifdef HAVE_ARPA_NAMESER_COMPAT_H #include #endif #include #include #include #include #include #include #include #include #include #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 + 1 >= 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 additional 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_INTFACELOCAL(&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); } int hnok_lenient(const char *dn) { int pch = '\0', ch = *dn++; while (ch != '\0') { /* can't start with . or - */ if (pch == '\0' && (ch == '.' || ch == '-')) return 0; if (pch == '.' && ch == '.') return 0; if (!(isalpha((unsigned char)ch) || isdigit((unsigned char)ch) || ch == '.' || ch == '-' || ch == '_')) return 0; pch = ch; ch = *dn++; } return 1; } /* Check if the hostname is localhost or if it's in the localhost domain */ int _asr_is_localhost(const char *hn) { size_t hnlen, localhostlen; if (hn == NULL) return 0; if (strcasecmp(hn, "localhost") == 0 || strcasecmp(hn, "localhost.") == 0) return 1; hnlen = strlen(hn); localhostlen = strlen(".localhost"); if (hnlen < localhostlen) return 0; if (strcasecmp(hn + hnlen - localhostlen, ".localhost") == 0) return 1; localhostlen++; if (hnlen < localhostlen) return 0; if (strcasecmp(hn + hnlen - localhostlen, ".localhost.") == 0) return 1; return 0; } opensmtpd-7.9.0p0/openbsd-compat/libasr/getaddrinfo.c000066400000000000000000000031231525355226100226260ustar00rootroot00000000000000/* $OpenBSD: getaddrinfo.c,v 1.9 2015/10/08 14:08:44 eric Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * 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 #include #include #include #include #include #include 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); opensmtpd-7.9.0p0/openbsd-compat/libasr/getaddrinfo_async.c000066400000000000000000000451601525355226100240320ustar00rootroot00000000000000/* $OpenBSD: getaddrinfo_async.c,v 1.62 2024/01/15 18:03:39 florian Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * 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 #include #include #include #include #ifdef HAVE_ARPA_NAMESER_COMPAT_H #include #endif #include #include #include #include #include #include #include #include #include #include #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, is_localhost = 0; 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; 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; } 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) { ar->ar_gai_errno = EAI_BADHINTS; 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 (!(ai->ai_flags & AI_NUMERICHOST)) is_localhost = _asr_is_localhost(as->as.ai.hostname); /* * If hostname is NULL, "localhost" or falls within the * ".localhost." domain, use local address. * RFC 6761, 6.3: * 3. Name resolution APIs and libraries SHOULD recognize * localhost names as special and SHOULD always return the IP * loopback address for address queries and negative responses * for all other query types. Name resolution APIs SHOULD NOT * send queries for localhost names to their configured caching * DNS server(s). */ if (as->as.ai.hostname == NULL || is_localhost) { 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 && !is_localhost) ? "0.0.0.0" : "127.0.0.1"; else /* PF_INET6 */ str = (ai->ai_flags & AI_PASSIVE && !is_localhost) ? "::" : "::1"; /* This can't fail */ _asr_sockaddr_from_str(&sa.sa, family, str); if ((r = addrinfo_add(as, &sa.sa, "localhost."))) { 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; } /* make sure there are no funny characters in hostname */ if (!hnok_lenient(as->as.ai.hostname)) { ar->ar_gai_errno = EAI_FAIL; 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; } /* * Retrieve 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); } opensmtpd-7.9.0p0/openbsd-compat/libasr/gethostnamadr.c000066400000000000000000000102561525355226100232050ustar00rootroot00000000000000/* $OpenBSD: gethostnamadr.c,v 1.13 2015/09/14 07:38:37 guenther Exp $ */ /* * Copyright (c) 2012,2013 Eric Faurot * * 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 /* ALIGN */ #include #include #include #include #include #include #include #include #include #include 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); } opensmtpd-7.9.0p0/openbsd-compat/libasr/gethostnamadr_async.c000066400000000000000000000403051525355226100244000ustar00rootroot00000000000000/* $OpenBSD: gethostnamadr_async.c,v 1.50 2024/09/03 18:20:35 op Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * 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 #include #include #include #include #ifdef HAVE_ARPA_NAMESER_COMPAT_H #include #endif #include #include #include #include #include /* for res_hnok */ #include #include #include #include #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; } if (!hnok_lenient(as->as.hostnamadr.name)) { ar->ar_h_errno = NETDB_INTERNAL; ar->ar_errno = EINVAL; async_set_state(as, ASR_STATE_HALT); break; } /* * If hostname is "localhost" or falls within the * ".localhost." domain, use local address. * RFC 6761, 6.3: * 3. Name resolution APIs and libraries SHOULD * recognize localhost names as special and SHOULD * always return the IP loopback address for address * queries and negative responses for all other query * types. Name resolution APIs SHOULD NOT send queries * for localhost names to their configured caching DNS * server(s). */ if (_asr_is_localhost(as->as.hostnamadr.name)) { inet_pton(as->as.hostnamadr.family, as->as.hostnamadr.family == AF_INET ? "127.0.0.1" : "::1", addr); 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. * Safeguard 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; } opensmtpd-7.9.0p0/openbsd-compat/libasr/getnameinfo.c000066400000000000000000000124031525355226100226350ustar00rootroot00000000000000/* $OpenBSD: getnameinfo.c,v 1.11 2022/12/27 17:10:06 jmc Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * 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 #include #include #include #include #include #include #include #include #include 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 * occurred, 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 * occurred, 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; r = getservbyport_r(port, proto, &s, &sd); if (r == 0) n = strlcpy(buf, s.s_name, buflen); endservent_r(&sd); errno = saved_errno; if (r == 0) return (n); } 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); opensmtpd-7.9.0p0/openbsd-compat/libasr/getnameinfo_async.c000066400000000000000000000163661525355226100240460ustar00rootroot00000000000000/* $OpenBSD: getnameinfo_async.c,v 1.15 2020/12/21 09:40:35 eric Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * 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 #include #include #include #include #include #include #include #include #include #include #include #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 n, 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); if (r == 0) n = strlcpy(buf, s.s_name, buflen); #else r = -1; #endif #ifdef HAVE_ENDSERVENT_R endservent_r(&sd); #endif if (r == 0) { if (n >= buflen) return (-1); return (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_INTFACELOCAL(&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); } opensmtpd-7.9.0p0/openbsd-compat/libasr/getnetnamadr.c000066400000000000000000000056271525355226100230240ustar00rootroot00000000000000/* $OpenBSD: getnetnamadr.c,v 1.9 2015/01/16 16:48:51 deraadt Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * 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 /* ALIGN */ #include #include #include #include #include #include #include #include #include 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); } opensmtpd-7.9.0p0/openbsd-compat/libasr/getnetnamadr_async.c000066400000000000000000000030501525355226100242050ustar00rootroot00000000000000/* $OpenBSD: getnetnamadr_async.c,v 1.26 2018/04/28 15:16:49 schwarze Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * 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 #include #include #include #include #include #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); opensmtpd-7.9.0p0/openbsd-compat/libasr/getrrsetbyname.c000066400000000000000000000040741525355226100234010ustar00rootroot00000000000000/* $OpenBSD: getrrsetbyname.c,v 1.6 2015/09/14 07:38:37 guenther Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * 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 #include #include #include #include #include #include #include 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); opensmtpd-7.9.0p0/openbsd-compat/libasr/getrrsetbyname_async.c000066400000000000000000000352301525355226100245740ustar00rootroot00000000000000/* $OpenBSD: getrrsetbyname_async.c,v 1.13 2023/03/15 22:12:00 millert Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * 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 #include #include #include #include #include #include #include #include #include #include #include #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 original implementation. */ /* $OpenBSD: getrrsetbyname_async.c,v 1.13 2023/03/15 22:12:00 millert 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; if (size <= HFIXEDSZ) return (NULL); /* 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]; #define NEED(need) \ do { \ if (*cp + need > answer + size) \ goto fail; \ } while (0) for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) { if (*cp >= answer + size) { fail: free_dns_query(head); return (NULL); } /* allocate and initialize struct */ curr = calloc(1, sizeof(struct dns_query)); if (curr == NULL) goto fail; 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); } NEED(length); *cp += length; /* type */ NEED(INT16SZ); curr->type = _getshort(*cp); *cp += INT16SZ; /* class */ NEED(INT16SZ); curr->class = _getshort(*cp); *cp += INT16SZ; } #undef NEED 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]; #define NEED(need) \ do { \ if (*cp + need > answer + size) \ goto fail; \ } while (0) for (i = 1, head = NULL, prev = NULL; i <= count; i++, prev = curr) { if (*cp >= answer + size) { fail: free_dns_rr(head); return (NULL); } /* allocate and initialize struct */ curr = calloc(1, sizeof(struct dns_rr)); if (curr == NULL) goto fail; 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); } NEED(length); *cp += length; /* type */ NEED(INT16SZ); curr->type = _getshort(*cp); *cp += INT16SZ; /* class */ NEED(INT16SZ); curr->class = _getshort(*cp); *cp += INT16SZ; /* ttl */ NEED(INT32SZ); curr->ttl = _getlong(*cp); *cp += INT32SZ; /* rdata size */ NEED(INT16SZ); curr->size = _getshort(*cp); *cp += INT16SZ; /* rdata itself */ NEED(curr->size); curr->rdata = malloc(curr->size); if (curr->rdata == NULL) { free_dns_rr(head); return (NULL); } memcpy(curr->rdata, *cp, curr->size); *cp += curr->size; } #undef NEED 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); } opensmtpd-7.9.0p0/openbsd-compat/libasr/libasr.la000066400000000000000000000017411525355226100217720ustar00rootroot00000000000000# 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' opensmtpd-7.9.0p0/openbsd-compat/libasr/res_debug.c000066400000000000000000000001171525355226100222770ustar00rootroot00000000000000/* $OpenBSD: res_debug.c,v 1.1 2012/09/08 11:08:21 eric Exp $ */ /* NOTHING */ opensmtpd-7.9.0p0/openbsd-compat/libasr/res_init.c000066400000000000000000000057471525355226100221720ustar00rootroot00000000000000/* $OpenBSD: res_init.c,v 1.11 2019/06/17 05:54:45 otto Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * 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 #include #include #include #include #include #include #include #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); opensmtpd-7.9.0p0/openbsd-compat/libasr/res_mkquery.c000066400000000000000000000063661525355226100227220ustar00rootroot00000000000000/* $OpenBSD: res_mkquery.c,v 1.14 2021/11/22 20:18:27 jca Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * 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 #include #include #include /* for MAXDNAME */ #include #include #include #include #include #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 #ifdef RES_TRUSTAD if (ac->ac_options & RES_TRUSTAD) h.flags |= AD_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)); } opensmtpd-7.9.0p0/openbsd-compat/libasr/res_query.c000066400000000000000000000047411525355226100223650ustar00rootroot00000000000000/* $OpenBSD: res_query.c,v 1.9 2015/10/05 02:57:16 guenther Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * 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 #include #include #include #include #include #include #include #include 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); } opensmtpd-7.9.0p0/openbsd-compat/libasr/res_search_async.c000066400000000000000000000177671525355226100236760ustar00rootroot00000000000000/* $OpenBSD: res_search_async.c,v 1.21 2017/02/27 10:44:46 jca Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * 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 #include #include #include #include #include #include #include #include #include #include #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); } } opensmtpd-7.9.0p0/openbsd-compat/libasr/res_send.c000066400000000000000000000030531525355226100221440ustar00rootroot00000000000000/* $OpenBSD: res_send.c,v 1.8 2014/03/26 18:13:15 eric Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * 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 #include #include #include #include #include #include #include #include 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); } opensmtpd-7.9.0p0/openbsd-compat/libasr/res_send_async.c000066400000000000000000000453001525355226100233420ustar00rootroot00000000000000/* $OpenBSD: res_send_async.c,v 1.41 2022/06/20 06:45:31 jca Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * 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 #include #include #include #include #ifdef HAVE_ARPA_NAMESER_COMPAT_H #include #endif #include #include #include #include #include #include /* for res_random */ #include #include #include #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 *); #ifdef RES_TRUSTAD static void clear_ad(struct asr_result *); #endif 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 have 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; #ifdef RES_TRUSTAD if (!(as->as_ctx->ac_options & RES_TRUSTAD)) clear_ad(ar); #endif 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 | SOCK_DNS, 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 #ifdef RES_TRUSTAD if (as->as_ctx->ac_options & RES_TRUSTAD) h.flags |= AD_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); } #ifdef RES_TRUSTAD /* * Clear AD flag in the answer. */ static void clear_ad(struct asr_result *ar) { struct asr_dns_header *h; uint16_t flags; h = (struct asr_dns_header *)ar->ar_data; flags = ntohs(h->flags); flags &= ~(AD_MASK); h->flags = htons(flags); } #endif /* * 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); } opensmtpd-7.9.0p0/openbsd-compat/libasr/sethostent.c000066400000000000000000000020211525355226100225340ustar00rootroot00000000000000/* $OpenBSD: sethostent.c,v 1.2 2018/04/28 15:09:35 schwarze Exp $ */ /* * Copyright (c) 2012 Eric Faurot * * 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 #include void sethostent(int stayopen) { } void endhostent(void) { } struct hostent * gethostent(void) { h_errno = NETDB_INTERNAL; return NULL; } opensmtpd-7.9.0p0/openbsd-compat/libasr/thread_private.h000066400000000000000000000003441525355226100233500ustar00rootroot00000000000000/* * */ #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) #define _THREAD_PRIVATE_DT(a, b, c, d) (d) opensmtpd-7.9.0p0/openbsd-compat/libtls/000077500000000000000000000000001525355226100202125ustar00rootroot00000000000000opensmtpd-7.9.0p0/openbsd-compat/libtls/a_time_posix.c000066400000000000000000000172761525355226100230530ustar00rootroot00000000000000/* $OpenBSD: a_time_posix.c,v 1.5 2024/02/18 16:28:38 tb Exp $ */ /* * Copyright (c) 2022, Google Inc. * Copyright (c) 2022, Bob Beck * * Permission to use, copy, modify, and/or 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. */ /* * Time conversion to/from POSIX time_t and struct tm, with no support * for time zones other than UTC */ #include "includes.h" #include #include #include #include #include #include #include #if !HAVE_OPENSSL_POSIX_TO_TM /* from libcrypto/crypto_internal.h */ #ifndef CTASSERT #define CTASSERT(x) \ extern char _ctassert[(x) ? 1 : -1] __attribute__((__unused__)) #endif /* CTASSERT */ #define SECS_PER_HOUR (int64_t)(60 * 60) #define SECS_PER_DAY (int64_t)(24 * SECS_PER_HOUR) /* * Is a year/month/day combination valid, in the range from year 0000 * to 9999? */ static int is_valid_date(int64_t year, int64_t month, int64_t day) { int days_in_month; if (day < 1 || month < 1 || year < 0 || year > 9999) return 0; switch (month) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: days_in_month = 31; break; case 4: case 6: case 9: case 11: days_in_month = 30; break; case 2: if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) days_in_month = 29; else days_in_month = 28; break; default: return 0; } return day <= days_in_month; } /* * Is a time valid? Leap seconds of 60 are not considered valid, as * the POSIX time in seconds does not include them. */ static int is_valid_time(int hours, int minutes, int seconds) { return hours >= 0 && minutes >= 0 && seconds >= 0 && hours <= 23 && minutes <= 59 && seconds <= 59; } /* 0000-01-01 00:00:00 UTC */ #define MIN_POSIX_TIME INT64_C(-62167219200) /* 9999-12-31 23:59:59 UTC */ #define MAX_POSIX_TIME INT64_C(253402300799) /* Is a int64 time representing a time within our expected range? */ static int is_valid_posix_time(int64_t time) { return MIN_POSIX_TIME <= time && time <= MAX_POSIX_TIME; } /* * Inspired by algorithms presented in * https://howardhinnant.github.io/date_algorithms.html * (Public Domain) */ static int posix_time_from_utc(int64_t year, int64_t month, int64_t day, int64_t hours, int64_t minutes, int64_t seconds, int64_t *out_time) { int64_t era, year_of_era, day_of_year, day_of_era, posix_days; if (!is_valid_date(year, month, day) || !is_valid_time(hours, minutes, seconds)) return 0; if (month <= 2) year--; /* Start years on Mar 1, so leap days end a year. */ /* At this point year will be in the range -1 and 9999.*/ era = (year >= 0 ? year : year - 399) / 400; year_of_era = year - era * 400; day_of_year = (153 * (month > 2 ? month - 3 : month + 9) + 2) / 5 + day - 1; day_of_era = year_of_era * 365 + year_of_era / 4 - year_of_era / 100 + day_of_year; posix_days = era * 146097 + day_of_era - 719468; *out_time = posix_days * SECS_PER_DAY + hours * SECS_PER_HOUR + minutes * 60 + seconds; return 1; } /* * Inspired by algorithms presented in * https://howardhinnant.github.io/date_algorithms.html * (Public Domain) */ static int utc_from_posix_time(int64_t time, int *out_year, int *out_month, int *out_day, int *out_hours, int *out_minutes, int *out_seconds) { int64_t days, leftover_seconds, era, day_of_era, year_of_era, day_of_year, month_of_year; if (!is_valid_posix_time(time)) return 0; days = time / SECS_PER_DAY; leftover_seconds = time % SECS_PER_DAY; if (leftover_seconds < 0) { days--; leftover_seconds += SECS_PER_DAY; } days += 719468; /* Shift to starting epoch of Mar 1 0000. */ /* At this point, days will be in the range -61 and 3652364. */ era = (days > 0 ? days : days - 146096) / 146097; day_of_era = days - era * 146097; year_of_era = (day_of_era - day_of_era / 1460 + day_of_era / 36524 - day_of_era / 146096) / 365; *out_year = year_of_era + era * 400; /* Year starts on Mar 1 */ day_of_year = day_of_era - (365 * year_of_era + year_of_era / 4 - year_of_era / 100); month_of_year = (5 * day_of_year + 2) / 153; *out_month = (month_of_year < 10 ? month_of_year + 3 : month_of_year - 9); if (*out_month <= 2) (*out_year)++; /* Adjust year back to Jan 1 start of year. */ *out_day = day_of_year - (153 * month_of_year + 2) / 5 + 1; *out_hours = leftover_seconds / SECS_PER_HOUR; leftover_seconds %= SECS_PER_HOUR; *out_minutes = leftover_seconds / 60; *out_seconds = leftover_seconds % 60; return 1; } int OPENSSL_tm_to_posix(const struct tm *tm, int64_t *out) { return posix_time_from_utc(tm->tm_year + (int64_t)1900, tm->tm_mon + (int64_t)1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, out); } int OPENSSL_posix_to_tm(int64_t time, struct tm *out_tm) { struct tm tmp_tm = {0}; memset(out_tm, 0, sizeof(*out_tm)); if (!utc_from_posix_time(time, &tmp_tm.tm_year, &tmp_tm.tm_mon, &tmp_tm.tm_mday, &tmp_tm.tm_hour, &tmp_tm.tm_min, &tmp_tm.tm_sec)) return 0; tmp_tm.tm_year -= 1900; tmp_tm.tm_mon -= 1; *out_tm = tmp_tm; return 1; } int asn1_time_tm_to_time_t(const struct tm *tm, time_t *out) { int64_t posix_time; if (!OPENSSL_tm_to_posix(tm, &posix_time)) return 0; #ifdef SMALL_TIME_T /* For portable. */ if (sizeof(time_t) == sizeof(int32_t) && (posix_time > INT32_MAX || posix_time < INT32_MIN)) return 0; #endif *out = posix_time; return 1; } int asn1_time_time_t_to_tm(const time_t *time, struct tm *out_tm) { int64_t posix_time = *time; return OPENSSL_posix_to_tm(posix_time, out_tm); } int OPENSSL_timegm(const struct tm *tm, time_t *out) { return asn1_time_tm_to_time_t(tm, out); } #endif /* !HAVE_OPENSSL_POSIX_TO_TM */ #if !HAVE_OPENSSL_GMTIME struct tm * OPENSSL_gmtime(const time_t *time, struct tm *out_tm) { if (!asn1_time_time_t_to_tm(time, out_tm)) return NULL; return out_tm; } /* Public API in OpenSSL. BoringSSL uses int64_t instead of long. */ int OPENSSL_gmtime_adj(struct tm *tm, int offset_day, int64_t offset_sec) { int64_t posix_time; if (!OPENSSL_tm_to_posix(tm, &posix_time)) return 0; CTASSERT(INT_MAX <= INT64_MAX / SECS_PER_DAY); CTASSERT(MAX_POSIX_TIME <= INT64_MAX - INT_MAX * SECS_PER_DAY); CTASSERT(MIN_POSIX_TIME >= INT64_MIN - INT_MIN * SECS_PER_DAY); posix_time += offset_day * SECS_PER_DAY; if (posix_time > 0 && offset_sec > INT64_MAX - posix_time) return 0; if (posix_time < 0 && offset_sec < INT64_MIN - posix_time) return 0; posix_time += offset_sec; if (!OPENSSL_posix_to_tm(posix_time, tm)) return 0; return 1; } int OPENSSL_gmtime_diff(int *out_days, int *out_secs, const struct tm *from, const struct tm *to) { int64_t time_to, time_from, timediff, daydiff; if (!OPENSSL_tm_to_posix(to, &time_to) || !OPENSSL_tm_to_posix(from, &time_from)) return 0; /* Times are in range, so these calculations cannot overflow. */ CTASSERT(SECS_PER_DAY <= INT_MAX); CTASSERT((MAX_POSIX_TIME - MIN_POSIX_TIME) / SECS_PER_DAY <= INT_MAX); timediff = time_to - time_from; daydiff = timediff / SECS_PER_DAY; timediff %= SECS_PER_DAY; *out_secs = timediff; *out_days = daydiff; return 1; } #endif /* !HAVE_OPENSSL_GMTIME */ opensmtpd-7.9.0p0/openbsd-compat/libtls/by_mem.c000066400000000000000000000116531525355226100216340ustar00rootroot00000000000000/* $OpenBSD: by_mem.c,v 1.4 2017/01/29 17:49:23 beck Exp $ */ /* 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 "includes.h" #ifndef HAVE_X509_LOOKUP_MEM #include #include #include #include #include #include #include #include #include #include #define X509error(r) ERR_PUT_error(ERR_LIB_X509,(0xfff),(r),__FILE__,__LINE__) #define X509_L_MEM 3 static int by_mem_ctrl(X509_LOOKUP *, int, const char *, long, char **); static X509_LOOKUP_METHOD *x509_mem_lookup; X509_LOOKUP_METHOD * X509_LOOKUP_mem(void) { if (x509_mem_lookup == NULL) { x509_mem_lookup = X509_LOOKUP_meth_new("Load cert from memory"); X509_LOOKUP_meth_set_ctrl(x509_mem_lookup, by_mem_ctrl); } return (x509_mem_lookup); } static int by_mem_ctrl(X509_LOOKUP *lu, int cmd, const char *buf, long type, char **ret) { STACK_OF(X509_INFO) *inf = NULL; const struct iovec *iov; X509_INFO *itmp; BIO *in = NULL; int i, count = 0, ok = 0; iov = (const struct iovec *)buf; if (!(cmd == X509_L_MEM && type == X509_FILETYPE_PEM)) goto done; if ((in = BIO_new_mem_buf(iov->iov_base, iov->iov_len)) == NULL) goto done; if ((inf = PEM_X509_INFO_read_bio(in, NULL, NULL, NULL)) == NULL) goto done; for (i = 0; i < sk_X509_INFO_num(inf); i++) { itmp = sk_X509_INFO_value(inf, i); if (itmp->x509) { ok = X509_STORE_add_cert(X509_LOOKUP_get_store(lu), itmp->x509); if (!ok) goto done; count++; } if (itmp->crl) { ok = X509_STORE_add_crl(X509_LOOKUP_get_store(lu), itmp->crl); if (!ok) goto done; count++; } } ok = count != 0; done: if (count == 0) X509error(ERR_R_PEM_LIB); if (inf != NULL) sk_X509_INFO_pop_free(inf, X509_INFO_free); if (in != NULL) BIO_free(in); return (ok); } #endif /* HAVE_X509_LOOKUP_MEM */ opensmtpd-7.9.0p0/openbsd-compat/libtls/openssl.c000066400000000000000000000152721525355226100220500ustar00rootroot00000000000000#include "includes.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "ssl.h" #ifndef HAVE_SSL_CTX_LOAD_VERIFY_MEM #define X509_LOOKUP_add_mem(x,iov,type) \ X509_LOOKUP_ctrl((x),X509_L_MEM,(const char *)(iov),\ (long)(type),NULL) #define X509_L_MEM 3 #define SSL_ECDH_CURVE "prime256v1" X509_LOOKUP_METHOD * X509_LOOKUP_mem(void); int X509_STORE_load_mem(X509_STORE *ctx, void *buf, int len) { X509_LOOKUP *lookup; struct iovec iov; lookup = X509_STORE_add_lookup(ctx, X509_LOOKUP_mem()); if (lookup == NULL) return (0); iov.iov_base = buf; iov.iov_len = len; if (X509_LOOKUP_add_mem(lookup, &iov, X509_FILETYPE_PEM) != 1) return (0); return (1); } int SSL_CTX_load_verify_mem(SSL_CTX *ctx, void *buf, int len) { return (X509_STORE_load_mem(SSL_CTX_get_cert_store(ctx), buf, len)); } #endif /* HAVE_SSL_CTX_LOAD_VERIFY_MEM */ #ifndef HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM /* 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 . */ /* * 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); } #endif /* HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM */ opensmtpd-7.9.0p0/openbsd-compat/libtls/tls.c000066400000000000000000000500401525355226100211570ustar00rootroot00000000000000/* $OpenBSD: tls.c,v 1.105 2026/04/16 07:28:00 tb Exp $ */ /* * Copyright (c) 2014 Joel Sing * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tls_internal.h" static struct tls_config *tls_config_default; static int tls_init_rv = -1; static void tls_do_init(void) { OPENSSL_init_ssl(OPENSSL_INIT_NO_LOAD_CONFIG, NULL); if (BIO_sock_init() != 1) return; if ((tls_config_default = tls_config_new_internal()) == NULL) return; tls_config_default->refcount++; tls_init_rv = 0; } int tls_init(void) { if (tls_init_rv == -1) tls_do_init(); return tls_init_rv; } const char * tls_error(struct tls *ctx) { return ctx->error.msg; } int tls_error_code(struct tls *ctx) { return ctx->error.code; } void tls_error_clear(struct tls_error *error) { free(error->msg); error->msg = NULL; error->code = TLS_ERROR_UNKNOWN; error->errno_value = 0; error->tls = 0; } static int tls_error_vset(struct tls_error *error, int code, int errno_value, const char *fmt, va_list ap) { char *errmsg = NULL; int rv = -1; tls_error_clear(error); error->code = code; error->errno_value = errno_value; error->tls = 1; if (vasprintf(&errmsg, fmt, ap) == -1) { errmsg = NULL; goto err; } if (errno_value == -1) { error->msg = errmsg; return (0); } if (asprintf(&error->msg, "%s: %s", errmsg, strerror(errno_value)) == -1) { error->msg = NULL; goto err; } rv = 0; err: free(errmsg); return (rv); } int tls_error_set(struct tls_error *error, int code, const char *fmt, ...) { va_list ap; int errno_value, rv; errno_value = errno; va_start(ap, fmt); rv = tls_error_vset(error, code, errno_value, fmt, ap); va_end(ap); return (rv); } int tls_error_setx(struct tls_error *error, int code, const char *fmt, ...) { va_list ap; int rv; va_start(ap, fmt); rv = tls_error_vset(error, code, -1, fmt, ap); va_end(ap); return (rv); } int tls_config_set_error(struct tls_config *config, int code, const char *fmt, ...) { va_list ap; int errno_value, rv; errno_value = errno; va_start(ap, fmt); rv = tls_error_vset(&config->error, code, errno_value, fmt, ap); va_end(ap); return (rv); } int tls_config_set_errorx(struct tls_config *config, int code, const char *fmt, ...) { va_list ap; int rv; va_start(ap, fmt); rv = tls_error_vset(&config->error, code, -1, fmt, ap); va_end(ap); return (rv); } int tls_set_error(struct tls *ctx, int code, const char *fmt, ...) { va_list ap; int errno_value, rv; errno_value = errno; va_start(ap, fmt); rv = tls_error_vset(&ctx->error, code, errno_value, fmt, ap); va_end(ap); return (rv); } int tls_set_errorx(struct tls *ctx, int code, const char *fmt, ...) { va_list ap; int rv; va_start(ap, fmt); rv = tls_error_vset(&ctx->error, code, -1, fmt, ap); va_end(ap); return (rv); } int tls_set_ssl_errorx(struct tls *ctx, int code, const char *fmt, ...) { va_list ap; int rv; /* Only set an error if a more specific one does not already exist. */ if (ctx->error.tls != 0) return (0); va_start(ap, fmt); rv = tls_error_vset(&ctx->error, code, -1, fmt, ap); va_end(ap); return (rv); } struct tls_sni_ctx * tls_sni_ctx_new(void) { return (calloc(1, sizeof(struct tls_sni_ctx))); } void tls_sni_ctx_free(struct tls_sni_ctx *sni_ctx) { if (sni_ctx == NULL) return; SSL_CTX_free(sni_ctx->ssl_ctx); X509_free(sni_ctx->ssl_cert); free(sni_ctx); } struct tls * tls_new(void) { struct tls *ctx; if ((ctx = calloc(1, sizeof(*ctx))) == NULL) return (NULL); tls_reset(ctx); if (tls_configure(ctx, tls_config_default) == -1) { free(ctx); return NULL; } return (ctx); } int tls_configure(struct tls *ctx, struct tls_config *config) { if (config == NULL) config = tls_config_default; config->refcount++; tls_config_free(ctx->config); ctx->config = config; ctx->keypair = config->keypair; if ((ctx->flags & TLS_SERVER) != 0) return (tls_configure_server(ctx)); return (0); } int tls_cert_hash(X509 *cert, char **hash) { char d[EVP_MAX_MD_SIZE], *dhex = NULL; int dlen, rv = -1; free(*hash); *hash = NULL; if (X509_digest(cert, EVP_sha256(), d, &dlen) != 1) goto err; if (tls_hex_string(d, dlen, &dhex, NULL) != 0) goto err; if (asprintf(hash, "SHA256:%s", dhex) == -1) { *hash = NULL; goto err; } rv = 0; err: free(dhex); return (rv); } int tls_cert_pubkey_hash(X509 *cert, char **hash) { char d[EVP_MAX_MD_SIZE], *dhex = NULL; int dlen, rv = -1; free(*hash); *hash = NULL; if (X509_pubkey_digest(cert, EVP_sha256(), d, &dlen) != 1) goto err; if (tls_hex_string(d, dlen, &dhex, NULL) != 0) goto err; if (asprintf(hash, "SHA256:%s", dhex) == -1) { *hash = NULL; goto err; } rv = 0; err: free(dhex); return (rv); } static int tls_keypair_to_pkey(struct tls *ctx, struct tls_keypair *keypair, EVP_PKEY **pkey) { BIO *bio = NULL; X509 *x509 = NULL; char *mem; size_t len; int ret = -1; *pkey = NULL; if (ctx->config->use_fake_private_key) { mem = keypair->cert_mem; len = keypair->cert_len; } else { mem = keypair->key_mem; len = keypair->key_len; } if (mem == NULL) return (0); if (len > INT_MAX) { tls_set_errorx(ctx, TLS_ERROR_INVALID_ARGUMENT, ctx->config->use_fake_private_key ? "certificate too long" : "key too long"); goto err; } if ((bio = BIO_new_mem_buf(mem, len)) == NULL) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to create buffer"); goto err; } if (ctx->config->use_fake_private_key) { if ((x509 = PEM_read_bio_X509(bio, NULL, tls_password_cb, NULL)) == NULL) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to read X509 certificate"); goto err; } if ((*pkey = X509_get_pubkey(x509)) == NULL) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to retrieve pubkey"); goto err; } } else { if ((*pkey = PEM_read_bio_PrivateKey(bio, NULL, tls_password_cb, NULL)) == NULL) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to read private key"); goto err; } } ret = 0; err: BIO_free(bio); X509_free(x509); return (ret); } static int tls_keypair_setup_pkey(struct tls *ctx, struct tls_keypair *keypair, EVP_PKEY *pkey) { RSA_METHOD *rsa_method; EC_KEY_METHOD *ecdsa_method; RSA *rsa = NULL; EC_KEY *eckey = NULL; int ret = -1; /* Only install the pubkey hash if fake private keys are used. */ if (!ctx->config->skip_private_key_check) return (0); if (keypair->pubkey_hash == NULL) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "public key hash not set"); goto err; } switch (EVP_PKEY_id(pkey)) { case EVP_PKEY_RSA: if ((rsa = EVP_PKEY_get1_RSA(pkey)) == NULL || RSA_set_ex_data(rsa, 0, keypair->pubkey_hash) == 0) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "RSA key setup failure"); goto err; } if (ctx->config->sign_cb != NULL) { rsa_method = tls_signer_rsa_method(); if (rsa_method == NULL || RSA_set_ex_data(rsa, 1, ctx->config) == 0 || RSA_set_method(rsa, rsa_method) == 0) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to setup RSA key"); goto err; } } /* Reset the key to work around caching in OpenSSL 3. */ if (EVP_PKEY_set1_RSA(pkey, rsa) == 0) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to set RSA key"); goto err; } break; case EVP_PKEY_EC: if ((eckey = EVP_PKEY_get1_EC_KEY(pkey)) == NULL || EC_KEY_set_ex_data(eckey, 0, keypair->pubkey_hash) == 0) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "EC key setup failure"); goto err; } if (ctx->config->sign_cb != NULL) { ecdsa_method = tls_signer_ecdsa_method(); if (ecdsa_method == NULL || EC_KEY_set_ex_data(eckey, 1, ctx->config) == 0 || EC_KEY_set_method(eckey, ecdsa_method) == 0) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to setup EC key"); goto err; } } /* Reset the key to work around caching in OpenSSL 3. */ if (EVP_PKEY_set1_EC_KEY(pkey, eckey) == 0) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to set EC key"); goto err; } break; default: tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "incorrect key type"); goto err; } ret = 0; err: RSA_free(rsa); EC_KEY_free(eckey); return (ret); } int tls_configure_ssl_keypair(struct tls *ctx, SSL_CTX *ssl_ctx, struct tls_keypair *keypair, int required) { EVP_PKEY *pkey = NULL; if (!required && keypair->cert_mem == NULL && keypair->key_mem == NULL) return(0); if (keypair->cert_mem != NULL) { if (keypair->cert_len > INT_MAX) { tls_set_errorx(ctx, TLS_ERROR_INVALID_ARGUMENT, "certificate too long"); goto err; } if (SSL_CTX_use_certificate_chain_mem(ssl_ctx, keypair->cert_mem, keypair->cert_len) != 1) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to load certificate"); goto err; } } if (tls_keypair_to_pkey(ctx, keypair, &pkey) == -1) goto err; if (pkey != NULL) { if (tls_keypair_setup_pkey(ctx, keypair, pkey) == -1) goto err; if (SSL_CTX_use_PrivateKey(ssl_ctx, pkey) != 1) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to load private key"); goto err; } EVP_PKEY_free(pkey); pkey = NULL; } if (!ctx->config->skip_private_key_check && SSL_CTX_check_private_key(ssl_ctx) != 1) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "private/public key mismatch"); goto err; } return (0); err: EVP_PKEY_free(pkey); return (-1); } int tls_configure_ssl(struct tls *ctx, SSL_CTX *ssl_ctx) { SSL_CTX_clear_mode(ssl_ctx, SSL_MODE_AUTO_RETRY); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE); SSL_CTX_set_mode(ssl_ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2); SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv3); SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1); SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_1); SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TLSv1_2); SSL_CTX_clear_options(ssl_ctx, SSL_OP_NO_TLSv1_3); if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_2) == 0) SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_2); if ((ctx->config->protocols & TLS_PROTOCOL_TLSv1_3) == 0) SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1_3); if (ctx->config->alpn != NULL) { if (SSL_CTX_set_alpn_protos(ssl_ctx, ctx->config->alpn, ctx->config->alpn_len) != 0) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to set alpn"); goto err; } } if (ctx->config->ciphers != NULL) { if (SSL_CTX_set_cipher_list(ssl_ctx, ctx->config->ciphers) != 1) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to set ciphers"); goto err; } } if (ctx->config->verify_time == 0) { X509_VERIFY_PARAM_set_flags(SSL_CTX_get0_param(ssl_ctx), X509_V_FLAG_NO_CHECK_TIME); } /* Disable any form of session caching by default */ SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_OFF); SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET); return (0); err: return (-1); } static int tls_ssl_cert_verify_cb(X509_STORE_CTX *x509_ctx, void *arg) { struct tls *ctx = arg; int x509_err; if (ctx->config->verify_cert == 0) return (1); if ((X509_verify_cert(x509_ctx)) < 0) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "X509 verify cert failed"); return (0); } x509_err = X509_STORE_CTX_get_error(x509_ctx); if (x509_err == X509_V_OK) return (1); tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "certificate verification failed: %s", X509_verify_cert_error_string(x509_err)); return (0); } int tls_configure_ssl_verify(struct tls *ctx, SSL_CTX *ssl_ctx, int verify) { size_t ca_len = ctx->config->ca_len; char *ca_mem = ctx->config->ca_mem; char *crl_mem = ctx->config->crl_mem; size_t crl_len = ctx->config->crl_len; char *ca_free = NULL; STACK_OF(X509_INFO) *xis = NULL; X509_STORE *store; X509_INFO *xi; BIO *bio = NULL; int rv = -1; int i; SSL_CTX_set_verify(ssl_ctx, verify, NULL); SSL_CTX_set_cert_verify_callback(ssl_ctx, tls_ssl_cert_verify_cb, ctx); if (ctx->config->verify_depth >= 0) SSL_CTX_set_verify_depth(ssl_ctx, ctx->config->verify_depth); if (ctx->config->verify_cert == 0) goto done; /* If no CA has been specified, attempt to load the default. */ if (ctx->config->ca_mem == NULL && ctx->config->ca_path == NULL) { if (tls_config_load_file(&ctx->error, "CA", tls_default_ca_cert_file(), &ca_mem, &ca_len) != 0) goto err; ca_free = ca_mem; } if (ca_mem != NULL) { if (ca_len > INT_MAX) { tls_set_errorx(ctx, TLS_ERROR_INVALID_ARGUMENT, "ca too long"); goto err; } if (SSL_CTX_load_verify_mem(ssl_ctx, ca_mem, ca_len) != 1) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "ssl verify memory setup failure"); goto err; } } else if (SSL_CTX_load_verify_locations(ssl_ctx, NULL, ctx->config->ca_path) != 1) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "ssl verify locations failure"); goto err; } if (crl_mem != NULL) { if (crl_len > INT_MAX) { tls_set_errorx(ctx, TLS_ERROR_INVALID_ARGUMENT, "crl too long"); goto err; } if ((bio = BIO_new_mem_buf(crl_mem, crl_len)) == NULL) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to create buffer"); goto err; } if ((xis = PEM_X509_INFO_read_bio(bio, NULL, tls_password_cb, NULL)) == NULL) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to parse crl"); goto err; } store = SSL_CTX_get_cert_store(ssl_ctx); for (i = 0; i < sk_X509_INFO_num(xis); i++) { xi = sk_X509_INFO_value(xis, i); if (xi->crl == NULL) continue; if (!X509_STORE_add_crl(store, xi->crl)) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to add crl"); goto err; } } X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); } done: rv = 0; err: sk_X509_INFO_pop_free(xis, X509_INFO_free); BIO_free(bio); free(ca_free); return (rv); } void tls_free(struct tls *ctx) { if (ctx == NULL) return; tls_reset(ctx); free(ctx); } void tls_reset(struct tls *ctx) { struct tls_sni_ctx *sni, *nsni; tls_config_free(ctx->config); ctx->config = NULL; SSL_CTX_free(ctx->ssl_ctx); SSL_free(ctx->ssl_conn); X509_free(ctx->ssl_peer_cert); ctx->ssl_conn = NULL; ctx->ssl_ctx = NULL; ctx->ssl_peer_cert = NULL; /* X509 objects in chain are freed with the SSL */ ctx->ssl_peer_chain = NULL; ctx->socket = -1; ctx->state = 0; free(ctx->servername); ctx->servername = NULL; free(ctx->error.msg); ctx->error.msg = NULL; ctx->error.errno_value = -1; tls_conninfo_free(ctx->conninfo); ctx->conninfo = NULL; tls_ocsp_free(ctx->ocsp); ctx->ocsp = NULL; for (sni = ctx->sni_ctx; sni != NULL; sni = nsni) { nsni = sni->next; tls_sni_ctx_free(sni); } ctx->sni_ctx = NULL; ctx->read_cb = NULL; ctx->write_cb = NULL; ctx->cb_arg = NULL; } int tls_ssl_error(struct tls *ctx, SSL *ssl_conn, int ssl_ret, const char *prefix) { const char *errstr = "unknown error"; unsigned long err; int ssl_err; ssl_err = SSL_get_error(ssl_conn, ssl_ret); switch (ssl_err) { case SSL_ERROR_NONE: case SSL_ERROR_ZERO_RETURN: return (0); case SSL_ERROR_WANT_READ: return (TLS_WANT_POLLIN); case SSL_ERROR_WANT_WRITE: return (TLS_WANT_POLLOUT); case SSL_ERROR_SYSCALL: if ((err = ERR_peek_error()) != 0) { errstr = ERR_error_string(err, NULL); } else if (ssl_ret == 0) { if ((ctx->state & TLS_HANDSHAKE_COMPLETE) != 0) { ctx->state |= TLS_EOF_NO_CLOSE_NOTIFY; return (0); } errstr = "unexpected EOF"; } else if (ssl_ret == -1) { errstr = strerror(errno); } tls_set_ssl_errorx(ctx, TLS_ERROR_UNKNOWN, "%s failed: %s", prefix, errstr); return (-1); case SSL_ERROR_SSL: if ((err = ERR_peek_error()) != 0) { errstr = ERR_error_string(err, NULL); } tls_set_ssl_errorx(ctx, TLS_ERROR_UNKNOWN, "%s failed: %s", prefix, errstr); return (-1); case SSL_ERROR_WANT_CONNECT: case SSL_ERROR_WANT_ACCEPT: case SSL_ERROR_WANT_X509_LOOKUP: default: tls_set_ssl_errorx(ctx, TLS_ERROR_UNKNOWN, "%s failed (%d)", prefix, ssl_err); return (-1); } } int tls_handshake(struct tls *ctx) { int rv = -1; tls_error_clear(&ctx->error); if ((ctx->flags & (TLS_CLIENT | TLS_SERVER_CONN)) == 0) { tls_set_errorx(ctx, TLS_ERROR_INVALID_CONTEXT, "invalid operation for context"); goto out; } if ((ctx->state & TLS_HANDSHAKE_COMPLETE) != 0) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "handshake already completed"); goto out; } if ((ctx->flags & TLS_CLIENT) != 0) rv = tls_handshake_client(ctx); else if ((ctx->flags & TLS_SERVER_CONN) != 0) rv = tls_handshake_server(ctx); if (rv == 0) { ctx->ssl_peer_cert = SSL_get_peer_certificate(ctx->ssl_conn); ctx->ssl_peer_chain = SSL_get_peer_cert_chain(ctx->ssl_conn); if (tls_conninfo_populate(ctx) == -1) rv = -1; if (ctx->ocsp == NULL) ctx->ocsp = tls_ocsp_setup_from_peer(ctx); } out: /* Prevent callers from performing incorrect error handling */ errno = 0; return (rv); } ssize_t tls_read(struct tls *ctx, void *buf, size_t buflen) { ssize_t rv = -1; int ssl_ret; tls_error_clear(&ctx->error); if ((ctx->state & TLS_HANDSHAKE_COMPLETE) == 0) { if ((rv = tls_handshake(ctx)) != 0) goto out; } if (buflen > INT_MAX) { tls_set_errorx(ctx, TLS_ERROR_INVALID_ARGUMENT, "buflen too long"); goto out; } ERR_clear_error(); if ((ssl_ret = SSL_read(ctx->ssl_conn, buf, buflen)) > 0) { rv = (ssize_t)ssl_ret; goto out; } rv = (ssize_t)tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "read"); out: /* Prevent callers from performing incorrect error handling */ errno = 0; return (rv); } ssize_t tls_write(struct tls *ctx, const void *buf, size_t buflen) { ssize_t rv = -1; int ssl_ret; tls_error_clear(&ctx->error); if ((ctx->state & TLS_HANDSHAKE_COMPLETE) == 0) { if ((rv = tls_handshake(ctx)) != 0) goto out; } if (buflen > INT_MAX) { tls_set_errorx(ctx, TLS_ERROR_INVALID_ARGUMENT, "buflen too long"); goto out; } ERR_clear_error(); if ((ssl_ret = SSL_write(ctx->ssl_conn, buf, buflen)) > 0) { rv = (ssize_t)ssl_ret; goto out; } rv = (ssize_t)tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "write"); out: /* Prevent callers from performing incorrect error handling */ errno = 0; return (rv); } int tls_close(struct tls *ctx) { int ssl_ret; int rv = 0; tls_error_clear(&ctx->error); if ((ctx->flags & (TLS_CLIENT | TLS_SERVER_CONN)) == 0) { tls_set_errorx(ctx, TLS_ERROR_INVALID_CONTEXT, "invalid operation for context"); rv = -1; goto out; } if (ctx->state & TLS_SSL_NEEDS_SHUTDOWN) { ERR_clear_error(); ssl_ret = SSL_shutdown(ctx->ssl_conn); if (ssl_ret < 0) { rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "shutdown"); if (rv == TLS_WANT_POLLIN || rv == TLS_WANT_POLLOUT) goto out; } ctx->state &= ~TLS_SSL_NEEDS_SHUTDOWN; } if (ctx->socket != -1) { if (shutdown(ctx->socket, SHUT_RDWR) != 0) { if (rv == 0 && errno != ENOTCONN && errno != ECONNRESET) { tls_set_error(ctx, TLS_ERROR_UNKNOWN, "shutdown"); rv = -1; } } if (close(ctx->socket) != 0) { if (rv == 0) { tls_set_error(ctx, TLS_ERROR_UNKNOWN, "close"); rv = -1; } } ctx->socket = -1; } if ((ctx->state & TLS_EOF_NO_CLOSE_NOTIFY) != 0) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "EOF without close notify"); rv = -1; } out: /* Prevent callers from performing incorrect error handling */ errno = 0; return (rv); } opensmtpd-7.9.0p0/openbsd-compat/libtls/tls.h000066400000000000000000000230031525355226100211630ustar00rootroot00000000000000/* $OpenBSD: tls.h,v 1.68 2024/12/10 08:40:30 tb Exp $ */ /* * Copyright (c) 2014 Joel Sing * * 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 HEADER_TLS_H #define HEADER_TLS_H #ifdef __cplusplus extern "C" { #endif #include #include #include #define TLS_API 20200120 /* * Deprecated versions of TLS. Using these effectively selects * the minimum supported version. */ #define TLS_PROTOCOL_TLSv1_0 (1 << 1) #define TLS_PROTOCOL_TLSv1_1 (1 << 2) /* Supported versions of TLS */ #define TLS_PROTOCOL_TLSv1_2 (1 << 3) #define TLS_PROTOCOL_TLSv1_3 (1 << 4) #define TLS_PROTOCOL_TLSv1 \ (TLS_PROTOCOL_TLSv1_2|TLS_PROTOCOL_TLSv1_3) #define TLS_PROTOCOLS_ALL TLS_PROTOCOL_TLSv1 #define TLS_PROTOCOLS_DEFAULT (TLS_PROTOCOL_TLSv1_2|TLS_PROTOCOL_TLSv1_3) #define TLS_WANT_POLLIN -2 #define TLS_WANT_POLLOUT -3 /* RFC 6960 Section 2.3 */ #define TLS_OCSP_RESPONSE_SUCCESSFUL 0 #define TLS_OCSP_RESPONSE_MALFORMED 1 #define TLS_OCSP_RESPONSE_INTERNALERROR 2 #define TLS_OCSP_RESPONSE_TRYLATER 3 #define TLS_OCSP_RESPONSE_SIGREQUIRED 4 #define TLS_OCSP_RESPONSE_UNAUTHORIZED 5 /* RFC 6960 Section 2.2 */ #define TLS_OCSP_CERT_GOOD 0 #define TLS_OCSP_CERT_REVOKED 1 #define TLS_OCSP_CERT_UNKNOWN 2 /* RFC 5280 Section 5.3.1 */ #define TLS_CRL_REASON_UNSPECIFIED 0 #define TLS_CRL_REASON_KEY_COMPROMISE 1 #define TLS_CRL_REASON_CA_COMPROMISE 2 #define TLS_CRL_REASON_AFFILIATION_CHANGED 3 #define TLS_CRL_REASON_SUPERSEDED 4 #define TLS_CRL_REASON_CESSATION_OF_OPERATION 5 #define TLS_CRL_REASON_CERTIFICATE_HOLD 6 #define TLS_CRL_REASON_REMOVE_FROM_CRL 8 #define TLS_CRL_REASON_PRIVILEGE_WITHDRAWN 9 #define TLS_CRL_REASON_AA_COMPROMISE 10 #define TLS_MAX_SESSION_ID_LENGTH 32 #define TLS_TICKET_KEY_SIZE 48 /* Error codes */ #define TLS_ERROR_UNKNOWN 0x0000 #define TLS_ERROR_OUT_OF_MEMORY 0x1000 #define TLS_ERROR_INVALID_CONTEXT 0x2000 #define TLS_ERROR_INVALID_ARGUMENT 0x2001 struct tls; struct tls_config; typedef ssize_t (*tls_read_cb)(struct tls *_ctx, void *_buf, size_t _buflen, void *_cb_arg); typedef ssize_t (*tls_write_cb)(struct tls *_ctx, const void *_buf, size_t _buflen, void *_cb_arg); int tls_init(void); const char *tls_config_error(struct tls_config *_config); const char *tls_error(struct tls *_ctx); int tls_config_error_code(struct tls_config *_config); int tls_error_code(struct tls *_ctx); struct tls_config *tls_config_new(void); void tls_config_free(struct tls_config *_config); const char *tls_default_ca_cert_file(void); int tls_config_add_keypair_file(struct tls_config *_config, const char *_cert_file, const char *_key_file); int tls_config_add_keypair_mem(struct tls_config *_config, const uint8_t *_cert, size_t _cert_len, const uint8_t *_key, size_t _key_len); int tls_config_add_keypair_ocsp_file(struct tls_config *_config, const char *_cert_file, const char *_key_file, const char *_ocsp_staple_file); int tls_config_add_keypair_ocsp_mem(struct tls_config *_config, const uint8_t *_cert, size_t _cert_len, const uint8_t *_key, size_t _key_len, const uint8_t *_staple, size_t _staple_len); int tls_config_set_alpn(struct tls_config *_config, const char *_alpn); int tls_config_set_ca_file(struct tls_config *_config, const char *_ca_file); int tls_config_set_ca_path(struct tls_config *_config, const char *_ca_path); int tls_config_set_ca_mem(struct tls_config *_config, const uint8_t *_ca, size_t _len); int tls_config_set_cert_file(struct tls_config *_config, const char *_cert_file); int tls_config_set_cert_mem(struct tls_config *_config, const uint8_t *_cert, size_t _len); int tls_config_set_ciphers(struct tls_config *_config, const char *_ciphers); int tls_config_set_crl_file(struct tls_config *_config, const char *_crl_file); int tls_config_set_crl_mem(struct tls_config *_config, const uint8_t *_crl, size_t _len); int tls_config_set_dheparams(struct tls_config *_config, const char *_params); int tls_config_set_ecdhecurve(struct tls_config *_config, const char *_curve); int tls_config_set_ecdhecurves(struct tls_config *_config, const char *_curves); int tls_config_set_key_file(struct tls_config *_config, const char *_key_file); int tls_config_set_key_mem(struct tls_config *_config, const uint8_t *_key, size_t _len); int tls_config_set_keypair_file(struct tls_config *_config, const char *_cert_file, const char *_key_file); int tls_config_set_keypair_mem(struct tls_config *_config, const uint8_t *_cert, size_t _cert_len, const uint8_t *_key, size_t _key_len); int tls_config_set_keypair_ocsp_file(struct tls_config *_config, const char *_cert_file, const char *_key_file, const char *_staple_file); int tls_config_set_keypair_ocsp_mem(struct tls_config *_config, const uint8_t *_cert, size_t _cert_len, const uint8_t *_key, size_t _key_len, const uint8_t *_staple, size_t staple_len); int tls_config_set_ocsp_staple_mem(struct tls_config *_config, const uint8_t *_staple, size_t _len); int tls_config_set_ocsp_staple_file(struct tls_config *_config, const char *_staple_file); int tls_config_set_protocols(struct tls_config *_config, uint32_t _protocols); int tls_config_set_session_fd(struct tls_config *_config, int _session_fd); int tls_config_set_verify_depth(struct tls_config *_config, int _verify_depth); void tls_config_prefer_ciphers_client(struct tls_config *_config); void tls_config_prefer_ciphers_server(struct tls_config *_config); void tls_config_insecure_noverifycert(struct tls_config *_config); void tls_config_insecure_noverifyname(struct tls_config *_config); void tls_config_insecure_noverifytime(struct tls_config *_config); void tls_config_verify(struct tls_config *_config); void tls_config_ocsp_require_stapling(struct tls_config *_config); void tls_config_verify_client(struct tls_config *_config); void tls_config_verify_client_optional(struct tls_config *_config); void tls_config_clear_keys(struct tls_config *_config); int tls_config_parse_protocols(uint32_t *_protocols, const char *_protostr); int tls_config_set_session_id(struct tls_config *_config, const unsigned char *_session_id, size_t _len); int tls_config_set_session_lifetime(struct tls_config *_config, int _lifetime); int tls_config_add_ticket_key(struct tls_config *_config, uint32_t _keyrev, unsigned char *_key, size_t _keylen); struct tls *tls_client(void); struct tls *tls_server(void); int tls_configure(struct tls *_ctx, struct tls_config *_config); void tls_reset(struct tls *_ctx); void tls_free(struct tls *_ctx); int tls_accept_fds(struct tls *_ctx, struct tls **_cctx, int _fd_read, int _fd_write); int tls_accept_socket(struct tls *_ctx, struct tls **_cctx, int _socket); int tls_accept_cbs(struct tls *_ctx, struct tls **_cctx, tls_read_cb _read_cb, tls_write_cb _write_cb, void *_cb_arg); int tls_connect(struct tls *_ctx, const char *_host, const char *_port); int tls_connect_fds(struct tls *_ctx, int _fd_read, int _fd_write, const char *_servername); int tls_connect_servername(struct tls *_ctx, const char *_host, const char *_port, const char *_servername); int tls_connect_socket(struct tls *_ctx, int _s, const char *_servername); int tls_connect_cbs(struct tls *_ctx, tls_read_cb _read_cb, tls_write_cb _write_cb, void *_cb_arg, const char *_servername); int tls_handshake(struct tls *_ctx); ssize_t tls_read(struct tls *_ctx, void *_buf, size_t _buflen); ssize_t tls_write(struct tls *_ctx, const void *_buf, size_t _buflen); int tls_close(struct tls *_ctx); int tls_peer_cert_provided(struct tls *_ctx); int tls_peer_cert_contains_name(struct tls *_ctx, const char *_name); const char *tls_peer_cert_common_name(struct tls *_ctx); const char *tls_peer_cert_hash(struct tls *_ctx); const char *tls_peer_cert_issuer(struct tls *_ctx); const char *tls_peer_cert_subject(struct tls *_ctx); time_t tls_peer_cert_notbefore(struct tls *_ctx); time_t tls_peer_cert_notafter(struct tls *_ctx); const uint8_t *tls_peer_cert_chain_pem(struct tls *_ctx, size_t *_len); const char *tls_conn_alpn_selected(struct tls *_ctx); const char *tls_conn_cipher(struct tls *_ctx); int tls_conn_cipher_strength(struct tls *_ctx); const char *tls_conn_servername(struct tls *_ctx); int tls_conn_session_resumed(struct tls *_ctx); const char *tls_conn_version(struct tls *_ctx); uint8_t *tls_load_file(const char *_file, size_t *_len, char *_password); void tls_unload_file(uint8_t *_buf, size_t len); int tls_ocsp_process_response(struct tls *_ctx, const unsigned char *_response, size_t _size); int tls_peer_ocsp_cert_status(struct tls *_ctx); int tls_peer_ocsp_crl_reason(struct tls *_ctx); time_t tls_peer_ocsp_next_update(struct tls *_ctx); int tls_peer_ocsp_response_status(struct tls *_ctx); const char *tls_peer_ocsp_result(struct tls *_ctx); time_t tls_peer_ocsp_revocation_time(struct tls *_ctx); time_t tls_peer_ocsp_this_update(struct tls *_ctx); const char *tls_peer_ocsp_url(struct tls *_ctx); #ifdef __cplusplus } #endif #endif /* HEADER_TLS_H */ opensmtpd-7.9.0p0/openbsd-compat/libtls/tls_bio_cb.c000066400000000000000000000074071525355226100224650ustar00rootroot00000000000000/* $OpenBSD: tls_bio_cb.c,v 1.22 2024/03/26 06:24:52 joshua Exp $ */ /* * Copyright (c) 2016 Tobias Pape * * 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 #include #include #include #include #include #include "tls_internal.h" static int bio_cb_write(BIO *bio, const char *buf, int num); static int bio_cb_read(BIO *bio, char *buf, int size); static int bio_cb_puts(BIO *bio, const char *str); static long bio_cb_ctrl(BIO *bio, int cmd, long num, void *ptr); static BIO_METHOD *bio_cb_method; static void bio_cb_method_init(void) { BIO_METHOD *bio_method; if (bio_cb_method != NULL) return; bio_method = BIO_meth_new(BIO_TYPE_MEM, "libtls_callbacks"); if (bio_method == NULL) return; BIO_meth_set_write(bio_method, bio_cb_write); BIO_meth_set_read(bio_method, bio_cb_read); BIO_meth_set_puts(bio_method, bio_cb_puts); BIO_meth_set_ctrl(bio_method, bio_cb_ctrl); bio_cb_method = bio_method; } static BIO_METHOD * bio_s_cb(void) { if (bio_cb_method != NULL) return (bio_cb_method); bio_cb_method_init(); return (bio_cb_method); } static int bio_cb_puts(BIO *bio, const char *str) { return (bio_cb_write(bio, str, strlen(str))); } static long bio_cb_ctrl(BIO *bio, int cmd, long num, void *ptr) { long ret = 1; switch (cmd) { case BIO_CTRL_GET_CLOSE: ret = (long)BIO_get_shutdown(bio); break; case BIO_CTRL_SET_CLOSE: BIO_set_shutdown(bio, (int)num); break; case BIO_CTRL_DUP: case BIO_CTRL_FLUSH: break; case BIO_CTRL_INFO: case BIO_CTRL_GET: case BIO_CTRL_SET: default: ret = BIO_ctrl(BIO_next(bio), cmd, num, ptr); } return (ret); } static int bio_cb_write(BIO *bio, const char *buf, int num) { struct tls *ctx = BIO_get_data(bio); int rv; BIO_clear_retry_flags(bio); rv = (ctx->write_cb)(ctx, buf, num, ctx->cb_arg); if (rv == TLS_WANT_POLLIN) { BIO_set_retry_read(bio); rv = -1; } else if (rv == TLS_WANT_POLLOUT) { BIO_set_retry_write(bio); rv = -1; } return (rv); } static int bio_cb_read(BIO *bio, char *buf, int size) { struct tls *ctx = BIO_get_data(bio); int rv; BIO_clear_retry_flags(bio); rv = (ctx->read_cb)(ctx, buf, size, ctx->cb_arg); if (rv == TLS_WANT_POLLIN) { BIO_set_retry_read(bio); rv = -1; } else if (rv == TLS_WANT_POLLOUT) { BIO_set_retry_write(bio); rv = -1; } return (rv); } int tls_set_cbs(struct tls *ctx, tls_read_cb read_cb, tls_write_cb write_cb, void *cb_arg) { const BIO_METHOD *bio_cb; BIO *bio; int rv = -1; if (read_cb == NULL || write_cb == NULL) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "no callbacks provided"); goto err; } ctx->read_cb = read_cb; ctx->write_cb = write_cb; ctx->cb_arg = cb_arg; if ((bio_cb = bio_s_cb()) == NULL) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to create callback method"); goto err; } if ((bio = BIO_new(bio_cb)) == NULL) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to create callback i/o"); goto err; } BIO_set_data(bio, ctx); BIO_set_init(bio, 1); SSL_set_bio(ctx->ssl_conn, bio, bio); rv = 0; err: return (rv); } opensmtpd-7.9.0p0/openbsd-compat/libtls/tls_client.c000066400000000000000000000266471525355226100225350ustar00rootroot00000000000000/* $OpenBSD: tls_client.c,v 1.52 2026/04/16 07:28:00 tb Exp $ */ /* * Copyright (c) 2014 Joel Sing * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include "tls_internal.h" struct tls * tls_client(void) { struct tls *ctx; if (tls_init() == -1) return (NULL); if ((ctx = tls_new()) == NULL) return (NULL); ctx->flags |= TLS_CLIENT; return (ctx); } int tls_connect(struct tls *ctx, const char *host, const char *port) { return tls_connect_servername(ctx, host, port, NULL); } int tls_connect_servername(struct tls *ctx, const char *host, const char *port, const char *servername) { struct addrinfo hints, *res, *res0; const char *h = NULL, *p = NULL; char *hs = NULL, *ps = NULL; int rv = -1, s = -1, ret; if ((ctx->flags & TLS_CLIENT) == 0) { tls_set_errorx(ctx, TLS_ERROR_INVALID_CONTEXT, "not a client context"); goto err; } if (host == NULL) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "host not specified"); goto err; } /* If port is NULL, try to extract a port from the specified host. */ if (port == NULL) { ret = tls_host_port(host, &hs, &ps); if (ret == -1) { tls_set_errorx(ctx, TLS_ERROR_OUT_OF_MEMORY, "out of memory"); goto err; } if (ret != 0) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "no port provided"); goto err; } } h = (hs != NULL) ? hs : host; p = (ps != NULL) ? ps : port; /* * First check if the host is specified as a numeric IP address, * either IPv4 or IPv6, before trying to resolve the host. * The AI_ADDRCONFIG resolver option will not return IPv4 or IPv6 * records if it is not configured on an interface; not considering * loopback addresses. Checking the numeric addresses first makes * sure that connection attempts to numeric addresses and especially * 127.0.0.1 or ::1 loopback addresses are always possible. */ memset(&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; /* try as an IPv4 literal */ hints.ai_family = AF_INET; hints.ai_flags = AI_NUMERICHOST; if (getaddrinfo(h, p, &hints, &res0) != 0) { /* try again as an IPv6 literal */ hints.ai_family = AF_INET6; if (getaddrinfo(h, p, &hints, &res0) != 0) { /* last try, with name resolution and save the error */ hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_ADDRCONFIG; if ((s = getaddrinfo(h, p, &hints, &res0)) != 0) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "%s", gai_strerror(s)); goto err; } } } /* It was resolved somehow; now try connecting to what we got */ s = -1; for (res = res0; res; res = res->ai_next) { s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (s == -1) { tls_set_error(ctx, TLS_ERROR_UNKNOWN, "socket"); continue; } if (connect(s, res->ai_addr, res->ai_addrlen) == -1) { tls_set_error(ctx, TLS_ERROR_UNKNOWN, "connect"); close(s); s = -1; continue; } break; /* Connected. */ } freeaddrinfo(res0); if (s == -1) goto err; if (servername == NULL) servername = h; if (tls_connect_socket(ctx, s, servername) != 0) { close(s); goto err; } ctx->socket = s; rv = 0; err: free(hs); free(ps); return (rv); } static int tls_client_read_session(struct tls *ctx) { int sfd = ctx->config->session_fd; uint8_t *session = NULL; size_t session_len = 0; SSL_SESSION *ss = NULL; BIO *bio = NULL; struct stat sb; ssize_t n; int rv = -1; if (fstat(sfd, &sb) == -1) { tls_set_error(ctx, TLS_ERROR_UNKNOWN, "failed to stat session file"); goto err; } if (sb.st_size < 0 || sb.st_size > INT_MAX) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "invalid session file size"); goto err; } session_len = (size_t)sb.st_size; /* A zero size file means that we do not yet have a valid session. */ if (session_len == 0) goto done; if ((session = malloc(session_len)) == NULL) goto err; n = pread(sfd, session, session_len, 0); if (n < 0 || (size_t)n != session_len) { tls_set_error(ctx, TLS_ERROR_UNKNOWN, "failed to read session file"); goto err; } if ((bio = BIO_new_mem_buf(session, session_len)) == NULL) goto err; if ((ss = PEM_read_bio_SSL_SESSION(bio, NULL, tls_password_cb, NULL)) == NULL) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to parse session"); goto err; } if (SSL_set_session(ctx->ssl_conn, ss) != 1) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to set session"); goto err; } done: rv = 0; err: freezero(session, session_len); SSL_SESSION_free(ss); BIO_free(bio); return rv; } static int tls_client_write_session(struct tls *ctx) { int sfd = ctx->config->session_fd; SSL_SESSION *ss = NULL; BIO *bio = NULL; long data_len; char *data; off_t offset; size_t len; ssize_t n; int rv = -1; if ((ss = SSL_get1_session(ctx->ssl_conn)) == NULL) { if (ftruncate(sfd, 0) == -1) { tls_set_error(ctx, TLS_ERROR_UNKNOWN, "failed to truncate session file"); goto err; } goto done; } if ((bio = BIO_new(BIO_s_mem())) == NULL) goto err; if (PEM_write_bio_SSL_SESSION(bio, ss) == 0) goto err; if ((data_len = BIO_get_mem_data(bio, &data)) <= 0) goto err; len = (size_t)data_len; offset = 0; if (ftruncate(sfd, len) == -1) { tls_set_error(ctx, TLS_ERROR_UNKNOWN, "failed to truncate session file"); goto err; } while (len > 0) { if ((n = pwrite(sfd, data + offset, len, offset)) == -1) { tls_set_error(ctx, TLS_ERROR_UNKNOWN, "failed to write session file"); goto err; } offset += n; len -= n; } done: rv = 0; err: SSL_SESSION_free(ss); BIO_free_all(bio); return (rv); } static int tls_connect_common(struct tls *ctx, const char *servername) { union tls_addr addrbuf; size_t servername_len; int rv = -1; if ((ctx->flags & TLS_CLIENT) == 0) { tls_set_errorx(ctx, TLS_ERROR_INVALID_CONTEXT, "not a client context"); goto err; } if (servername != NULL) { if ((ctx->servername = strdup(servername)) == NULL) { tls_set_errorx(ctx, TLS_ERROR_OUT_OF_MEMORY, "out of memory"); goto err; } /* * If there's a trailing dot, remove it. While an FQDN includes * the terminating dot representing the zero-length label of * the root (RFC 8499, section 2), the SNI explicitly does not * include it (RFC 6066, section 3). */ servername_len = strlen(ctx->servername); if (servername_len > 0 && ctx->servername[servername_len - 1] == '.') ctx->servername[servername_len - 1] = '\0'; } if ((ctx->ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "ssl context failure"); goto err; } if (tls_configure_ssl(ctx, ctx->ssl_ctx) != 0) goto err; if (tls_configure_ssl_keypair(ctx, ctx->ssl_ctx, ctx->config->keypair, 0) != 0) goto err; if (ctx->config->verify_name) { if (ctx->servername == NULL) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "server name not specified"); goto err; } } if (tls_configure_ssl_verify(ctx, ctx->ssl_ctx, SSL_VERIFY_PEER) == -1) goto err; if (ctx->config->ecdhecurves != NULL) { if (SSL_CTX_set1_groups(ctx->ssl_ctx, ctx->config->ecdhecurves, ctx->config->ecdhecurves_len) != 1) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to set ecdhe curves"); goto err; } } if (SSL_CTX_set_tlsext_status_cb(ctx->ssl_ctx, tls_ocsp_verify_cb) != 1) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "ssl OCSP verification setup failure"); goto err; } if ((ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "ssl connection failure"); goto err; } if (SSL_set_app_data(ctx->ssl_conn, ctx) != 1) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "ssl application data failure"); goto err; } if (ctx->config->session_fd != -1) { SSL_clear_options(ctx->ssl_conn, SSL_OP_NO_TICKET); if (tls_client_read_session(ctx) == -1) goto err; } if (SSL_set_tlsext_status_type(ctx->ssl_conn, TLSEXT_STATUSTYPE_ocsp) != 1) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "ssl OCSP extension setup failure"); goto err; } /* * RFC 6066 (SNI): Literal IPv4 and IPv6 addresses are not * permitted in "HostName". */ if (ctx->servername != NULL && inet_pton(AF_INET, ctx->servername, &addrbuf) != 1 && inet_pton(AF_INET6, ctx->servername, &addrbuf) != 1) { if (SSL_set_tlsext_host_name(ctx->ssl_conn, ctx->servername) == 0) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "server name indication failure"); goto err; } } ctx->state |= TLS_CONNECTED; rv = 0; err: return (rv); } int tls_connect_socket(struct tls *ctx, int s, const char *servername) { return tls_connect_fds(ctx, s, s, servername); } int tls_connect_fds(struct tls *ctx, int fd_read, int fd_write, const char *servername) { int rv = -1; if (fd_read < 0 || fd_write < 0) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "invalid file descriptors"); goto err; } if (tls_connect_common(ctx, servername) != 0) goto err; if (SSL_set_rfd(ctx->ssl_conn, fd_read) != 1 || SSL_set_wfd(ctx->ssl_conn, fd_write) != 1) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "ssl file descriptor failure"); goto err; } rv = 0; err: return (rv); } int tls_connect_cbs(struct tls *ctx, tls_read_cb read_cb, tls_write_cb write_cb, void *cb_arg, const char *servername) { int rv = -1; if (tls_connect_common(ctx, servername) != 0) goto err; if (tls_set_cbs(ctx, read_cb, write_cb, cb_arg) != 0) goto err; rv = 0; err: return (rv); } int tls_handshake_client(struct tls *ctx) { X509 *cert = NULL; int match, ssl_ret; int rv = -1; if ((ctx->flags & TLS_CLIENT) == 0) { tls_set_errorx(ctx, TLS_ERROR_INVALID_CONTEXT, "not a client context"); goto err; } if ((ctx->state & TLS_CONNECTED) == 0) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "context not connected"); goto err; } ctx->state |= TLS_SSL_NEEDS_SHUTDOWN; ERR_clear_error(); if ((ssl_ret = SSL_connect(ctx->ssl_conn)) != 1) { rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "handshake"); goto err; } if (ctx->config->verify_name) { cert = SSL_get_peer_certificate(ctx->ssl_conn); if (cert == NULL) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "no server certificate"); goto err; } if (tls_check_name(ctx, cert, ctx->servername, &match) == -1) goto err; if (!match) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "name `%s' not present in server certificate", ctx->servername); goto err; } } ctx->state |= TLS_HANDSHAKE_COMPLETE; if (ctx->config->session_fd != -1) { if (tls_client_write_session(ctx) == -1) goto err; } rv = 0; err: X509_free(cert); return (rv); } opensmtpd-7.9.0p0/openbsd-compat/libtls/tls_config.c000066400000000000000000000526321525355226100225150ustar00rootroot00000000000000/* $OpenBSD: tls_config.c,v 1.73 2026/04/16 07:33:11 tb Exp $ */ /* * Copyright (c) 2014 Joel Sing * * 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 #include #include #include #include #include #include #include #include "tls_internal.h" const char * tls_default_ca_cert_file(void) { #ifdef OPENSMTPD_CA_FILE return OPENSMTPD_CA_FILE; #else return X509_get_default_cert_file(); #endif } int tls_config_load_file(struct tls_error *error, const char *filetype, const char *filename, char **buf, size_t *len) { struct stat st; int fd = -1; ssize_t n; free(*buf); *buf = NULL; *len = 0; if ((fd = open(filename, O_RDONLY|O_CLOEXEC)) == -1) { tls_error_set(error, TLS_ERROR_UNKNOWN, "failed to open %s file '%s'", filetype, filename); goto err; } if (fstat(fd, &st) != 0) { tls_error_set(error, TLS_ERROR_UNKNOWN, "failed to stat %s file '%s'", filetype, filename); goto err; } if (st.st_size < 0) goto err; *len = (size_t)st.st_size; if ((*buf = malloc(*len)) == NULL) { tls_error_setx(error, TLS_ERROR_OUT_OF_MEMORY, "failed to allocate buffer for %s file", filetype); goto err; } n = read(fd, *buf, *len); if (n < 0 || (size_t)n != *len) { tls_error_set(error, TLS_ERROR_UNKNOWN, "failed to read %s file '%s'", filetype, filename); goto err; } close(fd); return 0; err: if (fd != -1) close(fd); freezero(*buf, *len); *buf = NULL; *len = 0; return -1; } struct tls_config * tls_config_new_internal(void) { struct tls_config *config; unsigned char sid[TLS_MAX_SESSION_ID_LENGTH]; if ((config = calloc(1, sizeof(*config))) == NULL) return (NULL); config->refcount = 1; config->session_fd = -1; if ((config->keypair = tls_keypair_new()) == NULL) goto err; /* * Default configuration. */ if (tls_config_set_dheparams(config, "none") != 0) goto err; if (tls_config_set_ecdhecurves(config, "default") != 0) goto err; if (tls_config_set_ciphers(config, "secure") != 0) goto err; if (tls_config_set_protocols(config, TLS_PROTOCOLS_DEFAULT) != 0) goto err; if (tls_config_set_verify_depth(config, 6) != 0) goto err; /* * Set session ID context to a random value. For the simple case * of a single process server this is good enough. For multiprocess * servers the session ID needs to be set by the caller. */ arc4random_buf(sid, sizeof(sid)); if (tls_config_set_session_id(config, sid, sizeof(sid)) != 0) goto err; config->ticket_keyrev = arc4random(); config->ticket_autorekey = 1; tls_config_prefer_ciphers_server(config); tls_config_verify(config); return (config); err: tls_config_free(config); return (NULL); } struct tls_config * tls_config_new(void) { if (tls_init() == -1) return (NULL); return tls_config_new_internal(); } void tls_config_free(struct tls_config *config) { struct tls_keypair *kp, *nkp; int refcount; if (config == NULL) return; refcount = --config->refcount; if (refcount > 0) return; for (kp = config->keypair; kp != NULL; kp = nkp) { nkp = kp->next; tls_keypair_free(kp); } free(config->error.msg); free(config->alpn); free((char *)config->ca_mem); free((char *)config->ca_path); free((char *)config->ciphers); free((char *)config->crl_mem); free(config->ecdhecurves); free(config); } static void tls_config_keypair_add(struct tls_config *config, struct tls_keypair *keypair) { struct tls_keypair *kp; kp = config->keypair; while (kp->next != NULL) kp = kp->next; kp->next = keypair; } const char * tls_config_error(struct tls_config *config) { return config->error.msg; } int tls_config_error_code(struct tls_config *config) { return config->error.code; } void tls_config_clear_keys(struct tls_config *config) { struct tls_keypair *kp; for (kp = config->keypair; kp != NULL; kp = kp->next) tls_keypair_clear_key(kp); } int tls_config_parse_protocols(uint32_t *protocols, const char *protostr) { uint32_t proto, protos = 0; char *s, *p, *q; int negate; if (protostr == NULL) { *protocols = TLS_PROTOCOLS_DEFAULT; return (0); } if ((s = strdup(protostr)) == NULL) return (-1); q = s; while ((p = strsep(&q, ",:")) != NULL) { while (*p == ' ' || *p == '\t') p++; negate = 0; if (*p == '!') { negate = 1; p++; } if (negate && protos == 0) protos = TLS_PROTOCOLS_ALL; proto = 0; if (strcasecmp(p, "all") == 0 || strcasecmp(p, "legacy") == 0) proto = TLS_PROTOCOLS_ALL; else if (strcasecmp(p, "default") == 0 || strcasecmp(p, "secure") == 0) proto = TLS_PROTOCOLS_DEFAULT; if (strcasecmp(p, "tlsv1") == 0) proto = TLS_PROTOCOL_TLSv1; else if (strcasecmp(p, "tlsv1.0") == 0) proto = TLS_PROTOCOL_TLSv1_0; else if (strcasecmp(p, "tlsv1.1") == 0) proto = TLS_PROTOCOL_TLSv1_1; else if (strcasecmp(p, "tlsv1.2") == 0) proto = TLS_PROTOCOL_TLSv1_2; else if (strcasecmp(p, "tlsv1.3") == 0) proto = TLS_PROTOCOL_TLSv1_3; if (proto == 0) { free(s); return (-1); } if (negate) protos &= ~proto; else protos |= proto; } *protocols = protos; free(s); return (0); } static int tls_config_parse_alpn(struct tls_config *config, const char *alpn, char **alpn_data, size_t *alpn_len) { size_t buf_len, i, len; char *buf = NULL; char *s = NULL; char *p, *q; free(*alpn_data); *alpn_data = NULL; *alpn_len = 0; if ((buf_len = strlen(alpn) + 1) > 65535) { tls_config_set_errorx(config, TLS_ERROR_INVALID_ARGUMENT, "alpn too large"); goto err; } if ((buf = malloc(buf_len)) == NULL) { tls_config_set_errorx(config, TLS_ERROR_OUT_OF_MEMORY, "out of memory"); goto err; } if ((s = strdup(alpn)) == NULL) { tls_config_set_errorx(config, TLS_ERROR_OUT_OF_MEMORY, "out of memory"); goto err; } i = 0; q = s; while ((p = strsep(&q, ",")) != NULL) { if ((len = strlen(p)) == 0) { tls_config_set_errorx(config, TLS_ERROR_INVALID_ARGUMENT, "alpn protocol with zero length"); goto err; } if (len > 255) { tls_config_set_errorx(config, TLS_ERROR_INVALID_ARGUMENT, "alpn protocol too long"); goto err; } buf[i++] = len & 0xff; memcpy(&buf[i], p, len); i += len; } free(s); *alpn_data = buf; *alpn_len = buf_len; return (0); err: free(buf); free(s); return (-1); } int tls_config_set_alpn(struct tls_config *config, const char *alpn) { return tls_config_parse_alpn(config, alpn, &config->alpn, &config->alpn_len); } static int tls_config_add_keypair_file_internal(struct tls_config *config, const char *cert_file, const char *key_file, const char *ocsp_file) { struct tls_keypair *keypair; if ((keypair = tls_keypair_new()) == NULL) return (-1); if (tls_keypair_set_cert_file(keypair, &config->error, cert_file) != 0) goto err; if (key_file != NULL && tls_keypair_set_key_file(keypair, &config->error, key_file) != 0) goto err; if (ocsp_file != NULL && tls_keypair_set_ocsp_staple_file(keypair, &config->error, ocsp_file) != 0) goto err; tls_config_keypair_add(config, keypair); return (0); err: tls_keypair_free(keypair); return (-1); } static int tls_config_add_keypair_mem_internal(struct tls_config *config, const uint8_t *cert, size_t cert_len, const uint8_t *key, size_t key_len, const uint8_t *staple, size_t staple_len) { struct tls_keypair *keypair; if ((keypair = tls_keypair_new()) == NULL) return (-1); if (tls_keypair_set_cert_mem(keypair, &config->error, cert, cert_len) != 0) goto err; if (key != NULL && tls_keypair_set_key_mem(keypair, &config->error, key, key_len) != 0) goto err; if (staple != NULL && tls_keypair_set_ocsp_staple_mem(keypair, &config->error, staple, staple_len) != 0) goto err; tls_config_keypair_add(config, keypair); return (0); err: tls_keypair_free(keypair); return (-1); } int tls_config_add_keypair_mem(struct tls_config *config, const uint8_t *cert, size_t cert_len, const uint8_t *key, size_t key_len) { return tls_config_add_keypair_mem_internal(config, cert, cert_len, key, key_len, NULL, 0); } int tls_config_add_keypair_file(struct tls_config *config, const char *cert_file, const char *key_file) { return tls_config_add_keypair_file_internal(config, cert_file, key_file, NULL); } int tls_config_add_keypair_ocsp_mem(struct tls_config *config, const uint8_t *cert, size_t cert_len, const uint8_t *key, size_t key_len, const uint8_t *staple, size_t staple_len) { return tls_config_add_keypair_mem_internal(config, cert, cert_len, key, key_len, staple, staple_len); } int tls_config_add_keypair_ocsp_file(struct tls_config *config, const char *cert_file, const char *key_file, const char *ocsp_file) { return tls_config_add_keypair_file_internal(config, cert_file, key_file, ocsp_file); } int tls_config_set_ca_file(struct tls_config *config, const char *ca_file) { return tls_config_load_file(&config->error, "CA", ca_file, &config->ca_mem, &config->ca_len); } int tls_config_set_ca_path(struct tls_config *config, const char *ca_path) { return tls_set_string(&config->ca_path, ca_path); } int tls_config_set_ca_mem(struct tls_config *config, const uint8_t *ca, size_t len) { return tls_set_mem(&config->ca_mem, &config->ca_len, ca, len); } int tls_config_set_cert_file(struct tls_config *config, const char *cert_file) { return tls_keypair_set_cert_file(config->keypair, &config->error, cert_file); } int tls_config_set_cert_mem(struct tls_config *config, const uint8_t *cert, size_t len) { return tls_keypair_set_cert_mem(config->keypair, &config->error, cert, len); } int tls_config_set_ciphers(struct tls_config *config, const char *ciphers) { SSL_CTX *ssl_ctx = NULL; if (ciphers == NULL || strcasecmp(ciphers, "default") == 0 || strcasecmp(ciphers, "secure") == 0) ciphers = TLS_CIPHERS_DEFAULT; else if (strcasecmp(ciphers, "compat") == 0) ciphers = TLS_CIPHERS_COMPAT; else if (strcasecmp(ciphers, "legacy") == 0) ciphers = TLS_CIPHERS_LEGACY; else if (strcasecmp(ciphers, "all") == 0 || strcasecmp(ciphers, "insecure") == 0) ciphers = TLS_CIPHERS_ALL; if ((ssl_ctx = SSL_CTX_new(SSLv23_method())) == NULL) { tls_config_set_errorx(config, TLS_ERROR_OUT_OF_MEMORY, "out of memory"); goto err; } if (SSL_CTX_set_cipher_list(ssl_ctx, ciphers) != 1) { tls_config_set_errorx(config, TLS_ERROR_UNKNOWN, "no ciphers for '%s'", ciphers); goto err; } SSL_CTX_free(ssl_ctx); return tls_set_string(&config->ciphers, ciphers); err: SSL_CTX_free(ssl_ctx); return -1; } int tls_config_set_crl_file(struct tls_config *config, const char *crl_file) { return tls_config_load_file(&config->error, "CRL", crl_file, &config->crl_mem, &config->crl_len); } int tls_config_set_crl_mem(struct tls_config *config, const uint8_t *crl, size_t len) { return tls_set_mem(&config->crl_mem, &config->crl_len, crl, len); } int tls_config_set_dheparams(struct tls_config *config, const char *params) { int keylen; if (params == NULL || strcasecmp(params, "none") == 0) keylen = 0; else if (strcasecmp(params, "auto") == 0) keylen = -1; else if (strcasecmp(params, "legacy") == 0) keylen = 1024; else { tls_config_set_errorx(config, TLS_ERROR_UNKNOWN, "invalid dhe param '%s'", params); return (-1); } config->dheparams = keylen; return (0); } int tls_config_set_ecdhecurve(struct tls_config *config, const char *curve) { if (curve == NULL || strcasecmp(curve, "none") == 0 || strcasecmp(curve, "auto") == 0) { curve = TLS_ECDHE_CURVES; } else if (strchr(curve, ',') != NULL || strchr(curve, ':') != NULL) { tls_config_set_errorx(config, TLS_ERROR_UNKNOWN, "invalid ecdhe curve '%s'", curve); return (-1); } return tls_config_set_ecdhecurves(config, curve); } int tls_config_set_ecdhecurves(struct tls_config *config, const char *curves) { int *curves_list = NULL, *curves_new; size_t curves_num = 0; char *cs = NULL; char *p, *q; int rv = -1; int is_default = 0; int nid; free(config->ecdhecurves); config->ecdhecurves = NULL; config->ecdhecurves_len = 0; if (curves == NULL || strcasecmp(curves, "default") == 0) { curves = TLS_ECDHE_CURVES; is_default = 1; } if ((cs = strdup(curves)) == NULL) { tls_config_set_errorx(config, TLS_ERROR_OUT_OF_MEMORY, "out of memory"); goto err; } q = cs; while ((p = strsep(&q, ",:")) != NULL) { while (*p == ' ' || *p == '\t') p++; nid = OBJ_sn2nid(p); if (nid == NID_undef) nid = OBJ_ln2nid(p); if (nid == NID_undef) nid = EC_curve_nist2nid(p); if (nid == NID_undef) { /* * The default list includes X25519MLKEM768 which * OpenSSL gained only in 3.5. to not fail with * out-of-the-box, skip herrors in the default * list, but still yield the proper error on * user-defined stuff. */ if (is_default) continue; tls_config_set_errorx(config, TLS_ERROR_UNKNOWN, "invalid ecdhe curve '%s'", p); goto err; } if ((curves_new = reallocarray(curves_list, curves_num + 1, sizeof(int))) == NULL) { tls_config_set_errorx(config, TLS_ERROR_OUT_OF_MEMORY, "out of memory"); goto err; } curves_list = curves_new; curves_list[curves_num] = nid; curves_num++; } config->ecdhecurves = curves_list; config->ecdhecurves_len = curves_num; curves_list = NULL; rv = 0; err: free(cs); free(curves_list); return (rv); } int tls_config_set_key_file(struct tls_config *config, const char *key_file) { return tls_keypair_set_key_file(config->keypair, &config->error, key_file); } int tls_config_set_key_mem(struct tls_config *config, const uint8_t *key, size_t len) { return tls_keypair_set_key_mem(config->keypair, &config->error, key, len); } static int tls_config_set_keypair_file_internal(struct tls_config *config, const char *cert_file, const char *key_file, const char *ocsp_file) { if (tls_config_set_cert_file(config, cert_file) != 0) return (-1); if (tls_config_set_key_file(config, key_file) != 0) return (-1); if (ocsp_file != NULL && tls_config_set_ocsp_staple_file(config, ocsp_file) != 0) return (-1); return (0); } static int tls_config_set_keypair_mem_internal(struct tls_config *config, const uint8_t *cert, size_t cert_len, const uint8_t *key, size_t key_len, const uint8_t *staple, size_t staple_len) { if (tls_config_set_cert_mem(config, cert, cert_len) != 0) return (-1); if (tls_config_set_key_mem(config, key, key_len) != 0) return (-1); if ((staple != NULL) && (tls_config_set_ocsp_staple_mem(config, staple, staple_len) != 0)) return (-1); return (0); } int tls_config_set_keypair_file(struct tls_config *config, const char *cert_file, const char *key_file) { return tls_config_set_keypair_file_internal(config, cert_file, key_file, NULL); } int tls_config_set_keypair_mem(struct tls_config *config, const uint8_t *cert, size_t cert_len, const uint8_t *key, size_t key_len) { return tls_config_set_keypair_mem_internal(config, cert, cert_len, key, key_len, NULL, 0); } int tls_config_set_keypair_ocsp_file(struct tls_config *config, const char *cert_file, const char *key_file, const char *ocsp_file) { return tls_config_set_keypair_file_internal(config, cert_file, key_file, ocsp_file); } int tls_config_set_keypair_ocsp_mem(struct tls_config *config, const uint8_t *cert, size_t cert_len, const uint8_t *key, size_t key_len, const uint8_t *staple, size_t staple_len) { return tls_config_set_keypair_mem_internal(config, cert, cert_len, key, key_len, staple, staple_len); } int tls_config_set_protocols(struct tls_config *config, uint32_t protocols) { config->protocols = protocols; return (0); } int tls_config_set_session_fd(struct tls_config *config, int session_fd) { struct stat sb; mode_t mugo; if (session_fd == -1) { config->session_fd = session_fd; return (0); } if (fstat(session_fd, &sb) == -1) { tls_config_set_error(config, TLS_ERROR_UNKNOWN, "failed to stat session file"); return (-1); } if (!S_ISREG(sb.st_mode)) { tls_config_set_errorx(config, TLS_ERROR_UNKNOWN, "session file is not a regular file"); return (-1); } if (sb.st_uid != getuid()) { tls_config_set_errorx(config, TLS_ERROR_UNKNOWN, "session file has incorrect owner (uid %u != %u)", sb.st_uid, getuid()); return (-1); } mugo = sb.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO); if (mugo != (S_IRUSR|S_IWUSR)) { tls_config_set_errorx(config, TLS_ERROR_UNKNOWN, "session file has incorrect permissions (%o != 600)", mugo); return (-1); } config->session_fd = session_fd; return (0); } int tls_config_set_sign_cb(struct tls_config *config, tls_sign_cb cb, void *cb_arg) { config->use_fake_private_key = 1; config->skip_private_key_check = 1; config->sign_cb = cb; config->sign_cb_arg = cb_arg; return (0); } int tls_config_set_verify_depth(struct tls_config *config, int verify_depth) { config->verify_depth = verify_depth; return (0); } void tls_config_prefer_ciphers_client(struct tls_config *config) { config->ciphers_server = 0; } void tls_config_prefer_ciphers_server(struct tls_config *config) { config->ciphers_server = 1; } void tls_config_insecure_noverifycert(struct tls_config *config) { config->verify_cert = 0; } void tls_config_insecure_noverifyname(struct tls_config *config) { config->verify_name = 0; } void tls_config_insecure_noverifytime(struct tls_config *config) { config->verify_time = 0; } void tls_config_verify(struct tls_config *config) { config->verify_cert = 1; config->verify_name = 1; config->verify_time = 1; } void tls_config_ocsp_require_stapling(struct tls_config *config) { config->ocsp_require_stapling = 1; } void tls_config_verify_client(struct tls_config *config) { config->verify_client = 1; } void tls_config_verify_client_optional(struct tls_config *config) { config->verify_client = 2; } void tls_config_skip_private_key_check(struct tls_config *config) { config->skip_private_key_check = 1; } void tls_config_use_fake_private_key(struct tls_config *config) { config->use_fake_private_key = 1; config->skip_private_key_check = 1; } int tls_config_set_ocsp_staple_file(struct tls_config *config, const char *staple_file) { return tls_keypair_set_ocsp_staple_file(config->keypair, &config->error, staple_file); } int tls_config_set_ocsp_staple_mem(struct tls_config *config, const uint8_t *staple, size_t len) { return tls_keypair_set_ocsp_staple_mem(config->keypair, &config->error, staple, len); } int tls_config_set_session_id(struct tls_config *config, const unsigned char *session_id, size_t len) { if (len > TLS_MAX_SESSION_ID_LENGTH) { tls_config_set_errorx(config, TLS_ERROR_INVALID_ARGUMENT, "session ID too large"); return (-1); } memset(config->session_id, 0, sizeof(config->session_id)); memcpy(config->session_id, session_id, len); return (0); } int tls_config_set_session_lifetime(struct tls_config *config, int lifetime) { if (lifetime > TLS_MAX_SESSION_TIMEOUT) { tls_config_set_errorx(config, TLS_ERROR_INVALID_ARGUMENT, "session lifetime too large"); return (-1); } if (lifetime != 0 && lifetime < TLS_MIN_SESSION_TIMEOUT) { tls_config_set_errorx(config, TLS_ERROR_INVALID_ARGUMENT, "session lifetime too small"); return (-1); } config->session_lifetime = lifetime; return (0); } int tls_config_add_ticket_key(struct tls_config *config, uint32_t keyrev, unsigned char *key, size_t keylen) { struct tls_ticket_key newkey; int i; if (TLS_TICKET_KEY_SIZE != keylen || sizeof(newkey.aes_key) + sizeof(newkey.hmac_key) > keylen) { tls_config_set_errorx(config, TLS_ERROR_UNKNOWN, "wrong amount of ticket key data"); return (-1); } keyrev = htonl(keyrev); memset(&newkey, 0, sizeof(newkey)); memcpy(newkey.key_name, &keyrev, sizeof(keyrev)); memcpy(newkey.aes_key, key, sizeof(newkey.aes_key)); memcpy(newkey.hmac_key, key + sizeof(newkey.aes_key), sizeof(newkey.hmac_key)); newkey.time = time(NULL); for (i = 0; i < TLS_NUM_TICKETS; i++) { struct tls_ticket_key *tk = &config->ticket_keys[i]; if (memcmp(newkey.key_name, tk->key_name, sizeof(tk->key_name)) != 0) continue; /* allow re-entry of most recent key */ if (i == 0 && memcmp(newkey.aes_key, tk->aes_key, sizeof(tk->aes_key)) == 0 && memcmp(newkey.hmac_key, tk->hmac_key, sizeof(tk->hmac_key)) == 0) return (0); tls_config_set_errorx(config, TLS_ERROR_UNKNOWN, "ticket key already present"); return (-1); } memmove(&config->ticket_keys[1], &config->ticket_keys[0], sizeof(config->ticket_keys) - sizeof(config->ticket_keys[0])); config->ticket_keys[0] = newkey; config->ticket_autorekey = 0; return (0); } int tls_config_ticket_autorekey(struct tls_config *config) { unsigned char key[TLS_TICKET_KEY_SIZE]; int rv; arc4random_buf(key, sizeof(key)); rv = tls_config_add_ticket_key(config, config->ticket_keyrev++, key, sizeof(key)); config->ticket_autorekey = 1; return (rv); } opensmtpd-7.9.0p0/openbsd-compat/libtls/tls_conninfo.c000066400000000000000000000176301525355226100230600ustar00rootroot00000000000000/* $OpenBSD: tls_conninfo.c,v 1.29 2026/03/28 11:33:33 tb Exp $ */ /* * Copyright (c) 2015 Joel Sing * Copyright (c) 2015 Bob Beck * * 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 #include #include #include #include #include "tls_internal.h" static int tls_convert_notafter(struct tm *tm, time_t *out_time) { int64_t posix_time; /* OPENSSL_timegm() fails if tm is not representable in a time_t */ if (OPENSSL_timegm(tm, out_time)) return 1; if (!OPENSSL_tm_to_posix(tm, &posix_time)) return 0; if (posix_time < INT32_MIN) return 0; *out_time = (posix_time > INT32_MAX) ? INT32_MAX : posix_time; return 1; } int tls_hex_string(const unsigned char *in, size_t inlen, char **out, size_t *outlen) { static const char hex[] = "0123456789abcdef"; size_t i, len; char *p; if (outlen != NULL) *outlen = 0; if (inlen >= SIZE_MAX) return (-1); if ((*out = reallocarray(NULL, inlen + 1, 2)) == NULL) return (-1); p = *out; len = 0; for (i = 0; i < inlen; i++) { p[len++] = hex[(in[i] >> 4) & 0x0f]; p[len++] = hex[in[i] & 0x0f]; } p[len++] = 0; if (outlen != NULL) *outlen = len; return (0); } static int tls_get_peer_cert_hash(struct tls *ctx, char **hash) { *hash = NULL; if (ctx->ssl_peer_cert == NULL) return (0); if (tls_cert_hash(ctx->ssl_peer_cert, hash) == -1) { tls_set_errorx(ctx, TLS_ERROR_OUT_OF_MEMORY, "out of memory"); *hash = NULL; return -1; } return 0; } static int tls_get_peer_cert_issuer(struct tls *ctx, char **issuer) { const X509_NAME *name = NULL; *issuer = NULL; if (ctx->ssl_peer_cert == NULL) return (-1); if ((name = X509_get_issuer_name(ctx->ssl_peer_cert)) == NULL) return (-1); *issuer = X509_NAME_oneline(name, 0, 0); if (*issuer == NULL) return (-1); return (0); } static int tls_get_peer_cert_subject(struct tls *ctx, char **subject) { const X509_NAME *name = NULL; *subject = NULL; if (ctx->ssl_peer_cert == NULL) return (-1); if ((name = X509_get_subject_name(ctx->ssl_peer_cert)) == NULL) return (-1); *subject = X509_NAME_oneline(name, 0, 0); if (*subject == NULL) return (-1); return (0); } static int tls_get_peer_cert_common_name(struct tls *ctx, char **common_name) { if (ctx->ssl_peer_cert == NULL) return (-1); return tls_get_common_name(ctx, ctx->ssl_peer_cert, NULL, common_name); } static int tls_get_peer_cert_times(struct tls *ctx, time_t *notbefore, time_t *notafter) { struct tm before_tm, after_tm; ASN1_TIME *before, *after; if (ctx->ssl_peer_cert == NULL) return (-1); if ((before = X509_get_notBefore(ctx->ssl_peer_cert)) == NULL) goto err; if ((after = X509_get_notAfter(ctx->ssl_peer_cert)) == NULL) goto err; if (!ASN1_TIME_to_tm(before, &before_tm)) goto err; if (!ASN1_TIME_to_tm(after, &after_tm)) goto err; if (!tls_convert_notafter(&after_tm, notafter)) goto err; if (!OPENSSL_timegm(&before_tm, notbefore)) goto err; return (0); err: return (-1); } static int tls_get_peer_cert_info(struct tls *ctx) { if (ctx->ssl_peer_cert == NULL) return (0); if (tls_get_peer_cert_hash(ctx, &ctx->conninfo->hash) == -1) goto err; if (tls_get_peer_cert_subject(ctx, &ctx->conninfo->subject) == -1) goto err; if (tls_get_peer_cert_issuer(ctx, &ctx->conninfo->issuer) == -1) goto err; if (tls_get_peer_cert_common_name(ctx, &ctx->conninfo->common_name) == -1) goto err; if (tls_get_peer_cert_times(ctx, &ctx->conninfo->notbefore, &ctx->conninfo->notafter) == -1) goto err; return (0); err: return (-1); } static int tls_conninfo_alpn_proto(struct tls *ctx) { const unsigned char *p; unsigned int len; free(ctx->conninfo->alpn); ctx->conninfo->alpn = NULL; SSL_get0_alpn_selected(ctx->ssl_conn, &p, &len); if (len > 0) { if ((ctx->conninfo->alpn = malloc(len + 1)) == NULL) return (-1); memcpy(ctx->conninfo->alpn, p, len); ctx->conninfo->alpn[len] = '\0'; } return (0); } static int tls_conninfo_cert_pem(struct tls *ctx) { int i, rv = -1; BIO *membio = NULL; BUF_MEM *bptr = NULL; if (ctx->ssl_peer_cert == NULL) return 0; if ((membio = BIO_new(BIO_s_mem()))== NULL) goto err; /* * We have to write the peer cert out separately, because * the certificate chain may or may not contain it. */ if (!PEM_write_bio_X509(membio, ctx->ssl_peer_cert)) goto err; for (i = 0; i < sk_X509_num(ctx->ssl_peer_chain); i++) { X509 *chaincert = sk_X509_value(ctx->ssl_peer_chain, i); if (chaincert != ctx->ssl_peer_cert && !PEM_write_bio_X509(membio, chaincert)) goto err; } BIO_get_mem_ptr(membio, &bptr); free(ctx->conninfo->peer_cert); ctx->conninfo->peer_cert_len = 0; if ((ctx->conninfo->peer_cert = malloc(bptr->length)) == NULL) goto err; ctx->conninfo->peer_cert_len = bptr->length; memcpy(ctx->conninfo->peer_cert, bptr->data, ctx->conninfo->peer_cert_len); /* BIO_free() will kill BUF_MEM - because we have not set BIO_NOCLOSE */ rv = 0; err: BIO_free(membio); return rv; } static int tls_conninfo_session(struct tls *ctx) { ctx->conninfo->session_resumed = SSL_session_reused(ctx->ssl_conn); return 0; } int tls_conninfo_populate(struct tls *ctx) { const char *tmp; tls_conninfo_free(ctx->conninfo); if ((ctx->conninfo = calloc(1, sizeof(struct tls_conninfo))) == NULL) { tls_set_errorx(ctx, TLS_ERROR_OUT_OF_MEMORY, "out of memory"); goto err; } if (tls_conninfo_alpn_proto(ctx) == -1) goto err; if ((tmp = SSL_get_cipher(ctx->ssl_conn)) == NULL) goto err; if ((ctx->conninfo->cipher = strdup(tmp)) == NULL) goto err; ctx->conninfo->cipher_strength = SSL_get_cipher_bits(ctx->ssl_conn, NULL); if (ctx->servername != NULL) { if ((ctx->conninfo->servername = strdup(ctx->servername)) == NULL) goto err; } if ((tmp = SSL_get_version(ctx->ssl_conn)) == NULL) goto err; if ((ctx->conninfo->version = strdup(tmp)) == NULL) goto err; if (tls_get_peer_cert_info(ctx) == -1) goto err; if (tls_conninfo_cert_pem(ctx) == -1) goto err; if (tls_conninfo_session(ctx) == -1) goto err; return (0); err: tls_conninfo_free(ctx->conninfo); ctx->conninfo = NULL; return (-1); } void tls_conninfo_free(struct tls_conninfo *conninfo) { if (conninfo == NULL) return; free(conninfo->alpn); free(conninfo->cipher); free(conninfo->servername); free(conninfo->version); free(conninfo->common_name); free(conninfo->hash); free(conninfo->issuer); free(conninfo->subject); free(conninfo->peer_cert); free(conninfo); } const char * tls_conn_alpn_selected(struct tls *ctx) { if (ctx->conninfo == NULL) return (NULL); return (ctx->conninfo->alpn); } const char * tls_conn_cipher(struct tls *ctx) { if (ctx->conninfo == NULL) return (NULL); return (ctx->conninfo->cipher); } int tls_conn_cipher_strength(struct tls *ctx) { if (ctx->conninfo == NULL) return (0); return (ctx->conninfo->cipher_strength); } const char * tls_conn_servername(struct tls *ctx) { if (ctx->conninfo == NULL) return (NULL); return (ctx->conninfo->servername); } int tls_conn_session_resumed(struct tls *ctx) { if (ctx->conninfo == NULL) return (0); return (ctx->conninfo->session_resumed); } const char * tls_conn_version(struct tls *ctx) { if (ctx->conninfo == NULL) return (NULL); return (ctx->conninfo->version); } opensmtpd-7.9.0p0/openbsd-compat/libtls/tls_internal.h000066400000000000000000000231741525355226100230700ustar00rootroot00000000000000/* $OpenBSD: tls_internal.h,v 1.87 2026/06/06 16:23:30 sthen Exp $ */ /* * Copyright (c) 2014 Jeremie Courreges-Anglas * Copyright (c) 2014 Joel Sing * * 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 HEADER_TLS_INTERNAL_H #define HEADER_TLS_INTERNAL_H #include #include #include #define TLS_CIPHERS_DEFAULT TLS_CIPHERS_COMPAT #define TLS_CIPHERS_COMPAT "HIGH:!aNULL" #define TLS_CIPHERS_LEGACY "HIGH:MEDIUM:!aNULL" #define TLS_CIPHERS_ALL "ALL:!aNULL:!eNULL" #define TLS_ECDHE_CURVES "X25519MLKEM768,X25519,P-256,P-384" union tls_addr { struct in_addr ip4; struct in6_addr ip6; }; struct tls_error { char *msg; int code; int errno_value; int tls; }; struct tls_keypair { struct tls_keypair *next; char *cert_mem; size_t cert_len; char *key_mem; size_t key_len; char *ocsp_staple; size_t ocsp_staple_len; char *pubkey_hash; }; #define TLS_MIN_SESSION_TIMEOUT (4) #define TLS_MAX_SESSION_TIMEOUT (24 * 60 * 60) #define TLS_NUM_TICKETS 4 #define TLS_TICKET_NAME_SIZE 16 #define TLS_TICKET_AES_SIZE 32 #define TLS_TICKET_HMAC_SIZE 16 struct tls_ticket_key { /* The key_name must be 16 bytes according to -lssl */ unsigned char key_name[TLS_TICKET_NAME_SIZE]; unsigned char aes_key[TLS_TICKET_AES_SIZE]; unsigned char hmac_key[TLS_TICKET_HMAC_SIZE]; time_t time; }; typedef int (*tls_sign_cb)(void *_cb_arg, const char *_pubkey_hash, const uint8_t *_input, size_t _input_len, int _padding_type, uint8_t **_out_signature, size_t *_out_signature_len); struct tls_config { struct tls_error error; int refcount; char *alpn; size_t alpn_len; const char *ca_path; char *ca_mem; size_t ca_len; const char *ciphers; int ciphers_server; char *crl_mem; size_t crl_len; int dheparams; int *ecdhecurves; size_t ecdhecurves_len; struct tls_keypair *keypair; int ocsp_require_stapling; uint32_t protocols; unsigned char session_id[TLS_MAX_SESSION_ID_LENGTH]; int session_fd; int session_lifetime; struct tls_ticket_key ticket_keys[TLS_NUM_TICKETS]; uint32_t ticket_keyrev; int ticket_autorekey; int verify_cert; int verify_client; int verify_depth; int verify_name; int verify_time; int skip_private_key_check; int use_fake_private_key; tls_sign_cb sign_cb; void *sign_cb_arg; }; struct tls_conninfo { char *alpn; char *cipher; int cipher_strength; char *servername; int session_resumed; char *version; char *common_name; char *hash; char *issuer; char *subject; uint8_t *peer_cert; size_t peer_cert_len; time_t notbefore; time_t notafter; }; #define TLS_CLIENT (1 << 0) #define TLS_SERVER (1 << 1) #define TLS_SERVER_CONN (1 << 2) #define TLS_EOF_NO_CLOSE_NOTIFY (1 << 0) #define TLS_CONNECTED (1 << 1) #define TLS_HANDSHAKE_COMPLETE (1 << 2) #define TLS_SSL_NEEDS_SHUTDOWN (1 << 3) struct tls_ocsp_result { const char *result_msg; int response_status; int cert_status; int crl_reason; time_t this_update; time_t next_update; time_t revocation_time; }; struct tls_ocsp { /* responder location */ char *ocsp_url; /* cert data, this struct does not own these */ X509 *main_cert; STACK_OF(X509) *extra_certs; struct tls_ocsp_result *ocsp_result; }; struct tls_sni_ctx { struct tls_sni_ctx *next; struct tls_keypair *keypair; SSL_CTX *ssl_ctx; X509 *ssl_cert; }; struct tls { struct tls_config *config; struct tls_keypair *keypair; struct tls_error error; uint32_t flags; uint32_t state; char *servername; int socket; SSL *ssl_conn; SSL_CTX *ssl_ctx; struct tls_sni_ctx *sni_ctx; X509 *ssl_peer_cert; STACK_OF(X509) *ssl_peer_chain; struct tls_conninfo *conninfo; struct tls_ocsp *ocsp; tls_read_cb read_cb; tls_write_cb write_cb; void *cb_arg; }; int tls_set_mem(char **_dest, size_t *_destlen, const void *_src, size_t _srclen); int tls_set_string(const char **_dest, const char *_src); struct tls_keypair *tls_keypair_new(void); void tls_keypair_clear_key(struct tls_keypair *_keypair); void tls_keypair_free(struct tls_keypair *_keypair); int tls_keypair_set_cert_file(struct tls_keypair *_keypair, struct tls_error *_error, const char *_cert_file); int tls_keypair_set_cert_mem(struct tls_keypair *_keypair, struct tls_error *_error, const uint8_t *_cert, size_t _len); int tls_keypair_set_key_file(struct tls_keypair *_keypair, struct tls_error *_error, const char *_key_file); int tls_keypair_set_key_mem(struct tls_keypair *_keypair, struct tls_error *_error, const uint8_t *_key, size_t _len); int tls_keypair_set_ocsp_staple_file(struct tls_keypair *_keypair, struct tls_error *_error, const char *_ocsp_file); int tls_keypair_set_ocsp_staple_mem(struct tls_keypair *_keypair, struct tls_error *_error, const uint8_t *_staple, size_t _len); int tls_keypair_load_cert(struct tls_keypair *_keypair, struct tls_error *_error, X509 **_cert); struct tls_sni_ctx *tls_sni_ctx_new(void); void tls_sni_ctx_free(struct tls_sni_ctx *sni_ctx); struct tls_config *tls_config_new_internal(void); struct tls *tls_new(void); struct tls *tls_server_conn(struct tls *ctx); int tls_get_common_name(struct tls *_ctx, X509 *_cert, const char *_in_name, char **_out_common_name); int tls_check_name(struct tls *ctx, X509 *cert, const char *servername, int *match); int tls_configure_server(struct tls *ctx); int tls_configure_ssl(struct tls *ctx, SSL_CTX *ssl_ctx); int tls_configure_ssl_keypair(struct tls *ctx, SSL_CTX *ssl_ctx, struct tls_keypair *keypair, int required); int tls_configure_ssl_verify(struct tls *ctx, SSL_CTX *ssl_ctx, int verify); int tls_handshake_client(struct tls *ctx); int tls_handshake_server(struct tls *ctx); int tls_config_load_file(struct tls_error *error, const char *filetype, const char *filename, char **buf, size_t *len); int tls_config_ticket_autorekey(struct tls_config *config); int tls_host_port(const char *hostport, char **host, char **port); int tls_set_cbs(struct tls *ctx, tls_read_cb read_cb, tls_write_cb write_cb, void *cb_arg); void tls_error_clear(struct tls_error *error); int tls_error_set(struct tls_error *error, int code, const char *fmt, ...) __attribute__((__format__ (printf, 3, 4))) __attribute__((__nonnull__ (3))); int tls_error_setx(struct tls_error *error, int code, const char *fmt, ...) __attribute__((__format__ (printf, 3, 4))) __attribute__((__nonnull__ (3))); int tls_config_set_error(struct tls_config *cfg, int code, const char *fmt, ...) __attribute__((__format__ (printf, 3, 4))) __attribute__((__nonnull__ (3))); int tls_config_set_errorx(struct tls_config *cfg, int code, const char *fmt, ...) __attribute__((__format__ (printf, 3, 4))) __attribute__((__nonnull__ (3))); int tls_set_error(struct tls *ctx, int code, const char *fmt, ...) __attribute__((__format__ (printf, 3, 4))) __attribute__((__nonnull__ (3))); int tls_set_errorx(struct tls *ctx, int code, const char *fmt, ...) __attribute__((__format__ (printf, 3, 4))) __attribute__((__nonnull__ (3))); int tls_set_ssl_errorx(struct tls *ctx, int code, const char *fmt, ...) __attribute__((__format__ (printf, 3, 4))) __attribute__((__nonnull__ (3))); int tls_ssl_error(struct tls *ctx, SSL *ssl_conn, int ssl_ret, const char *prefix); int tls_conninfo_populate(struct tls *ctx); void tls_conninfo_free(struct tls_conninfo *conninfo); int tls_ocsp_verify_cb(SSL *ssl, void *arg); int tls_ocsp_stapling_cb(SSL *ssl, void *arg); void tls_ocsp_free(struct tls_ocsp *ctx); struct tls_ocsp *tls_ocsp_setup_from_peer(struct tls *ctx); int tls_hex_string(const unsigned char *_in, size_t _inlen, char **_out, size_t *_outlen); int tls_cert_hash(X509 *_cert, char **_hash); int tls_cert_pubkey_hash(X509 *_cert, char **_hash); int tls_password_cb(char *_buf, int _size, int _rwflag, void *_u); RSA_METHOD *tls_signer_rsa_method(void); EC_KEY_METHOD *tls_signer_ecdsa_method(void); #define TLS_PADDING_NONE 0 #define TLS_PADDING_RSA_PKCS1 1 int tls_config_set_sign_cb(struct tls_config *_config, tls_sign_cb _cb, void *_cb_arg); struct tls_signer* tls_signer_new(void); void tls_signer_free(struct tls_signer * _signer); const char *tls_signer_error(struct tls_signer * _signer); int tls_signer_add_keypair_file(struct tls_signer *_signer, const char *_cert_file, const char *_key_file); int tls_signer_add_keypair_mem(struct tls_signer *_signer, const uint8_t *_cert, size_t _cert_len, const uint8_t *_key, size_t _key_len); int tls_signer_sign(struct tls_signer *_signer, const char *_pubkey_hash, const uint8_t *_input, size_t _input_len, int _padding_type, uint8_t **_out_signature, size_t *_out_signature_len); /* XXX this function is not fully hidden so relayd can use it */ void tls_config_skip_private_key_check(struct tls_config *config); void tls_config_use_fake_private_key(struct tls_config *config); #ifndef HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM int SSL_CTX_use_certificate_chain_mem(SSL_CTX *, void *, int); #endif #ifndef HAVE_SSL_CTX_LOAD_VERIFY_MEM int SSL_CTX_load_verify_mem(SSL_CTX *, void *, int); #endif #endif /* HEADER_TLS_INTERNAL_H */ opensmtpd-7.9.0p0/openbsd-compat/libtls/tls_keypair.c000066400000000000000000000105211525355226100227030ustar00rootroot00000000000000/* $OpenBSD: tls_keypair.c,v 1.13 2026/04/20 04:35:00 tb Exp $ */ /* * Copyright (c) 2014 Joel Sing * * 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 #include #include #include #include #include "tls_internal.h" struct tls_keypair * tls_keypair_new(void) { return calloc(1, sizeof(struct tls_keypair)); } static int tls_keypair_pubkey_hash(struct tls_keypair *keypair, struct tls_error *error) { X509 *cert = NULL; int rv = -1; free(keypair->pubkey_hash); keypair->pubkey_hash = NULL; if (keypair->cert_mem == NULL) { rv = 0; goto done; } if (tls_keypair_load_cert(keypair, error, &cert) == -1) goto err; if (tls_cert_pubkey_hash(cert, &keypair->pubkey_hash) == -1) goto err; rv = 0; err: X509_free(cert); done: return (rv); } void tls_keypair_clear_key(struct tls_keypair *keypair) { freezero(keypair->key_mem, keypair->key_len); keypair->key_mem = NULL; keypair->key_len = 0; } int tls_keypair_set_cert_file(struct tls_keypair *keypair, struct tls_error *error, const char *cert_file) { if (tls_config_load_file(error, "certificate", cert_file, &keypair->cert_mem, &keypair->cert_len) == -1) return -1; return tls_keypair_pubkey_hash(keypair, error); } int tls_keypair_set_cert_mem(struct tls_keypair *keypair, struct tls_error *error, const uint8_t *cert, size_t len) { if (tls_set_mem(&keypair->cert_mem, &keypair->cert_len, cert, len) == -1) return -1; return tls_keypair_pubkey_hash(keypair, error); } int tls_keypair_set_key_file(struct tls_keypair *keypair, struct tls_error *error, const char *key_file) { tls_keypair_clear_key(keypair); return tls_config_load_file(error, "key", key_file, &keypair->key_mem, &keypair->key_len); } int tls_keypair_set_key_mem(struct tls_keypair *keypair, struct tls_error *error, const uint8_t *key, size_t len) { tls_keypair_clear_key(keypair); return tls_set_mem(&keypair->key_mem, &keypair->key_len, key, len); } int tls_keypair_set_ocsp_staple_file(struct tls_keypair *keypair, struct tls_error *error, const char *ocsp_file) { return tls_config_load_file(error, "ocsp", ocsp_file, &keypair->ocsp_staple, &keypair->ocsp_staple_len); } int tls_keypair_set_ocsp_staple_mem(struct tls_keypair *keypair, struct tls_error *error, const uint8_t *staple, size_t len) { return tls_set_mem(&keypair->ocsp_staple, &keypair->ocsp_staple_len, staple, len); } void tls_keypair_free(struct tls_keypair *keypair) { if (keypair == NULL) return; tls_keypair_clear_key(keypair); free(keypair->cert_mem); free(keypair->ocsp_staple); free(keypair->pubkey_hash); free(keypair); } int tls_keypair_load_cert(struct tls_keypair *keypair, struct tls_error *error, X509 **cert) { char *errstr = "unknown"; BIO *cert_bio = NULL; unsigned long ssl_err; int rv = -1; X509_free(*cert); *cert = NULL; if (keypair->cert_mem == NULL) { tls_error_setx(error, TLS_ERROR_UNKNOWN, "keypair has no certificate"); goto err; } if (keypair->cert_len > INT_MAX) { tls_error_setx(error, TLS_ERROR_INVALID_ARGUMENT, "certificate too long"); goto err; } if ((cert_bio = BIO_new_mem_buf(keypair->cert_mem, keypair->cert_len)) == NULL) { tls_error_setx(error, TLS_ERROR_UNKNOWN, "failed to create certificate bio"); goto err; } if ((*cert = PEM_read_bio_X509(cert_bio, NULL, tls_password_cb, NULL)) == NULL) { if ((ssl_err = ERR_peek_error()) != 0) errstr = ERR_error_string(ssl_err, NULL); tls_error_setx(error, TLS_ERROR_UNKNOWN, "failed to load certificate: %s", errstr); goto err; } rv = 0; err: BIO_free(cert_bio); return (rv); } opensmtpd-7.9.0p0/openbsd-compat/libtls/tls_ocsp.c000066400000000000000000000266471525355226100222230ustar00rootroot00000000000000/* $OpenBSD: tls_ocsp.c,v 1.29 2026/04/16 07:35:25 tb Exp $ */ /* * Copyright (c) 2015 Marko Kreen * Copyright (c) 2016 Bob Beck * * 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 #include #include #include #include #include #include #include #include #include "tls_internal.h" #define MAXAGE_SEC (14*24*60*60) #define JITTER_SEC (60) /* * State for request. */ static struct tls_ocsp * tls_ocsp_new(void) { return (calloc(1, sizeof(struct tls_ocsp))); } void tls_ocsp_free(struct tls_ocsp *ocsp) { if (ocsp == NULL) return; X509_free(ocsp->main_cert); free(ocsp->ocsp_result); free(ocsp->ocsp_url); free(ocsp); } static int tls_ocsp_asn1_parse_time(struct tls *ctx, ASN1_GENERALIZEDTIME *gt, time_t *gt_time) { struct tm tm; if (gt == NULL) return -1; /* RFC 6960 specifies that all times in OCSP must be GENERALIZEDTIME */ if (!ASN1_GENERALIZEDTIME_check(gt)) return -1; if (!ASN1_TIME_to_tm(gt, &tm)) return -1; if (!OPENSSL_timegm(&tm, gt_time)) return -1; return 0; } static int tls_ocsp_fill_info(struct tls *ctx, int response_status, int cert_status, int crl_reason, ASN1_GENERALIZEDTIME *revtime, ASN1_GENERALIZEDTIME *thisupd, ASN1_GENERALIZEDTIME *nextupd) { struct tls_ocsp_result *info = NULL; free(ctx->ocsp->ocsp_result); ctx->ocsp->ocsp_result = NULL; if ((info = calloc(1, sizeof (struct tls_ocsp_result))) == NULL) { tls_set_errorx(ctx, TLS_ERROR_OUT_OF_MEMORY, "out of memory"); return -1; } info->response_status = response_status; info->cert_status = cert_status; info->crl_reason = crl_reason; if (info->response_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { info->result_msg = OCSP_response_status_str(info->response_status); } else if (info->cert_status != V_OCSP_CERTSTATUS_REVOKED) { info->result_msg = OCSP_cert_status_str(info->cert_status); } else { info->result_msg = OCSP_crl_reason_str(info->crl_reason); } info->revocation_time = info->this_update = info->next_update = -1; if (revtime != NULL && tls_ocsp_asn1_parse_time(ctx, revtime, &info->revocation_time) != 0) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "unable to parse revocation time in OCSP reply"); goto err; } if (thisupd != NULL && tls_ocsp_asn1_parse_time(ctx, thisupd, &info->this_update) != 0) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "unable to parse this update time in OCSP reply"); goto err; } if (nextupd != NULL && tls_ocsp_asn1_parse_time(ctx, nextupd, &info->next_update) != 0) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "unable to parse next update time in OCSP reply"); goto err; } ctx->ocsp->ocsp_result = info; return 0; err: free(info); return -1; } static OCSP_CERTID * tls_ocsp_get_certid(X509 *main_cert, STACK_OF(X509) *extra_certs, SSL_CTX *ssl_ctx) { const X509_NAME *issuer_name; X509 *issuer; X509_STORE_CTX *storectx = NULL; X509_OBJECT *obj = NULL; OCSP_CERTID *cid = NULL; X509_STORE *store; if ((issuer_name = X509_get_issuer_name(main_cert)) == NULL) goto out; if (extra_certs != NULL) { issuer = X509_find_by_subject(extra_certs, (X509_NAME *)issuer_name); if (issuer != NULL) { cid = OCSP_cert_to_id(NULL, main_cert, issuer); goto out; } } if ((store = SSL_CTX_get_cert_store(ssl_ctx)) == NULL) goto out; if ((storectx = X509_STORE_CTX_new()) == NULL) goto out; if (X509_STORE_CTX_init(storectx, store, main_cert, extra_certs) != 1) goto out; if ((obj = X509_STORE_CTX_get_obj_by_subject(storectx, X509_LU_X509, (X509_NAME *)issuer_name)) == NULL) goto out; cid = OCSP_cert_to_id(NULL, main_cert, X509_OBJECT_get0_X509(obj)); out: X509_STORE_CTX_free(storectx); X509_OBJECT_free(obj); return cid; } struct tls_ocsp * tls_ocsp_setup_from_peer(struct tls *ctx) { struct tls_ocsp *ocsp = NULL; STACK_OF(OPENSSL_STRING) *ocsp_urls = NULL; if ((ocsp = tls_ocsp_new()) == NULL) goto err; /* steal state from ctx struct */ ocsp->main_cert = SSL_get_peer_certificate(ctx->ssl_conn); ocsp->extra_certs = SSL_get_peer_cert_chain(ctx->ssl_conn); if (ocsp->main_cert == NULL) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "no peer certificate for OCSP"); goto err; } ocsp_urls = X509_get1_ocsp(ocsp->main_cert); if (ocsp_urls == NULL) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "no OCSP URLs in peer certificate"); goto err; } ocsp->ocsp_url = strdup(sk_OPENSSL_STRING_value(ocsp_urls, 0)); if (ocsp->ocsp_url == NULL) { tls_set_errorx(ctx, TLS_ERROR_OUT_OF_MEMORY, "out of memory"); goto err; } X509_email_free(ocsp_urls); return ocsp; err: tls_ocsp_free(ocsp); X509_email_free(ocsp_urls); return NULL; } static int tls_ocsp_verify_response(struct tls *ctx, OCSP_RESPONSE *resp) { OCSP_BASICRESP *br = NULL; ASN1_GENERALIZEDTIME *revtime = NULL, *thisupd = NULL, *nextupd = NULL; OCSP_CERTID *cid = NULL; STACK_OF(X509) *combined = NULL; int response_status=0, cert_status=0, crl_reason=0; int ret = -1; unsigned long flags; if ((br = OCSP_response_get1_basic(resp)) == NULL) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "cannot load ocsp reply"); goto err; } /* * Skip validation of 'extra_certs' as this should be done * already as part of main handshake. */ flags = OCSP_TRUSTOTHER; /* now verify */ if (OCSP_basic_verify(br, ctx->ocsp->extra_certs, SSL_CTX_get_cert_store(ctx->ssl_ctx), flags) != 1) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "ocsp verify failed"); goto err; } /* signature OK, look inside */ response_status = OCSP_response_status(resp); if (response_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "ocsp verify failed: response - %s", OCSP_response_status_str(response_status)); goto err; } cid = tls_ocsp_get_certid(ctx->ocsp->main_cert, ctx->ocsp->extra_certs, ctx->ssl_ctx); if (cid == NULL) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "ocsp verify failed: no issuer cert"); goto err; } if (OCSP_resp_find_status(br, cid, &cert_status, &crl_reason, &revtime, &thisupd, &nextupd) != 1) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "ocsp verify failed: no result for cert"); goto err; } if (OCSP_check_validity(thisupd, nextupd, JITTER_SEC, MAXAGE_SEC) != 1) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "ocsp verify failed: ocsp response not current"); goto err; } if (tls_ocsp_fill_info(ctx, response_status, cert_status, crl_reason, revtime, thisupd, nextupd) != 0) goto err; /* finally can look at status */ if (cert_status != V_OCSP_CERTSTATUS_GOOD && cert_status != V_OCSP_CERTSTATUS_UNKNOWN) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "ocsp verify failed: revoked cert - %s", OCSP_crl_reason_str(crl_reason)); goto err; } ret = 0; err: sk_X509_free(combined); OCSP_CERTID_free(cid); OCSP_BASICRESP_free(br); return ret; } /* * Process a raw OCSP response from an OCSP server request. * OCSP details can then be retrieved with tls_peer_ocsp_* functions. * returns 0 if certificate ok, -1 otherwise. */ static int tls_ocsp_process_response_internal(struct tls *ctx, const unsigned char *response, size_t size) { int ret; OCSP_RESPONSE *resp; resp = d2i_OCSP_RESPONSE(NULL, &response, size); if (resp == NULL) { tls_ocsp_free(ctx->ocsp); ctx->ocsp = NULL; tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "unable to parse OCSP response"); return -1; } ret = tls_ocsp_verify_response(ctx, resp); OCSP_RESPONSE_free(resp); return ret; } /* TLS handshake verification callback for stapled requests */ int tls_ocsp_verify_cb(SSL *ssl, void *arg) { const unsigned char *raw = NULL; int size, res = -1; struct tls *ctx; if ((ctx = SSL_get_app_data(ssl)) == NULL) return -1; size = SSL_get_tlsext_status_ocsp_resp(ssl, &raw); if (size <= 0) { if (ctx->config->ocsp_require_stapling) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "no stapled OCSP response provided"); return 0; } return 1; } tls_ocsp_free(ctx->ocsp); if ((ctx->ocsp = tls_ocsp_setup_from_peer(ctx)) == NULL) return 0; if (ctx->config->verify_cert == 0 || ctx->config->verify_time == 0) return 1; res = tls_ocsp_process_response_internal(ctx, raw, size); return (res == 0) ? 1 : 0; } /* Staple the OCSP information in ctx->ocsp to the server handshake. */ int tls_ocsp_stapling_cb(SSL *ssl, void *arg) { int ret = SSL_TLSEXT_ERR_ALERT_FATAL; unsigned char *ocsp_staple = NULL; struct tls *ctx; if ((ctx = SSL_get_app_data(ssl)) == NULL) goto err; if (ctx->keypair == NULL || ctx->keypair->ocsp_staple == NULL || ctx->keypair->ocsp_staple_len == 0) return SSL_TLSEXT_ERR_NOACK; if ((ocsp_staple = malloc(ctx->keypair->ocsp_staple_len)) == NULL) goto err; memcpy(ocsp_staple, ctx->keypair->ocsp_staple, ctx->keypair->ocsp_staple_len); if (SSL_set_tlsext_status_ocsp_resp(ctx->ssl_conn, ocsp_staple, ctx->keypair->ocsp_staple_len) != 1) goto err; ret = SSL_TLSEXT_ERR_OK; err: if (ret != SSL_TLSEXT_ERR_OK) free(ocsp_staple); return ret; } /* * Public API */ /* Retrieve OCSP URL from peer certificate, if present. */ const char * tls_peer_ocsp_url(struct tls *ctx) { if (ctx->ocsp == NULL) return NULL; return ctx->ocsp->ocsp_url; } const char * tls_peer_ocsp_result(struct tls *ctx) { if (ctx->ocsp == NULL) return NULL; if (ctx->ocsp->ocsp_result == NULL) return NULL; return ctx->ocsp->ocsp_result->result_msg; } int tls_peer_ocsp_response_status(struct tls *ctx) { if (ctx->ocsp == NULL) return -1; if (ctx->ocsp->ocsp_result == NULL) return -1; return ctx->ocsp->ocsp_result->response_status; } int tls_peer_ocsp_cert_status(struct tls *ctx) { if (ctx->ocsp == NULL) return -1; if (ctx->ocsp->ocsp_result == NULL) return -1; return ctx->ocsp->ocsp_result->cert_status; } int tls_peer_ocsp_crl_reason(struct tls *ctx) { if (ctx->ocsp == NULL) return -1; if (ctx->ocsp->ocsp_result == NULL) return -1; return ctx->ocsp->ocsp_result->crl_reason; } time_t tls_peer_ocsp_this_update(struct tls *ctx) { if (ctx->ocsp == NULL) return -1; if (ctx->ocsp->ocsp_result == NULL) return -1; return ctx->ocsp->ocsp_result->this_update; } time_t tls_peer_ocsp_next_update(struct tls *ctx) { if (ctx->ocsp == NULL) return -1; if (ctx->ocsp->ocsp_result == NULL) return -1; return ctx->ocsp->ocsp_result->next_update; } time_t tls_peer_ocsp_revocation_time(struct tls *ctx) { if (ctx->ocsp == NULL) return -1; if (ctx->ocsp->ocsp_result == NULL) return -1; return ctx->ocsp->ocsp_result->revocation_time; } int tls_ocsp_process_response(struct tls *ctx, const unsigned char *response, size_t size) { if ((ctx->state & TLS_HANDSHAKE_COMPLETE) == 0) return -1; return tls_ocsp_process_response_internal(ctx, response, size); } opensmtpd-7.9.0p0/openbsd-compat/libtls/tls_peer.c000066400000000000000000000046601525355226100222010ustar00rootroot00000000000000/* $OpenBSD: tls_peer.c,v 1.9 2024/12/10 08:40:30 tb Exp $ */ /* * Copyright (c) 2015 Joel Sing * Copyright (c) 2015 Bob Beck * * 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 #include #include #include "tls_internal.h" const char * tls_peer_cert_common_name(struct tls *ctx) { if (ctx->conninfo == NULL) return (NULL); return (ctx->conninfo->common_name); } const char * tls_peer_cert_hash(struct tls *ctx) { if (ctx->conninfo == NULL) return (NULL); return (ctx->conninfo->hash); } const char * tls_peer_cert_issuer(struct tls *ctx) { if (ctx->conninfo == NULL) return (NULL); return (ctx->conninfo->issuer); } const char * tls_peer_cert_subject(struct tls *ctx) { if (ctx->conninfo == NULL) return (NULL); return (ctx->conninfo->subject); } int tls_peer_cert_provided(struct tls *ctx) { return (ctx->ssl_peer_cert != NULL); } int tls_peer_cert_contains_name(struct tls *ctx, const char *name) { int match; if (ctx->ssl_peer_cert == NULL) return (0); if (tls_check_name(ctx, ctx->ssl_peer_cert, name, &match) == -1) return (0); return (match); } time_t tls_peer_cert_notbefore(struct tls *ctx) { if (ctx->ssl_peer_cert == NULL) return (-1); if (ctx->conninfo == NULL) return (-1); return (ctx->conninfo->notbefore); } time_t tls_peer_cert_notafter(struct tls *ctx) { if (ctx->ssl_peer_cert == NULL) return (-1); if (ctx->conninfo == NULL) return (-1); return (ctx->conninfo->notafter); } const uint8_t * tls_peer_cert_chain_pem(struct tls *ctx, size_t *size) { if (ctx->ssl_peer_cert == NULL) return (NULL); if (ctx->conninfo == NULL) return (NULL); *size = ctx->conninfo->peer_cert_len; return (ctx->conninfo->peer_cert); } opensmtpd-7.9.0p0/openbsd-compat/libtls/tls_server.c000066400000000000000000000266741525355226100225650ustar00rootroot00000000000000/* $OpenBSD: tls_server.c,v 1.53 2026/04/16 07:28:00 tb Exp $ */ /* * Copyright (c) 2014 Joel Sing * * 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 #include #include #include #include #include #include #include #include "tls_internal.h" struct tls * tls_server(void) { struct tls *ctx; if (tls_init() == -1) return (NULL); if ((ctx = tls_new()) == NULL) return (NULL); ctx->flags |= TLS_SERVER; return (ctx); } struct tls * tls_server_conn(struct tls *ctx) { struct tls *conn_ctx; if ((conn_ctx = tls_new()) == NULL) return (NULL); conn_ctx->flags |= TLS_SERVER_CONN; ctx->config->refcount++; conn_ctx->config = ctx->config; conn_ctx->keypair = ctx->config->keypair; return (conn_ctx); } static int tls_server_alpn_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) { struct tls *ctx = arg; if (SSL_select_next_proto((unsigned char**)out, outlen, ctx->config->alpn, ctx->config->alpn_len, in, inlen) == OPENSSL_NPN_NEGOTIATED) return (SSL_TLSEXT_ERR_OK); return (SSL_TLSEXT_ERR_ALERT_FATAL); } static int tls_servername_cb(SSL *ssl, int *al, void *arg) { struct tls *ctx = (struct tls *)arg; struct tls_sni_ctx *sni_ctx; union tls_addr addrbuf; struct tls *conn_ctx; const char *name; int match; if ((conn_ctx = SSL_get_app_data(ssl)) == NULL) goto err; if ((name = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name)) == NULL) { /* * The servername callback gets called even when there is no * TLS servername extension provided by the client. Sigh! */ return (SSL_TLSEXT_ERR_NOACK); } /* * Per RFC 6066 section 3: ensure that name is not an IP literal. * * While we should treat this as an error, a number of clients * (Python, Ruby and Safari) are not RFC compliant. To avoid handshake * failures, pretend that we did not receive the extension. */ if (inet_pton(AF_INET, name, &addrbuf) == 1 || inet_pton(AF_INET6, name, &addrbuf) == 1) return (SSL_TLSEXT_ERR_NOACK); free(conn_ctx->servername); if ((conn_ctx->servername = strdup(name)) == NULL) goto err; /* Find appropriate SSL context for requested servername. */ for (sni_ctx = ctx->sni_ctx; sni_ctx != NULL; sni_ctx = sni_ctx->next) { if (tls_check_name(ctx, sni_ctx->ssl_cert, name, &match) == -1) goto err; if (match) { conn_ctx->keypair = sni_ctx->keypair; SSL_set_SSL_CTX(conn_ctx->ssl_conn, sni_ctx->ssl_ctx); return (SSL_TLSEXT_ERR_OK); } } /* No match, use the existing context/certificate. */ return (SSL_TLSEXT_ERR_OK); err: /* * There is no way to tell libssl that an internal failure occurred. * The only option we have is to return a fatal alert. */ *al = SSL_AD_INTERNAL_ERROR; return (SSL_TLSEXT_ERR_ALERT_FATAL); } static struct tls_ticket_key * tls_server_ticket_key(struct tls_config *config, unsigned char *keyname) { struct tls_ticket_key *key = NULL; time_t now; int i; now = time(NULL); if (config->ticket_autorekey == 1) { if (now - 3 * (config->session_lifetime / 4) > config->ticket_keys[0].time) { if (tls_config_ticket_autorekey(config) == -1) return (NULL); } } for (i = 0; i < TLS_NUM_TICKETS; i++) { struct tls_ticket_key *tk = &config->ticket_keys[i]; if (now - config->session_lifetime > tk->time) continue; if (keyname == NULL || timingsafe_memcmp(keyname, tk->key_name, sizeof(tk->key_name)) == 0) { key = tk; break; } } return (key); } static int tls_server_ticket_cb(SSL *ssl, unsigned char *keyname, unsigned char *iv, EVP_CIPHER_CTX *ctx, HMAC_CTX *hctx, int mode) { struct tls_ticket_key *key; struct tls *tls_ctx; if ((tls_ctx = SSL_get_app_data(ssl)) == NULL) return (-1); if (mode == 1) { /* create new session */ key = tls_server_ticket_key(tls_ctx->config, NULL); if (key == NULL) { tls_set_errorx(tls_ctx, TLS_ERROR_UNKNOWN, "no valid ticket key found"); return (-1); } memcpy(keyname, key->key_name, sizeof(key->key_name)); arc4random_buf(iv, EVP_MAX_IV_LENGTH); if (!EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key->aes_key, iv)) { tls_set_errorx(tls_ctx, TLS_ERROR_UNKNOWN, "failed to init encrypt"); return (-1); } if (!HMAC_Init_ex(hctx, key->hmac_key, sizeof(key->hmac_key), EVP_sha256(), NULL)) { tls_set_errorx(tls_ctx, TLS_ERROR_UNKNOWN, "failed to init hmac"); return (-1); } return (0); } else { /* get key by name */ key = tls_server_ticket_key(tls_ctx->config, keyname); if (key == NULL) return (0); if (!EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), NULL, key->aes_key, iv)) { tls_set_errorx(tls_ctx, TLS_ERROR_UNKNOWN, "failed to init decrypt"); return (-1); } if (!HMAC_Init_ex(hctx, key->hmac_key, sizeof(key->hmac_key), EVP_sha256(), NULL)) { tls_set_errorx(tls_ctx, TLS_ERROR_UNKNOWN, "failed to init hmac"); return (-1); } /* time to renew the ticket? is it the primary key? */ if (key != &tls_ctx->config->ticket_keys[0]) return (2); return (1); } } static int tls_configure_server_ssl(struct tls *ctx, SSL_CTX **ssl_ctx, struct tls_keypair *keypair) { SSL_CTX_free(*ssl_ctx); if ((*ssl_ctx = SSL_CTX_new(SSLv23_server_method())) == NULL) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "ssl context failure"); goto err; } #ifdef SSL_OP_NO_CLIENT_RENEGOTIATION SSL_CTX_set_options(*ssl_ctx, SSL_OP_NO_CLIENT_RENEGOTIATION); #endif if (SSL_CTX_set_tlsext_servername_callback(*ssl_ctx, tls_servername_cb) != 1) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to set servername callback"); goto err; } if (SSL_CTX_set_tlsext_servername_arg(*ssl_ctx, ctx) != 1) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to set servername callback arg"); goto err; } if (tls_configure_ssl(ctx, *ssl_ctx) != 0) goto err; if (tls_configure_ssl_keypair(ctx, *ssl_ctx, keypair, 1) != 0) goto err; if (ctx->config->verify_client != 0) { int verify = SSL_VERIFY_PEER; if (ctx->config->verify_client == 1) verify |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; if (tls_configure_ssl_verify(ctx, *ssl_ctx, verify) == -1) goto err; } if (ctx->config->alpn != NULL) SSL_CTX_set_alpn_select_cb(*ssl_ctx, tls_server_alpn_cb, ctx); if (ctx->config->dheparams == -1) SSL_CTX_set_dh_auto(*ssl_ctx, 1); else if (ctx->config->dheparams == 1024) SSL_CTX_set_dh_auto(*ssl_ctx, 2); if (ctx->config->ecdhecurves != NULL) { SSL_CTX_set_ecdh_auto(*ssl_ctx, 1); if (SSL_CTX_set1_groups(*ssl_ctx, ctx->config->ecdhecurves, ctx->config->ecdhecurves_len) != 1) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to set ecdhe curves"); goto err; } } if (ctx->config->ciphers_server == 1) SSL_CTX_set_options(*ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); if (SSL_CTX_set_tlsext_status_cb(*ssl_ctx, tls_ocsp_stapling_cb) != 1) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to add OCSP stapling callback"); goto err; } if (ctx->config->session_lifetime > 0) { /* set the session lifetime and enable tickets */ SSL_CTX_set_timeout(*ssl_ctx, ctx->config->session_lifetime); SSL_CTX_clear_options(*ssl_ctx, SSL_OP_NO_TICKET); if (!SSL_CTX_set_tlsext_ticket_key_cb(*ssl_ctx, tls_server_ticket_cb)) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to set the TLS ticket callback"); goto err; } } if (SSL_CTX_set_session_id_context(*ssl_ctx, ctx->config->session_id, sizeof(ctx->config->session_id)) != 1) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "failed to set session id context"); goto err; } return (0); err: SSL_CTX_free(*ssl_ctx); *ssl_ctx = NULL; return (-1); } static int tls_configure_server_sni(struct tls *ctx) { struct tls_sni_ctx **sni_ctx; struct tls_keypair *kp; if (ctx->config->keypair->next == NULL) return (0); /* Set up additional SSL contexts for SNI. */ sni_ctx = &ctx->sni_ctx; for (kp = ctx->config->keypair->next; kp != NULL; kp = kp->next) { if ((*sni_ctx = tls_sni_ctx_new()) == NULL) { tls_set_errorx(ctx, TLS_ERROR_OUT_OF_MEMORY, "out of memory"); goto err; } (*sni_ctx)->keypair = kp; if (tls_configure_server_ssl(ctx, &(*sni_ctx)->ssl_ctx, kp) == -1) goto err; if (tls_keypair_load_cert(kp, &ctx->error, &(*sni_ctx)->ssl_cert) == -1) goto err; sni_ctx = &(*sni_ctx)->next; } return (0); err: return (-1); } int tls_configure_server(struct tls *ctx) { if (tls_configure_server_ssl(ctx, &ctx->ssl_ctx, ctx->config->keypair) == -1) goto err; if (tls_configure_server_sni(ctx) == -1) goto err; return (0); err: return (-1); } static struct tls * tls_accept_common(struct tls *ctx) { struct tls *conn_ctx = NULL; if ((ctx->flags & TLS_SERVER) == 0) { tls_set_errorx(ctx, TLS_ERROR_INVALID_CONTEXT, "not a server context"); goto err; } if ((conn_ctx = tls_server_conn(ctx)) == NULL) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "connection context failure"); goto err; } if ((conn_ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "ssl failure"); goto err; } if (SSL_set_app_data(conn_ctx->ssl_conn, conn_ctx) != 1) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "ssl application data failure"); goto err; } return conn_ctx; err: tls_free(conn_ctx); return (NULL); } int tls_accept_socket(struct tls *ctx, struct tls **cctx, int s) { return (tls_accept_fds(ctx, cctx, s, s)); } int tls_accept_fds(struct tls *ctx, struct tls **cctx, int fd_read, int fd_write) { struct tls *conn_ctx; if ((conn_ctx = tls_accept_common(ctx)) == NULL) goto err; if (SSL_set_rfd(conn_ctx->ssl_conn, fd_read) != 1 || SSL_set_wfd(conn_ctx->ssl_conn, fd_write) != 1) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "ssl file descriptor failure"); goto err; } *cctx = conn_ctx; return (0); err: tls_free(conn_ctx); *cctx = NULL; return (-1); } int tls_accept_cbs(struct tls *ctx, struct tls **cctx, tls_read_cb read_cb, tls_write_cb write_cb, void *cb_arg) { struct tls *conn_ctx; if ((conn_ctx = tls_accept_common(ctx)) == NULL) goto err; if (tls_set_cbs(conn_ctx, read_cb, write_cb, cb_arg) != 0) goto err; *cctx = conn_ctx; return (0); err: tls_free(conn_ctx); *cctx = NULL; return (-1); } int tls_handshake_server(struct tls *ctx) { int ssl_ret; int rv = -1; if ((ctx->flags & TLS_SERVER_CONN) == 0) { tls_set_errorx(ctx, TLS_ERROR_INVALID_CONTEXT, "not a server connection context"); goto err; } ctx->state |= TLS_SSL_NEEDS_SHUTDOWN; ERR_clear_error(); if ((ssl_ret = SSL_accept(ctx->ssl_conn)) != 1) { rv = tls_ssl_error(ctx, ctx->ssl_conn, ssl_ret, "handshake"); goto err; } ctx->state |= TLS_HANDSHAKE_COMPLETE; rv = 0; err: return (rv); } opensmtpd-7.9.0p0/openbsd-compat/libtls/tls_signer.c000066400000000000000000000261261525355226100225360ustar00rootroot00000000000000/* $OpenBSD: tls_signer.c,v 1.15 2026/04/16 07:35:25 tb Exp $ */ /* * Copyright (c) 2021 Eric Faurot * * 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 #include #include #include #include #include #include #include #include #include #include #include #include "tls.h" #include "tls_internal.h" struct tls_signer_key { char *hash; RSA *rsa; EC_KEY *ecdsa; struct tls_signer_key *next; }; struct tls_signer { struct tls_error error; struct tls_signer_key *keys; }; struct tls_signer * tls_signer_new(void) { struct tls_signer *signer; if ((signer = calloc(1, sizeof(*signer))) == NULL) return (NULL); return (signer); } void tls_signer_free(struct tls_signer *signer) { struct tls_signer_key *skey; if (signer == NULL) return; tls_error_clear(&signer->error); while (signer->keys) { skey = signer->keys; signer->keys = skey->next; RSA_free(skey->rsa); EC_KEY_free(skey->ecdsa); free(skey->hash); free(skey); } free(signer); } const char * tls_signer_error(struct tls_signer *signer) { return (signer->error.msg); } int tls_signer_add_keypair_mem(struct tls_signer *signer, const uint8_t *cert, size_t cert_len, const uint8_t *key, size_t key_len) { struct tls_signer_key *skey = NULL; char *errstr = "unknown"; int ssl_err; EVP_PKEY *pkey = NULL; X509 *x509 = NULL; BIO *bio = NULL; char *hash = NULL; /* Compute certificate hash */ if (cert_len > INT_MAX) { tls_error_setx(&signer->error, TLS_ERROR_INVALID_ARGUMENT, "certificate too long"); goto err; } if ((bio = BIO_new_mem_buf(cert, cert_len)) == NULL) { tls_error_setx(&signer->error, TLS_ERROR_UNKNOWN, "failed to create certificate bio"); goto err; } if ((x509 = PEM_read_bio_X509(bio, NULL, tls_password_cb, NULL)) == NULL) { if ((ssl_err = ERR_peek_error()) != 0) errstr = ERR_error_string(ssl_err, NULL); tls_error_setx(&signer->error, TLS_ERROR_UNKNOWN, "failed to load certificate: %s", errstr); goto err; } if (tls_cert_pubkey_hash(x509, &hash) == -1) { tls_error_setx(&signer->error, TLS_ERROR_UNKNOWN, "failed to get certificate hash"); goto err; } X509_free(x509); x509 = NULL; BIO_free(bio); bio = NULL; /* Read private key */ if (key_len > INT_MAX) { tls_error_setx(&signer->error, TLS_ERROR_INVALID_ARGUMENT, "private key too long"); goto err; } if ((bio = BIO_new_mem_buf(key, key_len)) == NULL) { tls_error_setx(&signer->error, TLS_ERROR_UNKNOWN, "failed to create key bio"); goto err; } if ((pkey = PEM_read_bio_PrivateKey(bio, NULL, tls_password_cb, NULL)) == NULL) { tls_error_setx(&signer->error, TLS_ERROR_UNKNOWN, "failed to read private key"); goto err; } if ((skey = calloc(1, sizeof(*skey))) == NULL) { tls_error_setx(&signer->error, TLS_ERROR_OUT_OF_MEMORY, "out of memory"); goto err; } skey->hash = hash; if ((skey->rsa = EVP_PKEY_get1_RSA(pkey)) == NULL && (skey->ecdsa = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) { tls_error_setx(&signer->error, TLS_ERROR_UNKNOWN, "unknown key type"); goto err; } skey->next = signer->keys; signer->keys = skey; EVP_PKEY_free(pkey); BIO_free(bio); return (0); err: EVP_PKEY_free(pkey); X509_free(x509); BIO_free(bio); free(hash); free(skey); return (-1); } int tls_signer_add_keypair_file(struct tls_signer *signer, const char *cert_file, const char *key_file) { char *cert = NULL, *key = NULL; size_t cert_len, key_len; int rv = -1; if (tls_config_load_file(&signer->error, "certificate", cert_file, &cert, &cert_len) == -1) goto err; if (tls_config_load_file(&signer->error, "key", key_file, &key, &key_len) == -1) goto err; rv = tls_signer_add_keypair_mem(signer, cert, cert_len, key, key_len); err: free(cert); free(key); return (rv); } static int tls_sign_rsa(struct tls_signer *signer, struct tls_signer_key *skey, const uint8_t *input, size_t input_len, int padding_type, uint8_t **out_signature, size_t *out_signature_len) { int rsa_padding, rsa_size, signature_len; char *signature = NULL; *out_signature = NULL; *out_signature_len = 0; if (padding_type == TLS_PADDING_NONE) { rsa_padding = RSA_NO_PADDING; } else if (padding_type == TLS_PADDING_RSA_PKCS1) { rsa_padding = RSA_PKCS1_PADDING; } else { tls_error_setx(&signer->error, TLS_ERROR_UNKNOWN, "invalid RSA padding type (%d)", padding_type); return (-1); } if (input_len > INT_MAX) { tls_error_setx(&signer->error, TLS_ERROR_INVALID_ARGUMENT, "input too large"); return (-1); } if ((rsa_size = RSA_size(skey->rsa)) <= 0) { tls_error_setx(&signer->error, TLS_ERROR_UNKNOWN, "invalid RSA size: %d", rsa_size); return (-1); } if ((signature = calloc(1, rsa_size)) == NULL) { tls_error_setx(&signer->error, TLS_ERROR_OUT_OF_MEMORY, "out of memory"); return (-1); } if ((signature_len = RSA_private_encrypt((int)input_len, input, signature, skey->rsa, rsa_padding)) <= 0) { /* XXX - include further details from libcrypto. */ tls_error_setx(&signer->error, TLS_ERROR_UNKNOWN, "RSA signing failed"); free(signature); return (-1); } *out_signature = signature; *out_signature_len = (size_t)signature_len; return (0); } static int tls_sign_ecdsa(struct tls_signer *signer, struct tls_signer_key *skey, const uint8_t *input, size_t input_len, int padding_type, uint8_t **out_signature, size_t *out_signature_len) { unsigned char *signature; int signature_len; *out_signature = NULL; *out_signature_len = 0; if (padding_type != TLS_PADDING_NONE) { tls_error_setx(&signer->error, TLS_ERROR_UNKNOWN, "invalid ECDSA padding"); return (-1); } if (input_len > INT_MAX) { tls_error_setx(&signer->error, TLS_ERROR_INVALID_ARGUMENT, "digest too large"); return (-1); } if ((signature_len = ECDSA_size(skey->ecdsa)) <= 0) { tls_error_setx(&signer->error, TLS_ERROR_UNKNOWN, "invalid ECDSA size: %d", signature_len); return (-1); } if ((signature = calloc(1, signature_len)) == NULL) { tls_error_setx(&signer->error, TLS_ERROR_OUT_OF_MEMORY, "out of memory"); return (-1); } if (!ECDSA_sign(0, input, input_len, signature, &signature_len, skey->ecdsa)) { /* XXX - include further details from libcrypto. */ tls_error_setx(&signer->error, TLS_ERROR_UNKNOWN, "ECDSA signing failed"); free(signature); return (-1); } *out_signature = signature; *out_signature_len = signature_len; return (0); } int tls_signer_sign(struct tls_signer *signer, const char *pubkey_hash, const uint8_t *input, size_t input_len, int padding_type, uint8_t **out_signature, size_t *out_signature_len) { struct tls_signer_key *skey; *out_signature = NULL; *out_signature_len = 0; for (skey = signer->keys; skey; skey = skey->next) if (!strcmp(pubkey_hash, skey->hash)) break; if (skey == NULL) { tls_error_setx(&signer->error, TLS_ERROR_UNKNOWN, "key not found"); return (-1); } if (skey->rsa != NULL) return tls_sign_rsa(signer, skey, input, input_len, padding_type, out_signature, out_signature_len); if (skey->ecdsa != NULL) return tls_sign_ecdsa(signer, skey, input, input_len, padding_type, out_signature, out_signature_len); tls_error_setx(&signer->error, TLS_ERROR_UNKNOWN, "unknown key type"); return (-1); } static int tls_rsa_priv_enc(int from_len, const unsigned char *from, unsigned char *to, RSA *rsa, int rsa_padding) { struct tls_config *config; uint8_t *signature = NULL; size_t signature_len = 0; const char *pubkey_hash; int padding_type; /* * This function is called via RSA_private_encrypt() and has to conform * to its calling convention/signature. The caller is required to * provide a 'to' buffer of at least RSA_size() bytes. */ pubkey_hash = RSA_get_ex_data(rsa, 0); config = RSA_get_ex_data(rsa, 1); if (pubkey_hash == NULL || config == NULL) goto err; if (rsa_padding == RSA_NO_PADDING) { padding_type = TLS_PADDING_NONE; } else if (rsa_padding == RSA_PKCS1_PADDING) { padding_type = TLS_PADDING_RSA_PKCS1; } else { goto err; } if (from_len < 0) goto err; if (config->sign_cb(config->sign_cb_arg, pubkey_hash, from, from_len, padding_type, &signature, &signature_len) == -1) goto err; if (signature_len > INT_MAX || (int)signature_len > RSA_size(rsa)) goto err; memcpy(to, signature, signature_len); free(signature); return ((int)signature_len); err: free(signature); return (-1); } RSA_METHOD * tls_signer_rsa_method(void) { static RSA_METHOD *rsa_method = NULL; if (rsa_method != NULL) goto out; rsa_method = RSA_meth_new("libtls RSA method", 0); if (rsa_method == NULL) goto out; RSA_meth_set_priv_enc(rsa_method, tls_rsa_priv_enc); out: return (rsa_method); } static ECDSA_SIG * tls_ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey) { struct tls_config *config; ECDSA_SIG *ecdsa_sig = NULL; uint8_t *signature = NULL; size_t signature_len = 0; const unsigned char *p; const char *pubkey_hash; /* * This function is called via ECDSA_do_sign_ex() and has to conform * to its calling convention/signature. */ pubkey_hash = EC_KEY_get_ex_data(eckey, 0); config = EC_KEY_get_ex_data(eckey, 1); if (pubkey_hash == NULL || config == NULL) goto err; if (dgst_len < 0) goto err; if (config->sign_cb(config->sign_cb_arg, pubkey_hash, dgst, dgst_len, TLS_PADDING_NONE, &signature, &signature_len) == -1) goto err; p = signature; if ((ecdsa_sig = d2i_ECDSA_SIG(NULL, &p, signature_len)) == NULL) goto err; free(signature); return (ecdsa_sig); err: free(signature); return (NULL); } EC_KEY_METHOD * tls_signer_ecdsa_method(void) { static EC_KEY_METHOD *ecdsa_method = NULL; const EC_KEY_METHOD *default_method; 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); int (*sign_setup)(EC_KEY *eckey, BN_CTX *ctx_in, BIGNUM **kinvp, BIGNUM **rp); if (ecdsa_method != NULL) goto out; default_method = EC_KEY_get_default_method(); ecdsa_method = EC_KEY_METHOD_new(default_method); if (ecdsa_method == NULL) goto out; EC_KEY_METHOD_get_sign(default_method, &sign, &sign_setup, NULL); EC_KEY_METHOD_set_sign(ecdsa_method, sign, sign_setup, tls_ecdsa_do_sign); out: return (ecdsa_method); } opensmtpd-7.9.0p0/openbsd-compat/libtls/tls_util.c000066400000000000000000000110621525355226100222150ustar00rootroot00000000000000/* $OpenBSD: tls_util.c,v 1.17 2026/03/10 05:26:04 deraadt Exp $ */ /* * Copyright (c) 2014 Joel Sing * Copyright (c) 2014 Ted Unangst * Copyright (c) 2015 Reyk Floeter * * 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 #include #include #include #include #include "tls.h" #include "tls_internal.h" static void * memdup(const void *in, size_t len) { void *out; if ((out = malloc(len)) == NULL) return NULL; memcpy(out, in, len); return out; } int tls_set_mem(char **dest, size_t *destlen, const void *src, size_t srclen) { free(*dest); *dest = NULL; *destlen = 0; if (src != NULL) { if ((*dest = memdup(src, srclen)) == NULL) return -1; *destlen = srclen; } return 0; } int tls_set_string(const char **dest, const char *src) { free((char *)*dest); *dest = NULL; if (src != NULL) if ((*dest = strdup(src)) == NULL) return -1; return 0; } /* * Extract the host and port from a colon separated value. For a literal IPv6 * address the address must be contained with square braces. If a host and * port are successfully extracted, the function will return 0 and the * caller is responsible for freeing the host and port. If no port is found * then the function will return 1, with both host and port being NULL. * On memory allocation failure -1 will be returned. */ int tls_host_port(const char *hostport, char **host, char **port) { char *h, *p, *s; int rv = 1; *host = NULL; *port = NULL; if ((s = strdup(hostport)) == NULL) goto err; h = p = s; /* See if this is an IPv6 literal with square braces. */ if (p[0] == '[') { h++; if ((p = strchr(s, ']')) == NULL) goto done; *p++ = '\0'; } /* Find the port separator. */ if ((p = strchr(p, ':')) == NULL) goto done; /* If there is another separator then we have issues. */ if (strchr(p + 1, ':') != NULL) goto done; *p++ = '\0'; if (asprintf(host, "%s", h) == -1) { *host = NULL; goto err; } if (asprintf(port, "%s", p) == -1) { *port = NULL; goto err; } rv = 0; goto done; err: free(*host); *host = NULL; free(*port); *port = NULL; rv = -1; done: free(s); return (rv); } int tls_password_cb(char *buf, int size, int rwflag, void *u) { size_t len; if (size < 0) return (0); if (u == NULL) { memset(buf, 0, size); return (0); } if ((len = strlcpy(buf, u, size)) >= (size_t)size) return (0); return (len); } uint8_t * tls_load_file(const char *name, size_t *len, char *password) { FILE *fp; EVP_PKEY *key = NULL; BIO *bio = NULL; char *data; uint8_t *buf = NULL; struct stat st; size_t size = 0; int fd = -1; ssize_t n; *len = 0; if ((fd = open(name, O_RDONLY|O_CLOEXEC)) == -1) return (NULL); /* Just load the file into memory without decryption */ if (password == NULL) { if (fstat(fd, &st) != 0) goto err; if (st.st_size < 0) goto err; size = (size_t)st.st_size; if ((buf = malloc(size)) == NULL) goto err; n = read(fd, buf, size); if (n < 0 || (size_t)n != size) goto err; close(fd); goto done; } /* Or read the (possibly) encrypted key from file */ if ((fp = fdopen(fd, "r")) == NULL) goto err; fd = -1; key = PEM_read_PrivateKey(fp, NULL, tls_password_cb, password); fclose(fp); if (key == NULL) goto err; /* Write unencrypted key to memory buffer */ if ((bio = BIO_new(BIO_s_mem())) == NULL) goto err; if (!PEM_write_bio_PrivateKey(bio, key, NULL, NULL, 0, NULL, NULL)) goto err; if ((size = BIO_get_mem_data(bio, &data)) <= 0) goto err; if ((buf = malloc(size)) == NULL) goto err; memcpy(buf, data, size); BIO_free_all(bio); EVP_PKEY_free(key); done: *len = size; return (buf); err: if (fd != -1) close(fd); freezero(buf, size); BIO_free_all(bio); EVP_PKEY_free(key); return (NULL); } void tls_unload_file(uint8_t *buf, size_t len) { freezero(buf, len); } opensmtpd-7.9.0p0/openbsd-compat/libtls/tls_verify.c000066400000000000000000000231021525355226100225420ustar00rootroot00000000000000/* $OpenBSD: tls_verify.c,v 1.34 2026/05/30 17:06:09 jsing Exp $ */ /* * Copyright (c) 2014 Jeremie Courreges-Anglas * * 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 #include #include #include #include #include #include "tls_internal.h" static int tls_match_name(const char *cert_name, const char *name) { const char *cert_domain, *domain, *next_dot; if (strcasecmp(cert_name, name) == 0) return 0; /* Wildcard match? */ if (cert_name[0] == '*') { /* * Valid wildcards: * - "*.domain.tld" * - "*.sub.domain.tld" * - etc. * Reject "*.tld". * No attempt to prevent the use of eg. "*.co.uk". */ cert_domain = &cert_name[1]; /* Disallow "*" */ if (cert_domain[0] == '\0') return -1; /* Disallow "*foo" */ if (cert_domain[0] != '.') return -1; /* Disallow "*.." */ if (cert_domain[1] == '.') return -1; next_dot = strchr(&cert_domain[1], '.'); /* Disallow "*.bar" */ if (next_dot == NULL) return -1; /* Disallow "*.bar." */ if (next_dot[1] == '\0') return -1; /* Disallow "*.bar.." */ if (next_dot[1] == '.') return -1; domain = strchr(name, '.'); /* No wildcard match against a name with no host part. */ if (name[0] == '.') return -1; /* No wildcard match against a name with no domain part. */ if (domain == NULL || strlen(domain) == 1) return -1; if (strcasecmp(cert_domain, domain) == 0) return 0; } return -1; } /* * See RFC 5280 section 4.2.1.6 for SubjectAltName details. * alt_match is set to 1 if a matching alternate name is found. * alt_exists is set to 1 if any known alternate name exists in the certificate. */ static int tls_check_subject_altname(struct tls *ctx, X509 *cert, const char *name, int *alt_match, int *alt_exists) { STACK_OF(GENERAL_NAME) *altname_stack = NULL; union tls_addr addrbuf; int addrlen, type; int count, i; int critical = 0; int rv = -1; *alt_match = 0; *alt_exists = 0; altname_stack = X509_get_ext_d2i(cert, NID_subject_alt_name, &critical, NULL); if (altname_stack == NULL) { if (critical != -1) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "error decoding subjectAltName"); goto err; } goto done; } if (inet_pton(AF_INET, name, &addrbuf) == 1) { type = GEN_IPADD; addrlen = 4; } else if (inet_pton(AF_INET6, name, &addrbuf) == 1) { type = GEN_IPADD; addrlen = 16; } else { type = GEN_DNS; addrlen = 0; } count = sk_GENERAL_NAME_num(altname_stack); for (i = 0; i < count; i++) { GENERAL_NAME *altname; altname = sk_GENERAL_NAME_value(altname_stack, i); if (altname->type == GEN_DNS || altname->type == GEN_IPADD) *alt_exists = 1; if (altname->type != type) continue; if (type == GEN_DNS) { const unsigned char *data; int format, len; format = ASN1_STRING_type(altname->d.dNSName); if (format == V_ASN1_IA5STRING) { data = ASN1_STRING_get0_data(altname->d.dNSName); len = ASN1_STRING_length(altname->d.dNSName); if (len < 0 || (size_t)len != strlen(data)) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "error verifying name '%s': " "NUL byte in subjectAltName, " "probably a malicious certificate", name); goto err; } /* * Per RFC 5280 section 4.2.1.6: * " " is a legal domain name, but that * dNSName must be rejected. */ if (strcmp(data, " ") == 0) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "error verifying name '%s': " "a dNSName of \" \" must not be " "used", name); goto err; } if (tls_match_name(data, name) == 0) { *alt_match = 1; goto done; } } else { #ifdef DEBUG fprintf(stdout, "%s: unhandled subjectAltName " "dNSName encoding (%d)\n", getprogname(), format); #endif } } else if (type == GEN_IPADD) { const unsigned char *data; int datalen; datalen = ASN1_STRING_length(altname->d.iPAddress); data = ASN1_STRING_get0_data(altname->d.iPAddress); if (datalen < 0) { tls_set_errorx(ctx, TLS_ERROR_UNKNOWN, "Unexpected negative length for an " "IP address: %d", datalen); goto err; } /* * Per RFC 5280 section 4.2.1.6: * IPv4 must use 4 octets and IPv6 must use 16 octets. */ if (datalen == addrlen && memcmp(data, &addrbuf, addrlen) == 0) { *alt_match = 1; goto done; } } } done: rv = 0; err: sk_GENERAL_NAME_pop_free(altname_stack, GENERAL_NAME_free); return rv; } static int tls_get_common_name_internal(X509 *cert, char **out_common_name, unsigned int *out_tlserr, const char **out_errstr) { unsigned char *utf8_bytes = NULL; const X509_NAME *subject_name; char *common_name = NULL; int common_name_len; const ASN1_STRING *data; int lastpos = -1; int rv = -1; *out_tlserr = TLS_ERROR_UNKNOWN; *out_errstr = "unknown"; free(*out_common_name); *out_common_name = NULL; subject_name = X509_get_subject_name(cert); if (subject_name == NULL) goto err; lastpos = X509_NAME_get_index_by_NID(subject_name, NID_commonName, lastpos); if (lastpos == -1) goto done; if (lastpos < 0) goto err; if (X509_NAME_get_index_by_NID(subject_name, NID_commonName, lastpos) != -1) { /* * Having multiple CN's is possible, and even happened back in * the glory days of mullets and Hammer pants. In anything like * a modern TLS cert, CN is as close to deprecated as it gets, * and having more than one is bad. We therefore fail if we have * more than one CN fed to us in the subject, treating the * certificate as hostile. */ *out_tlserr = TLS_ERROR_UNKNOWN; *out_errstr = "error getting common name: " "Certificate subject contains multiple Common Name fields, " "probably a malicious or malformed certificate"; goto err; } data = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subject_name, lastpos)); /* * Fail if we cannot encode the CN bytes as UTF-8. */ if ((common_name_len = ASN1_STRING_to_UTF8(&utf8_bytes, data)) < 0) { *out_tlserr = TLS_ERROR_UNKNOWN; *out_errstr = "error getting common name: " "Common Name field cannot be encoded as a UTF-8 string, " "probably a malicious certificate"; goto err; } /* * Fail if the CN is of invalid length. RFC 5280 specifies that a CN * must be between 1 and 64 bytes long. */ if (common_name_len < 1 || common_name_len > 64) { *out_tlserr = TLS_ERROR_UNKNOWN; *out_errstr = "error getting common name: " "Common Name field has invalid length, " "probably a malicious certificate"; goto err; } /* * Fail if the resulting text contains a NUL byte. */ if (memchr(utf8_bytes, 0, common_name_len) != NULL) { *out_tlserr = TLS_ERROR_UNKNOWN; *out_errstr = "error getting common name: " "NUL byte in Common Name field, " "probably a malicious certificate"; goto err; } common_name = strndup(utf8_bytes, common_name_len); if (common_name == NULL) { *out_tlserr = TLS_ERROR_OUT_OF_MEMORY; *out_errstr = "out of memory"; goto err; } *out_common_name = common_name; common_name = NULL; done: if (*out_common_name == NULL) *out_common_name = strdup(""); if (*out_common_name == NULL) { *out_tlserr = TLS_ERROR_OUT_OF_MEMORY; *out_errstr = "out of memory"; goto err; } rv = 0; err: free(utf8_bytes); free(common_name); return rv; } int tls_get_common_name(struct tls *ctx, X509 *cert, const char *in_name, char **out_common_name) { unsigned int errcode = TLS_ERROR_UNKNOWN; const char *errstr = "unknown"; if (tls_get_common_name_internal(cert, out_common_name, &errcode, &errstr) == -1) { const char *name = in_name; const char *space = " "; if (name == NULL) name = space = ""; tls_set_errorx(ctx, errcode, "%s%s%s", name, space, errstr); return -1; } return 0; } static int tls_check_common_name(struct tls *ctx, X509 *cert, const char *name, int *cn_match) { char *common_name = NULL; union tls_addr addrbuf; int rv = -1; if (tls_get_common_name(ctx, cert, name, &common_name) == -1) goto err; if (strlen(common_name) == 0) goto done; /* * We don't want to attempt wildcard matching against IP addresses, * so perform a simple comparison here. */ if (inet_pton(AF_INET, name, &addrbuf) == 1 || inet_pton(AF_INET6, name, &addrbuf) == 1) { if (strcmp(common_name, name) == 0) *cn_match = 1; goto done; } if (tls_match_name(common_name, name) == 0) *cn_match = 1; done: rv = 0; err: free(common_name); return rv; } int tls_check_name(struct tls *ctx, X509 *cert, const char *name, int *match) { int alt_exists; *match = 0; if (tls_check_subject_altname(ctx, cert, name, match, &alt_exists) == -1) return -1; /* * As per RFC 6125 section 6.4.4, if any known alternate name existed * in the certificate, we do not attempt to match on the CN. */ if (*match || alt_exists) return 0; return tls_check_common_name(ctx, cert, name, match); } opensmtpd-7.9.0p0/openbsd-compat/nanosleep.c000066400000000000000000000034151525355226100210540ustar00rootroot00000000000000 /* * 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. */ #include "includes.h" #include #ifdef HAVE_SYS_SELECT_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include #include #include #include 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); } opensmtpd-7.9.0p0/openbsd-compat/openbsd-compat.h000066400000000000000000000163311525355226100220110ustar00rootroot00000000000000/* $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 #include #include /* OpenBSD function replacements */ #include "base64.h" #include #include #include "bsd-vis.h" #ifdef HAVE_SYS_TIME_H #include #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_STRLCPY size_t strlcpy(char *dst, const char *src, size_t size); #endif #if !HAVE_STRLCAT size_t strlcat(char *dst, const char *src, size_t size); #endif #ifndef HAVE_STRMODE void strmode(mode_t 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_RES_RANDOMID unsigned int res_randomid(void); #endif #ifndef HAVE_STRSEP char *strsep(char **stringp, const char *delim); #endif #ifndef HAVE_SETPROCTITLE void setproctitle(const char *fmt, ...); void compat_init_setproctitle(int argc, char *argv[]); #endif #ifndef HAVE_GETDTABLECOUNT int getdtablecount(void); #endif #ifndef HAVE_GETDTABLESIZE int getdtablesize(void); #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-setres_id.h" */ /* #include "bsd-statvfs.h" */ #include "waitpid.h" /* #include "bsd-poll.h" */ #ifndef HAVE_GETPEEREID int getpeereid(int , uid_t *, gid_t *); #endif /* * XXX some old versions of LibreSSL (pre 3.8) provide * arc4random(), work around that. */ #if !defined(HAVE_ARC4RANDOM) || defined(LIBRESSL_VERSION_NUMBER) unsigned int arc4random(void); #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 /* #include XXX needed? For size_t */ #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_EXPLICIT_BZERO) || defined(LIBRESSL_VERSION_NUMBER) void explicit_bzero(void *p, size_t n); #endif /* OpenSMTPD-portable specific entries */ #ifndef HAVE_FGETLN #include #include char * fgetln(FILE *stream, size_t *len); #endif #ifndef HAVE_FPARSELN #include #include 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 #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 #if !defined(HAVE_INET_NET_PTON) || defined(HAVE_LIBBSD) int inet_net_pton(int, const char *, void *, size_t); #endif #ifndef HAVE_PLEDGE #define pledge(promises, paths) 0 #endif #ifndef HAVE_UNVEIL #define unveil(path, permissions) 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 #if !HAVE_DECL_IPV6_ADDR_SCOPE_INTFACELOCAL #define USE_IPV6_ADDR_SCOPE_NODELOCAL 1 #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_CRYPT_CHECKPASS int crypt_checkpass(const char *, const char *); #endif #ifndef HAVE_STRNDUP char * strndup(const char *, size_t); #endif #ifndef HAVE_STRNLEN size_t strnlen(const char *, size_t); #endif #ifndef HAVE_STRUCT_TIMEVAL struct timeval { long tv_sec; long tv_usec; } #endif #ifndef HAVE_TIMINGSAFE_MEMCMP int timingsafe_memcmp(const void *, const void *, size_t); #endif #ifndef HAVE_NANOSLEEP #ifndef HAVE_STRUCT_TIMESPEC struct timespec { time_t tv_sec; long tv_nsec; }; #endif int nanosleep(const struct timespec *, struct timespec *); #endif #ifndef HAVE_SETEGID int setegid(uid_t); #endif #ifndef HAVE_SETEUID int seteuid(uid_t); #endif #ifndef HAVE_SETSID #define setsid() setpgrp(0, getpid()) #endif #ifndef HAVE_SIGNAL typedef void (*mysig_t)(int); mysig_t mysignal(int sig, mysig_t act); #define signal(a,b) mysignal(a,b) #endif #ifndef HAVE_USLEEP int usleep(unsigned int useconds); #endif int pipe2(int pipefd[2], int flags); char *get_progname(char *); #ifndef HAVE_CLOCK_GETTIME #ifndef CLOCK_REALTIME #define CLOCK_REALTIME 0 #endif #ifndef CLOCK_MONOTONIC #define CLOCK_MONOTONIC 3 #endif int clock_gettime(int clock_id, struct timespec *tp); #endif #ifndef HAVE_SUBOPTARG #define getsubopt(optionp, tokens, valuep) openbsd_getsubopt(optionp, tokens, valuep) int openbsd_getsubopt(char **optionp, char * const *tokens, char **valuep); extern char *suboptarg; /* getsubopt(3) external variable */ #endif #endif /* _OPENBSD_COMPAT_H */ opensmtpd-7.9.0p0/openbsd-compat/openssl/000077500000000000000000000000001525355226100204045ustar00rootroot00000000000000opensmtpd-7.9.0p0/openbsd-compat/openssl/posix_time.h000066400000000000000000000037331525355226100227430ustar00rootroot00000000000000/* $OpenBSD: posix_time.h,v 1.1 2024/02/18 16:28:38 tb Exp $ */ /* * Copyright (c) 2022, Google Inc. * * Permission to use, copy, modify, and/or 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 OPENSSL_HEADER_POSIX_TIME_H #define OPENSSL_HEADER_POSIX_TIME_H #include #include #if defined(__cplusplus) extern "C" { #endif /* * OPENSSL_posix_to_tm converts a int64_t POSIX time value in |time|, which must * be in the range of year 0000 to 9999, to a broken out time value in |tm|. It * returns one on success and zero on error. */ int OPENSSL_posix_to_tm(int64_t time, struct tm *out_tm); /* * OPENSSL_tm_to_posix converts a time value between the years 0 and 9999 in * |tm| to a POSIX time value in |out|. One is returned on success, zero is * returned on failure. It is a failure if |tm| contains out of range values. */ int OPENSSL_tm_to_posix(const struct tm *tm, int64_t *out); /* * OPENSSL_timegm converts a time value between the years 0 and 9999 in |tm| to * a time_t value in |out|. One is returned on success, zero is returned on * failure. It is a failure if the converted time can not be represented in a * time_t, or if the tm contains out of range values. */ int OPENSSL_timegm(const struct tm *tm, time_t *out); #if defined(__cplusplus) } /* extern C */ #endif #endif /* OPENSSL_HEADER_POSIX_TIME_H */ opensmtpd-7.9.0p0/openbsd-compat/paths_h/000077500000000000000000000000001525355226100203475ustar00rootroot00000000000000opensmtpd-7.9.0p0/openbsd-compat/paths_h/paths.h000066400000000000000000000001541525355226100216370ustar00rootroot00000000000000#ifndef PATHS_H #define PATHS_H #ifndef _PATH_DEFPATH #define _PATH_DEFPATH "/bin:/usr/bin" #endif #endif opensmtpd-7.9.0p0/openbsd-compat/pidfile.c000066400000000000000000000056471525355226100205150ustar00rootroot00000000000000/* $OpenBSD: pidfile.c,v 1.14 2019/06/28 14:20:40 schwarze 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 #include #include #include #include #include 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; free(pidfile_path); pidfile_path = NULL; if (asprintf(&pidfile_path, "%s/%s.pid", SMTPD_PIDDIR, basename) == -1) 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 || fflush(f) != 0) { save_errno = errno; (void) fclose(f); (void) unlink(pidfile_path); free(pidfile_path); pidfile_path = NULL; errno = save_errno; return (-1); } (void) 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 opensmtpd-7.9.0p0/openbsd-compat/pipe2.c000066400000000000000000000026041525355226100201060ustar00rootroot00000000000000/* * Copyright (c) 2020 Gilles Chehade * Copyright (C) 2020 Jason A. Donenfeld . 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 #include 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; } opensmtpd-7.9.0p0/openbsd-compat/progname.c000066400000000000000000000030251525355226100206750ustar00rootroot00000000000000 /* * 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. */ #include "includes.h" #include #ifdef HAVE_SYS_SELECT_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include #include #include #include /* * 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; } opensmtpd-7.9.0p0/openbsd-compat/reallocarray.c000066400000000000000000000026421525355226100215510ustar00rootroot00000000000000/* $OpenBSD: reallocarray.c,v 1.3 2015/09/13 08:31:47 guenther Exp $ */ /* * Copyright (c) 2008 Otto Moerbeek * * 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 #include #include #include /* * 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 * 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); } opensmtpd-7.9.0p0/openbsd-compat/recallocarray.c000066400000000000000000000044501525355226100217130ustar00rootroot00000000000000/* $OpenBSD: recallocarray.c,v 1.2 2021/03/18 11:16:58 claudio Exp $ */ /* * Copyright (c) 2008, 2017 Otto Moerbeek * * 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 #include #include #include #include /* * 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; } opensmtpd-7.9.0p0/openbsd-compat/res_hnok.c000066400000000000000000000121761525355226100207040ustar00rootroot00000000000000/* $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