Compare commits

...

1361 Commits
1.0 ... master

Author SHA1 Message Date
Omar Polo c9ea70a36f regress: add test_ipv6_server 2024-05-29 09:06:45 +00:00
Omar Polo 7c723cf05f regress: add a knob to disable test_ipv6_addr
at least on the CI is failing with "can't connect to ::1:10965:
Address not available" which suggests IPv6 is broken there.
2024-05-29 09:05:06 +00:00
Omar Polo b5dd7091ad typo 2024-05-29 08:58:12 +00:00
Omar Polo 5b549c2805 regress: rename ipv4 test and add another with ipv6 2024-05-29 08:56:10 +00:00
Omar Polo b00f71ba97 iri: add support for raw IPv6 addresses 2024-05-29 08:52:25 +00:00
Omar Polo 6ff8de1f8a gg: unbreak -n 2024-05-29 08:37:39 +00:00
Omar Polo 9f675805d0 regress: run test_ip_addr with host=127.0.0.1 2024-05-29 08:33:36 +00:00
Omar Polo a91b0892bf explain why we disable runtime tests on macos 2024-05-29 08:12:51 +00:00
Omar Polo 610a4666cd regress: use the new gg -q to reduce the blabbering 2024-05-29 08:09:25 +00:00
Omar Polo 2f4926259f gg: add -q to avoid printing "Server says" 2024-05-29 08:08:36 +00:00
Omar Polo cd12ad1132 pretty-print the socket address at configuration parsing time
saves a getnameinfo(NI_NUMERICHOST) at runtime, even if it's pretty
cheap.
2024-05-29 08:03:59 +00:00
Omar Polo b2782022c9 add regress that hit gmid via a raw IPv4 address 2024-05-29 07:54:03 +00:00
Omar Polo 1ef0cd0cdb relax the SNI requirement
There are legitimate cases where SNI can't be used, for example
when connecting via an IPv6 address, so don't rejects those requests.
Instead, fill the requested domain with the address (literal) of
the socket they're connected to and attempt to match on it.

This possibly still incur in a "won't proxy" error if the client
then requests a different hostname.

See the github issue https://github.com/omar-polo/gmid/issues/25
2024-05-29 07:52:13 +00:00
Omar Polo 42e2af25ae github: add workflow to build images for ghcr.io 2024-05-27 15:16:38 +00:00
Omar Polo 89dca7ab54 s/MIN/MINIMUM/g 2024-05-25 17:44:35 +00:00
Omar Polo 359c56ce35 contrib/gmid.service: remove User and Group
May cause weird errors (status=216/GROUP) on some distros, and
running as root is already the default, so remove the two lines.
Reported by and debugged together with leandro del Flug, thanks!
2024-04-27 17:12:09 +00:00
Omar Polo c2dcb5fa6e contrib/gmid.service: start as root by default
Various techniques used by gmid are effective only when the daemon
is started as root.  Strongly suggest to do so by switching the
sample configuration.  This way, provided that a local user is
created as well, the chroot configuration will work out-of-the-box
and the TLS certificates can be readable only by root.
2024-04-27 16:17:37 +00:00
Omar Polo 5d12e6a104 improve the description for -f 2024-04-27 16:10:46 +00:00
Omar Polo 0d8eb9b60c typo: semicolors -> semicolons 2024-04-11 09:42:15 +00:00
Omar Polo 5864f3ce3c set next version 2024-04-04 19:28:14 +00:00
Omar Polo 9536c8ca63 prepare release 2.0.1 2024-04-04 19:16:33 +00:00
Omar Polo 40b71b6861 changelog for 2.0.2 2024-04-04 19:07:04 +00:00
Omar Polo 42235e3fc2 add a test for the config dumping 2024-04-04 13:07:09 +00:00
Omar Polo f53f5e5fe1 fix config dumping (-nn) handling
with the privsep rework the config dumping was unadvertitely broken,
it prints the content of the key itself.
2024-04-04 11:22:06 +00:00
Omar Polo 40ea7b163e use -Werror=implicit-function-declaration for function detection
the previous -Werror triggers too easily: on NixOS for example the
FORTIFY_SOURCE #warning about a missing optimization level breaks all
the checks when using -O0 (which is the default for non-release builds).
2024-04-03 14:03:42 +00:00
Omar Polo be265175c6 fix landlock test
include stddef.h for size_t
2024-04-03 14:01:34 +00:00
Omar Polo 8aba5d8b21 remove dead code 2024-03-12 14:10:36 +00:00
Omar Polo 7c83689428 update mac CI target to sonoma 2024-03-06 10:11:17 +00:00
Omar Polo 248fb833f9 fix `log access path' with chroot
We should open the log file inside the chroot; missed in
4acf495f41.

See https://github.com/omar-polo/gmid/issues/24
2024-03-03 15:43:58 +00:00
Omar Polo 0ed763b03d revert 9f1cce3d0e
we actually should open the log file in the chroot, the bug is in
the code.
2024-03-03 15:42:06 +00:00
Omar Polo 9f1cce3d0e fix log access doc: path is not relative to the chroot
Reported by Colin Henein, thanks!

See https://github.com/omar-polo/gmid/issues/24
2024-03-03 15:27:03 +00:00
Omar Polo 8f543d941e add a note regarding the usage of the bundled libtls
See https://codeberg.org/op/gmid/issues/2
2024-02-02 08:24:28 +00:00
Omar Polo acf244c516 sync changelog 2024-01-30 09:39:07 +00:00
Anna “CyberTailor” 53ad458e22 contrib/vim: fix indent 2024-01-30 09:35:37 +00:00
Omar Polo bb5a25d287 rename the @common_opt macro back to @common
now common is no longer a reserved keyword
2024-01-30 09:31:09 +00:00
Omar Polo f862d389ff turn log styles into strings from yacc point of view
having styles as reserved keywords means that variables / macros can't
be called `common', `condensed', etc...  which is not great and not
obvious either.

Instead, let's keep the log styles as strings and match on them.  This
also allows to have a slightly better error message in case of a typo.

See: https://codeberg.org/op/gmid/issues/1
2024-01-30 09:30:50 +00:00
Omar Polo 574f71f7a3 remove stray space 2024-01-30 09:28:54 +00:00
Omar Polo ebe2e54900 tweak and update freebsd task 2024-01-26 17:02:08 +00:00
Omar Polo ddb089c157 rework the grammar so that ; is accepted after variables and options
See Codeberg issue #1.
2024-01-26 16:54:58 +00:00
Omar Polo 3524375abe add a test that uses @-style macros
See Codeberg issue #1.
2024-01-26 15:34:46 +00:00
Omar Polo fe37d79200 change the default PUBKEY for the verify-release target
doesn't play well with minor releases such as 2.0.1 since for them
I reuse the 2.0 key.
2024-01-24 15:21:19 +00:00
Omar Polo 33a5425235 set next version 2024-01-24 15:13:35 +00:00
Omar Polo cd5e264f9d prepare release 2.0.1 2024-01-24 15:06:08 +00:00
Omar Polo 0baf7066ac changelog for 2.0.1 2024-01-24 15:06:08 +00:00
Omar Polo 83a2644bfb convert remaining code to the imsg getters
Now gmid doesn't touch anymore the internals of the imsg structs.
2024-01-21 19:40:06 +00:00
Omar Polo 4f3b85e6d7 convert the remaining bit of crypto.c to the ibuf_* APIs 2024-01-21 19:30:43 +00:00
Omar Polo 2a822b03ba please macos
for some reason that's not entirely clear to me, __dead doesn't
seem to work on macos, so clang thinks datalen is used un-initialized.

meh
2024-01-21 12:53:01 +00:00
Omar Polo 3f16db6263 update imsg test: gmid now requires the new API too 2024-01-21 12:35:46 +00:00
Omar Polo 561b9f0067 convert crypto.c to the new imsg API 2024-01-21 12:33:33 +00:00
Omar Polo aa2cb5c274 rename ibuf to imsgbuf in crypto
soon we'll be using a struct ibuf and it'll be confusing.
2024-01-21 12:27:42 +00:00
Omar Polo 63e6b0bd0c remove proc_forward_imsg since it's unused 2024-01-21 12:23:28 +00:00
Omar Polo 6dec2ad700 convert most of gmid to the new imsg APIs
Makes parsing and handling of imsgs simpler / clearer.  only crypto.c
is left as-is.
2024-01-21 12:23:28 +00:00
Omar Polo b03e976aa2 convert to use imsg_get_fd()
since proc_forward_imsg() never forwards a file descriptor (it's
never called actually) just use -1 there.
2024-01-21 12:23:16 +00:00
Anna “CyberTailor” 6bce8180d9 configure: fix --mandir handling 2024-01-14 10:30:06 +00:00
Omar Polo 20fa7cded6 ops; pretty big omission among the breaking changes 2024-01-11 16:32:18 +00:00
Omar Polo 2865452c40 typo 2024-01-11 16:26:50 +00:00
Omar Polo 1ee636a45c start the 2.1 release cycle 2024-01-11 16:24:35 +00:00
Omar Polo eabbdf5a10 prepare release 2.0 2024-01-11 16:24:10 +00:00
Omar Polo 38a0d7ee8f add release date 2024-01-11 16:19:31 +00:00
Omar Polo ad3bf17681 update contrib after Dockerfile changes 2024-01-11 15:45:47 +00:00
Omar Polo 398253f3f5 contrib/Dockerfile: fix the build and improve the usage
Install a sample config, include a self-signed cert and setup a
local user and chroot.
2024-01-11 15:42:02 +00:00
Omar Polo d8df67565c fix missing listen on warning
printed the wrong value for the hostname
2024-01-11 13:18:15 +00:00
Omar Polo 0d5282b647 configure: add --sysconfdir / $SYSCONFDIR handling
so that we don't have to hardcode /etc in gmid.c.  Helps on systems
like FreeBSD where the non-base programs are expected to look for
their config in /usr/local/etc.
2024-01-11 13:17:44 +00:00
Omar Polo e78e2923ea install titan too
while here, sort the binaries and the manpages by section and name.
2024-01-11 13:06:08 +00:00
Omar Polo a08e0451ed other misc tweaks for the site 2024-01-11 12:44:09 +00:00
Omar Polo 89b564c312 tweak 2.0 changelog 2024-01-11 10:48:10 +00:00
Omar Polo 13f900092d update/fix description of the subprocess and what they do 2024-01-11 10:43:03 +00:00
Omar Polo 84285be948 better phrasing 2024-01-11 10:40:42 +00:00
Omar Polo 3b9388d8d0 fix the dependencies
libtls is for now bundled
2024-01-11 10:39:58 +00:00
Omar Polo fa3b459472 remove the warning; 2.0 is almost here 2024-01-11 10:38:44 +00:00
Omar Polo a9092d0ee8 gmid.conf.5: finish the sentence about fastcgi strip 2024-01-11 10:37:30 +00:00
Omar Polo 8d0573e84a fix release target; add verify-release 2024-01-10 17:48:11 +00:00
Omar Polo dd40d59659 rename gmid-1.9.pub -> gmid-2.0.pub; next version will be 2.0 2024-01-10 17:45:12 +00:00
Omar Polo 707ec003e5 add changelog for 2.0 2024-01-10 17:39:53 +00:00
Omar Polo c86654c907 add signify pubkeys 2024-01-10 17:33:55 +00:00
Omar Polo e3dd9e66af fix SRCS and DISTFILES; forgot iri.h, landlock is long gone 2024-01-10 17:32:45 +00:00
Omar Polo 14d22e8007 add missing entries in have/Makefile distfiles 2024-01-10 17:32:07 +00:00
Omar Polo 432c31e6cc update vim screenshot 2024-01-10 17:06:46 +00:00
Omar Polo e371817b34 fix configtest with chroot
The configtest checks try to open the root directories too, operation
that can fail when they're expected to be inside a chroot.
2024-01-09 14:15:58 +00:00
Omar Polo ef5057cdec adjust vim syntax file: `fastcgi param' is not deprecated
Only a server-level bare `param' is (and in hindsight it was just
a terrible idea to add it in the first place.)
2024-01-09 09:47:44 +00:00
Omar Polo e6adaaba61 update ChangeLog 2024-01-09 08:44:33 +00:00
Anna “CyberTailor” b7e7b9178d Update Vim syntax file 2024-01-09 08:43:46 +00:00
Anna “CyberTailor” 4b77fc3240 contrib/vim: add ALE linter
ALE is faster and otherwise better alternative to Syntastic.
2024-01-09 08:41:08 +00:00
Omar Polo dd3f04b227 titan: accept either one or two positional arguments 2024-01-08 08:39:18 +00:00
Omar Polo fd48b7c0c4 titan: usage and exit on unknown flags 2024-01-08 08:38:47 +00:00
Omar Polo ac46710a4b fix ge^W gemexp version string 2024-01-08 08:34:36 +00:00
Omar Polo 104a2059e6 make `serve-gemini' use gemexp (from repo) instead of gmid
the standalone / config-less mode was removed from gmid and put
into gemexp time ago...
2024-01-08 08:32:19 +00:00
Omar Polo 822125ca3f add more endian.h macros implementation for macos 2024-01-07 19:01:40 +00:00
Omar Polo 08e6d6ed7a mark private function as static 2024-01-07 18:59:10 +00:00
Omar Polo 0c5dd8ffe9 remove explicit #include endian.h
unfortunately that's not portable.  config.h will include the right
stuff however.
2024-01-07 18:58:26 +00:00
Omar Polo ebfc578491 sync libtls 2024-01-07 18:52:51 +00:00
Omar Polo 12eb29d878 sync imsg 2024-01-07 18:27:41 +00:00
Omar Polo b8537c1c54 add sentence to contrib.gmi 2024-01-07 17:25:15 +00:00
Omar Polo 59953b20b2 disable -Werror=use-after free for CI
gcc' use after free detection is busted and sees one in vis.c where
it's not possible.
2024-01-07 16:35:02 +00:00
Omar Polo acc862155d update the quistart for gmid 2.0 2024-01-07 16:22:43 +00:00
Omar Polo dad248f4cf improve the home page for upcoming 2.0 2024-01-05 20:14:02 +00:00
Omar Polo 6660c0bd41 update faqs 2024-01-05 20:06:02 +00:00
Omar Polo ac6c76f8a8 fix link 2024-01-05 19:13:59 +00:00
Omar Polo f522971968 capitalize Titan in .Nd 2023-10-20 15:48:16 +00:00
Omar Polo d1f84895f7 gmid.8: use a more on-point description 2023-10-20 15:46:42 +00:00
Omar Polo 5bee9bd73e grammar 2023-10-20 15:42:20 +00:00
Omar Polo 797c527a00 minor semplifications to some sentences 2023-10-19 21:33:38 +00:00
Omar Polo 36b2905a68 gmid.conf.5: mention types before servers
There's no strict ordering enforced; yet that list may be used to
structure a configuration and having the types after the list of
server is not something I'd encourage for clarity.
2023-10-19 16:04:44 +00:00
Omar Polo 91b90ad440 some initial touches at the site 2023-10-18 19:29:55 +00:00
Omar Polo 4d51ac4f4a sync manpages; tweak the HTML conversion 2023-10-18 19:29:55 +00:00
Omar Polo dba9907a71 SEE ALSO for titan(1) 2023-10-18 19:29:55 +00:00
Omar Polo 4c12885779 `make lint' to check titan.1 too 2023-10-18 19:29:55 +00:00
Omar Polo 4645a68a10 SEE ALSO for gg too 2023-10-18 19:29:55 +00:00
Omar Polo 33f2a7f735 add a few Xr in SEE ALSO 2023-10-18 19:29:53 +00:00
Omar Polo 17b7d61fc9 sync changelog 2023-10-18 18:47:35 +00:00
Omar Polo 07d86a0beb tweak certificate generation log messages 2023-10-18 18:07:28 +00:00
Omar Polo bab32701fb gemexp: add -R to generate an RSA (4096) key instead of an EC one (default) 2023-10-18 18:06:08 +00:00
Omar Polo adaae5163c rework gencert(); make gemexp generate EC certs
Taking inspiration from acme-client.
2023-10-18 17:47:29 +00:00
Omar Polo 43b38b2dbb copyright++ 2023-10-18 17:15:47 +00:00
Omar Polo e8b2d8e3f0 gg: prepend "Server says" to the reply code 2023-10-18 17:15:03 +00:00
Omar Polo 7980a5d2a8 gg: print the response header for non-2x replies to standard error 2023-10-18 17:13:51 +00:00
Omar Polo c7bcd4a8a9 gg: exit with the gemini response code
0 is still used for 2X replies, and "external" failures (e.g.
network, tls, ...) are still using exit code 1.  Gemini non-2x
replies now get their response code reported as-is as status code.
2023-10-15 15:06:34 +00:00
Omar Polo 4db22ea654 pass LDFLAGS before LIBS 2023-10-14 18:53:37 +00:00
Omar Polo 6e4ba8184d copyright# 2023-10-14 18:52:12 +00:00
Omar Polo 60ff0ebcb5 gemexp doesn't have -v; remove from getopt and usage() 2023-08-29 10:44:08 +00:00
Omar Polo 23e92df733 extend/sync .gitignore 2023-08-29 09:39:28 +00:00
Omar Polo 1218bca611 add -e to gencert usage() string 2023-08-29 09:36:36 +00:00
Omar Polo 96515efd27 tweak gemexp logs
gemexp is meant for local testing so I'm not too worried about
changing its log format if it makes sense to do so.
2023-08-29 09:35:07 +00:00
Omar Polo 80745f0411 regress: change naming scheme for certs, add GENCERT_FLAGS knob
Call the certificates .pem and the keys .key; use contrib/gencert
to generate the certificates and provide a GENCERT_FLAGS knob so
that regress can be run with EC keys (GENCERT_FLAGS=-e).  Still no
automatic way of testing with both RSA and EC keys.
2023-08-29 09:30:28 +00:00
Omar Polo eaca1ed4db sync changelog 2023-08-29 09:26:55 +00:00
Omar Polo a6d07f09e0 gemexp: save certs to $XDG_DATA_HOME/gemexp, not /gmid 2023-08-29 09:26:14 +00:00
Omar Polo f31289a8ac gemexp: change the naming scheme for the certificates
Using what the manpage advertised.  The regress adaptations will
follow.  The directory will also change (and the key type too.)
2023-08-29 09:24:14 +00:00
Omar Polo b894573ad9 fix typo; the size computation is done using len, not ret 2023-08-29 09:18:55 +00:00
Omar Polo c440a0ded9 log ip address and port when tls_handshake fails
These connection are not otherwise logged and it could be helpful
to track down the bad ip.
2023-08-28 21:42:58 +00:00
Omar Polo 6be41efe33 tweak log_request() comment
gg -> gemexp and better wording.
2023-08-28 21:39:59 +00:00
Omar Polo a6c8b8051e don't let crypto_dispatch_server handle IMSG_CRYPTO_ECDSA_SIGN
in this codepath.  otherwise we end up with a mismatch where we
expect a request but were sent a response.
2023-08-28 21:38:09 +00:00
Omar Polo 3cba037a11 pre-increment reqid
otherwise we send the request id N and expect to receive N+1
2023-08-28 21:37:27 +00:00
Omar Polo 6c86d810fc typo; was filling the wrong iov_len... 2023-08-28 21:36:58 +00:00
Omar Polo 7bbf17a857 plug a leak
all other rules are freeing the value of `listen_addr'
2023-08-25 12:19:49 +00:00
Omar Polo c2c051f28e fix automatic guessing of `listen on'
default_host needs to be NULL for getaddrinfo(3) to listen on
everything.
2023-08-25 12:19:00 +00:00
Omar Polo 3cb7e8d7ac ignore some errors from socket(2)
There's no much we can do if we resolv an IPv6 address but its
support is disabled in the current kernel, so ignore and go ahead.
Spotted while testing gmid i n a FreeBSD jail without IPv6.
2023-08-25 09:40:09 +00:00
Omar Polo 39204b7e48 we're in public alpha right now! 2023-08-24 23:21:56 +00:00
Omar Polo ee42fb87e2 remove configure.local{,.example}
unused, un-updated and ignored for quite some time now.
2023-08-23 20:47:17 +00:00
Omar Polo 4f97572edc tweak readme 2023-08-23 20:34:06 +00:00
Omar Polo ae32f1aa8e don't leave HAVE_GETENTROPY undefined 2023-08-23 20:31:02 +00:00
Omar Polo 618b1d4dce resurrect openlog() + tzset() in the logger
They're not needed on OpenBSD nor in other systems... except under
sandbox.  These were added for capsicum() if I remember correctly,
but also with landlock it's better to initialize these things
earlier.
2023-08-23 20:30:39 +00:00
Omar Polo 4d9d3093d4 resurrect landlock support
this time targetting ABI level 3; partially based on how claudio@
handled it in rpki-client.  Fun how this bit of code has come full
circle (gmid inspired what I wrote for got, which inspired what was
written for rpki-client, which has come back.)
2023-08-23 20:18:59 +00:00
Omar Polo cedef5b09c fix build of regress 2023-08-23 19:46:53 +00:00
Omar Polo 01d3961d6f adding forgotten file 2023-08-23 19:39:27 +00:00
Omar Polo 74c6900c91 fix previous; check for getentropy only when arc4random is missing
on macos we have the situation where we have arc4random available
but no getentropy().
2023-08-23 19:22:22 +00:00
Omar Polo 21b4a5163c comment out seed_from_prngd
it's not used (we define OPENSSL_PRNG_ONLY) and fails the build
with -Werror.  Keep the function commented instead of deleting it
just in case we need to undefine OPENSSL_PRNG_ONLY in the future.
2023-08-23 19:13:46 +00:00
Omar Polo 258131b1b6 typo 2023-08-23 17:44:35 +00:00
Omar Polo 8f8d721301 enable privsep crypto on all systems
now that we have a bundled libtls we can actually do this.  Retain
the knob to disable it "just in case".
2023-08-23 17:39:28 +00:00
Omar Polo f9ab77a898 bundle libtls
gmid (like all other daemons that want to do privsep crypto) has a
very close relationship with libtls and need to stay in sync with
it.

OpenBSD' libtls was recently changed to use OpenSSL' EC_KEY_METHOD
instead of the older ECDSA_METHOD, on the gmid side we have to do
the same otherwise failures happens at runtime.  In a similar manner,
privsep crypto is silently broken in the current libretls (next
version should fix it.)

The proper solution would be to complete the signer APIs so that
applications don't need to dive into the library' internals, but
that's a mid-term goal, for the immediate bundling the 'little'
libtls is the lesser evil.

The configure script has gained a new (undocumented for the time
being) flag `--with-libtls=bundled|system' to control which libtls
to use.  It defaults to `bundled' except for OpenBSD where it uses
the `system' one.  Note that OpenBSD versions before 7.3 (inclusive)
ought to use --with-libtls=bundled too since they still do ECDSA_METHOD.
2023-08-23 17:38:49 +00:00
Omar Polo 9019e55e7e sync DISTFILES 2023-08-22 19:58:29 +00:00
Omar Polo 7ea8725593 sync have/* files 2023-08-22 19:43:47 +00:00
Omar Polo 9cd81f93d7 sync changelog 2023-08-22 19:24:45 +00:00
Omar Polo e872053b20 send all the params as per RFC3875 (CGI) and sync documentation 2023-08-18 12:40:10 +00:00
Omar Polo f5dc7eddd7 fix INSTALL handling
Set a sane default for INSTALL, allow it to be changed either as
environment variable or configure argument, and propagate it correctly
to the generated config.mk.

Issue reported by xavi, thanks!
2023-08-13 09:53:12 +00:00
Omar Polo 81634643db fix comment (ge -> gemexp) 2023-08-11 16:18:37 +00:00
Omar Polo 07ad491025 getcwd(NULL) is an extension; don't rely on it
also, while here, add some error checking too
2023-08-11 10:40:58 +00:00
Omar Polo 95500a936a remove not so useful starts_with()
replace its only usage with strncmp().  it's likely faster too.
2023-08-11 10:38:34 +00:00
Omar Polo 8bb1b23633 remove a long, long unused function 2023-08-11 10:33:40 +00:00
Omar Polo cf2784df75 remove useless logging 2023-08-09 19:13:48 +00:00
Omar Polo 390d312b22 don't call client_close() from fcgi/proxy bev handlers
We might end up calling client_close() from start_reply(), but that
will free the fcgi/proxy bufferevent while they're still used on the
stack.

Instead, start_reply() only sets REQUEST_DONE and exits, returning the
error eventually, so callers know when to stop.
2023-08-09 19:13:13 +00:00
Omar Polo 01481c255a update changelog 2023-08-08 18:08:37 +00:00
Omar Polo a1e159c917 fix PATH_INFO / SCRIPT_NAME splitting 2023-08-08 18:07:54 +00:00
Omar Polo 03d671e2aa implement fastcgi strip number 2023-08-08 17:35:11 +00:00
Omar Polo 4f7492c36e lower debug log priority 2023-08-08 17:33:43 +00:00
Omar Polo b27dc2b0a3 draft the PATH_INFO and SCRIPT_NAME handling
The idea is to require SCRIPT_NAME to be defined and strip it from
the beginning of the path to get PATH_INFO.  Soon(tm) a `fastcgi
request strip' option will be added too.  Maybe even `fastcgi script
name "path"` that sets SCRIPT_NAME automatically.
2023-08-08 16:06:17 +00:00
Omar Polo 08c0f676fd remove prototype for function killed long ago 2023-08-08 15:57:12 +00:00
Omar Polo d98ef73494 move strip_path to utils.c 2023-08-08 15:56:27 +00:00
Omar Polo 877b479bcc add missing include of config.h in vis.c 2023-08-07 17:57:24 +00:00
Omar Polo 9e6fc13b35 drop the __bounded__ attribute
breaks the build with -Werror depending on the compiler.
2023-08-07 17:54:34 +00:00
Omar Polo 36363b011c drop #include <sys/cdefs.h> from vis.h
Doesn't seem to be available on many systems.  It is also not strictly
needed since we include vis.h only after headers like stdlib.h that
already pulls in the type it needs.
2023-08-07 16:09:00 +00:00
Omar Polo d13b044d59 address the strnvis(3) portability fiasco
strnvis originates on OpenBSD.  When NetBSD added it to their libc
they decided to swap the argument.  Without starting a holy war on
the "best" argument order, adding an implementation of a function
that's widely available and making its signature purposefully
incompatible is beyond justification.  FreeBSD (and so macos too?)
followed NetBSD in this, so we end up with *two* major and incompatible
strnvis implementations.  libbsd is in a limbo, they started with
the OpenBSD version but they'll probably switch to the NetBSD version
in the future.

That's why we can't have nice things.

Do the right thing(tm) and check for the presence of the original
strnvis(3), if not available or broken use the bundled one.
2023-08-07 15:39:57 +00:00
Omar Polo ddbcd3c13f use the subject' common name as the user field in log 2023-08-07 14:04:47 +00:00
Omar Polo 35579431eb remove debugging leftover 2023-08-07 13:58:43 +00:00
Omar Polo d72ac636bb unbreak config_test() when !use_privsep_crypto
The new config_test() fails miserably when the privsep crypto engine is
not enabled.  As a temporary workaround, forcibly disable it during
config_test() as we're not going to run anyway.
2023-08-07 13:18:04 +00:00
Omar Polo 36a98d50e5 update changelog 2023-08-07 12:40:50 +00:00
Omar Polo 3b431c09d9 try hard at loading the configuration during conftest (-n)
Attempt to do also a few more steps that were previously done only
at runtime.  This can help verifying that the keypairs are matching
for example, but also that there are no typos in the path to the
root directories.

Was requested some time ago by Marian Mizik, thanks for the feature
request!
2023-08-07 12:40:44 +00:00
Omar Polo 9abba172b6 add `log syslog facility' to use a different syslog(3) facility
Was requested ages ago by Karl Jeacle, now that there is some better
support for configuring the logging there's no excuse to add this.
It helps with filtering from syslog.d / syslog.conf.
2023-08-07 09:34:19 +00:00
Omar Polo 3a93c90445 sort logopt 2023-08-07 09:08:23 +00:00
Omar Polo a250aff257 comment out the condensed log style in the manpage 2023-08-04 10:07:02 +00:00
Omar Polo f23b708726 set the default logging style back to legacy
I think the condensed is better but it'll need to change post 2.0
to accomodate for logging the number of bytes read in the body of
a titan request (and it's weird to hardcode a zero there.)  2.0
will ship with the legacy logging style thus.
2023-08-04 10:05:44 +00:00
Omar Polo 5d38e5d88d titan: clean up IRI/URI/URL mess; it's a IRI what we parse 2023-08-04 10:02:18 +00:00
Omar Polo 11ff7f934e titan: error if the URI is not titan:// 2023-08-04 10:01:18 +00:00
Omar Polo d671434bc7 titan: better logging for unexpected EOFs
upon an unexpected EOF, tls_error() returns NULL, so log the real
reason iomux returned -1.
2023-08-04 09:57:27 +00:00
Omar Polo 5905156665 titan: rename parse_err to errstr 2023-08-04 09:53:18 +00:00
Omar Polo 1b1a6fb7ee titan: iomux: return -1 on EOF without receving anything
otherwise it enters an infinite loop where it tries to read, return
zero, and tries again...
2023-08-04 09:52:27 +00:00
Omar Polo fcc5a371b1 titan: fix appending of path parameters
the parameters need to be added at the end of the path, not at the
end of the URL.
2023-08-04 09:49:01 +00:00
Omar Polo 26df50981f actually use the specified log style 2023-08-03 22:37:34 +00:00
Omar Polo 56054fe197 use the legacy style in the tests for now 2023-08-03 22:37:05 +00:00
Omar Polo f736c9579c fix test after log style condensed change 2023-08-03 22:35:49 +00:00
Omar Polo 161651fa5e gmid.conf.5: add one example of proxy relay-to 2023-08-03 22:24:09 +00:00
Omar Polo c2bcf6a402 Nm does not make any sense there 2023-08-03 22:17:32 +00:00
Omar Polo 0eeee6f321 minor tweaks to gg(1) 2023-08-03 22:07:40 +00:00
Omar Polo 603e4dd82f two more missing ge -> gemexp 2023-08-03 22:04:36 +00:00
Omar Polo 564c2ad166 remove the LOGGING section; it's covered by gmid.conf(5) 2023-08-03 22:03:43 +00:00
Omar Polo 547437cc40 fix the macro example 2023-08-01 17:07:46 +00:00
Omar Polo 2c079c9e69 point out that `user' is mandatory if `chroot' is used 2023-08-01 17:06:30 +00:00
Omar Polo b5963536c8 change the 'condensed' style to include the size of the request too
will be used in the future to log how much byte a titan request
uploaded.
2023-08-01 16:59:25 +00:00
Omar Polo 98827de5ab use `log syslog off' in regress to avoid spamming syslog 2023-07-26 08:11:39 +00:00
Omar Polo 0c39da5145 move log syslog after log style 2023-07-26 08:11:21 +00:00
Omar Polo 46bcc4ea95 add log syslog off; don't turn syslog off when log access is specified 2023-07-26 08:10:12 +00:00
Omar Polo cba01a8687 rename IMSG_LOG_TYPE to ACCESS 2023-07-26 07:55:51 +00:00
Omar Polo a84492b75f ge->gemexp forgotten in previous 2023-07-25 20:27:31 +00:00
Omar Polo 471a5250e3 rename ge -> gemexp in regress too 2023-07-25 20:26:26 +00:00
Omar Polo fdb4572d2f revamp helper section of the README and mention titan(1) 2023-07-25 20:24:44 +00:00
Omar Polo 82947e8be7 typo 2023-07-25 20:18:44 +00:00
Omar Polo f8bfba4723 retroactively rename ge -> gemexp in changelog 2023-07-25 20:16:08 +00:00
Omar Polo f59543490d rename ge -> gemexp
gemserv is already taken...
2023-07-25 20:15:40 +00:00
Omar Polo 857635a107 use the condensed logging style in ge too 2023-07-25 20:10:53 +00:00
Omar Polo aea87ce91f sync changelog 2023-07-25 20:08:12 +00:00
Omar Polo abd261d25b allow to change the logging style; introduce some new ones
add `log style <style>'; The old default is called `legacy' now, a
new default format is added called `condensed', and `common' and
`combined' to mimick Apache httpd and nginx (respectively) are also
added.
2023-07-25 20:07:26 +00:00
Omar Polo 2a28b04424 update/sync/correct ChangeLog 2023-07-25 16:30:10 +00:00
Omar Polo e137cb0348 add missing -include titan.d 2023-07-25 16:11:01 +00:00
Omar Polo e075021085 still respect `log' when in debug mode 2023-07-24 14:07:28 +00:00
Omar Polo e5f060f0d2 add a manpage for titan(1) 2023-07-24 09:59:02 +00:00
Omar Polo 3927336855 titan: parse the response code and exit accordingly
Exit with 0 if the response code was in the 2x or 3x range, or with
2 for other codes.  It already exits with 1 upon any other error
(including parsing errors.)

Print the redirect code on 3x to stdout and the meta to stderr for
the 1x, 4x, 5x and 6x ranges.
2023-07-24 09:56:37 +00:00
Omar Polo 56d32bb51a bump man date 2023-07-24 09:29:34 +00:00
Omar Polo 32b1638ebc run tls_connect_socket() after dropping the "inet dns" pledge promises 2023-07-24 09:28:01 +00:00
Omar Polo 9888507cd4 read from stdin if no file is given 2023-07-24 09:28:01 +00:00
Omar Polo bf9ca5e71c sync changelog 2023-07-24 09:07:21 +00:00
Omar Polo 4acf495f41 open the log files inside the chroot 2023-07-24 09:05:33 +00:00
Omar Polo 3bda540e34 reopen log files upon SIGUSR2 2023-07-24 09:00:19 +00:00
Omar Polo 60b4efa1e2 add a test for the file logging 2023-07-24 08:51:35 +00:00
Omar Polo 226f13ece0 add ability to log to files with log access <path> 2023-07-24 08:50:49 +00:00
Omar Polo 3826d7de43 logger use dprintf and a fd instead of a FILE
simplifies further handling.  The stdio layer introduces its own
buffering and for the logs I'd like to avoid it.  fflush(3) is an
option, but using a raw fd and dprintf(2) requires less code.
2023-07-24 08:37:39 +00:00
Omar Polo bf7a7fd7b2 sync changelog 2023-07-23 22:21:23 +00:00
Omar Polo 692a9f5fea remove unused IMSG types 2023-07-23 21:43:18 +00:00
Omar Polo 45c946b37b avoid use-after-free 2023-07-23 19:30:14 +00:00
Omar Polo 3d56cb5336 fix a reduce/reduce conflict
location -> error and locopt -> fastcgi -> error both end up with
a optnl that can be reduced to the empty string.
2023-07-23 19:11:09 +00:00
Omar Polo 60f4107da6 add a test with fastcgi, locations and forceful disabling 2023-07-23 19:04:53 +00:00
Omar Polo 6a8387e5f5 add `fastcgi off' to forceful skip fastcgi for a route 2023-07-23 19:04:37 +00:00
Omar Polo fdd67729b4 adjust syntax in fastcgi test; add another test for the old syntax 2023-07-23 18:45:43 +00:00
Omar Polo a1ba9650a9 revamp fastcgi configuration: make it per-location
this revamps the syntax in the configuration to better match httpd(8)
(and in general be less weird) and to allow per-location fastcgi
configurations.

the bare `param' is now deprecated, but for compatibility it acts
like `fastcgi param' would do now.  Same story for `fastcgi <path>'.
2023-07-23 18:45:05 +00:00
Omar Polo f36ba9be59 move struct envlist and alist up 2023-07-23 18:17:59 +00:00
Omar Polo 0995ecdb87 plug memleak 2023-07-23 17:43:27 +00:00
Omar Polo cf35bdce9a add titan to .gitignore 2023-07-23 17:18:04 +00:00
Omar Polo 88c03474f8 revert previous
Somehow the compat for __dead is not working properly on macos
(cirrus ci) since it complains that parse_debug() does not return
a value in all control paths when it uses usage() (marked as __dead)
to catch a wrong usage.
2023-07-23 14:13:23 +00:00
Omar Polo 67ae3d8e03 use __dead instead of __attribute__((noreturn)) 2023-07-22 16:08:21 +00:00
Omar Polo da3fc66ee8 titan: add compat shims to compile on !OpenBSD 2023-07-22 15:50:22 +00:00
Omar Polo 800aa93c05 gg: warn when the TLS layer is not closed properly
various servers are not handling correctly the close notify so for
the moment don't turn this into an hard error but just warn.
Hopefully, given some time, most servers will be fixed.

while here, drop the gotos and just use a break to exit the main
loop.
2023-07-22 15:47:07 +00:00
Omar Polo 2ff1e2a923 add titan(1) -- a draft titan client 2023-07-22 15:47:02 +00:00
Omar Polo 81bab00246 split out iri.h from gmid.h 2023-07-22 13:49:07 +00:00
Omar Polo 6a996ec20f fmt 2023-07-22 08:19:26 +00:00
Omar Polo bd23307690 drop engine support 2023-07-22 08:17:02 +00:00
Omar Polo 21617eda73 remove the useless logging methods
it makes more clear where the magic is.  adapted from the smtpd'
ca.c diff.
2023-07-22 08:13:15 +00:00
Omar Polo 5d2f4b1611 add compat for endian (now required by imsg) 2023-07-02 09:14:44 +00:00
Omar Polo c9c44c6571 update imsg 2023-07-02 08:55:38 +00:00
Omar Polo 71b02f6390 rename do_accept() -> server_accept() 2023-07-01 22:00:08 +00:00
Omar Polo 2b0b2661ea enrich the description of the server process 2023-07-01 18:44:42 +00:00
Omar Polo 1134ea149a typo 2023-07-01 18:42:16 +00:00
Omar Polo 2c3810687f change log_request to take the code and meta unpacked
don't know what i was smoking when I wrote log_request() like that...
2023-07-01 18:41:46 +00:00
Omar Polo 2247b66842 improve fcgi test: send more than one chunk of data 2023-07-01 18:38:22 +00:00
Omar Polo 0f7fdd2105 parse (and log) the header from fastcgi 2023-07-01 18:37:59 +00:00
Omar Polo a6f2cfe792 multiple -v don't make it more verbose anymore 2023-07-01 14:28:12 +00:00
Omar Polo e3ce19dcc1 change on fatalx -> log_warnx
we already check the validity of the format string, but still avoid a
gratious fatal() at runtime.
2023-07-01 14:22:26 +00:00
Omar Polo 80192f4589 rename fmt_sbuf -> fmtbuf; make the buffer explicit 2023-07-01 14:21:41 +00:00
Omar Polo 57ee9057af add some ideas 2023-07-01 14:11:21 +00:00
Omar Polo 994fc034e5 avoid needless strlen() 2023-07-01 14:11:21 +00:00
Omar Polo e2003e7e30 simplify request handling
get rid of check_path(), it's overly complicated.  Instead, inline
open_file() in client_read() and rework open_dir() to just use
openat() instead of the complicate dance it was doing.

Simplify open_dir() too in the process: if the directory entry for the
index is not a regular file, pretend it doesn't exist.
2023-07-01 14:11:18 +00:00
Omar Polo 2339a71178 use a function-local buffer for the canonical redirect 2023-07-01 13:13:04 +00:00
Omar Polo fef06f06ac remove the fcgi debug code 2023-07-01 13:07:18 +00:00
Omar Polo 911156fb95 make `listen on' defaults on port 1965 2023-06-29 15:01:16 +00:00
Omar Polo c9e878d6a4 use snprintf() instead of chain of strlcpy/cat 2023-06-26 10:17:43 +00:00
Omar Polo ed164e7221 call getnameinfo() only once per request 2023-06-26 09:44:46 +00:00
Omar Polo da0821b6cb avoid gratious strlen; evbuffer_readln returns the length 2023-06-24 20:19:33 +00:00
Omar Polo a452496a96 rework check 2023-06-24 19:42:31 +00:00
Omar Polo 287ab86538 default chroot to user' home if unset 2023-06-24 19:42:31 +00:00
Omar Polo 0fc92ad2fd update changelog 2023-06-24 14:25:12 +00:00
Omar Polo 841633cfec plug memory leak in client_close_ev 2023-06-24 14:22:12 +00:00
Omar Polo ddf7a437de fix client_close_ev when tls_close() returns TLS_WANT_POLLIN/OUT
in those cases we need to reschedule the function and return, instead
of going on with the cleanup.
2023-06-24 14:21:57 +00:00
Omar Polo c5edb15740 properly handle handshake failures
If a TLS handshake fails there's nothing we can do, so don't attempt
to reply an error (the connected client is not speaking Gemini as
it's not using TLS at all) and instead just close the connection.

Fixes issue #13
2023-06-24 14:15:57 +00:00
Omar Polo 53bdae38fe update changelog 2023-06-24 13:30:13 +00:00
Omar Polo 23f0ac49ed fix previous 2023-06-24 13:20:51 +00:00
Omar Polo 6a60134c64 mention gg and ge 2023-06-24 13:19:26 +00:00
Omar Polo fea6a85623 update the README after recent developments 2023-06-24 13:17:44 +00:00
Omar Polo 24f644dbb6 there's no more any `static' target 2023-06-24 13:10:31 +00:00
Omar Polo 3a877237a7 gg.1: improve -d description 2023-06-24 13:04:40 +00:00
Omar Polo 7edcf2b341 update gmid.conf.5: drop old rules and add news
- minor improvements to the wording - drop the removed global options
ipv6 and port - remove config-less mention - document `listen on'
- update examples

While `ipv6' and `port' are still supported for backward compatibility,
it's better to not document them anymore.
2023-06-24 13:04:15 +00:00
Omar Polo b3010dbbaf update gmid.8
- it doesn't run anymore without a config file - a reload is enough
to re-open the directories.

This last point in particular was done this way to allow using
capsicum(4) on FreeBSD, something that is currently impossible.  I
may just remove it.
2023-06-24 13:02:44 +00:00
Omar Polo c3d502d455 add a `lint' maintainer target to check the manpages 2023-06-24 13:01:31 +00:00
Omar Polo eac9287d29 copyright years++ 2023-06-24 10:07:17 +00:00
Omar Polo df6282815f remove unused global flag 2023-06-24 10:03:03 +00:00
Omar Polo aa30aaedc8 don't match host if connecting from the wrong socket
limit how one given virtual host can be reached based on its `listen
on' lists
2023-06-24 10:02:46 +00:00
Omar Polo 35dd3fc8ce typo 2023-06-24 09:51:05 +00:00
Omar Polo e50f85adcb load the certs per listening address 2023-06-24 09:50:30 +00:00
Omar Polo a0a42860d2 send host addresses to the server process 2023-06-24 09:14:35 +00:00
Omar Polo 9fda962861 better fd rampage avoidance
flush imsg right in config_send_file()
2023-06-24 09:04:21 +00:00
Omar Polo 2c0716fd7d hopefully fix the macos ci 2023-06-23 22:40:58 +00:00
Omar Polo 2d0a2b21f5 add missing include of ../config.h in regress/*.c
otherwise we get a nice 'no previous prototype' due to
-Wmissing-prototypes.
2023-06-23 22:40:09 +00:00
Omar Polo 5134078414 macos' clang is retarded
thinks rsa and ecdsa may be used un-initialized... if we enter the
branch with fatalx().

sigh
2023-06-23 22:39:37 +00:00
Omar Polo 3d0204e7ef don't quote $5 when calling pkg-config
otherwise we fail the openssl test 'libcrypto libssl'
2023-06-23 22:38:10 +00:00
Omar Polo 5a34572282 use REGRESS_HOST to specify the host to listen to; use in CI
some CI envs don't like `listen on localhost' but tolerate INADDR_ANY
or IN6ADDR_ANY_INIT.
2023-06-23 22:00:21 +00:00
Omar Polo a7a998ac9b fix `listen on *' 2023-06-23 21:59:11 +00:00
Omar Polo 509d0509a5 implement `listen on'
Listening by default on all the addresses is so bad I don't know
why I haven't changed this before.  Anyway.

Add a `listen on $hostname port $port' syntax to the config file
and deprecate the old "port" and "ipv6" global setting.  Still try
to honour them when no "listen on" directive is used for backward
compatibily, but this will go away in the next next version hopefully.

At the moment the `listen on' in server context don't filter the
host, i.e. one can still reach a host from a address not specified
in the corresponding `liste on', this will be added later.
2023-06-23 21:03:29 +00:00
Omar Polo 37df23d183 rename client->addr to raddr (remote address) and keep original length 2023-06-23 21:03:29 +00:00
Omar Polo ab55c7815e typo; use the `l' variable not `len' 2023-06-23 21:03:24 +00:00
Omar Polo f29d705e04 add missing -include of *.d files 2023-06-23 21:03:24 +00:00
Omar Polo abc599e031 drop debug log 2023-06-23 21:03:24 +00:00
Omar Polo 55b74a9691 remove the new_*() declarations that were moved to utils.c 2023-06-23 21:03:24 +00:00
Omar Polo fc2d207c79 use host->domain to report errors, $2 is free'd 2023-06-23 16:23:59 +00:00
Omar Polo c5ded53a8e sort pledge promises as per pledge(2) 2023-06-23 15:52:20 +00:00
Omar Polo b692d8bd5b drop `proc' pledge in the main process
unlike the name might suggest, proc_kill() doesn't use kill(2) so
proc is not needed.
2023-06-23 15:52:04 +00:00
Omar Polo 5dad390015 add `release' target 2023-06-14 07:15:00 +00:00
Omar Polo 1959cda3d8 more avoiding of void pointer arithmetics
This time with a temporary variable to avoid not to trigger
-Wpointer-sign, sigh.
2023-06-13 17:36:42 +00:00
Omar Polo b90faa1605 simplify check
brought to my attention by gcc who isn't smart enough to figure out
that `ret' is always set.
2023-06-13 17:10:13 +00:00
Omar Polo cf5cf697a8 enable -Werror on CI
-Wno-deprecated-declarations is needed because of OpenSSL 3 (and macos
stupidly deprecating daemon(3) in favour of that trash of posix_spawn.)
2023-06-13 16:41:39 +00:00
Omar Polo 7604fc903a drop questionable #warning 2023-06-13 16:39:32 +00:00
Omar Polo 1610f9541d rework the configure script
now it resembles less oconfigure and more the configure scripts I'm
using in my recent projects.  I'd argue it's more easy to use it.
2023-06-13 16:36:10 +00:00
Omar Polo 10cc819309 avoid arithmetic on void pointers (GNU extension)
not really sold on this one, I don't see what other interpretation could
be given, but it's not standard so...
2023-06-13 16:36:06 +00:00
Omar Polo d6d9f9b2a9 add mac_task with disabled runtime tests for the time being
the runtime tests fails on the ci (gg: Connection refused); will be
revisited after we get a real `listen on' directive.
2023-06-13 11:02:51 +00:00
Omar Polo 8af9da9843 fix the build with some yacc implementations 2023-06-13 11:00:07 +00:00
Omar Polo 611dffe816 remove regress/sha
we can use cmp to tell if two files are different, which also has
the benefit of being available everywhere and reporting the byte
offset of the first difference.  Reduces the test dependencies on
some systems.
2023-06-13 10:59:50 +00:00
Omar Polo 1b9031f1fc work around missing SOCK_NONBLOCK/CLOEXEC on macos 2023-06-13 10:59:46 +00:00
Omar Polo 94893746ae use the default prefork in tests 2023-06-12 21:28:28 +00:00
Omar Polo 2cef5cf42a load_ca: get a buffer instead of a fd
We dup(1) the ca fd and send it to various processes, so they fail
loading it.  Instead, use load_file to get a buffer with the file
content and pass that to load_ca which then loads via BIO.
2023-06-12 21:27:24 +00:00
Omar Polo 89cfcb4569 simplify config_send_kp: use config_send_file 2023-06-12 21:09:49 +00:00
Omar Polo 5d160453f2 remove proc_ispeer()
unused, and was dropped by other copies of proc.c; reduces the diff
with httpd' proc.c.
2023-06-11 12:45:42 +00:00
Omar Polo ba290ef3af disable the privsep crypto engine on !OpenBSD
it fails bandly at runtime on various linux distros and on freebsd.
Until a fix is found, disable it so I can move forward.
2023-06-11 12:18:27 +00:00
Omar Polo 237095fd9a remove has_siginfo
and wrap siginfo behind #ifdef SIGINFO.  avoids some warnings in !BSD.
2023-06-11 11:36:31 +00:00
Omar Polo 1ef09e6313 add -Wpointer-sign to the mix
It's not present in -W -Wall -Wextra on OpenBSD but it is enabled
on other systems.
2023-06-11 11:33:38 +00:00
Omar Polo b8d68fc8e4 fixes for -Wpointer-sign 2023-06-11 11:31:22 +00:00
Omar Polo d1739e3f03 cast uint64_t to unsigned long long 2023-06-11 11:31:06 +00:00
Omar Polo ec96a0ad3b work around different signature for ecdsae_compute_key 2023-06-11 11:30:20 +00:00
Omar Polo 86693a33ab add a privsep crypto engine
Incorporate the OpenSMTPD' privsep crypto engine.  The idea behind
it is to never load the certificate' private keys in a networked
process, instead they are loaded in a separate process (the `crypto'
one) which signs payloads on the behalf of the server processes.
This way, we greatly reduce the risk of leaking the certificate'
private key should the server process be compromised.

This currently compiles only on LibreSSL (portable fix is in the
way).
2023-06-11 11:03:59 +00:00
Omar Polo f81a97b356 drop useless debug statement 2023-06-11 09:49:01 +00:00
Omar Polo 725457a9e4 move setproctitle/privsep_process earlier
We don't always do privilege dropping (as we may start as unprivileged
user), so set these two beforehand so when we skip privdrop we don't
miss to set privsep_process and set the process' title.
2023-06-11 09:26:16 +00:00
Omar Polo 4ad573d0d5 rework load_file to use pread()
avoids issues since the same file is sent to multiple processes
after being dup()'ed.  Since these files are meant to be regular
files, I don't expect short reads.
2023-06-11 09:21:34 +00:00
Omar Polo 1a99859b35 adjust how locations are received 2023-06-11 09:19:42 +00:00
Omar Polo 15e60fdf0c simplify ocsp sending using config_send_file
while here add an explicit flush to avoid a fd rampage.
2023-06-11 09:18:30 +00:00
Omar Polo 2e880a57f8 change config_send_file to take the process id as argument
i.e. not hardcode PROC_SERVER
2023-06-10 11:03:29 +00:00
Omar Polo 892f3a5cf8 gencert: use secp384r1
prime256v1 should be perfectly fine for all I understand, but
OpenBSD' acme-client uses secp384r1 and who am I to disagree :)
2023-06-09 20:43:12 +00:00
Omar Polo 7fff8aa6cb parse the config file only once
Don't have all the processes read gmid.conf.  The parent needs to do
that, and the will send the config to the children (already
happening.)  The other processes were reading the config anyway to
figure out the user and the chroot (if enabled); make the parent pass
additional flag to propagate that info.

We dissociate a bit from the "usual" proc.c but it's a change worth
having.
2023-06-09 17:50:28 +00:00
Omar Polo 5af19830c3 move print_conf and make it take the config as argument 2023-06-09 17:29:52 +00:00
Omar Polo 792f302ace use fatal/fatalx instead of err/errx in daemon code 2023-06-09 17:27:41 +00:00
Omar Polo 68368f4c29 parse_conf: don't die on error, return -1
this avoids having the daemon dieing on SIGHUP with a bad config
file.
2023-06-09 17:24:37 +00:00
Omar Polo af1dab1870 don't have the config being a global 2023-06-09 17:18:04 +00:00
Omar Polo e45334e6ae move hosts into the config struct 2023-06-09 16:54:04 +00:00
Omar Polo d273c0648d ignore and clean fcgi.sock 2023-06-09 10:51:24 +00:00
Omar Polo fe7cdaa479 fcgi-test: be less verbose 2023-06-09 10:47:20 +00:00
Omar Polo 9adeb26579 re-establish fastcgi test 2023-06-09 10:46:50 +00:00
Omar Polo 5d22294a59 move fastcgi from global var to the config struct
while here also make them a list rather than a fixed-size array.
2023-06-09 10:42:36 +00:00
Omar Polo 1962764c62 fix sandbox_server_process
it does the unveil(2)ing based on the first config, which breaks
config-reloading.
2023-06-09 10:40:08 +00:00
Omar Polo cd1ede6dd3 rework fcgi-test so that it binds a local socket
still not re-enabled.
2023-06-09 10:39:05 +00:00
Omar Polo deadd9e131 readd proxy certs and `require client ca' support
Was temporarly disabled during the transition to real privsep.
While here, fix a memory leak when using `require client ca'.

Also, avoid leaking info about the parent address space layout to
server processes by not sending pointer values.
2023-06-09 09:28:26 +00:00
Omar Polo c144b1b6f8 configure: look for WAIT_ANY 2023-06-08 19:46:06 +00:00
Omar Polo 309dab3a90 fix typo 2023-06-08 19:41:38 +00:00
Omar Polo fc440833ad provide sandbox_main_process on !OpenBSD 2023-06-08 19:41:25 +00:00
Omar Polo 9b89eaeb55 fix build of proc.c on !OpenBSD 2023-06-08 19:41:00 +00:00
Omar Polo 9b2587bb33 safety measure, explicitly memset config in config_init 2023-06-08 19:35:05 +00:00
Omar Polo 1c6967b33a keep cert/key/ocsp path as strings and don't send them via imsg 2023-06-08 19:34:49 +00:00
Omar Polo 49bd46a150 fix ge build 2023-06-08 19:30:26 +00:00
Omar Polo 8eeb992206 less logger.h 2023-06-08 19:30:10 +00:00
Omar Polo f5c8360ade fix previous 2023-06-08 19:24:37 +00:00
Omar Polo ca84625a7f remove foreground / verbose from config
set them as global vars; rename foreground -> debug
2023-06-08 17:29:08 +00:00
Omar Polo 85a575a444 remove forgotten include of logger.h 2023-06-08 17:28:33 +00:00
Omar Polo cbb7f9fc28 move logger() prototype to gmid.h and delete logger.h 2023-06-08 17:03:13 +00:00
Omar Polo 797c4609a9 make ge work again 2023-06-08 16:22:03 +00:00
Omar Polo 3886afceec make server_init and server_configure_done 'public'
server_configure_done is the code we ran in IMSG_RECONF_END splitted
in a separate functions.

This is all needed for ge.c which doesn't do privsep but needs to
bootstrap the server process.
2023-06-08 16:21:31 +00:00
Omar Polo 47b0ff105a move log_request to gmid.c
so that ge can provide its own log_request without requiring a
separate logger process.
2023-06-08 16:16:14 +00:00
Omar Polo 4f4937f06a move make_socket to config.c and make it private 2023-06-08 16:07:01 +00:00
Omar Polo fc9cc497e0 move some new_* functions from parse.y to utils.c 2023-06-08 15:59:53 +00:00
Omar Polo e69e1151f6 drop now unused dispatch_imsg 2023-06-08 15:57:11 +00:00
Omar Polo 2b4ef796d7 remove debug code 2023-06-08 15:47:03 +00:00
Omar Polo 61febd28af remove now unused ibuf variable 2023-06-08 15:44:34 +00:00
Omar Polo 846842e138 sync DISTFILES 2023-06-08 15:41:47 +00:00
Omar Polo 68e38f49b2 use -MMD if the compiler supports it
it's better than the previous Makefile.depend approach since this
automatically adapts to the included headers without requiring
manual intervention to regen the list.
2023-06-08 14:43:29 +00:00
Omar Polo 3483609593 remove Makefile.depend 2023-06-08 14:37:29 +00:00
Omar Polo c727f8dd75 reformat 2023-06-08 14:36:29 +00:00
Omar Polo c26f2460e4 rework the daemon to do fork+exec
It uses the 'common' proc.c from various OpenBSD-daemons.

gmid grew organically bit by bit and it was also the first place where I
tried to implement privsep.  It wasn't done very well, in fact the
parent process (that retains root privileges) just fork()s a generation
of servers, all sharing *exactly* the same address space.  No good!

Now, we fork() and re-exec() ourselves, so that each process has a fresh
address space.

Some features (require client ca for example) are temporarly disabled,
will be fixed in subsequent commits.  The "ge" program is also
temporarly disabled as it needs tweaks to do privsep too.
2023-06-08 13:59:31 +00:00
Omar Polo 99f1fbb0c7 regress: use -P with an absolute path 2023-06-08 13:56:09 +00:00
Omar Polo f1f13cb7dc absolutify the path to the pid file 2023-06-08 13:56:09 +00:00
Omar Polo 9b1750057c add missing include of gmid.h 2023-06-06 11:57:33 +00:00
Omar Polo 2dd5994ae1 use fatal() in code used in the daemon 2023-06-06 11:52:43 +00:00
Omar Polo bc525c73db fix asprintf failure check 2023-06-06 11:48:02 +00:00
Omar Polo eae52ad493 switch to the more usual log.c 2023-06-06 11:46:40 +00:00
Omar Polo 58fae4ea90 use memchr instead of rolling a custom one 2023-06-06 10:46:44 +00:00
Omar Polo 281a8852b3 rename log.[ch] to logger.[ch] 2023-06-06 08:50:54 +00:00
Omar Polo 3dd89fbb44 predeclare struct client 2023-06-06 08:34:54 +00:00
Omar Polo 3a8c76eab2 rename PROC_MAX to PREFORK_MAX 2023-06-06 08:34:31 +00:00
Omar Polo 4267093e37 nitpick: fix snprintf check 2023-06-06 08:22:18 +00:00
Omar Polo f057c92622 adjust comments 2023-06-06 08:20:09 +00:00
Omar Polo c68baad22a move config-related code to config.c
reuse it in ge too.
2023-06-06 08:18:57 +00:00
Omar Polo 0046c1fe9c call setlocale() 2023-06-06 08:11:30 +00:00
Omar Polo bb595bff79 remove unused define 2023-06-05 21:11:40 +00:00
Omar Polo 070b32952c move and dedup the tls initalization in server.c 2023-06-05 21:10:18 +00:00
Omar Polo 114e9a4206 fix off-by-one in recent fatal change 2023-06-05 20:45:28 +00:00
Omar Polo 0ac785a6fa revert aae8f6bf2b
it's just not worth it to inflict this breaking change to the users.
2023-06-05 20:35:12 +00:00
Omar Polo e1e04caa4f +log.h 2023-06-05 20:27:27 +00:00
Omar Polo df5058c919 provide a more usual fatal
fatal usually appends the error string.  Add 'fatalx' that doesn't.
Fix callers and move the prototypes to log.h
2023-06-05 17:07:52 +00:00
Omar Polo a01a91db06 move some server-related code to server.c 2023-06-05 16:18:57 +00:00
Omar Polo 9a821f8c0f fold long lines 2023-06-05 15:07:24 +00:00
Omar Polo ac9f55ba32 gencert: add -e flag to generate a cert using an EC key 2023-06-05 14:35:23 +00:00
Omar Polo 34886b1e55 add tags target 2023-05-08 10:30:00 +00:00
Omar Polo 1e0b974519 send capsicum/landlock/seccomp hack to Valhalla 2023-05-08 10:27:32 +00:00
Omar Polo 0b62f4842d drop landlock/seccomp and capsicum support
it reached a point where this stuff is not maintenable.  I'd like
to move forward with gmid, but the restriction of capsicum and the
linux environment at large that make landlock unusable (how can you
resolve DNS portably when under landlock?) -and don't get me started
on seccomp- makes it impossible for me to do any work.

So, I prefer removing the crap, resuming working on gmid by cleaning
stuff and consolidating the features, improving various things
etc... and then eventually see how to introduce some sandboxing
again on other systems.  Patches to resume sandboxing are, as always,
welcome!
2023-05-08 10:27:32 +00:00
Anna “CyberTailor” c9e97a6ecb include grp.h for setgroup(2) on linux 2022-12-24 08:41:09 +00:00
Omar Polo 837156014c add a disclaimer 2022-12-02 15:37:49 +00:00
Omar Polo 195f32d3a4 update the site for 1.8.6 2022-12-02 15:21:20 +00:00
Omar Polo bd8683d0fd add tests and compat for setresuid and setresgid 2022-12-02 11:53:35 +00:00
Omar Polo 06035a0237 more is*() unsigned char cast
continuation of 6130e0eeac
2022-11-29 23:03:55 +00:00
Omar Polo 97b306cbee add an implicit fastcgi parameter: GEMINI_SEARCH_STRING
it’s the QUERY_STRING decoded if it’s a search-string (i.e. not a
key-value pair.)  It’s useful for scripts to avoid percent-decoding
the querystring in the most common case of a query, because in Gemini
querystrings key-value paired are not common.

Idea from a discussion with Allen Sobot.
2022-11-27 15:35:10 +00:00
Omar Polo 77718c121f correction: QUERY_STRING is *not* urldecoded.
RFC3875 § 4.1.7 states that "the QUERY_STRING variable contains a
URL-encoded search or parameter string".
2022-11-27 12:52:17 +00:00
Omar Polo 17493a486c return after FCGI_END_REQUEST
this fixes a possible crash if `client_write' closes the connection,
because client_close can end up freeing the fastcgi bufferevent while
we're looping.

We don't support fastcgi multiplexing, so once we get an END_REQUEST
there's nothing more to do.

Prodded into looking here after a bug report from Allen Sobot, thanks!
2022-11-27 10:34:30 +00:00
Omar Polo eb4f96c10a typo 2022-11-27 10:06:08 +00:00
Omar Polo e92efb0d8e don't crash when specifying fcgi UNIX sockets to connect to 2022-11-27 10:05:56 +00:00
Omar Polo 872a717687 when switching user also set the groups 2022-11-27 10:05:13 +00:00
Omar Polo b24c6fcc1c adjust pledge/unveil on OpenBSD
to connect to unix-domain sockets the `unix' pledge is needed and also
unveil "w".  gmid can't mutate files because it doesn't pledge `wpath'
nor `cpath'.
2022-11-27 10:04:39 +00:00
Omar Polo 6130e0eeac always cast is*() arguments to unsigned char 2022-11-17 09:21:38 +00:00
Omar Polo 71cac3a08b fix git url 2022-11-01 07:28:20 +00:00
Omar Polo 8295757f96 update the site for 1.8.5 release 2022-11-01 07:28:10 +00:00
Omar Polo 0ab57224fb bump version ahead of 1.8 branch 2022-10-31 22:53:16 +00:00
Omar Polo a4180f1d0b disable test_unknown_host temporarly
breaks on some distro and needs further investigations; it's not that
interesting fortunately.
2022-10-31 22:53:16 +00:00
Omar Polo 19a8d9fe74 add memmem compat 2022-10-31 22:53:16 +00:00
Omar Polo 4b93be289b rework `make dist' 2022-10-31 22:53:16 +00:00
Omar Polo 21cf735f37 remove -v from gg
undocumented flag to dump to stdout the request before doing it.  Not
useful, it's debugging leftover.
2022-10-30 08:26:22 +00:00
Omar Polo e59b7f30e7 fix previous 2022-10-30 07:43:18 +00:00
Omar Polo 8141737318 work around missing HOST_NAME_MAX too 2022-10-30 07:40:27 +00:00
Omar Polo fad3441ba9 work around missing LOGIN_NAME_MAX
Both Linux and OpenBSD have LOGIN_NAME_MAX available when including
limits.h, FreeBSD, Darwin and possibly others don't.

FreeBSD (and maybe Darwin) have MAXLOGNAME, so try to use that if
available.  Otherwise use _POSIX_LOGIN_NAME_MAX, but only has a fallback
since it has a lower value (9 at the time of writing).

If everything fails, use 32 which is what OpenBSD use by default;
OpenSMTPd also defaults to it.

(compat copied from kamid.)
2022-10-30 07:30:23 +00:00
nytpu 50a8f9107c always send custom list of fcgi parameters
The code in fcgi_req to send the custom params set in the config file was
placed inside the conditional for `tls_peer_cert_provided`, so the custom
parameters would not be sent if a client certificate is not provided.
2022-10-30 08:21:39 +01:00
Omar Polo 7b00c8900b remove the last tentacles of the hidden `span' fcgi feature 2022-10-05 15:30:22 +00:00
Omar Polo d2da235ad9 sync config syntax with reality 2022-10-05 15:30:02 +00:00
Omar Polo 534afd0ddc make the various strings in the config fixed-length
will help in future restructuring to have fixed-size objects.
2022-10-05 15:10:44 +00:00
Omar Polo 4ceb570910 remove stale comment 2022-10-05 10:53:43 +00:00
Omar Polo 9715efe6f3 retire fcgi' prog field
spawning programs was a hidden feature used only for testing.  It's
gross and when got removed, I forgot to remove the field as well.
2022-10-05 10:41:54 +00:00
Omar Polo c5b4db930e specify custom version strings for the various cmds 2022-09-10 14:29:40 +00:00
Omar Polo 71ddfd2023 fmt 2022-09-10 13:54:09 +00:00
Omar Polo 83c62ff950 sync 2022-09-10 13:31:37 +00:00
Omar Polo 5c4855299c remove remnats tentacles of the executor process 2022-09-10 13:28:19 +00:00
Omar Polo d040746a37 server: inline dispatch_imsg 2022-09-10 13:18:24 +00:00
Omar Polo 24232204eb update depends 2022-09-10 12:24:39 +00:00
Omar Polo 934f957f29 remove unused entrypoint field
ventige of the cgi support (and lack of url rewriting)
2022-09-10 10:22:24 +00:00
Omar Polo 7bb80ca90d don't count twice the failing tests on gmid crash 2022-09-10 10:12:50 +00:00
Omar Polo edc5ca667d properly initialize the TAILQs at vhost creation 2022-09-10 10:12:37 +00:00
Omar Polo 2025e96d97 drop cgi vestiges from the struct host
The `env' list is no longer used since CGI scripts were removed
2022-09-10 09:48:30 +00:00
Omar Polo cd5826b8ba retire the deprecated `mime' and `map' config options 2022-09-10 09:43:57 +00:00
Omar Polo aa9543b9fd make the mime types fixed-sized too 2022-09-10 09:40:05 +00:00
Omar Polo 7277bb7dc2 make config fields `chroot' and `user' fixed-size 2022-09-10 09:21:09 +00:00
Omar Polo aae8f6bf2b change the flags to be consistent with other OpenBSD daemons
-d is `debug' (run in the foreground)
 -f to load the configuration file

adjust regress and contrib accordingly
2022-09-08 21:34:29 +00:00
Omar Polo 3eabd37fe7 adjust install/uninstall target
gmid.1 was moved as gmid.8 and now we have `ge' too
2022-09-08 21:18:04 +00:00
Omar Polo 9b15e4c628 move gmid to the eight section of the manual 2022-09-08 21:15:02 +00:00
Omar Polo 7f03b52bd6 shim for __dead 2022-09-08 20:51:13 +00:00
Omar Polo f7c6f7155a sync 2022-09-08 20:45:19 +00:00
Omar Polo a5fb2593a9 adjust regress to use `ge' for the old configless test 2022-09-08 20:45:12 +00:00
Omar Polo 32fbc47803 drop the configless mode from gmid; now it's provided by `ge' 2022-09-08 20:44:35 +00:00
Omar Polo 0126d91d1d add ge: gemini export! 2022-09-07 20:47:33 +00:00
Omar Polo 7600099513 optionally disable the sandbox on some systems
The FreeBSD and Linux' sandbox can't deal with `fastcgi' and `proxy'
configuration rules: new sockets needs to be opened and it's either
impossible (the former) or a huge pain in the arse (the latter).

The sandbox is still always used in case only static files are served.
2022-09-06 16:40:38 +00:00
Omar Polo 36e6e793a1 gc FILE_EXECUTABLE 2022-09-06 16:25:10 +00:00
Omar Polo 1ab7c96bb3 gc sandbox_executor_process 2022-09-06 16:24:45 +00:00
Omar Polo d29a2ee224 get rid of the CGI support
I really want to get rid of the `executor' process hack for CGI scripts
and its escalation to allow fastcgi and proxying to work on non-OpenBSD.

This drops the CGI support and the `executor' process entirely and is
the first step towards gmid 2.0.  It also allows to have more secure
defaults.

On non-OpenBSD systems this means that the sandbox will be deactivated
as soon as fastcgi or proxying are used: you can't open sockets under
FreeBSD' capsicum(4) and I don't want to go thru the pain of making it
work under linux' seccomp/landlock.  Patches are always welcome however.

For folks using CGI scripts (hey, I'm one of you!) not all hope is lost:
fcgiwrap or OpenBSD' slowcgi(8) are ways to run CGI scripts as they were
FastCGI applications.

fixes for the documentation and to the non-OpenBSD sandboxes will
follow.
2022-09-06 16:11:09 +00:00
Omar Polo 5df699d1ab count ran/failed tests number 2022-08-29 16:44:07 +00:00
Omar Polo 39ad4b8d7f sync 2022-07-07 18:19:34 +00:00
Omar Polo bd2eb09520 call tzset(3) to fix times in logs
reported by Karl Jeacle, thanks!
2022-07-07 18:15:06 +00:00
Omar Polo 93124fce99 mention Anna' patch too 2022-07-07 08:57:28 +00:00
Omar Polo 9ac625870e mention the removal of the rc file 2022-07-07 08:56:23 +00:00
Omar Polo ddd291dd7c mention the 1.8.4 release 2022-07-07 08:55:32 +00:00
Omar Polo de8dc03c4d sync 2022-07-07 08:54:26 +00:00
Omar Polo be97b57fc8 don't set d_reclen
It's not actually used and doesn't seem to be available on DragonflyBSD
(at least) judging from the patching in dports:

f66bf875f7
2022-07-07 08:54:26 +00:00
Omar Polo 2573332d7f don't hardcode urls; use TREE 'macro' 2022-07-07 08:54:26 +00:00
Anna “CyberTailor” 07fbbf0610 include contrib/ in dist 2022-07-07 10:38:49 +02:00
Omar Polo 3bba4fc55f remove the OpenBSD' rc file
now that gmid is packaged on OpenBSD the rc file is maintained in the
ports tree.
2022-07-04 19:22:08 +00:00
Omar Polo f759119d2f bump version 2022-07-04 19:18:39 +00:00
Omar Polo f3f4543119 don't hardcode version numbers 2022-07-04 19:18:30 +00:00
Omar Polo b48eb0db52 add missing manpage for gg 2022-07-04 15:04:46 +00:00
Omar Polo 1f6de7491c changes for 1.8.4 2022-07-04 14:51:39 +00:00
Omar Polo 1670b5f3ae mention the codeberg mirror too 2022-07-04 14:48:18 +00:00
Omar Polo c6fb44edd8 remove mentions of the precompiled binaries 2022-07-04 14:48:10 +00:00
Omar Polo d45d5306e3 fix test_auto_index 2022-07-04 14:33:45 +00:00
Omar Polo 0d823f229d sync 2022-07-04 14:31:20 +00:00
Omar Polo 543f4a66fe add a trailing / for dirs in the directory index. 2022-07-04 14:31:17 +00:00
Omar Polo 5734723f92 typo 2022-07-04 14:09:52 +00:00
Omar Polo 870063d344 include Makefile.depend too 2022-07-04 13:53:51 +00:00
Omar Polo 7fc52d5104 add an example configure.local based on mandoc-portable' one 2022-07-04 13:44:53 +00:00
Omar Polo 96e3c01314 refactor the makefile / configure
steal more (good) stuff from mandoc-portable :)
2022-07-04 13:44:38 +00:00
Omar Polo 225686d7fd fix getprogname shim
HAVE_PROGRAM_INVOCATION_SHORT_NAME is always defined; look at wether
it's zero or not.
2022-07-04 13:43:27 +00:00
Omar Polo a3ebbccb8e the install target ought to depend on gg too 2022-07-04 09:52:44 +00:00
Omar Polo a555e0d67b copyright years 2022-07-04 09:48:39 +00:00
Omar Polo cb2b85512c add proper copyrights
was a mistake on my part and a leftover from the first iterations.
Initially I only stole a few bits, that's why the comment but not the
copyright, but then i threwed out and restarted using oconfigure.
Then the configure script has its own independent changes, but need to
give attribution nevertheless.
2022-07-04 09:43:51 +00:00
Omar Polo 617ae38546 add some more regress for the encodings 2022-07-04 09:36:55 +00:00
Omar Polo 475205fa0f check for the expected page in test_auto_index
not just the number of lines, check the full page!
2022-07-04 09:36:31 +00:00
Omar Polo ddd3c5529f sync 2022-07-04 09:32:02 +00:00
Omar Polo f2f8eb35c8 encode file names in the directory index
Spotted the hard way by cage
2022-07-04 09:31:36 +00:00
Omar Polo 3bd4a6dea0 log when it fails to open a file because of permissions 2022-07-04 08:25:15 +00:00
Omar Polo df40c96931 changelog 2022-07-04 08:15:45 +00:00
Omar Polo 5e41063f1b bugfix: allow @ and : in paths
gmid would disallow the '@' and ':' characters in paths (unless
percent-encoded.)  Issue reported by freezr.
2022-07-04 08:15:39 +00:00
Omar Polo df9b379081 remove mentions of GPG, I stopped signing with it for a while already 2022-05-02 18:48:40 +00:00
Omar Polo db48d276a7 tweaks 2022-05-02 17:48:55 +00:00
Omar Polo 7b27af8388 log when the certificate was successfully generated 2022-04-12 17:40:58 +00:00
Omar Polo 2962200607 there are no aarch64 binaries currently 2022-04-08 16:40:18 +00:00
Omar Polo 48589d9de9 move the preview to github (temporarly) 2022-04-08 16:40:18 +00:00
Omar Polo 60d8851fec typos 2022-04-08 16:40:18 +00:00
Omar Polo bc70003f93 wording 2022-04-08 16:40:18 +00:00
Omar Polo 8addd12c1a don't need to re-exec ./configure && make 2022-04-08 16:36:25 +00:00
Omar Polo b6c093c99a bundle a copy of /usr/share/misc/mime.types 2022-04-08 15:50:53 +00:00
Omar Polo 5c4e8e97bd wording 2022-04-08 15:45:46 +00:00
Omar Polo 93cab6dc78 document the implicit mapping for text/gemini
and remove the explicit mapping from the documentation and test files.
2022-04-08 15:36:59 +00:00
Omar Polo 6d362b3556 revise changelog 2022-04-08 15:26:16 +00:00
Omar Polo 8fb0df4d79 hardcode the mapping text/gemini -> .gmi/.gemini
It's still possible to override this automatic mapping by just adding
an entry like

	application/octet-stream	gemini gmi

in the `types' block.
2022-04-08 15:25:50 +00:00
Omar Polo 99a95569a4 sync 2022-04-08 15:14:21 +00:00
Omar Polo 18bd83915e sort the MIME mappings and do a binary search to match 2022-04-08 15:14:09 +00:00
Omar Polo ca44613693 tweaks 2022-04-08 14:19:58 +00:00
Omar Polo 5eb842cd1f rework the 'running gmid as a daemon' example a bit 2022-04-08 14:08:33 +00:00
Omar Polo 5f03bf170f document the `types' behavior 2022-04-08 14:01:26 +00:00
Omar Polo 16c391a063 add an entry for text/gemini in the `types' example
if using `types' the internal listing is not used, so show how to add
the entries for text/gemini when using /usr/share/misc/mime.types.
2022-04-08 13:59:20 +00:00
Omar Polo fde8750859 fix test_include_mime after `types' change 2022-04-08 13:52:48 +00:00
Omar Polo 54203115cd don't load the built-in list when using `types' 2022-04-08 13:52:35 +00:00
Omar Polo 9b64995359 wrong email... 2022-04-08 13:45:33 +00:00
Omar Polo 1d6cbdd0df sync 2022-04-08 13:45:26 +00:00
Omar Polo d8d170aa5e allow add_mime to fail
add_mime nows allocate dinamically copies of the passed strings, so
that we can actually free what we parse from the config file.

This matters a lot especially with lengthy `types' block: strings that
reach the internal mapping are never free'd, so every manual addition
is leaked.
2022-04-08 13:44:49 +00:00
Omar Polo aa6b8cf8ac +jpeg 2022-04-08 13:33:05 +00:00
Omar Polo 72e7fbbcb7 master is now 1.8.3-current 2022-04-07 18:10:13 +00:00
Omar Polo 8036523fff add forgotten entries 2022-04-07 18:04:50 +00:00
Omar Polo f731e4a781 update changelog 2022-04-07 17:59:25 +00:00
Omar Polo 19243c768e link gmid.conf.5 in the capsule/website too 2022-04-07 17:56:27 +00:00
Omar Polo bf2850b938 install gmid.conf.5 too 2022-04-07 17:51:26 +00:00
Omar Polo 1595c277ef move the documentation of the config file into gmid.conf.5 2022-04-07 17:50:37 +00:00
Omar Polo 9bcc265f58 lowercase FAQ -- all the other links are in lowercase 2022-04-07 16:29:22 +00:00
Omar Polo ce264ddc58 rename the quickstart page to guide in the navbar
it occupies less horizontal space which avoids the wrapping on (some) mobiles
2022-04-07 16:27:57 +00:00
Omar Polo 445816c2cd add the FAQ page finally! 2022-04-07 16:27:16 +00:00
Omar Polo ffd92e638c tweak the quickstart guide a bit 2022-04-07 16:26:48 +00:00
Omar Polo 9448a01fdb use the types block instead of `map' in the tests too 2022-04-07 16:21:56 +00:00
Omar Polo 6468868fee print a deprecation message for the map rule 2022-04-07 16:15:55 +00:00
Omar Polo 4ee085938f don't mention the `map' rule anymore: `types' is way better 2022-04-07 16:12:58 +00:00
Omar Polo 381fccdc56 lower padding 2022-04-07 16:01:55 +00:00
Omar Polo a9885c6d6b remove outdated note 2022-04-07 12:31:00 +00:00
Omar Polo effbc069ba suggest to reload daemons too 2022-04-07 12:29:04 +00:00
Omar Polo 379d2608e9 suggest /etc/systemd/system instead of /lib/ 2022-04-07 12:27:55 +00:00
Omar Polo a9dc6fc606 rephrase dockerfile description 2022-04-07 12:27:26 +00:00
Omar Polo 456a4c6b6e add a "contributing" section 2022-03-29 12:25:18 +00:00
Omar Polo 2a7f69f4ee fix `make static': compile `gg' too! 2022-03-29 12:21:08 +00:00
Omar Polo 4252e62cad "a posix libc" can be left implicit 2022-03-29 12:13:25 +00:00
Omar Polo a68203f089 remove docker section; it's already showed off in the contrib page 2022-03-29 12:05:29 +00:00
Omar Polo f6a65aaef9 link the tar.bz2 too 2022-03-27 13:42:31 +00:00
Omar Polo efe75a7660 missing version bump in the site generator 2022-03-27 13:28:14 +00:00
Omar Polo 1a04137e18 tag 1.8.3 -- "Lightbulb Sun" bugfix release
gmid 1.8.3 "Lightbulb Sun" bugfix release
=========================================

Released March 27, 2022.

signify(1) pubkeys for this release:
RWTy3UJQzpxBUAymBwb2EGLLm0b3H/1n8hzhaC9HYFYzNuTavGt9QSwC


Bug Fixes
~~~~~~~~~

    * fix a possible out-of-bound access in the CGI handling.  It was
      introduced last October during a refactoring, but due to how
      many malloc(3) implementations works this hasn't been found
      until now.  Otto' malloc is more strict fortunately.
2022-03-27 12:59:57 +00:00
Omar Polo ea27eaaa83 fix an out-of-bound access in start_cgi
Long time ago, client->req was a static buffer so the memcpy was safe.
However, it's been since moved to a dynamically allocated string, so
it's very often smaller than sizeof(req.buf) (1024), hence the out of
bound access which results in a SIGSEGV very often on OpenBSD thanks to
Otto' malloc.

The situation with the iri parser, client->req and how the request is
forwarded to the other process needs to be improved: this is just a fix
to address the issue quickly, a better one would be to restructure the
iri parser APIs and rethink how the info is forwarded to the ex process.
2022-03-27 12:52:59 +00:00
Omar Polo 6084a9a5ba prefer sizeof(x) instead of datalen 2022-03-27 12:52:58 +00:00
Omar Polo 62a46b03c6 tag 1.8.2 -- "Lightbulb Sun" bugfix release
gmid 1.8.2 "Lightbulb Sun" bugfix release
=========================================

Released March 26, 2022.

signify(1) pubkeys for this release:
RWTy3UJQzpxBUAymBwb2EGLLm0b3H/1n8hzhaC9HYFYzNuTavGt9QSwC


Bug Fixes
~~~~~~~~~

    * fix a CGI timing issue: if a connection handled by a CGI scripts
      is interrupted with the right timing it causes the server
      process to exit with "fatal in client_by_id: invalid id X".


New Features
~~~~~~~~~~~~

    * add a new block `type { ... }' to define mime types mapping.


Improvements
~~~~~~~~~~~~

    * use shell built-in `command' instead of which(1), prodded by
      cage and Allen Sobot.

    * configure script: allow to set MANDIR from cmdline (Allen Sobot)

    * add systemd-sysusers sample file in contrib/ (Nakaya)

    * [linux/seccomp] allow fstatat64(2), llseek(2) and sigreturn(2),
      needed by glibc on armv7.  (Tobias Berger)

    * [linux/seccomp] tightens rules by allowing openat(2) only with
      the O_RDONLY flag.
2022-03-26 13:22:22 +00:00
Omar Polo da613aba4b bump Mdocdate 2022-03-26 13:22:13 +00:00
Omar Polo 57d2fca4b4 add target `test' alias for `regress' 2022-03-26 11:57:27 +00:00
Omar Polo 3fdc457c8d swap try_client_by_id with client_by_id
i.e. allow client_by_id to fail and return NULL.

Initially I thought it was a good idea to shut down a server process
if we receive an invalid client id as reply from one of our requests
to the executor process.  This turned out not to be correct since a
client can (read: will) disconnect in the delay beteewn we acknowledge
their request and the cgi script execution.

The fastcgi and proxy handler already handled this situation, so
they're unaffected.

This allows an attacker to make gmid unresponsible by just making
enough requests until they hit the right timing.
2022-03-26 11:32:26 +00:00
Omar Polo 409a2599b3 move to a different server 2022-03-26 11:18:02 +00:00
Omar Polo 91971201e5 break out if check_reply fails
it's not a problem when we have only one check_reply at then end,
since $? is kept across function boundaries, but when we have multiple
checks we need to quit on the first error.
2022-03-26 10:56:06 +00:00
Omar Polo 894e998423 sync imsg-buffer.c
original commit from eric@:

 change the barrier so that fd's are always passed and received with
 the first byte of the imsg they belong to.

 idea, tweaks and ok claudio@
2022-03-19 14:54:31 +00:00
Omar Polo c7949fd545 sync recallocarary.c
original commit from claudio@:

 Type-cast getpagesize() from int to size_t for the comparison with d.
 getpagesize() will only return positive numbers (there is no negative
 page size system) and it can not fail.
 Should fix some compiler warnings seen in -portable projects.
 OK otto@
2022-03-19 14:52:11 +00:00
Omar Polo e5d82d9472 const-ify some tables
matches found with

	% grep -R '=[	 ]*{' . | fgrep -v const
2022-03-19 11:02:42 +00:00
Omar Polo 9db5e7051e get rid of `which` in the configure too
should have been done already in 12fcba2; reminded by Allen Sobot,
thanks!
2022-03-18 16:31:05 +00:00
Omar Polo 5f966fbe41 configure: allow to set MANDIR from args
diff by Allen Sobot (chilledfrogs at disroot dot org), thanks!
2022-03-18 16:23:45 +00:00
Omar Polo a00e67f1bf mention gmid.sysusers in contrib together with gmid.service
related to github issue #14
2022-03-09 16:43:22 +00:00
Omar Polo efd54f4f8a add missing newline 2022-03-09 16:35:28 +00:00
Nakaya 3c04ffc096 add description about systemd-sysusers on quickstart 2022-03-09 17:33:33 +01:00
Nakaya bff125b842 add systemd-sysusers' config file 2022-03-09 17:33:33 +01:00
Omar Polo 12fcba2f80 use shell built-in `command' instead of which(1)
it's specified by POSIX AFAIK and requires less redirections.
2022-02-27 16:24:45 +00:00
Omar Polo cd3e28ffe4 fix email 2022-02-26 14:19:30 +00:00
Omar Polo 74994ae7e5 sync changelog 2022-02-26 14:19:24 +00:00
Omar Polo e0bf950dff document the type { ... } block 2022-02-26 14:13:46 +00:00
Omar Polo fb1212266f add tests for the type block 2022-02-26 14:02:45 +00:00
Omar Polo ee219d702e add type { ... } block to define mime types mapping
The `map' rule is powerful but quite annoying to use if you have/need
lots of entries (and clutters the configuration file too.)

The `type' block is blatantly stolen from httpd(8) and allows for a way
more nice usage:

	type {
		include "/usr/share/misc/mime.types"
	}

or even

	type {
		text/markdown		md markdown
		text/x-perl		pl pm
		# ...
	}
2022-02-26 14:00:20 +00:00
Omar Polo 88971f9a4e add missing token include to the list of tokens 2022-02-26 13:49:24 +00:00
Omar Polo d98ae929b2 don't log errno, it's always zero after libtls returns
The libevent error value is much more interesting!
see github issue #13
2022-02-19 18:11:05 +00:00
Omar Polo ed78e81b9c remove paragraph "locally installed libressl" + some tweaks
libtls is now widely available, it's at least on gentoo, arch, void,
alpine, fedora and debian sid; there's no need to show how to compile to
a locally installed one.
2022-02-18 09:09:23 +00:00
Omar Polo c273bc0e41 sync changelog 2022-02-13 16:21:37 +00:00
Omar Polo 4f0e893cd3 tightens seccomp filter: allow only openat(O_RDONLY)
be more strict and allow an openat only with the O_RDONLY flag.  This
is kind of redundant with landlock, but still good to have.  Landlock
is not yet widely available and won't kill the process upon policy
violation; furthermore, landlock can be disabled at boot time.

tested on GNU and musl libc on arch and alpine amd64.
2022-02-13 16:20:27 +00:00
Omar Polo 94c5f99ab0 sort syscalls in seccomp filter 2022-02-13 15:32:10 +00:00
Omar Polo 67347fb021 sync changelog 2022-02-13 15:29:20 +00:00
Tobias Berger d0e0be1e43 Allow Arch-Armv7 syscalls in sandbox.c 2022-02-13 15:29:33 +01:00
Omar Polo c6ae2561a0 update the site for the release 2022-02-10 23:14:32 +00:00
Omar Polo b4b432e78e sync changelog 2022-02-10 23:14:17 +00:00
Omar Polo f2dbf375a0 tag 1.8.1 -- "Lightbulb Sun" bugfix release
gmid 1.8.1 "Lightbulb Sun" bugfix release
=========================================

Released Feb 10, 2022.

signify(1) pubkeys for this release:
RWTy3UJQzpxBUAymBwb2EGLLm0b3H/1n8hzhaC9HYFYzNuTavGt9QSwC


Bug fixes
---------

    * fix landlock usage on linux: don't assume that access
      capabilities not listed are implicitly denied, because they are
      not.  Mickaël Salaün, the landlock author, found the same error
      on game of trees:

> In a nutshell, the ruleset's handled_access_fs is required for
> backward and forward compatibility (i.e. the kernel and user space may
> not know each other's supported restrictions), hence the need to be
> explicit about the denied-by-default access rights.

      In practice this affects only linux and only partially: thanks
      to the design of the daemon and the seccomp filter the effects
      of this mistake in handling landlock are fortunately limited.
      However, in theory at least, gmid could be for e.g. tricked into
      truncating existing files, so it's highly suggested to update.


Improvements
------------

All by Anna “CyberTailor”, thanks!

    * don't skip unit tests when SKIP_RUNTIME_TEST is set
    * add `gg' to the regress target dependencies
    * fix the "implicit declaration of asprintf" warning
    * sync vim syntax
2022-02-10 22:51:32 +00:00
Omar Polo 98c6f8de41 fix landlock usage
Mickaël Salaün, the landlock author, pointed out the same error on the
got implementation.  The assumption that not listed access
capabilities are implicitly denied is completely wrong:

> In a nutshell, the ruleset's handled_access_fs is required for
> backward and forward compatibility (i.e. the kernel and user space may
> not know each other's supported restrictions), hence the need to be
> explicit about the denied-by-default access rights.
2022-02-10 22:29:51 +00:00
Anna “CyberTailor” be88c5d657 sync vim syntax 2022-02-03 11:13:45 +01:00
Anna “CyberTailor” 1f1f381068 include gmid.h before other headers to get all the prototypes
> implicit declaration of function 'asprintf'; did you mean 'vsprintf'?
2022-02-03 11:13:45 +01:00
Anna “CyberTailor” e20755180f add gg to `make regress` dependencies 2022-02-03 11:13:44 +01:00
Anna “CyberTailor” c1a72dab7a typo 2022-02-03 11:13:43 +01:00
Anna “CyberTailor” 1ca7a0f3bf don't skip unit tests when SKIP_RUNTIME_TESTS is set
IRI and Punycode tests don't run gmid binary and can be safely executed.
2022-02-03 11:13:41 +01:00
Omar Polo f525aa55b8 start new release cycle 2022-02-03 09:57:51 +00:00
Omar Polo a14b9f779f prepare 1.8 release 2022-01-30 13:35:45 +00:00
Omar Polo de721c431c add changelog for 1.8 2022-01-30 13:35:45 +00:00
Omar Polo 135307cfb0 install gg too 2022-01-30 13:35:45 +00:00
Omar Polo d78dbe8db5 fix gg -P flag description: the `:' is in the optional part 2022-01-30 13:31:29 +00:00
Omar Polo 5266f50060 posixify lib.sh: use = instead of == for test(1) 2022-01-30 13:31:29 +00:00
Omar Polo 7ac42a178b typo 2022-01-30 13:05:13 +00:00
Omar Polo d23cd12c68 sync changelog 2022-01-30 13:05:13 +00:00
Omar Polo c586dfead7 rename MIME section to "MEDIA TYPES" 2022-01-30 10:15:10 +00:00
Omar Polo 1cdea97b6c allow using a custom hostname for SNI during proxying
add a `sni' option for the `proxy' block: the given name is used instead
of the one extracted by the `relay-to' rule.
2022-01-30 10:14:44 +00:00
Omar Polo 1b626eae83 define TREE to shorten some url a bit
for the time being keep the https:// url, gmnigit renames file without
extension to .txt (i.e. Dockerfile -> Dockerfile.txt) so linking via
gemini is broken :/
2022-01-28 13:34:15 +00:00
Omar Polo 151af94eba only memcpy into imsg->data if datalen is not zero
Passing a NULL pointer to memcpy is UB no matter if len is 0 (commit by
claudio@, backporting for gmid.)
2022-01-28 12:34:42 +00:00
Omar Polo 12a5e730e9 update gitignore for site/ 2022-01-28 12:34:29 +00:00
Omar Polo 4e8ca48c85 site: point out that OpenBSD' make is needed 2022-01-28 08:34:57 +00:00
Omar Polo 21dc379479 misc tweaks + changelog.gmi 2022-01-28 08:34:39 +00:00
Omar Polo ac42bb6c7f change how the site is built
copy the kamid/site "framework" here too; in other words: don't maintain
two copies of every page! \o/
2022-01-28 08:34:22 +00:00
Omar Polo 964686ce0b remove unused function 2022-01-27 10:57:45 +00:00
Omar Polo e89f473904 s/whole/all 2022-01-27 10:37:28 +00:00
Omar Polo e0f6dc646d improve proxy error path
properly release everything when during client_close if the request
was managed by a proxy.
2022-01-27 09:55:52 +00:00
Omar Polo d28bd963c2 always mark requests as done when their code is != 20 2022-01-27 09:54:48 +00:00
Omar Polo 89efa81bcc set REQUEST_PROXY only when proxy_init is successful 2022-01-27 09:53:43 +00:00
Omar Polo b9b77f5344 fix comment 2022-01-27 09:28:27 +00:00
Omar Polo b3602923d0 shuffle the code a bit to improve readability 2022-01-13 10:34:55 +00:00
Omar Polo febfcde82b fix and sync gg usage 2022-01-13 10:13:32 +00:00
Omar Polo e2534fd11f note who requested what 2022-01-06 18:13:14 +00:00
Omar Polo 901905e0cf bail out of client_read if we've already decide what to do
libevent2 can still somehowe call client_read even in code paths
that never enable reading from the evbuffer.  Can't reproduce on
the libevent in base on OpenBSD.  It's a bit ugly, but it's a small
workaround for something that otherwise *always* make gmid crash
when linked against libevent2.  (client_read works under the
assumption that c->host != NULL, matched_proxy crashes otherwise.)
2022-01-05 18:58:01 +00:00
Omar Polo 876a417023 tweak comment 2022-01-05 18:03:47 +00:00
Omar Polo c57dc42b31 tweak a sentence 2022-01-05 17:30:50 +00:00
Omar Polo 14cee92646 some Pa -> Ar; while here drop two `The' from the start of the sentence 2022-01-04 23:22:14 +00:00
Omar Polo 7c2374af2c document `require client ca' for proxies 2022-01-04 23:17:28 +00:00
Omar Polo d474a97922 add missing prototype 2022-01-04 23:15:13 +00:00
Omar Polo ba94a608a8 add `require client ca' for proxy blocks
refactor the code that calls validate_against_ca into an helper
function to reuse it in both apply_require_ca and (optionally) in
apply_reverse_proxy.
2022-01-04 23:14:34 +00:00
Omar Polo 280fd79b8f ignore .d too 2022-01-03 18:25:27 +00:00
Omar Polo 718c0d6c71 work around freebsd' printf
printf: Illegal option -i

this is why we can't have nice things, isn't it?
2022-01-03 18:11:51 +00:00
Omar Polo 7c956fefc2 track file dependency using -MMD if available
While here, move the SRCS variable to the configure and add the
-includes in Makefile.local; it de-clutters the Makefile a bit.
2022-01-03 18:05:54 +00:00
Omar Polo 613c827165 add `gg' to the list of things to clean 2022-01-03 17:12:07 +00:00
Omar Polo 8dfacf0242 remove TAGS and compile_flags.txt from the list of default actions 2022-01-03 17:11:45 +00:00
Omar Polo 92a9f41d3d add set_proxy to simplify testing + more checks in test_proxy_with_certs 2022-01-03 13:31:34 +00:00
Omar Polo 7b7def0a8f set prefork to 1 during regress 2022-01-03 13:31:34 +00:00
Omar Polo dd208aa2cf don't print `config OK' 2022-01-03 13:31:34 +00:00
Omar Polo 6effb78141 sync changelog 2022-01-02 16:38:59 +00:00
Omar Polo 5df593f3a6 oops, wrong email address 2022-01-02 16:37:26 +00:00
Omar Polo 207dc0f9f6 sync manpage 2022-01-02 16:37:07 +00:00
Omar Polo b7967bc1f6 proxy: allow multiple proxy blocks, matching options and validations
as a side effect the order of the content of a server block is relaxed:
options, location or proxy blocks can be put in any order.
2022-01-02 16:33:28 +00:00
Omar Polo e2f167afb3 bump copyright year :) 2022-01-02 15:50:14 +00:00
Omar Polo 8e7e7cacf2 fix regress when TESTS is more than one word 2022-01-02 13:54:54 +00:00
Omar Polo e19f21f5a6 parethesize `proxy', I'm about to change the syntax 2022-01-02 13:48:32 +00:00
Omar Polo ec5c5ced4a make clean: don't clean regress, add cleanall for that 2022-01-02 11:49:48 +00:00
Omar Polo 53884ec00a document use-tls 2022-01-01 20:18:08 +00:00
Omar Polo 593e412b49 allow to disable TLS when proxying requests 2022-01-01 20:16:14 +00:00
Omar Polo 294a57275a move %token USER to its own line 2022-01-01 19:51:53 +00:00
Omar Polo ee68c36198 document the new proxy stuff 2022-01-01 19:25:07 +00:00
Omar Polo 3c4b712bb2 plug some memory leaks in config parsing
I forgot to free some strings when they're no more used.
2022-01-01 19:10:00 +00:00
Omar Polo c7c8ef448b add `protocols' option to `proxy' rule 2022-01-01 19:04:50 +00:00
Omar Polo 5128c0b0e3 add `verifyname' option for `proxy' rule 2022-01-01 18:50:10 +00:00
Omar Polo 7bdcc91ec7 simplify the proxying code
it doesn't make any sense to keep the proxying info per-location:
proxying only one per-vhost.  It can't work differently, it doesn't make
sense anyway.
2022-01-01 17:08:39 +00:00
Omar Polo bd5f79542c update copyright years 2022-01-01 16:36:17 +00:00
Omar Polo a9cb709d44 sync changelog 2022-01-01 16:34:37 +00:00
Omar Polo c064f3de1d add proxying with client certificate test 2022-01-01 16:33:44 +00:00
Omar Polo e6ab6b25fc typo 2022-01-01 16:33:44 +00:00
Omar Polo d49093c105 support optional client certificate for proxy rule 2022-01-01 16:33:44 +00:00
Omar Polo da2185f37f move proxy sub-options to their own rule and allow grouping with { ... } 2022-01-01 16:33:40 +00:00
Omar Polo cc1c8f548c sync changelog 2022-01-01 10:54:17 +00:00
Omar Polo c3eb759a79 mention the reverse proxying 2021-12-29 20:53:22 +00:00
Omar Polo 6a6b4a2a98 typo 2021-12-29 20:36:54 +00:00
Omar Polo 4b5b1e82a9 add a test for the `proxy relay-to' rule 2021-12-29 20:36:54 +00:00
Omar Polo c143dcfa85 document the `proxy relay-to' rule 2021-12-29 20:36:54 +00:00
Omar Polo 72b033ef18 add ability to proxy requests
Add to gmid the ability to forwad a request to another gemini server and
thus acting like a reverse proxy.  The current syntax for the config
file is

	server "example.com" {
		...
		proxy relay-to host:port
	}

Further options (like the use of custom certificates) are planned.

cf. github issue #7
2021-12-29 20:36:54 +00:00
Omar Polo 054387bb26 move struct iri and parser at the top of the file 2021-12-29 19:24:33 +00:00
Omar Polo a62c63f261 retire the old gg 2021-12-29 18:18:41 +00:00
Omar Polo 7a4ae106ec switch to the new gg for the regress suite 2021-12-29 18:16:00 +00:00
Omar Polo ce09569f7b typo 2021-12-29 18:13:55 +00:00
Omar Polo 5c7abf0151 reimplement gg
This is a better version of gg.  Initially it grew with flags directly
needed to the specific test cases I wanted to write, so it's ugly to use
but handy for tests.

This is a new and re-thought implementation that it is (hopefully)
easier to use both and "curl-like for gemini" but also for scripts and
tests cases.

One completely new feature is the proxying support with -P to send the
request to the given host.
2021-12-29 18:01:08 +00:00
Omar Polo fe903d30e7 sync changelog 2021-12-11 09:12:10 +00:00
Max 63bf54b646 [seccomp] allow ugetrlimit(2), needed by glibc on armv7l 2021-12-11 09:08:50 +00:00
Omar Polo 2d6b9b53ac missing dots
(not adding one in the gemini version because it could be mistakenly
for part of the command)
2021-12-10 09:16:22 +00:00
Omar Polo 79c3a02152 specify that renew-certs needs gmid from the master branch 2021-12-10 09:15:39 +00:00
Omar Polo 52c92ef680 relax the "wont proxy request" check: don't check the port number
Don't refuse to serve the request if the port number doesn't match the
one we're listening on, as initially suggested by Allen Sobot.

Complex setup may have a gmid instance reachable from multiple ports and
the meaning of the check in the first places was to avoid tricking
clients into thinking that we're serving for those domains: the port
number is way less important than the schema or domain name.

In the long run, the best way would probably to add a `listen on'
keyword for the servers blocks, just like OpenBSD' httpd, but gmid can't
listen on multiple ports/interfaces yet
2021-12-09 20:59:05 +00:00
Omar Polo 34c4ca6ebc configure: support --prefix=...
It's like passing PREFIX=... on the command line
2021-12-09 19:24:18 +00:00
Omar Polo ae0d3cdbbe typo
forgot to rename variable in lib.sh: dont_check ->
dont_check_server_alive.
2021-12-09 19:13:30 +00:00
Omar Polo e5285d5422 rename `runtime' to `regress'
while there also kill an unused rule `regress', it's now redundant since
all the tests are run together.
2021-12-09 20:06:38 +01:00
Omar Polo a721c233ee rename variable 2021-12-09 19:03:18 +00:00
Omar Polo 5741561d57 run all kinds of tests via tests.sh/runtime
while there also change the dependency in the makefile: iri_test should
be alone, not as deps of `data'.
2021-12-09 19:02:09 +00:00
Omar Polo ea47a245aa add ocs stapling checking for gg 2021-12-09 18:51:02 +00:00
Omar Polo ebf3373d66 explain config-less mode 2021-12-02 08:32:47 +00:00
Omar Polo aa76c675eb sync changelog 2021-11-16 09:06:01 +00:00
Omar Polo eb77afa8d3 don't run the test binaries, compilation status is enough
During a cross-compilation we can compile the test binaries but not
run in the host machine.  Furthermore, the exit status of the test
isn't really important for the types of check we have, the compilation
status is enough.

Reported by Nikolay Korotkiy (@sikmir) on Github, fixes issue #8
2021-11-16 08:48:04 +00:00
Omar Polo 193380eaa4 free OCSP path when clearing the config
was forgotten in ff05125eb8
2021-10-24 16:57:31 +00:00
Omar Polo dcfdb969a2 don't list the exact pledge promises
It's easy to forgot to update the README after a code change (already
happened in the past) and they're easy to discover by reading
sandbox.c
2021-10-24 16:52:39 +00:00
Omar Polo d65fa58c1b use gmid from the repo instead of the system installed one 2021-10-24 16:38:11 +00:00
Omar Polo eaa02ac8c7 sync changelog 2021-10-24 16:34:34 +00:00
Omar Polo a81dd6cda6 fix "double slash" in logs
gmid ended up printing two slashes between the hostname and the path
when logging the request IRI.
2021-10-24 16:34:30 +00:00
Omar Polo 93edd35305 master is now 1.8-dev 2021-10-23 10:46:35 +00:00
Omar Polo 4842c72d9f fmt 2021-10-18 10:05:55 +00:00
Omar Polo f8bcc13822 typo 2021-10-15 09:19:17 +00:00
Omar Polo e84ee6a729 update the site for 1.7.5 2021-10-15 09:12:54 +00:00
Omar Polo 818d6aeeb6 sync changelog 2021-10-15 08:28:35 +00:00
Omar Polo 7fa6717647 fmt 2021-10-15 08:03:11 +00:00
Stephen Gregoratto ff05125eb8 Implement OCSP stapling support
Currently dogfooding this patch at gemini.sgregoratto.me. To test,
run the following command and look for the "OCSP response" header:

 openssl s_client -connect "gemini.sgregoratto.me:1965" -status
2021-10-15 09:58:23 +02:00
Omar Polo 387b976b99 c->req may be NULL now 2021-10-15 07:51:15 +00:00
Omar Polo 8044493865 move bufferevent initialization early in handle_handshake
the error path needs an initialized bufferevent too, otherwise it'll
crash when trying to write the response.

This moves the initialisation early, right after the tls_handshake.
Another option would be to initialise it in do_accept, but that may be
too early.
2021-10-15 07:46:30 +00:00
Omar Polo 33c4c3a5ba more small improvements for the quickstart 2021-10-13 20:52:19 +00:00
Omar Polo 2ae10bb4ee sync 2021-10-13 20:50:37 +00:00
Omar Polo c62a411f4f don't die on ECONNABORTED
ECONNABORTED is returned if a connections gets aborted after being
queued before the accept(2).  I had some cases of

	accept: Software caused connection abort

on FreeBSD, this should avoid that.
2021-10-13 20:49:58 +00:00
Omar Polo 8af884dff4 fix navigation links 2021-10-11 17:59:15 +00:00
Omar Polo 7c6bf71dc8 fix navigation links in *.gmi pages 2021-10-11 15:49:34 +00:00
Omar Polo fc4b58d450 tweak the quickstart guide 2021-10-11 15:46:41 +00:00
Omar Polo c0f81ed39e mention renew-certs in contrib.{gmi,html} 2021-10-11 15:32:47 +00:00
Omar Polo 536026c565 add script to automatically renew self-signed certificates 2021-10-11 14:42:11 +00:00
Omar Polo f0a01fc742 two -n to dump the parsed configuration
This adds a barebone dumping of the parsed configuration.  It is not
complete, but I'm interested in dumping the full path to `cert' and
`key' in order to write some scripts that can inspect the
configuration, extract the certificates and renew them when expired
automatically.

It's not easy to parse gmid configuration otherwise because the syntax
is flexible and users can use macros.  Instead, the idea is to run
gmid and let it dump the configuration once it's been parsed in a
static and predictable format.

Now is possible to parse gmid configuration with, say, awk or perl.
2021-10-09 21:40:55 +00:00
Omar Polo 5eb3fc905f don't work around a missing -Wno-unused-parameter
It's been there for a long time, and it's frankly annoying to pretend
to use parameters.  Most of the time, they're there to satisfy an
interface and nothings more.
2021-10-09 18:54:41 +00:00
Omar Polo c6bcc919c6 don't wrap the paths inside a <code></code> 2021-10-09 17:19:25 +00:00
Omar Polo c7b79c9eed fix navigation in contrib page 2021-10-09 17:13:35 +00:00
Omar Polo 1298e4307f split the cert generation example on multiple lines 2021-10-09 17:09:56 +00:00
Omar Polo 679d5d1a61 tweak the style for the manpage exported in HTML 2021-10-09 17:09:56 +00:00
Omar Polo 35340c9f1e typos 2021-10-09 16:54:33 +00:00
Omar Polo 540d05dedc typo 2021-10-09 16:31:43 +00:00
Omar Polo 0111ad5d26 add the Quickstart page 2021-10-09 16:30:36 +00:00
Omar Polo a83ba84448 mention gencert in contrib.{gmi,html} 2021-10-09 14:16:28 +00:00
Omar Polo d7e2e22c58 add gencert, a simple script to generate self-signed certs 2021-10-09 14:07:21 +00:00
Omar Polo 9bb2f62e24 tweak landlock comment 2021-10-08 15:55:48 +00:00
Omar Polo 807869c14e print the error too if we can't open a directory
It's not intuitive to print

	open ... for domain xyz

it doesn't convey that the open failed.

now it appends the error string, at least the user can understand that
something went wrong.

reported by cage on irc, thanks!
2021-10-07 17:19:45 +00:00
Omar Polo 492a274fd7 add compat for sys/tree.h 2021-10-07 11:36:25 +00:00
Omar Polo 207b3e80d8 Store clients inside a splay tree
From day one we've been using a static array of client struct to hold
the clients data.  This has variuos drawbacks, among which:

 * reuse of the storage  ("shades of heartbleed")
 * maximum fixed amount of clients connected at the same time
 * bugs are harder to debug

The last point in particular is important because if we mess the client
ids, or try to execute some functions (e.g. the various fcgi_*) after a
client has been disconnected, it's harder to "see" this "use after
free"-tier kind of bug.

Now I'm using a splay tree to hold the data about the live connections.
Each client' data is managed by malloc.  If we try to access a client
data after the disconnection we'll probably crash with a SIGSEGV and
find the bug is more easy.  

Performance-wise the connection phase should be faster since we don't
have to loop anymore to find an empty spot in the clients array, but
some operations could be slightly slower (compare the O(1) access in an
array with a SPLAY_FIND operation -- still be faster than O(n) thought.)
2021-10-07 11:20:34 +00:00
Omar Polo 4cd2520965 one FastCGI connection per client
FastCGI is designed to multiplex requests over a single connection, so
ideally the server can open only one connection per worker to the
FastCGI application and that's that.

Doing this kind of multiplexing makes the code harder to follow and
easier to break/leak etc on the gmid side however.  OpenBSD' httpd
seems to open one connection per client, so why can't we too?

One connection per request is still way better (lighter) than using
CGI, and we can avoid all the pitfalls of the multiplexing (keeping
track of "live ids", properly shut down etc...)
2021-10-07 10:47:02 +00:00
Omar Polo 3096da4ef4 allow to run only a subset of the runtime tests
with
	make TESTS='test_1 test_2 ...' regress

now it's possible to run only that specified subset of tests.  It's
really useful during debugging :)
2021-10-07 08:55:44 +00:00
Omar Polo e4daebe44a plug a memory leak
c->req is set in client_read but never deallocated
2021-10-06 17:38:37 +00:00
Omar Polo 807a80cb9e fmt 2021-10-06 16:36:31 +00:00
Omar Polo b4c6cd9768 add the upload target to ease publishing the site 2021-10-04 13:01:41 +00:00
Omar Polo 9212cf1ba9 [gemini] tweak the contrib page
I find it more readable with some empty lines here and there
2021-10-04 12:57:46 +00:00
Omar Polo eb82dcfbf4 improve the service file usage instructions
Thanks Martin for providing these information :)
2021-10-04 12:56:11 +00:00
Omar Polo 12866f1911 add targets to serve the site locally 2021-10-04 12:54:46 +00:00
Omar Polo ae6870fa3b import the capsule/website 2021-10-04 10:42:35 +00:00
Omar Polo 568419b2c1 add .cirrus.yml
Add a cirrus CI config file that runs the regression suite on linux
amd64/aarch64 and on freebsd.
2021-10-04 10:05:34 +00:00
Omar Polo 6e0f14d51e re-add sha script; it's used in the Makefile
While there, use it in the tests too
2021-10-04 09:40:05 +00:00
Omar Polo 2072343d6b sync changelog 2021-10-04 09:35:17 +00:00
Omar Polo 260becda9c reduced the timeout time for single checks 2021-10-04 09:34:39 +00:00
Omar Polo d046e4d6b5 copy only `len' bytes, not the whole buffer
We ended up copying too much data from the fastcgi process.
2021-10-04 09:31:43 +00:00
Omar Polo 4a2a525d7c allow running only specific tests
It's now possible to run only a subset of the tests with:

	./runtime test1 test2 ...
2021-10-04 09:30:18 +00:00
Omar Polo c1272f63e4 sync 2021-10-04 09:08:21 +00:00
Omar Polo 99c91b4a51 remove unused script 2021-10-04 09:07:42 +00:00
Omar Polo 176179b2a9 rework the regression suite
The tests are still there, the suite is equivalent to the old one, but
this one is better structured.

The biggest annoyance I had with the old one was that it wasn't
straightforward to test only a specific set of tests.  It's still
impossible, but it's way easier to do it now.

This extract all the tests to their own functions.  It's overall
better in all possible regards.
2021-10-04 09:04:33 +00:00
Omar Polo c28994868e update clean target 2021-10-02 19:35:30 +00:00
Omar Polo a49800c86a sync 2021-10-02 17:21:26 +00:00
Omar Polo acafce5b7d libevent2 fix: unfreeze the client evbuffer
libevent2 has this concept of "freezeness" of a buffer.  It's a way to
avoid accidentally write/remove data from the wrong "edge" of the
buffer.  The client_tls_{read,write} functions need to add/drain data
from the opposite edge, hence the need for the unfreeze call.

This is the minimum change in order to work on libevent2 too.  Another
way would be to define evbuffer_{un,}freeze as NOP on libevent 1, but
it's ugly IMHO.
2021-10-02 17:20:56 +00:00
Omar Polo efb6210d77 improve libevent2 handling
* add configure check
* change the way the headers are required (copied from tmux)
2021-10-02 17:20:56 +00:00
Omar Polo d0071d8321 verbose logging for getnameinfo 2021-10-02 17:20:56 +00:00
Omar Polo 827cc37cff update tests
* we don't add a space before the lang anymore
* we're more strict in CGI handling: the `invalid' CGI script now
  triggers a 42 CGI ERROR
2021-10-02 17:20:56 +00:00
Omar Polo fa0299a26d drop now unused trim_req_iri 2021-10-02 17:20:56 +00:00
Omar Polo efe7d18029 new I/O handling on top of bufferevents
This is a big change in how gmid handles I/O.  Initially we used a
hand-written loop over poll(2), that then was evolved into something
powered by libevent basic API.  This meant that there were a lot of
small "asynchronous" function that did one step, eventually scheduling
the re-execution, that called each others in a chain.

The new implementation revolves completely around libevent'
bufferevents.  It's more clear, as everything is implemented around the
client_read and client_write functions.

There is still space for improvements, like adding timeouts for one, but
it's solid enough to be committed as is and then further improved.
2021-10-02 17:20:56 +00:00
Omar Polo 403c422041 [cgi] switch from pipe(2) to socketpair(2)
We can't use normal pipe(2)s with libevent in some cases.  Switch to
socketpair(2), which doesn't have the same problem.

This has the drawback that it doesn't prevent the CGI script from
reading stdout, for instance.  (sockets are two-way, pipes only one-way)
2021-10-02 17:20:56 +00:00
Omar Polo b618111a68 log more details for FastCGI errors
add the reported request id if there's a mismatch and both the gai error
and the errno value if getnameinfo fails.
2021-10-02 17:20:10 +00:00
Omar Polo 5f37f9c20d simplify error check 2021-10-02 17:20:10 +00:00
Omar Polo c016b65ca9 typo 2021-10-02 17:20:10 +00:00
Omar Polo f7ee799023 enforce PR_SET_NO_NEW_PRIVS in the logger process
otherwise landlock will refuse to enable itself and the logger process
dies.
2021-10-02 17:20:10 +00:00
Omar Polo 0c66b6ad55 forgot include 2021-09-26 20:01:32 +00:00
Omar Polo 6f27d2595a [seccomp] allow ioctl(FIONREAD)
it's needed by bufferevent_read
2021-09-26 20:00:38 +00:00
Omar Polo 2a44a2ab6e sync changelog 2021-09-26 17:00:16 +00:00
Omar Polo 741b69be96 fastcgi completely asynchronous
This changes the fastcgi implementation from a blocking I/O to an
async implementation on top of libevent' bufferevents.

Should improve the responsiveness of gmid especially when using remote
fastcgi applications.
2021-09-26 17:00:07 +00:00
Omar Polo 83fe545a2b initialize mbufhead 2021-09-26 16:43:19 +00:00
Omar Polo cb28978f0a refactor landlock
refactor the landlock-related code into something more manageable.
The only real difference is that before the logger process would try
to landlock itself to "/" without perms, something that landlock
doesn't support (now it enables landlock and then restrict itself,
which is the correct move.)
2021-09-25 08:47:29 +00:00
Omar Polo 775ef04f82 mention the thanks to cage for the bugfix
Since I was in a hurry, I forgot to mention it in the tag message :/
2021-09-24 14:21:16 +00:00
Omar Polo 0d9a5b7a18 sync release 2021-09-24 11:16:32 +00:00
Omar Polo 3571854e94 fix possible out-of-bound access
While computing the parent directory it an out-of-bound access can
occur, which usually means the server process dies.

In particular, it can be triggered by making a request for a
non-existent file in the root of a virtual host if the path matches
the `cgi` pattern.

Thanks cage for helping in debugging!
2021-09-24 10:48:51 +00:00
Omar Polo 353e3c8ebe style 2021-09-24 08:16:28 +00:00
Omar Polo e15fc95736 change struct initialization
makes more explicit which fields we're setting.

(and kill an extra empty line)
2021-09-24 08:12:40 +00:00
Omar Polo 81e0f00078 fmt 2021-09-24 08:10:07 +00:00
Omar Polo df0c2926cc use memset(3) rather than bzero(3)
There's no difference, but bzero(3) says

STANDARDS
     The bzero() function conforms to the X/Open System Interfaces option of
     the IEEE Std 1003.1-2004 (“POSIX.1”) specification.  It was removed from
     the standard in IEEE Std 1003.1-2008 (“POSIX.1”), which recommends using
     memset(3) instead.

so here we are.
2021-09-24 08:08:49 +00:00
Omar Polo a91ad7f2ff drop unnecessary bzero
the whole struct client is already memset'd to 0 in do_accept.
handle_handshake doesn't touch the request or iri buffer in the code
path that leads to handle_open_conn.  (It does so in the error router
alone.)
2021-09-24 08:08:49 +00:00
Omar Polo 79288c8b60 making more explicit the case of missing SNI
Missing SNI (i.e. servname == NULL) is already handled correctly.
puny_decode refuses to work on NULL servname, c->domain is still the
empty string and everything flows as expected towards the error at the
end.  However, it's better to bail out early and make more explicit
how the case of missing SNI is handled.
2021-09-24 07:40:24 +00:00
Martin 2b38d395cd Improve gmid.service
Changed gmid.service to not to fork the server and forced to run under
user "gmid".  gmid now waits for the network stack beeing available
before starting. Also "gmid" is now the syslog id.
2021-09-21 19:15:50 +02:00
Omar Polo 36d2d7b4b3 sync changelog 2021-09-19 17:15:24 +00:00
Omar Polo b0be065390 landlock the logger process too
Disallow everything landlock can handle.  The logger process doesn't
need any fs access (on OpenBSD it runs with pledge("stdio recvfd")).
2021-09-19 17:08:12 +00:00
Omar Polo 0ea22af280 add helper function gmid_create_landlock_rs 2021-09-19 17:08:12 +00:00
Omar Polo 67c49bc5c7 mention landlock in the README 2021-09-19 17:08:12 +00:00
Omar Polo 3499ce5a9a landlock the server process
Trying to implement some landlock policies (rules?) where possible.
The server process is, of course, the most dangerous process so start
with that.

The following should be equivalent to the unveil(2) call on OpenBSD:
allows only to read files and directories inside the vhost roots.

I'm assuming seccomp is enabled so I'm not trying to disallow actions
such as LANDLOCK_ACCESS_FS_EXECUTE or LANDLOCK_ACCESS_FS_REMOVE_FILE
which require syscalls that are already disallowed.  I'm only trying
to limit the damage that the currently allowed system calls can do.
e.g. since write(2) is allowed, gmid could modify *any* file it has
access to; this is now forbidden by landlock.

There are still too many #ifdefs for my tastes, but it's still better
than the seccomp code.
2021-09-19 17:08:12 +00:00
Omar Polo d85aa60208 define HAVE_LANDLOCK accordingly 2021-09-19 17:08:12 +00:00
Omar Polo 4c31de2915 add configure check and shim for landlock
First move towards landlock support (#3).  The shim is needed until
libc provides the proper wrappers for the landlock APIs; I hope it
doesn't take too long, but landlock was merged back in May and are
still missing.
2021-09-19 17:08:12 +00:00
Omar Polo fba809b5c7 tag 1.7.3
bump version number
2021-09-19 14:48:07 +00:00
Omar Polo ca3d8bfde6 retroactively mention the fstat64 fix 2021-09-18 11:15:40 +00:00
Omar Polo 39c5bfd64b fix changelog syntax 2021-09-18 11:09:36 +00:00
Omar Polo e30e5385f1 sort tests 2021-09-18 11:17:28 +02:00
Omar Polo b03b7d5bae sync changelog 2021-09-17 16:17:16 +00:00
Omar Polo db623d7551 fix syslog logging on FreeBSD
Due to capsicum(4), opening the log on-demand via syslog(3) fails.
openlog(LOG_NDELAY) forces the log to be opened immediately, before we
enter capsicum.

This doesn't affect OpenBSD (where pledge(2) doesn't stop syslog) nor
linux (where the log process is not sandboxed.)

Reported by Karl Jeacle, thank you!
2021-09-17 16:17:16 +00:00
Omar Polo f2478b332f sync 2021-08-23 22:44:55 +00:00
Omar Polo 2e31927606 don't crash if -n is given without -c
If -n is given without -c, config_path is still NULL and it would
crash due to a NULL deference.
2021-08-23 22:43:56 +00:00
Omar Polo 83272dfee9 don't yyerror if we can't open the file
the global var file could still be NULL and yyerror would crash.
2021-08-23 22:42:59 +00:00
Omar Polo ef352ec3b8 fix wrong arguments order for send_log 2021-08-23 22:36:01 +00:00
Omar Polo 03747a6c51 sync 2021-07-29 07:54:41 +00:00
Omar Polo 2bb1776bac keep the openssl key generation example under 80 columns 2021-07-29 09:48:43 +02:00
Anna “CyberTailor” 301e039d24 gmid.1: some spelling, grammar and crosslinks 2021-07-29 09:38:03 +02:00
Anna “CyberTailor” e308526cf6 gmid.1: provide certificate generation example 2021-07-29 09:35:09 +02:00
Anna “CyberTailor” e58a447a28 gmid.1: document logging 2021-07-29 09:31:09 +02:00
Omar Polo 2887c631ee sync 2021-07-27 09:22:17 +00:00
Omar Polo efb48052dc relax openat rule: follow symlinks
O_NOFOLLOW acts only on *the last component*, so on
open("/foo/bar/baz") only when baz is a symlink open fails.
Checking every path component is not viable.

gh issue #5 related (sort of)
2021-07-27 09:21:42 +00:00
Omar Polo a8e1e8d738 typo
Since there was 0 reports in a month can I assume it's not actually
used anywhere?
2021-07-23 06:55:32 +00:00
Omar Polo 21f7d24699 allow fstat64
used by glibc on aarch64.

Found and tested by pine, thanks!
2021-07-23 06:50:30 +00:00
Omar Polo af5f9b271e use \e instead of \\ as a mean to escape \
Just read in a mail from Ingo to tech@

> Using \\ outside macro definitions (i.e., outside the bodies of roff(7)
> .de and similar requests) is almost always wrong even in low-level roff
> code, and \\ must never be used in manual pages.
2021-07-21 07:56:41 +00:00
Omar Polo 3e0164f4c3 use certs from regress/ 2021-07-19 11:37:01 +00:00
Omar Polo 735336b21e unbreak configless mode
An un-initialized field in the configless code path leads to a crash
on the first request.
2021-07-19 11:15:44 +00:00
Omar Polo abc8801d60 typo 2021-07-19 07:45:55 +00:00
Omar Polo c74c70304d drop leading T from token names 2021-07-19 07:39:13 +00:00
Omar Polo 9a818eebc1 sort tokens 2021-07-19 07:34:29 +00:00
Omar Polo 41395640e0 introduce IMSG_LOG_REQUEST 2021-07-19 07:31:40 +00:00
Omar Polo 2b5e2a5f04 send_log: accept explicit imsg type param 2021-07-19 07:23:38 +00:00
Omar Polo 9ffdade26c sort 2021-07-13 11:45:51 +00:00
Omar Polo a7da55710e sync 2021-07-13 11:43:07 +00:00
Anna “CyberTailor” f3966209e5 contrib/vim: add Syntastic integration
Error and warning messages are prefixed with "error: " and "warning: "
correspondingly to ease integration with automated tooling.

`yywarn' function added. Off-by-one line numbers in warnings are fixed.

Two error messages are reworded to avoid repeating like
"error: error in server directive" or "error: syntax error".
2021-07-13 13:38:42 +02:00
Omar Polo a556718a24 sort and tweak rc description 2021-07-13 11:12:20 +02:00
la-ninpre caf4e5fb22 add an example rc(8) script for openbsd
copy it to `/etc/rc.d`, write your config in `/etc/gmid.conf` and launch
using `rcctl enable gmid && rcctl start gmid`.
2021-07-13 11:09:13 +02:00
Omar Polo dbbfd0fb9f point to contrib.html 2021-07-11 20:58:57 +00:00
Omar Polo 3b0eff06ff fix the dockerfile 2021-07-11 20:58:42 +00:00
Omar Polo 02ccd9d940 move the Dockerfile to contrib/ 2021-07-11 18:43:24 +00:00
Omar Polo e8b8003098 add an example systemd unit file 2021-07-11 15:44:44 +00:00
Omar Polo e2d3a8f222 add missing `env' keyword 2021-07-11 14:49:20 +00:00
Anna “CyberTailor” 1a115616cb add simple vim syntax file 2021-07-11 14:27:31 +02:00
Omar Polo 8068d2ff33 tag v1.7.1
This version includes two bugfixes:

 - use ${MAKE} to recursively call make
 - fix the misleading example in the man page: macros name may not be
 reserved words

Both bugs found and fixed by Anna “CyberTailor”, thanks!
2021-07-11 09:50:26 +02:00
Omar Polo 094bdacd40 sync 2021-07-11 09:48:29 +02:00
Omar Polo ad15003be8 align 2021-07-11 09:47:19 +02:00
Anna “CyberTailor” 2d06ae62ae fix misleading example in man page
> Macros names may not be reserved words
2021-07-11 09:23:56 +02:00
Anna “CyberTailor” 8518014cb9 fix "warning: jobserver unavailable: using -j1"
https://www.gnu.org/software/make/manual/html_node/Error-Messages.html
2021-07-11 09:23:53 +02:00
Omar Polo b79b2d811b tag v1.7 2021-07-10 09:22:19 +00:00
Omar Polo f361f79979 move version number to configure script 2021-07-10 09:19:44 +00:00
Omar Polo a82d4dfa25 provide a fallback value for SKIP_RUNTIME_TESTS
some [ implementation will warn about "bad number"
2021-07-10 09:09:26 +00:00
Omar Polo 9f075bd689 sync 2021-07-09 13:36:22 +00:00
Omar Polo 391825e360 move parse_portno to gmid.c
it's used only to parse the -p flag.  While there add check_port_num
to check the range for the port.
2021-07-09 13:06:58 +00:00
Omar Polo e3b2a0f8de missing newline in deprecation notice 2021-07-09 12:57:58 +00:00
Omar Polo 1bd706dc41 drop unsigned
it causes a cascade of warnings on some architectures.  Also, for what
we're doing here, the signed/unsigned property isn't important.
2021-07-09 12:56:59 +00:00
Omar Polo 528f37cd2d sync macro usage and mention the `include' keyword 2021-07-09 12:50:24 +00:00
Omar Polo 67f494057a @-macros, rollback changes to strings and optional semicolons
* expand $-macros as string, only the new @-macros get expanded as-is
* rollback changes to characters allowed in bare strings
* optional semicolons in optnl, useful for readable @-macros
2021-07-09 12:49:15 +00:00
Omar Polo c39be742cf parsing: bring lots of goodies from OpenBSD' parse.y
This allows to solve the problem with the \n in the grammar (before
two following macro declaration were treated as invalid.  This also
brings in a nice `include' keyword.
2021-07-09 11:25:25 +00:00
Omar Polo a5d822e542 typo 2021-07-09 08:12:05 +00:00
Omar Polo be52e954c1 sync readme.md with sandbox.c 2021-07-09 08:11:57 +00:00
Omar Polo 3d132b2833 `param' is forbidden inside `location's too
and while there sort the list of disallowed rules.
2021-07-09 08:04:12 +00:00
Omar Polo 2b520ad595 misc improvements to the manual 2021-07-09 08:01:22 +00:00
Omar Polo efacb859a7 change (again) the env/param separator: use '='
Given that env/param are new features of this release, no support for
the "old" syntax is needed.
2021-07-09 07:27:15 +00:00
Omar Polo 762b9b991f add => in env/param and `port' between hostname and port for fastcgi
In the same spite of the last commit, add the missing separators
between strings to avoid the auto-concat pitfalls.  `=>' is used to
separate between `env' and `param' arguments, while for `fastcgi' the
keyword `port' is required between the hostname/ip address and the
port (if provided).

Since `env', `param' and `fastcgi' are all new stuff, there's no need
to keep compatibility.
2021-07-09 08:30:55 +02:00
Omar Polo ff954a3e76 typo... 2021-07-09 08:30:55 +02:00
Omar Polo d19951cf03 rename `mime MIME EXT' to `map MIME to-ext EXT'
With the newish automatic string concatenation, options like `mime'
that accepts two strings as parameter start to become ambiguous: which
strings gets concatenated?  Instead of trying to document in the
manpage which argument(s) is subject to string concatenation, do the
concat always and introduce a separator.  In the case of mime,
`to-ext' now acts as a separator to distinguish.  While there, also
use a new keyword because it sounds better.

It's dead-easy to upgrade to the new configuration, possibly with some
sed magic, but for the moment the old `mime' form is preserved: (with
a warning!)  Will be dropped in the next release.
2021-07-09 08:30:55 +02:00
Omar Polo d93c819182 use bsearch to match the keywords
not that it's a bottleneck, but it's fancier this way.
2021-07-09 08:30:55 +02:00
Omar Polo bffa7daab8 provide a means to skip the runtime tests
Setting the environment variable SKIP_RUNTIME_TESTS to 1 will prevent
the runtime tests.  This is useful when running the tests inside a
sandbox.

based on a similar diff by Anna "CyberTailor"
2021-07-08 17:13:17 +02:00
Anna “CyberTailor” 8c9b8f487e replace add_{cflags,ldflags} with add_library 2021-07-08 16:56:29 +02:00
Anna “CyberTailor” 9c84a04f9c check freedesktop's libbsd 2021-07-08 16:55:58 +02:00
Anna “CyberTailor” 9797799ad4 regress/Makefile: add missing LDFLAGS 2021-07-08 16:55:08 +02:00
Omar Polo 2e2e189b01 PF_UNIX is not a valid protocol for socketpair
OpenBSD accept it, but FreeBSD disallows it.  PF_UNSPEC (or 0) should
be used instead.  The FastCGI bit in the regress suite still doesn't
work on FreeBSD, but at least now it starts.
2021-07-08 10:09:24 +00:00
Omar Polo e7c6502bf3 don't leak a file descriptor
make sure we always close every fd in every possible code path; while
there, also add a log_err if fork(2) failed.
2021-07-08 09:40:23 +00:00
Omar Polo a8a1f43921 style(9)-ify 2021-07-07 09:46:37 +00:00
Omar Polo 68ef117092 always append -W flags to CFLAGS 2021-07-07 09:19:24 +00:00
Omar Polo 8a50fc0330 initialize the logger early
Initialize the logger as soon as possible and log by default to
stderr.  With this, some (common?) errors are printed early instead of
ending up in syslog.

	# NB: this is in configless mode
	% ./gmid -p 80
	[2021-07-07 11:05:57] bind: Address already in use
	% ./gmid -p 81
	[2021-07-07 11:13:53] bind: Permission denied
	%
2021-07-07 09:18:24 +00:00
Omar Polo d336aeafd4 refactor add_{cflags,ldflags} 2021-07-07 08:46:23 +00:00
Omar Polo 489e1655a0 add --enable-sandbox
patch from Anna "CyberTailor"

It's handy for helpers like ebuild's use_enable.

(the sandbox is still always enabled by default)
2021-07-07 08:43:33 +00:00
Omar Polo 1a572d4221 check libimsg too
patch from Anna "CyberTailor"
2021-07-07 08:41:27 +00:00
Omar Polo af15474448 always append LDFLAGS and CFLAGS
Initial patch from Anna "CyberTailor", tweaked by me (drop guessing_*
and always append CFLAGS)
2021-07-07 08:38:19 +00:00
Omar Polo 321341f6c0 add -T timeout to gg; don't let the regress test hang 2021-07-06 13:09:24 +00:00
Omar Polo 6edcfca97f try to preserve as much as possible CFLAGS and LDFLAGS from env
but still try to autodetect with pkg-config if they aren't provided.

Passing CFLAGS/LDFLAGS from the command line will still override the
guessed ones.
2021-07-06 13:01:11 +00:00
Omar Polo eb877bffaa explain the OpenBSD bit 2021-07-06 12:18:53 +00:00
Omar Polo ca3f67564c don't log too much by default 2021-07-06 11:53:24 +00:00
Omar Polo fb4102a5ff test macro expansion too 2021-07-06 11:52:28 +00:00
Omar Polo 3759d3eb56 show macro usage in the example 2021-07-06 11:41:24 +00:00
Omar Polo 7700ab07d3 fmt 2021-07-06 10:56:13 +00:00
Omar Polo 59c7ee13b4 fmt 2021-07-06 10:56:01 +00:00
Omar Polo 090b8a89fa gracefully shut down fastcgi backends
we need to delete the events associated with the backends, otherwise
the server process won't ever quit.

Here, we add a pending counter to every backend and shut down
immediately if they aren't handling any client; otherwise we try to
close them as soon as possible (i.e. when they close the connection to
the last connected client.)
2021-07-06 10:54:27 +00:00
Omar Polo ea976e8743 don't let CGI scripts inherit our stderr
our stderr could have been sent to the logger process, so it may be
invalid.  Furthermore, in the future we may want to capture also the
stderr of the processes.
2021-07-06 10:48:59 +00:00
Omar Polo ef945cf415 reset fcgi array in free_config
otherwise path, port and/or prog could become an invalid pointer.
2021-07-06 10:46:50 +00:00
Omar Polo 4aa1dd553a typo 2021-07-03 18:30:07 +00:00
Omar Polo b24021d4a2 fix seccomp filter for ppc64le
before we matched ppc64le as ppc64 (which is big ending I presume), so
the seccomp filter would always kill gmid

#4 related
2021-07-03 17:38:50 +00:00
Omar Polo 7322a054f5 use -subj /CN=name when generating the keys for the tests
`printf | openssl` is ugly :/
2021-07-03 08:13:42 +00:00
Omar Polo 8bb8cf2ad4 configure: add --disable-sandbox
Calling `configure' with --disable-sandbox will disable the sandbox
support *completely* at compile time.  gmid will still complain at
compile time and during the startup.

Users shouldn't disable the sandbox if possible, but instead report
problem upstream so they get fixed (hopefully.)

#4 related
2021-07-02 09:26:59 +00:00
Omar Polo 137def5ff4 reworked seccomp filter
* SECCOMP_AUDIT_ARCH extended to support more architectures
* relax fcntl policy: allow the syscall regardless of the flags
* wrap every syscall in a ifdef, and add some (statx, fcntl64, ...)
  used in x86

Some bits were taken from dhcpcd[0], thanks!

#4 related


[0]: https://roy.marples.name/git/dhcpcd/blob/HEAD:/src/privsep-linux.c
2021-07-02 09:11:40 +00:00
Omar Polo 4f9a27603c revert 2c16dbd548 -- macro names can't be reserved words
While one can define a macro using a reserved word as name using
-Dname=val, inside the configuration file it'll fail.
2021-07-02 09:05:22 +00:00
Omar Polo 2c16dbd548 macro names can be reserved words 2021-06-29 16:49:38 +00:00
Omar Polo 4ede0fe495 fix macro example 2021-06-29 16:45:09 +00:00
Omar Polo eaf7f776f1 document the c-like handling of strings 2021-06-29 16:44:17 +00:00
Omar Polo bfb076ed7e don't expand macros inside the quotes
Now that we have this auto concat string thingy, macros can simply
expand to standalone strings in place, as single words.

Forgot to point it out in previous commits, but now we can

	cert = "/etc/keys"

	server "foo" {
		cert $cert "/foo.crt"
		...
	}
2021-06-29 16:35:06 +00:00
Omar Polo 7252049dd7 align 2021-06-29 16:10:40 +00:00
Omar Polo 98f5217822 [config] concat two or more strings next to each others 2021-06-29 16:10:10 +00:00
Omar Polo 8b743ddacb document macros: both -D and syntax 2021-06-29 14:19:35 +00:00
Omar Polo 6b86655a10 don't require the strict order macro > options > servers 2021-06-29 12:42:44 +00:00
Omar Polo f98e9045ae add -D to define macros from the cmd line 2021-06-29 12:39:34 +00:00
Omar Polo 3b21cca385 allow to define macros in the config file
Macros can be defined at the top of the configuration file:

	dir = "/var/gemini"
	cert = "/etc/keys"

and re-used later, for example

	server "foo" {
		root "$dir/foo"       # -> /var/gemini/foo
		cert "$cert/foo.pem" # -> /etc/keys/foo.pem
	}
2021-06-29 12:17:40 +00:00
Omar Polo fafc684957 trailing whitespaces 2021-06-29 11:21:32 +00:00
Omar Polo 8235a81c8f give a name to the anonymous union 2021-06-29 11:11:43 +00:00
Omar Polo 0be2a537e6 sync the usage; while there also change order and capitalize 2021-06-29 11:04:29 +00:00
Omar Polo ba65dcc8fa mention -V/--version and --help in the manpage 2021-06-29 11:00:28 +00:00
Omar Polo fdb43a4c0e define GMID_STRING and reuse-it
GMID_VERSION follows the CGI/FastCGI style, i.e. project_name/version.

Define GMID_STRING with a more "human" variant "project_name version",
and reuse that in the --help and --version codepath.
2021-06-29 10:56:07 +00:00
Omar Polo 0233b0f6b9 add version in usage 2021-06-29 10:51:42 +00:00
Omar Polo 9327bc045a use getprogname() in usage() 2021-06-29 10:50:39 +00:00
Omar Polo 5777923bb0 use getopt_long, add --help as synonym of -h and -V/--version 2021-06-29 10:49:59 +00:00
Omar Polo 2c43dcceb6 document `log' option 2021-06-17 09:27:09 +00:00
Omar Polo 1b78bd563a strncpy -> strlcpy
quoting strncpy(3)

     strncpy() only NUL terminates the destination string when the
     length of the source string is less than the length parameter.

strlcpy is more intuitive.

this is another warning gcc 8 found that clang didn't.
2021-06-16 15:06:10 +00:00
Omar Polo 80fbf1e934 make sure l is always initialized
I can't think of cases where we reach serialize_iri and path is NULL,
but let's keep the safe side and initialize l.  gcc 8 found this,
clang didn't.
2021-06-16 15:04:42 +00:00
Omar Polo 2d383cbd5f remove last occurrences of lex 2021-06-16 15:00:52 +00:00
Omar Polo 9e659275b0 remove now unused lex.l 2021-06-16 14:58:48 +00:00
Omar Polo ef129b08ef move lineno/colno into the token struct 2021-06-16 14:51:57 +00:00
Omar Polo 74f0778b9a drop the dependency on lex by implementing yylex by ourselves
The actual implementation is based off doas' parse.y.  This gave us
various benefits, like cleaner code, \ to break long lines, better
handling of quotes etc...
2021-06-16 14:43:16 +00:00
Omar Polo 984c46a82e kill unused variable
leftover from the handle_imsg_log -> do_log transition.
2021-06-16 14:38:47 +00:00
Omar Polo 89c110fe7b be quieter
nobody really cares if must_read fails, as it normally shouldn't.  It
only clutters the regression tests with scary messages that looks like
failure but are, in fact, expected.
2021-06-15 08:07:44 +00:00
Omar Polo e952c5052a allow sending fd to log on to the logger process
the logger process now can receive a file descriptor to write logs
to.  At the moment the logic is simple, if it receives a file it logs
there, otherwise it logs to syslog.  This will allow to log on custom
log files.
2021-06-15 08:06:10 +00:00
Omar Polo 0f2124e291 add some simple test for fastcgi
and while there reorder the targets in the Makefile a bit
2021-06-12 13:47:01 +00:00
Omar Polo 03fcfb79d6 add simple fcgi application for test purposes 2021-06-12 13:46:05 +00:00
Omar Polo 24d362cd67 explicitly use c->fd instead of fd
Yep, fd should be the file descriptor, but for lazyness when manually
calling the function sometimes we supply 0 as fd and event.  Instead of
fixing the usage, do as other of such functions do in this
circumstances: use c->fd.
2021-06-12 13:42:43 +00:00
Omar Polo 89c88caa3c mark backend as FCGI_READY when getting a fd
otherwise clients will remain stuck waiting for a pending request that
doesn't exist (see apply_fastcgi switch.)
2021-06-12 13:41:33 +00:00
Omar Polo e18b070da8 indentation 2021-06-12 12:35:15 +00:00
Omar Polo ab1e0169b9 free fastcgi param list 2021-06-12 10:20:36 +00:00
Omar Polo 3b33eab3ad TAILQ_REMOVE env and aliases during config_free
it's not technically required, since a couple of lines below we free
whole host struct, and we don't have code that may use
h->{env,aliases} afterwards, but it's nice not to have invalid
pointers around.  it may bite in the future.
2021-06-12 10:19:17 +00:00
Omar Polo a6c809551e mention FastCGI in the README.md 2021-06-11 16:11:58 +00:00
Omar Polo fd1ae83d20 fix warnings in manpage
man -Tlint
2021-06-11 16:06:24 +00:00
Omar Polo ada16f8731 document fastcgi and the new `param' options 2021-06-11 16:04:22 +00:00
Omar Polo c92b802b6a add `param' keyword 2021-06-11 15:49:46 +00:00
Omar Polo f740b61b03 more params from and send a custom list 2021-06-11 15:48:56 +00:00
Omar Polo 6957a8c27f reword last sentence 2021-06-04 07:14:50 +00:00
Omar Polo 0c7a1d3cf3 improve docker example 2021-06-04 07:13:37 +00:00
Omar Polo 3a1bd9ab49 sync 2021-05-27 09:46:00 +00:00
Omar Polo f48e3b85a9 move gg to regress 2021-05-24 13:48:55 +00:00
Omar Polo 0d047efcb4 refactor fastcgi block 2021-05-24 09:09:10 +00:00
Omar Polo 8ff40039e8 document `fastcgi' config option 2021-05-24 09:07:28 +00:00
Omar Polo ce2c9edbc2 define and use GMID_VERSION 2021-05-15 11:11:56 +00:00
Omar Polo d1051bfaa0 define some more fcgi param 2021-05-15 11:10:55 +00:00
Omar Polo 1feaf2a618 use the correct document root
pass the correct loc_off to the executor, so the various variables
that depends on the matched location (like DOCUMENT_ROOT) are computed
correctly.
2021-05-15 10:31:43 +00:00
Omar Polo 91b9f2a8f9 const-ify strip_path 2021-05-15 10:07:21 +00:00
Omar Polo 571d20fbb3 fmt 2021-05-15 10:04:58 +00:00
Omar Polo 0773413c1d correct the description of `strip'
now it's also used in the path lookup process
2021-05-15 09:51:45 +00:00
Omar Polo 1eb3631d10 certificate generation (bugfix and improvement)
don't add gmid as organisation when generating the certificate, and
set the version to 3, so it's compatible with java/android clients.

Found by Gnuserland, thanks!
2021-05-12 17:37:22 +00:00
Omar Polo 58d51f2c11 gmid don't enforce anymore that a root is specified 2021-05-11 20:20:02 +00:00
Omar Polo 23a5aab4ce drop forward declaration of struct client
it's been since the switch to libevent that is no longer needed.
2021-05-11 19:41:16 +00:00
Omar Polo 84821dc0ac sync 2021-05-09 18:31:18 +00:00
Omar Polo 8ad1c57024 fastcgi: a first implementation
Not production-ready yet, but it's a start.

This adds a third ``backend'' for gmid: until now there it served
local files or CGI scripts, now FastCGI applications too.

FastCGI is meant to be an improvement over CGI: instead of exec'ing a
script for every request, it allows to open a single connection to an
``application'' and send the requests/receive the responses over that
socket using a simple binary protocol.

At the moment gmid supports three different methods of opening a
fastcgi connection:

 - local unix sockets, with: fastcgi "/path/to/sock"
 - network sockets, with: fastcgi tcp "host" [port]
   port defaults to 9000 and can be either a string or a number
 - subprocess, with: fastcgi spawn "/path/to/program"
   the fastcgi protocol is done over the executed program stdin

of these, the last is only for testing and may be removed in the
future.

P.S.: the fastcgi rule is per-location of course :)
2021-05-09 18:23:36 +00:00
Omar Polo 50310aff33 added missing prototype 2021-05-04 18:19:47 +00:00
Omar Polo 419e32b05e sync 2021-05-03 16:12:53 +00:00
Omar Polo c705ecb1ba factorize all "only once" checks
and while there add some more
2021-05-03 16:11:28 +00:00
Omar Polo 7b2d743260 move pidfile & cgi to global vars 2021-05-01 12:17:42 +00:00
Omar Polo 7aa9258633 make more explicit the per-location roots test 2021-04-30 19:08:27 +00:00
Omar Polo 737a6b50c5 ensure %p (path) is always absolute
with the recent changes, sometimes the path may not start with a '/'.
This ensures that %s is ALWAYS an absolute path.
2021-04-30 19:07:37 +00:00
Omar Polo fdea6aa0bc allow ``root'' rule to be specified per-location block 2021-04-30 17:16:34 +00:00
Omar Polo adbe6a6493 always use ensure_absolute_path and improve its error message 2021-04-30 16:17:07 +00:00
Omar Polo 17a04409b5 typo in docker example 2021-04-29 19:52:50 +00:00
Omar Polo 8070ffa70f typo (again) 2021-04-29 19:50:25 +00:00
Omar Polo 7a4f742657 typo 2021-04-29 19:48:47 +00:00
Omar Polo 08ce6f5273 tweaked the readme a bit 2021-04-29 19:46:51 +00:00
Omar Polo d06d6f4bba sort tokens 2021-04-29 19:13:16 +00:00
Omar Polo 6a5e709beb typo: missing Ic 2021-04-29 18:29:55 +00:00
Omar Polo cc8c2901ad added ``alias'' option to define hostname aliases for a server 2021-04-29 18:23:35 +00:00
Omar Polo 8e8b2e252c pidfile support with `-P pidfile' 2021-04-28 12:45:22 +00:00
Omar Polo 2ef7f631db wait for logger after fatal()
With -f, when the main process exits after a fatal() (usually) the
shell prompt is printed before the logger message.

This adds a small poll to wait for the logger process to exit.
2021-04-28 12:43:17 +00:00
Omar Polo 48b69cb2dc fix some logging
- we can't use log_* or fatal() before logger_init
- err -> errx if errno isn't involved
2021-04-28 12:43:17 +00:00
Omar Polo d89a9060f8 fatal: send log to logger 2021-04-28 12:43:17 +00:00
Omar Polo 42447f673f send priority to the logger process 2021-04-28 12:43:17 +00:00
Omar Polo 9cc630aa63 added ``env'' option to define environment vars for CGI scripts 2021-04-28 12:43:17 +00:00
Omar Polo e6ca8eb156 lower log priority for fatal errors from CRIT to ERR
found by Anna, thanks!
2021-04-28 12:43:17 +00:00
Omar Polo 419a423520 keep verbosity level after config reload 2021-04-28 12:42:36 +00:00
Omar Polo c79b63f580 typo 2021-04-27 07:14:00 +00:00
Omar Polo 1b333122a9 typo 2021-04-26 20:58:06 +00:00
Omar Polo 578f13e4b2 add compat for reallocarray 2021-04-25 12:50:19 +00:00
Omar Polo b5fdc32e75 use memcpy instead of bcopy
memcpy is ANSI C89, bcopy seems to be an history BSD function.
2021-04-25 12:49:21 +00:00
Omar Polo 5aba63956a d->d_namelen doesn't seem to be available on linux 2021-04-25 12:45:17 +00:00
Omar Polo e76f2c74b8 don't save the directory fd in c->pfd
scandir_fd already calls closedir, which in turns closes the fd
2021-04-25 12:19:06 +00:00
Omar Polo 11c986679a sort the auto index alphabetically 2021-04-25 12:06:54 +00:00
Omar Polo bb4be662f1 specify the develop process 2021-04-25 12:01:34 +00:00
Omar Polo 072e3557c4 build gmid statically inside the docker container 2021-04-24 19:54:22 +00:00
Omar Polo 0bfdcb1799 missing comma , 2021-04-22 19:10:08 +00:00
Omar Polo 4d820b6cd2 use `text/x-patch' for .patch and .diff files 2021-04-21 07:51:29 +00:00
Omar Polo 5d1474a561 typo in comment 2021-04-20 09:53:33 +00:00
Omar Polo 3841a36930 restore signal handlers before exec'ing CGI scripts 2021-04-20 09:53:03 +00:00
Omar Polo 74c0c7e4ce rename reschedule_* to yield_* 2021-04-20 09:40:09 +00:00
Omar Polo 3f47867e1d typo... in the program name...
Just how retarded can I be?
2021-04-16 13:28:56 +00:00
Omar Polo b7a9906151 typo 2021-04-15 20:14:18 +00:00
Omar Polo dafaf46734 change description of `entrypoint' 2021-04-15 20:13:44 +00:00
Omar Polo 1de922891b typos 2021-04-15 19:43:46 +00:00
Omar Polo 548c2849fa hide output of etags
even the message "sh: etags: not such file or directory" or whatever
seems to be confusing for users, so silent it.

(maybe it would be better not to automatically generate the TAGS, but
it's so handy...)
2021-04-14 15:06:27 +00:00
Omar Polo b33425827e print the datetime when logging to stderr 2021-04-14 14:52:47 +00:00
Omar Polo 89541eeec0 define TLS_VERSION, TLS_CIPHER and TLS_CIPHER_STRENGTH for CGI scripts 2021-04-13 06:59:54 +00:00
Omar Polo b211d0f7d9 sync changelog 2021-04-12 21:13:51 +00:00
Omar Polo 9d092b607a fix IRI-parsing bug
Some particularly crafted IRIs can cause a denial of service (DOS).
IRIs which have a trailing `..' segment and resolve to a valid IRI
(i.e. a .. that's not escaping the root directory) will make the
server process loop forever.

This is """just""" an DOS vulnerability, it doesn't expose anything
sensitive or give an attacker anything else.
2021-04-12 20:11:47 +00:00
Omar Polo f2522b4313 check if sys/queue.h provides TAILQ_FOREACH_SAFE 2021-03-31 16:52:07 +00:00
Omar Polo b8e64ccd44 list instead of fixed-size array for vhosts and locations
saves some bytes of memory and removes the limit on the maximum number
of vhosts and location blocks.
2021-03-31 16:32:18 +00:00
Omar Polo e0ebdf2d94 sync changelog 2021-03-31 13:58:31 +00:00
Omar Polo 7e1df73d7d fix mkdirs: create directories recursively 2021-03-31 13:56:58 +00:00
Omar Polo 33b455ada3 sync changes 2021-03-31 13:23:29 +00:00
Omar Polo 2ddc927113 mention the `static' target 2021-03-29 10:13:17 +00:00
Omar Polo 8b462a5d17 fix static target 2021-03-29 09:42:06 +00:00
Omar Polo c836cdfadb handle CGI scripts that replies with the maximum header length allowed
the 1024 bytes limits is for the META only, not for the whole
response.  That means that the maximum size for the header line is
1029!
2021-03-29 09:42:06 +00:00
omar-polo 071dce449d
Add missing libevent-dev dependency
Merge pull request #2 from kornellapacz/master, thanks!
2021-03-29 11:01:38 +02:00
Korneliusz Łapacz c928dcc7ba add missing libevent-dev dependency 2021-03-29 05:35:32 +02:00
Omar Polo c8249bada3 gmid v1.6 -- "Stargazers" 2021-03-20 12:04:23 +00:00
Omar Polo b890467e60 typo 2021-03-20 11:46:12 +00:00
Omar Polo 94be0bf03b update README 2021-03-20 11:08:17 +00:00
Omar Polo 8e09ee1d50 typo 2021-03-20 10:48:11 +00:00
Omar Polo b9c9123b8e fix signal handling so it works on linux too
it seems that linux calls the signal handlers even when we're waiting
on sigwait for that signal.  Work around that.
2021-03-20 10:43:23 +00:00
Omar Polo e3d81f49cc [seccomp] allow prlimit64
it's needed by getdtablesize, at least on glibc
2021-03-20 09:24:44 +00:00
Omar Polo d632468df5 fix correct shutdown after SIG{INT,TERM} 2021-03-20 09:23:53 +00:00
Omar Polo 7e3aaf3752 sync changelog 2021-03-20 08:54:03 +00:00
Omar Polo 052c9d2403 kill only $pid during regress testing
before we did `pkill gmid', but that would influence also other
instances of gmid!
2021-03-20 08:51:58 +00:00
Omar Polo 1d3eb470b0 quit every process cleanly when receiving SIGINT or SIGTERM 2021-03-20 08:51:17 +00:00
Omar Polo 62e001b067 move all sandbox-related code to sandbox.c
while there, add capsicum for the logger process
2021-03-20 08:42:08 +00:00
Omar Polo ad5301d1a0 sync ChangeLog 2021-03-20 07:46:47 +00:00
Omar Polo bc99d868bc refactoring: imsg everywhere
use imsg to handle ALL kinds of IPC in gmid.  This simplifies and shorten the
code, and  makes everything more uniform too.
2021-03-19 19:21:29 +00:00
Omar Polo 1fbac5ba7c handle SIGHUP gracefully
i.e. don't print scary messages with LOG_CRIT priority!
2021-03-03 17:52:13 +00:00
Omar Polo dbe262a45d split usage string into two lines 2021-03-03 17:51:42 +00:00
Omar Polo de62c38d81 fix out-of-bounds access
obviously msg[datalen] is an off-by-one
2021-03-03 17:51:30 +00:00
Omar Polo 2c3e53dac6 give each server process its own socket for the executor
this fixes a bug introduced with the prefork mechanics: every server
process shared the same socket, and this would cause a race condition
when multiple server processes asked for a script cgi being executed.

This gives each server process its own socket to talk to the executor,
so the race cannot happen.
2021-03-03 17:22:01 +00:00
Omar Polo fda7b99fc7 include sys/types.s to fix the build on OpenBSD 2021-02-23 14:07:49 +00:00
Omar Polo 290b5baa97 updated readme 2021-02-23 13:22:29 +00:00
Omar Polo 9899a837af
[seccomp] allow sendmsg 2021-02-23 13:44:20 +01:00
Omar Polo 17458f11e1
sync global variables 2021-02-23 13:44:20 +01:00
Omar Polo 376a540764
move log_init & vars to gmid.c, retain logger_main in log.c
this is to let the regression suite compile
2021-02-23 13:44:20 +01:00
Omar Polo aa627c91fc
checks for compat + don't initialise HAVE_* 2021-02-23 13:44:20 +01:00
Omar Polo 4f8128452c
fix various error in compat/err.c 2021-02-23 13:43:53 +01:00
Omar Polo a3c5f7451a
make sure -o is passed to ${CC} in suffix rule 2021-02-23 13:43:33 +01:00
Omar Polo 6b78d24c31
clean objects in compat dir 2021-02-23 13:43:33 +01:00
Omar Polo 3c56e99ab0
add compat/*.o
got doesn't seem to match (say) compat/freezero.o with *.o
2021-02-23 13:43:33 +01:00
Omar Polo 4604dc9671
move vhost_should_log call to server.c
log.o is linked to some regress/ stuff.  Calling from there a vhost_*
function means that we should link the regress/stuff to server.o too
(and that would pull in other stuff...).  Moving the call is easier,
and also probably better.
2021-02-23 13:43:33 +01:00
Omar Polo 793835cb26
add `log on/off' to enable/disable logs per-location 2021-02-23 13:43:24 +01:00
Omar Polo fd9a486925
don't directly include sys/queue.h 2021-02-23 13:43:23 +01:00
Omar Polo 6b191ed52a
tests and compat for imsg 2021-02-23 13:43:14 +01:00
Omar Polo d278a0c3c5
moving logging to its own process 2021-02-23 13:40:59 +01:00
Omar Polo c39b26d308 mark reschedule_write inline & static 2021-02-12 20:25:48 +00:00
Omar Polo eecad7a3ca other s/fnmatch/matches 2021-02-12 19:51:54 +00:00
Omar Polo dfa6d77b70 typo 2021-02-12 12:55:29 +00:00
Omar Polo 5b6dc93c8a kill debug printf 2021-02-12 12:51:47 +00:00
Omar Polo 7689871446 include config.h first 2021-02-12 12:49:36 +00:00
Omar Polo 52418c8d82 fix various compilation errors
Include gmid.h as first header in every file, as it then includes
config.h (that defines _GNU_SOURCE for instance).

Fix also a warning about unsigned vs signed const char pointers in
openssl.
2021-02-12 12:47:20 +00:00
Omar Polo 3cb3dd4d42 accept4 -> accept
accept4(2) isn't part of any standard (even though it'll be part in
the future) and raises warnings on some linux distro.  Moreover, we
don't have thread that may fork at any time, so doing a mark_nonblock
after isn't a big deal.
2021-02-12 11:59:03 +00:00
Omar Polo 9356f61a63 BUFSIZ is variable, we need *exactly* 1024
Using BUFSIZ in sbuf is not OK.  It's variable, and in various places
we assume that sbuf is 1024 (like handle_cgi_reply).  We could patch
those, but we aren't sure BUFSIZ is >= 1024!  Let's keep the hardcoded
number.

(found by debugging on arch on amd64, where BUFSIZ is bigger)
2021-02-12 11:56:13 +00:00
Omar Polo 5e3285d52e typo 2021-02-12 11:34:17 +00:00
Omar Polo 98ee8406aa fix occurrence of (killed) load_file 2021-02-12 11:32:49 +00:00
Omar Polo a4188b7865 echo parsing test result 2021-02-12 11:27:52 +00:00
Omar Polo 27b2fa9ae5 don't mmap
Before we mmap(2) file for reading, and use a buffer to handle CGI
scripts.  Turns out, for sequential access over the whole mmap isn't
better than our loop on read.  This has also the additional advantage
that we can use handle_cgi (now handle_copy) for both files and CGI,
which is pretty cool.

This also fixes a nasty bug where we could hang a connection forever,
because we scheduled the wrong type of event (read on POLLOUT and
write on POLLIN, it's the other way around!)
2021-02-12 11:27:33 +00:00
Omar Polo a6e689d745 fix config reload
the old server processes would stick around waiting on the signals
events.  While there, also drop the `struct server_events' and define
events as globals.
2021-02-12 08:50:25 +00:00
Omar Polo e1b4612359 dup line 2021-02-12 08:49:31 +00:00
Omar Polo a72cc6be31 improve compat/getprogname on GNU libc
but why'd they called it program_invocation_short_name?  They couldn't
find a longer name?
2021-02-11 09:13:58 +00:00
Omar Polo 4d7043e33c add check for program_invocation_short_name 2021-02-11 09:12:40 +00:00
Omar Polo 1606927ea1 add -levent in the example 2021-02-11 09:12:10 +00:00
Omar Polo 8e56d6adc4 use fatal instead of err/fprintf+exit
fatal logs to the correct place, err only on stderr.
2021-02-11 09:07:28 +00:00
Omar Polo 2a911637be fix compilation on OSes without sandbox 2021-02-11 09:04:47 +00:00
Omar Polo 0b040b9909 fix setproctitle detection on FreeBSD 2021-02-10 22:17:20 +00:00
Omar Polo 4ad0601d18 link to compat/ even in regress
this unlocks the full regress tests on arch, for instance, or every
OS where we don't have strl*
2021-02-10 22:06:16 +00:00
Omar Polo 6827d2781e [seccomp] allow newfstatat and gettimeofday
these are required to run on arch linux (at least)
2021-02-10 19:20:59 +00:00
Omar Polo 4c857c0afc [seccomp] epoll_wait(2) isn't available on every arch 2021-02-10 18:02:08 +00:00
Omar Polo aa37287565 add newline after usage 2021-02-10 17:58:43 +00:00
Omar Polo 49b73ba1ab fix "first location" bug
reported by devel at datenbrei dot de.  The first location would
overwrite the default value for a server, triggering the "`foo' rule
specified more than once" error.  This also needed a small tweak on
how we match locations to avoid breaking other tests.
2021-02-10 16:37:08 +00:00
Omar Polo 2898780aea fix ca generation 2021-02-10 15:09:08 +00:00
Omar Polo 4283d65fc1 don't allocate BIGNUM on the stack
on fedora 33 the BIGNUM type is opaque.  Allocate always to avoid headaches.
2021-02-10 14:25:39 +00:00
Omar Polo f6b9a079e3 allow epoll_wait
fedora 33 issue an epoll_wait instead of pwait.
2021-02-10 14:21:56 +00:00
Omar Polo 2d34f732f1 typo 2021-02-10 13:13:17 +00:00
Omar Polo 002a84a123 improve errors during config parsing 2021-02-10 11:53:05 +00:00
Omar Polo d3f073c9ed don't delete valid.ext 2021-02-09 22:40:04 +00:00
Omar Polo eb59f87e67 sort 2021-02-09 22:38:10 +00:00
Omar Polo 02be96c6dd add `require client ca' rule to require certs signed by a CA 2021-02-09 22:30:04 +00:00
Omar Polo 2ff026b09b add conf for a ca 2021-02-09 22:12:19 +00:00
Omar Polo 31b3662c54 gg: add support for client certs 2021-02-09 15:01:12 +00:00
Omar Polo 57ec3e776e refactor apply_block_return
move the strip and fmt logic to their own function
2021-02-08 20:50:30 +00:00
Omar Polo 747b35d0da align 2021-02-08 18:54:34 +00:00
Omar Polo b9231167fb mention libevent 2021-02-08 18:47:36 +00:00
Omar Polo c214d1ab67 allow sigreturn and sigaction on linux 2021-02-08 18:39:23 +00:00
Omar Polo df58efff26 fix seccomp for the new event loop
add/remove syscalls from the BPF filter and move sandbox() after
libevent initialisation
2021-02-08 12:46:46 +00:00
Omar Polo d090dc8491 define config_path as global variable 2021-02-08 12:44:34 +00:00
Omar Polo 319b7fa909 define conf in the test program, not in puny.c 2021-02-08 12:43:36 +00:00
Omar Polo 1516fd4dad fix detection on non-openbsd systems 2021-02-08 10:28:56 +00:00
Omar Polo 337c74209b look for libevent 2021-02-08 10:12:04 +00:00
Omar Polo 0b8133c264 add check for libevent 2021-02-08 10:11:23 +00:00
Omar Polo abc007d2b3 rewrite main loop using libevent 2021-02-08 10:01:45 +00:00
Omar Polo b63e30ff44 define TLS_CLIENT_NOT_BEFORE/NOT_AFTER in CGI scripts 2021-02-07 21:47:01 +00:00
Omar Polo 9f006a2127 [cgi] split the query in words if needed and add them to the argv 2021-02-07 18:55:04 +00:00
Omar Polo a13739138b add trailing \n in yyerror 2021-02-07 18:08:50 +00:00
Omar Polo 7e9b7812b3 move logging code to log.c 2021-02-07 16:15:51 +00:00
Omar Polo 3077ce5bee don't fprintf 2021-02-07 16:10:09 +00:00
Omar Polo a64959c99a use log_err instead of fprintf 2021-02-07 16:09:49 +00:00
Omar Polo 3abf91b0b4 improve logs management 2021-02-07 15:30:28 +00:00
Omar Polo a709ddf5eb added prefork option 2021-02-07 12:05:32 +00:00
Omar Polo cfb8a77fd4 handle also EAGAIN together with EWOULDBLOCK 2021-02-07 12:04:11 +00:00
Omar Polo 2564eddee4 add set -e to env script
It avoids some long sequences of "echo: IO error" when the client
close the connection before EOF.
2021-02-07 11:32:53 +00:00
Omar Polo 809c4bd0f8 keep original CFLAGS 2021-02-07 11:28:42 +00:00
Omar Polo eef0492e35 add missing declaration of TENTRYPOINT 2021-02-07 11:27:15 +00:00
Omar Polo 60dbafbf2f define MANDIR 2021-02-07 09:24:00 +00:00
Omar Polo 1487e11eda drop sample.conf and add something to the README.md 2021-02-06 19:05:40 +00:00
Omar Polo 2b92a4477a reorder stuff 2021-02-06 18:50:42 +00:00
Omar Polo 3704b36bda add script for the regress suite 2021-02-06 18:29:36 +00:00
Omar Polo 6c6f242a54 escape % 2021-02-06 18:28:56 +00:00
Omar Polo e3ddf39095 add the ``entrypoint'' option 2021-02-06 18:28:43 +00:00
Omar Polo afc025ff60 restart the daemon instead of kill+start
this way we can also test the reload capability!
2021-02-06 17:35:48 +00:00
Omar Polo cd76162494 swap check in vhost_* fns
it's faster (statistically speaking) to first compute if the option is
set and then fnmatch than the inverse.  This way we can avoid
unnecessary fnmatch.
2021-02-06 17:31:03 +00:00
Omar Polo 6abda252e9 added ``block return'' and ``strip'' options 2021-02-06 17:22:37 +00:00
Omar Polo daac4a9452 fix auto index precedence 2021-02-06 14:36:26 +00:00
Omar Polo 4125c94fda make sure @ is allowed, and rephrase another test 2021-02-06 13:57:12 +00:00
Omar Polo 52053e1a61 ensure CGI stdout it's blocking 2021-02-06 09:56:34 +00:00
Omar Polo 19e7bd00a3 [iri] accept also : and @
again, to be RFC3986 compliant.
2021-02-06 09:33:48 +00:00
Omar Polo 8404ec301f don't %-decode the query 2021-02-05 14:31:53 +00:00
Omar Polo 709f4c9447 alter the string, don't advance pointer
if we advance the pointer, we crash during reload due to an invalid
address given to free.  altering the string is the way.
2021-02-04 20:04:12 +00:00
Omar Polo 102d2e9cce missing argument for LOGI 2021-02-04 18:09:42 +00:00
Omar Polo 9543e3fbaf avoid race-condition
what if we receive a SIGHUP right after unblock_signal (or during the
whole block_signals...unblock_signals) but *before* the wait_sighup?
Yeah.
2021-02-04 18:09:30 +00:00
Omar Polo 38d4db740d improve manpage 2021-02-04 14:38:37 +00:00
Omar Polo fe90d24fd0 add compat for setproctitle
This adds a check for setproctitle and for the (linux) prctl
PR_SET_NAME.  If setproctitle is not available, on linux we provide an
implementation that use prctl (taken from tmux compat layer.)
2021-02-04 13:42:35 +00:00
Omar Polo 86edc4f400 mention configuration reload 2021-02-04 13:34:43 +00:00
Omar Polo 3007f56596 mention SIGHUP in manpage 2021-02-04 13:34:27 +00:00
Omar Polo ca21e10043 reload configuration on SIGHUP 2021-02-04 13:23:15 +00:00
Omar Polo 1e3ef7ab4f use upper bound given by poll
it's a waste to loop through all fds.  We know the *exact* number of
clients that needs attention, so use that information to limit the
looping.
2021-02-03 21:14:48 +00:00
Omar Polo e824d03efa drop unnecessary check around close 2021-02-03 16:53:34 +00:00
Omar Polo 4e2e2ab1d3 refactor executor_main
now it's symmetrical to listener_main().
2021-02-03 16:37:53 +00:00
Omar Polo d672b8fba1 refactoring startup logic 2021-02-03 16:28:00 +00:00
Omar Polo 9b8f5ed2c0 revert commit 346f28eeaa
keep mark_nonblock in utils.c, as otherwise the build for the regress
suite will fail (mark_nonblock needs fatal which is in gmid.c, and
we can't link gmid.o with the regress suite...)
2021-02-03 14:16:39 +00:00
Omar Polo 9edb828251 drop privileges after the fork 2021-02-03 14:13:32 +00:00
Omar Polo 4ee08bd148 fix location example 2021-02-03 14:01:00 +00:00
Omar Polo 346f28eeaa move mark_nonblock to utils.c 2021-02-02 23:03:33 +00:00
Omar Polo fe40638928 mark various functions as static
By marking all those function as static, the compiler is free to do
more optimizations.  In addition, those functions are not used outside
server.c
2021-02-02 23:01:09 +00:00
Omar Polo 87f2b68b58 cgi now follows globbing rules 2021-02-02 22:38:35 +00:00
Omar Polo 5b4a641893 let LEX and YACC be customizable via args 2021-02-02 09:58:05 +00:00
Omar Polo b1d52c3105 ignore clangd' compile_flags.txt 2021-02-02 09:55:11 +00:00
Omar Polo 5f715ce43f print the header in the directory listing 2021-02-02 09:48:32 +00:00
Omar Polo 3c680bddab configless: fixing the case of the implicit "."
I got bitten by the scope visibility rules.  After the end of the
block, the path variable is no longer valid, and in fact later
load_vhosts fails to open that (because the buffer gets invalidated)
2021-02-02 09:30:24 +00:00
Omar Polo 35744950aa simplify handle_cgi
Now that I got rid of the enum+switch, adding more state is easier.
Before, we used an hack to remember if we had read the CGI reply or
not (c->code = -1).

This introduces a new state, handle_cgi_reply that reads the CGI
script reply, logs it, and only then switches to handle_cgi.
handle_cgi itself is cleaner, now it only reads into c->sbuf and send
what it had red.

We even get, almost for free, the 42 error.  If read exists with -1 or
0 from in handle_cgi_reply, we return a proper error to the client.
We can extend this further in the future and also try to validate the
CGI reply (for now we're only looking for a \n).
2021-02-01 22:04:51 +00:00
Omar Polo b06f80cdf4 switch to handle_open_conn right after handshake
So we don't re-enter the handle_handsahke and re-do the loop on
fnmatch etc.  This way, once we're successfully past the handshake,
we'll re-enter no handle_open_conn.
2021-02-01 20:27:08 +00:00
Omar Polo 92da82858b document the DFA 2021-02-01 20:24:14 +00:00
Omar Polo 112802ea31 client state machine: function pointers instead of enum+switch 2021-02-01 20:00:33 +00:00
Omar Polo 1e7591a922 parse_err is a const pointer 2021-02-01 19:58:04 +00:00
Omar Polo ad58806e30 drop unused target iri_test 2021-02-01 19:30:55 +00:00
Omar Polo d5f4d615cf generate compile_flags for clangd 2021-02-01 19:30:38 +00:00
Omar Polo 3e5418099f [cgi] always set some variables 2021-02-01 13:48:23 +00:00
Omar Polo ee655e64d7 sync CGI section with actual implementation 2021-02-01 13:19:06 +00:00
Omar Polo 38f8a798b1 mention v1.5 2021-02-01 12:30:34 +00:00
Omar Polo 67528c1f82 don't add the query to argv
FRC3875 says that if the query does not contain any unecnoded "="
characters, we SHOULD treat the query string as a "search-string",
split in on "+" and add every word to the CGI argv.

In launch_cgi it's too late because iri->query is the *decoded* query!

I have in mind some refactoring around how we decode things, so this
is postponed.
2021-02-01 12:28:30 +00:00
Omar Polo 2fafa2d23e bring the CGI implementation in par with GLV-1.12556 2021-02-01 11:11:43 +00:00
Omar Polo e17642a7bb require absolute paths in config file 2021-02-01 11:08:57 +00:00
Omar Polo bcf5d929e6 ensure absolute paths in config-less mode 2021-02-01 11:07:57 +00:00
Omar Polo 6ff23c6739 use absolute paths in the regression suite 2021-02-01 11:04:21 +00:00
Omar Polo 7808978667 fix computed offset
Oh my, this is such a stupid mistake.  It went undiscovered only
because I always used CGI scripts on the first vhost (and hence the
offset would be 0) and never on the others.
2021-02-01 08:38:48 +00:00
Omar Polo 57d0d0adba ensure iri.host isn't NULL 2021-01-31 11:50:01 +00:00
Omar Polo ee29b5a63b sync 2021-01-31 11:09:15 +00:00
Omar Polo 8503a1431d set cloexec status on the socketpair fds
the executor forks to spawn the cgi scripts, and they inherit the
socket for communication with the listener process.  Make that
impossible.
2021-01-31 11:07:12 +00:00
Omar Polo 91d7870bb7 typo 2021-01-30 14:38:32 +00:00
Omar Polo 1b246dbd3c remove unused target
"test" was replaced by "regress" a while ago
2021-01-30 14:37:57 +00:00
Omar Polo 39a7b61145 sync date 2021-01-30 12:16:18 +00:00
Omar Polo e99923477a sync 2021-01-30 12:13:49 +00:00
Omar Polo b59f3cdd27 typo 2021-01-30 12:12:37 +00:00
Omar Polo 6016a593a3 invert the location precedence: first match wins
It's how httpd(8) does it, and it allows us to call fnmatch less time
2021-01-30 12:04:20 +00:00
Omar Polo 601bc1cc37 readme updates 2021-01-30 11:49:48 +00:00
Omar Polo 714685c10c manpage improvements 2021-01-30 11:49:27 +00:00
Omar Polo e7c7f19c4e more IRI tests
ensure non-encoded and pct-encoded hostnames are parsed correctly
2021-01-29 18:52:36 +00:00
Omar Polo cef60084e6 fix puny-test build 2021-01-29 18:51:35 +00:00
Omar Polo dceca3b69d sync 2021-01-29 17:29:21 +00:00
Omar Polo a8d4a89770 don't ignore punycode errors when decoding SNI-provided servname 2021-01-29 17:29:14 +00:00
Omar Polo 4a3ab60928 const-ify var + missing argument 2021-01-29 17:26:56 +00:00
Omar Polo 117ac52cdd accept a wider range of UNICODE codepoints while parsing hostnames 2021-01-29 17:26:23 +00:00
Omar Polo a2fd801327 puny_decode: set an error string 2021-01-29 17:11:03 +00:00
Omar Polo 22a742e4cb typo 2021-01-29 17:07:26 +00:00
Omar Polo 32693ee6db include string.h
openbsd and freebsd yacc include string.h in the generated sources,
bison (tested on alpine at least) don't
2021-01-28 17:50:22 +00:00
Omar Polo 415ac7a229 fix build 2021-01-28 16:33:20 +00:00
Omar Polo 75fbb1789e manual improvements & add section about MIME 2021-01-28 16:29:06 +00:00
Omar Polo 90cb9eea8a don't log the SNI & matching
I'll re-enable this when i'll improve the logging
2021-01-28 16:28:44 +00:00
Omar Polo 8ef09de3d0 don't include err.h, gmid.h (via config.h) does that 2021-01-28 16:28:10 +00:00
Omar Polo cbeee4cad1 warn about punycode in server definition 2021-01-28 16:27:40 +00:00
Omar Polo 9a672b3712 legibility: use p[n] instead of (*(p + n)) 2021-01-28 16:26:49 +00:00
Omar Polo 35cf19e3f3 punycode: refactoring
use the correct term "label" instead of "component" when speaking
about the parts of a hostname.  Also, fix the sizes for max hostname
and label.
2021-01-28 16:25:59 +00:00
Omar Polo 473e4531d5 delete unused constant 2021-01-28 16:24:48 +00:00
Omar Polo d2b941f34b correct copyright date 2021-01-28 16:24:03 +00:00
Omar Polo ef0f2ae88c remove unused function 2021-01-28 14:21:57 +00:00
Omar Polo 8904fa0e64 -v for verbose logging 2021-01-27 23:14:16 +00:00
Omar Polo 08a2fee055 the daemon option was removed 2021-01-27 17:02:45 +00:00
Omar Polo 747f13af33 add certificate generation in features list 2021-01-27 16:59:54 +00:00
Omar Polo 13ed2fb63f improve configuration parsing error 2021-01-27 16:45:45 +00:00
Omar Polo 40dc608f1f adjust CFLAGS detection 2021-01-27 16:05:55 +00:00
Omar Polo fe5967cd02 const-ify strings in struct location 2021-01-27 15:53:30 +00:00
Omar Polo 0cd6675437 compiler warnings 2021-01-27 15:41:33 +00:00
Omar Polo b736ca35f2 drop unused variable 2021-01-27 15:35:55 +00:00
Omar Polo 44ee1bac8b use starts_with in puny.c 2021-01-27 15:35:09 +00:00
Omar Polo 22c6d6334d log info about SNI, punycode and matched vhost 2021-01-27 15:06:15 +00:00
Omar Polo caad03081b some null checks 2021-01-27 15:05:50 +00:00
Omar Polo c4f682f855 trim_req_iri: set error string 2021-01-27 15:05:16 +00:00
Omar Polo 72bbed9136 make configless mode works again. also accept every host 2021-01-27 15:00:55 +00:00
Omar Polo 58173ca20e customizable servname & verbose + misc tweaks
* add -H for explicit servname (for SNI)
* add -v to print also the request
* misc tweaks: move *flag variables to global scope (initialisation to
  0 for free!) and explicit handshake.
2021-01-27 14:48:23 +00:00
Omar Polo 286c4f40a5 readme tweaks
fix features list and talk about I18N
2021-01-27 14:42:40 +00:00
Omar Polo 46af8c6cfe drop the daemon config in favour of the -f flag
Now it daemonize by default when running with a config, unless the -f
flag is given.
2021-01-27 12:04:37 +00:00
Omar Polo 7957cbd9aa const-ify puny_decode (and add puny.c) 2021-01-27 11:21:23 +00:00
Omar Polo 42650adec0 sync deps and sandbox info 2021-01-27 10:52:37 +00:00
Omar Polo 909ea500a4 typo 2021-01-27 10:52:11 +00:00
Omar Polo 3300cbe06a initial punycode support 2021-01-27 10:47:49 +00:00
Omar Polo 390a611893 sane defaults for the config-less mode 2021-01-27 10:43:55 +00:00
Omar Polo 2d3f837ac5 [seccomp] allow getrandom 2021-01-25 15:25:04 +00:00
Omar Polo 333a671107 gen compat for getprogname 2021-01-25 15:09:18 +00:00
Omar Polo 2b15ad2860 adding openssl test 2021-01-25 15:06:59 +00:00
Omar Polo 1078229283 unveil x the vhosts directories 2021-01-25 15:02:55 +00:00
Omar Polo 2d3cc76f6d we don't need unveil "x" in listener
not a big deal, since the pledge prohibits us to exec, but
nevertheless.
2021-01-25 14:58:54 +00:00
Omar Polo 6c117838be fix docker example 2021-01-25 14:55:03 +00:00
Omar Polo 0a0e6ba7f9 typo 2021-01-25 14:52:57 +00:00
Omar Polo 2f09adb085 readme addition 2021-01-25 14:52:19 +00:00
Omar Polo 71cf39757d added missing argument for -H 2021-01-25 14:32:16 +00:00
Omar Polo a5d04ea066 sync 2021-01-25 14:31:40 +00:00
Omar Polo f28d96d399 sync manpage with actual usage and improve examples 2021-01-25 14:31:34 +00:00
Omar Polo 8443bff77a rework the configless mode: change flags and generate certs 2021-01-25 14:08:31 +00:00
Omar Polo 0b00962d37 mention chroot 2021-01-25 10:36:21 +00:00
Omar Polo 145ba43e16 sync 2021-01-25 10:34:05 +00:00
Omar Polo ae08ec7da5 chroot & drop privileges 2021-01-25 10:30:07 +00:00
Omar Polo 2030e31486 order 2021-01-25 09:45:09 +00:00
Omar Polo f88311e534 [seccomp] allow fcntl F_SETFD
musl does a F_SETFD in its fdopendir
2021-01-24 19:12:32 +00:00
Omar Polo 252908e6bb added support for location blocks 2021-01-24 18:53:26 +00:00
Omar Polo c8b7433918 added support for location blocks 2021-01-24 14:11:40 +00:00
Omar Polo 501e489c90 initialize config 2021-01-24 12:45:22 +00:00
Omar Polo 8d6ae384be move configuration init stuff in its own function 2021-01-24 10:39:48 +00:00
Omar Polo 7b31a6386d test CGI with big files 2021-01-24 10:24:34 +00:00
Omar Polo 28ec617809 sync the CGI environment with the manpage
while there also add SERVER_PROTOCOL
2021-01-24 10:06:48 +00:00
Omar Polo ba7b2faa86 sync 2021-01-24 09:55:19 +00:00
Omar Polo 07b0a14218 void-ify some functions
their return value is no longer used, it's only confusing at this point.
2021-01-24 09:54:44 +00:00
Omar Polo 0baf6bed2a pass the fd, not the path! 2021-01-24 09:53:54 +00:00
Omar Polo a87f662565 refactoring state management
instead of having a flag to discern between two different behaviours
in S_SENDING, split that state into S_SENDING_FILE and S_SENDING_CGI
(this will also make it easier in the future to add other sending
states).  While there, also get rid of `goodbye' and make start_reply
advance the state machine by itself.
2021-01-24 09:49:09 +00:00
Omar Polo a7b9bb4d24 specify in which directory CGI scripts are executed 2021-01-24 09:20:38 +00:00
Omar Polo 05748e49fe chdir to the vhost root before exec'ing the CGI script 2021-01-24 09:15:05 +00:00
Omar Polo e7a2a99b5a added index option 2021-01-24 09:14:01 +00:00
Omar Polo 9adde3d8b2 reuse the same buffer for the request and response 2021-01-23 17:45:56 +00:00
Omar Polo 6cdecad88d fix test portability
wc return 2048 on fedora (it doesn't count the trailing \n), while on
OpenBSD, Void linux on aarch64 and Alpine linux on vmd(8) returns
2049.
2021-01-23 17:44:23 +00:00
Omar Polo f62aab517d handle TLS_WANT_POLL{IN,OUT}
libtls doesn't seem to return when doing blocking I/O, but libretls
does every single time.
2021-01-23 17:43:04 +00:00
Omar Polo 2349b02b26 bump the key size a little
on fedora with libretls (running on top of openssl) it won't open the
key otherwise.
2021-01-23 17:42:04 +00:00
Omar Polo c6b9ddecf6 gg may need some compat 2021-01-23 17:39:12 +00:00
Omar Polo ce06cdc5bd drop gg.py 2021-01-23 17:11:16 +00:00
Omar Polo 31a4993a94 use gg instead of gg.py for the regression suite 2021-01-23 17:10:00 +00:00
Omar Polo d760973a5b don't alter the struct iri 2021-01-23 17:07:52 +00:00
Omar Polo 497977d501 add gg, a barebones Gemini client 2021-01-23 16:56:24 +00:00
Omar Polo 3309ef975c accumulate the whole response line for CGI scripts 2021-01-23 15:32:38 +00:00
Omar Polo 45b4aa6e57 fix local libretls example 2021-01-23 12:22:09 +00:00
Omar Polo 1a49166de4 fix date 2021-01-23 11:29:02 +00:00
Omar Polo e29dbd7217 added missic copyright notice 2021-01-23 11:28:44 +00:00
Omar Polo 7f740a1440 typo 2021-01-22 17:54:37 +00:00
Omar Polo e0e15c30d7 simplify glob for pem 2021-01-22 17:41:12 +00:00
Omar Polo dd8cc7d3ce mention testing in readme 2021-01-22 17:28:39 +00:00
Omar Polo c421bd46ed drop genbigfile 2021-01-22 17:24:33 +00:00
Omar Polo 609fc9f9d9 use a c program to fill the file
it's several order of magnitude faster than the equivalent shell script
2021-01-22 17:18:55 +00:00
Omar Polo 99f95f7762 fix for sha256sum 2021-01-22 17:14:40 +00:00
Omar Polo 7e26256394 fixing compat compilation
include headers if needed and include config.h
2021-01-22 17:00:56 +00:00
Omar Polo 3527b7a58a missing ; in prototype 2021-01-22 16:53:21 +00:00
Omar Polo 30008a68d3 deleted old test stuff 2021-01-22 16:48:18 +00:00
Omar Polo 5c2e310ede brand new regress suite 2021-01-22 16:48:04 +00:00
Omar Polo f890c8c54d use a helper to handle no-body replies 2021-01-22 13:58:54 +00:00
Omar Polo 36162ed86d rename goodbye -> close_conn 2021-01-22 13:45:27 +00:00
Omar Polo 42bbdc7978 trim initial forward slashes
this parse gemini://example.com///foo into an IRI whose path is
"foo".  I'm not 100% this is standard-compliant but:

1. it seems a logical consequence of the URI/IRI cleaning algo (where
   we drop sequential slashes)
2. practically speaking serving file a sequence of forward slashes
   doesn't really make sense, even in the case of CGI scripts
2021-01-21 22:48:16 +00:00
Omar Polo f77a8c867e add ends_with 2021-01-21 22:45:49 +00:00
Omar Polo d1ca3911d2 fix redirects
make sure redirect starts with /.  This also requires a tweak in
check_path, in the case we go open_file -> send_dir -> open_file ->
check-path and the path starts with a slash.
2021-01-21 22:44:41 +00:00
Omar Polo 06f233ad8a ensure we're in the correct state 2021-01-21 19:46:36 +00:00
Omar Polo 0ac2dceccc sync changes 2021-01-21 16:19:11 +00:00
Omar Polo 2d7abb7017 add missing getprogname test + eventually include err.h 2021-01-21 16:15:35 +00:00
Omar Polo ea28a44931 const-ify parameter to strtonum 2021-01-21 16:15:10 +00:00
Omar Polo 95210bb396 use recallocarray
it also does an overflow check on multiplication, other than being
more readable.
2021-01-21 16:14:01 +00:00
Omar Polo a5a09e44b2 use strtonum 2021-01-21 16:07:12 +00:00
Omar Polo 5c342d059f more OpenBSD goodies 2021-01-21 15:37:02 +00:00
Omar Polo b2a6b61371 rename mimes to mime ; pass config esplicitly to mime* functions 2021-01-21 15:01:37 +00:00
Omar Polo 20c9ff466b typo 2021-01-21 14:51:09 +00:00
Omar Polo 33ac26a0e5 fix prototypes 2021-01-21 14:49:11 +00:00
Omar Polo 642b37d5ac don't include config.h there 2021-01-21 14:48:26 +00:00
Omar Polo ac69e83aab improve install target 2021-01-21 13:50:11 +00:00
Omar Polo fb2c20c301 fix dockerfile
now we have also some sub-directories, COPY . . is more future-proof
at this point.
2021-01-21 13:49:52 +00:00
Omar Polo 86939cb45b simplify CFLAGS/LDFLAGS autodetection 2021-01-21 13:40:22 +00:00
Omar Polo f980545b58 typo x 2 2021-01-21 13:16:14 +00:00
Omar Polo 04397b3293 typo 2021-01-21 13:14:55 +00:00
Omar Polo 8f0da58068 mention the configure script and how to link to a local libtls 2021-01-21 13:12:35 +00:00
Omar Polo 597ddf6ca8 typo 2021-01-21 13:01:54 +00:00
Omar Polo 12042ad700 add a configure script and some compat
tested on openbsd, alpine and void
2021-01-21 11:57:46 +00:00
Omar Polo 338f06f4e5 drop seccomp.h: not needed 2021-01-21 11:55:52 +00:00
Omar Polo 32befb74a9 drop mention to the static target
it works on OpenBSD but not on alpine, it requires further tweaks.
2021-01-21 09:19:13 +00:00
Omar Polo 1213c735bf sync 2021-01-21 09:17:19 +00:00
Omar Polo de27389e83 add globbing notice and sync example with actual flags 2021-01-21 09:17:13 +00:00
Omar Polo b4d409cf29 use fnmatch on the domain name 2021-01-21 09:07:26 +00:00
Omar Polo 15209ad9ed typo 2021-01-21 08:51:17 +00:00
Omar Polo ce79c944bc simplify flow when SNI is missing
this also gives a chance for users to use the default host even if SNI
is missing.
2021-01-21 08:46:07 +00:00
Omar Polo 0ab65593e2 don't crash on wrong vhost or missing SNI
the new logging code was crashing if the client didn't support SNI or
if required an unknown vhost: this because we short-circuit in
handle_handshake to an error, so c->iri isn't populated yet (we don't
even read the request).

fixes #1
2021-01-21 08:26:21 +00:00
Omar Polo 0be51733ef improve logs
now we log the full IRI requested (before was only the path) and the
response line (even for CGI).
2021-01-20 18:54:44 +00:00
Omar Polo 61f8d630c8 fmt 2021-01-20 16:22:35 +00:00
Omar Polo f2b3a5193f allow clock_gettime and a bit of fmt
alpine on amd64 (under OpenBSD vmd) tries to do a clock_gettime.  I
don't know why, but it doesn't seem a problem to allow it.
2021-01-20 16:19:54 +00:00
Omar Polo 3c0375e405 fix BPF 2021-01-20 16:09:04 +00:00
Omar Polo de4f713184 tighten the rules for fcntl
allow only the F_GETFL and F_SETFL commands
2021-01-20 15:54:26 +00:00
Omar Polo 298e4b96dc explain the poll mess 2021-01-20 15:44:11 +00:00
Omar Polo 2debfe2b1f drop useless field
was probably added to distinguish between AF_INET and AF_INET6, to use
different functions for logging.  But it wasn't really used, and now
we use getnameinfo anyway.
2021-01-19 18:49:36 +00:00
Omar Polo 710a71b2bb don't leak file descriptors 2021-01-19 18:16:09 +00:00
Omar Polo 6119e13e8a moving "default type" from global options to server options 2021-01-19 11:28:41 +00:00
Omar Polo df79b4c1d5 rename reason to meta 2021-01-19 11:00:06 +00:00
Omar Polo 05c23a54ea add "lang" server option 2021-01-19 10:58:29 +00:00
Omar Polo 17b09e3cb7 add a dockerfile 2021-01-18 23:15:45 +00:00
Omar Polo 94a79035ec __NR_poll doesn't seem to be defined on aarch64 2021-01-18 23:08:16 +00:00
Omar Polo 5bc29bc915 remove README.md target
leftover from when README.md was generated by gmid.1
2021-01-18 22:52:13 +00:00
Omar Polo d89aa7630d typo 2021-01-18 22:09:17 +00:00
Omar Polo e7ecf9a528 sync 2021-01-18 21:53:36 +00:00
Omar Polo eb6997835a trying to get the man on par with the recent changes 2021-01-18 21:52:01 +00:00
Omar Polo a010b0ddc5 move mimes into the struct config 2021-01-18 18:48:37 +00:00
Omar Polo 982069a120 add "mime" and "default type" option for the configuration 2021-01-18 18:43:47 +00:00
Omar Polo 0fbe79b33c improve mime handling
we still have an hardcoded list, but this implements the API needed to
modify the mappings.
2021-01-18 18:18:57 +00:00
Omar Polo 132cae8c6f removing err/warn functions with our fatal
err/warn is not available on some systems (unfortunately!) and in any
case don't play well with our daemon mode (that closes std{in,out,err}).
Use our fatal that is daemon-aware.
2021-01-18 18:15:58 +00:00
Omar Polo 568a09c220 fix requri construction
when we switched from one process to two, I introduced a small
optimisation: empty string are not send, so we receive NULL.
Constructing requri we need to make sure that relpath is not null.
2021-01-18 15:25:49 +00:00
Omar Polo f87bf597ea removing O_CLOEXEC
we don't fork anymore in that process, so the flag is extra.
2021-01-17 23:25:59 +00:00
Omar Polo c0224a266c removed 2021-01-17 23:24:26 +00:00
Omar Polo d3a08f4d17 reorganize: move bunch of functions to server.c
cgi.c wasn't really needed; it better to group all the server related
functions together, cgi or not.  Now gmid.c contains only startup and
utility code.
2021-01-17 23:23:58 +00:00
Omar Polo 5f564d23e9 mention libretls 2021-01-17 13:53:08 +00:00
Omar Polo 65fba1d570 [seccomp] allow also poll
on the latest fedora we glibc uses poll.  On the other linux distro I
tried (void), musl is probably providing poll as a ppoll wrapper.
2021-01-17 13:51:09 +00:00
Omar Polo 771d8f281b build docs + install and static targets 2021-01-17 12:12:37 +00:00
Omar Polo 0170ba023f re-enable daemon mode
with the split into two process, the call to daemon was lost
2021-01-17 11:56:50 +00:00
Omar Polo b3927be446 fix runtime test on linux 2021-01-17 11:49:32 +00:00
Omar Polo 364f10dbcd add runtime tests for the server 2021-01-17 11:34:06 +00:00
Omar Polo 76fd55f437 grammar 2021-01-17 10:06:52 +00:00
Omar Polo 7a11e8a5d3 sync changes 2021-01-17 09:43:29 +00:00
Omar Polo ea58dab1a7 update readme 2021-01-17 09:42:10 +00:00
Omar Polo c2e39fcfed we don't need to check for CGI anymore 2021-01-17 09:37:44 +00:00
Omar Polo 71b7eb2f8c initial seccomp support 2021-01-17 09:34:27 +00:00
Omar Polo 7146dd552f missing break 2021-01-17 09:34:06 +00:00
Omar Polo d939d0f09e switch to getcwd 2021-01-17 09:33:45 +00:00
Omar Polo 881dc835d0 wording 2021-01-16 20:14:02 +00:00
Omar Polo 881a9dd9c2 split into two processes: listener and executor
this way, we can sandbox the listener with seccomp (todo) or capsicum
(already done) and still have CGI scripts.  When we want to exec, we
tell the executor what to do, the executor executes the scripts and
send the fd backt to the listener.
2021-01-16 19:41:34 +00:00
Omar Polo bd726b55be set SERVER_NAME to the vhost when executing CGI scripts 2021-01-15 19:23:43 +00:00
Omar Polo 98c8f038ac drop inet_ntop leftovers
we switched to getnameinfo some time ago, but that call to inet_ntop
remained.  Fortunately, it doesn't harm, since what i wrote is
overwritten by getnameinfo and the provided buffer should be large
enough.
2021-01-15 19:15:13 +00:00
Omar Polo 7edc455ab4 move cgi stuff to its own file 2021-01-15 19:08:28 +00:00
Omar Polo 5bc3c98ed4 add protocols to the config 2021-01-15 18:55:05 +00:00
Omar Polo 8696c5ea24 sync changes 2021-01-15 18:24:58 +00:00
Omar Polo b777bf4b2b check also that the port number matches 2021-01-15 18:24:24 +00:00
Omar Polo f7b816dc39 style 2021-01-15 15:21:51 +00:00
Omar Polo dafb57b8af sandbox also on FreeBSD with capsicum 2021-01-15 14:03:45 +00:00
Omar Polo 4ad11931ec update README 2021-01-15 09:35:06 +00:00
Omar Polo 6214c12719 added sample configuration 2021-01-15 09:31:52 +00:00
Omar Polo e4d82becb7 normalize host name when parsing the IRI
RFC3986 3.2.2 "Host" says that

> Although host is case-insensitive, producers and normalizers should
> use lowercase for registered names and hexadecimal addresses for the
> sake of uniformity, while only using uppercase letters for
> percent-encodings.

so we cope with that.
2021-01-15 09:27:42 +00:00
Omar Polo 1590277007 conf & vhosts
* gmid.c (main): changed behaviour: daemon off by default
(main): changed -c in -C (cert option)
(main): changed -k in -K (key option, for consistency with -C)
(main): added -c to load a configuration
(main): certs, key and doc (-C -K and -d) doesn't have a default value anymore
(handle_handshake): add vhosts support
2021-01-15 09:17:43 +00:00
Omar Polo 4d2ec6d705 typo 2021-01-13 19:06:51 +00:00
Omar Polo 9939ddcf9d sync changes 2021-01-13 19:03:21 +00:00
Omar Polo de428fff65 normalize schema when parsing the IRI
RFC3986 in section 3.1 "Scheme" says that

> Although schemes are case-insensitive, the canonical form is
> lowercase and documents that specify schemes must do so with
> lowercase letters.  An implementation should accept uppercase
> letters as equivalent to lowercase in scheme names (e.g., allow
> "HTTP" as well as "http") for the sake of robustness but should only
> produce lowercase scheme names for consistency.

so we cope with that.  The other possibility would have been to use
strcasecmp instead of strcmp when checking on the protocol, but since
the "case" version, although popular, is not part of any standard
AFAIK I prefer downcasing while parsing and be done with it.
2021-01-13 19:00:53 +00:00
Omar Polo 9862b637c2 track handshakes
This alter the current state machine by adding S_HANDSHAKE as the
initial state.  There, we ensure we did the handshake and we check
SNI.  ATM we simply continue in S_OPEN, but later we can add virtual
host checks there, and skip to S_INITIALIZING with an error state if
the client is accessing a wrong host.
2021-01-13 18:40:18 +00:00
Omar Polo 0d7a38c4ce avoid __dead and return at the end of main 2021-01-11 15:28:25 +00:00
Omar Polo 796c6e75d7 fix build on non-OpenBSD systems
not every system has a __dead attribute for functions.  This fixes the
build on FreeBSD.
2021-01-11 15:16:50 +00:00
Omar Polo 7b1d979032 reject non-gemini protocols with 53 2021-01-11 14:45:57 +00:00
Omar Polo 6a9ae70773 remove infinite loop 2021-01-11 14:26:43 +00:00
Omar Polo 3c1cf9d07c s/uri/iri since we accept IRIs 2021-01-11 13:08:00 +00:00
Omar Polo 28778244d6 fix build 2021-01-11 12:53:46 +00:00
Omar Polo b9220ca4de new README + wording in manpage 2021-01-11 12:51:25 +00:00
Omar Polo 85dff1f9c3 fix remote_user for CGI and add -6 flag to enable ipv6 2021-01-11 12:08:50 +00:00
Omar Polo 33756bd235 accept both ipv4 and ipv6 2021-01-10 22:56:33 +00:00
Omar Polo 709d6e5ead log also the port of the client 2021-01-10 22:29:22 +00:00
Omar Polo 80bbcad5f2 make FATAL and LOG inline functions
other functions that aren't macros anymore.  LOG went under a rename
to logs because log is a builtin (the math function), or so gcc says.
2021-01-10 15:50:27 +00:00
Omar Polo 945d22d1f2 inline should be before type, gcc says
clang doesn't seem to care.
2021-01-10 15:47:13 +00:00
Omar Polo 9b374f41ee macro reordering
and while there replace SAFE_SETENV with an inline function.  LOG is
more difficult to transform into an inline function, given the string
concatenations it does.  The other LOG* and FATAL macros are fine as
they already are.
2021-01-10 14:40:14 +00:00
Omar Polo 579e21b35a switch some macros to inline functions
they expanded multiple times their argument, since inline is standard
(and works on GCC 4 too!) why don't use it?
2021-01-09 20:38:26 +00:00
Omar Polo ef04b55160 switch to Bjoern Hoehrmann UTF-8 decoder
It's correct, while my hacked valid_multibyte_utf8 would allow things
that aren't technically UTF8.
2021-01-09 20:32:23 +00:00
Omar Polo 578ba2d81b remove leftovers from debugging 2021-01-08 17:01:42 +00:00
Omar Polo 16ace04d45 simplify loop
todo was initially there for an optimization: don't loop to MAX_USERS
when you know the upper limit is todo.
2021-01-08 16:59:50 +00:00
Omar Polo 4c4167393a
simplify unveil/pledge calls 2021-01-01 13:03:04 +01:00
Omar Polo 6c6c7a0ed7
rename cgi_setpoll_on_* to cgi_poll_on_* 2020-12-28 18:01:06 +01:00
Omar Polo b3deee7b38
initialize error string to avoid returning garbage 2020-12-28 17:50:26 +01:00
Omar Polo 3d9a1c73de
fixing prototype order 2020-12-28 09:57:58 +01:00
Omar Polo 4a28dd014a
ignore also SIGHUP
SIGHUP is sent when the tty is detached and by default kills the
process.  When we run in the background we don't care anymore about
the tty, so it should be safe for us to ignore SIGHUP.  (frankly,
I expected daemon(3) to do stuff like this for us).
2020-12-28 09:55:56 +01:00
Omar Polo 5a40216f37
fix CGI with new IRI parser
With new IRI, parser the old assumption of path starting with ./
is no longer valid.
2020-12-28 09:52:39 +01:00
Omar Polo 6d91731552
fmt 2020-12-27 13:27:15 +01:00
Omar Polo f722f3c5aa
typos 2020-12-26 11:22:07 +01:00
Omar Polo 60ff035518
mention IRI in changelong 2020-12-26 10:15:16 +01:00
Omar Polo 00781742c5
reject %00 2020-12-26 00:37:43 +01:00
Omar Polo df6ca41da3
IRI support
This extends the URI parser so it supports full IRI (Internationalized
Resource Identifiers, RFC3987).  Some areas of it can/may be improved,
but here's a start.

Note: we assume UTF-8 encoded IRI.
2020-12-26 00:33:11 +01:00
Omar Polo 043acc97b1
mention the %2F caveat 2020-12-25 13:15:15 +01:00
Omar Polo 33d32d1fd6
implement a valid RFC3986 (URI) parser
Up until now I used a "poor man" approach: the uri parser is barely a
parser, it tries to extract the path from the request, with some minor
checking, and that's all.  This obviously is not RFC3986-compliant.

The new RFC3986 (URI) parser should be fully compliant.  It may accept
some invalid URI, but shouldn't reject or mis-parse valid URI.  (in
particular, the rule for the path is way more relaxed in this parser
than it is in the RFC text).

A difference with RFC3986 is that we don't even try to parse the
(optional) userinfo part of a URI: following the Gemini spec we treat
it as an error.

A further caveats is that %2F in the path part of the URI is
indistinguishable from a literal '/': this is NOT conforming, but due
to the scope and use of gmid, I don't see how treat a %2F sequence in
the path (reject the URI?).
2020-12-25 13:13:12 +01:00
Omar Polo d5aba4c791
added missing copyright stuff 2020-12-24 16:49:55 +01:00
Omar Polo 488f059ac4
moving declarations to header file 2020-12-24 16:48:39 +01:00
Omar Polo 83000e2dd4
improve wording
"concurrently" means at the same time, which can be confusing when we
say that it's single-threaded on a single process.
2020-12-21 15:51:09 +01:00
Omar Polo 51d876f030
always check absolutify_path return 2020-12-21 15:42:06 +01:00
Omar Polo 4f12842dfe
mention change in changelog file 2020-12-21 14:40:04 +01:00
Omar Polo 231bfcdc03
make -d handle correctly non-absolute paths
before the -d option only accepted absolute paths, and this wasn't
documented.  Even more, with the default value of "docs" it won't
work.  Now it transforms all relative paths to absolute paths before
going on.
2020-12-21 14:38:31 +01:00
Omar Polo dd3e110a70
reorder & fmt 2020-12-20 20:03:55 +01:00
Omar Polo d7802bb44a
logging reworked and daemonize by default
The -l option was removed: now it logs on syslog if -f (foreground) is
not passed.
2020-12-02 21:18:01 +01:00
Omar Polo 677afbd3f8
clients certs support for CGI
internally, gmid doesn’t care if the client issued a certificate, but
now we pass that information to the CGI script in some new environment
variables.
2020-12-02 15:17:19 +01:00
Omar Polo c603f1260d
fix bug introduced in last version regarding full URLs
gemini://example.com was incorrectly parsed.
2020-11-18 09:45:28 +01:00
Omar Polo dd3895586f
mention the bugfix and tag 1.3.1 2020-11-18 09:36:18 +01:00
Omar Polo 7cd6880b84
correctly handle also non absolute URLs 2020-11-18 09:34:42 +01:00
Omar Polo ae2ad03ec0
handle //example.com urls 2020-11-18 09:32:07 +01:00
Omar Polo 721e232529
make port number configurable 2020-11-18 09:12:27 +01:00
Omar Polo d431188c66
fixed changelog format for old entries 2020-11-10 18:14:07 +01:00
Omar Polo a5d310bc0d
[cgi] added support for path parameters
enhance the CGI scripting support so that script can take path
parameters.  That is, a script at /cgi/foo is called when the request
path is /cgi/foo/bar/...

This commit also introduce some backward incompatible changes as the
default env variables set for the CGI script changed.
2020-11-10 14:07:36 +01:00
Omar Polo 92e66347ed
regen readme 2020-11-06 18:12:57 +01:00
Omar Polo 0ed5656795
documentation improvements 2020-11-06 18:11:45 +01:00
Omar Polo 72f653b652
[cgi] execute cgi scripts only inside a specific directory
change the meaning of the -x flag: now it takes a string and executes
CGI scripts only if they are inside a directory with the given name,
relatively to the document root.
2020-11-06 18:05:44 +01:00
Omar Polo f65ed01841
[cgi] set QUERY_STRING only if there is a query string 2020-11-06 18:02:41 +01:00
Omar Polo 120381c93d
[debug] print number of connected clients on SIGINFO
or SIGUSR2 for poor peoples
2020-11-06 17:09:30 +01:00
Omar Polo aff8d19010
handle CGI concurrently
don’t stop-the-world-until-cgi-end, but rather poll on the script, so
we can handle other requests in the meantime.
2020-11-06 17:09:14 +01:00
Omar Polo 60ba426e7e
[cgi] setenv instead of building an env + define more env variables 2020-11-06 15:07:33 +01:00
Omar Polo 75d233f00c
pass the query parameter to start_cgi 2020-11-06 15:06:50 +01:00
Omar Polo aa0fe0cf24
extract query parameters from the URL 2020-11-06 15:04:16 +01:00
Omar Polo 3c19febb01
ensure the requested protocol is “gemini”
…and not something else that happens to be 6-bytes long.
2020-11-06 14:24:25 +01:00
Omar Polo e8cac16e03
mark every open file as close-on-exec 2020-11-06 14:18:17 +01:00
Omar Polo 72342dc960
implementing CGI – NOT READY YET!
This is a first try at implementing CGI scripting.  The idea is that,
if CGI is explicitly enabled by the user, when a user requires an
executable file instead of serving it to the client, that file will be
executed and its output fed to the client.

There are various pieces that are still lacking, the firsts that comes
to mind are:

 - performance: the handle_cgi just loops ignoring the
   WANT_POLLIN/POLLOUT and blocking if the child process hasn’t
   outputted anything.
 - we don’t parse query variable (yet)
 - we need to set more variables in the child environment
   side question: it’s better to set the variables using setenv() or
   by providing an explicit environment?
 - document what environment the CGI script will get
 - improve the horrible unveil/pledge(cgi ? …)

but now I can serve “hello-world”-tier script from gmid!
2020-11-06 13:01:31 +01:00
Omar Polo 2c3a40faf8
added option to log to a file 2020-11-06 10:58:30 +01:00
Omar Polo 932b001a3f
typo 2020-11-06 10:45:06 +01:00
Omar Polo 20f688381a
doc: point out that we can handle multiple clients concurrently 2020-11-05 17:06:12 +01:00
Omar Polo dd080e1f75
define a MIME type for xml files 2020-11-05 17:05:28 +01:00
Omar Polo 19f68393e6
update the changelog and tag 1.2 2020-11-03 15:51:13 +01:00
Omar Polo 0cf902af62
ignore SIGPIPE
bad clients can shutdown the socket and we will exit due to a SIGPIPE.
it ain’t fun.
2020-11-03 15:48:26 +01:00
Omar Polo 9468027ba7
initial work for ipv6
make_socket can now return an ipv6 socket, and everything else still
works.  ipv4 is still hardcoded tho.
2020-10-15 13:45:57 +02:00
Omar Polo 4f6eb772cb
new release: 1.1 2020-10-14 10:17:27 +02:00
Omar Polo 2b897a2cea
added a changelog file 2020-10-14 10:14:11 +02:00
Omar Polo f28f931139
switching to mmap() based file handling 2020-10-14 10:09:06 +02:00
Omar Polo 9c56b0a78a
style 2020-10-14 10:09:00 +02:00
Omar Polo 10fed44c79
improve make error message if etags is not found
the ‘|| true’ idiom leads to better make error.  Now the ‘-’ becomes a
bit redundant tho…
2020-10-07 17:59:55 +02:00
Omar Polo 09a1b97896
put back the missing lseek, and change variables name
(I know, changing variables names AND introducing changes is better
done in more commits, but…)

Added back an lseek that was missing.  If TLS_WANT_POLL{IN,OUT}, we
need to re-send that block, but we need also to rewind the file, in
order to read(2) that chunk again.  This doesn’t solve the corruption
in transferring big files, but reduces them.  I still haven’t tracked
down the corruption :/
2020-10-07 17:59:31 +02:00
182 changed files with 29670 additions and 850 deletions

46
.cirrus.yml Normal file
View File

@ -0,0 +1,46 @@
# gcc' -Werror=use-after-free gets tripped by vis.c: it sees a use
# after free where it's not possible and breaks the CI.
# seems that inside the CI it's not currently possible to bind to ::1
# so set HAVE_IPV6=no.
linux_amd64_task:
container:
image: alpine:latest
test_script:
- apk add alpine-sdk linux-headers bison libretls-dev libevent-dev
- ./configure CFLAGS='-O2 -pipe -Wno-deprecated-declarations -Wno-use-after-free' -Werror
- make
- make regress REGRESS_HOST="*" HAVE_IPV6=no
linux_arm_task:
arm_container:
image: alpine:latest
test_script:
- apk add alpine-sdk linux-headers bison libretls-dev libevent-dev
- ./configure CFLAGS='-O2 -pipe -Wno-deprecated-declarations -Wno-use-after-free' -Werror
- make
- make regress REGRESS_HOST="*" HAVE_IPV6=no
freebsd_14_task:
freebsd_instance:
image_family: freebsd-14-0
install_script: pkg install -y libevent libressl pkgconf
script:
- ./configure CFLAGS='-O2 -pipe -Wno-deprecated-declarations' -Werror
- make
- make regress HAVE_IPV6=no
#
# There are some issues with imsg fd passing on macos at the moment that
# seem to be triggered only in applications that do a heavy use of them,
# like gmid or opensmtpd. Still, keep macos to ensure gmid builds here.
#
mac_task:
macos_instance:
image: ghcr.io/cirruslabs/macos-sonoma-xcode:latest
test_script:
- brew install libevent openssl libretls
- PKG_CONFIG_PATH="$(brew --prefix openssl)/lib/pkgconfig" ./configure CFLAGS='-O2 -pipe -Wno-deprecated-declarations' -Werror
- make
- SKIP_RUNTIME_TESTS=1 make regress

11
.dockerignore Normal file
View File

@ -0,0 +1,11 @@
**
!*.c
!*.h
!*.y
!*.[1-9]
!compat
!have/*.c
!Makefile
!configure
!contrib/Docker.gmid.conf
!contrib/gencert

33
.github/workflows/alpine-release.yml vendored Normal file
View File

@ -0,0 +1,33 @@
name: release docker image
on:
push:
tags:
env:
IMAGE_NAME: "gmid"
jobs:
build:
permissions: write-all
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: build the image
run: docker build -f contrib/Dockerfile -t gmid:alpine .
- name: login to ghcr.io
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: push the image
run: |
IMAGE_ID=ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME
IMAGE_ID=$(echo $IMAGE_ID | tr A-Z a-z)
# strip git ref prefix from version
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
echo IMAGE_ID=$IMAGE_ID
echo VERSION=$VERSION
docker tag gmid:alpine $IMAGE_ID:$VERSION
docker push $IMAGE_ID:$VERSION

37
.gitignore vendored
View File

@ -1,6 +1,37 @@
cert.pem
key.pem
*~
*.pem
TAGS
gmid
*.o
gg
gemexp
titan
**/*.[do]
*.swp
docs
y.tab.*
compile_flags.txt
config.h
config.h.old
config.log
config.log.old
config.mk
configure.local
!contrib/gmid
!contrib/vim/ale_linters/gmid
!contrib/vim/syntax_checkers/gmid
regress/testdata
regress/*.pem
regress/*.key
regress/*.crt
regress/*.csr
regress/*.srl
regress/reg.conf
regress/fcgi-test
regress/fcgi.sock
regress/fill-file
regress/iri_test
regress/puny-test
regress/gmid.pid
site/gemini
site/www

686
ChangeLog Normal file
View File

@ -0,0 +1,686 @@
2024-04-03 Omar Polo <op@omarpolo.com>
* configure: improve function checking in the configure
* have/landlock.c: fix landlock test
* gmid.c (main_print_conf): fix config dumping with -nn
2024-03-03 Omar Polo <op@omarpolo.com>
* gmid.c: fix `log access path' with a chroot
2024-01-30 Anna “CyberTailor”
* contrib/vim/indent/gmid.vim: fix indent
2024-01-30 Omar Polo <op@omarpolo.com>
* parse.y: don't make log styles reserved keywords. Unbreaks the
example in the manpage with `common = ...'.
2024-01-26 Omar Polo <op@omarpolo.com>
* parse.y: rework grammar to allow the semicolon after
variables/macros definition and top-level options
2024-01-24 Omar Polo <op@omarpolo.com>
* configure (VERSION): release 2.0.1
2024-01-21 Omar Polo <op@omarpolo.com>
* convert gmid to the new imsg API
2024-01-14 Anna “CyberTailor”
* configure: fix --mandir handling
2024-01-11 Omar Polo <op@omarpolo.com>
* configure (VERSION): release 2.0
2024-01-09 Anna “CyberTailor”
* contrib/vim/syntax/gmid.vim: update Vim syntax file
* contrib/vim/ale_listers/gmid/gmid.vim: add ALE linter
2023-10-18 Omar Polo <op@omarpolo.com>
* ge.c (load_local_cert): generate EC certs by default, use -R to keep generating RSA ones.
* utils.c (gencert): generate EC too
* gg.c (get): print the response header for non-2x replies to standard error
2023-10-15 Omar Polo <op@omarpolo.com>
* gg.c (main): exit with the gemini response code (unless it's 2x)
2023-08-29 Omar Polo <op@omarpolo.com>
* ge.c (data_dir): use $XDG_DATA_HOME/gemexp instead of /gmid for the certificates.
2023-08-18 Omar Polo <op@omarpolo.com>
* fcgi.c (fcgi_req): sync the parameters with RFC3875 (CGI)
(fcgi_req): send "GET" as REQUEST_METHOD
2023-08-08 Omar Polo <op@omarpolo.com>
* fcgi.c (fcgi_req): implement SCRIPT_NAME / PATH_INFO splitting for fastcgi
(fcgi_req): add `fastcgi strip'
2023-08-07 Omar Polo <op@omarpolo.com>
* logger.c (logger_dispatch_server): allow to change the syslog(3) facility.
* gmid.c (main): attempt to load TLS certificates and load mimes and virtual hosts root as part of configtest (-n) rather than verifying the syntax of the configuration only.
2023-07-25 Omar Polo <op@omarpolo.com>
* gmid.c (log_request): allow to change the logging style.
2023-07-24 Omar Polo <op@omarpolo.com>
* parse.y: add ability to log to files with `log access <path>'.
2023-07-23 Omar Polo <op@omarpolo.com>
* parse.y: revamp fastcgi configuration; make it per-location
2023-07-22 Omar Polo <op@omarpolo.com>
* titan.c (main): add titan(1), a simple titan client
* gg.c (get): warn when the server doesn't use TLS' close notify
2023-07-01 Omar Polo <op@omarpolo.com>
* fcgi.c (fcgi_handle_stdout): parse and log the fastcgi reply
2023-06-24 Omar Polo <op@omarpolo.com>
* server.c (handle_handshake): correctly handle TLS handshake failures.
* server.c (client_close_ev): plug memory leak
2023-06-23 Omar Polo <op@omarpolo.com>
* parse.y: implement `listen on'
2023-06-13 Omar Polo <op@omarpolo.com>
* regress/sha: remove regress/sha; sha256/sha256sum is no more required for the regress suite.
2023-06-12 Omar Polo <op@omarpolo.com>
* regress/lib.sh (run_test): use the default prefork in tests
2023-06-11 Omar Polo <op@omarpolo.com>
* crypto.c: add a privsep crypto engine (enabled only on OpenBSD)
* configure: add -Wpointer-sign to the mix, adjust the code to cope.
2023-05-05 Omar Polo <op@omarpolo.com>
* contrib/gencert: add -e to generate EC keys
2023-05-08 Omar Polo <op@omarpolo.com>
* sandbox.c: drop landlock, seccomp and capsicum support
2022-09-10 Omar Polo <op@omarpolo.com>
* parse.y (string): retire the deprecated `mime' and `map' config options
2022-09-07 Omar Polo <op@omarpolo.com>
* ge.c (main): add `gemexp': small program to quickly export a directory over Gemini.
2022-09-06 Omar Polo <op@omarpolo.com>
* server.c: drop CGI support.
2022-07-07 Omar Polo <op@omarpolo.com>
Included as part of gmid 1.8.5:
* log.c (logger_main): fix timestamps in log files. Reported by Karl Jeacle, thanks!
* dirs.c (scandir_fd): drop d_reclen; it's not available on DragonflyBSD (at least.)
2022-07-07 Anna “CyberTailor”
* Makefile: include contrib/ in dist
2022-07-04 Omar Polo <op@omarpolo.com>
* contrib/gmid: remove OpenBSD' rc file: it's now maintained in the ports tree
* configure (VERSION): 1.8.4 tagged
* server.c (check_path): log when it fails to open a file because of permissions
(client_write): encode file names in the directory index, spotted by cage
(client_write): add a trailing / for dirs in the directory index.
* iri.c (parse_path): allow '@' and ':' in paths
2022-04-08 Omar Polo <op@omarpolo.com>
* mime.c (mime): do a binary search to match the MIME time
* gmid.c (listener_main): don't load the default mime listing when
`types' is used. (text/gemini -> gmi/gemini is still hardcoded)
* mime.c (add_mime): fix memory leak in the mime handling
2022-04-07 Omar Polo <op@omarpolo.com>
* gmid.conf.5: move config file documentation into a new man page
* parse.y (option): deprecate `map' rule in favour of the `types' block
2022-03-27 Omar Polo <op@omarpolo.com>
* configure (VERSION): 1.8.3 tagged
* server.c (start_cgi): fix a possible out-of-bound access in the
CGI handling.
2022-03-26 Omar Polo <op@omarpolo.com>
* configure (VERSION): 1.8.2 tagged
* server.c (handle_imsg_cgi_res): fix a CGI timing issue: if a
connection handled by a CGI scripts is interrupted with the right
timing it causes the server process to exit with "fatal in
client_by_id: invalid id X"
2022-02-26 Omar Polo <op@omarpolo.com>
* parse.y: add the types block
2022-02-13 Omar Polo <op@omarpolo.com>
* sandbox.c (filter): tightens seccomp policy wrt openat: allow
only with the O_RDONLY flag.
2022-02-13 Tobias Berger <tobi.berger13@gmail.com>
* sandbox.c (filter): allow fstatat64, llseek and sigreturn;
needed by glibc on armv7
2022-02-10 Omar Polo <op@omarpolo.com>
* configure (VERSION): 1.8.1 tagged
* sandbox.c (open_landlock): fix landlock usage
2022-01-30 Omar Polo <op@omarpolo.com>
* configure (VERSION): 1.8 “Lightbulb Sun” tagged
* proxy.c (proxy_setup_tls): allow to specify a custom hostname as SNI for the TLS handshake with the proxied host.
2022-01-02 Omar Polo <op@omarpolo.com>
* server.c (matched_proxy): allow to specify multiple proxy blocks and add matching rules
* parse.y (servbody): relax the strict ordering of options, location and proxy blocks
2022-01-01 Omar Polo <op@omarpolo.com>
* proxy.c (proxy_init): add support for client certificate when proxying
2021-12-29 Omar Polo <op@omarpolo.com>
* proxy.c (proxy_init): add proxying support via `proxy relay-to'
2021-12-11 Max <vdrummer@posteo.net>
* sandbox.c (filter): [seccomp]: allow ugetrlimit(2), needed by glibc on armv7l
2021-12-09 Omar Polo <op@omarpolo.com>
* server.c (client_read): don't check if the port in the request is the same we're listening on. Suggested by Allen Sobot, thanks!
* configure: add --prefix=... long argument.
2021-11-16 Omar Polo <op@omarpolo.com>
* configure (singletest): fix the cross-compilation: don't run the test binaries just built. Suggested by Nikolay Korotkiy (@sikmir), thanks!
2021-10-24 Omar Polo <op@omarpolo.com>
* log.c (log_request): fix "double slash" in logs: gmid ended up printing two slashes between the hostname and the path when logging the request IRI.
2021-10-15 Stephen Gregoratto <dev@sgregoratto.me>
* gmid.c (add_keypair): implement OCSP stapling support
2021-10-13 Omar Polo <op@omarpolo.com>
* server.c (do_accept): don't die on ECONNABORTED (``Software caused connection abort'')
2021-10-11 Omar Polo <op@omarpolo.com>
* contrib/renew-certs: add script to automatically renew self-signed certificates
2021-10-09 Omar Polo <op@omarpolo.com>
* parse.y (print_conf): multiple -n to dump the parsed configuration
* contrib/gencert: add gencert, a simple script to generate self-signed certs
2021-10-04 Omar Polo <op@omarpolo.com>
* regress/lib.sh (raw): reduced the timeout time for single checks from 30 to 10 seconds
* regress/runtime: regression test restructured. still implemented as a set of POSIX-scripts
2021-10-02 Omar Polo <op@omarpolo.com>
* server.c (client_read): reworked the internal structure. Now we leverage libevent' bufferevents more.
(cgi_read): be more strict with CGI scripts: don't pass through illegal CGI responses.
2021-09-26 Omar Polo <op@omarpolo.com>
* fcgi.c (fcgi_req): the FastCGI implementation is fully asynchronous
2021-09-24 Omar Polo <op@omarpolo.com>
* configure (VERSION): 1.7.4 tagged
* server.c (check_for_cgi): fix out-of-bound access, found with lots of help from cage, thanks!
2021-09-19 Omar Polo <op@omarpolo.com>
* sandbox.c (gmid_create_landlock_rs): [linux] use landlock on the server and logger process to reduce the fs access
* configure (VERSION): 1.7.3 tagged
2021-09-17 Omar Polo <op@omarpolo.com>
* log.c (logger_main): fix syslog logging on FreeBSD
2021-08-24 Omar Polo <op@omarpolo.com>
* gmid.c (main): don't crash on -n without -c
2021-07-29 Anna “CyberTailor”
* gmid.1: document logging, openssl key generation example, spelling/grammar and crosslinks
2021-07-27 Omar Polo <op@omarpolo.com>
* server.c (check_path): allow symlinks
2021-07-23 Omar Polo <op@omarpolo.com>
* sandbox.c: seccomp allow fstat64 (used by glibc an aarch64). Reported by pine, thanks!
2021-07-19 Omar Polo <op@omarpolo.com>
* gmid.c (setup_configless): unbreak configless mode
2021-07-13 Anna “CyberTailor”
* parse.y (yyerror): error and warning messages are prefixed with "error:" and "warning:"
(yywarn): fix off-by-one line number in warnings
2021-07-11 Omar Polo <op@omarpolo.com>
* configure (VERSION): 1.7.1 tagged
2021-07-11 Anna “CyberTailor”
* gmid.1 (common): fix misleading example in man page: macros names may not be reserved words
2021-07-10 Omar Polo <op@omarpolo.com>
* configure (VERSION): 1.7 tagged
2021-07-09 Omar Polo <op@omarpolo.com>
* parse.y (STRING): add `include' directive
(yylex): add @-macros (real macros)
2021-07-08 Omar Polo <op@omarpolo.com>
* parse.y (option): rename `mime MIME EXT' to `map MIME to-ext EXT', but retain the old `mime' for compatibility.
2021-07-06 Omar Polo <op@omarpolo.com>
* regress/gg.c (main): add -T timeout
* configure (guessing_cflags): try to preserve CFLAGS/LDFLAGS
2021-07-02 Omar Polo <op@omarpolo.com>
* sandbox.c (filter): seccomp filter reworked: now it should work on x86 and possibly other arches too!
2021-06-29 Omar Polo <op@omarpolo.com>
* parse.y (conf): don't require the strict order macro > options > servers
c-like strings: when two or more strings are next to each others, concat them
* gmid.c (main): add -D to define macros from the cmd line
* parse.y (yylex): allow to define macros in the config file
* gmid.c (main): use getopt_long, add --help as synonym of -h and -V/--version
2021-06-17 Omar Polo <op@omarpolo.com>
* gmid.1: document `log' option
2021-06-16 Omar Polo <op@omarpolo.com>
* parse.y (yylex): drop the dependency on lex by implementing
yylex by ourselves (the actual implementation is based off doas'
parse.y). This gave us various benefits, like cleaner code, \ to
break long lines, better handling of quotes etc...
2021-06-11 Omar Polo <op@omarpolo.com>
* parse.y (servopt): add `param' keyword
* fcgi.c (send_fcgi_req): send GATEWAY_INTERFACE, AUTH_TYPE,
REMOTE_USER, TLS_CLIENT_ISSUER, TLS_CLIENT_HASH, TLS_VERSION,
TLS_CIPHER, TLS_CIPHER_STRENGTH and TLS_CLIENT_NOT_BEFORE/AFTER.
(send_fcgi_req): support a custom list of params
2021-05-24 Omar Polo <op@omarpolo.com>
* gg.c: move `gg' to regress, as it's only used for the tests
2021-05-12 Omar Polo <op@omarpolo.com>
* utils.c (gen_certificate): don't add gmid as organisation when generating the certificate, and now it set the version 3, so it's compatible with java/android
2021-05-09 Omar Polo <op@omarpolo.com>
* server.c (apply_fastcgi): added fastcgi support!
2021-05-03 Omar Polo <op@omarpolo.com>
* parse.y: errors on duplicate values instead of silently using only the last value.
2021-04-30 Omar Polo <op@omarpolo.com>
* server.c (fmt_sbuf): ensure %p (path) is always absolute
* gmid.c (load_vhosts): allow ``root'' rule to be specified per-location block
2021-04-29 Omar Polo <op@omarpolo.com>
* parse.y (servopt): added ``alias'' option to define hostname aliases for a server
2021-04-28 Omar Polo <op@omarpolo.com>
* gmid.c (main): pidfile support with `-P pidfile'
2021-04-27 Omar Polo <op@omarpolo.com>
* parse.y (servopt): added ``env'' option to define environment vars for CGI scripts
* log.c (fatal): lower the log priority for fatal errors from CRIT to ERR
2021-04-25 Omar Polo <op@omarpolo.com>
* server.c (open_dir): sort the auto index alphabetically
2021-04-21 Omar Polo <op@omarpolo.com>
* mime.c (load_default_mime): use `text/x-patch' for .patch and .diff files
2021-04-14 Omar Polo <op@omarpolo.com>
* log.c (handle_imsg_log): print the datetime when logging to stderr
2021-04-13 Omar Polo <op@omarpolo.com>
* ex.c (launch_cgi): define TLS_VERSION, TLS_CIPHER and TLS_CIPHER_STRENGTH for CGI scripts
2021-04-12 Omar Polo <op@omarpolo.com>
* 1.6.1 tagged
* iri.c (path_clean): fix possible infinite loop in the IRI parsing code.
2021-03-31 Omar Polo <op@omarpolo.com>
* gmid.h (struct vhost): remove limits on the number of vhosts and location blocks
* gmid.c (mkdirs): fix recursive mkdirs for configless mode
2021-03-29 Omar Polo <op@omarpolo.com>
* Makefile (static): fixed `static' target
2021-03-29 kornellapacz @ github
* Dockerfile: add missing libevent-dev dependency, thanks!
2021-03-27 Omar Polo <op@omarpolo.com>
* gmid.h (struct client): correctly handle CGI scripts that replies with the maximum header length allowed
2021-03-20 Omar Polo <op@omarpolo.com>
* 1.6 tagged
* sandbox.c (sandbox_logger_process): add capsicum to the logger process
2021-03-19 Omar Polo <op@omarpolo.com>
* gmid.c (main): use imsg for all IPC; only the main process
listens for SIGHUP: this means that finally the config reload will
work with OpenBSD' rc (and probably other service manager too)
2021-02-22 Omar Polo <op@omarpolo.com>
* log.c (log_request): add `log on/off' to enable/disable logs per-server or per-location
2021-02-09 Omar Polo <op@omarpolo.com>
* parse.y (locopt): add `require client ca' rule to require client certs signed by a specified CA
2021-02-07 Omar Polo <op@omarpolo.com>
* ex.c (do_exec): [cgi] split the query in words if needed and add them to the argv
(launch_cgi): define TLS_CLIENT_NOT_BEFORE/NOT_AFTER in CGI scripts
* parse.y (option): added prefork option
2021-02-06 Omar Polo <op@omarpolo.com>
* parse.y (locopt): added ``block return'' and ``strip'' options
(servopt): add the ``entrypoint'' option
2021-02-05 Omar Polo <op@omarpolo.com>
* iri.c (parse_query): don't %-decode the query part. This affects the value of QUERY_STRING for CGI scripts too, since that must be %-encoded and we're currently shipping it decoded.
2021-02-04 Omar Polo <op@omarpolo.com>
* gmid.c (main): reload configuration on SIGHUP, without disconnecting the clients
2021-02-02 Omar Polo <op@omarpolo.com>
* server.c (handle_dirlist_head): print the header in the directory listing
(open_file): cgi follows globbing rules, just like location and hostname matching
2021-02-01 Omar Polo <op@omarpolo.com>
* parse.y (servopt): require absolute paths in config file
2021-01-31 Omar Polo <op@omarpolo.com>
* gmid.c (main): cgi scripts now have only std{in,out,err} open
2021-01-30 Omar Polo <op@omarpolo.com>
* 1.5 tagged
* server.c: change precedence of location rules
2021-01-29 Omar Polo <op@omarpolo.com>
* iri.c (parse_authority): accept a wider range of unicode
codepoints while parsing the host name.
2021-01-26 Omar Polo <op@omarpolo.com>
* puny.c (puny_decode): initial punycode support!
2021-01-25 Omar Polo <op@omarpolo.com>
* gmid.1: manpage improvements (example and usage)
* gmid.c (main): Dropping -C, -K, -f, changing the meaning of -d:
now it's the certificate directory. Serve the directory given as
positional argument (or the current one) when running in
config-less mode.
(gen_certificate): automatically generate a certificate
* parse.y (option): added chroot and user options
2021-01-24 Omar Polo <op@omarpolo.com>
* server.c (open_dir): add directory listing (disabled by default)
* parse.y (vhost): added support for location blocks
* server.c (send_dir): make the directory index customizable
2021-01-23 Omar Polo <op@omarpolo.com>
* gg.c (main): added gg, a barebone gemini client. used by the
regress suite
2021-01-21 Omar Polo <op@omarpolo.com>
* configure: added a configure script
* server.c (handle_handshake): glob for vhost domain
* gmid.c (log_request): logs the full IRI and the response code (even for CGI)
2021-01-19 Omar Polo <op@omarpolo.com>
* parse.y (servopt): add "lang" server option
(servopt): moving "default type" from global options to server options
* Dockerfile: add a dockerfile
2021-01-18 Omar Polo <op@omarpolo.com>
* parse.y (option): add mime and "default type" options for media types.
2021-01-17 Omar Polo <op@omarpolo.com>
* sandbox.c (sandbox): added initial seccomp(2) support
2021-01-15 Omar Polo <op@omarpolo.com>
* cgi.c (start_cgi): set SERVER_NAME to the vhost when executing CGI scripts
* parse.y (option): add ability to specify the tls versions with "protocols"
* gmid.c (handle_open_conn): ensure the port number of the request matches
* sandbox.c (sandbox): sandbox on OpenBSD (pledge/unveil, as before) and on FreeBSD (capsicum) too
* sample.conf: added sample configuration
* gmid.c (main): changed behaviour: daemon off by default
(main): changed -c in -C (cert option)
(main): changed -k in -K (key option, for consistency with -C)
(main): added -c to load a configuration
(main): certs, key and doc (-C -K and -d) doesn't have a default value anymore
(handle_handshake): add vhosts support
2021-01-13 Omar Polo <op@omarpolo.com>
* iri.c (parse_scheme): normalize scheme while parsing, so we're
RFC3986 compliant.
2021-01-11 Omar Polo <op@omarpolo.com>
* 1.4 and 1.4.1 tagged
* gmid.c (main): ipv6 disabled by default and -6 flag to enable it
(handle): reject non-gemini protocols with 53
2021-01-10 Omar Polo <op@omarpolo.com>
* gmid.c (logs): log also the port of the client
(loop): accept both ipv4 and ipv6
2020-12-26 Omar Polo <op@omarpolo.com>
* uri.c (parse_uri): IRI support
2020-12-21 Omar Polo <op@omarpolo.com>
* gmid.c (main): -d supports relative paths
2020-12-02 Omar Polo <op@omarpolo.com>
* gmid.c: logging reworked and daemonize. The -l option was
removed: now it logs on syslog if -f (foreground) is not passed.
2020-11-18 Omar Polo <op@omarpolo.com>
* 1.3.2 tagged
* gmid.c (url_after_proto): fix bug introduced in last version
regarding full URLs with explicit gemini protocol.
* 1.3.1 tagged
* gmid.c (url_after_proto): correct url parsing: accept URLs
without explicit protocol
(url_start_of_request): correct handling of relative URLs
2020-11-17 Omar Polo <op@omarpolo.com>
* gmid.c (main): add flag -p to change the port
2020-11-10 Omar Polo <op@omarpolo.com>
* ChangeLog: 1.3 tagged, fixed ChangeLog format
* gmid.c (open_file): added support for path parameters for CGI
scripts
2020-11-06 Omar Polo <op@omarpolo.com>
* gmid.1: great improvements to the documentation
* gmid.c (url_after_proto): ensure that the requested protocol is
“gemini” and not something else thats long 6 bytes.
* gmid.c (loop): added support for cgi scripts (can handle multiple
concurrently)
2020-11-06 Omar Polo <op@venera>
* gmid.1: added option to log to a file
2020-11-05 Omar Polo <op@omarpolo.com>
* gmid.c (filetypes): add MIME type for xml files
2020-11-03 Omar Polo <op@omarpolo.com>
* 1.2 tagged
* gmid.c (main): ignore SIGPIPE
2020-10-14 Omar Polo <op@omarpolo.com>
* 1.1 tagged
* switching to mmap() based file handling
2020-10-07 Omar Polo <op@omarpolo.com>
* 1.0 tagged

View File

@ -1,43 +0,0 @@
# Installing gmid
## Dependencies
gmid depends on libtls and a C compiler. It's reported to compile with gcc 4.2, so it should work pretty everywhere now.
The compilation is as easy as
```
make
```
Note that there isn't an install target yet.
If you're a packager, don't forget to install also the manpage gmid.1
### etags
during the build process, etags(1) is invoked to generate a TAGS file. This is done to aid the development. Anyway, etags(1) is NOT NEEDED as dependency. So, if you get something like
```
$ make
...
etags gmid.c
make: exec(etags) failed (No such file or directory)
*** Error code 1 (ignored)
```
you don't need to worry, it is OK.
## Notes for specific OSes
### OpenBSD
Everything you need is in base, just run make.
### FreeBSD
You need to install security/libressl and then
```
make CFLAGS=-I/usr/local/include LDFLAGS='-L/usr/local/lib -ltls'
```

View File

@ -1,4 +1,4 @@
Copyright (c) 2020 Omar Polo <op@omarpolo.com>
Copyright (c) 2020, 2021, 2022 Omar Polo <op@omarpolo.com>
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above

195
Makefile
View File

@ -1,19 +1,190 @@
CC = cc
CFLAGS = -Wall -Wextra -g
LDFLAGS = -ltls
# Copyright (c) 2022 Omar Polo <op@omarpolo.com>
# Copyright (c) 2011, 2013-2022 Ingo Schwarze <schwarze@openbsd.org>
# Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
#
# 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.
.PHONY: all clean
# tests to run as a port of the regression suite. Leave empty to run
# all.
TESTS=
all: gmid TAGS README.md
# host to bind to during regress
REGRESS_HOST = localhost
gmid: gmid.o
${CC} gmid.o -o gmid ${LDFLAGS}
# -- build-related variables --
TAGS: gmid.c
-etags gmid.c
COBJS = ${COMPATS:.c=.o}
README.md: gmid.1
mandoc -Tmarkdown gmid.1 | sed -e '1d' -e '$$d' > README.md
GMID_SRCS = gmid.c config.c crypto.c dirs.c fcgi.c iri.c log.c \
logger.c mime.c proc.c proxy.c puny.c sandbox.c \
server.c utf8.c utils.c y.tab.c
GMID_OBJS = ${GMID_SRCS:.c=.o} ${COBJS}
GEMEXP_SRCS = ge.c config.c crypto.c dirs.c fcgi.c iri.c log.c mime.c \
proc.c proxy.c puny.c sandbox.c server.c utf8.c utils.c
GEMEXP_OBJS = ${GEMEXP_SRCS:.c=.o} ${COBJS}
GG_SRCS = gg.c iri.c log.c utf8.c
GG_OBJS = ${GG_SRCS:.c=.o} ${COBJS}
TITAN_SRCS = titan.c iri.c log.c utf8.c
TITAN_OBJS = ${TITAN_SRCS:.c=.o} ${COBJS}
SRCS = gmid.h iri.h log.h parse.y proc.h \
${GMID_SRCS} ${GEMEXP_SRCS} ${GG_SRCS} ${TITAN_SRCS}
DISTNAME = gmid-${VERSION}
# -- public targets --
all: config.mk gmid gemexp gg titan
.PHONY: all tags clean cleanall test regress install
config.mk config.h: configure
@echo "$@ is out of date; please run ./configure"
@exit 1
include config.mk
clean:
rm -f gmid.o gmid
rm -f gmid gemexp gg
rm -f *.[do] compat/*.[do] compat/libtls/*.[do]
rm -f y.tab.c y.tab.h y.output
rm -f compile_flags.txt
${MAKE} -C regress clean
distclean: clean
rm -f config.h config.h.old config.log config.log.old config.mk
test: regress
regress: all
${MAKE} 'TESTS=${TESTS}' -C regress all
install: gmid gg gemexp
mkdir -p ${DESTDIR}${BINDIR}
mkdir -p ${DESTDIR}${MANDIR}/man1
mkdir -p ${DESTDIR}${MANDIR}/man5
mkdir -p ${DESTDIR}${MANDIR}/man8
${INSTALL_PROGRAM} gemexp ${DESTDIR}${BINDIR}
${INSTALL_PROGRAM} gg ${DESTDIR}${BINDIR}
${INSTALL_PROGRAM} gmid ${DESTDIR}${BINDIR}
${INSTALL_PROGRAM} titan ${DESTDIR}${BINDIR}
${INSTALL_MAN} gemexp.1 ${DESTDIR}${MANDIR}/man1
${INSTALL_MAN} gg.1 ${DESTDIR}${MANDIR}/man1
${INSTALL_MAN} titan.1 ${DESTDIR}${MANDIR}/man1
${INSTALL_MAN} gmid.conf.5 ${DESTDIR}${MANDIR}/man5
${INSTALL_MAN} gmid.8 ${DESTDIR}${MANDIR}/man8
uninstall:
rm ${DESTDIR}${BINDIR}/gemexp
rm ${DESTDIR}${BINDIR}/gg
rm ${DESTDIR}${BINDIR}/gmid
rm ${DESTDIR}${BINDIR}/titan
rm ${DESTDIR}${MANDIR}/man1/gemexp.1
rm ${DESTDIR}${MANDIR}/man1/gg.1
rm ${DESTDIR}${MANDIR}/man1/titan.1
rm ${DESTDIR}${MANDIR}/man5/gmid.conf.5
rm ${DESTDIR}${MANDIR}/man8/gmid.8
tags:
ctags ${SRCS}
# --internal build targets --
gmid: ${GMID_OBJS}
${CC} ${GMID_OBJS} -o $@ ${LDFLAGS} ${LIBS}
gemexp: ${GEMEXP_OBJS}
${CC} ${GEMEXP_OBJS} -o $@ ${LDFLAGS} ${LIBS}
gg: ${GG_OBJS}
${CC} ${GG_OBJS} -o $@ ${LDFLAGS} ${LIBS}
titan: ${TITAN_OBJS}
${CC} ${TITAN_OBJS} -o $@ ${LDFLAGS} ${LIBS}
y.tab.c: parse.y
${YACC} -b y parse.y
# make sure we pass -o to ${CC}. OpenBSD default suffix rule doesn't
.SUFFIXES: .c .o
.c.o:
${CC} ${CFLAGS} -c $< -o $@
# -- maintainer targets --
.PHONY: lint release dist
lint:
man -Tlint -Wstyle -l gmid.8 gmid.conf.5 gemexp.1 gg.1 titan.1
PUBKEY = keys/gmid-2.0.pub
PRIVKEY = set-PRIVKEY
DISTFILES = .cirrus.yml .dockerignore .gitignore ChangeLog LICENSE \
Makefile README.md config.c configure crypto.c dirs.c fcgi.c \
ge.c gemexp.1 gg.1 gg.c gmid.8 gmid.c gmid.conf.5 gmid.h \
iri.c iri.h log.c log.h logger.c mime.c parse.y proc.c \
proc.h proxy.c puny.c sandbox.c server.c titan.1 titan.c \
utf8.c utils.c y.tab.c
release:
sed -i -e '/^RELEASE=/s/no/yes/' configure
${MAKE} dist
sed -i -e '/^RELEASE=/s/yes/no/' configure
signify -S -e -m ${DISTNAME}.sha256 -s ${PRIVKEY}
verify-release:
signify -C -p ${PUBKEY} -x ${DISTNAME}.sha256.sig
dist: ${DISTNAME}.sha256
${DISTNAME}.sha256: ${DISTNAME}.tar.gz
sha256 ${DISTNAME}.tar.gz > $@
${DISTNAME}.tar.gz: ${DISTFILES}
mkdir -p .dist/${DISTNAME}/
${INSTALL} -m 0644 ${DISTFILES} .dist/${DISTNAME}/
cd .dist/${DISTNAME} && chmod 755 configure
${MAKE} -C compat DESTDIR=${PWD}/.dist/${DISTNAME}/compat dist
${MAKE} -C contrib DESTDIR=${PWD}/.dist/${DISTNAME}/contrib dist
${MAKE} -C have DESTDIR=${PWD}/.dist/${DISTNAME}/have dist
${MAKE} -C keys DESTDIR=${PWD}/.dist/${DISTNAME}/keys dist
${MAKE} -C regress DESTDIR=${PWD}/.dist/${DISTNAME}/regress dist
cd .dist/ && tar zcf ../$@ ${DISTNAME}
rm -rf .dist/
# -- dependencies --
-include config.d
-include crypto.d
-include dirs.d
-include fcgi.d
-include ge.d
-include gg.d
-include gmid.d
-include iri.d
-include log.d
-include logger.d
-include mime.d
-include proc.d
-include proxy.d
-include puny.d
-include sandbox.d
-include server.d
-include titan.d
-include utf8.d
-include utils.d
-include y.tab.d

192
README.md
View File

@ -1,97 +1,147 @@
# gmid
# NAME
gmid is a full-featured Gemini server written with security in mind.
It can serve static files, has optional FastCGI and proxying support,
and a rich configuration syntax.
**gmid** - dead simple zero configuration gemini server
A few helper programs are shipped as part of gmid:
# SYNOPSIS
- `gg` is a simple command-line Gemini client.
**gmid**
\[**-h**]
\[**-c**&nbsp;*cert.pem*]
\[**-d**&nbsp;*docs*]
\[**-k**&nbsp;*key.pem*]
- `gemexp` is a stripped-down config-less version of gmid to quickly
serve a directory from the command line.
# DESCRIPTION
- `titan` is a command-line titan client.
**gmid**
is a very simple and minimal gemini server.
It only supports serving static content, and strive to be as simple as
possible.
**gmid**
will strip any sequence of
*../*
or trailing
*..*
in the requests made by clients, so it's impossible to serve content
outside the
*docs*
directory by mistake, and will also refuse to follow symlink.
Furthermore, on
OpenBSD,
pledge(2)
and
unveil(2)
are used to ensure that
**gmid**
dosen't do anything else than read files from the given directory and
accept network connections.
## Internationalisation (IRIs, UNICODE, punycode, all that stuff)
It should be noted that
**gmid**
is very simple in its implementation, and so it may not be appropriate
for serving site with lots of users.
After all, the code is single threaded and use a single process.
Even thought the current Gemini specification doesn't mention anything
in this regard, I do think these are important things and so I tried
to implement them in the most user-friendly way I could think of.
If a user request path is a directory,
**gmid**
will try to serve a
*index.gmi*
file inside that directory.
If not found, it will return an error 51 (not found) to the user.
For starters, gmid has full support for IRI (RFC3987 —
Internationalized Resource Identifiers). IRIs are a superset of URIs,
so there aren't incompatibilities with URI-only clients.
The options are as follows:
There is full support also for punycode. In theory, the user doesn't
even need to know that punycode is a thing. The hostname in the
configuration file can (and must be) in the decoded form (e.g. `naïve`
and not `xn--nave-6pa`), gmid will do the rest.
**-c** *cert.pem*
The only missing piece is UNICODE normalisation of the IRI path: gmid
doesn't do that (yet).
> The certificate to use, by default is
> *cert.pem*.
**-d** *docs*
## Configuration
> The root directory to serve.
> **gmid**
> won't serve any file that is outside that directory.
[httpd]: https://man.openbsd.org/httpd.8
**-h**
gmid has a rich configuration file, heavily inspired by OpenBSD'
[httpd(8)][httpd], with every detail carefully documented in the
manpage. Here's a minimal example of a config file:
> Print the usage and exit.
```conf
server "example.com" {
listen on * port 1965
cert "/path/to/cert.pem"
key "/path/to/key.pem"
root "/var/gemini/example.com"
}
```
**-k** *key.pem*
and a slightly more complex one
> The key for the certificate, by default is
> *key.pem*.
```conf
cert_root = "/path/to/keys"
# EXAMPLES
server "example.com" {
listen on * port 1965
To quickly getting started
alias "foobar.com"
$ # generate a cert and a key
$ openssl req -x509 -newkey rsa:4096 -keyout key.pem \
-out cert.pem -days 365 -nodes
$ mkdir docs
$ cat <<EOF > docs/index.gmi
# Hello world
test paragraph...
EOF
$ gmid -c cert.pem -k key.pem -d docs
cert $cert_root "/example.com.crt"
key $cert_root "/example.com.pem"
root "/var/gemini/example.com"
now you can visit gemini://localhost/ with your preferred gemini client.
# lang for text/gemini files
lang "en"
# CAVEATS
# only for locations that matches /files/*
location "/files/*" {
# generate directory listings
auto index on
}
* it doesn't support virtual hosts: the host part of the request URL is
completely ignored.
location "/repo/*" {
# change the index file name
index "README.gmi"
lang "it"
}
}
```
* it doesn't fork in the background or anything like that.
## Building
gmid depends on libevent2, LibreSSL or OpenSSL, and yacc or GNU bison.
The build is as simple as
$ ./configure
$ make
If the configure scripts fails to pick up something, please open an
issue or notify me via email.
To install execute:
# make install
Please keep in mind that the master branch, from time to time, may be
accidentally broken on some platforms. gmid is developed primarily on
OpenBSD/amd64 and commits on the master branch don't get always tested
in other OSes. Before tagging a release however, a comprehensive
testing on various platform is done to ensure that everything is
working as intended.
### Testing
Execute
$ make regress
to start the suite. Keep in mind that the regression tests needs to
create a few file inside the `regress` directory and bind the 10965
port.
## Contributing
Any form of contribution is welcome, not only patches or bug reports.
If you have a sample configuration for some specific use-case, a
script or anything that could be useful to others, consider adding it
to the `contrib` directory.
## Architecture/Security considerations
The internal architecture was revisited for the 2.0 release. For
earlier releases, please refer to previous revision of this file.
gmid has a privsep design, where the operations done by the daemon are
splitted into multiple processes:
- main: the main process is the only one that keeps the original
privileges. It opens the TLS certificates on the behalf of the
`server` and `crypto` processes, reloads the configuration upon
`SIGHUP` and re-opens the log files upon `SIGUSR1`.
- logger: handles the logging with syslog and/or local files.
- server: listens for connections and serves the request. It also
speaks FastCGI and do the proxying.
- crypto: holds the TLS private keys to avoid a compromised `server`
process to disclose them.

41
compat/Makefile Normal file
View File

@ -0,0 +1,41 @@
DISTFILES = Makefile \
arc4random.c \
arc4random.h \
chacha_private.h \
err.c \
explicit_bzero.c \
freezero.c \
getdtablecount.c \
getdtablesize.c \
getentropy.c \
getprogname.c \
imsg-buffer.c \
imsg.c \
imsg.h \
memmem.c \
queue.h \
reallocarray.c \
recallocarray.c \
setproctitle.c \
setresgid.c \
setresuid.c \
strlcat.c \
strlcpy.c \
strtonum.c \
timingsafe_memcmp.c \
tree.h \
vasprintf.c \
vis.c
all:
false
dist: ${DISTFILES}
mkdir -p ${DESTDIR}/
${INSTALL} -m 0644 ${DISTFILES} ${DESTDIR}/
mkdir -p ${DESTDIR}/vis
${INSTALL} -m 0644 vis/vis.h ${DESTDIR}/vis
${MAKE} -C libtls DESTDIR=${DESTDIR}/libtls dist
.PHONY: all dist
include ../config.mk

252
compat/arc4random.c Normal file
View File

@ -0,0 +1,252 @@
/* $OpenBSD: arc4random.c,v 1.58 2022/07/31 13:41:45 tb Exp $ */
/*
* Copyright (c) 1996, David Mazieres <dm@uun.org>
* Copyright (c) 2008, Damien Miller <djm@openbsd.org>
* Copyright (c) 2013, Markus Friedl <markus@openbsd.org>
* Copyright (c) 2014, Theo de Raadt <deraadt@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* ChaCha based random number generator for OpenBSD.
*/
/* OPENBSD ORIGINAL: lib/libc/crypt/arc4random.c */
#include "../config.h"
#include <sys/types.h>
#include <fcntl.h>
#include <limits.h>
#include <signal.h>
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
#ifndef HAVE_ARC4RANDOM
/*
* Always use the getentropy implementation from bsd-getentropy.c, which
* will call a native getentropy if available then fall back as required.
* We use a different name so that OpenSSL cannot call the wrong getentropy.
*/
int _ssh_compat_getentropy(void *, size_t);
#ifdef getentropy
# undef getentropy
#endif
#define getentropy(x, y) (_ssh_compat_getentropy((x), (y)))
#include "log.h"
#define KEYSTREAM_ONLY
#include "chacha_private.h"
#define minimum(a, b) ((a) < (b) ? (a) : (b))
#if defined(__GNUC__) || defined(_MSC_VER)
#define inline __inline
#else /* __GNUC__ || _MSC_VER */
#define inline
#endif /* !__GNUC__ && !_MSC_VER */
#define KEYSZ 32
#define IVSZ 8
#define BLOCKSZ 64
#define RSBUFSZ (16*BLOCKSZ)
#define REKEY_BASE (1024*1024) /* NB. should be a power of 2 */
/* Marked MAP_INHERIT_ZERO, so zero'd out in fork children. */
static struct _rs {
size_t rs_have; /* valid bytes at end of rs_buf */
size_t rs_count; /* bytes till reseed */
} *rs;
/* Maybe be preserved in fork children, if _rs_allocate() decides. */
static struct _rsx {
chacha_ctx rs_chacha; /* chacha context for random keystream */
u_char rs_buf[RSBUFSZ]; /* keystream blocks */
} *rsx;
static inline int _rs_allocate(struct _rs **, struct _rsx **);
static inline void _rs_forkdetect(void);
#include "arc4random.h"
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;
if (rs == NULL) {
if (_rs_allocate(&rs, &rsx) == -1)
_exit(1);
}
chacha_keysetup(&rsx->rs_chacha, buf, KEYSZ * 8);
chacha_ivsetup(&rsx->rs_chacha, buf + KEYSZ);
}
static void
_rs_stir(void)
{
u_char rnd[KEYSZ + IVSZ];
uint32_t rekey_fuzz = 0;
if (getentropy(rnd, sizeof rnd) == -1)
_getentropy_fail();
if (!rs)
_rs_init(rnd, sizeof(rnd));
else
_rs_rekey(rnd, sizeof(rnd));
explicit_bzero(rnd, sizeof(rnd)); /* discard source seed */
/* invalidate rs_buf */
rs->rs_have = 0;
memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
/* rekey interval should not be predictable */
chacha_encrypt_bytes(&rsx->rs_chacha, (uint8_t *)&rekey_fuzz,
(uint8_t *)&rekey_fuzz, sizeof(rekey_fuzz));
rs->rs_count = REKEY_BASE + (rekey_fuzz % REKEY_BASE);
}
static inline void
_rs_stir_if_needed(size_t len)
{
_rs_forkdetect();
if (!rs || rs->rs_count <= len)
_rs_stir();
if (rs->rs_count <= len)
rs->rs_count = 0;
else
rs->rs_count -= len;
}
static inline void
_rs_rekey(u_char *dat, size_t datlen)
{
#ifndef KEYSTREAM_ONLY
memset(rsx->rs_buf, 0, sizeof(rsx->rs_buf));
#endif
/* fill rs_buf with the keystream */
chacha_encrypt_bytes(&rsx->rs_chacha, rsx->rs_buf,
rsx->rs_buf, sizeof(rsx->rs_buf));
/* mix in optional user provided data */
if (dat) {
size_t i, m;
m = minimum(datlen, KEYSZ + IVSZ);
for (i = 0; i < m; i++)
rsx->rs_buf[i] ^= dat[i];
}
/* immediately reinit for backtracking resistance */
_rs_init(rsx->rs_buf, KEYSZ + IVSZ);
memset(rsx->rs_buf, 0, KEYSZ + IVSZ);
rs->rs_have = sizeof(rsx->rs_buf) - KEYSZ - IVSZ;
}
static inline void
_rs_random_buf(void *_buf, size_t n)
{
u_char *buf = (u_char *)_buf;
u_char *keystream;
size_t m;
_rs_stir_if_needed(n);
while (n > 0) {
if (rs->rs_have > 0) {
m = minimum(n, rs->rs_have);
keystream = rsx->rs_buf + sizeof(rsx->rs_buf)
- rs->rs_have;
memcpy(buf, keystream, m);
memset(keystream, 0, m);
buf += m;
n -= m;
rs->rs_have -= m;
}
if (rs->rs_have == 0)
_rs_rekey(NULL, 0);
}
}
static inline void
_rs_random_u32(uint32_t *val)
{
u_char *keystream;
_rs_stir_if_needed(sizeof(*val));
if (rs->rs_have < sizeof(*val))
_rs_rekey(NULL, 0);
keystream = rsx->rs_buf + sizeof(rsx->rs_buf) - rs->rs_have;
memcpy(val, keystream, sizeof(*val));
memset(keystream, 0, sizeof(*val));
rs->rs_have -= sizeof(*val);
}
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().
*/
# ifndef HAVE_ARC4RANDOM_BUF
void
arc4random_buf(void *buf, size_t n)
{
_ARC4_LOCK();
_rs_random_buf(buf, n);
_ARC4_UNLOCK();
}
# endif /* !HAVE_ARC4RANDOM_BUF */
#endif /* !HAVE_ARC4RANDOM */
/* arc4random_buf() that uses platform arc4random() */
#if !defined(HAVE_ARC4RANDOM_BUF) && defined(HAVE_ARC4RANDOM)
void
arc4random_buf(void *_buf, size_t n)
{
size_t i;
u_int32_t r = 0;
char *buf = (char *)_buf;
for (i = 0; i < n; i++) {
if (i % 4 == 0)
r = arc4random();
buf[i] = r & 0xff;
r >>= 8;
}
explicit_bzero(&r, sizeof(r));
}
#endif /* !defined(HAVE_ARC4RANDOM_BUF) && defined(HAVE_ARC4RANDOM) */

89
compat/arc4random.h Normal file
View File

@ -0,0 +1,89 @@
/* $OpenBSD: arc4random_linux.h,v 1.12 2019/07/11 10:37:28 inoguchi Exp $ */
/*
* Copyright (c) 1996, David Mazieres <dm@uun.org>
* Copyright (c) 2008, Damien Miller <djm@openbsd.org>
* Copyright (c) 2013, Markus Friedl <markus@openbsd.org>
* Copyright (c) 2014, Theo de Raadt <deraadt@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Stub functions for portability. From LibreSSL with some adaptations.
*/
#include <sys/mman.h>
#include <signal.h>
/* OpenSSH isn't multithreaded */
#define _ARC4_LOCK()
#define _ARC4_UNLOCK()
#define _ARC4_ATFORK(f)
static inline void
_getentropy_fail(void)
{
fatal("getentropy failed");
}
static volatile sig_atomic_t _rs_forked;
static inline void
_rs_forkhandler(void)
{
_rs_forked = 1;
}
static inline void
_rs_forkdetect(void)
{
static pid_t _rs_pid = 0;
pid_t pid = getpid();
if (_rs_pid == 0 || _rs_pid == 1 || _rs_pid != pid || _rs_forked) {
_rs_pid = pid;
_rs_forked = 0;
if (rs)
memset(rs, 0, sizeof(*rs));
}
}
static inline int
_rs_allocate(struct _rs **rsp, struct _rsx **rsxp)
{
#if defined(MAP_ANON) && defined(MAP_PRIVATE)
if ((*rsp = mmap(NULL, sizeof(**rsp), PROT_READ|PROT_WRITE,
MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED)
return (-1);
if ((*rsxp = mmap(NULL, sizeof(**rsxp), PROT_READ|PROT_WRITE,
MAP_ANON|MAP_PRIVATE, -1, 0)) == MAP_FAILED) {
munmap(*rsp, sizeof(**rsp));
*rsp = NULL;
return (-1);
}
#else
if ((*rsp = calloc(1, sizeof(**rsp))) == NULL)
return (-1);
if ((*rsxp = calloc(1, sizeof(**rsxp))) == NULL) {
free(*rsp);
*rsp = NULL;
return (-1);
}
#endif
_ARC4_ATFORK(_rs_forkhandler);
return (0);
}

224
compat/chacha_private.h Normal file
View File

@ -0,0 +1,224 @@
/* OPENBSD ORIGINAL: lib/libc/crypt/chacha_private.h */
/*
chacha-merged.c version 20080118
D. J. Bernstein
Public domain.
*/
/* $OpenBSD: chacha_private.h,v 1.3 2022/02/28 21:56:29 dtucker Exp $ */
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)
{
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
}
}

84
compat/err.c Normal file
View File

@ -0,0 +1,84 @@
/*
* Copyright (c) 2021 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "../config.h"
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void vwarn_impl(const char*, va_list);
static void vwarnx_impl(const char*, va_list);
static void
vwarn_impl(const char *fmt, va_list ap)
{
fprintf(stderr, "%s: ", getprogname());
vfprintf(stderr, fmt, ap);
fprintf(stderr, ": %s\n", strerror(errno));
}
static void
vwarnx_impl(const char *fmt, va_list ap)
{
fprintf(stderr, "%s: ", getprogname());
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
}
void
err(int ret, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vwarn_impl(fmt, ap);
va_end(ap);
exit(ret);
}
void
errx(int ret, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vwarnx_impl(fmt, ap);
va_end(ap);
exit(ret);
}
void
warn(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vwarn_impl(fmt, ap);
va_end(ap);
}
void
warnx(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vwarnx_impl(fmt, ap);
va_end(ap);
}

14
compat/explicit_bzero.c Normal file
View File

@ -0,0 +1,14 @@
/*
* Public domain.
* Written by Matthew Dempsky.
*/
#include "../config.h"
#include <string.h>
void
explicit_bzero(void *buf, size_t len)
{
memset(buf, 0, len);
}

30
compat/freezero.c Normal file
View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2021 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "../config.h"
#include <stdlib.h>
#include <string.h>
void
freezero(void *ptr, size_t len)
{
if (ptr == NULL)
return;
memset(ptr, 0, len);
free(ptr);
}

26
compat/getdtablecount.c Normal file
View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2021 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* XXX: on linux it's possible to glob("/proc/$pid/fd/ *") to know the
* dtablecount. */
#include "../config.h"
int
getdtablecount(void)
{
return 0;
}

25
compat/getdtablesize.c Normal file
View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2021 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "../config.h"
#include <unistd.h>
int
getdtablesize(void)
{
return sysconf(_SC_OPEN_MAX);
}

96
compat/getentropy.c Normal file
View File

@ -0,0 +1,96 @@
/*
* Copyright (c) 1996, David Mazieres <dm@uun.org>
* Copyright (c) 2008, Damien Miller <djm@openbsd.org>
* Copyright (c) 2013, Markus Friedl <markus@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "../config.h"
#ifndef SSH_RANDOM_DEV
# define SSH_RANDOM_DEV "/dev/urandom"
#endif /* SSH_RANDOM_DEV */
#include <sys/types.h>
#ifdef HAVE_SYS_RANDOM_H
# include <sys/random.h>
#endif
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifdef WITH_OPENSSL
#include <openssl/rand.h>
#include <openssl/err.h>
#endif
#include "log.h"
int _ssh_compat_getentropy(void *, size_t);
static int
seed_from_prngd(unsigned char *buf, size_t bytes)
{
return -1;
}
int
_ssh_compat_getentropy(void *s, size_t len)
{
#if defined(WITH_OPENSSL) && defined(OPENSSL_PRNG_ONLY)
if (RAND_bytes(s, len) <= 0)
fatal("Couldn't obtain random bytes (error 0x%lx)",
(unsigned long)ERR_get_error());
#else
int fd, save_errno;
ssize_t r;
size_t o = 0;
#ifdef WITH_OPENSSL
if (RAND_bytes(s, len) == 1)
return 0;
#endif
#ifdef HAVE_GETENTROPY
if ((r = getentropy(s, len)) == 0)
return 0;
#endif /* HAVE_GETENTROPY */
#ifdef HAVE_GETRANDOM
if ((r = getrandom(s, len, 0)) > 0 && (size_t)r == len)
return 0;
#endif /* HAVE_GETRANDOM */
if ((fd = open(SSH_RANDOM_DEV, O_RDONLY)) == -1) {
save_errno = errno;
/* Try egd/prngd before giving up. */
if (seed_from_prngd(s, len) == 0)
return 0;
fatal("Couldn't open %s: %s", SSH_RANDOM_DEV,
strerror(save_errno));
}
while (o < len) {
r = read(fd, (u_char *)s + o, len - o);
if (r < 0) {
if (errno == EAGAIN || errno == EINTR ||
errno == EWOULDBLOCK)
continue;
fatal("read %s: %s", SSH_RANDOM_DEV, strerror(errno));
}
o += r;
}
close(fd);
#endif /* WITH_OPENSSL */
return 0;
}

39
compat/getprogname.c Normal file
View File

@ -0,0 +1,39 @@
/*
* Copyright (c) 2021 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "../config.h"
#if HAVE_PROGRAM_INVOCATION_SHORT_NAME
#include <errno.h>
extern char *program_invocation_short_name;
const char *
getprogname(void)
{
return program_invocation_short_name;
}
#else
const char *
getprogname(void)
{
return "gmid";
}
#endif

697
compat/imsg-buffer.c Normal file
View File

@ -0,0 +1,697 @@
/* $OpenBSD: imsg-buffer.c,v 1.18 2023/12/12 15:47:41 claudio Exp $ */
/*
* Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "../config.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <limits.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "imsg.h"
static int ibuf_realloc(struct ibuf *, size_t);
static void ibuf_enqueue(struct msgbuf *, struct ibuf *);
static void ibuf_dequeue(struct msgbuf *, struct ibuf *);
static void msgbuf_drain(struct msgbuf *, size_t);
struct ibuf *
ibuf_open(size_t len)
{
struct ibuf *buf;
if (len == 0) {
errno = EINVAL;
return (NULL);
}
if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
return (NULL);
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);
}
static int
ibuf_realloc(struct ibuf *buf, size_t len)
{
unsigned char *b;
/* on static buffers max is eq size and so the following fails */
if (len > SIZE_MAX - buf->wpos || buf->wpos + len > buf->max) {
errno = ERANGE;
return (-1);
}
b = recallocarray(buf->buf, buf->size, buf->wpos + len, 1);
if (b == NULL)
return (-1);
buf->buf = b;
buf->size = buf->wpos + len;
return (0);
}
void *
ibuf_reserve(struct ibuf *buf, size_t len)
{
void *b;
if (len > SIZE_MAX - buf->wpos || buf->max == 0) {
errno = ERANGE;
return (NULL);
}
if (buf->wpos + len > buf->size)
if (ibuf_realloc(buf, len) == -1)
return (NULL);
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 ((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));
}
/* remove after tree is converted */
int
ibuf_add_buf(struct ibuf *buf, const struct ibuf *from)
{
return ibuf_add_ibuf(buf, 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 ((b = ibuf_reserve(buf, len)) == NULL)
return (-1);
memset(b, 0, len);
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);
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)));
}
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)
{
if (buf->max == 0)
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->max == 0) {
/* only allow to truncate down */
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)
{
ibuf_enqueue(msgbuf, 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 = -1;
}
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_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);
}
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_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)
{
if (buf == NULL)
return;
if (buf->max == 0) /* if buf lives on the stack */
abort(); /* abort before causing more harm */
if (buf->fd != -1)
close(buf->fd);
freezero(buf->buf, buf->size);
free(buf);
}
int
ibuf_fd_avail(struct ibuf *buf)
{
return (buf->fd != -1);
}
int
ibuf_fd_get(struct ibuf *buf)
{
int fd;
fd = buf->fd;
buf->fd = -1;
return (fd);
}
void
ibuf_fd_set(struct ibuf *buf, int fd)
{
if (buf->max == 0) /* if buf lives on the stack */
abort(); /* abort before causing more harm */
if (buf->fd != -1)
close(buf->fd);
buf->fd = fd;
}
int
ibuf_write(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, entry) {
if (i >= IOV_MAX)
break;
iov[i].iov_base = ibuf_data(buf);
iov[i].iov_len = ibuf_size(buf);
i++;
}
again:
if ((n = writev(msgbuf->fd, iov, i)) == -1) {
if (errno == EINTR)
goto again;
if (errno == ENOBUFS)
errno = EAGAIN;
return (-1);
}
if (n == 0) { /* connection closed */
errno = 0;
return (0);
}
msgbuf_drain(msgbuf, n);
return (1);
}
void
msgbuf_init(struct msgbuf *msgbuf)
{
msgbuf->queued = 0;
msgbuf->fd = -1;
TAILQ_INIT(&msgbuf->bufs);
}
static void
msgbuf_drain(struct msgbuf *msgbuf, size_t n)
{
struct ibuf *buf, *next;
for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
buf = next) {
next = TAILQ_NEXT(buf, entry);
if (n >= ibuf_size(buf)) {
n -= ibuf_size(buf);
ibuf_dequeue(msgbuf, buf);
} else {
buf->rpos += n;
n = 0;
}
}
}
void
msgbuf_clear(struct msgbuf *msgbuf)
{
struct ibuf *buf;
while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL)
ibuf_dequeue(msgbuf, buf);
}
int
msgbuf_write(struct msgbuf *msgbuf)
{
struct iovec iov[IOV_MAX];
struct ibuf *buf, *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, 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;
}
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(msgbuf->fd, &msg, 0)) == -1) {
if (errno == EINTR)
goto again;
if (errno == ENOBUFS)
errno = EAGAIN;
return (-1);
}
if (n == 0) { /* connection closed */
errno = 0;
return (0);
}
/*
* assumption: fd got sent if sendmsg sent anything
* this works because fds are passed one at a time
*/
if (buf0 != NULL) {
close(buf0->fd);
buf0->fd = -1;
}
msgbuf_drain(msgbuf, n);
return (1);
}
uint32_t
msgbuf_queuelen(struct msgbuf *msgbuf)
{
return (msgbuf->queued);
}
static void
ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
{
if (buf->max == 0) /* if buf lives on the stack */
abort(); /* abort before causing more harm */
TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
msgbuf->queued++;
}
static void
ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
{
TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
msgbuf->queued--;
ibuf_free(buf);
}

449
compat/imsg.c Normal file
View File

@ -0,0 +1,449 @@
/* $OpenBSD: imsg.c,v 1.23 2023/12/12 15:47:41 claudio Exp $ */
/*
* Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "../config.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "imsg.h"
struct imsg_fd {
TAILQ_ENTRY(imsg_fd) entry;
int fd;
};
int imsg_fd_overhead = 0;
static int imsg_dequeue_fd(struct imsgbuf *);
void
imsg_init(struct imsgbuf *imsgbuf, int fd)
{
msgbuf_init(&imsgbuf->w);
memset(&imsgbuf->r, 0, sizeof(imsgbuf->r));
imsgbuf->fd = fd;
imsgbuf->w.fd = fd;
imsgbuf->pid = getpid();
TAILQ_INIT(&imsgbuf->fds);
}
ssize_t
imsg_read(struct imsgbuf *imsgbuf)
{
struct msghdr msg;
struct cmsghdr *cmsg;
union {
struct cmsghdr hdr;
char buf[CMSG_SPACE(sizeof(int) * 1)];
} cmsgbuf;
struct iovec iov;
ssize_t n = -1;
int fd;
struct imsg_fd *ifd;
memset(&msg, 0, sizeof(msg));
memset(&cmsgbuf, 0, sizeof(cmsgbuf));
iov.iov_base = imsgbuf->r.buf + imsgbuf->r.wpos;
iov.iov_len = sizeof(imsgbuf->r.buf) - imsgbuf->r.wpos;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
msg.msg_control = &cmsgbuf.buf;
msg.msg_controllen = sizeof(cmsgbuf.buf);
if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL)
return (-1);
again:
if (getdtablecount() + imsg_fd_overhead +
(int)((CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int))
>= getdtablesize()) {
errno = EAGAIN;
free(ifd);
return (-1);
}
if ((n = recvmsg(imsgbuf->fd, &msg, 0)) == -1) {
if (errno == EINTR)
goto again;
goto fail;
}
imsgbuf->r.wpos += n;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SCM_RIGHTS) {
int i;
int j;
/*
* We only accept one file descriptor. Due to C
* padding rules, our control buffer might contain
* more than one fd, and we must close them.
*/
j = ((char *)cmsg + cmsg->cmsg_len -
(char *)CMSG_DATA(cmsg)) / sizeof(int);
for (i = 0; i < j; i++) {
fd = ((int *)CMSG_DATA(cmsg))[i];
if (ifd != NULL) {
ifd->fd = fd;
TAILQ_INSERT_TAIL(&imsgbuf->fds, ifd,
entry);
ifd = NULL;
} else
close(fd);
}
}
/* we do not handle other ctl data level */
}
fail:
free(ifd);
return (n);
}
ssize_t
imsg_get(struct imsgbuf *imsgbuf, struct imsg *imsg)
{
struct imsg m;
size_t av, left, datalen;
av = imsgbuf->r.wpos;
if (IMSG_HEADER_SIZE > av)
return (0);
memcpy(&m.hdr, imsgbuf->r.buf, sizeof(m.hdr));
if (m.hdr.len < IMSG_HEADER_SIZE ||
m.hdr.len > MAX_IMSGSIZE) {
errno = ERANGE;
return (-1);
}
if (m.hdr.len > av)
return (0);
m.fd = -1;
m.buf = NULL;
m.data = NULL;
datalen = m.hdr.len - IMSG_HEADER_SIZE;
imsgbuf->r.rptr = imsgbuf->r.buf + IMSG_HEADER_SIZE;
if (datalen != 0) {
if ((m.buf = ibuf_open(datalen)) == NULL)
return (-1);
if (ibuf_add(m.buf, imsgbuf->r.rptr, datalen) == -1) {
/* this should never fail */
ibuf_free(m.buf);
return (-1);
}
m.data = ibuf_data(m.buf);
}
if (m.hdr.flags & IMSGF_HASFD)
m.fd = imsg_dequeue_fd(imsgbuf);
if (m.hdr.len < av) {
left = av - m.hdr.len;
memmove(&imsgbuf->r.buf, imsgbuf->r.buf + m.hdr.len, left);
imsgbuf->r.wpos = left;
} else
imsgbuf->r.wpos = 0;
*imsg = m;
return (datalen + IMSG_HEADER_SIZE);
}
int
imsg_get_ibuf(struct imsg *imsg, struct ibuf *ibuf)
{
if (imsg->buf == NULL) {
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 (imsg->buf == NULL || ibuf_size(imsg->buf) != len) {
errno = EBADMSG;
return (-1);
}
return ibuf_get(imsg->buf, data, len);
}
int
imsg_get_fd(struct imsg *imsg)
{
int fd = imsg->fd;
imsg->fd = -1;
return fd;
}
uint32_t
imsg_get_id(struct imsg *imsg)
{
return (imsg->hdr.peerid);
}
size_t
imsg_get_len(struct imsg *imsg)
{
if (imsg->buf == NULL)
return 0;
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)
return (-1);
if (imsg_add(wbuf, data, datalen) == -1)
return (-1);
ibuf_fd_set(wbuf, fd);
imsg_close(imsgbuf, 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)
return (-1);
for (i = 0; i < iovcnt; i++)
if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
return (-1);
ibuf_fd_set(wbuf, fd);
imsg_close(imsgbuf, wbuf);
return (1);
}
/*
* Enqueue imsg with payload from ibuf buf. fd passing is not possible
* with this function.
*/
int
imsg_compose_ibuf(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id,
pid_t pid, struct ibuf *buf)
{
struct ibuf *hdrbuf = NULL;
struct imsg_hdr hdr;
int save_errno;
if (ibuf_size(buf) + IMSG_HEADER_SIZE > MAX_IMSGSIZE) {
errno = ERANGE;
goto fail;
}
hdr.type = type;
hdr.len = ibuf_size(buf) + IMSG_HEADER_SIZE;
hdr.flags = 0;
hdr.peerid = id;
if ((hdr.pid = pid) == 0)
hdr.pid = imsgbuf->pid;
if ((hdrbuf = ibuf_open(IMSG_HEADER_SIZE)) == NULL)
goto fail;
if (imsg_add(hdrbuf, &hdr, sizeof(hdr)) == -1)
goto fail;
ibuf_close(&imsgbuf->w, hdrbuf);
ibuf_close(&imsgbuf->w, buf);
return (1);
fail:
save_errno = errno;
ibuf_free(buf);
ibuf_free(hdrbuf);
errno = save_errno;
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 = 0;
if (msg->fd != -1) {
close(msg->fd);
msg->fd = -1;
}
if (msg->buf != NULL) {
ibuf_rewind(msg->buf);
len = ibuf_size(msg->buf);
}
if ((wbuf = imsg_create(imsgbuf, msg->hdr.type, msg->hdr.peerid,
msg->hdr.pid, len)) == NULL)
return (-1);
if (msg->buf != NULL) {
if (ibuf_add_buf(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 > MAX_IMSGSIZE) {
errno = ERANGE;
return (NULL);
}
hdr.type = type;
hdr.flags = 0;
hdr.peerid = id;
if ((hdr.pid = pid) == 0)
hdr.pid = imsgbuf->pid;
if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
return (NULL);
}
if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
return (NULL);
return (wbuf);
}
int
imsg_add(struct ibuf *msg, const void *data, size_t datalen)
{
if (datalen)
if (ibuf_add(msg, data, datalen) == -1) {
ibuf_free(msg);
return (-1);
}
return (datalen);
}
void
imsg_close(struct imsgbuf *imsgbuf, struct ibuf *msg)
{
struct imsg_hdr *hdr;
hdr = (struct imsg_hdr *)msg->buf;
hdr->flags &= ~IMSGF_HASFD;
if (ibuf_fd_avail(msg))
hdr->flags |= IMSGF_HASFD;
hdr->len = ibuf_size(msg);
ibuf_close(&imsgbuf->w, msg);
}
void
imsg_free(struct imsg *imsg)
{
ibuf_free(imsg->buf);
}
static int
imsg_dequeue_fd(struct imsgbuf *imsgbuf)
{
int fd;
struct imsg_fd *ifd;
if ((ifd = TAILQ_FIRST(&imsgbuf->fds)) == NULL)
return (-1);
fd = ifd->fd;
TAILQ_REMOVE(&imsgbuf->fds, ifd, entry);
free(ifd);
return (fd);
}
int
imsg_flush(struct imsgbuf *imsgbuf)
{
while (imsgbuf->w.queued)
if (msgbuf_write(&imsgbuf->w) <= 0)
return (-1);
return (0);
}
void
imsg_clear(struct imsgbuf *imsgbuf)
{
int fd;
msgbuf_clear(&imsgbuf->w);
while ((fd = imsg_dequeue_fd(imsgbuf)) != -1)
close(fd);
}

158
compat/imsg.h Normal file
View File

@ -0,0 +1,158 @@
/* $OpenBSD: imsg.h,v 1.8 2023/12/12 15:47:41 claudio Exp $ */
/*
* Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
* Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
* Copyright (c) 2006, 2007, 2008 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef _IMSG_H_
#define _IMSG_H_
#include <sys/types.h>
#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 msgbuf {
TAILQ_HEAD(, ibuf) bufs;
uint32_t queued;
int fd;
};
struct ibuf_read {
unsigned char buf[IBUF_READ_SIZE];
unsigned char *rptr;
size_t wpos;
};
struct imsg_fd;
struct imsgbuf {
TAILQ_HEAD(, imsg_fd) fds;
struct ibuf_read r;
struct msgbuf w;
int fd;
pid_t pid;
};
#define IMSGF_HASFD 1
struct imsg_hdr {
uint32_t type;
uint16_t len;
uint16_t flags;
uint32_t peerid;
uint32_t pid;
};
struct imsg {
struct imsg_hdr hdr;
int fd;
void *data;
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_buf(struct ibuf *, const struct ibuf *);
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);
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);
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 *);
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);
int ibuf_write(struct msgbuf *);
void msgbuf_init(struct msgbuf *);
void msgbuf_clear(struct msgbuf *);
uint32_t msgbuf_queuelen(struct msgbuf *);
int msgbuf_write(struct msgbuf *);
/* imsg.c */
void imsg_init(struct imsgbuf *, int);
ssize_t imsg_read(struct imsgbuf *);
ssize_t imsg_get(struct imsgbuf *, struct imsg *);
int imsg_get_ibuf(struct imsg *, struct ibuf *);
int imsg_get_data(struct imsg *, void *, 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);
int imsg_compose_ibuf(struct imsgbuf *, uint32_t, uint32_t, pid_t,
struct ibuf *);
struct ibuf *imsg_create(struct imsgbuf *, uint32_t, uint32_t, pid_t, size_t);
int imsg_add(struct ibuf *, const void *, size_t);
void imsg_close(struct imsgbuf *, struct ibuf *);
void imsg_free(struct imsg *);
int imsg_flush(struct imsgbuf *);
void imsg_clear(struct imsgbuf *);
#endif

28
compat/libtls/Makefile Normal file
View File

@ -0,0 +1,28 @@
DISTFILES = Makefile \
asn.c \
by_mem.c \
openssl.c \
tls.c \
tls.h \
tls_bio_cb.c \
tls_client.c \
tls_config.c \
tls_conninfo.c \
tls_internal.h \
tls_keypair.c \
tls_ocsp.c \
tls_peer.c \
tls_server.c \
tls_signer.c \
tls_util.c \
tls_verify.c
all:
false
dist: ${DISTFILES}
mkdir -p ${DESTDIR}/
${INSTALL} -m 0644 ${DISTFILES} ${DESTDIR}/
.PHONY: all dist
include ../../config.mk

80
compat/libtls/asn.c Normal file
View File

@ -0,0 +1,80 @@
/* $OpenBSD: a_time_tm.c,v 1.15 2018/04/25 11:48:21 tb Exp $ */
/*
* Copyright (c) 2015 Bob Beck <beck@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#define GENTIME_LENGTH 15
#define UTCTIME_LENGTH 13
#define V_ASN1_UTCTIME 23
#define V_ASN1_GENERALIZEDTIME 24
#ifndef HAVE_ASN1_TIME_TM_CMP
int
ASN1_time_tm_cmp(struct tm *tm1, struct tm *tm2)
{
if (tm1->tm_year < tm2->tm_year)
return (-1);
if (tm1->tm_year > tm2->tm_year)
return (1);
if (tm1->tm_mon < tm2->tm_mon)
return (-1);
if (tm1->tm_mon > tm2->tm_mon)
return (1);
if (tm1->tm_mday < tm2->tm_mday)
return (-1);
if (tm1->tm_mday > tm2->tm_mday)
return (1);
if (tm1->tm_hour < tm2->tm_hour)
return (-1);
if (tm1->tm_hour > tm2->tm_hour)
return (1);
if (tm1->tm_min < tm2->tm_min)
return (-1);
if (tm1->tm_min > tm2->tm_min)
return (1);
if (tm1->tm_sec < tm2->tm_sec)
return (-1);
if (tm1->tm_sec > tm2->tm_sec)
return (1);
return 0;
}
#endif
#ifndef HAVE_ASN1_TIME_TM_CLAMP_NOTAFTER
int
ASN1_time_tm_clamp_notafter(struct tm *tm)
{
#ifdef SMALL_TIME_T
struct tm broken_os_epoch_tm;
time_t broken_os_epoch_time = INT_MAX;
if (gmtime_r(&broken_os_epoch_time, &broken_os_epoch_tm) == NULL)
return 0;
if (ASN1_time_tm_cmp(tm, &broken_os_epoch_tm) == 1)
memcpy(tm, &broken_os_epoch_tm, sizeof(*tm));
#endif
return 1;
}
#endif

141
compat/libtls/by_mem.c Normal file
View File

@ -0,0 +1,141 @@
/* $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 "config.h"
#ifndef HAVE_X509_LOOKUP_MEM
#include <sys/uio.h>
#include <errno.h>
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <openssl/buffer.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/lhash.h>
#include <openssl/x509.h>
#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 */

212
compat/libtls/openssl.c Normal file
View File

@ -0,0 +1,212 @@
#include "config.h"
#include <sys/types.h>
#include <sys/uio.h>
#include <limits.h>
#include <unistd.h>
#include <stdio.h>
#include <openssl/err.h>
#include <openssl/bio.h>
#include <openssl/objects.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
#include <openssl/pem.h>
#include <openssl/ssl.h>
#include "log.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);
static 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 */

927
compat/libtls/tls.c Normal file
View File

@ -0,0 +1,927 @@
/* $OpenBSD: tls.c,v 1.98 2023/07/02 06:37:27 beck Exp $ */
/*
* Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <sys/socket.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/safestack.h>
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <tls.h>
#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;
}
void
tls_error_clear(struct tls_error *error)
{
free(error->msg);
error->msg = NULL;
error->num = 0;
error->tls = 0;
}
static int
tls_error_vset(struct tls_error *error, int errnum, const char *fmt, va_list ap)
{
char *errmsg = NULL;
int rv = -1;
tls_error_clear(error);
error->num = errnum;
error->tls = 1;
if (vasprintf(&errmsg, fmt, ap) == -1) {
errmsg = NULL;
goto err;
}
if (errnum == -1) {
error->msg = errmsg;
return (0);
}
if (asprintf(&error->msg, "%s: %s", errmsg, strerror(errnum)) == -1) {
error->msg = NULL;
goto err;
}
rv = 0;
err:
free(errmsg);
return (rv);
}
int
tls_error_set(struct tls_error *error, const char *fmt, ...)
{
va_list ap;
int errnum, rv;
errnum = errno;
va_start(ap, fmt);
rv = tls_error_vset(error, errnum, fmt, ap);
va_end(ap);
return (rv);
}
int
tls_error_setx(struct tls_error *error, const char *fmt, ...)
{
va_list ap;
int rv;
va_start(ap, fmt);
rv = tls_error_vset(error, -1, fmt, ap);
va_end(ap);
return (rv);
}
int
tls_config_set_error(struct tls_config *config, const char *fmt, ...)
{
va_list ap;
int errnum, rv;
errnum = errno;
va_start(ap, fmt);
rv = tls_error_vset(&config->error, errnum, fmt, ap);
va_end(ap);
return (rv);
}
int
tls_config_set_errorx(struct tls_config *config, const char *fmt, ...)
{
va_list ap;
int rv;
va_start(ap, fmt);
rv = tls_error_vset(&config->error, -1, fmt, ap);
va_end(ap);
return (rv);
}
int
tls_set_error(struct tls *ctx, const char *fmt, ...)
{
va_list ap;
int errnum, rv;
errnum = errno;
va_start(ap, fmt);
rv = tls_error_vset(&ctx->error, errnum, fmt, ap);
va_end(ap);
return (rv);
}
int
tls_set_errorx(struct tls *ctx, const char *fmt, ...)
{
va_list ap;
int rv;
va_start(ap, fmt);
rv = tls_error_vset(&ctx->error, -1, fmt, ap);
va_end(ap);
return (rv);
}
int
tls_set_ssl_errorx(struct tls *ctx, 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, -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, ctx->config->use_fake_private_key ?
"cert too long" : "key too long");
goto err;
}
if ((bio = BIO_new_mem_buf(mem, len)) == NULL) {
tls_set_errorx(ctx, "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, "failed to read X509 certificate");
goto err;
}
if ((*pkey = X509_get_pubkey(x509)) == NULL) {
tls_set_errorx(ctx, "failed to retrieve pubkey");
goto err;
}
} else {
if ((*pkey = PEM_read_bio_PrivateKey(bio, NULL, tls_password_cb,
NULL)) == NULL) {
tls_set_errorx(ctx, "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, "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, "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, "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, "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, "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, "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, "failed to set EC key");
goto err;
}
break;
default:
tls_set_errorx(ctx, "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, "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, "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, "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, "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, "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, "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, "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, "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, "ca too long");
goto err;
}
if (SSL_CTX_load_verify_mem(ssl_ctx, ca_mem, ca_len) != 1) {
tls_set_errorx(ctx, "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, "ssl verify locations failure");
goto err;
}
if (crl_mem != NULL) {
if (crl_len > INT_MAX) {
tls_set_errorx(ctx, "crl too long");
goto err;
}
if ((bio = BIO_new_mem_buf(crl_mem, crl_len)) == NULL) {
tls_set_errorx(ctx, "failed to create buffer");
goto err;
}
if ((xis = PEM_X509_INFO_read_bio(bio, NULL, tls_password_cb,
NULL)) == NULL) {
tls_set_errorx(ctx, "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_error(ctx, "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.num = -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, "%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, "%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, "%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, "invalid operation for context");
goto out;
}
if ((ctx->state & TLS_HANDSHAKE_COMPLETE) != 0) {
tls_set_errorx(ctx, "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, "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, "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, "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, "shutdown");
rv = -1;
}
}
if (close(ctx->socket) != 0) {
if (rv == 0) {
tls_set_error(ctx, "close");
rv = -1;
}
}
ctx->socket = -1;
}
if ((ctx->state & TLS_EOF_NO_CLOSE_NOTIFY) != 0) {
tls_set_errorx(ctx, "EOF without close notify");
rv = -1;
}
out:
/* Prevent callers from performing incorrect error handling */
errno = 0;
return (rv);
}

223
compat/libtls/tls.h Normal file
View File

@ -0,0 +1,223 @@
/* $OpenBSD: tls.h,v 1.63 2023/07/02 06:37:27 beck Exp $ */
/*
* Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef HEADER_TLS_H
#define HEADER_TLS_H
#ifdef __cplusplus
extern "C" {
#endif
#include <sys/types.h>
#include <stddef.h>
#include <stdint.h>
#define TLS_API 20200120
/*
* Deprecated versions of TLS. Using these effectively selects
* the minimum supported version.
*/
#define TLS_PROTOCOL_TLSv1_0 (1 << 3)
#define TLS_PROTOCOL_TLSv1_1 (1 << 3)
/* 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
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);
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_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 */

169
compat/libtls/tls_bio_cb.c Normal file
View File

@ -0,0 +1,169 @@
/* $OpenBSD: tls_bio_cb.c,v 1.21 2023/05/14 07:26:25 op Exp $ */
/*
* Copyright (c) 2016 Tobias Pape <tobias@netshed.de>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <openssl/bio.h>
#include <tls.h>
#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, "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, "failed to create callback method");
goto err;
}
if ((bio = BIO_new(bio_cb)) == NULL) {
tls_set_errorx(ctx, "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);
}

487
compat/libtls/tls_client.c Normal file
View File

@ -0,0 +1,487 @@
/* $OpenBSD: tls_client.c,v 1.49 2023/05/14 07:26:25 op Exp $ */
/*
* Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <limits.h>
#include <netdb.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <openssl/err.h>
#include <openssl/x509.h>
#include <tls.h>
#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, "not a client context");
goto err;
}
if (host == NULL) {
tls_set_errorx(ctx, "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, "memory allocation failure");
goto err;
}
if (ret != 0) {
tls_set_errorx(ctx, "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_error(ctx, "%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, "socket");
continue;
}
if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
tls_set_error(ctx, "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, "failed to stat session file");
goto err;
}
if (sb.st_size < 0 || sb.st_size > INT_MAX) {
tls_set_errorx(ctx, "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, "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, "failed to parse session");
goto err;
}
if (SSL_set_session(ctx->ssl_conn, ss) != 1) {
tls_set_errorx(ctx, "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, "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, "failed to truncate session file");
goto err;
}
while (len > 0) {
if ((n = pwrite(sfd, data + offset, len, offset)) == -1) {
tls_set_error(ctx, "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, "not a client context");
goto err;
}
if (servername != NULL) {
if ((ctx->servername = strdup(servername)) == NULL) {
tls_set_errorx(ctx, "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, "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, "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, "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, "ssl OCSP verification setup failure");
goto err;
}
if ((ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) {
tls_set_errorx(ctx, "ssl connection failure");
goto err;
}
if (SSL_set_app_data(ctx->ssl_conn, ctx) != 1) {
tls_set_errorx(ctx, "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, "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, "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, "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, "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, "not a client context");
goto err;
}
if ((ctx->state & TLS_CONNECTED) == 0) {
tls_set_errorx(ctx, "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, "no server certificate");
goto err;
}
if (tls_check_name(ctx, cert, ctx->servername, &match) == -1)
goto err;
if (!match) {
tls_set_errorx(ctx, "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);
}

926
compat/libtls/tls_config.c Normal file
View File

@ -0,0 +1,926 @@
/* $OpenBSD: tls_config.c,v 1.67 2023/07/02 06:37:27 beck Exp $ */
/*
* Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <tls.h>
#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)) == -1) {
tls_error_set(error, "failed to open %s file '%s'",
filetype, filename);
goto err;
}
if (fstat(fd, &st) != 0) {
tls_error_set(error, "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_set(error, "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, "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;
}
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_2;
else if (strcasecmp(p, "tlsv1.1") == 0)
proto = TLS_PROTOCOL_TLSv1_2;
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, "alpn too large");
goto err;
}
if ((buf = malloc(buf_len)) == NULL) {
tls_config_set_errorx(config, "out of memory");
goto err;
}
if ((s = strdup(alpn)) == NULL) {
tls_config_set_errorx(config, "out of memory");
goto err;
}
i = 0;
q = s;
while ((p = strsep(&q, ",")) != NULL) {
if ((len = strlen(p)) == 0) {
tls_config_set_errorx(config,
"alpn protocol with zero length");
goto err;
}
if (len > 255) {
tls_config_set_errorx(config,
"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, "out of memory");
goto err;
}
if (SSL_CTX_set_cipher_list(ssl_ctx, ciphers) != 1) {
tls_config_set_errorx(config, "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, "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, "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 nid;
free(config->ecdhecurves);
config->ecdhecurves = NULL;
config->ecdhecurves_len = 0;
if (curves == NULL || strcasecmp(curves, "default") == 0)
curves = TLS_ECDHE_CURVES;
if ((cs = strdup(curves)) == NULL) {
tls_config_set_errorx(config, "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) {
tls_config_set_errorx(config,
"invalid ecdhe curve '%s'", p);
goto err;
}
if ((curves_new = reallocarray(curves_list, curves_num + 1,
sizeof(int))) == NULL) {
tls_config_set_errorx(config, "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, "failed to stat session file");
return (-1);
}
if (!S_ISREG(sb.st_mode)) {
tls_config_set_errorx(config,
"session file is not a regular file");
return (-1);
}
if (sb.st_uid != getuid()) {
tls_config_set_errorx(config, "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, "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, "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, "session lifetime too large");
return (-1);
}
if (lifetime != 0 && lifetime < TLS_MIN_SESSION_TIMEOUT) {
tls_config_set_errorx(config, "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,
"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, "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);
}

View File

@ -0,0 +1,346 @@
/* $OpenBSD: tls_conninfo.c,v 1.24 2023/11/13 10:51:49 tb Exp $ */
/*
* Copyright (c) 2015 Joel Sing <jsing@openbsd.org>
* Copyright (c) 2015 Bob Beck <beck@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <openssl/x509.h>
#include <tls.h>
#include "tls_internal.h"
int ASN1_time_tm_clamp_notafter(struct tm *tm);
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, "unable to compute peer certificate hash - out of memory");
*hash = NULL;
return -1;
}
return 0;
}
static int
tls_get_peer_cert_issuer(struct tls *ctx, char **issuer)
{
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)
{
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_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 (!ASN1_time_tm_clamp_notafter(&after_tm))
goto err;
if ((*notbefore = timegm(&before_tm)) == -1)
goto err;
if ((*notafter = timegm(&after_tm)) == -1)
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_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, "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->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);
}

View File

@ -0,0 +1,323 @@
/* $OpenBSD: tls_internal.h,v 1.83 2023/06/27 18:19:59 tb Exp $ */
/*
* Copyright (c) 2014 Jeremie Courreges-Anglas <jca@openbsd.org>
* Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef HEADER_TLS_INTERNAL_H
#define HEADER_TLS_INTERNAL_H
#include <arpa/inet.h>
#include <netinet/in.h>
#include <openssl/ssl.h>
#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 "X25519,P-256,P-384"
union tls_addr {
struct in_addr ip4;
struct in6_addr ip6;
};
struct tls_error {
char *msg;
int num;
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 *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_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, const char *fmt, ...)
__attribute__((__format__ (printf, 2, 3)))
__attribute__((__nonnull__ (2)));
int tls_error_setx(struct tls_error *error, const char *fmt, ...)
__attribute__((__format__ (printf, 2, 3)))
__attribute__((__nonnull__ (2)));
int tls_config_set_error(struct tls_config *cfg, const char *fmt, ...)
__attribute__((__format__ (printf, 2, 3)))
__attribute__((__nonnull__ (2)));
int tls_config_set_errorx(struct tls_config *cfg, const char *fmt, ...)
__attribute__((__format__ (printf, 2, 3)))
__attribute__((__nonnull__ (2)));
int tls_set_error(struct tls *ctx, const char *fmt, ...)
__attribute__((__format__ (printf, 2, 3)))
__attribute__((__nonnull__ (2)));
int tls_set_errorx(struct tls *ctx, const char *fmt, ...)
__attribute__((__format__ (printf, 2, 3)))
__attribute__((__nonnull__ (2)));
int tls_set_ssl_errorx(struct tls *ctx, const char *fmt, ...)
__attribute__((__format__ (printf, 2, 3)))
__attribute__((__nonnull__ (2)));
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 */

171
compat/libtls/tls_keypair.c Normal file
View File

@ -0,0 +1,171 @@
/* $OpenBSD: tls_keypair.c,v 1.8 2021/01/05 17:37:12 jsing Exp $ */
/*
* Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <tls.h>
#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_set(error, "keypair has no certificate");
goto err;
}
if ((cert_bio = BIO_new_mem_buf(keypair->cert_mem,
keypair->cert_len)) == NULL) {
tls_error_set(error, "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_set(error, "failed to load certificate: %s", errstr);
goto err;
}
rv = 0;
err:
BIO_free(cert_bio);
return (rv);
}

466
compat/libtls/tls_ocsp.c Normal file
View File

@ -0,0 +1,466 @@
/* $OpenBSD: tls_ocsp.c,v 1.24 2023/11/13 10:56:19 tb Exp $ */
/*
* Copyright (c) 2015 Marko Kreen <markokr@gmail.com>
* Copyright (c) 2016 Bob Beck <beck@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <openssl/err.h>
#include <openssl/ocsp.h>
#include <openssl/x509.h>
#include <tls.h>
#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 ((*gt_time = timegm(&tm)) == -1)
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_error(ctx, "calloc");
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_error(ctx,
"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_error(ctx,
"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_error(ctx,
"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)
{
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, 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,
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, "no peer certificate for OCSP");
goto err;
}
ocsp_urls = X509_get1_ocsp(ocsp->main_cert);
if (ocsp_urls == NULL) {
tls_set_errorx(ctx, "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, "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, "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, "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, "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, "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, "ocsp verify failed: no result for cert");
goto err;
}
if (OCSP_check_validity(thisupd, nextupd, JITTER_SEC,
MAXAGE_SEC) != 1) {
tls_set_errorx(ctx,
"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, "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_error(ctx, "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, "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);
}

101
compat/libtls/tls_peer.c Normal file
View File

@ -0,0 +1,101 @@
/* $OpenBSD: tls_peer.c,v 1.8 2017/04/10 17:11:13 jsing Exp $ */
/*
* Copyright (c) 2015 Joel Sing <jsing@openbsd.org>
* Copyright (c) 2015 Bob Beck <beck@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <stdio.h>
#include <openssl/x509.h>
#include <tls.h>
#include "tls_internal.h"
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);
}

471
compat/libtls/tls_server.c Normal file
View File

@ -0,0 +1,471 @@
/* $OpenBSD: tls_server.c,v 1.49 2023/05/14 07:26:25 op Exp $ */
/*
* Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <openssl/asn1.h>
#include <openssl/ec.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <tls.h>
#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_NOACK);
}
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, "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, "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, "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, "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, "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, "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_error(ctx, "failed to set servername callback");
goto err;
}
if (SSL_CTX_set_tlsext_servername_arg(*ssl_ctx, ctx) != 1) {
tls_set_error(ctx, "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, "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, "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_error(ctx,
"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_error(ctx, "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, "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, "not a server context");
goto err;
}
if ((conn_ctx = tls_server_conn(ctx)) == NULL) {
tls_set_errorx(ctx, "connection context failure");
goto err;
}
if ((conn_ctx->ssl_conn = SSL_new(ctx->ssl_ctx)) == NULL) {
tls_set_errorx(ctx, "ssl failure");
goto err;
}
if (SSL_set_app_data(conn_ctx->ssl_conn, conn_ctx) != 1) {
tls_set_errorx(ctx, "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, "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, "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);
}

443
compat/libtls/tls_signer.c Normal file
View File

@ -0,0 +1,443 @@
/* $OpenBSD: tls_signer.c,v 1.9 2023/06/18 19:12:58 tb Exp $ */
/*
* Copyright (c) 2021 Eric Faurot <eric@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <limits.h>
#include <openssl/ecdsa.h>
#include <openssl/err.h>
#include <openssl/rsa.h>
#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 ((bio = BIO_new_mem_buf(cert, cert_len)) == NULL) {
tls_error_setx(&signer->error,
"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, "failed to load certificate: %s",
errstr);
goto err;
}
if (tls_cert_pubkey_hash(x509, &hash) == -1) {
tls_error_setx(&signer->error,
"failed to get certificate hash");
goto err;
}
X509_free(x509);
x509 = NULL;
BIO_free(bio);
bio = NULL;
/* Read private key */
if ((bio = BIO_new_mem_buf(key, key_len)) == NULL) {
tls_error_setx(&signer->error, "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, "failed to read private key");
goto err;
}
if ((skey = calloc(1, sizeof(*skey))) == NULL) {
tls_error_set(&signer->error, "failed to create key entry");
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, "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, "invalid RSA padding type (%d)",
padding_type);
return (-1);
}
if (input_len > INT_MAX) {
tls_error_setx(&signer->error, "input too large");
return (-1);
}
if ((rsa_size = RSA_size(skey->rsa)) <= 0) {
tls_error_setx(&signer->error, "invalid RSA size: %d",
rsa_size);
return (-1);
}
if ((signature = calloc(1, rsa_size)) == NULL) {
tls_error_set(&signer->error, "RSA signature");
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, "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, "invalid ECDSA padding");
return (-1);
}
if (input_len > INT_MAX) {
tls_error_setx(&signer->error, "digest too large");
return (-1);
}
if ((signature_len = ECDSA_size(skey->ecdsa)) <= 0) {
tls_error_setx(&signer->error, "invalid ECDSA size: %d",
signature_len);
return (-1);
}
if ((signature = calloc(1, signature_len)) == NULL) {
tls_error_set(&signer->error, "ECDSA signature");
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, "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, "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, "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);
}

228
compat/libtls/tls_util.c Normal file
View File

@ -0,0 +1,228 @@
/* $OpenBSD: tls_util.c,v 1.16 2023/05/14 07:26:25 op Exp $ */
/*
* Copyright (c) 2014 Joel Sing <jsing@openbsd.org>
* Copyright (c) 2014 Ted Unangst <tedu@openbsd.org>
* Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#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)) == -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);
}

333
compat/libtls/tls_verify.c Normal file
View File

@ -0,0 +1,333 @@
/* $OpenBSD: tls_verify.c,v 1.29 2023/11/22 18:23:09 op Exp $ */
/*
* Copyright (c) 2014 Jeremie Courreges-Anglas <jca@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <openssl/x509v3.h>
#include <tls.h>
#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] == '.')
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, "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,
"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,
"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,
"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_check_common_name(struct tls *ctx, X509 *cert, const char *name,
int *cn_match)
{
unsigned char *utf8_bytes = NULL;
X509_NAME *subject_name;
char *common_name = NULL;
union tls_addr addrbuf;
int common_name_len;
ASN1_STRING *data;
int lastpos = -1;
int rv = -1;
*cn_match = 0;
subject_name = X509_get_subject_name(cert);
if (subject_name == NULL)
goto done;
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.
*/
tls_set_errorx(ctx, "error verifying name '%s': "
"Certificate subject contains multiple Common Name fields, "
"probably a malicious or malformed certificate", name);
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) {
tls_set_errorx(ctx, "error verifying name '%s': "
"Common Name field cannot be encoded as a UTF-8 string, "
"probably a malicious certificate", name);
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) {
tls_set_errorx(ctx, "error verifying name '%s': "
"Common Name field has invalid length, "
"probably a malicious certificate", name);
goto err;
}
/*
* Fail if the resulting text contains a NUL byte.
*/
if (memchr(utf8_bytes, 0, common_name_len) != NULL) {
tls_set_errorx(ctx, "error verifying name '%s': "
"NUL byte in Common Name field, "
"probably a malicious certificate", name);
goto err;
}
common_name = strndup(utf8_bytes, common_name_len);
if (common_name == NULL) {
tls_set_error(ctx, "out of memory");
goto err;
}
/*
* 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(utf8_bytes);
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);
}

185
compat/memmem.c Normal file
View File

@ -0,0 +1,185 @@
/* $OpenBSD: memmem.c,v 1.5 2020/04/16 12:39:28 claudio Exp $ */
/*
* Copyright (c) 2005-2020 Rich Felker, et al.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "../config.h"
#include <string.h>
#include <stdint.h>
static char *
twobyte_memmem(const unsigned char *h, size_t k, const unsigned char *n)
{
uint16_t nw = n[0]<<8 | n[1], hw = h[0]<<8 | h[1];
for (h+=2, k-=2; k; k--, hw = hw<<8 | *h++)
if (hw == nw) return (char *)h-2;
return hw == nw ? (char *)h-2 : 0;
}
static char *
threebyte_memmem(const unsigned char *h, size_t k, const unsigned char *n)
{
uint32_t nw = n[0]<<24 | n[1]<<16 | n[2]<<8;
uint32_t hw = h[0]<<24 | h[1]<<16 | h[2]<<8;
for (h+=3, k-=3; k; k--, hw = (hw|*h++)<<8)
if (hw == nw) return (char *)h-3;
return hw == nw ? (char *)h-3 : 0;
}
static char *
fourbyte_memmem(const unsigned char *h, size_t k, const unsigned char *n)
{
uint32_t nw = n[0]<<24 | n[1]<<16 | n[2]<<8 | n[3];
uint32_t hw = h[0]<<24 | h[1]<<16 | h[2]<<8 | h[3];
for (h+=4, k-=4; k; k--, hw = hw<<8 | *h++)
if (hw == nw) return (char *)h-4;
return hw == nw ? (char *)h-4 : 0;
}
#define MAX(a,b) ((a)>(b)?(a):(b))
#define MIN(a,b) ((a)<(b)?(a):(b))
#define BITOP(a,b,op) \
((a)[(size_t)(b)/(8*sizeof *(a))] op (size_t)1<<((size_t)(b)%(8*sizeof *(a))))
/*
* Maxime Crochemore and Dominique Perrin, Two-way string-matching,
* Journal of the ACM, 38(3):651-675, July 1991.
*/
static char *
twoway_memmem(const unsigned char *h, const unsigned char *z,
const unsigned char *n, size_t l)
{
size_t i, ip, jp, k, p, ms, p0, mem, mem0;
size_t byteset[32 / sizeof(size_t)] = { 0 };
size_t shift[256];
/* Computing length of needle and fill shift table */
for (i=0; i<l; i++)
BITOP(byteset, n[i], |=), shift[n[i]] = i+1;
/* Compute maximal suffix */
ip = -1; jp = 0; k = p = 1;
while (jp+k<l) {
if (n[ip+k] == n[jp+k]) {
if (k == p) {
jp += p;
k = 1;
} else k++;
} else if (n[ip+k] > n[jp+k]) {
jp += k;
k = 1;
p = jp - ip;
} else {
ip = jp++;
k = p = 1;
}
}
ms = ip;
p0 = p;
/* And with the opposite comparison */
ip = -1; jp = 0; k = p = 1;
while (jp+k<l) {
if (n[ip+k] == n[jp+k]) {
if (k == p) {
jp += p;
k = 1;
} else k++;
} else if (n[ip+k] < n[jp+k]) {
jp += k;
k = 1;
p = jp - ip;
} else {
ip = jp++;
k = p = 1;
}
}
if (ip+1 > ms+1) ms = ip;
else p = p0;
/* Periodic needle? */
if (memcmp(n, n+p, ms+1)) {
mem0 = 0;
p = MAX(ms, l-ms-1) + 1;
} else mem0 = l-p;
mem = 0;
/* Search loop */
for (;;) {
/* If remainder of haystack is shorter than needle, done */
if (z-h < l) return 0;
/* Check last byte first; advance by shift on mismatch */
if (BITOP(byteset, h[l-1], &)) {
k = l-shift[h[l-1]];
if (k) {
if (k < mem) k = mem;
h += k;
mem = 0;
continue;
}
} else {
h += l;
mem = 0;
continue;
}
/* Compare right half */
for (k=MAX(ms+1,mem); k<l && n[k] == h[k]; k++);
if (k < l) {
h += k-ms;
mem = 0;
continue;
}
/* Compare left half */
for (k=ms+1; k>mem && n[k-1] == h[k-1]; k--);
if (k <= mem) return (char *)h;
h += p;
mem = mem0;
}
}
void *
memmem(const void *h0, size_t k, const void *n0, size_t l)
{
const unsigned char *h = h0, *n = n0;
/* Return immediately on empty needle */
if (!l) return (void *)h;
/* Return immediately when needle is longer than haystack */
if (k<l) return 0;
/* Use faster algorithms for short needles */
h = memchr(h0, *n, k);
if (!h || l==1) return (void *)h;
k -= h - (const unsigned char *)h0;
if (k<l) return 0;
if (l==2) return twobyte_memmem(h, k, n);
if (l==3) return threebyte_memmem(h, k, n);
if (l==4) return fourbyte_memmem(h, k, n);
return twoway_memmem(h, h+k, n, l);
}

534
compat/queue.h Normal file
View File

@ -0,0 +1,534 @@
/* $OpenBSD: queue.h,v 1.45 2018/07/12 14:22:54 sashan Exp $ */
/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
/*
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)queue.h 8.5 (Berkeley) 8/20/94
*/
#ifndef _SYS_QUEUE_H_
#define _SYS_QUEUE_H_
/*
* This file defines five types of data structures: singly-linked lists,
* lists, simple queues, tail queues and XOR simple queues.
*
*
* A singly-linked list is headed by a single forward pointer. The elements
* are singly linked for minimum space and pointer manipulation overhead at
* the expense of O(n) removal for arbitrary elements. New elements can be
* added to the list after an existing element or at the head of the list.
* Elements being removed from the head of the list should use the explicit
* macro for this purpose for optimum efficiency. A singly-linked list may
* only be traversed in the forward direction. Singly-linked lists are ideal
* for applications with large datasets and few or no removals or for
* implementing a LIFO queue.
*
* A list is headed by a single forward pointer (or an array of forward
* pointers for a hash table header). The elements are doubly linked
* so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before
* or after an existing element or at the head of the list. A list
* may only be traversed in the forward direction.
*
* A simple queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are singly
* linked to save space, so elements can only be removed from the
* head of the list. New elements can be added to the list before or after
* an existing element, at the head of the list, or at the end of the
* list. A simple queue may only be traversed in the forward direction.
*
* A tail queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or
* after an existing element, at the head of the list, or at the end of
* the list. A tail queue may be traversed in either direction.
*
* An XOR simple queue is used in the same way as a regular simple queue.
* The difference is that the head structure also includes a "cookie" that
* is XOR'd with the queue pointer (first, last or next) to generate the
* real pointer value.
*
* For details on the use of these macros, see the queue(3) manual page.
*/
#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
#define _Q_INVALID ((void *)-1)
#define _Q_INVALIDATE(a) (a) = _Q_INVALID
#else
#define _Q_INVALIDATE(a)
#endif
/*
* Singly-linked List definitions.
*/
#define SLIST_HEAD(name, type) \
struct name { \
struct type *slh_first; /* first element */ \
}
#define SLIST_HEAD_INITIALIZER(head) \
{ NULL }
#define SLIST_ENTRY(type) \
struct { \
struct type *sle_next; /* next element */ \
}
/*
* Singly-linked List access methods.
*/
#define SLIST_FIRST(head) ((head)->slh_first)
#define SLIST_END(head) NULL
#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
#define SLIST_FOREACH(var, head, field) \
for((var) = SLIST_FIRST(head); \
(var) != SLIST_END(head); \
(var) = SLIST_NEXT(var, field))
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = SLIST_FIRST(head); \
(var) && ((tvar) = SLIST_NEXT(var, field), 1); \
(var) = (tvar))
/*
* Singly-linked List functions.
*/
#define SLIST_INIT(head) { \
SLIST_FIRST(head) = SLIST_END(head); \
}
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
(elm)->field.sle_next = (slistelm)->field.sle_next; \
(slistelm)->field.sle_next = (elm); \
} while (0)
#define SLIST_INSERT_HEAD(head, elm, field) do { \
(elm)->field.sle_next = (head)->slh_first; \
(head)->slh_first = (elm); \
} while (0)
#define SLIST_REMOVE_AFTER(elm, field) do { \
(elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
} while (0)
#define SLIST_REMOVE_HEAD(head, field) do { \
(head)->slh_first = (head)->slh_first->field.sle_next; \
} while (0)
#define SLIST_REMOVE(head, elm, type, field) do { \
if ((head)->slh_first == (elm)) { \
SLIST_REMOVE_HEAD((head), field); \
} else { \
struct type *curelm = (head)->slh_first; \
\
while (curelm->field.sle_next != (elm)) \
curelm = curelm->field.sle_next; \
curelm->field.sle_next = \
curelm->field.sle_next->field.sle_next; \
} \
_Q_INVALIDATE((elm)->field.sle_next); \
} while (0)
/*
* List definitions.
*/
#define LIST_HEAD(name, type) \
struct name { \
struct type *lh_first; /* first element */ \
}
#define LIST_HEAD_INITIALIZER(head) \
{ NULL }
#define LIST_ENTRY(type) \
struct { \
struct type *le_next; /* next element */ \
struct type **le_prev; /* address of previous next element */ \
}
/*
* List access methods.
*/
#define LIST_FIRST(head) ((head)->lh_first)
#define LIST_END(head) NULL
#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
#define LIST_FOREACH(var, head, field) \
for((var) = LIST_FIRST(head); \
(var)!= LIST_END(head); \
(var) = LIST_NEXT(var, field))
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = LIST_FIRST(head); \
(var) && ((tvar) = LIST_NEXT(var, field), 1); \
(var) = (tvar))
/*
* List functions.
*/
#define LIST_INIT(head) do { \
LIST_FIRST(head) = LIST_END(head); \
} while (0)
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
(listelm)->field.le_next->field.le_prev = \
&(elm)->field.le_next; \
(listelm)->field.le_next = (elm); \
(elm)->field.le_prev = &(listelm)->field.le_next; \
} while (0)
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.le_prev = (listelm)->field.le_prev; \
(elm)->field.le_next = (listelm); \
*(listelm)->field.le_prev = (elm); \
(listelm)->field.le_prev = &(elm)->field.le_next; \
} while (0)
#define LIST_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
(head)->lh_first = (elm); \
(elm)->field.le_prev = &(head)->lh_first; \
} while (0)
#define LIST_REMOVE(elm, field) do { \
if ((elm)->field.le_next != NULL) \
(elm)->field.le_next->field.le_prev = \
(elm)->field.le_prev; \
*(elm)->field.le_prev = (elm)->field.le_next; \
_Q_INVALIDATE((elm)->field.le_prev); \
_Q_INVALIDATE((elm)->field.le_next); \
} while (0)
#define LIST_REPLACE(elm, elm2, field) do { \
if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
(elm2)->field.le_next->field.le_prev = \
&(elm2)->field.le_next; \
(elm2)->field.le_prev = (elm)->field.le_prev; \
*(elm2)->field.le_prev = (elm2); \
_Q_INVALIDATE((elm)->field.le_prev); \
_Q_INVALIDATE((elm)->field.le_next); \
} while (0)
/*
* Simple queue definitions.
*/
#define SIMPLEQ_HEAD(name, type) \
struct name { \
struct type *sqh_first; /* first element */ \
struct type **sqh_last; /* addr of last next element */ \
}
#define SIMPLEQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).sqh_first }
#define SIMPLEQ_ENTRY(type) \
struct { \
struct type *sqe_next; /* next element */ \
}
/*
* Simple queue access methods.
*/
#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
#define SIMPLEQ_END(head) NULL
#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
#define SIMPLEQ_FOREACH(var, head, field) \
for((var) = SIMPLEQ_FIRST(head); \
(var) != SIMPLEQ_END(head); \
(var) = SIMPLEQ_NEXT(var, field))
#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = SIMPLEQ_FIRST(head); \
(var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \
(var) = (tvar))
/*
* Simple queue functions.
*/
#define SIMPLEQ_INIT(head) do { \
(head)->sqh_first = NULL; \
(head)->sqh_last = &(head)->sqh_first; \
} while (0)
#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
(head)->sqh_last = &(elm)->field.sqe_next; \
(head)->sqh_first = (elm); \
} while (0)
#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.sqe_next = NULL; \
*(head)->sqh_last = (elm); \
(head)->sqh_last = &(elm)->field.sqe_next; \
} while (0)
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
(head)->sqh_last = &(elm)->field.sqe_next; \
(listelm)->field.sqe_next = (elm); \
} while (0)
#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
(head)->sqh_last = &(head)->sqh_first; \
} while (0)
#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
== NULL) \
(head)->sqh_last = &(elm)->field.sqe_next; \
} while (0)
#define SIMPLEQ_CONCAT(head1, head2) do { \
if (!SIMPLEQ_EMPTY((head2))) { \
*(head1)->sqh_last = (head2)->sqh_first; \
(head1)->sqh_last = (head2)->sqh_last; \
SIMPLEQ_INIT((head2)); \
} \
} while (0)
/*
* XOR Simple queue definitions.
*/
#define XSIMPLEQ_HEAD(name, type) \
struct name { \
struct type *sqx_first; /* first element */ \
struct type **sqx_last; /* addr of last next element */ \
unsigned long sqx_cookie; \
}
#define XSIMPLEQ_ENTRY(type) \
struct { \
struct type *sqx_next; /* next element */ \
}
/*
* XOR Simple queue access methods.
*/
#define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \
(unsigned long)(ptr)))
#define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first))
#define XSIMPLEQ_END(head) NULL
#define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head))
#define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next))
#define XSIMPLEQ_FOREACH(var, head, field) \
for ((var) = XSIMPLEQ_FIRST(head); \
(var) != XSIMPLEQ_END(head); \
(var) = XSIMPLEQ_NEXT(head, var, field))
#define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = XSIMPLEQ_FIRST(head); \
(var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \
(var) = (tvar))
/*
* XOR Simple queue functions.
*/
#define XSIMPLEQ_INIT(head) do { \
arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \
(head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
} while (0)
#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.sqx_next = (head)->sqx_first) == \
XSIMPLEQ_XOR(head, NULL)) \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
(head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \
} while (0)
#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \
*(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
} while (0)
#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \
XSIMPLEQ_XOR(head, NULL)) \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
(listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \
} while (0)
#define XSIMPLEQ_REMOVE_HEAD(head, field) do { \
if (((head)->sqx_first = XSIMPLEQ_XOR(head, \
(head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \
(head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
} while (0)
#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \
(elm)->field.sqx_next)->field.sqx_next) \
== XSIMPLEQ_XOR(head, NULL)) \
(head)->sqx_last = \
XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
} while (0)
/*
* Tail queue definitions.
*/
#define TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* first element */ \
struct type **tqh_last; /* addr of last next element */ \
}
#define TAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).tqh_first }
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
}
/*
* Tail queue access methods.
*/
#define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAILQ_END(head) NULL
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define TAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->tqh_last))->tqh_last))
/* XXX */
#define TAILQ_PREV(elm, headname, field) \
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#define TAILQ_EMPTY(head) \
(TAILQ_FIRST(head) == TAILQ_END(head))
#define TAILQ_FOREACH(var, head, field) \
for((var) = TAILQ_FIRST(head); \
(var) != TAILQ_END(head); \
(var) = TAILQ_NEXT(var, field))
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = TAILQ_FIRST(head); \
(var) != TAILQ_END(head) && \
((tvar) = TAILQ_NEXT(var, field), 1); \
(var) = (tvar))
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
for((var) = TAILQ_LAST(head, headname); \
(var) != TAILQ_END(head); \
(var) = TAILQ_PREV(var, headname, field))
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
for ((var) = TAILQ_LAST(head, headname); \
(var) != TAILQ_END(head) && \
((tvar) = TAILQ_PREV(var, headname, field), 1); \
(var) = (tvar))
/*
* Tail queue functions.
*/
#define TAILQ_INIT(head) do { \
(head)->tqh_first = NULL; \
(head)->tqh_last = &(head)->tqh_first; \
} while (0)
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
(head)->tqh_first->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(head)->tqh_first = (elm); \
(elm)->field.tqe_prev = &(head)->tqh_first; \
} while (0)
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
(elm)->field.tqe_next = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
(head)->tqh_last = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
(elm)->field.tqe_next->field.tqe_prev = \
&(elm)->field.tqe_next; \
else \
(head)->tqh_last = &(elm)->field.tqe_next; \
(listelm)->field.tqe_next = (elm); \
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
} while (0)
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
(elm)->field.tqe_next = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
} while (0)
#define TAILQ_REMOVE(head, elm, field) do { \
if (((elm)->field.tqe_next) != NULL) \
(elm)->field.tqe_next->field.tqe_prev = \
(elm)->field.tqe_prev; \
else \
(head)->tqh_last = (elm)->field.tqe_prev; \
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
_Q_INVALIDATE((elm)->field.tqe_prev); \
_Q_INVALIDATE((elm)->field.tqe_next); \
} while (0)
#define TAILQ_REPLACE(head, elm, elm2, field) do { \
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
(elm2)->field.tqe_next->field.tqe_prev = \
&(elm2)->field.tqe_next; \
else \
(head)->tqh_last = &(elm2)->field.tqe_next; \
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
*(elm2)->field.tqe_prev = (elm2); \
_Q_INVALIDATE((elm)->field.tqe_prev); \
_Q_INVALIDATE((elm)->field.tqe_next); \
} while (0)
#define TAILQ_CONCAT(head1, head2, field) do { \
if (!TAILQ_EMPTY(head2)) { \
*(head1)->tqh_last = (head2)->tqh_first; \
(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
(head1)->tqh_last = (head2)->tqh_last; \
TAILQ_INIT((head2)); \
} \
} while (0)
#endif /* !_SYS_QUEUE_H_ */

40
compat/reallocarray.c Normal file
View File

@ -0,0 +1,40 @@
/* $OpenBSD: reallocarray.c,v 1.3 2015/09/13 08:31:47 guenther Exp $ */
/*
* Copyright (c) 2008 Otto Moerbeek <otto@drijf.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "../config.h"
#include <sys/types.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
/*
* This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
* if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
*/
#define MUL_NO_OVERFLOW ((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);
}

90
compat/recallocarray.c Normal file
View File

@ -0,0 +1,90 @@
/* $OpenBSD: recallocarray.c,v 1.2 2021/03/18 11:16:58 claudio Exp $ */
/*
* Copyright (c) 2008, 2017 Otto Moerbeek <otto@drijf.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "../config.h"
#include <errno.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
/*
* This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
* if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
*/
#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
/*
* Even though specified in POSIX, the PAGESIZE and PAGE_SIZE
* macros have very poor portability. Since we only use this
* to avoid free() overhead for small shrinking, simply pick
* an arbitrary number.
*/
#define getpagesize() (1UL << 12)
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;
}

54
compat/setproctitle.c Normal file
View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 2016 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF 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 "../config.h"
#include <sys/types.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#if HAVE_PR_SET_NAME
#include <sys/prctl.h>
void
setproctitle(const char *fmt, ...)
{
char title[16], name[16], *cp;
va_list ap;
int used;
va_start(ap, fmt);
vsnprintf(title, sizeof title, fmt, ap);
va_end(ap);
used = snprintf(name, sizeof name, "%s: %s", getprogname(), title);
if (used >= (int)sizeof name) {
cp = strrchr(name, ' ');
if (cp != NULL)
*cp = '\0';
}
prctl(PR_SET_NAME, name);
}
#else
void
setproctitle(const char *fmt, ...)
{
(void)fmt;
}
#endif

34
compat/setresgid.c Normal file
View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2004, 2005 Darren Tucker (dtucker at zip com au).
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "../config.h"
#include <sys/types.h>
#include <unistd.h>
int
setresgid(gid_t rgid, gid_t egid, gid_t sgid)
{
/* this is the only configuration tested */
if (rgid != egid || egid != sgid)
return -1;
if (setregid(rgid, egid) == -1)
return -1;
return 0;
}

62
compat/setresuid.c Normal file
View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2004, 2005 Darren Tucker (dtucker at zip com au).
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "../config.h"
#include <sys/types.h>
#include <errno.h>
#include <unistd.h>
int
setresuid(uid_t ruid, uid_t euid, uid_t suid)
{
uid_t ouid;
int ret = -1;
/* Allow only the tested configuration. */
if (ruid != euid || euid != suid) {
errno = ENOSYS;
return -1;
}
ouid = getuid();
if ((ret = setreuid(euid, euid)) == -1)
return -1;
/*
* When real, effective and saved uids are the same and we have
* changed uids, sanity check that we cannot restore the old uid.
*/
if (ruid == euid && euid == suid && ouid != ruid &&
setuid(ouid) != -1 && seteuid(ouid) != -1) {
errno = EINVAL;
return -1;
}
/*
* Finally, check that the real and effective uids are what we
* expect.
*/
if (getuid() != ruid || geteuid() != euid) {
errno = EACCES;
return -1;
}
return ret;
}

55
compat/strlcat.c Normal file
View File

@ -0,0 +1,55 @@
/*
* Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "../config.h"
#include <sys/types.h>
#include <string.h>
/*
* Appends src to string dst of size dsize (unlike strncat, dsize is the
* full size of dst, not space left). At most dsize-1 characters
* will be copied. Always NUL terminates (unless dsize <= strlen(dst)).
* Returns strlen(src) + MIN(dsize, strlen(initial dst)).
* If retval >= dsize, truncation occurred.
*/
size_t
strlcat(char *dst, const char *src, size_t dsize)
{
const char *odst = dst;
const char *osrc = src;
size_t n = dsize;
size_t dlen;
/* Find the end of dst and adjust bytes left but don't go past end. */
while (n-- != 0 && *dst != '\0')
dst++;
dlen = dst - odst;
n = dsize - dlen;
if (n-- == 0)
return(dlen + strlen(src));
while (*src != '\0') {
if (n != 0) {
*dst++ = *src;
n--;
}
src++;
}
*dst = '\0';
return(dlen + (src - osrc)); /* count does not include NUL */
}

50
compat/strlcpy.c Normal file
View File

@ -0,0 +1,50 @@
/*
* Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "../config.h"
#include <sys/types.h>
#include <string.h>
/*
* Copy string src to buffer dst of size dsize. At most dsize-1
* chars will be copied. Always NUL terminates (unless dsize == 0).
* Returns strlen(src); if retval >= dsize, truncation occurred.
*/
size_t
strlcpy(char *dst, const char *src, size_t dsize)
{
const char *osrc = src;
size_t nleft = dsize;
/* Copy as many bytes as will fit. */
if (nleft != 0) {
while (--nleft != 0) {
if ((*dst++ = *src++) == '\0')
break;
}
}
/* Not enough room in dst, add NUL and traverse rest of src. */
if (nleft == 0) {
if (dsize != 0)
*dst = '\0'; /* NUL-terminate dst */
while (*src++)
;
}
return(src - osrc - 1); /* count does not include NUL */
}

65
compat/strtonum.c Normal file
View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2004 Ted Unangst and Todd Miller
* All rights reserved.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "../config.h"
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#define INVALID 1
#define TOOSMALL 2
#define TOOLARGE 3
long long
strtonum(const char *numstr, long long minval, long long maxval,
const char **errstrp)
{
long long ll = 0;
int error = 0;
char *ep;
struct errval {
const char *errstr;
int err;
} ev[4] = {
{ NULL, 0 },
{ "invalid", EINVAL },
{ "too small", ERANGE },
{ "too large", ERANGE },
};
ev[0].err = errno;
errno = 0;
if (minval > maxval) {
error = INVALID;
} else {
ll = strtoll(numstr, &ep, 10);
if (numstr == ep || *ep != '\0')
error = INVALID;
else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval)
error = TOOSMALL;
else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval)
error = TOOLARGE;
}
if (errstrp != NULL)
*errstrp = ev[error].errstr;
errno = ev[error].err;
if (error)
ll = 0;
return (ll);
}

View File

@ -0,0 +1,48 @@
/* $OpenBSD: timingsafe_memcmp.c,v 1.2 2015/08/31 02:53:57 guenther Exp $ */
/*
* Copyright (c) 2014 Google Inc.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "../config.h"
#include <limits.h>
#include <string.h>
int
timingsafe_memcmp(const void *b1, const void *b2, size_t len)
{
const unsigned char *p1 = b1, *p2 = b2;
size_t i;
int res = 0, done = 0;
for (i = 0; i < len; i++) {
/* lt is -1 if p1[i] < p2[i]; else 0. */
int lt = (p1[i] - p2[i]) >> CHAR_BIT;
/* gt is -1 if p1[i] > p2[i]; else 0. */
int gt = (p2[i] - p1[i]) >> CHAR_BIT;
/* cmp is 1 if p1[i] > p2[i]; -1 if p1[i] < p2[i]; else 0. */
int cmp = lt - gt;
/* set res = cmp if !done. */
res |= cmp & ~done;
/* set done if p1[i] != p2[i]. */
done |= lt | gt;
}
return (res);
}

1012
compat/tree.h Normal file

File diff suppressed because it is too large Load Diff

48
compat/vasprintf.c Normal file
View File

@ -0,0 +1,48 @@
/* from mandoc, with tweaks */
/*
* Copyright (c) 2015 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* This fallback implementation is not efficient:
* It does the formatting twice.
* Short of fiddling with the unknown internals of the system's
* printf(3) or completely reimplementing printf(3), i can't think
* of another portable solution.
*/
#include "../config.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
int
vasprintf(char **ret, const char *format, va_list ap)
{
char buf[2];
va_list ap2;
int sz;
va_copy(ap2, ap);
sz = vsnprintf(buf, sizeof(buf), format, ap2);
va_end(ap2);
if (sz != -1 && (*ret = malloc(sz + 1)) != NULL) {
if (vsnprintf(*ret, sz + 1, format, ap) == sz)
return sz;
free(*ret);
}
*ret = NULL;
return -1;
}

274
compat/vis.c Normal file
View File

@ -0,0 +1,274 @@
/* $OpenBSD: vis.c,v 1.26 2022/05/04 18:57:50 deraadt Exp $ */
/*-
* Copyright (c) 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "../config.h"
#include <sys/types.h>
#include <errno.h>
#include <ctype.h>
#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include <vis.h>
static int
isoctal(int c)
{
u_char uc = c;
return uc >= '0' && uc <= '7';
}
static int
isvisible(int c, int flag)
{
int vis_sp = flag & VIS_SP;
int vis_tab = flag & VIS_TAB;
int vis_nl = flag & VIS_NL;
int vis_safe = flag & VIS_SAFE;
int vis_glob = flag & VIS_GLOB;
int vis_all = flag & VIS_ALL;
u_char uc = c;
if (c == '\\' || !vis_all) {
if ((u_int)c <= UCHAR_MAX && isascii(uc) &&
((c != '*' && c != '?' && c != '[' && c != '#') || !vis_glob) &&
isgraph(uc))
return 1;
if (!vis_sp && c == ' ')
return 1;
if (!vis_tab && c == '\t')
return 1;
if (!vis_nl && c == '\n')
return 1;
if (vis_safe && (c == '\b' || c == '\007' || c == '\r' || isgraph(uc)))
return 1;
}
return 0;
}
/*
* vis - visually encode characters
*/
char *
vis(char *dst, int c, int flag, int nextc)
{
int vis_dq = flag & VIS_DQ;
int vis_noslash = flag & VIS_NOSLASH;
int vis_cstyle = flag & VIS_CSTYLE;
int vis_octal = flag & VIS_OCTAL;
int vis_glob = flag & VIS_GLOB;
if (isvisible(c, flag)) {
if ((c == '"' && vis_dq) ||
(c == '\\' && !vis_noslash))
*dst++ = '\\';
*dst++ = c;
*dst = '\0';
return (dst);
}
if (vis_cstyle) {
switch (c) {
case '\n':
*dst++ = '\\';
*dst++ = 'n';
goto done;
case '\r':
*dst++ = '\\';
*dst++ = 'r';
goto done;
case '\b':
*dst++ = '\\';
*dst++ = 'b';
goto done;
case '\a':
*dst++ = '\\';
*dst++ = 'a';
goto done;
case '\v':
*dst++ = '\\';
*dst++ = 'v';
goto done;
case '\t':
*dst++ = '\\';
*dst++ = 't';
goto done;
case '\f':
*dst++ = '\\';
*dst++ = 'f';
goto done;
case ' ':
*dst++ = '\\';
*dst++ = 's';
goto done;
case '\0':
*dst++ = '\\';
*dst++ = '0';
if (isoctal(nextc)) {
*dst++ = '0';
*dst++ = '0';
}
goto done;
}
}
if (((c & 0177) == ' ') || vis_octal ||
(vis_glob && (c == '*' || c == '?' || c == '[' || c == '#'))) {
*dst++ = '\\';
*dst++ = ((u_char)c >> 6 & 07) + '0';
*dst++ = ((u_char)c >> 3 & 07) + '0';
*dst++ = ((u_char)c & 07) + '0';
goto done;
}
if (!vis_noslash)
*dst++ = '\\';
if (c & 0200) {
c &= 0177;
*dst++ = 'M';
}
if (iscntrl((u_char)c)) {
*dst++ = '^';
if (c == 0177)
*dst++ = '?';
else
*dst++ = c + '@';
} else {
*dst++ = '-';
*dst++ = c;
}
done:
*dst = '\0';
return (dst);
}
/*
* strvis, strnvis, strvisx - visually encode characters from src into dst
*
* Dst must be 4 times the size of src to account for possible
* expansion. The length of dst, not including the trailing NULL,
* is returned.
*
* Strnvis will write no more than siz-1 bytes (and will NULL terminate).
* The number of bytes needed to fully encode the string is returned.
*
* Strvisx encodes exactly len bytes from src into dst.
* This is useful for encoding a block of data.
*/
int
strvis(char *dst, const char *src, int flag)
{
char c;
char *start;
for (start = dst; (c = *src);)
dst = vis(dst, c, flag, *++src);
*dst = '\0';
return (dst - start);
}
int
strnvis(char *dst, const char *src, size_t siz, int flag)
{
int vis_dq = flag & VIS_DQ;
int vis_noslash = flag & VIS_NOSLASH;
char *start, *end;
char tbuf[5];
int c, i;
i = 0;
for (start = dst, end = start + siz - 1; (c = *src) && dst < end; ) {
if (isvisible(c, flag)) {
if ((c == '"' && vis_dq) ||
(c == '\\' && !vis_noslash)) {
/* need space for the extra '\\' */
if (dst + 1 >= end) {
i = 2;
break;
}
*dst++ = '\\';
}
i = 1;
*dst++ = c;
src++;
} else {
i = vis(tbuf, c, flag, *++src) - tbuf;
if (dst + i <= end) {
memcpy(dst, tbuf, i);
dst += i;
} else {
src--;
break;
}
}
}
if (siz > 0)
*dst = '\0';
if (dst + i > end) {
/* adjust return value for truncation */
while ((c = *src))
dst += vis(tbuf, c, flag, *++src) - tbuf;
}
return (dst - start);
}
int
stravis(char **outp, const char *src, int flag)
{
char *buf;
int len, serrno;
buf = reallocarray(NULL, 4, strlen(src) + 1);
if (buf == NULL)
return -1;
len = strvis(buf, src, flag);
serrno = errno;
*outp = realloc(buf, len + 1);
if (*outp == NULL) {
*outp = buf;
errno = serrno;
}
return (len);
}
int
strvisx(char *dst, const char *src, size_t len, int flag)
{
char c;
char *start;
for (start = dst; len > 1; len--) {
c = *src;
dst = vis(dst, c, flag, *++src);
}
if (len)
dst = vis(dst, *src, flag, '\0');
*dst = '\0';
return (dst - start);
}

85
compat/vis/vis.h Normal file
View File

@ -0,0 +1,85 @@
/* $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
*/
#ifndef _VIS_H_
#define _VIS_H_
/*
* to select alternate encoding format
*/
#define VIS_OCTAL 0x01 /* use octal \ddd format */
#define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropriate */
/*
* to alter set of characters encoded (default is to encode all
* non-graphic except space, tab, and newline).
*/
#define VIS_SP 0x04 /* also encode space */
#define VIS_TAB 0x08 /* also encode tab */
#define VIS_NL 0x10 /* also encode newline */
#define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL)
#define VIS_SAFE 0x20 /* only encode "unsafe" characters */
#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);
int strvisx(char *, const char *, size_t, int);
int strunvis(char *, const char *);
int unvis(char *, char, int *, int);
ssize_t strnunvis(char *, const char *, size_t);
#endif /* !_VIS_H_ */

799
config.c Normal file
View File

@ -0,0 +1,799 @@
/*
* Copyright (c) 2023 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "gmid.h"
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <string.h>
#include <syslog.h>
#include <openssl/pem.h>
#include "log.h"
#include "proc.h"
struct conf *
config_new(void)
{
struct conf *conf;
conf = xcalloc(1, sizeof(*conf));
TAILQ_INIT(&conf->fcgi);
TAILQ_INIT(&conf->hosts);
TAILQ_INIT(&conf->pkis);
TAILQ_INIT(&conf->addrs);
conf->protos = TLS_PROTOCOL_TLSv1_2 | TLS_PROTOCOL_TLSv1_3;
init_mime(&conf->mime);
conf->prefork = 3;
conf->log_syslog = 1;
conf->log_facility = LOG_DAEMON;
conf->log_format = LOG_FORMAT_LEGACY;
conf->use_privsep_crypto = 1;
return conf;
}
void
config_purge(struct conf *conf)
{
struct privsep *ps;
struct fcgi *f, *tf;
struct vhost *h, *th;
struct location *l, *tl;
struct proxy *p, *tp;
struct envlist *e, *te;
struct alist *a, *ta;
struct pki *pki, *tpki;
struct address *addr, *taddr;
int use_privsep_crypto, log_format;
ps = conf->ps;
use_privsep_crypto = conf->use_privsep_crypto;
log_format = conf->log_format;
free(conf->log_access);
free_mime(&conf->mime);
TAILQ_FOREACH_SAFE(f, &conf->fcgi, fcgi, tf) {
TAILQ_REMOVE(&conf->fcgi, f, fcgi);
free(f);
}
TAILQ_FOREACH_SAFE(h, &conf->hosts, vhosts, th) {
free(h->cert_path);
free(h->key_path);
free(h->ocsp_path);
free(h->cert);
free(h->key);
free(h->ocsp);
TAILQ_FOREACH_SAFE(addr, &h->addrs, addrs, taddr) {
TAILQ_REMOVE(&h->addrs, addr, addrs);
free(addr);
}
TAILQ_FOREACH_SAFE(l, &h->locations, locations, tl) {
TAILQ_REMOVE(&h->locations, l, locations);
if (l->dirfd != -1)
close(l->dirfd);
free(l->reqca_path);
X509_STORE_free(l->reqca);
TAILQ_FOREACH_SAFE(e, &l->params, envs, te) {
TAILQ_REMOVE(&l->params, e, envs);
free(e);
}
free(l);
}
TAILQ_FOREACH_SAFE(a, &h->aliases, aliases, ta) {
TAILQ_REMOVE(&h->aliases, a, aliases);
free(a);
}
TAILQ_FOREACH_SAFE(p, &h->proxies, proxies, tp) {
TAILQ_REMOVE(&h->proxies, p, proxies);
free(p->cert_path);
free(p->cert);
free(p->key_path);
free(p->key);
free(p->reqca_path);
X509_STORE_free(p->reqca);
free(p);
}
TAILQ_REMOVE(&conf->hosts, h, vhosts);
free(h);
}
TAILQ_FOREACH_SAFE(pki, &conf->pkis, pkis, tpki) {
TAILQ_REMOVE(&conf->pkis, pki, pkis);
free(pki->hash);
EVP_PKEY_free(pki->pkey);
free(pki);
}
TAILQ_FOREACH_SAFE(addr, &conf->addrs, addrs, taddr) {
TAILQ_REMOVE(&conf->addrs, addr, addrs);
if (addr->sock != -1) {
close(addr->sock);
event_del(&addr->evsock);
tls_free(addr->ctx);
}
free(addr);
}
memset(conf, 0, sizeof(*conf));
conf->ps = ps;
conf->use_privsep_crypto = use_privsep_crypto;
conf->protos = TLS_PROTOCOL_TLSv1_2 | TLS_PROTOCOL_TLSv1_3;
conf->log_syslog = 1;
conf->log_facility = LOG_DAEMON;
conf->log_format = log_format;
init_mime(&conf->mime);
TAILQ_INIT(&conf->fcgi);
TAILQ_INIT(&conf->hosts);
TAILQ_INIT(&conf->pkis);
}
static int
config_send_file(struct privsep *ps, enum privsep_procid id, int type,
int fd, void *data, size_t l)
{
int n, m, d;
n = -1;
proc_range(ps, id, &n, &m);
for (n = 0; n < m; ++n) {
d = -1;
if (fd != -1 && (d = dup(fd)) == -1)
fatal("dup %d", fd);
if (proc_compose_imsg(ps, id, n, type, -1, d, data, l)
== -1)
return -1;
}
if (fd != -1)
close(fd);
/* avoid fd rampage */
if (proc_flush_imsg(ps, id, -1) == -1) {
log_warn("%s: proc_fush_imsg", __func__);
return -1;
}
return 0;
}
static int
config_open_send(struct privsep *ps, enum privsep_procid id, int type,
const char *path)
{
int fd;
log_debug("sending %s", path);
if ((fd = open(path, O_RDONLY)) == -1)
fatal("can't open %s", path);
return config_send_file(ps, id, type, fd, NULL, 0);
}
static int
config_send_kp(struct privsep *ps, int cert_type, int key_type,
const char *cert, const char *key)
{
struct conf *conf = ps->ps_env;
int fd, d, key_target;
log_debug("sending %s", cert);
if ((fd = open(cert, O_RDONLY)) == -1)
fatal("can't open %s", cert);
if ((d = dup(fd)) == -1)
fatal("fd");
if (config_send_file(ps, PROC_SERVER, cert_type, fd, NULL, 0) == -1) {
close(d);
return -1;
}
if (conf->use_privsep_crypto &&
config_send_file(ps, PROC_CRYPTO, cert_type, d, NULL, 0) == -1)
return -1;
key_target = PROC_CRYPTO;
if (!conf->use_privsep_crypto)
key_target = PROC_SERVER;
if (config_open_send(ps, key_target, key_type, key) == -1)
return -1;
return 0;
}
static int
config_send_socks(struct conf *conf)
{
struct privsep *ps = conf->ps;
struct address *addr, a;
int sock, v;
TAILQ_FOREACH(addr, &conf->addrs, addrs) {
sock = socket(addr->ai_family, addr->ai_socktype,
addr->ai_protocol);
if (sock == -1) {
if (errno == EAFNOSUPPORT || errno == EPROTONOSUPPORT)
continue;
fatal("socket");
}
v = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v))
== -1)
fatal("setsockopt(SO_REUSEADDR)");
v = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &v, sizeof(v))
== -1)
fatal("setsockopt(SO_REUSEPORT)");
mark_nonblock(sock);
if (bind(sock, (struct sockaddr *)&addr->ss, addr->slen)
== -1)
fatal("bind");
if (listen(sock, 16) == -1)
fatal("listen");
memcpy(&a, addr, sizeof(a));
a.conf = NULL;
a.sock = -1;
memset(&a.evsock, 0, sizeof(a.evsock));
memset(&a.addrs, 0, sizeof(a.addrs));
if (config_send_file(ps, PROC_SERVER, IMSG_RECONF_SOCK, sock,
&a, sizeof(a)) == -1)
return -1;
}
return 0;
}
int
config_send(struct conf *conf)
{
struct privsep *ps = conf->ps;
struct etm *m;
struct fcgi *fcgi;
struct vhost *h;
struct location *l;
struct proxy *p;
struct envlist *e;
struct alist *a;
size_t i;
if (proc_compose(ps, PROC_SERVER, IMSG_RECONF_LOG_FMT,
&conf->log_format, sizeof(conf->log_format)) == -1)
return -1;
for (i = 0; i < conf->mime.len; ++i) {
m = &conf->mime.t[i];
if (proc_compose(ps, PROC_SERVER, IMSG_RECONF_MIME,
m, sizeof(*m)) == -1)
return -1;
}
if (proc_compose(ps, PROC_SERVER, IMSG_RECONF_PROTOS,
&conf->protos, sizeof(conf->protos)) == -1)
return -1;
if (config_send_socks(conf) == -1)
return -1;
TAILQ_FOREACH(fcgi, &conf->fcgi, fcgi) {
log_debug("sending fastcgi %s", fcgi->path);
if (proc_compose(ps, PROC_SERVER, IMSG_RECONF_FCGI,
fcgi, sizeof(*fcgi)) == -1)
return -1;
}
TAILQ_FOREACH(h, &conf->hosts, vhosts) {
struct vhost vcopy;
struct address *addr, acopy;
memcpy(&vcopy, h, sizeof(vcopy));
vcopy.cert_path = NULL;
vcopy.key_path = NULL;
vcopy.ocsp_path = NULL;
log_debug("sending host %s", h->domain);
if (proc_compose(ps, PROC_SERVER, IMSG_RECONF_HOST,
&vcopy, sizeof(vcopy)) == -1)
return -1;
if (config_send_kp(ps, IMSG_RECONF_CERT, IMSG_RECONF_KEY,
h->cert_path, h->key_path) == -1)
return -1;
if (h->ocsp_path != NULL) {
if (config_open_send(ps, PROC_SERVER, IMSG_RECONF_OCSP,
h->ocsp_path) == -1)
return -1;
}
TAILQ_FOREACH(addr, &h->addrs, addrs) {
memcpy(&acopy, addr, sizeof(acopy));
memset(&acopy.addrs, 0, sizeof(acopy.addrs));
if (proc_compose(ps, PROC_SERVER,
IMSG_RECONF_HOST_ADDR, &acopy, sizeof(acopy))
== -1)
return -1;
}
if (proc_flush_imsg(ps, PROC_SERVER, -1) == -1) {
log_warn("%s: proc_fush_imsg", __func__);
return -1;
}
TAILQ_FOREACH(l, &h->locations, locations) {
struct location lcopy;
int fd = -1;
memcpy(&lcopy, l, sizeof(lcopy));
lcopy.reqca_path = NULL;
lcopy.reqca = NULL;
lcopy.dirfd = -1;
memset(&lcopy.locations, 0, sizeof(lcopy.locations));
if (l->reqca_path != NULL &&
(fd = open(l->reqca_path, O_RDONLY)) == -1)
fatal("can't open %s", l->reqca_path);
if (config_send_file(ps, PROC_SERVER, IMSG_RECONF_LOC,
fd, &lcopy, sizeof(lcopy)) == -1)
return -1;
TAILQ_FOREACH(e, &l->params, envs) {
if (proc_compose(ps, PROC_SERVER,
IMSG_RECONF_ENV, e, sizeof(*e)) == -1)
return -1;
}
}
if (proc_flush_imsg(ps, PROC_SERVER, -1) == -1)
return -1;
TAILQ_FOREACH(a, &h->aliases, aliases) {
if (proc_compose(ps, PROC_SERVER, IMSG_RECONF_ALIAS,
a, sizeof(*a)) == -1)
return -1;
}
if (proc_flush_imsg(ps, PROC_SERVER, -1) == -1)
return -1;
TAILQ_FOREACH(p, &h->proxies, proxies) {
struct proxy pcopy;
int fd = -1;
memcpy(&pcopy, p, sizeof(pcopy));
pcopy.cert_path = NULL;
pcopy.cert = NULL;
pcopy.certlen = 0;
pcopy.key_path = NULL;
pcopy.key = NULL;
pcopy.keylen = 0;
pcopy.reqca_path = NULL;
pcopy.reqca = NULL;
if (p->reqca_path != NULL) {
fd = open(p->reqca_path, O_RDONLY);
if (fd == -1)
fatal("can't open %s", p->reqca_path);
}
if (config_send_file(ps, PROC_SERVER, IMSG_RECONF_PROXY,
fd, &pcopy, sizeof(pcopy)) == -1)
return -1;
if (p->cert_path == NULL || p->key_path == NULL)
continue;
if (config_open_send(ps, PROC_SERVER,
IMSG_RECONF_PROXY_CERT, p->cert_path) == -1 ||
config_open_send(ps, PROC_SERVER,
IMSG_RECONF_PROXY_KEY, p->key_path) == -1)
return -1;
}
}
return 0;
}
static int
load_file(int fd, uint8_t **data, size_t *len)
{
struct stat sb;
ssize_t r;
if (fstat(fd, &sb) == -1)
fatal("fstat");
if (sb.st_size < 0 /* || sb.st_size > SIZE_MAX */) {
log_warnx("file too large");
close(fd);
return -1;
}
*len = sb.st_size;
if ((*data = malloc(*len)) == NULL)
fatal("malloc");
r = pread(fd, *data, *len, 0);
if (r == -1 || (size_t)r != *len) {
log_warn("read failed");
close(fd);
free(*data);
return -1;
}
close(fd);
return 0;
}
static int
config_crypto_recv_kp(struct conf *conf, struct imsg *imsg)
{
static struct pki *pki;
uint8_t *d;
size_t len;
int fd;
/* XXX: check for duplicates */
if ((fd = imsg_get_fd(imsg)) == -1)
fatalx("%s: no fd for imsg %d", __func__, imsg_get_type(imsg));
switch (imsg_get_type(imsg)) {
case IMSG_RECONF_CERT:
if (pki != NULL)
fatalx("imsg in wrong order; pki is not NULL");
if ((pki = calloc(1, sizeof(*pki))) == NULL)
fatal("calloc");
if (load_file(fd, &d, &len) == -1)
fatalx("can't load file");
if ((pki->hash = ssl_pubkey_hash(d, len)) == NULL)
fatalx("failed to compute cert hash");
free(d);
TAILQ_INSERT_TAIL(&conf->pkis, pki, pkis);
break;
case IMSG_RECONF_KEY:
if (pki == NULL)
fatalx("%s: RECONF_KEY: got key without cert",
__func__);
if (load_file(fd, &d, &len) == -1)
fatalx("failed to load private key");
if ((pki->pkey = ssl_load_pkey(d, len)) == NULL)
fatalx("failed load private key");
free(d);
pki = NULL;
break;
default:
return -1;
}
return 0;
}
int
config_recv(struct conf *conf, struct imsg *imsg)
{
static struct vhost *h;
static struct location *l;
static struct proxy *p;
struct privsep *ps = conf->ps;
struct etm m;
struct fcgi *fcgi;
struct vhost *vh, vht;
struct location *loc;
struct envlist *env;
struct alist *alias;
struct proxy *proxy;
struct address *addr;
uint8_t *d;
size_t len;
int fd;
switch (imsg_get_type(imsg)) {
case IMSG_RECONF_START:
config_purge(conf);
h = NULL;
p = NULL;
break;
case IMSG_RECONF_LOG_FMT:
if (imsg_get_data(imsg, &conf->log_format,
sizeof(conf->log_format)) == -1)
fatalx("bad length imsg LOG_FMT");
break;
case IMSG_RECONF_MIME:
if (imsg_get_data(imsg, &m, sizeof(m)) == -1)
fatalx("bad length imsg RECONF_MIME");
if (m.mime[sizeof(m.mime) - 1] != '\0' ||
m.ext[sizeof(m.ext) - 1] != '\0')
fatal("received corrupted IMSG_RECONF_MIME");
if (add_mime(&conf->mime, m.mime, m.ext) == -1)
fatal("failed to add mime mapping %s -> %s",
m.mime, m.ext);
break;
case IMSG_RECONF_PROTOS:
if (imsg_get_data(imsg, &conf->protos, sizeof(conf->protos))
== -1)
fatalx("bad length imsg RECONF_PROTOS");
break;
case IMSG_RECONF_SOCK:
addr = xcalloc(1, sizeof(*addr));
if (imsg_get_data(imsg, addr, sizeof(*addr)) == -1)
fatalx("bad length imsg RECONF_SOCK");
if ((fd = imsg_get_fd(imsg)) == -1)
fatalx("missing socket for IMSG_RECONF_SOCK");
addr->conf = conf;
addr->sock = fd;
event_set(&addr->evsock, addr->sock, EV_READ|EV_PERSIST,
server_accept, addr);
if ((addr->ctx = tls_server()) == NULL)
fatal("tls_server failure");
TAILQ_INSERT_HEAD(&conf->addrs, addr, addrs);
break;
case IMSG_RECONF_FCGI:
fcgi = xcalloc(1, sizeof(*fcgi));
if (imsg_get_data(imsg, fcgi, sizeof(*fcgi)) == -1)
fatalx("bad length imsg RECONF_FCGI");
log_debug("received fcgi %s", fcgi->path);
TAILQ_INSERT_TAIL(&conf->fcgi, fcgi, fcgi);
break;
case IMSG_RECONF_HOST:
if (imsg_get_data(imsg, &vht, sizeof(vht)) == -1)
fatalx("bad length imsg RECONF_HOST");
vh = new_vhost();
strlcpy(vh->domain, vht.domain, sizeof(vh->domain));
h = vh;
TAILQ_INSERT_TAIL(&conf->hosts, h, vhosts);
/* reset location and proxy */
l = NULL;
p = NULL;
break;
case IMSG_RECONF_CERT:
log_debug("receiving cert");
if (privsep_process == PROC_CRYPTO)
return config_crypto_recv_kp(conf, imsg);
if (h == NULL)
fatalx("recv'd cert without host");
if (h->cert != NULL)
fatalx("cert already received");
if ((fd = imsg_get_fd(imsg)) == -1)
fatalx("no fd for IMSG_RECONF_CERT");
if (load_file(fd, &h->cert, &h->certlen) == -1)
fatalx("failed to load cert for %s",
h->domain);
break;
case IMSG_RECONF_KEY:
log_debug("receiving key");
if (privsep_process == PROC_CRYPTO)
return config_crypto_recv_kp(conf, imsg);
if (h == NULL)
fatalx("recv'd key without host");
if (h->key != NULL)
fatalx("key already received");
if ((fd = imsg_get_fd(imsg)) == -1)
fatalx("no fd for IMSG_RECONF_KEY");
if (load_file(fd, &h->key, &h->keylen) == -1)
fatalx("failed to load key for %s",
h->domain);
break;
case IMSG_RECONF_OCSP:
log_debug("receiving ocsp");
if (h == NULL)
fatalx("recv'd ocsp without host");
if (h->ocsp != NULL)
fatalx("ocsp already received");
if ((fd = imsg_get_fd(imsg)) == -1)
fatalx("no fd for IMSG_RECONF_OCSP");
if (load_file(fd, &h->ocsp, &h->ocsplen) == -1)
fatalx("failed to load ocsp for %s",
h->domain);
break;
case IMSG_RECONF_HOST_ADDR:
log_debug("receiving host addr");
if (h == NULL)
fatalx("recv'd host address withouth host");
addr = xcalloc(1, sizeof(*addr));
if (imsg_get_data(imsg, addr, sizeof(*addr)) == -1)
fatalx("bad length imsg RECONF_HOST_ADDR");
TAILQ_INSERT_TAIL(&h->addrs, addr, addrs);
break;
case IMSG_RECONF_LOC:
if (h == NULL)
fatalx("recv'd location without host");
loc = xcalloc(1, sizeof(*loc));
if (imsg_get_data(imsg, loc, sizeof(*loc)) == -1)
fatalx("bad length imsg RECONF_LOC");
TAILQ_INIT(&loc->params);
if ((fd = imsg_get_fd(imsg)) != -1) {
if (load_file(fd, &d, &len) == -1)
fatal("load_file");
loc->reqca = load_ca(d, len);
if (loc->reqca == NULL)
fatalx("failed to load CA");
free(d);
}
l = loc;
TAILQ_INSERT_TAIL(&h->locations, loc, locations);
break;
case IMSG_RECONF_ENV:
if (l == NULL)
fatalx("recv'd env without location");
env = xcalloc(1, sizeof(*env));
if (imsg_get_data(imsg, env, sizeof(*env)) == -1)
fatalx("bad length imsg RECONF_ENV");
TAILQ_INSERT_TAIL(&l->params, env, envs);
break;
case IMSG_RECONF_ALIAS:
if (h == NULL)
fatalx("recv'd alias without host");
alias = xcalloc(1, sizeof(*alias));
if (imsg_get_data(imsg, alias, sizeof(*alias)) == -1)
fatalx("bad length imsg RECONF_ALIAS");
TAILQ_INSERT_TAIL(&h->aliases, alias, aliases);
break;
case IMSG_RECONF_PROXY:
log_debug("receiving proxy");
if (h == NULL)
fatalx("recv'd proxy without host");
proxy = xcalloc(1, sizeof(*proxy));
if (imsg_get_data(imsg, proxy, sizeof(*proxy)) == -1)
fatalx("bad length imsg RECONF_PROXY");
if ((fd = imsg_get_fd(imsg)) != -1) {
if (load_file(fd, &d, &len) == -1)
fatal("load_file");
proxy->reqca = load_ca(d, len);
if (proxy->reqca == NULL)
fatal("failed to load CA");
free(d);
}
TAILQ_INSERT_TAIL(&h->proxies, proxy, proxies);
p = proxy;
break;
case IMSG_RECONF_PROXY_CERT:
log_debug("receiving proxy cert");
if (p == NULL)
fatalx("recv'd proxy cert without proxy");
if (p->cert != NULL)
fatalx("proxy cert already received");
if ((fd = imsg_get_fd(imsg)) == -1)
fatalx("no fd for IMSG_RECONF_PROXY_CERT");
if (load_file(fd, &p->cert, &p->certlen) == -1)
fatalx("failed to load cert for proxy %s of %s",
p->host, h->domain);
break;
case IMSG_RECONF_PROXY_KEY:
log_debug("receiving proxy key");
if (p == NULL)
fatalx("recv'd proxy key without proxy");
if (p->key != NULL)
fatalx("proxy key already received");
if ((fd = imsg_get_fd(imsg)) == -1)
fatalx("no fd for IMSG_RECONF_PROXY_KEY");
if (load_file(fd, &p->key, &p->keylen) == -1)
fatalx("failed to load key for proxy %s of %s",
p->host, h->domain);
break;
case IMSG_RECONF_END:
if (proc_compose(ps, PROC_PARENT, IMSG_RECONF_DONE,
NULL, 0) == -1)
return -1;
break;
default:
return -1;
}
return 0;
}
int
config_test(struct conf *conf)
{
struct vhost *h;
struct address *addr;
int fd;
/*
* can't use config_crypto_recv_kp() because not on all platforms
* we're using the privsep crypto engine (yet).
*/
conf->use_privsep_crypto = 0;
TAILQ_FOREACH(h, &conf->hosts, vhosts) {
if ((fd = open(h->cert_path, O_RDONLY)) == -1) {
log_warn("can't open %s", h->cert_path);
return -1;
}
if (load_file(fd, &h->cert, &h->certlen) == -1) {
log_warnx("failed to load cert for %s",
h->domain);
return -1;
}
if ((fd = open(h->key_path, O_RDONLY)) == -1) {
log_warn("can't open %s", h->key_path);
return -1;
}
if (load_file(fd, &h->key, &h->keylen) == -1) {
log_warnx("failed to load key for %s",
h->domain);
return -1;
}
}
TAILQ_FOREACH(addr, &conf->addrs, addrs) {
if ((addr->ctx = tls_server()) == NULL)
fatal("tls_server failed");
addr->sock = -1;
}
if (server_configure_done(conf))
return -1;
return 0;
}

691
configure vendored Executable file
View File

@ -0,0 +1,691 @@
#!/bin/sh
#
# Copyright (c) 2021, 2022, 2023 Omar Polo <op@omarpolo.com>
# Copyright (c) 2011, 2013-2022 Ingo Schwarze <schwarze@openbsd.org>
# Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
#
# 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.
set -e
RELEASE=no
VERSION=2.0.2-current
usage()
{
echo "usage: $0 [--help] [--prefix=prefix] [OPTION=VALUE...]" >&2
exit 1
}
if command -v yacc 2>/dev/null >&2; then
YACC=yacc
elif command -v bison 2>/dev/null >&2; then
YACC=bison
else
# assume yacc by default. Make will fail building parse.y if
# not from a release tarball, but at least it'll have a decent
# error message.
YACC=yacc
fi
pkgconfig=
CC=${CC:-cc}
if [ "$RELEASE" = no ]; then
CFLAGS=${CFLAGS:--O0 -g3}
else
CFLAGS=${CFLAGS:--O2 -pipe}
fi
INSTALL=${INSTALL-install}
PREFIX=${PREFIX-/usr/local}
SYSCONFDIR=${SYSCONFDIR-/etc}
CDIAGFLAGS=
CDIAGFLAGS="${CDIAGFLAGS} -W -Wall -Wextra -Wpointer-arith -Wuninitialized"
CDIAGFLAGS="${CDIAGFLAGS} -Wstrict-prototypes -Wmissing-prototypes -Wunused"
CDIAGFLAGS="${CDIAGFLAGS} -Wsign-compare -Wno-unused-parameter" # -Wshadow
CDIAGFLAGS="${CDIAGFLAGS} -Wno-missing-field-initializers"
CDIAGFLAGS="${CDIAGFLAGS} -Wno-pointer-sign"
# On all OSes except OpenBSD use the bundled one. It may crash at
# runtime otherwise since we depend on the libtls internals for the
# privsep crypto engine.
# See <https://codeberg.org/op/gmid/issues/2>.
LIBTLS=bundled # or system
if [ "$(uname || true)" = OpenBSD ]; then
LIBTLS=system
fi
while [ $# -gt 0 ]; do
key="${1%%=*}"
val="${1#*=}"
if [ "$key" = --help ]; then
usage
fi
if [ "$key" = -Werror ]; then
CDIAGFLAGS="$CDIAGFLAGS -Werror"
shift
continue
fi
if [ "$key" = --enable-sandbox ]; then
key=DISABLE_SANDBOX
val=0
fi
if [ "$key" = --disable-sandbox ]; then
key=DISABLE_SANDBOX
val=1
fi
if [ "$key" = "$1" ]; then
# if no --xy=, look at the next arg
if ! shift 2>/dev/null; then
echo "$0: missing value for $key" >&2
exit 1
fi
val="$1"
fi
case "$key" in
--bindir) key=BINDIR ;;
--mandir) key=MANDIR ;;
--prefix) key=PREFIX ;;
--sysconfdir) key=SYSCONFDIR ;;
--with-libtls) key=LIBTLS ;;
esac
case "$key" in
LIBTLS)
case "$val" in
bundled) LIBTLS=bundled ;;
system) LIBTLS=system ;;
*) usage ;;
esac
;;
BINDIR) BINDIR="$val" ;;
CC) CC="$val" ;;
CFLAGS) CFLAGS="$val" ;;
CDIAGFLAGS) CDIAGFLAGS="$val" ;;
DISABLE_SANDBOX) DISABLE_SANDBOX="$val" ;;
INSTALL) INSTALL="$val" ;;
LDFLAGS) LDFLAGS="$val" ;;
MANDIR) MANDIR="$val" ;;
PKG_CONFIG) PKG_CONFIG="$val" ;;
PREFIX) PREFIX="$val" ;;
SYSCONFDIR) SYSCONFDIR="$val" ;;
YACC) YACC="$val" ;;
*) usage
esac
shift
done
[ -w config.log ] && mv config.log config.log.old
[ -w config.h ] && mv config.h config.h.old
exec 3> config.log
echo "file config.log: writing..."
NEED_GNU_SOURCE=0
NEED_OPENBSD_SOURCE=0
NEED_LIBBSD_OPENBSD_VIS=0
COMPATS=
COMP="${CC} ${CFLAGS} -Werror=implicit-function-declaration"
# singletest name var extra-cflags extra-libs msg
singletest() {
msg="$5"
if [ -z "$msg" ]; then
if [ -n "$3" ]; then
msg=" ($3)"
elif [ -n "$4" ]; then
msg=" ($4)"
fi
elif [ "$msg" = no ]; then
msg=""
fi
cat >&3 <<EOF
${1}: testing...
$COMP have/${1}.c $3 -o test-$1 $LDFLAGS $4
EOF
if $COMP have/${1}.c $3 -o test-$1 $LDFLAGS $4 >&3 2>&3; then
rm -f test-${1} test-${1}.d
echo "${1}: $CC$msg succeeded" >&3
echo "${1}$msg: yes"
echo >&3
return 0
fi
echo "${1}: $CC$msg failed $?" >&3
echo "${1}$msg: no"
echo >&3
return 1
}
# deptest name var
deptest() {
if singletest "$1" "$2" "${CFLAGS}" "${LIBS}" no; then
eval HAVE_${2}=1
return 0
fi
if [ -f compat/${1}.c ]; then
COMPATS="compat/$1.c $COMPATS"
fi
eval HAVE_${2}=0
return 1
}
# runtest name var extra-cflags extra-libs pkgconfig-name
runtest() {
if singletest "$1" "$2" "" ""; then
eval HAVE_${2}=1
return 0
fi
if [ -n "$3" -o -n "$4" ]; then
echo "retrying with ${3+$3 }$4" >&3
if singletest "$1" "$2" "$3" "$4"; then
eval HAVE_${2}=1
if [ "$3" = -D_GNU_SOURCE ]; then
NEED_GNU_SOURCE=1
return 0
fi
if [ "$4" = -D_OPENBSD_SOURCE ]; then
NEED_OPENBSD_SOURCE=1
return 0
fi
if [ "$4" = -DLIBBSD_OPENBSD_VIS ]; then
NEED_LIBBSD_OPENBSD_VIS=1
return 0
fi
if [ -n "$3" ]; then
CFLAGS="$CFLAGS $3"
fi
if [ -n "$4" ]; then
LIBS="$LIBS $4"
fi
return 0
fi
fi
if [ -n "$5" -a -n "$pkgconfig" ]; then
if $pkgconfig $5; then
cflags="$($pkgconfig --cflags $5)"
ldflags="$($pkgconfig --libs $5)"
echo "retrying with pkg-config" >&3
if singletest "$1" "$2" "$cflags" "$ldflags"; then
CFLAGS="$CFLAGS $cflags"
LIBS="$LIBS $ldflags"
eval HAVE_$2=1
return 0
fi
fi
fi
if [ -f compat/$1.c ]; then
COMPATS="compat/$1.c $COMPATS"
fi
eval HAVE_$2=0
return 1
}
if [ "$PKG_CONFIG" = no ]; then
echo "pkg-config: disabled"
elif [ -n "$PKG_CONFIG" ]; then
pkgconfig="$PKG_CONFIG"
echo "pkg-config: (manual) $PKG_CONFIG"
elif command -v pkg-config 2>/dev/null >&2; then
pkgconfig="pkg-config"
echo "pkg-config: (auto) pkg-config"
else
echo "pkg-config: not found"
fi
if singletest noop MMD -MMD; then
CFLAGS="${CFLAGS} -MMD"
fi
if ! runtest wait_any WAIT_ANY; then
CFLAGS="${CFLAGS} -DWAIT_ANY=-1"
fi
HAVE_ENDIAN_H=0
HAVE_SYS_ENDIAN_H=0
HAVE_MACHINE_ENDIAN=0
runtest endian_h ENDIAN_H || \
runtest sys_endian_h SYS_ENDIAN_H || \
runtest machine_endian MACHINE_ENDIAN || true
if [ ${HAVE_ENDIAN_H} -eq 0 -a \
${HAVE_SYS_ENDIAN_H} -eq 0 -a \
${HAVE_MACHINE_ENDIAN} -eq 0 ]; then
echo "FATAL: no endian header found" 1>&2
echo "FATAL: no endian header found" 1>&3
exit 1
fi
runtest arc4random ARC4RANDOM || true
runtest arc4random_buf ARC4RANDOM_BUF || true
runtest err ERR || true
runtest explicit_bzero EXPLICIT_BZERO || true
runtest freezero FREEZERO || true
runtest getdtablecount GETDTABLECOUNT || true
runtest getdtablesize GETDTABLESIZE || true
runtest getprogname GETPROGNAME || true
runtest imsg IMSG "" -lutil libimsg || true
runtest landlock LANDLOCK || true
runtest libevent LIBEVENT "" -levent libevent_core|| true
runtest memmem MEMMEM -D_GNU_SOURCE || true
runtest openssl OPENSSL "" '-lcrypto -lssl' 'libcrypto libssl' || true
runtest pr_set_name PR_SET_NAME || true
runtest program_invocation_short_name PROGRAM_INVOCATION_SHORT_NAME -D_GNU_SOURCE || true
runtest queue_h QUEUE_H || true
runtest reallocarray REALLOCARRAY -D_OPENBSD_SOURCE || true
runtest recallocarray RECALLOCARRAY || true
runtest setproctitle SETPROCTITLE || true
runtest setresgid SETRESGID -D_GNU_SOURCE || true
runtest setresuid SETRESUID -D_GNU_SOURCE || true
runtest strlcat STRLCAT || true
runtest strlcpy STRLCPY || true
runtest strtonum STRTONUM -D_OPENBSD_SOURCE || true
runtest timingsafe_memcmp TIMINGSAFE_MEMCMP || true
runtest tree_h TREE_H || true
runtest vasprintf VASPRINTF -D_GNU_SOURCE || true
runtest vis VIS -DLIBBSD_OPENBSD_VIS || true
if [ ${HAVE_ARC4RANDOM} -eq 1 -a ${HAVE_ARC4RANDOM_BUF} -eq 0 ]; then
COMPATS="compat/arc4random.c ${COMPATS}"
fi
if [ ${HAVE_ARC4RANDOM} -eq 0 ]; then
runtest getentropy GETENTROPY || true
else
# fake it
HAVE_GETENTROPY=1
fi
if [ ${HAVE_ARC4RANDOM} -eq 0 -a ${HAVE_GETENTROPY} -eq 1 ]; then
COMPATS="compat/getentropy.c ${COMPATS}"
fi
if [ "${LIBTLS}" = system ]; then
runtest libtls LIBTLS "" -ltls libtls || true
# not actually needed
HAVE_ASN1_TIME_TM_CMP=1
HAVE_ASN1_TIME_TM_CLAMP_NOTAFTER=1
HAVE_ASN1_TIME_PARSE=1
HAVE_SSL_CTX_UCCM=1
HAVE_SSL_CTX_LVM=1
HAVE_X509_LOOKUP_MEM=1
else
# use bundled one
HAVE_LIBTLS=1
for f in compat/libtls/*.c; do
COMPATS="$f ${COMPATS}"
done
CFLAGS="-Icompat/libtls -I../compat/libtls ${CFLAGS}"
deptest ASN1_time_tm_cmp ASN1_TIME_TM_CMP || true
deptest ASN1_time_tm_clamp_notafter ASN1_TIME_TM_CLAMP_NOTAFTER || true
deptest ASN1_time_parse ASN1_TIME_PARSE || true
deptest SSL_CTX_use_certificate_chain_mem SSL_CTX_UCCM || true
deptest SSL_CTX_load_verify_mem SSL_CTX_LVM || true
deptest X509_LOOKUP_mem X509_LOOKUP_MEM || true
fi
deptest libevent2 LIBEVENT2 || true
if [ ${HAVE_LIBTLS} -eq 0 ]; then
echo "FATAL: openssl not found" 1>&2
echo "FATAL: openssl not found" 1>&3
exit 1
fi
if [ ${HAVE_OPENSSL} -eq 0 ]; then
echo "FATAL: openssl not found" 1>&2
echo "FATAL: openssl not found" 1>&3
exit 1
fi
if [ ${HAVE_LIBEVENT} -eq 0 ]; then
echo "FATAL: libevent not found" 1>&2
echo "FATAL: libevent not found" 1>&3
exit 1
fi
if [ ${HAVE_IMSG} -eq 0 ]; then
# compat/imsg.c is implicitly added
COMPATS="$COMPATS compat/imsg-buffer.c"
fi
if [ ${HAVE_QUEUE_H} -eq 0 -o ${HAVE_IMSG} -eq 0 -o ${HAVE_TREE_H} -eq 0 ]; then
CFLAGS="${CFLAGS} -I ${PWD}/compat"
fi
if [ ${HAVE_VIS} -eq 0 ]; then
CFLAGS="${CFLAGS} -I ${PWD}/compat/vis"
fi
if [ $HAVE_LIBEVENT2 -eq 1 ]; then
CFLAGS="$CFLAGS -DHAVE_LIBEVENT2=1"
fi
if [ $NEED_GNU_SOURCE = 1 ]; then
CFLAGS="$CFLAGS -D_GNU_SOURCE"
fi
if [ $NEED_OPENBSD_SOURCE = 1 ]; then
CFLAGS="$CFLAGS -D_OPENBSD_SOURCE"
fi
if [ $NEED_LIBBSD_OPENBSD_VIS = 1 ]; then
CFLAGS="$CFLAGS -DLIBBSD_OPENBSD_VIS"
fi
CFLAGS="-I. ${CFLAGS} ${CDIAGFLAGS}"
exec > config.h
echo "config.h: writing.." >&2
cat <<__HEREDOC__
#ifdef __cplusplus
#error "Do not use C++."
#endif
__HEREDOC__
[ ${HAVE_STRLCAT} -eq 0 -o ${HAVE_STRLCPY} -eq 0 -o ${HAVE_IMSG} -eq 0 ] \
&& echo "#include <sys/types.h>"
[ ${HAVE_VASPRINTF} -eq 0 ] && echo "#include <stdarg.h>"
if [ ${HAVE_QUEUE_H} -eq 1 ]; then
echo "#include <sys/queue.h>"
else
echo "#include <queue.h>"
fi
if [ ${HAVE_TREE_H} -eq 1 ]; then
echo "#include <sys/tree.h>"
else
echo "#include <tree.h>"
fi
echo "#include <sys/types.h>"
echo "#include <sys/uio.h>"
echo "#include <stdint.h>"
echo "#include <imsg.h>"
echo "#include <limits.h>"
cat <<__HEREDOC__
#define VERSION "${VERSION}"
#define DISABLE_SANDBOX ${DISABLE_SANDBOX}
#define HAVE_LANDLOCK ${HAVE_LANDLOCK}
#ifndef SYSCONFDIR
# define SYSCONFDIR "${SYSCONFDIR}"
#endif
__HEREDOC__
if [ ${HAVE_ENDIAN_H} -eq 1 ]; then
echo "#include <endian.h>"
elif [ ${HAVE_SYS_ENDIAN_H} -eq 1 ]; then
echo "#include <sys/endian.h>"
elif [ ${HAVE_MACHINE_ENDIAN} -eq 1 ]; then
cat <<__HEREDOC__
#include <machine/endian.h>
#include <libkern/OSByteOrder.h>
# 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)
__HEREDOC__
fi
[ ${HAVE_ARC4RANDOM_BUF} -eq 0 -o \
${HAVE_ASN1_TIME_PARSE} -eq 0 -o \
${HAVE_EXPLICIT_BZERO} -eq 0 -o \
${HAVE_FREEZERO} -eq 0 -o \
${HAVE_GETENTROPY} -eq 0 -o \
${HAVE_REALLOCARRAY} -eq 0 -o \
${HAVE_RECALLOCARRAY} -eq 0 -o \
${HAVE_STRLCAT} -eq 0 -o \
${HAVE_STRLCPY} -eq 0 -o \
${HAVE_STRTONUM} -eq 0 -o \
${HAVE_TIMINGSAFE_MEMCMP} -eq 0 ] && echo "#include <stddef.h>"
[ ${HAVE_ARC4RANDOM} -eq 0 ] && echo "#include <stdint.h>"
[ ${HAVE_SETRESGID} -eq 0 -o \
${HAVE_SETRESUID} -eq 0 ] && echo "#include <unistd.h>"
if [ ${HAVE_GETENTROPY} -eq 1 ]; then
echo "#define HAVE_GETENTROPY 1"
else
echo "#define WITH_OPENSSL 1"
echo "#define OPENSSL_PRNG_ONLY 1"
fi
if [ ${HAVE_ARC4RANDOM} -eq 0 ]; then
echo "extern uint32_t arc4random(void);"
else
echo "#define HAVE_ARC4RANDOM 1"
fi
if [ ${HAVE_ARC4RANDOM_BUF} -eq 0 ]; then
echo "extern void arc4random_buf(void *, size_t);"
else
echo "#define HAVE_ARC4RANDOM_BUF 1"
fi
if [ ${HAVE_ASN1_TIME_TM_CMP} -eq 0 ]; then
echo "struct tm;"
echo "extern int ASN1_time_tm_cmp(struct tm *, struct tm *);"
else
echo "#define HAVE_ASN1_TIME_TM_CMP 1"
fi
if [ ${HAVE_ASN1_TIME_TM_CLAMP_NOTAFTER} -eq 0 ]; then
echo "struct tm;"
echo "extern int ASN1_time_tm_clamp_notafter(struct tm *);"
else
echo "#define HAVE_ASN1_TIME_TM_CLAMP_NOTAFTER 1"
fi
if [ ${HAVE_ASN1_TIME_PARSE} -eq 0 ]; then
echo "struct tm;"
echo "extern int ASN1_time_parse(const char *, size_t, struct tm *, int);"
else
echo "#define HAVE_ASN1_TIME_PARSE 1"
fi
if [ ${HAVE_ERR} -eq 0 ]; then
echo "extern void err(int, const char*, ...);"
echo "extern void errx(int, const char*, ...);"
echo "extern void warn(const char*, ...);"
echo "extern void warnx(const char*, ...);"
else
echo "#include <err.h>"
fi
if [ ${HAVE_EXPLICIT_BZERO} -eq 0 ]; then
echo "extern void explicit_bzero(void*, size_t);"
fi
if [ ${HAVE_FREEZERO} -eq 0 ]; then
echo "extern void freezero(void*, size_t);"
fi
if [ ${HAVE_GETDTABLECOUNT} -eq 0 ]; then
echo "extern int getdtablecount(void);"
fi
if [ ${HAVE_GETDTABLESIZE} -eq 0 ]; then
echo "extern int getdtablesize(void);"
fi
if [ ${HAVE_GETENTROPY} -eq 0 ]; then
echo "extern int getentropy(void *, size_t);"
fi
if [ ${HAVE_GETPROGNAME} -eq 0 ]; then
echo "extern const char *getprogname(void);"
fi
if [ ${HAVE_MEMMEM} -eq 0 ]; then
echo "extern void *memmem(const void *, size_t, const void *, size_t);"
fi
if [ ${HAVE_REALLOCARRAY} -eq 0 ]; then
echo "extern void *reallocarray(void*, size_t, size_t);"
fi
if [ ${HAVE_RECALLOCARRAY} -eq 0 ]; then
echo "extern void *recallocarray(void*, size_t, size_t, size_t);"
fi
if [ ${HAVE_SETPROCTITLE} -eq 0 ]; then
echo "extern void setproctitle(const char *fmt, ...);"
fi
if [ ${HAVE_SETRESGID} -eq 0 ]; then
echo "extern int setresgid(gid_t, gid_t, gid_t);"
fi
if [ ${HAVE_SETRESUID} -eq 0 ]; then
echo "extern int setresuid(uid_t, uid_t, uid_t);"
fi
if [ ${HAVE_STRLCAT} -eq 0 ]; then
echo "extern size_t strlcat(char*, const char*, size_t);"
fi
if [ ${HAVE_STRLCPY} -eq 0 ]; then
echo "extern size_t strlcpy(char*, const char*, size_t);"
fi
if [ ${HAVE_STRTONUM} -eq 0 ]; then
echo "extern long long strtonum(const char*, long long, long long, const char**);"
fi
if [ ${HAVE_TIMINGSAFE_MEMCMP} -eq 0 ]; then
echo "extern int timingsafe_memcmp(const void *, const void *, size_t);"
fi
if [ ${HAVE_VASPRINTF} -eq 0 ]; then
echo "extern int vasprintf(char**, const char*, va_list);"
fi
if [ ${HAVE_ASN1_TIME_TM_CMP} -eq 0 ]; then
echo "#include <openssl/asn1.h>"
echo "struct tm;"
echo "int ASN1_time_tm_cmp(struct tm *, struct tm *);"
else
echo "#define HAVE_ASN1_TIME_TM_CMP 1"
fi
if [ ${HAVE_SSL_CTX_UCCM} -eq 0 -o ${HAVE_SSL_CTX_LVM} -eq 0 ]; then
echo "#include <openssl/ssl.h>"
fi
if [ ${HAVE_SSL_CTX_UCCM} -eq 0 ]; then
echo "int SSL_CTX_use_certificate_chain_mem(SSL_CTX *, void *, int);"
else
echo "#define HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM 1"
fi
if [ ${HAVE_SSL_CTX_LVM} -eq 0 ]; then
echo "int SSL_CTX_load_verify_mem(SSL_CTX *, void *, int);"
else
echo "#define HAVE_SSL_CTX_LOAD_VERIFY_MEM 1"
fi
if [ ${HAVE_X509_LOOKUP_MEM} -eq 0 ]; then
echo "#include <openssl/x509_vfy.h>"
echo "X509_LOOKUP_METHOD *X509_LOOKUP_mem(void);"
else
echo "#define HAVE_X509_LOOKUP_MEM 1"
fi
cat <<__HEREDOC__
#ifndef __dead
#define __dead __attribute__((noreturn))
#endif
/* Linux and OpenBSD have LOGIN_NAME_MAX, FreeBSD MAXLOGNAME. */
#ifndef LOGIN_NAME_MAX
# if defined(MAXLOGNAME)
# define LOGIN_NAME_MAX MAXLOGNAME
# elif defined(_POSIX_LOGIN_NAME_MAX)
# define LOGIN_NAME_MAX _POSIX_LOGIN_NAME_MAX
# else
# define LOGIN_NAME_MAX 32
# endif
#endif
#ifndef HOST_NAME_MAX
# if defined(_POSIX_HOST_NAME_MAX)
# define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
# else
# define HOST_NAME_MAX 255
# endif
#endif
__HEREDOC__
echo "file config.h: written" 1>&2
echo "file config.h: written" 1>&3
exec > config.mk
[ -z "${BINDIR}" ] && BINDIR="\${PREFIX}/bin"
[ -z "${MANDIR}" ] && MANDIR="\${PREFIX}/man"
cat << __HEREDOC__
CC = ${CC}
CFLAGS = ${CFLAGS}
LDFLAGS = ${LDFLAGS}
LIBS = ${LIBS}
YACC = ${YACC}
STATIC = ${STATIC}
PREFIX = ${PREFIX}
BINDIR = ${BINDIR}
MANDIR = ${MANDIR}
INSTALL = ${INSTALL}
INSTALL_PROGRAM = \${INSTALL} -m 0555
INSTALL_LIB = \${INSTALL} -m 0444
INSTALL_MAN = \${INSTALL} -m 0444
INSTALL_DATA = \${INSTALL} -m 0444
COMPATS= ${COMPATS}
VERSION = ${VERSION}
__HEREDOC__
echo "file config.mk: written" 1>&2
echo "file config.mk: written" 1>&3
echo >&2
echo "Now run \`make' to compile." >&2
echo >&2
exit 0

9
contrib/Docker.gmid.conf Normal file
View File

@ -0,0 +1,9 @@
user gmid
chroot "/var/gemini"
server "localhost" {
listen on * port 1965
cert "/etc/ssl/localhost.pem"
key "/etc/ssl/private/localhost.key"
root "/"
}

19
contrib/Dockerfile Normal file
View File

@ -0,0 +1,19 @@
FROM alpine
WORKDIR /build
RUN apk update && \
apk upgrade && \
apk add --repository=https://dl-cdn.alpinelinux.org/alpine/edge/main \
alpine-sdk \
linux-headers \
bison \
libevent-dev \
openssl-dev
COPY . .
RUN ./configure && make && make install
RUN adduser -H -S -s /sbin/nologin gmid
RUN mkdir /var/gemini
RUN ./contrib/gencert -e localhost && \
mv localhost.pem /etc/ssl && \
mv localhost.key /etc/ssl/private
RUN mv contrib/Docker.gmid.conf /etc/gmid.conf
ENTRYPOINT ["gmid", "-f"]

21
contrib/Makefile Normal file
View File

@ -0,0 +1,21 @@
DISTFILES = Makefile \
Docker.gmid.conf \
Dockerfile \
README \
gencert \
gmid.service \
gmid.sysusers \
mime.types \
renew-certs
all:
false
dist: ${DISTFILES}
mkdir -p ${DESTDIR}/
${INSTALL} -m 0644 ${DISTFILES} ${DESTDIR}/
cd ${DESTDIR} && chmod 755 gencert renew-certs
cp -R vim ${DESTDIR}/vim
.PHONY: all dist
include ../config.mk

38
contrib/README Normal file
View File

@ -0,0 +1,38 @@
This directory is for additional contributed files which may be
useful.
Dockerfile
Sample Dockerfile to build alpine-based gmid images.
gencert
Simple shell script to generate self-signed certificates.
gmid.service
Simple systemd service file.
gmid.sysusers
Sample systemd-sysusers' config file.
mime.types
A copy of OpenBSD' /usr/share/misc/mime.types to be included
within a `types' block.
renew-certs
Flexible script meant to be run in a cronjob to watch for cert
expiration. It can optionally regen the (self-signed)
certificate in place and restart the server too.
vim
Syntax highlighting of gmid configuration for vim, to be
placed into ~/.vim/ or /usr/share/vim/vimfiles/.
To enable Syntastic checker, put this line in your vimrc:
let g:syntastic_gmid_checkers = ['gmid']

104
contrib/gencert Executable file
View File

@ -0,0 +1,104 @@
#!/bin/sh
#
# NAME
# gencert - generate certificates
#
# SYNOPSIS
# ./gencert [-efh] [-D days] [-d destdir] hostname
#
# DESCRIPTION
# A simple script to generate self-signed X.509 certificates for
# gmid.
#
# The option are as follows:
# -D Specify the number of days the certificate
# will be valid for. Use 365 (a year) by default.
# -d Save the certificates to the given directory.
# By default the current directory is used.
# -e Use an EC key instead of RSA.
# -f Forcefully overwrite existing certificates
# without prompting.
# -h Display usage and exit.
#
# SEE ALSO
# openssl(1)
#
progname="$(basename -- "$0")"
usage() {
echo "usage: $progname [-fhe] [-d destdir] [-D days] hostname" >&2
echo "Please read the comment at the top of $0 for the usage." >&2
exit $1
}
ec=no
force=no
destdir=.
days=365
while getopts "D:d:efh" flag; do
case $flag in
D) days="$OPTARG" ;;
d) destdir="${OPTARG%/}" ;;
e) ec=yes ;;
f) force=yes ;;
h) usage 0 ;;
?) usage 1 ;;
esac
done
shift $(($OPTIND - 1))
if [ $# -ne 1 ]; then
usage 1
fi
if [ ! -d "${destdir}" ]; then
echo "${progname}: ${destdir} is not a directory." >&2
usage 1
fi
hostname="${1}"
pem="${destdir}/${hostname}.pem"
key="${destdir}/${hostname}.key"
if [ -f "$pem" -o -f "$key" ]; then
if [ $force = no ]; then
while :; do
printf "Overwrite existing certificate $pem? [y/n] "
if ! read -r reply; then
echo
exit 1
fi
case "$reply" in
[yY]) echo "overwriting"; break ;;
[nN]) echo "quitting"; exit 0 ;;
esac
done
fi
fi
if [ $ec = yes ]; then
openssl ecparam -name secp384r1 -genkey -noout -out "${key}" && \
openssl req -new -x509 -key "${key}" -out "${pem}" -days "${days}" \
-nodes -subj "/CN=$hostname"
else
openssl req -x509 \
-newkey rsa:4096 \
-out "${pem}" \
-keyout "${key}" \
-days "${days}" \
-nodes \
-subj "/CN=$hostname"
fi
e=$?
if [ $e -ne 0 ]; then
exit $e
fi
echo
echo "Generated files:"
echo " $pem : certificate"
echo " $key : private key"

19
contrib/gmid.service Normal file
View File

@ -0,0 +1,19 @@
[Unit]
Description=The gmid Gemini server
Documentation=https://gmid.omarpolo.com/
After=network-online.target syslog.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/gmid -f -c /etc/gmid.conf
ExecStop=/bin/kill -TERM $MAINPID
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=30
StandardOutput=journal
StandardError=inherit
SyslogIdentifier=gmid
[Install]
WantedBy=multi-user.target

1
contrib/gmid.sysusers Normal file
View File

@ -0,0 +1 @@
u gmid - "gmid Gemini server" - -

101
contrib/mime.types Normal file
View File

@ -0,0 +1,101 @@
# $OpenBSD: mime.types,v 1.4 2018/01/06 22:05:03 sthen Exp $
application/atom+xml atom
application/font-woff woff
application/java-archive jar war ear
application/javascript js
application/json json
application/mac-binhex40 hqx
application/msword doc
application/octet-stream bin exe dll
application/octet-stream deb
application/octet-stream dmg
application/octet-stream fs iso img
application/octet-stream msi msp msm
application/pdf pdf
application/postscript ps eps ai
application/rss+xml rss
application/rtf rtf
application/vnd.apple.mpegurl m3u8
application/vnd.google-earth.kml+xml kml
application/vnd.google-earth.kmz kmz
application/vnd.ms-excel xls
application/vnd.ms-fontobject eot
application/vnd.ms-powerpoint ppt
application/vnd.oasis.opendocument.chart odc
application/vnd.oasis.opendocument.chart-template otc
application/vnd.oasis.opendocument.database odb
application/vnd.oasis.opendocument.formula odf
application/vnd.oasis.opendocument.formula-template otf
application/vnd.oasis.opendocument.graphics odg
application/vnd.oasis.opendocument.graphics-template otg
application/vnd.oasis.opendocument.image odi
application/vnd.oasis.opendocument.image-template oti
application/vnd.oasis.opendocument.presentation odp
application/vnd.oasis.opendocument.presentation-template otp
application/vnd.oasis.opendocument.spreadsheet ods
application/vnd.oasis.opendocument.spreadsheet-template ots
application/vnd.oasis.opendocument.text odt
application/vnd.oasis.opendocument.text-master odm
application/vnd.oasis.opendocument.text-template ott
application/vnd.oasis.opendocument.text-web oth
application/vnd.wap.wmlc wmlc
application/x-7z-compressed 7z
application/x-cocoa cco
application/x-java-archive-diff jardiff
application/x-java-jnlp-file jnlp
application/x-makeself run
application/x-ns-proxy-autoconfig pac
application/x-perl pl pm
application/x-pilot prc pdb
application/x-rar-compressed rar
application/x-redhat-package-manager rpm
application/x-sea sea
application/x-shockwave-flash swf
application/x-stuffit sit
application/x-tcl tcl tk
application/x-x509-ca-cert der pem crt
application/x-xpinstall xpi
application/xhtml+xml xhtml
application/zip zip
audio/basic au snd
audio/midi mid midi kar
audio/mpeg mp3
audio/ogg ogg
audio/x-m4a m4a
audio/x-realaudio ra
image/gif gif
image/jpeg jpeg jpg
image/png png
image/svg+xml svg svgz
image/tiff tif tiff
image/vnd.wap.wbmp wbmp
image/webp webp
image/x-icon ico
image/x-jng jng
image/x-ms-bmp bmp
text/css css
text/html html htm shtml
text/mathml mml
text/plain txt
text/vnd.sun.j2me.app-descriptor jad
text/vnd.wap.wml wml
text/x-component htc
text/xml xml
video/3gpp 3gpp 3gp
video/mp2t ts
video/mp4 mp4
video/mpeg mpeg mpg
video/quicktime mov
video/webm webm
video/x-flv flv
video/x-m4v m4v
video/x-matroska mkv
video/x-mng mng
video/x-ms-asf asx asf
video/x-ms-wmv wmv
video/x-msvideo avi

200
contrib/renew-certs Executable file
View File

@ -0,0 +1,200 @@
#!/usr/bin/env perl
#
# Copyright (c) 2021 Omar Polo <op@omarpolo.com>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#
# You can read the documentation for this script using
#
# $ perldoc renew-certs
#
use v5.10;
use strict;
use warnings;
use Getopt::Std;
use Time::Piece;
my $auto = 0;
my $conf = '/etc/gmid.conf';
my $days = 365;
my $gmid = 'gmid';
my $restart = 0;
my $threshold = 24 * 60 * 60;
my %options = ();
getopts("ac:d:g:r", \%options);
foreach my $flag (keys %options) {
if ($flag eq 'a') {
$auto = 1;
} elsif ($flag eq 'c') {
$conf = $options{c};
} elsif ($flag eq 'd') {
$days = int($options{d}) or exit 1;
} elsif ($flag eq 'g') {
$gmid = $options{g};
} elsif ($flag eq 'r') {
$auto = 1;
$restart = 1;
} elsif ($flag eq 't') {
$threshold = int($options{t}) or exit 1;
}
}
my $now = localtime()->epoch + $threshold;
my $found_one = 0;
my $c = `$gmid -nn -c $conf @ARGV 2>/dev/null`;
die "$gmid failed to parse $conf" if $? != 0;
while ($c =~ /server \"(.*)\"/g) {
my $server = $1;
$c =~ /cert \"(.*)\"/gc;
my $cert = $1;
$c =~ /key \"(.*)\"/gc;
my $key = $1;
if (expired($cert)) {
$found_one = 1;
if ($auto) {
renew($server, $cert, $key);
} else {
say $server;
}
}
}
if ($found_one && $restart) {
my @cmd = ("pkill", "-HUP", $gmid);
system(@cmd);
}
exit !$found_one;
sub expired {
my ($cert) = @_;
my $exp = `openssl x509 -noout -enddate -in $cert`;
die 'failed to execute openssl' if $? != 0;
chomp $exp;
my $d = Time::Piece->strptime($exp, "notAfter=%b %e %T %Y %Z");
return $d->epoch < $now;
}
sub renew {
my ($hostname, $cert, $key) = @_;
my @cmd = (
"openssl", "req", "-x509",
"-newkey", "rsa:4096",
"-out", $cert,
"-keyout", $key,
"-days", $days,
"-nodes",
"-subj", "/CN=".$hostname,
);
system(@cmd) == 0
or die "system @cmd failed: $?";
}
__END__
=head1 NAME
B<renew-certs> - automatically renew gmid certificates
=head1 SYNOPSIS
B<renew-certs> [-ar] [-c I<conf>] [-d I<days>] [-g I<gmid>] [-t I<threshold>] [-- I<gmid flags...>]
=head1 DESCRIPTION
B<renew-certs> attempts to renew the certificates used by gmid if they
are close to the expiration date and can optionally restart the
server. It's meant to be used in a crontab(5) file.
B<renew-certs> needs at least B<gmid> 1.8.
The arguments are as follows:
=over
=item -a
Automatically generate a new set of certificates.
=item -c I<conf>
Path to the gmid configuration. By default is F</etc/gmid.conf>.
=item -d I<days>
Number of I<days> the newly generated certificates will be valid for;
365 by default.
=item -g I<gmid>
Path to the gmid(1) executable.
=item -r
Restart B<gmid> after re-generating the certificates by killing it
with SIGHUP. Implies -a.
=item -t I<threshold>
Tweak the expiring I<threshold>. Certificates whose I<notAfter> field
ends before I<threshold> seconds will be considered outdated. By
default is 86400, or 24 * 60 * 60, 24 hours.
=item I<gmid flags>
Additional flags to be passed to gmid(1).
=back
=head1 EXIT STATUS
The B<renew-certs> utility exits on 0 when at least one certificate is
about to expire and >0 otherwise, or if an error occurs.
=head1 EXAMPLES
Some examples of how to use B<renew-certs> in a crontab(5) file
follows:
# automatically renew and restart gmid
0 0 * * * renew-certs -r
# like the previous, but pass a custom flag to gmid
0 0 * * * renew-certs -r -- -Dcerts=/etc/ssl/
# automatically renew the certs but use a custom
# command (rcctl in this case) to restart the server
0 0 * * * renew-certs -a && rcctl restart gmid
# only check for expiration. `cmd' can read the names of the
# servers with an expiring certificate from stdin, one per
# line
0 0 * * * renew-certs | cmd
=head1 SEE ALSO
crontab(1) gmid(1) openssl(1) crontab(5)
=cut

View File

@ -0,0 +1,36 @@
" Linter for ALE
" Language: gmid(1) configuration file
" Licence: ISC
call ale#Set('gmid_executable', 'gmid')
function! ale_linters#gmid#gmid#Handle(buffer, lines) abort
let l:output = []
let l:gmid_type_to_ale_type = {
\ 'error': 'E',
\ 'warning': 'W',
\}
let l:pattern = '\v^(.*):(\d*) ([a-z]+): (.*)$'
for l:match in ale#util#GetMatches(a:lines, l:pattern)
call add(l:output, {
\ 'filename': l:match[1],
\ 'lnum': l:match[2],
\ 'type': get(l:gmid_type_to_ale_type, l:match[3], 'E'),
\ 'text': l:match[4],
\})
endfor
return l:output
endfunction
call ale#linter#Define('gmid', {
\ 'name': 'gmid',
\ 'executable': {buffer -> ale#Var(buffer, 'gmid_executable')},
\ 'command': '%e -nc %s',
\ 'output_stream': 'both',
\ 'lint_file': 1,
\ 'callback': 'ale_linters#gmid#gmid#Handle',
\})
" vim: set sw=4 sts=4 et fdm=marker:

View File

@ -0,0 +1,7 @@
" Vim filetype detection file
" Language: gmid(1) configuration files
" Licence: ISC
au BufNewFile,BufRead *.gmid set filetype=gmid
au BufNewFile,BufRead */etc/gmid/* set filetype=gmid
au BufNewFile,BufRead gmid.conf set filetype=gmid

View File

@ -0,0 +1,10 @@
if exists("b:did_ftplugin")
finish
endif
let b:did_ftplugin = 1
let b:undo_ftplugin = "setl cms< sua<"
setlocal suffixesadd+=.conf,.gmid
" vim-commentary support
setlocal commentstring=#\ %s

View File

@ -0,0 +1,13 @@
if exists("b:did_indent")
finish
endif
let b:did_indent = 1
setlocal indentexpr=
" cindent actually works for simple file structure
setlocal cindent
" Just make sure that the comments are not reset as defs would be.
setlocal cinkeys-=0#
" And indentation works correctly without semicolons.
setlocal cinoptions=+0

228
contrib/vim/syntax/gmid.vim Normal file
View File

@ -0,0 +1,228 @@
" Vim syntax file
" Language: gmid(1) configuration files
" Licence: ISC
if exists("b:current_syntax")
finish
endif
" Syntax Definition: {{{1
" ==================
syn case match
setlocal iskeyword+=-
" Value Types: {{{2
" ============
syn keyword gmidBoolean on contained
syn keyword gmidBoolean off contained
syn match gmidNumber "\<\d\+\>" display
syn keyword gmidStyle common contained
syn keyword gmidStyle combined contained
syn keyword gmidStyle legacy contained
syn keyword gmidFacility daemon contained
syn keyword gmidFacility ftp contained
syn keyword gmidFacility local0 contained
syn keyword gmidFacility local1 contained
syn keyword gmidFacility local2 contained
syn keyword gmidFacility local3 contained
syn keyword gmidFacility local4 contained
syn keyword gmidFacility local5 contained
syn keyword gmidFacility local6 contained
syn keyword gmidFacility local7 contained
syn keyword gmidFacility user contained
syn region gmidQuotedString start=+"+ end=+"+ skip=+\\"+
syn region gmidQuotedString start=+'+ end=+'+ skip=+\\'+
syn match gmidVariable "\$\w\w*" display
syn match gmidMacro "@\w\w*" display
syn cluster gmidValues contains=gmidNumber,
\ gmidQuotedString,
\ gmidVariable,
\ gmidMacro,
\ gmidDeprecated
" Errors: {{{2
" ============
" TODO: write comprehensive syntax rules so it can be checked with:
" syn match gmidError '.'
syn keyword gmidDeprecated ipv6 nextgroup=gmidBoolean skipwhite
" Comments: {{{2
" =========
syn match gmidComment "\s*#.*$" display
" Global Options: {{{2
" ===============
syn keyword gmidDirective chroot
syn keyword gmidDirective include
syn keyword gmidDirective prefork nextgroup=gmidNumber skipwhite
syn keyword gmidDirective protocols
syn keyword gmidDirective user
" Logging options
syn match gmidDirective "\<log\s\+access\>" display
syn match gmidDirective "\<log\s\+style\>" display
\ nextgroup=gmidStyle skipwhite
syn match gmidDirective "\<log\s\+syslog\>" display
\ nextgroup=gmidBoolean skipwhite
syn match gmidDirective "\<log\s\+syslog\s\+facility\>" display
\ nextgroup=gmidFacility skipwhite
" Global Log Blocks: {{{3
" ==================
syn region gmidBlockLog start="log\s\+{" end="}"
\ fold transparent
\ contains=gmidDirectiveLog,
\ @gmidValues
syn keyword gmidDirectiveBlock log contained containedin=gmidBlockLog
syn keyword gmidDirectiveLog access contained
syn keyword gmidDirectiveLog style contained nextgroup=gmidStyle skipwhite
syn keyword gmidDirectiveLog syslog contained nextgroup=gmidBoolean skipwhite
syn match gmidDirectiveLog "\<syslog\s\+facility\>" display
\ contained nextgroup=gmidFacility skipwhite
" Server Blocks: {{{2
" ==============
syn region gmidBlockServer start="server\s\+.\+\s\+{" end="}"
\ fold transparent
\ contains=gmidDirectiveServer,
\ gmidDirectiveParamServer,
\ gmidDirectiveHost,
\ gmidDirectiveParamHost,
\ gmidBlockLocation,
\ gmidBlockFastcgi,
\ gmidBlockProxy,
\ @gmidValues
syn keyword gmidDirectiveBlock server contained containedin=gmidBlockServer
syn region gmidBlockLocation start="location\s\+.\+\s\+{" end="}"
\ fold transparent contained
\ contains=gmidDirectiveHost,
\ gmidDirectiveParamHost,
\ gmidBlockFastcgi,
\ @gmidValues
syn keyword gmidDirectiveBlock location contained containedin=gmidBlockLocation
syn match gmidDirectiveHost "\<auto\s\+index\>" display
\ contained nextgroup=gmidBoolean skipwhite
syn keyword gmidDirectiveHost block contained
syn keyword gmidDirectiveParamHost return contained nextgroup=gmidNumber skipwhite
syn match gmidDirectiveHost "\<default\s\+type\>" display contained
syn keyword gmidDirectiveHost index contained
syn keyword gmidDirectiveHost lang contained
syn keyword gmidDirectiveHost log contained nextgroup=gmidBoolean skipwhite
syn keyword gmidDirectiveHost ocsp contained
syn keyword gmidDirectiveHost root contained
syn match gmidDirectiveHost "\<require\s\+client\s\+ca\>" display contained
syn keyword gmidDirectiveHost strip contained nextgroup=gmidNumber skipwhite
" FastCGI options
syn match gmidDirectiveHost "\<fastcgi\s\+off\>" display contained
syn match gmidDirectiveHost "\<fastcgi\s\+socket\>" display contained
syn keyword gmidDirectiveParamHost tcp contained
syn match gmidDirectiveHost "\<fastcgi\s\+strip\>" display
\ contained nextgroup=gmidNumber skipwhite
" Options unavailable for `location`
syn keyword gmidDirectiveServer alias contained
syn keyword gmidDirectiveServer cert contained
syn keyword gmidDirectiveServer key contained
syn match gmidDirectiveServer "\<listen\s\+on\>" display contained
" Ambiguos, can be used both in `listen on` and `fastcgi socket`
syn keyword gmidDirectiveParamHost port contained nextgroup=gmidNumber skipwhite
" FastCGI Blocks: {{{3
" ===============
syn region gmidBlockFastcgi start="fastcgi\s\+{" end="}"
\ fold transparent contained
\ contains=gmidDirectiveFastcgi,
\ gmidDirectiveParamFastcgi,
\ @gmidValues
syn keyword gmidDirectiveBlock fastcgi contained containedin=gmidBlockFastcgi
syn keyword gmidDirectiveFastcgi param contained
syn keyword gmidDirectiveFastcgi socket contained
syn keyword gmidDirectiveParamFastcgi tcp contained
syn keyword gmidDirectiveParamFastcgi port contained nextgroup=gmidNumber skipwhite
syn keyword gmidDirectiveFastcgi strip contained nextgroup=gmidNumber skipwhite
" Proxy Blocks: {{{3
" =============
syn region gmidBlockProxy start="proxy\s\+\(.*\s\+\)\?{" end="}"
\ fold transparent contained
\ contains=gmidDirectiveProxy,
\ gmidDirectiveParamProxy,
\ @gmidValues
syn keyword gmidDirectiveBlock proxy contained containedin=gmidBlockProxy
syn keyword gmidDirectiveParamProxy proto contained
syn keyword gmidDirectiveParamProxy for-host contained
syn keyword gmidDirectiveProxy cert contained
syn keyword gmidDirectiveProxy key contained
syn keyword gmidDirectiveProxy protocols contained
syn keyword gmidDirectiveProxy relay-to contained
syn match gmidDirectiveProxy "\<require\s\+client\s\+ca\>" display contained
syn keyword gmidDirectiveProxy sni contained
syn keyword gmidDirectiveProxy use-tls contained nextgroup=gmidBoolean skipwhite
syn keyword gmidDirectiveProxy verifyname contained nextgroup=gmidBoolean skipwhite
" Ambiguos, can be used both in `proxy` and `relay-to`
syn keyword gmidDirectiveParamProxy port contained nextgroup=gmidNumber skipwhite
" Types Blocks: {{{2
" =============
syn region gmidBlockTypes start="types\s\+{" end="}"
\ fold transparent
\ contains=gmidDirectiveTypes,
\ @gmidValues
syn keyword gmidDirectiveBlock types contained containedin=gmidBlockTypes
syn keyword gmidDirectiveTypes include contained
" Highlighting Settings: {{{1
" ======================
" Create aliases
hi def link gmidDirectiveLog gmidDirective
hi def link gmidDirectiveTypes gmidDirective
hi def link gmidDirectiveServer gmidDirective
hi def link gmidDirectiveParamServer gmidDirectiveParam
hi def link gmidDirectiveHost gmidDirective
hi def link gmidDirectiveParamHost gmidDirectiveParam
hi def link gmidDirectiveFastcgi gmidDirective
hi def link gmidDirectiveParamFastcgi gmidDirectiveParam
hi def link gmidDirectiveProxy gmidDirective
hi def link gmidDirectiveParamProxy gmidDirectiveParam
" Map to standard types
hi def link gmidComment Comment
hi def link gmidBoolean Boolean
hi def link gmidNumber Number
hi def link gmidStyle Constant
hi def link gmidFacility Constant
hi def link gmidQuotedString String
hi def link gmidVariable Identifier
hi def link gmidMacro Macro
hi def link gmidDirective Keyword
hi def link gmidDirectiveBlock Function
hi def link gmidDirectiveParam Type
hi def link gmidDeprecated Error
let b:current_syntax = "gmid"

View File

@ -0,0 +1,36 @@
" Syntax checking plugin for syntastic
" Language: gmid(1) configuration file
" Licence: ISC
if exists('g:loaded_syntastic_gmid_gmid_checker')
finish
endif
let g:loaded_syntastic_gmid_gmid_checker = 1
let s:save_cpo = &cpo
set cpo&vim
function! SyntaxCheckers_gmid_gmid_GetLocList() dict
let makeprg = self.makeprgBuild({ 'args': '-nc' })
let errorformat =
\ '%-Gconfig OK,' .
\ '%f:%l %tarning: %m,' .
\ '%f:%l %trror: %m'
return SyntasticMake({
\ 'makeprg': makeprg,
\ 'errorformat': errorformat,
\ 'defaults': {'type': 'E'},
\ 'returns': [0, 1] })
endfunction
call g:SyntasticRegistry.CreateAndRegisterChecker({
\ 'filetype': 'gmid',
\ 'name': 'gmid',
\ 'exec': 'gmid'})
let &cpo = s:save_cpo
unlet s:save_cpo
" vim: set sw=4 sts=4 et fdm=marker:

580
crypto.c Normal file
View File

@ -0,0 +1,580 @@
/*
* Copyright (c) 2023 Omar Polo <op@omarpolo.com>
* Copyright (c) 2014 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "gmid.h"
#include <string.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include "log.h"
#include "proc.h"
#ifndef nitems
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#endif
static void crypto_init(struct privsep *, struct privsep_proc *, void *);
static int crypto_dispatch_parent(int, struct privsep_proc *, struct imsg *);
static int crypto_dispatch_server(int, struct privsep_proc *, struct imsg *);
static struct privsep_proc procs[] = {
{ "parent", PROC_PARENT, crypto_dispatch_parent },
{ "server", PROC_SERVER, crypto_dispatch_server },
};
struct imsg_crypto_req {
uint64_t id;
char hash[TLS_CERT_HASH_SIZE];
size_t flen;
size_t tlen;
int padding;
/* followed by flen bytes of `from'. */
};
struct imsg_crypto_res {
uint64_t id;
int ret;
size_t len;
/* followed by len bytes of reply */
};
static uint64_t reqid;
static struct conf *conf;
void
crypto(struct privsep *ps, struct privsep_proc *p)
{
proc_run(ps, p, procs, nitems(procs), crypto_init, NULL);
}
static void
crypto_init(struct privsep *ps, struct privsep_proc *p, void *arg)
{
#if 0
static volatile int attached;
while (!attached) sleep(1);
#endif
conf = ps->ps_env;
sandbox_crypto_process();
}
static int
crypto_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
{
switch (imsg_get_type(imsg)) {
case IMSG_RECONF_START:
case IMSG_RECONF_CERT:
case IMSG_RECONF_KEY:
case IMSG_RECONF_END:
if (config_recv(conf, imsg) == -1)
return -1;
break;
default:
return -1;
}
return 0;
}
static EVP_PKEY *
get_pkey(const char *hash)
{
struct pki *pki;
TAILQ_FOREACH(pki, &conf->pkis, pkis) {
if (!strcmp(pki->hash, hash))
return pki->pkey;
}
return NULL;
}
static int
crypto_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg)
{
struct privsep *ps = p->p_ps;
RSA *rsa = NULL;
EC_KEY *ecdsa = NULL;
EVP_PKEY *pkey;
struct imsg_crypto_req req;
struct imsg_crypto_res res;
struct ibuf ibuf;
struct iovec iov[2];
const void *from;
unsigned char *to;
int n, ret, type;
unsigned int len;
pid_t pid;
if (imsg_get_ibuf(imsg, &ibuf) == -1)
fatalx("%s: couldn't get an ibuf", __func__);
pid = imsg_get_pid(imsg);
switch (type = imsg_get_type(imsg)) {
case IMSG_CRYPTO_RSA_PRIVENC:
case IMSG_CRYPTO_RSA_PRIVDEC:
if (ibuf_get(&ibuf, &req, sizeof(req)) == -1 ||
ibuf_size(&ibuf) != req.flen)
fatalx("size mismatch for imsg %d", type);
from = ibuf_data(&ibuf);
if ((pkey = get_pkey(req.hash)) == NULL ||
(rsa = EVP_PKEY_get1_RSA(pkey)) == NULL)
fatalx("invalid pkey hash");
if ((to = calloc(1, req.tlen)) == NULL)
fatal("calloc");
if (type == IMSG_CRYPTO_RSA_PRIVENC)
ret = RSA_private_encrypt(req.flen, from,
to, rsa, req.padding);
else
ret = RSA_private_decrypt(req.flen, from,
to, rsa, req.padding);
memset(&res, 0, sizeof(res));
res.id = req.id;
res.ret = ret;
memset(&iov, 0, sizeof(iov));
n = 0;
iov[n].iov_base = &res;
iov[n].iov_len = sizeof(res);
n++;
if (ret > 0) {
res.len = ret;
iov[n].iov_base = to;
iov[n].iov_len = ret;
n++;
}
log_debug("replying to server #%d", pid);
if (proc_composev_imsg(ps, PROC_SERVER, pid - 1,
type, 0, -1, iov, n) == -1)
fatal("proc_composev_imsg");
if (proc_flush_imsg(ps, PROC_SERVER, pid - 1) == -1)
fatal("proc_flush_imsg");
free(to);
RSA_free(rsa);
break;
case IMSG_CRYPTO_ECDSA_SIGN:
if (ibuf_get(&ibuf, &req, sizeof(req)) == -1 ||
ibuf_size(&ibuf) != req.flen)
fatalx("size mismatch for imsg %d", type);
from = ibuf_data(&ibuf);
if ((pkey = get_pkey(req.hash)) == NULL ||
(ecdsa = EVP_PKEY_get1_EC_KEY(pkey)) == NULL)
fatalx("invalid pkey hash");
len = ECDSA_size(ecdsa);
if ((to = calloc(1, len)) == NULL)
fatal("calloc");
ret = ECDSA_sign(0, from, req.flen, to, &len, ecdsa);
memset(&res, 0, sizeof(res));
res.id = req.id;
res.ret = ret;
memset(&iov, 0, sizeof(iov));
n = 0;
iov[0].iov_base = &res;
iov[0].iov_len = sizeof(res);
n++;
if (ret > 0) {
res.len = len;
iov[n].iov_base = to;
iov[n].iov_len = len;
n++;
}
log_debug("replying to server #%d", pid);
if (proc_composev_imsg(ps, PROC_SERVER, pid - 1,
type, 0, -1, iov, n) == -1)
fatal("proc_composev_imsg");
if (proc_flush_imsg(ps, PROC_SERVER, pid - 1) == -1)
fatal("proc_flush_imsg");
free(to);
EC_KEY_free(ecdsa);
break;
default:
return -1;
}
return 0;
}
/*
* RSA privsep engine (called from unprivileged processes)
*/
static const RSA_METHOD *rsa_default;
static RSA_METHOD *rsae_method;
static int
rsae_send_imsg(int flen, const unsigned char *from, unsigned char *to,
RSA *rsa, int padding, unsigned int cmd)
{
struct imsg_crypto_req req;
struct iovec iov[2];
struct imsg_crypto_res res;
struct imsgev *iev;
struct privsep_proc *p;
struct privsep *ps = conf->ps;
struct imsgbuf *imsgbuf;
struct imsg imsg;
struct ibuf ibuf;
int ret = 0;
int n, done = 0;
const void *toptr;
char *hash;
if ((hash = RSA_get_ex_data(rsa, 0)) == NULL)
return (0);
/*
* Send a synchronous imsg because we cannot defer the RSA
* operation in OpenSSL's engine layer.
*/
memset(&req, 0, sizeof(req));
req.id = ++reqid;
if (strlcpy(req.hash, hash, sizeof(req.hash)) >= sizeof(req.hash))
fatalx("%s: hash too long (%zu)", __func__, strlen(hash));
req.flen = flen;
req.tlen = RSA_size(rsa);
req.padding = padding;
memset(&iov, 0, sizeof(iov));
iov[0].iov_base = &req;
iov[0].iov_len = sizeof(req);
iov[1].iov_base = (void *)from;
iov[1].iov_len = flen;
if (proc_composev(ps, PROC_CRYPTO, cmd, iov, 2) == -1)
fatal("proc_composev");
if (proc_flush_imsg(ps, PROC_CRYPTO, -1) == -1)
fatal("proc_flush_imsg");
iev = ps->ps_ievs[PROC_CRYPTO];
p = iev->proc;
imsgbuf = &iev->ibuf;
while (!done) {
if ((n = imsg_read(imsgbuf)) == -1 && errno != EAGAIN)
fatalx("imsg_read");
if (n == 0)
fatalx("pipe closed");
while (!done) {
if ((n = imsg_get(imsgbuf, &imsg)) == -1)
fatalx("imsg_get error");
if (n == 0)
break;
#if DEBUG > 1
log_debug(
"%s: %s %d got imsg %d id %d from %s %d",
__func__, title, 1, imsg_get_type(&imsg),
imsg_get_id(&imsg), "crypto", imsg_get_pid(&imsg));
#endif
if ((p->p_cb)(imsgbuf->fd, p, &imsg) == 0) {
/* Message was handled by the callback */
imsg_free(&imsg);
continue;
}
switch (imsg_get_type(&imsg)) {
case IMSG_CRYPTO_RSA_PRIVENC:
case IMSG_CRYPTO_RSA_PRIVDEC:
break;
default:
fatalx("%s: %s %d got invalid imsg %d"
" id %d from %s %d",
__func__, "server", ps->ps_instance + 1,
imsg_get_type(&imsg), imsg_get_id(&imsg),
"crypto", imsg_get_pid(&imsg));
}
if (imsg_get_ibuf(&imsg, &ibuf) == -1 ||
ibuf_get(&ibuf, &res, sizeof(res)) == -1 ||
(int)ibuf_size(&ibuf) != res.ret)
fatalx("size mismatch for imsg %d",
imsg.hdr.type);
ret = res.ret;
toptr = ibuf_data(&ibuf);
if (res.id != reqid)
fatalx("invalid id; got %llu, want %llu",
(unsigned long long)res.id,
(unsigned long long)reqid);
if (res.ret > 0)
memcpy(to, toptr, res.len);
done = 1;
imsg_free(&imsg);
}
}
imsg_event_add(iev);
return (ret);
}
static int
rsae_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa,
int padding)
{
log_debug("debug: %s", __func__);
if (RSA_get_ex_data(rsa, 0) != NULL)
return (rsae_send_imsg(flen, from, to, rsa, padding,
IMSG_CRYPTO_RSA_PRIVENC));
return (RSA_meth_get_priv_enc(rsa_default)(flen, from, to, rsa, padding));
}
static int
rsae_priv_dec(int flen, const unsigned char *from, unsigned char *to, RSA *rsa,
int padding)
{
log_debug("debug: %s", __func__);
if (RSA_get_ex_data(rsa, 0) != NULL)
return (rsae_send_imsg(flen, from, to, rsa, padding,
IMSG_CRYPTO_RSA_PRIVDEC));
return (RSA_meth_get_priv_dec(rsa_default)(flen, from, to, rsa, padding));
}
/*
* ECDSA privsep engine (called from unprivileged processes)
*/
static const EC_KEY_METHOD *ecdsa_default;
static EC_KEY_METHOD *ecdsae_method;
static ECDSA_SIG *
ecdsae_send_enc_imsg(const unsigned char *dgst, int dgst_len,
const BIGNUM *inv, const BIGNUM *rp, EC_KEY *eckey)
{
ECDSA_SIG *sig = NULL;
struct imsg_crypto_req req;
struct iovec iov[2];
struct imsg_crypto_res res;
struct imsgev *iev;
struct privsep_proc *p;
struct privsep *ps = conf->ps;
struct imsgbuf *imsgbuf;
struct imsg imsg;
struct ibuf ibuf;
int n, done = 0;
const void *toptr;
char *hash;
if ((hash = EC_KEY_get_ex_data(eckey, 0)) == NULL)
return (0);
/*
* Send a synchronous imsg because we cannot defer the RSA
* operation in OpenSSL's engine layer.
*/
memset(&req, 0, sizeof(req));
req.id = ++reqid;
if (strlcpy(req.hash, hash, sizeof(req.hash)) >= sizeof(req.hash))
fatalx("%s: hash too long (%zu)", __func__, strlen(hash));
req.flen = dgst_len;
memset(&iov, 0, sizeof(iov));
iov[0].iov_base = &req;
iov[0].iov_len = sizeof(req);
iov[1].iov_base = (void *)dgst;
iov[1].iov_len = dgst_len;
if (proc_composev(ps, PROC_CRYPTO, IMSG_CRYPTO_ECDSA_SIGN, iov, 2) == -1)
fatal("proc_composev");
if (proc_flush_imsg(ps, PROC_CRYPTO, -1) == -1)
fatal("proc_flush_imsg");
iev = ps->ps_ievs[PROC_CRYPTO];
p = iev->proc;
imsgbuf = &iev->ibuf;
while (!done) {
if ((n = imsg_read(imsgbuf)) == -1 && errno != EAGAIN)
fatalx("imsg_read");
if (n == 0)
fatalx("pipe closed");
while (!done) {
if ((n = imsg_get(imsgbuf, &imsg)) == -1)
fatalx("imsg_get error");
if (n == 0)
break;
#if DEBUG > 1
log_debug(
"%s: %s %d got imsg %d peerid %d from %s %d",
__func__, title, 1, imsg.hdr.type,
imsg.hdr.peerid, "crypto", imsg.hdr.pid);
#endif
if (imsg.hdr.type != IMSG_CRYPTO_ECDSA_SIGN &&
crypto_dispatch_server(imsgbuf->fd, p, &imsg)
== 0) {
/* Message was handled by the callback */
imsg_free(&imsg);
continue;
}
if (imsg.hdr.type != IMSG_CRYPTO_ECDSA_SIGN)
fatalx("%s: %s %d got invalid imsg %d"
" peerid %d from %s %d",
__func__, "server", ps->ps_instance + 1,
imsg.hdr.type, imsg.hdr.peerid,
"crypto", imsg.hdr.pid);
if (imsg_get_ibuf(&imsg, &ibuf) == -1 ||
ibuf_get(&ibuf, &res, sizeof(res)) == -1 ||
ibuf_size(&ibuf) != res.len)
fatalx("size mismatch for imsg %d",
imsg.hdr.type);
toptr = ibuf_data(&ibuf);
if (res.id != reqid)
fatalx("invalid response id");
if (res.ret > 0) {
d2i_ECDSA_SIG(&sig,
(const unsigned char **)&toptr, res.len);
}
done = 1;
imsg_free(&imsg);
}
}
imsg_event_add(iev);
return (sig);
}
static ECDSA_SIG *
ecdsae_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv,
const BIGNUM *rp, EC_KEY *eckey)
{
ECDSA_SIG *(*psign_sig)(const unsigned char *, int, const BIGNUM *,
const BIGNUM *, EC_KEY *);
log_debug("debug: %s", __func__);
if (EC_KEY_get_ex_data(eckey, 0) != NULL)
return (ecdsae_send_enc_imsg(dgst, dgst_len, inv, rp, eckey));
EC_KEY_METHOD_get_sign(ecdsa_default, NULL, NULL, &psign_sig);
return (psign_sig(dgst, dgst_len, inv, rp, eckey));
}
/*
* Initialize the two engines.
*/
static void
rsa_engine_init(void)
{
const char *errstr;
if ((rsa_default = RSA_get_default_method()) == NULL) {
errstr = "RSA_get_default_method";
goto fail;
}
if ((rsae_method = RSA_meth_dup(rsa_default)) == NULL) {
errstr = "RSA_meth_dup";
goto fail;
}
RSA_meth_set_priv_enc(rsae_method, rsae_priv_enc);
RSA_meth_set_priv_dec(rsae_method, rsae_priv_dec);
RSA_meth_set_flags(rsae_method,
RSA_meth_get_flags(rsa_default) | RSA_METHOD_FLAG_NO_CHECK);
RSA_meth_set0_app_data(rsae_method,
RSA_meth_get0_app_data(rsa_default));
RSA_set_default_method(rsae_method);
return;
fail:
ssl_error(errstr);
fatalx("%s", errstr);
}
static void
ecdsa_engine_init(void)
{
int (*sign)(int, const unsigned char *, int, unsigned char *,
unsigned int *, const BIGNUM *, const BIGNUM *, EC_KEY *);
int (*sign_setup)(EC_KEY *, BN_CTX *, BIGNUM **, BIGNUM **);
const char *errstr;
if ((ecdsa_default = EC_KEY_get_default_method()) == NULL) {
errstr = "EC_KEY_get_default_method";
goto fail;
}
if ((ecdsae_method = EC_KEY_METHOD_new(ecdsa_default)) == NULL) {
errstr = "EC_KEY_METHOD_new";
goto fail;
}
EC_KEY_METHOD_get_sign(ecdsa_default, &sign, &sign_setup, NULL);
EC_KEY_METHOD_set_sign(ecdsae_method, sign, sign_setup,
ecdsae_do_sign);
EC_KEY_set_default_method(ecdsae_method);
return;
fail:
ssl_error(errstr);
fatalx("%s", errstr);
}
void
crypto_engine_init(struct conf *c)
{
conf = c;
rsa_engine_init();
ecdsa_engine_init();
}

159
dirs.c Normal file
View File

@ -0,0 +1,159 @@
/* $OpenBSD: scandir.c,v 1.21 2019/06/28 13:32:41 deraadt Exp $ */
/*
* This is a modified verision of scandir that takes an explicit
* directory file descriptor as parameter.
*/
/*
* Copyright (c) 1983, 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.
*/
/*
* Scan the directory fd calling select to make a list of selected
* directory entries then sort using qsort and compare routine dcomp.
* Returns the number of entries and a pointer to a list of pointers to
* struct dirent (through namelist). Returns -1 if there were any errors.
*/
#include "gmid.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
/*
* The DIRSIZ macro is the minimum record length which will hold the directory
* entry. This requires the amount of space in struct dirent without the
* d_name field, plus enough space for the name and a terminating nul byte
* (dp->d_namlen + 1), rounded up to a 4 byte boundary.
*/
#undef DIRSIZ
#define DIRSIZ(dp, namlen) \
((sizeof(struct dirent) - sizeof(dp)->d_name) + \
((namlen + 1 + 3) &~ 3))
int
scandir_fd(int fd, struct dirent ***namelist,
int (*select)(const struct dirent *),
int (*dcomp)(const struct dirent **, const struct dirent **))
{
struct dirent *d, *p, **names = NULL;
size_t arraysz, namlen, nitems = 0;
struct stat stb;
DIR *dirp;
if ((dirp = fdopendir(fd)) == NULL)
return (-1);
if (fstat(fd, &stb) == -1)
goto fail;
/*
* estimate the array size by taking the size of the directory file
* and dividing it by a multiple of the minimum size entry.
*/
arraysz = MAXIMUM(stb.st_size / 24, 16);
if (arraysz > SIZE_MAX / sizeof(struct dirent *)) {
errno = ENOMEM;
goto fail;
}
names = calloc(arraysz, sizeof(struct dirent *));
if (names == NULL)
goto fail;
while ((d = readdir(dirp)) != NULL) {
if (select != NULL && !(*select)(d))
continue; /* just selected names */
/*
* Check to make sure the array has space left and
* realloc the maximum size.
*/
if (nitems >= arraysz) {
struct dirent **nnames;
if (fstat(fd, &stb) == -1)
goto fail;
arraysz *= 2;
if (SIZE_MAX / sizeof(struct dirent *) < arraysz)
goto fail;
nnames = reallocarray(names,
arraysz, sizeof(struct dirent *));
if (nnames == NULL)
goto fail;
names = nnames;
}
/*
* Make a minimum size copy of the data
*/
namlen = strlen(d->d_name);
p = malloc(DIRSIZ(d, namlen));
if (p == NULL)
goto fail;
p->d_ino = d->d_ino;
p->d_type = d->d_type;
memcpy(p->d_name, d->d_name, namlen + 1);
names[nitems++] = p;
}
closedir(dirp);
if (nitems && dcomp != NULL)
qsort(names, nitems, sizeof(struct dirent *),
(int(*)(const void *, const void *))dcomp);
*namelist = names;
return (nitems);
fail:
while (nitems > 0)
free(names[--nitems]);
free(names);
closedir(dirp);
return (-1);
}
int
select_non_dot(const struct dirent *d)
{
return strcmp(d->d_name, ".");
}
int
select_non_dotdot(const struct dirent *d)
{
return strcmp(d->d_name, ".") && strcmp(d->d_name, "..");
}

506
fcgi.c Normal file
View File

@ -0,0 +1,506 @@
/*
* Copyright (c) 2021, 2022, 2023 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "gmid.h"
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include "log.h"
struct fcgi_header {
unsigned char version;
unsigned char type;
unsigned char req_id1;
unsigned char req_id0;
unsigned char content_len1;
unsigned char content_len0;
unsigned char padding;
unsigned char reserved;
};
/*
* number of bytes in a FCGI_HEADER. Future version of the protocol
* will not reduce this number.
*/
#define FCGI_HEADER_LEN 8
/*
* values for the version component
*/
#define FCGI_VERSION_1 1
/*
* values for the type component
*/
#define FCGI_BEGIN_REQUEST 1
#define FCGI_ABORT_REQUEST 2
#define FCGI_END_REQUEST 3
#define FCGI_PARAMS 4
#define FCGI_STDIN 5
#define FCGI_STDOUT 6
#define FCGI_STDERR 7
#define FCGI_DATA 8
#define FCGI_GET_VALUES 9
#define FCGI_GET_VALUES_RESULT 10
#define FCGI_UNKNOWN_TYPE 11
#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)
struct fcgi_begin_req {
unsigned char role1;
unsigned char role0;
unsigned char flags;
unsigned char reserved[5];
};
struct fcgi_begin_req_record {
struct fcgi_header header;
struct fcgi_begin_req body;
};
/*
* mask for flags;
*/
#define FCGI_KEEP_CONN 1
/*
* values for the role
*/
#define FCGI_RESPONDER 1
#define FCGI_AUTHORIZER 2
#define FCGI_FILTER 3
struct fcgi_end_req_body {
unsigned char app_status3;
unsigned char app_status2;
unsigned char app_status1;
unsigned char app_status0;
unsigned char proto_status;
unsigned char reserved[3];
};
/*
* values for proto_status
*/
#define FCGI_REQUEST_COMPLETE 0
#define FCGI_CANT_MPX_CONN 1
#define FCGI_OVERLOADED 2
#define FCGI_UNKNOWN_ROLE 3
/*
* Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT
* records.
*/
#define FCGI_MAX_CONNS "FCGI_MAX_CONNS"
#define FCGI_MAX_REQS "FCGI_MAX_REQS"
#define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS"
static int
prepare_header(struct fcgi_header *h, int type, size_t size,
size_t padding)
{
int id = 1;
memset(h, 0, sizeof(*h));
h->version = FCGI_VERSION_1;
h->type = type;
h->req_id1 = (id >> 8);
h->req_id0 = (id & 0xFF);
h->content_len1 = (size >> 8);
h->content_len0 = (size & 0xFF);
h->padding = padding;
return 0;
}
static int
fcgi_begin_request(struct bufferevent *bev)
{
struct fcgi_begin_req_record r;
memset(&r, 0, sizeof(r));
prepare_header(&r.header, FCGI_BEGIN_REQUEST, sizeof(r.body), 0);
assert(sizeof(r.body) == FCGI_HEADER_LEN);
r.body.role1 = 0;
r.body.role0 = FCGI_RESPONDER;
r.body.flags = FCGI_KEEP_CONN;
if (bufferevent_write(bev, &r, sizeof(r)) == -1)
return -1;
return 0;
}
static int
fcgi_send_param(struct bufferevent *bev, const char *name,
const char *value)
{
struct fcgi_header h;
uint32_t namlen, vallen, padlen;
uint8_t s[8];
size_t size;
const char padding[8] = { 0 };
namlen = strlen(name);
vallen = strlen(value);
size = namlen + vallen + 8; /* 4 for the sizes */
padlen = (8 - (size & 0x7)) & 0x7;
s[0] = ( namlen >> 24) | 0x80;
s[1] = ((namlen >> 16) & 0xFF);
s[2] = ((namlen >> 8) & 0xFF);
s[3] = ( namlen & 0xFF);
s[4] = ( vallen >> 24) | 0x80;
s[5] = ((vallen >> 16) & 0xFF);
s[6] = ((vallen >> 8) & 0xFF);
s[7] = ( vallen & 0xFF);
prepare_header(&h, FCGI_PARAMS, size, padlen);
if (bufferevent_write(bev, &h, sizeof(h)) == -1 ||
bufferevent_write(bev, s, sizeof(s)) == -1 ||
bufferevent_write(bev, name, namlen) == -1 ||
bufferevent_write(bev, value, vallen) == -1 ||
bufferevent_write(bev, padding, padlen) == -1)
return -1;
return 0;
}
static int
fcgi_end_param(struct bufferevent *bev)
{
struct fcgi_header h;
prepare_header(&h, FCGI_PARAMS, 0, 0);
if (bufferevent_write(bev, &h, sizeof(h)) == -1)
return -1;
prepare_header(&h, FCGI_STDIN, 0, 0);
if (bufferevent_write(bev, &h, sizeof(h)) == -1)
return -1;
return 0;
}
static inline int
recid(struct fcgi_header *h)
{
return h->req_id0 + (h->req_id1 << 8);
}
static inline int
reclen(struct fcgi_header *h)
{
return h->content_len0 + (h->content_len1 << 8);
}
static void
fcgi_handle_stdout(struct client *c, struct evbuffer *src, size_t len)
{
struct bufferevent *bev = c->cgibev;
char *t;
size_t l;
int code;
if (c->code == 0) {
l = len;
if (l > sizeof(c->sbuf) - c->soff)
l = sizeof(c->sbuf) - c->soff;
memcpy(&c->sbuf[c->soff], EVBUFFER_DATA(src), l);
c->soff += l;
evbuffer_drain(src, l);
len -= l;
if ((t = memmem(c->sbuf, c->soff, "\r\n", 2)) == NULL) {
if (c->soff == sizeof(c->sbuf)) {
log_warnx("FastCGI application is trying to"
" send a header that's too long.");
fcgi_error(bev, EVBUFFER_ERROR, c);
}
/* wait a bit */
return;
}
*t = '\0';
t += 2; /* skip CRLF */
if (!isdigit((unsigned char)c->sbuf[0]) ||
!isdigit((unsigned char)c->sbuf[1]) ||
c->sbuf[2] != ' ') {
fcgi_error(bev, EVBUFFER_ERROR, c);
return;
}
code = (c->sbuf[0] - '0') * 10 + (c->sbuf[1] - '0');
if (code < 10 || code >= 70) {
log_warnx("FastCGI application is trying to send an"
" invalid reply code: %d", code);
fcgi_error(bev, EVBUFFER_ERROR, c);
return;
}
if (start_reply(c, code, c->sbuf + 3) == -1 ||
c->code < 20 || c->code > 29) {
fcgi_error(bev, EVBUFFER_EOF, c);
return;
}
bufferevent_write(c->bev, t, &c->sbuf[c->soff] - t);
}
bufferevent_write(c->bev, EVBUFFER_DATA(src), len);
evbuffer_drain(src, len);
}
void
fcgi_read(struct bufferevent *bev, void *d)
{
struct client *c = d;
struct evbuffer *src = EVBUFFER_INPUT(bev);
struct fcgi_header hdr;
struct fcgi_end_req_body end;
size_t len;
while (c->type != REQUEST_DONE) {
if (EVBUFFER_LENGTH(src) < sizeof(hdr))
return;
memcpy(&hdr, EVBUFFER_DATA(src), sizeof(hdr));
if (recid(&hdr) != 1) {
log_warnx("got invalid client id %d from fcgi backend",
recid(&hdr));
goto err;
}
len = reclen(&hdr);
if (EVBUFFER_LENGTH(src) < sizeof(hdr) + len + hdr.padding)
return;
evbuffer_drain(src, sizeof(hdr));
switch (hdr.type) {
case FCGI_END_REQUEST:
if (len != sizeof(end)) {
log_warnx("got invalid end request"
" record size");
goto err;
}
bufferevent_read(bev, &end, sizeof(end));
/* TODO: do something with the status? */
c->type = REQUEST_DONE;
break;
case FCGI_STDERR:
/* discard stderr (for now) */
evbuffer_drain(src, len);
break;
case FCGI_STDOUT:
fcgi_handle_stdout(c, src, len);
break;
default:
log_warnx("got invalid fcgi record (type=%d)",
hdr.type);
goto err;
}
evbuffer_drain(src, hdr.padding);
}
err:
fcgi_error(bev, EVBUFFER_ERROR, c);
client_write(c->bev, c);
}
void
fcgi_write(struct bufferevent *bev, void *d)
{
/*
* There's no much use for the write callback.
*/
return;
}
void
fcgi_error(struct bufferevent *bev, short err, void *d)
{
struct client *c = d;
/*
* If we're here it means that some kind of non-recoverable
* error happened.
*
* Don't free bev as we might be called by a function that
* still uses it.
*/
bufferevent_disable(bev, EVBUFFER_READ);
close(c->pfd);
c->pfd = -1;
/* EOF and no header */
if (c->code == 0) {
start_reply(c, CGI_ERROR, "CGI error");
return;
}
c->type = REQUEST_DONE;
}
static void
path_translate(const char *path, struct location *loc, struct location *rloc,
char *buf, size_t len)
{
const char *root, *sufx;
buf[0] = '\0';
if (*loc->dir != '\0')
root = loc->dir;
else if (*rloc->dir != '\0')
root = rloc->dir;
else
return;
sufx = "";
if (*root != '\0')
sufx = root[strlen(root) - 1] == '/' ? "" : "/";
while (*path == '/')
path++;
snprintf(buf, len, "%s%s%s", root, sufx, path);
}
void
fcgi_req(struct client *c, struct location *loc)
{
char buf[22], path[GEMINI_URL_LEN], path_tr[PATH_MAX];
char *scriptname, *qs;
const char *stripped, *port;
size_t l;
time_t tim;
struct tm tminfo;
struct envlist *p;
fcgi_begin_request(c->cgibev);
stripped = strip_path(c->iri.path, loc->fcgi_strip);
if (*stripped != '/')
snprintf(path, sizeof(path), "/%s", stripped);
else
strlcpy(path, stripped, sizeof(path));
port = c->iri.host;
if (port == NULL || *port == '\0')
port = "1965";
scriptname = "";
TAILQ_FOREACH(p, &loc->params, envs) {
if (!strcmp(p->name, "SCRIPT_NAME")) {
scriptname = p->value;
break;
}
}
l = strlen(scriptname);
while (l > 0 && scriptname[l - 1] == '/')
l--;
if (!strncmp(scriptname, path, l) && (path[l] == '/' ||
path[l] == '\0')) {
fcgi_send_param(c->cgibev, "PATH_INFO", &path[l]);
path_translate(&path[l], loc, TAILQ_FIRST(&c->host->locations),
path_tr, sizeof(path_tr));
path[l] = '\0';
fcgi_send_param(c->cgibev, "SCRIPT_NAME", path);
} else {
path_translate(stripped, loc, TAILQ_FIRST(&c->host->locations),
path_tr, sizeof(path_tr));
fcgi_send_param(c->cgibev, "PATH_INFO", stripped);
fcgi_send_param(c->cgibev, "SCRIPT_NAME", scriptname);
}
fcgi_send_param(c->cgibev, "GATEWAY_INTERFACE", "CGI/1.1");
fcgi_send_param(c->cgibev, "PATH_TRANSLATED", path_tr);
fcgi_send_param(c->cgibev, "QUERY_STRING", c->iri.query);
fcgi_send_param(c->cgibev, "REMOTE_ADDR", c->rhost);
fcgi_send_param(c->cgibev, "REMOTE_HOST", c->rhost);
fcgi_send_param(c->cgibev, "REQUEST_METHOD", "GET");
fcgi_send_param(c->cgibev, "SERVER_NAME", c->iri.host);
fcgi_send_param(c->cgibev, "SERVER_PORT", port);
fcgi_send_param(c->cgibev, "SERVER_PROTOCOL", "GEMINI");
fcgi_send_param(c->cgibev, "SERVER_SOFTWARE", GMID_VERSION);
fcgi_send_param(c->cgibev, "GEMINI_URL_PATH", c->iri.path);
if (*c->iri.query != '\0' &&
strchr(c->iri.query, '=') == NULL &&
(qs = strdup(c->iri.query)) != NULL) {
pct_decode_str(qs);
fcgi_send_param(c->cgibev, "GEMINI_SEARCH_STRING", qs);
free(qs);
}
TAILQ_FOREACH(p, &loc->params, envs) {
if (!strcmp(p->name, "SCRIPT_NAME"))
continue;
fcgi_send_param(c->cgibev, p->name, p->value);
}
if (tls_peer_cert_provided(c->ctx)) {
fcgi_send_param(c->cgibev, "AUTH_TYPE", "CERTIFICATE");
fcgi_send_param(c->cgibev, "REMOTE_USER",
tls_peer_cert_subject(c->ctx));
fcgi_send_param(c->cgibev, "TLS_CLIENT_ISSUER",
tls_peer_cert_issuer(c->ctx));
fcgi_send_param(c->cgibev, "TLS_CLIENT_HASH",
tls_peer_cert_hash(c->ctx));
fcgi_send_param(c->cgibev, "TLS_VERSION",
tls_conn_version(c->ctx));
fcgi_send_param(c->cgibev, "TLS_CIPHER",
tls_conn_cipher(c->ctx));
snprintf(buf, sizeof(buf), "%d",
tls_conn_cipher_strength(c->ctx));
fcgi_send_param(c->cgibev, "TLS_CIPHER_STRENGTH", buf);
tim = tls_peer_cert_notbefore(c->ctx);
strftime(buf, sizeof(buf), "%FT%TZ",
gmtime_r(&tim, &tminfo));
fcgi_send_param(c->cgibev, "TLS_CLIENT_NOT_BEFORE", buf);
tim = tls_peer_cert_notafter(c->ctx);
strftime(buf, sizeof(buf), "%FT%TZ",
gmtime_r(&tim, &tminfo));
fcgi_send_param(c->cgibev, "TLS_CLIENT_NOT_AFTER", buf);
} else
fcgi_send_param(c->cgibev, "AUTH_TYPE", "");
if (fcgi_end_param(c->cgibev) == -1)
fcgi_error(c->cgibev, EVBUFFER_ERROR, c);
}

347
ge.c Normal file
View File

@ -0,0 +1,347 @@
/*
* Copyright (c) 2022, 2023 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "gmid.h"
#include <sys/stat.h>
#include <sys/wait.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <locale.h>
#include <libgen.h>
#include <signal.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <vis.h>
#include "log.h"
static int gen_eckey = 1;
int privsep_process;
static const struct option opts[] = {
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'V'},
{NULL, 0, NULL, 0},
};
void
log_request(struct client *c, int code, const char *meta)
{
char b[GEMINI_URL_LEN];
char cntmp[64], cn[64] = "-";
const char *t;
if (c->iri.schema != NULL) {
/* serialize the IRI */
strlcpy(b, c->iri.schema, sizeof(b));
strlcat(b, "://", sizeof(b));
/* log the decoded host name, but if it was invalid
* use the raw one. */
if (*c->domain != '\0')
strlcat(b, c->domain, sizeof(b));
else
strlcat(b, c->iri.host, sizeof(b));
if (*c->iri.path != '/')
strlcat(b, "/", sizeof(b));
strlcat(b, c->iri.path, sizeof(b)); /* TODO: sanitize UTF8 */
if (*c->iri.query != '\0') { /* TODO: sanitize UTF8 */
strlcat(b, "?", sizeof(b));
strlcat(b, c->iri.query, sizeof(b));
}
} else {
if ((t = c->req) == NULL)
t = "";
strlcpy(b, t, sizeof(b));
}
if (tls_peer_cert_provided(c->ctx)) {
const char *subj;
char *n;
subj = tls_peer_cert_subject(c->ctx);
if ((n = strstr(subj, "/CN=")) != NULL) {
strlcpy(cntmp, subj + 4, sizeof(cntmp));
if ((n = strchr(cntmp, '/')) != NULL)
*n = '\0';
strnvis(cn, cntmp, sizeof(cn), VIS_WHITE|VIS_DQ);
}
}
fprintf(stderr, "%s %s %s %s %d %s\n", c->rhost, cn,
*c->domain == '\0' ? c->iri.host : c->domain, b, code, meta);
}
void
load_local_cert(struct vhost *h, const char *hostname, const char *dir)
{
char *cert, *key;
if (asprintf(&cert, "%s/%s.pem", dir, hostname) == -1)
fatal("asprintf");
if (asprintf(&key, "%s/%s.key", dir, hostname) == -1)
fatal("asprintf");
if (access(cert, R_OK) == -1 || access(key, R_OK) == -1)
gencert(hostname, cert, key, gen_eckey);
h->cert = tls_load_file(cert, &h->certlen, NULL);
if (h->cert == NULL)
fatal("can't load %s", cert);
h->key = tls_load_file(key, &h->keylen, NULL);
if (h->key == NULL)
fatal("can't load %s", key);
strlcpy(h->domain, hostname, sizeof(h->domain));
}
/* wrapper around dirname(3). dn must be PATH_MAX+1 at least. */
static void
pdirname(const char *path, char *dn)
{
char p[PATH_MAX+1];
char *t;
strlcpy(p, path, sizeof(p));
t = dirname(p);
memmove(dn, t, strlen(t)+1);
}
static void
mkdirs(const char *path, mode_t mode)
{
char dname[PATH_MAX+1];
pdirname(path, dname);
if (!strcmp(dname, "/"))
return;
mkdirs(dname, mode);
if (mkdir(path, mode) != 0 && errno != EEXIST)
fatal("can't mkdir %s", path);
}
/* $XDG_DATA_HOME/gemexp */
char *
data_dir(void)
{
const char *home, *xdg;
char *t;
if ((xdg = getenv("XDG_DATA_HOME")) == NULL) {
if ((home = getenv("HOME")) == NULL)
fatalx("XDG_DATA_HOME and HOME both empty");
if (asprintf(&t, "%s/.local/share/gemexp", home) == -1)
fatalx("asprintf");
} else {
if (asprintf(&t, "%s/gemexp", xdg) == -1)
fatal("asprintf");
}
mkdirs(t, 0755);
return t;
}
static int
serve(struct conf *conf, const char *host, int port, const char *dir)
{
struct addrinfo hints, *res, *res0;
struct vhost *vh = TAILQ_FIRST(&conf->hosts);
struct address *addr, *acp;
int r, error, saved_errno, sock = -1;
const char *cause = NULL;
char service[32];
int any = 0;
event_init();
r = snprintf(service, sizeof(service), "%d", port);
if (r < 0 || (size_t)r >= sizeof(service))
fatal("snprintf");
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
error = getaddrinfo(host, service, &hints, &res0);
if (error)
fatalx("%s", gai_strerror(error));
for (res = res0; res; res = res->ai_next) {
sock = socket(res->ai_family, res->ai_socktype,
res->ai_protocol);
if (sock == -1) {
cause = "socket";
continue;
}
if (bind(sock, res->ai_addr, res->ai_addrlen) == -1) {
cause = "bind";
saved_errno = errno;
close(sock);
errno = saved_errno;
continue;
}
if (listen(sock, 5) == -1)
fatal("listen");
any = 1;
addr = xcalloc(1, sizeof(*addr));
addr->ai_flags = res->ai_flags;
addr->ai_family = res->ai_family;
addr->ai_socktype = res->ai_socktype;
addr->ai_protocol = res->ai_protocol;
addr->slen = res->ai_addrlen;
memcpy(&addr->ss, res->ai_addr, res->ai_addrlen);
addr->conf = conf;
addr->sock = sock;
event_set(&addr->evsock, addr->sock, EV_READ|EV_PERSIST,
server_accept, addr);
if ((addr->ctx = tls_server()) == NULL)
fatal("tls_server failure");
TAILQ_INSERT_HEAD(&conf->addrs, addr, addrs);
acp = xcalloc(1, sizeof(*acp));
memcpy(acp, addr, sizeof(*acp));
acp->sock = -1;
memset(&acp->evsock, 0, sizeof(acp->evsock));
TAILQ_INSERT_HEAD(&vh->addrs, addr, addrs);
}
if (!any)
fatal("%s", cause);
freeaddrinfo(res0);
server_init(NULL, NULL, NULL);
if (server_configure_done(conf) == -1)
fatalx("server configuration failed");
log_info("serving %s on port %d", dir, port);
event_dispatch();
log_info("quitting");
return 0;
}
static __dead void
usage(void)
{
fprintf(stderr,
"Version: " GEMEXP_STRING "\n"
"Usage: %s [-hRV] [-d certs-dir] [-H hostname] [-p port] [dir]\n",
getprogname());
exit(1);
}
int
main(int argc, char **argv)
{
struct conf *conf;
struct vhost *host;
struct location *loc;
const char *errstr, *certs_dir = NULL, *hostname = "localhost";
char path[PATH_MAX];
int ch, port = 1965;
setlocale(LC_CTYPE, "");
log_init(1, LOG_DAEMON);
log_setverbose(0);
conf = config_new();
/* ge doesn't do privsep so no privsep crypto engine. */
conf->use_privsep_crypto = 0;
while ((ch = getopt_long(argc, argv, "d:H:hp:RV", opts, NULL)) != -1) {
switch (ch) {
case 'd':
certs_dir = optarg;
break;
case 'H':
hostname = optarg;
break;
case 'h':
usage();
break;
case 'p':
port = strtonum(optarg, 0, UINT16_MAX, &errstr);
if (errstr)
fatalx("port number is %s: %s", errstr,
optarg);
break;
case 'R':
gen_eckey = 0;
break;
case 'V':
puts("Version: " GEMEXP_STRING);
return 0;
default:
usage();
break;
}
}
argc -= optind;
argv += optind;
if (argc > 1)
usage();
/* prepare the configuration */
init_mime(&conf->mime);
if (certs_dir == NULL)
certs_dir = data_dir();
/* set up the implicit vhost and location */
host = xcalloc(1, sizeof(*host));
TAILQ_INSERT_HEAD(&conf->hosts, host, vhosts);
loc = xcalloc(1, sizeof(*loc));
loc->fcgi = -1;
TAILQ_INSERT_HEAD(&host->locations, loc, locations);
load_local_cert(host, hostname, certs_dir);
strlcpy(host->domain, "*", sizeof(host->domain));
loc->auto_index = 1;
strlcpy(loc->match, "*", sizeof(loc->match));
if (*argv == NULL) {
if (getcwd(path, sizeof(path)) == NULL)
fatal("getcwd");
strlcpy(loc->dir, path, sizeof(loc->dir));
} else {
char *tmp;
tmp = absolutify_path(*argv);
strlcpy(loc->dir, tmp, sizeof(loc->dir));
free(tmp);
}
/* start the server */
signal(SIGPIPE, SIG_IGN);
setproctitle("%s", loc->dir);
return serve(conf, hostname, port, loc->dir);
}

90
gemexp.1 Normal file
View File

@ -0,0 +1,90 @@
.\" Copyright (c) 2022, 2023 Omar Polo <op@omarpolo.com>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.Dd October 18, 2023
.Dt GEMEXP 1
.Os
.Sh NAME
.Nm gemexp
.Nd export a directory over Gemini
.Sh SYNOPSIS
.Nm
.Bk -words
.Op Fl hRV
.Op Fl d Ar certs-dir
.Op Fl H Ar hostname
.Op Fl p Ar port
.Op Ar directory
.Ek
.Sh DESCRIPTION
.Nm
exports the given
.Ar directory
over the Gemini protocol.
It's intended to be used interactively mostly for testing purposes,
for a full-fledged daemon look for
.Xr gmid 8 .
.Pp
The arguments are as follows:
.Bl -tag -width Ds
.It Fl d Ar certs-path
Directory where certificates are stored.
By default is
.Pa $XDG_DATA_HOME/gemexp ,
i.e.\&
.Pa ~/.local/share/gemexp .
.It Fl H Ar hostname
The
.Ar hostname
to use,
.Ar localhost
by default.
Certificates for the given
.Ar hostname
are searched inside the
.Ar certs-dir
specified with the
.Fl d
option.
The certificate files are named
.Ar hostname Ns .pem
and
.Ar hostname Ns .key
and are implicitly generated if not found.
.It Fl h , Fl -help
Print the usage and exit.
.It Fl p Ar port
The port to bind to, 1965 by default.
.It Fl R
Generate an RSA key instead of an EC one.
.It Fl V , Fl -version
Print the version and exit.
.It Ar directory
The root directory to serve, or the current working directory if not
specified.
.El
.Sh SEE ALSO
.Xr gg 1 ,
.Xr gmid 8
.Sh ACKNOWLEDGEMENTS
.Nm
uses the
.Dq Flexible and Economical
UTF-8 decoder written by
.An Bjoern Hoehrmann .
.Sh AUTHORS
.An -nosplit
The
.Nm
program was written by
.An Omar Polo Aq Mt op@omarpolo.com .

121
gg.1 Normal file
View File

@ -0,0 +1,121 @@
.\" Copyright (c) 2021, 2022 Omar Polo <op@omarpolo.com>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.Dd $Mdocdate: October 19 2023$
.Dt GG 1
.Os
.Sh NAME
.Nm gg
.Nd gemini client
.Sh SYNOPSIS
.Nm
.Bk -words
.Op Fl 23Nnq
.Op Fl C Ar cert
.Op Fl d Ar mode
.Op Fl H Ar sni
.Op Fl K Ar key
.Op Fl P Ar host Ns Oo : Ns Ar port Oc
.Op Fl T Ar seconds
.Ar gemini://...
.Ek
.Sh DESCRIPTION
.Nm
.Pq gemini get
fetches the given gemini page and prints it to standard output.
.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl 2
Use TLSv1.2.
.It Fl 3
Use TLSv1.3.
.It Fl C Ar certificate
Use the given client
.Ar certificate .
.It Fl d Ar mode
Specify what
.Nm
should print.
.Ar mode
can be one of:
.Bl -tag -width header -compact
.It Ic none
print only the body of the reply, the default.
.It Ic code
print only the response code.
.It Ic header
print only the response header.
.It Ic meta
print only the response meta.
.It Ic all
print the whole response as-is.
.El
.It Fl H Ar sni
Use the given
.Ar sni
host name instead of the one deducted by the IRI or proxy.
.It Fl K Ar key
Specify the key for the certificate.
It's mandatory if
.Fl C
is used.
.It Fl N
Disables the server name verification.
.It Fl n
Check the given IRI for validity, but don't issue any requests.
.It Fl P Ar host Ns Oo : Ns Ar port Oc
Connect to the given
.Ar host
and
.Ar port
to do the request instead of the ones extracted by the IRI.
.Ar port
is by default 1965.
.It Fl q
Don't print server error messages to standard error.
.It Fl T Ar seconds
Kill
.Nm
after
.Ar seconds .
.El
.Sh EXIT STATUS
The
.Nm
utility exits with zero if the response code was in the 2x range.
If a failure occurs, it exits with status code 1.
Otherwise, the error code reflects the Gemini response code.
.Sh ACKNOWLEDGEMENTS
.Nm
uses the
.Dq Flexible and Economical
UTF-8 decoder written by
.An Bjoern Hoehrmann .
.Sh SEE ALSO
.Xr ftp 1 ,
.Xr titan 1
.Sh AUTHORS
.An -nosplit
The
.Nm
utility was written by
.An Omar Polo Aq Mt op@omarpolo.com .
.Sh CAVEATS
.Nm
doesn't perform TOFU
.Pq Trust On First Use
or any X.509 certificate validation beyond the name verification.
.Pp
.Nm
doesn't follow redirects.

467
gg.c Normal file
View File

@ -0,0 +1,467 @@
/*
* Copyright (c) 2021-2023 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "gmid.h"
#include <sys/socket.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <locale.h>
#include <string.h>
#include <wchar.h>
enum debug {
DEBUG_NONE,
DEBUG_CODE,
DEBUG_HEADER,
DEBUG_META,
DEBUG_ALL,
};
/* flags */
int debug;
int dont_verify_name;
int flag2;
int flag3;
int nop;
int redirects = 5;
int timer;
int quiet;
const char *cert;
const char *key;
const char *proxy_host;
const char *proxy_port;
const char *sni;
/* state */
struct tls_config *tls_conf;
static void
timeout(int signo)
{
dprintf(2, "%s: timer expired\n", getprogname());
exit(1);
}
static void
load_tls_conf(void)
{
if ((tls_conf = tls_config_new()) == NULL)
err(1, "tls_config_new");
tls_config_insecure_noverifycert(tls_conf);
if (dont_verify_name)
tls_config_insecure_noverifyname(tls_conf);
if (flag2 &&
tls_config_set_protocols(tls_conf, TLS_PROTOCOL_TLSv1_2) == -1)
errx(1, "can't set TLSv1.2");
if (flag3 &&
tls_config_set_protocols(tls_conf, TLS_PROTOCOL_TLSv1_3) == -1)
errx(1, "can't set TLSv1.3");
if (cert != NULL &&
tls_config_set_keypair_file(tls_conf, cert, key) == -1)
errx(1, "can't load client certificate %s", cert);
}
static void
connectto(struct tls *ctx, const char *host, const char *port)
{
struct addrinfo hints, *res, *res0;
int error;
int saved_errno;
int s;
const char *cause = NULL;
const char *sname;
if (proxy_host != NULL) {
host = proxy_host;
port = proxy_port;
}
if ((sname = sni) == NULL)
sname = host;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
error = getaddrinfo(host, port, &hints, &res0);
if (error)
errx(1, "%s", gai_strerror(error));
s = -1;
for (res = res0; res != NULL; res = res->ai_next) {
s = socket(res->ai_family, res->ai_socktype,
res->ai_protocol);
if (s == -1) {
cause = "socket";
continue;
}
if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
cause = "connect";
saved_errno = errno;
close(s);
errno = saved_errno;
s = -1;
continue;
}
break;
}
if (s == -1)
err(1, "%s: can't connect to %s:%s", cause,
host, port);
freeaddrinfo(res0);
if (tls_connect_socket(ctx, s, sname) == -1)
errx(1, "tls_connect_socket: %s", tls_error(ctx));
}
static void
doreq(struct tls *ctx, const char *buf)
{
size_t s;
ssize_t w;
s = strlen(buf);
while (s != 0) {
switch (w = tls_write(ctx, buf, s)) {
case 0:
case -1:
errx(1, "tls_write: %s", tls_error(ctx));
case TLS_WANT_POLLIN:
case TLS_WANT_POLLOUT:
continue;
}
s -= w;
buf += w;
}
}
static size_t
dorep(struct tls *ctx, uint8_t *buf, size_t len)
{
ssize_t w;
size_t tot = 0;
while (len != 0) {
switch (w = tls_read(ctx, buf, len)) {
case 0:
return tot;
case -1:
errx(1, "tls_write: %s", tls_error(ctx));
case TLS_WANT_POLLIN:
case TLS_WANT_POLLOUT:
continue;
}
len -= w;
buf += w;
tot += w;
}
return tot;
}
static void
safeprint(FILE *fp, const char *str)
{
int len;
wchar_t wc;
for (; *str != '\0'; str += len) {
if ((len = mbtowc(&wc, str, MB_CUR_MAX)) == -1) {
mbtowc(NULL, NULL, MB_CUR_MAX);
fputc('?', fp);
len = 1;
} else if (wcwidth(wc) == -1) {
fputc('?', fp);
} else if (wc != L'\n')
putwc(wc, fp);
}
fputc('\n', fp);
}
static int
get(const char *r)
{
struct tls *ctx;
struct iri iri;
int foundhdr = 0, code = -1, od;
char iribuf[GEMINI_URL_LEN];
char req[GEMINI_URL_LEN];
uint8_t buf[2048];
const char *parse_err, *host, *port;
if (strlcpy(iribuf, r, sizeof(iribuf)) >= sizeof(iribuf))
errx(1, "iri too long: %s", r);
if (strlcpy(req, r, sizeof(req)) >= sizeof(req))
errx(1, "iri too long: %s", r);
if (strlcat(req, "\r\n", sizeof(req)) >= sizeof(req))
errx(1, "iri too long: %s", r);
if (!parse_iri(iribuf, &iri, &parse_err))
errx(1, "invalid IRI: %s", parse_err);
if (nop)
errx(0, "IRI OK");
if ((ctx = tls_client()) == NULL)
errx(1, "can't create tls context");
if (tls_configure(ctx, tls_conf) == -1)
errx(1, "tls_configure: %s", tls_error(ctx));
host = iri.host;
port = "1965";
if (*iri.port != '\0')
port = iri.port;
connectto(ctx, host, port);
od = 0;
while (!od) {
switch (tls_handshake(ctx)) {
case 0:
od = 1;
break;
case -1:
errx(1, "handshake: %s", tls_error(ctx));
}
}
doreq(ctx, req);
for (;;) {
uint8_t *t;
size_t len;
len = dorep(ctx, buf, sizeof(buf));
if (len == 0)
break;
if (foundhdr) {
write(1, buf, len);
continue;
}
foundhdr = 1;
if (memmem(buf, len, "\r\n", 2) == NULL)
errx(1, "invalid reply: no \\r\\n");
if (!isdigit((unsigned char)buf[0]) ||
!isdigit((unsigned char)buf[1]) ||
buf[2] != ' ')
errx(1, "invalid reply: invalid response format");
code = (buf[0] - '0') * 10 + buf[1] - '0';
if (debug == DEBUG_CODE) {
printf("%d\n", code);
break;
}
if (debug == DEBUG_HEADER) {
t = memmem(buf, len, "\r\n", 2);
assert(t != NULL);
*t = '\0';
printf("%s\n", buf);
break;
}
if (debug == DEBUG_META) {
t = memmem(buf, len, "\r\n", 2);
assert(t != NULL);
*t = '\0';
printf("%s\n", buf+3);
break;
}
if (debug == DEBUG_ALL) {
write(1, buf, len);
continue;
}
/* skip the header */
t = memmem(buf, len, "\r\n", 2);
assert(t != NULL);
if (code < 20 || code >= 30) {
*t = '\0';
if (!quiet) {
fprintf(stderr, "Server says: ");
/* skip return code */
safeprint(stderr, buf + 3);
}
}
t += 2; /* skip \r\n */
len -= t - buf;
write(1, t, len);
}
for (;;) {
switch (tls_close(ctx)) {
case TLS_WANT_POLLIN:
case TLS_WANT_POLLOUT:
continue;
case -1:
warnx("tls_close: %s", tls_error(ctx));
/* fallthrough */
default:
tls_free(ctx);
return code;
}
}
}
static void __attribute__((noreturn))
usage(void)
{
fprintf(stderr, "version: " GG_STRING "\n");
fprintf(stderr, "usage: %s [-23Nnq] [-C cert] [-d mode] [-H sni] "
"[-K key] [-P host[:port]]\n",
getprogname());
fprintf(stderr, " [-T seconds] gemini://...\n");
exit(1);
}
static int
parse_debug(const char *arg)
{
if (!strcmp(arg, "none"))
return DEBUG_NONE;
if (!strcmp(arg, "code"))
return DEBUG_CODE;
if (!strcmp(arg, "header"))
return DEBUG_HEADER;
if (!strcmp(arg, "meta"))
return DEBUG_META;
if (!strcmp(arg, "all"))
return DEBUG_ALL;
usage();
}
static void
parse_proxy(const char *arg)
{
char *at;
if ((proxy_host = strdup(arg)) == NULL)
err(1, "strdup");
proxy_port = "1965";
if ((at = strchr(proxy_host, ':')) == NULL)
return;
*at = '\0';
proxy_port = ++at;
if (strchr(proxy_port, ':') != NULL)
errx(1, "invalid port %s", proxy_port);
}
int
main(int argc, char **argv)
{
int ch, code;
const char *errstr;
setlocale(LC_CTYPE, "");
while ((ch = getopt(argc, argv, "23C:d:H:K:nNP:qT:")) != -1) {
switch (ch) {
case '2':
flag2 = 1;
break;
case '3':
flag3 = 1;
break;
case 'C':
cert = optarg;
break;
case 'd':
debug = parse_debug(optarg);
break;
case 'H':
sni = optarg;
break;
case 'K':
key = optarg;
break;
case 'N':
dont_verify_name = 1;
break;
case 'n':
nop = 1;
break;
case 'P':
parse_proxy(optarg);
dont_verify_name = 1;
break;
case 'q':
quiet = 1;
break;
case 'T':
timer = strtonum(optarg, 1, 1000, &errstr);
if (errstr != NULL)
errx(1, "timeout is %s: %s",
errstr, optarg);
signal(SIGALRM, timeout);
alarm(timer);
break;
default:
usage();
}
}
argc -= optind;
argv += optind;
if (flag2 + flag3 > 1) {
warnx("only -2 or -3 can be specified at the same time");
usage();
}
if ((cert != NULL && key == NULL) ||
(cert == NULL && key != NULL)) {
warnx("cert or key is missing");
usage();
}
if (argc != 1)
usage();
load_tls_conf();
signal(SIGPIPE, SIG_IGN);
#ifdef __OpenBSD__
if (pledge("stdio inet dns", NULL) == -1)
err(1, "pledge");
#endif
code = get(*argv);
if (code >= 20 && code < 30)
return 0;
return code;
}

103
gmid.1
View File

@ -1,103 +0,0 @@
.\" Copyright (c) 2020 Omar Polo <op@omarpolo.com>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.Dd $Mdocdate: October 2 2020$
.Dt GMIND 1
.Os
.Sh NAME
.Nm gmid
.Nd dead simple zero configuration gemini server
.Sh SYNOPSIS
.Nm
.Bk -words
.Op Fl h
.Op Fl c Ar cert.pem
.Op Fl d Ar docs
.Op Fl k Ar key.pem
.Ek
.Sh DESCRIPTION
.Nm
is a very simple and minimal gemini server.
It only supports serving static content, and strive to be as simple as
possible.
.Pp
.Nm
will strip any sequence of
.Pa ../
or trailing
.Pa ..
in the requests made by clients, so it's impossible to serve content
outside the
.Pa docs
directory by mistake, and will also refuse to follow symlink.
Furthermore, on
.Ox ,
.Xr pledge 2
and
.Xr unveil 2
are used to ensure that
.Nm
dosen't do anything else than read files from the given directory and
accept network connections.
.Pp
It should be noted that
.Nm
is very simple in its implementation, and so it may not be appropriate
for serving site with lots of users.
After all, the code is single threaded and use a single process.
.Pp
If a user request path is a directory,
.Nm
will try to serve a
.Pa index.gmi
file inside that directory.
If not found, it will return an error 51 (not found) to the user.
.Pp
The options are as follows:
.Bl -tag -width 12m
.It Fl c Ar cert.pem
The certificate to use, by default is
.Pa cert.pem .
.It Fl d Ar docs
The root directory to serve.
.Nm
won't serve any file that is outside that directory.
.It Fl h
Print the usage and exit.
.It Fl k Ar key.pem
The key for the certificate, by default is
.Pa key.pem .
.El
.Sh EXAMPLES
To quickly getting started
.Bd -literal -offset indent
$ # generate a cert and a key
$ openssl req -x509 -newkey rsa:4096 -keyout key.pem \\
-out cert.pem -days 365 -nodes
$ mkdir docs
$ cat <<EOF > docs/index.gmi
# Hello world
test paragraph...
EOF
$ gmid -c cert.pem -k key.pem -d docs
.Ed
.Pp
now you can visit gemini://localhost/ with your preferred gemini client.
.Sh CAVEATS
.Bl -bullet
.It
it doesn't support virtual hosts: the host part of the request URL is
completely ignored.
.It
it doesn't fork in the background or anything like that.
.El

127
gmid.8 Normal file
View File

@ -0,0 +1,127 @@
.\" Copyright (c) 2021, 2022, 2023, 2024 Omar Polo <op@omarpolo.com>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.Dd April 27, 2024
.Dt GMID 8
.Os
.Sh NAME
.Nm gmid
.Nd Gemini server
.Sh SYNOPSIS
.Nm
.Bk -words
.Op Fl fhnVv
.Op Fl c Ar config
.Op Fl D Ar macro Ns = Ns Ar value
.Op Fl P Ar pidfile
.Ek
.Sh DESCRIPTION
.Nm
is a simple and minimal gemini server that can serve static files,
talk to FastCGI applications and act as a gemini reverse proxy.
.Pp
.Nm
rereads the configuration file when it receives
.Dv SIGHUP
and reopens log files when it receives
.Dv SIGUSR1 .
.Pp
The options are as follows:
.Bl -tag -width 14m
.It Fl c Ar config
Specifies the configuration file.
The default is
.Pa /etc/gmid.conf .
.It Fl D Ar macro Ns = Ns Ar value
Define
.Ar macro
to be set to
.Ar value
on the command line.
Overrides the definition of
.Ar macro
in the config file if present.
.It Fl f
Do not daemonize.
Stay and log in the foreground.
.It Fl h , Fl -help
Print the usage and exit.
.It Fl n
Check that the configuration is valid, but don't start the server.
If specified two or more time, dump the configuration in addition to
verify it.
.It Fl P Ar pidfile
Write daemon's pid to the given location.
.Ar pidfile
will also act as lock: if another process is holding a lock on that
file,
.Nm
will refuse to start.
.It Fl V , Fl -version
Print the version and exit.
.It Fl v
Verbose mode.
.El
.Sh EXAMPLES
To run
.Nm
a configuration file and a X.509 certificate must be provided.
A self-signed certificate, which are commonly used in the Geminispace,
can be generated using for e.g.\&
.Xr openssl 1 :
.Bd -literal -offset indent
# openssl req \-x509 \-newkey rsa:4096 \-nodes \e
\-keyout /etc/ssl/private/example.com.key \e
\-out /etc/ssl/example.com.pem \e
\-days 365 \-subj "/CN=example.com"
# chmod 600 /etc/ssl/example.com.crt
# chmod 600 /etc/ssl/private/example.com.key
.Ed
.Pp
Then
.Nm
can be started with
.Bd -literal -offset indent
# gmid -c /etc/gmid.conf
.Ed
.Sh SEE ALSO
.Xr gemexp 1 ,
.Xr gg 1 ,
.Xr gmid.conf 5
.Sh ACKNOWLEDGEMENTS
.Nm
uses the
.Dq Flexible and Economical
UTF-8 decoder written by
.An Bjoern Hoehrmann .
.Sh AUTHORS
.An -nosplit
The
.Nm
program was written by
.An Omar Polo Aq Mt op@omarpolo.com .
.Sh CAVEATS
.Bl -bullet
.It
All the root directories are opened during the daemon configuration;
if a root directory is deleted and then re-created,
.Nm
won't be able to serve files inside that directory until a reload.
This restriction only applies to the root directories and not their
content.
.It
a %2F sequence is indistinguishable from a literal slash: this is not
RFC3986-compliant.
.It
a %00 sequence is treated as invalid character and thus rejected.
.El

1167
gmid.c

File diff suppressed because it is too large Load Diff

698
gmid.conf.5 Normal file
View File

@ -0,0 +1,698 @@
.\" Copyright (c) 2022, 2023, 2024 Omar Polo <op@omarpolo.com>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
.\" copyright notice and this permission notice appear in all copies.
.\"
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.Dd April 4, 2024
.Dt GMID.CONF 5
.Os
.Sh NAME
.Nm gmid.conf
.Nd gmid Gemini server configuration file
.Sh DESCRIPTION
.Nm
is the configuration file format for the
.Xr gmid 8
Gemini server.
.Pp
The configuration file is divided into the following sections:
.Bl -tag -width Ds
.It Sy Macros
User-defined variables may be defined and used later, simplifying the
configuration file.
.It Sy Global Options
Global settings for
.Xr gmid 8 .
.It Sy Types
Media types and extensions.
.It Sy Servers
Virtual hosts definition.
.El
.Pp
Within the sections, empty lines are ignored and comments can be put
anywhere in the file using a hash mark
.Pq Sq #
and extend to the end of the current line.
A boolean is either the symbol
.Sq on
or
.Sq off .
A string is a sequence of characters wrapped in double quotes,
.Dq like this .
Multiple strings one next to the other are joined into a single
string:
.Bd -literal -offset indent
# equivalent to "temporary-failure"
block return 40 "temporary" "-" "failure"
.Ed
.Pp
Furthermore, quoting is necessary only when a string needs to contain
special characters
.Pq like spaces or punctuation ,
something that looks like a number or a reserved keyword.
The last example could have been written also as:
.Bd -literal -offset indent
block return 40 temporary "-" failure
.Ed
.Pp
Strict ordering of the sections is not enforced, so that is possible
to mix macros, options and
.Ic server
blocks.
However, defining all the
.Ic server
blocks after the macros and the global options is recommended.
.Pp
Newlines are often optional, except around top-level instructions, and
semicolons
.Dq \&;
can also be optionally used to separate options.
.Pp
Additional configuration files can be included with the
.Ic include
keyword, for example:
.Bd -literal -offset indent
include "/etc/gmid.conf.local"
.Ed
.Ss Macros
Macros can be defined that will later be expanded in context.
Macro names must start with a letter, digit or underscore and may
contain any of those characters.
Macros names may not be reserved words.
Macros are not expanded inside quotes.
.Pp
Two kinds of macros are supported: variable-like and proper macros.
When a macro is invoked with a
.Dq $
before its name its expanded as a string, whereas when it's invoked
with a
.Dq @
its expanded in-place.
.Pp
For example:
.Bd -literal -offset indent
ext_ip = "10.0.0.1"
dir = "/var/gemini"
certdir = "/etc/keys"
common = "lang it; auto index on"
server "foo" {
listen on $ext_ip
root $dir "/foo" # "/var/gemini/foo"
cert $certdir "/foo.pem" # "/etc/keys/foo.pem"
key $certdir "/foo.key" # "/etc/keys/foo.key"
@common
}
.Ed
.Ss Global Options
.Bl -tag -width 12m
.It Ic chroot Ar path
.Xr chroot 2
the process to the given
.Ar path .
The daemon has to be run with root privileges and thus the option
.Ic user
needs to be provided too, so privileges can be dropped afterwards.
All the paths in the configuration file are relative to the chroot
directory, except for the
.Ic cert ,
.Ic key
and
.Ic ocsp
paths.
Defaults to the
.Ic user
home directory, if provided.
.It Ic log Ar options
Specify logging options.
Multiple options may be provided within curly braces.
The available options are as follows:
.Bl -tag -width Ds
.It Ic access Ar file
Log the requests to
.Ar file .
The path is relative to the
.Ic chroot .
.It Ic style Ar style
Set the logging style, defaults to
.Ic legacy .
The
.Ar style
can be one of:
.Bl -tag -width Ds
.It Ic common
Attempt to be compatible with the default Apache httpd log format.
Each line is formatted as follows: the matching host name,
the remote IP address, one dash
.Sq - ,
Common Name of the client certificate
.Pq if provided, '-' otherwise ,
the timestamp of the request, the request URI wrapped in double quotes,
the response code and the size of the response.
.It Ic combined
Attempt to be compatible with the default nginx log format.
Each line is formatted as follows: the remote IP address, one dash
.Sq - ,
Common Name of the client certificate
.Pq if provided, '-' otherwise ,
the timestamp wrapped in square brackets, the request URI wrapped in
double quotes, the response code, the size of the response, a dash
wrapped in double quotes and "".
The strangness of these two last fields is because Gemini doesn't have
the notion of the
.Dq Referer
header nor the
.Dq User-agent .
.\" .It Ic condensed
.\" The native
.\" .Xr gmid 8
.\" format since 2.0.
.\" Each line is formatted as follows: RFC 3339 date time,
.\" remote IP address, Common Name of the client certificate
.\" .Pq if provided, '-' otherwise ,
.\" the matching host name, the request URI, the size of the request,
.\" the size of the response, the response code and meta.
.It Ic legacy
Each line is formatted as follows: the remote IP address and port, the
.Sq GET
keyword, the request URI, the response code and meta.
.El
.It Ic syslog Op Ic off
Log to syslog.
It is enabled by default, use the
.Ic off
argument to disable.
.It Ic syslog facility Ar facility
Log to
.Xr syslog 3
using specified
.Ar facility .
Available facilities are as follows: daemon, ftp, local0 through local7 and
user.
These are case insensitive and can be prefixed with
.Sq LOG_ .
Not all level may be available on all operating systems.
The default facility is
.Ev LOG_DAEMON .
.El
.It Ic prefork Ar number
Run the specified number of server processes.
This increases the performance and prevents delays when connecting to
a server.
.Xr gmid 8
runs 3 server processes by default.
The maximum number allowed is 16.
.It Ic protocols Ar string
Specify the TLS protocols to enable.
Refer to
.Xr tls_config_parse_protocols 3
for the valid protocol string values.
By default, both TLSv1.3 and TLSv1.2 are enabled.
Use
.Dq tlsv1.3
to enable only TLSv1.3.
.It Ic user Ar string
Run the daemon as the given user.
Mandatory if the
.Ic chroot
option is used.
.El
.Ss Servers
Every virtual host is defined by a
.Ic server
block:
.Bl -tag -width Ds
.It Ic server Ar hostname Brq ...
Match the server name using shell globbing rules.
It can be an explicit name,
.Ar www.example.com ,
or a name including wildcards,
.Ar *.example.com .
.El
.Pp
Followed by a block of options that is enclosed in curly brackets:
.Bl -tag -width Ds
.It Ic alias Ar name
Specify an additional alias
.Ar name
for this server.
.It Ic auto Ic index Ar bool
If no index file is found, automatically generate a directory listing.
Disabled by default.
.It Ic block Op Ic return Ar code Op Ar meta
Send a reply and close the connection;
by default
.Ar code
is 40
and
.Ar meta
is
.Dq temporary failure .
If
.Ar code
is in the 3x range, then
.Ar meta
is mandatory.
Inside
.Ar meta ,
the following special sequences are supported:
.Bl -tag -width Ds -compact
.It \&%\&%
is replaced with a single
.Sq \&% .
.It \&%p
is replaced with the request path.
.It \&%q
is replaced with the query string of the request.
.It \&%P
is replaced with the server port.
.It \&%N
is replaced with the server name.
.El
.It Ic cert Ar file
Path to the certificate to use for this server.
.Ar file
should contain a PEM encoded certificate.
This option is mandatory.
.It Ic default type Ar string
Set the default media type that is used if the media type for a
specified extension is not found.
If not specified, the
.Ic default type
is set to
.Dq application/octet-stream .
.It Ic fastcgi Ar option
Enable FastCGI instead of serving files.
Multiple options may be specified within curly braces.
Valid options are:
.Bl -tag -width Ds
.It Ic param Ar name Cm = Ar value
Set the param
.Ar name
to
.Ar value .
.It Ic socket Oo Ic tcp Oc Ar socket Oo Cm port Ar port Oc
The
.Ar socket
can either be a UNIX-domain socket or a TCP socket.
If the FastCGI application is listening on a UNIX domain socket,
.Ar socket
is a local path name within the
.Xr chroot 2
root directory of
.Xr gmid 8 .
Otherwise, the
.Ic tcp
keyword must be provided and
.Ar socket
is interpreted as a hostname or an IP address.
.Ar port
can be either a port number or the name of a service enclosed in
double quotes.
If not specified defaults to 9000.
.It Ic strip Ar number
Strip
.Ar number
leading path components from the request URL before splitting it in
.Ev SCRIPT_NAME
and
.Ev PATH_INFO .
.El
.Pp
The FastCGI handler will be given the following variables by default:
.Bl -tag -width 24m
.\" .It Ev GEMINI_DOCUMENT_ROOT
.\" The root directory of the virtual host.
.It Ev GEMINI_URL_PATH
Full path of the request.
.It Ev GEMINI_SEARCH_STRING
The decoded
.Ev QUERY_STRING
if defined in the request and if it doesn't contain any unencoded
.Sq =
characters, otherwise unset.
.It Ev GATEWAY_INTERFACE
.Dq CGI/1.1
.It Ev AUTH_TYPE
The string "Certificate" if the client used a certificate, otherwise
unset.
.It Ev PATH_INFO
The portion of the requested path that is derived from the the IRI
path hierarchy following
.Ev SCRIPT_NAME .
Can be unset.
.It Ev PATH_TRANSLATED
Present if and only if
.Ev PATH_INFO
is set.
It represent the translation of the
.Ev PATH_INFO .
.Nm gmid
builds this by appending the
.Ev PATH_INFO
to the virtual host directory root.
.It Ev QUERY_STRING
The URL-encoded search or parameter string.
.It Ev REMOTE_ADDR , Ev REMOTE_HOST
Textual representation of the client IP.
.It Ev REQUEST_METHOD
This is present only for RFC3875 (CGI) compliance.
It's always set to
.Dq GET .
.It Ev SCRIPT_NAME
The virtual URI path to the script.
Since it's impossible to determine in all cases the correct
.Ev SCRIPT_NAME
programmatically
.Nm gmid
assumes it's the empty string.
It is recommended to manually specify this parameter when serving a
sub-tree of a virtual host via FastCGI.
.It Ev SERVER_NAME
The name of the server
.It Ev SERVER_PORT
The port the server is listening on.
.It Ev SERVER_PROTOCOL
.Dq GEMINI
.It Ev SERVER_SOFTWARE
The name and version of the server, i.e.
.Dq gmid/2.0.2
.It Ev REMOTE_USER
The subject of the client certificate if provided, otherwise unset.
.It Ev TLS_CLIENT_ISSUER
The is the issuer of the client certificate if provided, otherwise
unset.
.It Ev TLS_CLIENT_HASH
The hash of the client certificate if provided, otherwise unset.
The format is
.Dq ALGO:HASH .
.It Ev TLS_VERSION
The TLS version negotiated with the peer.
.It Ev TLS_CIPHER
The cipher suite negotiated with the peer.
.It Ev TLS_CIPHER_STRENGTH
The strength in bits for the symmetric cipher that is being used with
the peer.
.It Ev TLS_CLIENT_NOT_AFTER
The time corresponding to the end of the validity period of the peer
certificate in the ISO 8601 format
.Pq e.g. Dq 2021-02-07T20:17:41Z .
.It Ev TLS_CLIENT_NOT_BEFORE
The time corresponding to the start of the validity period of the peer
certificate in the ISO 8601 format.
.El
.It Ic fastcgi off
Disable FastCGI handling in the current location.
.It Ic index Ar string
Set the directory index file.
If not specified, it defaults to
.Pa index.gmi .
.It Ic key Ar file
Specify the private key to use for this server.
.Ar file
should contain a PEM encoded private key.
This option is mandatory.
.It Ic lang Ar string
Specify the language tag for the text/gemini content served.
If not specified, no
.Dq lang
parameter will be added in the response.
.It Ic listen on Ar address Op Ic port Ar number
Set the listen
.Ar address
and
.Ar port
which defaults to
.Sq 1965 .
This statement can be specified multiple times.
If
.Ar address
is
.Sq *
then
.Xr gmid 8
will listen on all IPv4 and IPv6 addresses.
.Ar 0.0.0.0
can be used to listen on all IPv4 addresses and
.Ar ::
on all IPv6 addresses.
.It Ic location Ar path Brq ...
Specify server configuration rules for a specific location.
.Ar path
argument will be matched against the request path with shell globbing
rules.
In case of multiple location statements in the same context, the first
matching location will be put into effect and the later ones ignored.
Therefore is advisable to match for more specific paths first and for
generic ones later on.
A
.Ic location
section may include most of the server configuration rules
except
.Ic alias , Ic cert , Ic key , Ic listen , Ic location
and
.Ic proxy .
.It Ic log Ar bool
Enable or disable the logging for the current server or location block.
.It Ic ocsp Ar file
Specify an OCSP response to be stapled during TLS handshakes
with this server.
The
.Ar file
should contain a DER-format OCSP response retrieved from an
OCSP server for the
.Ic cert
in use.
If the OCSP response in
.Ar file
is empty, OCSP stapling will not be used.
The default is to not use OCSP stapling.
.It Ic proxy Oo Cm proto Ar name Oc Oo Cm for-host Ar host Oo Cm port Ar port Oc Oc Brq ...
Set up a reverse proxy.
The optional matching rules
.Cm proto
and
.Cm for-host
can be used to enable proxying only for protocols matching
.Ar name
.Po Dq gemini
by default
.Pc
and/or whose request IRI matches
.Ar host
and
.Ar port
.Pq 1965 by default .
Matching happens using shell globbing rules.
.Pp
In case of multiple matching proxy blocks in the same context, the
first matching proxy will be put into effect and the later ones
ignored.
.Pp
Valid options are:
.Bl -tag -width Ds
.It Ic cert Ar file
Specify the client certificate to use when making requests.
.It Ic key Ar file
Specify the client certificate key to use when making requests.
.It Ic protocols Ar string
Specify the TLS protocols allowed when making remote requests.
Refer to the
.Xr tls_config_parse_protocols 3
function for the valid protocol string values.
By default, both TLSv1.2 and TLSv1.3 are enabled.
.It Ic relay-to Ar host Op Cm port Ar port
Relay the request to the given
.Ar host
at the given
.Ar port ,
1965 by default.
This is the only mandatory option in a
.Ic proxy
block.
.It Ic require Ic client Ic ca Ar file
Allow the proxying only from clients that provide a certificate
signed by the CA certificate in
.Ar file .
.It Ic sni Ar hostname
Use the given
.Ar hostname
instead of the one extracted from the
.Ic relay-to
rule for the TLS handshake with the proxied gemini server.
.It Ic use-tls Ar bool
Specify whether to use TLS when connecting to the proxied host.
Enabled by default.
.It Ic verifyname Ar bool
Enable or disable the TLS server name verification.
Enabled by default.
.El
.It Ic root Ar directory
Specify the root directory for this server
.Pq alas the current Dq document root .
It's relative to the chroot if enabled.
.It Ic require Ic client Ic ca Ar path
Allow requests only from clients that provide a certificate signed by
the CA certificate in
.Ar path .
It needs to be a PEM-encoded certificate and it's not relative to the
chroot.
.It Ic strip Ar number
Strip
.Ar number
components from the beginning of the path before doing a lookup in the
root directory.
It's also considered for the
.Ar meta
parameter in the scope of a
.Ic block return .
.El
.Ss Types
The
.Ic types
section must include one or more lines of the following syntax, enclosed
in curly brances:
.Bl -tag -width Ds
.It Ar type Ns / Ns Ar subtype Ar name Op Ar name ...
Set the media
.Ar type
and
.Ar subtype
to the specified extension
.Ar name .
One or more names can be specified per line.
Earch line may end with an optional semicolon.
.It Ic include Ar file
Include types definition from an external file, for example
.Pa /usr/share/misc/mime.types .
.El
.Pp
By default
.Nm gmid
uses the following mapping if no
.Ic types
block is defined:
.Pp
.Bl -tag -offset indent -width 15m -compact
.It application/pdf
pdf
.It image/gif
gif
.It image/jpeg
jpg jpeg
.It image/png
png
.It image/svg+xml
svg
.It text/gemini
gemini gmi
.It text/markdown
markdown md
.It text/x-patch
diff patch
.It text/xml
xml
.El
.Pp
As an exception,
.Nm gmid
uses the MIME type
.Ar text/gemini
for file extensions
.Ar gemini
or
.Ar gmi
if no mapping was found.
.Sh EXAMPLES
The following is an example of a possible configuration for a site
that enables only TLSv1.3, adds the MIME types mapping from
.Pa /usr/share/misc/mime.types
and defines two virtual host:
.Bd -literal -offset indent
protocols "tlsv1.3"
types {
include "/usr/share/misc/mime.types"
}
server "example.com" {
listen on * port 1965
cert "/etc/ssl/example.com.pem"
key "/etc/ssl/private/example.com.key"
root "/var/gemini/example.com"
}
server "example.it" {
listen on * port 1965
cert "/etc/ssl/example.it.pem"
key "/etc/ssl/private/example.it.key"
root "/var/gemini/example.it"
# set the language for text/gemini files
lang "it"
}
.Ed
.Pp
Yet another example, showing how to enable a
.Ic chroot
and use
.Ic location
rule
.Bd -literal -offset indent
chroot "/var/gemini"
user "_gmid"
server "example.com" {
listen on * port 1965
# absolute paths:
cert "/etc/ssl/example.com.pem"
key "/etc/ssl/private/example.com.key"
# relative to the chroot:
root "/example.com"
location "/static/*" {
# load the following rules only for
# requests that matches "/static/*"
auto index on
index "index.gemini"
}
}
.Ed
.Pp
This shows how to set up a reverse proxy: all request for
.Sq example.com
will be forwarded to 10.0.0.6 transparently.
Proxying establish a new TLS connection, so any client-certificates used
to connect to
.Xr gmid 8
cannot be provided to the proxied server as well.
.Bd -literal -offset indent
server "example.com" {
listen on * port 1965
cert "/etc/ssl/example.com.pem"
key "/etc/ssl/private/example.com.key"
proxy {
relay-to 10.0.0.6 port 1965
}
}
.Ed
.Sh SEE ALSO
.Xr gmid 8 ,
.Xr slowcgi 8
.Sh AUTHORS
.An -nosplit
The
.Nm gmid
program was written by
.An Omar Polo Aq Mt op@omarpolo.com .

473
gmid.h Normal file
View File

@ -0,0 +1,473 @@
/*
* Copyright (c) 2020, 2021, 2022, 2023 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef GMID_H
#define GMID_H
#include "config.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <dirent.h>
#include <limits.h>
#include <netdb.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <tls.h>
#include <unistd.h>
#include <openssl/x509.h>
#if HAVE_EVENT2
# include <event2/event.h>
# include <event2/event_compat.h>
# include <event2/event_struct.h>
# include <event2/buffer.h>
# include <event2/buffer_compat.h>
# include <event2/bufferevent.h>
# include <event2/bufferevent_struct.h>
# include <event2/bufferevent_compat.h>
#else
# include <event.h>
#endif
#include "iri.h"
#define VERSION_STR(n) n " " VERSION
#define GEMEXP_STRING VERSION_STR("gemexp")
#define GG_STRING VERSION_STR("gg")
#define GMID_STRING VERSION_STR("gmid")
#define GMID_VERSION "gmid/" VERSION
#define GEMINI_URL_LEN (1024+3) /* URL max len + \r\n + \0 */
#define SUCCESS 20
#define TEMP_REDIRECT 30
#define TEMP_FAILURE 40
#define CGI_ERROR 42
#define PROXY_ERROR 43
#define NOT_FOUND 51
#define PROXY_REFUSED 53
#define BAD_REQUEST 59
#define CLIENT_CERT_REQ 60
#define CERT_NOT_AUTH 61
/* maximum hostname and label length, +1 for the NUL-terminator */
#define DOMAIN_NAME_LEN (253+1)
#define LABEL_LEN (63+1)
#define MEDIATYPE_NAMEMAX 128 /* file name extension */
#define MEDIATYPE_TYPEMAX 128 /* length of type/subtype */
#define FCGI_NAME_MAX 511
#define FCGI_VAL_MAX 511
#define PROC_MAX_INSTANCES 16
#define TLS_CERT_HASH_SIZE 128
/* forward declaration */
struct privsep;
struct privsep_proc;
enum log_format {
LOG_FORMAT_CONDENSED,
LOG_FORMAT_COMMON,
LOG_FORMAT_COMBINED,
LOG_FORMAT_LEGACY,
};
struct parser {
char *iri;
struct iri *parsed;
const char *err;
};
struct conf;
TAILQ_HEAD(addrhead, address);
struct address {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
struct sockaddr_storage ss;
socklen_t slen;
int16_t port;
/* pretty-printed version of `ss' */
char pp[NI_MAXHOST];
/* used in the server */
struct conf *conf;
int sock;
struct event evsock; /* set if sock != -1 */
struct tls *ctx;
TAILQ_ENTRY(address) addrs;
};
TAILQ_HEAD(fcgihead, fcgi);
struct fcgi {
int id;
char path[PATH_MAX];
char port[32];
TAILQ_ENTRY(fcgi) fcgi;
};
TAILQ_HEAD(envhead, envlist);
struct envlist {
char name[FCGI_NAME_MAX];
char value[FCGI_VAL_MAX];
TAILQ_ENTRY(envlist) envs;
};
TAILQ_HEAD(aliashead, alist);
struct alist {
char alias[HOST_NAME_MAX + 1];
TAILQ_ENTRY(alist) aliases;
};
TAILQ_HEAD(proxyhead, proxy);
struct proxy {
char match_proto[32];
char match_host[HOST_NAME_MAX + 1];
char match_port[32];
char host[HOST_NAME_MAX + 1];
char port[32];
char sni[HOST_NAME_MAX];
int notls;
uint32_t protocols;
int noverifyname;
char *cert_path;
uint8_t *cert;
size_t certlen;
char *key_path;
uint8_t *key;
size_t keylen;
char *reqca_path;
X509_STORE *reqca;
TAILQ_ENTRY(proxy) proxies;
};
TAILQ_HEAD(lochead, location);
struct location {
char match[128];
char lang[32];
char default_mime[MEDIATYPE_TYPEMAX];
char index[PATH_MAX];
int auto_index; /* 0 auto, -1 off, 1 on */
int block_code;
char block_fmt[GEMINI_URL_LEN];
int strip;
char *reqca_path;
X509_STORE *reqca;
int disable_log;
int fcgi;
int nofcgi;
int fcgi_strip;
struct envhead params;
char dir[PATH_MAX];
int dirfd;
TAILQ_ENTRY(location) locations;
};
TAILQ_HEAD(vhosthead, vhost);
struct vhost {
char domain[HOST_NAME_MAX + 1];
char *cert_path;
char *key_path;
char *ocsp_path;
uint8_t *cert;
size_t certlen;
uint8_t *key;
size_t keylen;
uint8_t *ocsp;
size_t ocsplen;
TAILQ_ENTRY(vhost) vhosts;
struct addrhead addrs;
/*
* the first location rule is always '*' and holds the default
* settings for the vhost, then follows the "real" location
* rules as specified in the configuration.
*/
struct lochead locations;
struct aliashead aliases;
struct proxyhead proxies;
};
struct etm { /* extension to mime */
char mime[MEDIATYPE_TYPEMAX];
char ext[MEDIATYPE_NAMEMAX];
};
struct mime {
struct etm *t;
size_t len;
size_t cap;
};
TAILQ_HEAD(pkihead, pki);
struct pki {
char *hash;
EVP_PKEY *pkey;
TAILQ_ENTRY(pki) pkis;
};
struct conf {
struct privsep *ps;
uint32_t protos;
struct mime mime;
char chroot[PATH_MAX];
char user[LOGIN_NAME_MAX];
int prefork;
int reload;
int log_syslog;
int log_facility;
char *log_access;
enum log_format log_format;
int use_privsep_crypto;
int conftest;
struct fcgihead fcgi;
struct vhosthead hosts;
struct pkihead pkis;
struct addrhead addrs;
};
extern const char *config_path;
extern int servpipes[PROC_MAX_INSTANCES];
extern int privsep_process;
typedef void (imsg_handlerfn)(struct imsgbuf*, struct imsg*, size_t);
enum {
REQUEST_UNDECIDED,
REQUEST_FILE,
REQUEST_DIR,
REQUEST_FCGI,
REQUEST_PROXY,
REQUEST_DONE,
};
struct client {
struct conf *conf;
struct address *addr;
uint32_t id;
struct tls *ctx;
char *req;
size_t reqlen;
struct iri iri;
char domain[DOMAIN_NAME_LEN];
char rhost[NI_MAXHOST];
char rserv[NI_MAXSERV];
struct bufferevent *bev;
int type;
struct bufferevent *cgibev;
struct proxy *proxy;
struct bufferevent *proxybev;
struct tls *proxyctx;
int proxyevset;
struct event proxyev;
char *header;
int code;
const char *meta;
int fd, pfd;
struct dirent **dir;
int dirlen, diroff;
/* big enough to store STATUS + SPACE + META + CRLF */
char sbuf[1029];
size_t soff;
struct sockaddr_storage raddr;
socklen_t raddrlen;
struct vhost *host; /* host they're talking to */
size_t loc; /* location matched */
SPLAY_ENTRY(client) entry;
};
SPLAY_HEAD(client_tree_id, client);
extern struct client_tree_id clients;
struct connreq {
char host[NI_MAXHOST];
char port[NI_MAXSERV];
int flag;
};
enum imsg_type {
IMSG_LOG_REQUEST,
IMSG_LOG_ACCESS,
IMSG_LOG_SYSLOG,
IMSG_LOG_FACILITY,
IMSG_RECONF_START,
IMSG_RECONF_LOG_FMT,
IMSG_RECONF_MIME,
IMSG_RECONF_PROTOS,
IMSG_RECONF_SOCK,
IMSG_RECONF_FCGI,
IMSG_RECONF_HOST,
IMSG_RECONF_CERT,
IMSG_RECONF_KEY,
IMSG_RECONF_OCSP,
IMSG_RECONF_HOST_ADDR,
IMSG_RECONF_LOC,
IMSG_RECONF_ENV,
IMSG_RECONF_ALIAS,
IMSG_RECONF_PROXY,
IMSG_RECONF_PROXY_CERT,
IMSG_RECONF_PROXY_KEY,
IMSG_RECONF_END,
IMSG_RECONF_DONE,
IMSG_CRYPTO_RSA_PRIVENC,
IMSG_CRYPTO_RSA_PRIVDEC,
IMSG_CRYPTO_ECDSA_SIGN,
IMSG_CTL_PROCFD,
};
/* gmid.c */
char *data_dir(void);
void load_local_cert(struct vhost*, const char*, const char*);
/* gmid.c / ge.c */
void log_request(struct client *, int, const char *);
/* config.c */
struct conf *config_new(void);
void config_purge(struct conf *);
int config_send(struct conf *);
int config_recv(struct conf *, struct imsg *);
int config_test(struct conf *);
/* crypto.c */
void crypto(struct privsep *, struct privsep_proc *);
void crypto_engine_init(struct conf *);
/* parse.y */
void yyerror(const char*, ...);
int parse_conf(struct conf *, const char*);
int cmdline_symset(char *);
/* mime.c */
void init_mime(struct mime*);
int add_mime(struct mime*, const char*, const char*);
int load_default_mime(struct mime*);
void sort_mime(struct mime *);
const char *mime(struct conf *, struct vhost*, const char*);
void free_mime(struct mime *);
/* server.c */
extern int shutting_down;
const char *vhost_lang(struct vhost*, const char*);
const char *vhost_default_mime(struct vhost*, const char*);
const char *vhost_index(struct vhost*, const char*);
int vhost_auto_index(struct vhost*, const char*);
int vhost_block_return(struct vhost*, const char*, int*, const char**);
struct location *vhost_fastcgi(struct vhost*, const char*);
int vhost_dirfd(struct vhost*, const char*, size_t*);
int vhost_strip(struct vhost*, const char*);
X509_STORE *vhost_require_ca(struct vhost*, const char*);
int vhost_disable_log(struct vhost*, const char*);
void mark_nonblock(int);
void client_write(struct bufferevent *, void *);
int start_reply(struct client*, int, const char*);
void client_close(struct client *);
void server_accept(int, short, void *);
void server_init(struct privsep *, struct privsep_proc *, void *);
int server_configure_done(struct conf *);
void server(struct privsep *ps, struct privsep_proc *);
int client_tree_cmp(struct client *, struct client *);
SPLAY_PROTOTYPE(client_tree_id, client, entry, client_tree_cmp);
/* dirs.c */
int scandir_fd(int, struct dirent***, int(*)(const struct dirent*),
int(*)(const struct dirent**, const struct dirent**));
int select_non_dot(const struct dirent*);
int select_non_dotdot(const struct dirent*);
/* fcgi.c */
void fcgi_read(struct bufferevent *, void *);
void fcgi_write(struct bufferevent *, void *);
void fcgi_error(struct bufferevent *, short, void *);
void fcgi_req(struct client *, struct location *);
/* sandbox.c */
void sandbox_main_process(void);
void sandbox_server_process(void);
void sandbox_crypto_process(void);
void sandbox_logger_process(void);
/* utf8.c */
int valid_multibyte_utf8(struct parser*);
char *utf8_nth(char*, size_t);
/* logger.c */
void logger(struct privsep *, struct privsep_proc *);
/* proxy.c */
int proxy_init(struct client *);
/* puny.c */
int puny_decode(const char*, char*, size_t, const char**);
/* utils.c */
const char *strip_path(const char *, int);
int ends_with(const char*, const char*);
char *absolutify_path(const char*);
char *xstrdup(const char*);
void *xcalloc(size_t, size_t);
void gencert(const char *, const char *, const char *, int);
X509_STORE *load_ca(uint8_t *, size_t);
int validate_against_ca(X509_STORE*, const uint8_t*, size_t);
void ssl_error(const char *);
char *ssl_pubkey_hash(const uint8_t *, size_t);
EVP_PKEY *ssl_load_pkey(const uint8_t *, size_t);
struct vhost *new_vhost(void);
struct location *new_location(void);
struct proxy *new_proxy(void);
#endif

26
have/ASN1_time_parse.c Normal file
View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2023 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdlib.h>
#include <openssl/asn1.h>
int
main(void)
{
return ASN1_time_parse("", 0, NULL, 0);
}

View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2023 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdlib.h>
#include <openssl/asn1.h>
struct tm;
int ASN1_time_tm_clamp_notafter(struct tm *tm);
int
main(void)
{
return ASN1_time_tm_clamp_notafter(NULL);
}

26
have/ASN1_time_tm_cmp.c Normal file
View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2023 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdlib.h>
#include <openssl/asn1.h>
int
main(void)
{
return ASN1_time_tm_cmp(NULL, NULL);
}

53
have/Makefile Normal file
View File

@ -0,0 +1,53 @@
DISTFILES = ASN1_time_parse.c \
ASN1_time_tm_clamp_notafter.c \
ASN1_time_tm_cmp.c \
Makefile \
SSL_CTX_load_verify_mem.c \
SSL_CTX_use_certificate_chain_mem.c \
X509_LOOKUP_mem.c \
arc4random.c \
arc4random_buf.c \
endian_h.c \
err.c \
explicit_bzero.c \
freezero.c \
getdtablecount.c \
getdtablesize.c \
getentropy.c \
getprogname.c \
imsg.c \
landlock.c \
libevent.c \
libevent2.c \
libtls.c \
machine_endian.c \
memmem.c \
noop.c \
openssl.c \
pr_set_name.c \
program_invocation_short_name.c \
queue_h.c \
reallocarray.c \
recallocarray.c \
setproctitle.c \
setresgid.c \
setresuid.c \
strlcat.c \
strlcpy.c \
strtonum.c \
sys_endian_h.c \
timingsafe_memcmp.c \
tree_h.c \
vasprintf.c \
vis.c \
wait_any.c
all:
false
dist: ${DISTFILES}
mkdir -p ${DESTDIR}/
${INSTALL} -m 0644 ${DISTFILES} ${DESTDIR}/
.PHONY: all dist
include ../config.mk

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2023 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdio.h>
#include <openssl/ssl.h>
int
main(void)
{
return SSL_CTX_load_verify_mem(NULL, NULL, 0);
}

View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2023 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdio.h>
#include <openssl/ssl.h>
int
main(void)
{
return SSL_CTX_use_certificate_chain_mem(NULL, NULL, 0);
}

24
have/X509_LOOKUP_mem.c Normal file
View File

@ -0,0 +1,24 @@
/*
* Copyright (c) 2023 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdio.h>
#include <openssl/x509_vfy.h>
int
main(void)
{
return X509_LOOKUP_mem() != NULL;
}

23
have/arc4random.c Normal file
View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2023 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdlib.h>
int
main(void)
{
return arc4random();
}

26
have/arc4random_buf.c Normal file
View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2023 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdlib.h>
int
main(void)
{
char buf[128];
arc4random_buf(buf, sizeof(buf));
return 0;
}

27
have/endian_h.c Normal file
View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 2023 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <endian.h>
#include <stdint.h>
int
main(void)
{
uint16_t x;
x = 42;
return (htobe16(x));
}

26
have/err.c Normal file
View File

@ -0,0 +1,26 @@
/*
* Copyright (c) 2021 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <err.h>
int
main(void)
{
warnx("%d. warnx", 1);
warn("%d. warn", 2);
err(0, "%d. err", 3);
return 1;
}

10
have/explicit_bzero.c Normal file
View File

@ -0,0 +1,10 @@
#include <string.h>
int
main(void)
{
char buf[] = "hello world";
explicit_bzero(buf, sizeof(buf));
return strcmp(buf, "");
}

23
have/freezero.c Normal file
View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2021 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdlib.h>
int
main(void)
{
freezero(NULL, 0);
}

23
have/getdtablecount.c Normal file
View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2021 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <unistd.h>
int
main(void)
{
return getdtablecount() == 0;
}

23
have/getdtablesize.c Normal file
View File

@ -0,0 +1,23 @@
/*
* Copyright (c) 2021 Omar Polo <op@omarpolo.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <unistd.h>
int
main(void)
{
return getdtablesize() == 0;
}

Some files were not shown because too many files have changed in this diff Show More