Compare commits

...

433 Commits
1.8 ... master

Author SHA1 Message Date
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
119 changed files with 17003 additions and 4976 deletions

View File

@ -1,26 +1,38 @@
# 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.
linux_amd64_task:
container:
image: alpine:latest
test_script:
- apk add alpine-sdk linux-headers bison libretls-dev libevent-dev
- ./configure
- ./configure CFLAGS='-O2 -pipe -Wno-deprecated-declarations -Wno-use-after-free' -Werror
- make
- make regress
- make regress REGRESS_HOST="*"
linux_arm_task:
arm_container:
image: alpine:latest
test_script:
- apk add alpine-sdk linux-headers bison libretls-dev libevent-dev
- ./configure
- ./configure CFLAGS='-O2 -pipe -Wno-deprecated-declarations -Wno-use-after-free' -Werror
- make
- make regress REGRESS_HOST="*"
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
freebsd_13_task:
freebsd_instance:
image_family: freebsd-13-0
mac_task:
macos_instance:
image: ghcr.io/cirruslabs/macos-sonoma-xcode:latest
test_script:
- pkg install -y libevent libressl pkgconf
- ./configure
- brew install libevent openssl libretls
- PKG_CONFIG_PATH="$(brew --prefix openssl)/lib/pkgconfig" ./configure CFLAGS='-O2 -pipe -Wno-deprecated-declarations' -Werror
- make
- make regress
- SKIP_RUNTIME_TESTS=1 make regress

View File

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

15
.gitignore vendored
View File

@ -3,25 +3,23 @@
TAGS
gmid
gg
*.d
*.o
gemexp
titan
**/*.[do]
*.swp
have/*.d
compat/*.d
compat/*.o
docs
y.tab.*
Makefile.local
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/*.d
regress/*.pem
regress/*.key
regress/*.crt
@ -29,11 +27,10 @@ regress/*.csr
regress/*.srl
regress/reg.conf
regress/fcgi-test
regress/fcgi.sock
regress/fill-file
regress/iri_test
regress/puny-test
regress/*.o
regress/gg
regress/gmid.pid
site/gemini

143
ChangeLog
View File

@ -1,5 +1,148 @@
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.)

273
Makefile
View File

@ -18,214 +18,173 @@
# all.
TESTS=
TESTSRCS = have/err.c \
have/explicit_bzero.c \
have/freezero.c \
have/getdtablecount.c \
have/getdtablesize.c \
have/getprogname.c \
have/imsg.c \
have/landlock.c \
have/libevent.c \
have/libevent2.c \
have/libtls.c \
have/noop.c \
have/openssl.c \
have/pr_set_name.c \
have/program_invocation_short_name.c \
have/queue_h.c \
have/reallocarray.c \
have/recallocarray.c \
have/setproctitle.c \
have/strlcat.c \
have/strlcpy.c \
have/strtonum.c \
have/tree_h.c \
have/vasprintf.c
# host to bind to during regress
REGRESS_HOST = localhost
COMPATS = compat/err.c \
compat/explicit_bzero.c \
compat/freezero.c \
compat/getdtablecount.c \
compat/getdtablesize.c \
compat/getprogname.c \
compat/imsg-buffer.c \
compat/imsg.c \
compat/imsg.h \
compat/queue.h \
compat/reallocarray.c \
compat/recallocarray.c \
compat/setproctitle.c \
compat/strlcat.c \
compat/strlcpy.c \
compat/strtonum.c \
compat/tree.h \
compat/vasprintf.c
# -- build-related variables --
GMID_SRCS = dirs.c \
ex.c \
fcgi.c \
gmid.c \
iri.c \
log.c \
mime.c \
proxy.c \
puny.c \
sandbox.c \
server.c \
utf8.c \
utils.c \
y.tab.c \
COBJS = ${COMPATS:.c=.o}
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}
GG_SRCS = gg.c \
iri.c \
utf8.c
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}
SRCS = gmid.h \
landlock_shim.h \
parse.y \
${GMID_SRCS} \
${GG_SRCS}
TITAN_SRCS = titan.c iri.c log.c utf8.c
TITAN_OBJS = ${TITAN_SRCS:.c=.o} ${COBJS}
REGRESSFILES = regress/Makefile \
regress/env \
regress/err \
regress/example.mime.types \
regress/fcgi-test.c \
regress/fill-file.c \
regress/hello \
regress/invalid \
regress/iri_test.c \
regress/lib.sh \
regress/max-length-reply \
regress/puny-test.c \
regress/regress \
regress/serve-bigfile \
regress/sha \
regress/slow \
regress/tests.sh \
regress/valid.ext
EXTRAS = ChangeLog \
LICENSE \
Makefile \
Makefile.depend \
README.md \
configure \
configure.local.example \
gg.1 \
gmid.1 \
gmid.conf.5
CONTRIB = contrib/Dockerfile \
contrib/gencert \
contrib/gmid.service \
contrib/gmid.sysusers \
contrib/mime.types \
contrib/README \
contrib/renew-certs \
contrib/vim/ftdetect/gmid.vim \
contrib/vim/ftplugin/gmid.vim \
contrib/vim/indent/gmid.vim \
contrib/vim/syntax_checkers/gmid/gmid.vim \
contrib/vim/syntax/gmid.vim
DISTFILES = ${EXTRAS} \
${CONTRIB} \
${COMPATS} \
${REGRESSFILES} \
${SRCS} \
${TESTSRCS}
SRCS = gmid.h iri.h log.h parse.y proc.h \
${GMID_SRCS} ${GEMEXP_SRCS} ${GG_SRCS} ${TITAN_SRCS}
DISTNAME = gmid-${VERSION}
all: Makefile.local gmid gg
.PHONY: all static clean cleanall test regress install
# -- public targets --
Makefile.local config.h: configure ${TESTSRCS}
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 Makefile.local
include Makefile.depend
y.tab.c: parse.y
${YACC} -b y parse.y
gmid: ${GMID_OBJS}
${CC} ${GMID_OBJS} -o $@ ${LDFLAGS}
gg: ${GG_OBJS}
${CC} ${GG_OBJS} -o $@ ${LDFLAGS}
static: ${GMID_OBJS} ${GG_OBJS}
${CC} ${GMID_OBJS} -o gmid ${LDFLAGS} ${STATIC}
${CC} ${GG_OBJS} -o gg ${LDFLAGS} ${STATIC}
include config.mk
clean:
rm -f *.o compat/*.o y.tab.c y.tab.h y.output gmid gg
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 Makefile.local config.h config.h.old config.log config.log.old
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
install: gmid gg gemexp
mkdir -p ${DESTDIR}${BINDIR}
mkdir -p ${DESTDIR}${MANDIR}/man1
mkdir -p ${DESTDIR}${MANDIR}/man5
${INSTALL_PROGRAM} gmid ${DESTDIR}${BINDIR}
mkdir -p ${DESTDIR}${MANDIR}/man8
${INSTALL_PROGRAM} gemexp ${DESTDIR}${BINDIR}
${INSTALL_PROGRAM} gg ${DESTDIR}${BINDIR}
${INSTALL_MAN} gmid.1 ${DESTDIR}${MANDIR}/man1
${INSTALL_MAN} gmid.conf.5 ${DESTDIR}${MANDIR}/man5
${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/gmid.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 $@
depend: config.h y.tab.c
mkdep -f Makefile.tmp1 ${CFLAGS} ${GMID_SRCS} ${GG_SRCS} ${COBJSx:.o=.c}
perl -e 'undef $$/; $$_ = <>; s|/usr/include/\S+||g; \
s|\\\n||g; s| +| |g; s| $$||mg; print;' \
Makefile.tmp1 > Makefile.tmp2
rm Makefile.tmp1
mv Makefile.tmp2 Makefile.depend
# -- 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 ${SRCS} ${EXTRAS} .dist/${DISTNAME}
${INSTALL} -m 0644 ${DISTFILES} .dist/${DISTNAME}/
cd .dist/${DISTNAME} && chmod 755 configure
mkdir -p .dist/${DISTNAME}/contrib
${INSTALL} -m 0644 ${CONTRIB} .dist/${DISTNAME}/contrib
cd .dist/${DISTNAME}/contrib && chmod 755 gencert renew-certs
mkdir -p .dist/${DISTNAME}/compat
${INSTALL} -m 0644 ${COMPATS} .dist/${DISTNAME}/compat
mkdir -p .dist/${DISTNAME}/have
${INSTALL} -m 0644 ${TESTSRCS} .dist/${DISTNAME}/have
mkdir -p .dist/${DISTNAME}/regress
${INSTALL} -m 0644 ${REGRESSFILES} .dist/${DISTNAME}/regress
cd .dist/${DISTNAME}/regress && chmod 755 env err hello invalid \
max-length-reply regress sha slow
${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

View File

@ -1,17 +0,0 @@
dirs.o: dirs.c gmid.h config.h
ex.o: ex.c gmid.h config.h
fcgi.o: fcgi.c gmid.h config.h
gmid.o: gmid.c gmid.h config.h
iri.o: iri.c gmid.h config.h
log.o: log.c gmid.h config.h
mime.o: mime.c gmid.h config.h
proxy.o: proxy.c gmid.h config.h
puny.o: puny.c gmid.h config.h
sandbox.o: sandbox.c gmid.h config.h
server.o: server.c gmid.h config.h
utf8.o: utf8.c gmid.h config.h
utils.o: utils.c gmid.h config.h
y.tab.o: y.tab.c gmid.h config.h
gg.o: gg.c gmid.h config.h
iri.o: iri.c gmid.h config.h
utf8.o: utf8.c gmid.h config.h

View File

@ -1,27 +1,17 @@
# gmid
gmid is a fast Gemini server written with security in mind. I
initially wrote it to serve static files, but it has grown into a
featureful server.
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.
A few helper programs are shipped as part of gmid:
## Features
- `gg` is a simple command-line Gemini client.
(random order)
- `gemexp` is a stripped-down config-less version of gmid to quickly
serve a directory from the command line.
- sandboxed by default on OpenBSD, Linux and FreeBSD
- reconfiguration: reload the running configuration without
interruption
- automatic redirect/error pages (see `block return`)
- IRI support (RFC3987)
- automatic certificate generation for config-less mode
- reverse proxying
- CGI and FastCGI support
- virtual hosts
- location rules
- event-based asynchronous I/O model
- low memory footprint
- small codebase, easily hackable
- `titan` is a command-line titan client.
## Internationalisation (IRIs, UNICODE, punycode, all that stuff)
@ -45,12 +35,15 @@ doesn't do that (yet).
## Configuration
[httpd]: https://man.openbsd.org/httpd.8
gmid has a rich configuration file, heavily inspired by OpenBSD'
httpd, with every detail carefully documented in the manpage. Here's
a minimal example of a config file:
[httpd(8)][httpd], with every detail carefully documented in the
manpage. Here's a minimal example of a config file:
```conf
server "example.com" {
listen on * port 1965
cert "/path/to/cert.pem"
key "/path/to/key.pem"
root "/var/gemini/example.com"
@ -60,12 +53,11 @@ server "example.com" {
and a slightly more complex one
```conf
ipv6 on # enable ipv6
# define a macro
cert_root = "/path/to/keys"
server "example.com" {
listen on * port 1965
alias "foobar.com"
cert $cert_root "/example.com.crt"
@ -75,9 +67,6 @@ server "example.com" {
# lang for text/gemini files
lang "en"
# execute CGI scripts in /cgi/
cgi "/cgi/*"
# only for locations that matches /files/*
location "/files/*" {
# generate directory listings
@ -95,23 +84,19 @@ server "example.com" {
## Building
gmid depends on libevent2, OpenSSL/LibreSSL and libtls (provided
either by LibreSSL or libretls). At build time, yacc (or GNU bison)
is also needed.
gmid depends on libevent2, LibreSSL or OpenSSL, and yacc or GNU bison.
The build is as simple as
./configure
make
or `make static` to build a statically-linked executable.
$ ./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
# 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
@ -125,10 +110,11 @@ working as intended.
Execute
make regress
$ make regress
to start the suite. Keep in mind that the regression tests needs to
create files inside the `regress` directory and bind the 10965 port.
create a few file inside the `regress` directory and bind the 10965
port.
## Contributing
@ -141,24 +127,21 @@ to the `contrib` directory.
## Architecture/Security considerations
gmid is composed by four processes: the parent process, the logger,
the listener and the executor. The parent process is the only one
that doesn't drop privileges, but all it does is to wait for a SIGHUP
to reload the configuration and spawn a new generation of children
process. The logger process gathers the logs and prints 'em to
stderr or syslog (for the time being.) The listener process is the
only one that needs internet access and is sandboxed by default. The
executor process exists only to fork and execute CGI scripts, and
optionally to connect to FastCGI applications.
The internal architecture was revisited for the 2.0 release. For
earlier releases, please refer to previous revision of this file.
On OpenBSD the processes are all `pledge(2)`d and `unveil(2)`ed.
gmid has a privsep design, where the operations done by the daemon are
splitted into multiple processes:
On FreeBSD, the listener and logger process are sandboxed with `capsicum(4)`.
- 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`.
On Linux, a `seccomp(2)` filter is installed in the listener to allow
only certain syscalls, see [sandbox.c](sandbox.c) for more information
about the BPF program. If available, landlock is used to limit the
portion of the file system gmid can access (requires linux 5.13+.)
- logger: handles the logging with syslog and/or local files.
In any case, it's advisable to run gmid inside some sort of
container/jail/chroot.
- 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
}
}

View File

@ -14,6 +14,8 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "../config.h"
#include <unistd.h>
int

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;
}

View File

@ -1,6 +1,7 @@
/* $OpenBSD: imsg-buffer.c,v 1.13 2021/03/31 17:42:24 eric Exp $ */
/* $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
@ -24,6 +25,7 @@
#include <limits.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
@ -33,15 +35,20 @@
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 = malloc(len)) == NULL) {
if ((buf->buf = calloc(len, 1)) == NULL) {
free(buf);
return (NULL);
}
@ -56,14 +63,22 @@ ibuf_dynamic(size_t len, size_t max)
{
struct ibuf *buf;
if (max < len)
if (max == 0 || max < len) {
errno = EINVAL;
return (NULL);
}
if ((buf = ibuf_open(len)) == NULL)
if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
return (NULL);
if (max > 0)
buf->max = max;
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);
}
@ -74,7 +89,7 @@ ibuf_realloc(struct ibuf *buf, size_t len)
unsigned char *b;
/* on static buffers max is eq size and so the following fails */
if (buf->wpos + len > buf->max) {
if (len > SIZE_MAX - buf->wpos || buf->wpos + len > buf->max) {
errno = ERANGE;
return (-1);
}
@ -88,23 +103,16 @@ ibuf_realloc(struct ibuf *buf, size_t len)
return (0);
}
int
ibuf_add(struct ibuf *buf, const void *data, size_t len)
{
if (buf->wpos + len > buf->size)
if (ibuf_realloc(buf, len) == -1)
return (-1);
memcpy(buf->buf + buf->wpos, data, len);
buf->wpos += len;
return (0);
}
void *
ibuf_reserve(struct ibuf *buf, size_t len)
{
void *b;
if (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);
@ -114,34 +122,416 @@ ibuf_reserve(struct ibuf *buf, size_t 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 allowed to seek in already written parts */
if (pos + len > buf->wpos)
/* 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 + pos);
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(struct ibuf *buf)
ibuf_size(const struct ibuf *buf)
{
return (buf->wpos);
return (buf->wpos - buf->rpos);
}
size_t
ibuf_left(struct ibuf *buf)
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)
{
@ -154,8 +544,8 @@ ibuf_write(struct msgbuf *msgbuf)
TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
if (i >= IOV_MAX)
break;
iov[i].iov_base = buf->buf + buf->rpos;
iov[i].iov_len = buf->wpos - buf->rpos;
iov[i].iov_base = ibuf_data(buf);
iov[i].iov_len = ibuf_size(buf);
i++;
}
@ -178,15 +568,6 @@ again:
return (1);
}
void
ibuf_free(struct ibuf *buf)
{
if (buf == NULL)
return;
freezero(buf->buf, buf->size);
free(buf);
}
void
msgbuf_init(struct msgbuf *msgbuf)
{
@ -195,7 +576,7 @@ msgbuf_init(struct msgbuf *msgbuf)
TAILQ_INIT(&msgbuf->bufs);
}
void
static void
msgbuf_drain(struct msgbuf *msgbuf, size_t n)
{
struct ibuf *buf, *next;
@ -203,8 +584,8 @@ msgbuf_drain(struct msgbuf *msgbuf, size_t n)
for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
buf = next) {
next = TAILQ_NEXT(buf, entry);
if (buf->rpos + n >= buf->wpos) {
n -= buf->wpos - buf->rpos;
if (n >= ibuf_size(buf)) {
n -= ibuf_size(buf);
ibuf_dequeue(msgbuf, buf);
} else {
buf->rpos += n;
@ -244,8 +625,8 @@ msgbuf_write(struct msgbuf *msgbuf)
break;
if (i > 0 && buf->fd != -1)
break;
iov[i].iov_base = buf->buf + buf->rpos;
iov[i].iov_len = buf->wpos - buf->rpos;
iov[i].iov_base = ibuf_data(buf);
iov[i].iov_len = ibuf_size(buf);
i++;
if (buf->fd != -1)
buf0 = buf;
@ -292,9 +673,17 @@ again:
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++;
}
@ -303,10 +692,6 @@ static void
ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
{
TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
if (buf->fd != -1)
close(buf->fd);
msgbuf->queued--;
ibuf_free(buf);
}

View File

@ -1,6 +1,7 @@
/* $OpenBSD: imsg.c,v 1.17 2022/01/28 10:41:44 claudio Exp $ */
/* $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
@ -29,23 +30,28 @@
#include "imsg.h"
struct imsg_fd {
TAILQ_ENTRY(imsg_fd) entry;
int fd;
};
int imsg_fd_overhead = 0;
static int imsg_get_fd(struct imsgbuf *);
static int imsg_dequeue_fd(struct imsgbuf *);
void
imsg_init(struct imsgbuf *ibuf, int fd)
imsg_init(struct imsgbuf *imsgbuf, int fd)
{
msgbuf_init(&ibuf->w);
memset(&ibuf->r, 0, sizeof(ibuf->r));
ibuf->fd = fd;
ibuf->w.fd = fd;
ibuf->pid = getpid();
TAILQ_INIT(&ibuf->fds);
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 *ibuf)
imsg_read(struct imsgbuf *imsgbuf)
{
struct msghdr msg;
struct cmsghdr *cmsg;
@ -61,8 +67,8 @@ imsg_read(struct imsgbuf *ibuf)
memset(&msg, 0, sizeof(msg));
memset(&cmsgbuf, 0, sizeof(cmsgbuf));
iov.iov_base = ibuf->r.buf + ibuf->r.wpos;
iov.iov_len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
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;
@ -80,13 +86,13 @@ again:
return (-1);
}
if ((n = recvmsg(ibuf->fd, &msg, 0)) == -1) {
if ((n = recvmsg(imsgbuf->fd, &msg, 0)) == -1) {
if (errno == EINTR)
goto again;
goto fail;
}
ibuf->r.wpos += n;
imsgbuf->r.wpos += n;
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
@ -106,7 +112,7 @@ again:
fd = ((int *)CMSG_DATA(cmsg))[i];
if (ifd != NULL) {
ifd->fd = fd;
TAILQ_INSERT_TAIL(&ibuf->fds, ifd,
TAILQ_INSERT_TAIL(&imsgbuf->fds, ifd,
entry);
ifd = NULL;
} else
@ -122,95 +128,235 @@ fail:
}
ssize_t
imsg_get(struct imsgbuf *ibuf, struct imsg *imsg)
imsg_get(struct imsgbuf *imsgbuf, struct imsg *imsg)
{
struct imsg m;
size_t av, left, datalen;
av = ibuf->r.wpos;
av = imsgbuf->r.wpos;
if (IMSG_HEADER_SIZE > av)
return (0);
memcpy(&imsg->hdr, ibuf->r.buf, sizeof(imsg->hdr));
if (imsg->hdr.len < IMSG_HEADER_SIZE ||
imsg->hdr.len > MAX_IMSGSIZE) {
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 (imsg->hdr.len > av)
if (m.hdr.len > av)
return (0);
datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
ibuf->r.rptr = ibuf->r.buf + IMSG_HEADER_SIZE;
if (datalen == 0)
imsg->data = NULL;
else if ((imsg->data = malloc(datalen)) == NULL)
return (-1);
if (imsg->hdr.flags & IMSGF_HASFD)
imsg->fd = imsg_get_fd(ibuf);
else
imsg->fd = -1;
m.fd = -1;
m.buf = NULL;
m.data = NULL;
if (datalen != 0)
memcpy(imsg->data, ibuf->r.rptr, datalen);
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 (imsg->hdr.len < av) {
left = av - imsg->hdr.len;
memmove(&ibuf->r.buf, ibuf->r.buf + imsg->hdr.len, left);
ibuf->r.wpos = left;
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
ibuf->r.wpos = 0;
imsgbuf->r.wpos = 0;
*imsg = m;
return (datalen + IMSG_HEADER_SIZE);
}
int
imsg_compose(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
int fd, const void *data, uint16_t datalen)
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(ibuf, type, peerid, pid, datalen)) == NULL)
if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
return (-1);
if (imsg_add(wbuf, data, datalen) == -1)
return (-1);
wbuf->fd = fd;
imsg_close(ibuf, wbuf);
ibuf_fd_set(wbuf, fd);
imsg_close(imsgbuf, wbuf);
return (1);
}
int
imsg_composev(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
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, datalen = 0;
int i;
size_t datalen = 0;
for (i = 0; i < iovcnt; i++)
datalen += iov[i].iov_len;
if ((wbuf = imsg_create(ibuf, type, peerid, pid, datalen)) == NULL)
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);
wbuf->fd = fd;
imsg_close(ibuf, wbuf);
ibuf_fd_set(wbuf, fd);
imsg_close(imsgbuf, wbuf);
return (1);
}
/* ARGSUSED */
/*
* 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 *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
uint16_t datalen)
imsg_create(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
size_t datalen)
{
struct ibuf *wbuf;
struct imsg_hdr hdr;
@ -223,9 +369,9 @@ imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
hdr.type = type;
hdr.flags = 0;
hdr.peerid = peerid;
hdr.peerid = id;
if ((hdr.pid = pid) == 0)
hdr.pid = ibuf->pid;
hdr.pid = imsgbuf->pid;
if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
return (NULL);
}
@ -236,7 +382,7 @@ imsg_create(struct imsgbuf *ibuf, uint32_t type, uint32_t peerid, pid_t pid,
}
int
imsg_add(struct ibuf *msg, const void *data, uint16_t datalen)
imsg_add(struct ibuf *msg, const void *data, size_t datalen)
{
if (datalen)
if (ibuf_add(msg, data, datalen) == -1) {
@ -247,58 +393,57 @@ imsg_add(struct ibuf *msg, const void *data, uint16_t datalen)
}
void
imsg_close(struct imsgbuf *ibuf, struct ibuf *msg)
imsg_close(struct imsgbuf *imsgbuf, struct ibuf *msg)
{
struct imsg_hdr *hdr;
hdr = (struct imsg_hdr *)msg->buf;
hdr->flags &= ~IMSGF_HASFD;
if (msg->fd != -1)
if (ibuf_fd_avail(msg))
hdr->flags |= IMSGF_HASFD;
hdr->len = ibuf_size(msg);
hdr->len = (uint16_t)msg->wpos;
ibuf_close(&ibuf->w, msg);
ibuf_close(&imsgbuf->w, msg);
}
void
imsg_free(struct imsg *imsg)
{
freezero(imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE);
ibuf_free(imsg->buf);
}
static int
imsg_get_fd(struct imsgbuf *ibuf)
imsg_dequeue_fd(struct imsgbuf *imsgbuf)
{
int fd;
struct imsg_fd *ifd;
if ((ifd = TAILQ_FIRST(&ibuf->fds)) == NULL)
if ((ifd = TAILQ_FIRST(&imsgbuf->fds)) == NULL)
return (-1);
fd = ifd->fd;
TAILQ_REMOVE(&ibuf->fds, ifd, entry);
TAILQ_REMOVE(&imsgbuf->fds, ifd, entry);
free(ifd);
return (fd);
}
int
imsg_flush(struct imsgbuf *ibuf)
imsg_flush(struct imsgbuf *imsgbuf)
{
while (ibuf->w.queued)
if (msgbuf_write(&ibuf->w) <= 0)
while (imsgbuf->w.queued)
if (msgbuf_write(&imsgbuf->w) <= 0)
return (-1);
return (0);
}
void
imsg_clear(struct imsgbuf *ibuf)
imsg_clear(struct imsgbuf *imsgbuf)
{
int fd;
msgbuf_clear(&ibuf->w);
while ((fd = imsg_get_fd(ibuf)) != -1)
msgbuf_clear(&imsgbuf->w);
while ((fd = imsg_dequeue_fd(imsgbuf)) != -1)
close(fd);
}

View File

@ -1,6 +1,7 @@
/* $OpenBSD: imsg.h,v 1.5 2019/01/20 02:50:03 bcook Exp $ */
/* $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>
@ -21,7 +22,7 @@
#ifndef _IMSG_H_
#define _IMSG_H_
#include <stdint.h>
#include <sys/types.h>
#define IBUF_READ_SIZE 65535
#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr)
@ -49,11 +50,7 @@ struct ibuf_read {
size_t wpos;
};
struct imsg_fd {
TAILQ_ENTRY(imsg_fd) entry;
int fd;
};
struct imsg_fd;
struct imsgbuf {
TAILQ_HEAD(, imsg_fd) fds;
struct ibuf_read r;
@ -76,35 +73,83 @@ struct imsg {
struct imsg_hdr hdr;
int fd;
void *data;
struct ibuf *buf;
};
struct iovec;
/* buffer.c */
/* 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);
size_t ibuf_size(struct ibuf *);
size_t ibuf_left(struct ibuf *);
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 *);
int ibuf_write(struct msgbuf *);
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 *);
void msgbuf_drain(struct msgbuf *, size_t);
/* 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 *, uint16_t);
const void *, size_t);
int imsg_composev(struct imsgbuf *, uint32_t, uint32_t, pid_t, int,
const struct iovec *, int);
struct ibuf *imsg_create(struct imsgbuf *, uint32_t, uint32_t, pid_t, uint16_t);
int imsg_add(struct ibuf *, const void *, uint16_t);
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 *);

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);
}

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;
}

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);
}

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;
}

774
configure vendored
View File

@ -1,6 +1,6 @@
#!/bin/sh
#
# Copyright (c) 2021, 2022 Omar Polo <op@omarpolo.com>
# 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>
#
@ -18,254 +18,352 @@
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
# Output file descriptor usage:
# 1 (stdout): config.h, Makefile.local
# 2 (stderr): original stderr, usually to the console
# 3: config.log
exec 3> config.log
echo "file config.log: writing..."
# --------
# default settings: initialize all vars here such that nothing is
# leaked from the environment except for CC, CFLAGS and LDFLAGS
VERSION=1.8.5-current
CC=`printf "all:\\n\\t@echo \\\$(CC)\\n" | make ${MAKE_FLAGS} -sf -`
if [ -z "${CFLAGS}" ]; then
CFLAGS=`printf "all:\\n\\t@echo \\\$(CFLAGS)\\n" | make ${MAKE_FLAGS} -sf -`
fi
CFLAGS="${CFLAGS} -W -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes"
CFLAGS="${CFLAGS} -Wwrite-strings -Wno-unused-parameter"
if [ -z "${LDFLAGS}" ]; then
LDFLAGS=`printf "all:\\n\\t@echo \\\$(LDFLAGS)\\n" | make ${MAKE_FLAGS} -sf -`
LDFLAGS="-ltls -levent -lcrypto"
fi
LD_IMSG=
STATIC=
YACC=yacc
COBJS=
DISABLE_SANDBOX=0
NEED_GNU_SOURCE=0
NEED_OPENBSD_SOURCE=0
NEED_LIBBSD_OPENBSD_VIS=0
PREFIX="/usr/local"
BINDIR=
COMPATS=
COMP="${CC} ${CFLAGS} -Werror=implicit-function-declaration"
INSTALL="install"
add_library() {
if pkg-config "$1"; then
CFLAGS="${CFLAGS} $(pkg-config --cflags "$1")"
LDFLAGS="${LDFLAGS} $(pkg-config --libs "$1")"
fi
}
# try to auto detect CFLAGS and LDFLAGS
if command -v pkg-config >/dev/null; then
add_library "libtls"
add_library "openssl"
case "$(uname)" in
OpenBSD)
# use libevent and imsg in base
;;
*)
add_library "libevent"
add_library "libimsg"
;;
esac
case "$(uname)" in
*BSD|DragonFly|Darwin)
;;
*)
add_library "libbsd-ctor libbsd-overlay"
;;
esac
fi
# auto detect yacc/bison
command -v ${YACC} >/dev/null || {
echo "${YACC} not found: trying bison" 1>&2
echo "${YACC} not found: trying bison" 1>&3
YACC=bison
command -v ${YACC} >/dev/null || {
echo "${YACC} not found: giving up" 1>&2
echo "${YACC} not found: giving up" 1>&3
}
}
# --------
# allow certain variables to be overridden on the command line
for keyvals in "$@"; do
if [ "$keyvals" = "--disable-sandbox" ]; then
DISABLE_SANDBOX=1
continue
fi
if [ "$keyvals" = "--enable-sandbox" ]; then
DISABLE_SANDBOX=0
continue
fi
key=`echo $keyvals | cut -s -d '=' -f1`
if [ -z "$key" ]; then
echo "$0: invalid key-value: $keyvals" 1>&2
exit 1
fi
val=`echo $keyvals | cut -d '=' -f 2-`
case "$key" in
BINDIR) BINDIR="$val" ;;
CC) CC="$val" ;;
CFLAGS) CFLAGS="$val" ;;
DESTDIR) DESTDIR="$val" ;;
LDFLAGS) LDFLAGS="$val" ;;
MANDIR) MANDIR="$val" ;;
PREFIX) PREFIX="$val" ;;
YACC) YACC="$val" ;;
--prefix) PREFIX="$val" ;;
*)
echo "$0: invalid key: $key" 1>&2
exit 1
esac
done
# --------
# Allow configure.local to override all variables, default settings,
# command-line arguments, and tested features, above.
if [ -r ./configure.local ]; then
echo "configure.local: reading..." 1>&2
echo "configure.local: reading..." 1>&3
cat ./configure.local 1>&3
. ./configure.local
else
echo "configure.local: no (fully automatic configuration)" 1>&2
echo "configure.local: no (fully automatic configuration)" 1>&3
fi
# --------
# tests functions
# Check whether this HAVE_ setting is manually overridden.
# If yes, use the override, if no, do not decide anything yet.
# Arguments: test file name, test var name, manual value
ismanual() {
[ -z "${3}" ] && return 1
echo "tested ${1}: HAVE_${2}=${3} (manual)" 1>&2
echo "tested ${1}: HAVE_${2}=${3} (manual)" 1>&3
echo 1>&3
return 0
}
# Run a single autoconfiguration test.
# In case of success, enable the feature.
# In case of failure, do not decide anything yet.
# Arguments: test file name, test var name, additional CFLAGS
# singletest name var extra-cflags extra-libs msg
singletest() {
n=${1}${3}
cat 1>&3 << __HEREDOC__
testing ${n} ...
${COMP} -o have/${1} have/${1}.c ${3} ${LDFLAGS}
__HEREDOC__
if ${COMP} -o "have/${1}" "have/${1}.c" ${3} ${LDFLAGS} 1>&3 2>&3
then
echo "tested ${n}: yes" 1>&2
echo "tested ${n}: yes" 1>&3
echo 1>&3
eval HAVE_${2}=1
[ "${3}" = "-D_GNU_SOURCE" ] && NEED_GNU_SOURCE=1
[ "${3}" = "-D_OPENBSD_SOURCE" ] && NEED_OPENBSD_SOURCE=1
[ "${3}" = "-lutil" ] && LD_IMSG="-lutil"
rm "have/${1}"
return 0
else
echo "tested ${n}: no (compilation failed)" 1>&2
echo "result of ${n}: ${CC} failed with exit status $?" 1>&3
echo "result of compiling ${n}: no" 1>&3
echo 1>&3
return 1
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
}
# Run a complete autoconfiguration test, including the check for
# a manual override and disabling the feature on failure.
# Arguments: test file name, test var name, additional CFLAGS
# The final argument can optionally be repeated a second time.
runtest() {
eval _manual=\${HAVE_${2}}
ismanual "${1}" "${2}" "${_manual}" && return 0
singletest "${1}" "${2}" "${3}" && return 0
[ -n "${4}" ] && singletest "${1}" "${2}" "${4}" && return 0
# 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
}
# --------
# compiler options
# runtest name var extra-cflags extra-libs pkgconfig-name
runtest() {
if singletest "$1" "$2" "" ""; then
eval HAVE_${2}=1
return 0
fi
COMP="${CC} ${CFLAGS} -Wno-unused -Werror"
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
echo "selected CFLAGS=\"${CFLAGS}\"" 1>&2
echo "selected CFLAGS=\"${CFLAGS}\"" 1>&3
echo 1>&3
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 [ -n "${STATIC}" ]; then
echo "selected STATIC=\"${STATIC}\" (manual)" 1>&2
echo "selected STATIC=\"${STATIC}\" (manual)" 1>&3
echo 1>&3
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
runtest noop STATIC -static || true
[ ${HAVE_STATIC} -eq 0 ] || STATIC="-static"
echo "selected STATIC=\"${STATIC}\"" 1>&2
echo "selected STATIC=\"${STATIC}\"" 1>&3
echo 1>&3
echo "pkg-config: not found"
fi
# --------
# tests for config.h
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 || true
runtest imsg IMSG "" -lutil libimsg || true
runtest landlock LANDLOCK || true
runtest libevent LIBEVENT || true
runtest libevent2 LIBEVENT2 || true
runtest libtls LIBTLS || true
runtest openssl OPENSSL || 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 program_invocation_short_name PROGRAM_INVOCATION_SHORT_NAME -D_GNU_SOURCE || true
runtest queue_h QUEUE_H || true
runtest reallocarray REALLOCARRAY || 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 || 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 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: libtls not found" 1>&2
echo "FATAL: libtls not found" 1>&3
echo "FATAL: openssl not found" 1>&2
echo "FATAL: openssl not found" 1>&3
exit 1
fi
@ -281,14 +379,37 @@ if [ ${HAVE_LIBEVENT} -eq 0 ]; then
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
# --------
# write config.h
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
@ -296,9 +417,6 @@ cat <<__HEREDOC__
#endif
__HEREDOC__
[ ${NEED_GNU_SOURCE} -eq 0 ] || echo "#define _GNU_SOURCE"
[ ${NEED_OPENBSD_SOURCE} -eq 0 ] || echo "#define _OPENBSD_SOURCE"
[ ${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>"
@ -319,143 +437,255 @@ 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_ERR ${HAVE_ERR}
#define HAVE_EXPLICIT_BZERO ${HAVE_EXPLICIT_BZERO}
#define HAVE_FREEZERO ${HAVE_FREEZERO}
#define HAVE_GETDTABLECOUNT ${HAVE_GETDTABLECOUNT}
#define HAVE_GETDTABLESIZE ${HAVE_GETDTABLESIZE}
#define HAVE_GETPROGNAME ${HAVE_GETPROGNAME}
#define HAVE_IMSG ${HAVE_IMSG}
#define HAVE_LANDLOCK ${HAVE_LANDLOCK}
#define HAVE_LIBEVENT ${HAVE_LIBEVENT}
#define HAVE_LIBEVENT2 ${HAVE_LIBEVENT2}
#define HAVE_PROGRAM_INVOCATION_SHORT_NAME ${HAVE_PROGRAM_INVOCATION_SHORT_NAME}
#define HAVE_PR_SET_NAME ${HAVE_PR_SET_NAME}
#define HAVE_QUEUE_H ${HAVE_QUEUE_H}
#define HAVE_REALLOCARRAY ${HAVE_REALLOCARRAY}
#define HAVE_RECALLOCARRAY ${HAVE_RECALLOCARRAY}
#define HAVE_SETPROCTITLE ${HAVE_SETPROCTITLE}
#define HAVE_STRLCAT ${HAVE_STRLCAT}
#define HAVE_STRLCPY ${HAVE_STRLCPY}
#define HAVE_STRTONUM ${HAVE_STRTONUM}
#define HAVE_TREE_H ${HAVE_TREE_H}
#define HAVE_VASPRINTF ${HAVE_VASPRINTF}
#ifndef SYSCONFDIR
# define SYSCONFDIR "${SYSCONFDIR}"
#endif
__HEREDOC__
[ ${HAVE_EXPLICIT_BZERO} -eq 0 -o \
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 ] && echo "#include <stddef.h>"
${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*, ...);"
COBJS="${COBJS} compat/err.o"
else
echo "#include <err.h>"
fi
if [ ${HAVE_EXPLICIT_BZERO} -eq 0 ]; then
echo "extern void explicit_bzero(void*, size_t);"
COBJS="${COBJS} compat/explicit_bzero.o"
fi
if [ ${HAVE_FREEZERO} -eq 0 ]; then
echo "extern void freezero(void*, size_t);"
COBJS="${COBJS} compat/freezero.o"
fi
if [ ${HAVE_GETDTABLECOUNT} -eq 0 ]; then
echo "extern int getdtablecount(void);"
COBJS="${COBJS} compat/getdtablecount.o"
fi
if [ ${HAVE_GETDTABLESIZE} -eq 0 ]; then
echo "extern int getdtablesize(void);"
COBJS="${COBJS} compat/getdtablesize.o"
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);"
COBJS="${COBJS} compat/getprogname.o"
fi
if [ ${HAVE_IMSG} -eq 0 ]; then
COBJS="${COBJS} compat/imsg.o compat/imsg-buffer.o"
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);"
COBJS="${COBJS} compat/reallocarray.o"
fi
if [ ${HAVE_RECALLOCARRAY} -eq 0 ]; then
echo "extern void *recallocarray(void*, size_t, size_t, size_t);"
COBJS="${COBJS} compat/recallocarray.o"
fi
if [ ${HAVE_SETPROCTITLE} -eq 0 ]; then
echo "extern void setproctitle(const char *fmt, ...);"
COBJS="${COBJS} compat/setproctitle.o"
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);"
COBJS="${COBJS} compat/strlcat.o"
fi
if [ ${HAVE_STRLCPY} -eq 0 ]; then
echo "extern size_t strlcpy(char*, const char*, size_t);"
COBJS="${COBJS} compat/strlcpy.o"
fi
if [ ${HAVE_STRTONUM} -eq 0 ]; then
echo "extern long long strtonum(const char*, long long, long long, const char**);"
COBJS="${COBJS} compat/strtonum.o"
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);"
COBJS="${COBJS} compat/vasprintf.o"
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
# --------
# tests for Makefile.local
exec > config.mk
exec > Makefile.local
[ -z "${BINDIR}" ] && BINDIR="${PREFIX}/bin"
[ -z "${MANDIR}" ] && MANDIR="${PREFIX}/man"
[ -z "${INSTALL_PROGRAM}" ] && INSTALL_PROGRAM="${INSTALL} -m 0555"
[ -z "${INSTALL_LIB}" ] && INSTALL_LIB="${INSTALL} -m 0444"
[ -z "${INSTALL_MAN}" ] && INSTALL_MAN="${INSTALL} -m 0444"
[ -z "${INSTALL_DATA}" ] && INSTALL_DATA="${INSTALL} -m 0444"
[ -z "${BINDIR}" ] && BINDIR="\${PREFIX}/bin"
[ -z "${MANDIR}" ] && MANDIR="\${PREFIX}/man"
cat << __HEREDOC__
CC = ${CC}
CFLAGS = ${CFLAGS}
LDFLAGS = ${LDFLAGS} ${LD_IMSG}
LDFLAGS = ${LDFLAGS}
LIBS = ${LIBS}
YACC = ${YACC}
STATIC = ${STATIC}
PREFIX = ${PREFIX}
BINDIR = ${BINDIR}
MANDIR = ${MANDIR}
INCLUDEDIR = ${INCLUDEDIR}
INSTALL = ${INSTALL}
INSTALL_PROGRAM = ${INSTALL_PROGRAM}
INSTALL_LIB = ${INSTALL_LIB}
INSTALL_MAN = ${INSTALL_MAN}
INSTALL_DATA = ${INSTALL_DATA}
COBJS = ${COBJS}
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 Makefile.local: written" 1>&2
echo "file Makefile.local: written" 1>&3
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

View File

@ -1,66 +0,0 @@
#
# Copyright (c) 2014-2022 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.
# For all settings documented in this file, there are reasonable
# defaults and/or the ./configure script attempts autodetection.
# Consequently, you only need to create a file ./configure.local
# and put any of these settings into it if ./configure autodetection
# fails or if you want to make different choices for other reasons.
# If autodetection fails, please tell <gmid@omarpolo.com>.
# We recommend that you write ./configure.local from scratch and
# only put the lines there you need. This file contains examples.
# It is not intended as a template to be copied as a whole.
# Some systems may want to set additional linker flags for all the
# binaries, not only for those using libmandoc, for example for
# hardening options.
LDFLAGS="-Wl,-z,relro"
# It is possible to change the utility program used for installation
# and the modes files are installed with. The defaults are:
INSTALL="install"
INSTALL_PROGRAM="${INSTALL} -m 0555"
INSTALL_LIB="${INSTALL} -m 0444"
INSTALL_MAN="${INSTALL} -m 0444"
INSTALL_DATA="${INSTALL} -m 0444"
# In rare cases, it may be required to skip individual automatic tests.
# Each of the following variables can be set to 0 (test will not be run
# and will be regarded as failed) or 1 (test will not be run and will
# be regarded as successful).
HAVE_ERR=0
HAVE_EXPLICIT_BZERO=0
HAVE_FREEZERO=0
HAVE_GETDTABLECOUNT=0
HAVE_GETDTABLESIZE=0
HAVE_GETPROGNAME=0
HAVE_IMSG=0
HAVE_LANDLOCK=0
HAVE_PR_SET_NAME=0
HAVE_PROGRAM_INVOCATION_SHORT_NAME=0
HAVE_QUEUE_H=0
HAVE_REALLOCARRAY=0
HAVE_RECALLOCARRAY=0
HAVE_SETPROCTITLE=0
HAVE_STRLCAT=0
HAVE_STRLCPY=0
HAVE_STRTONUM=0
HAVE_TREE_H=0
HAVE_VASPRINTF=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 "/"
}

View File

@ -1,4 +1,4 @@
FROM alpine as builder
FROM alpine
WORKDIR /build
RUN apk update && \
apk upgrade && \
@ -6,14 +6,14 @@ RUN apk update && \
alpine-sdk \
linux-headers \
bison \
libretls-dev \
libretls-static \
libevent-dev \
libevent-static
libevent-dev \
openssl-dev
COPY . .
RUN make static
FROM alpine
RUN apk update && apk upgrade
COPY --from=builder /build/gmid /bin/gmid
ENTRYPOINT ["gmid"]
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

View File

@ -4,7 +4,7 @@
# gencert - generate certificates
#
# SYNOPSIS
# ./gencert [-fh] [-D days] [-d destdir] hostname
# ./gencert [-efh] [-D days] [-d destdir] hostname
#
# DESCRIPTION
# A simple script to generate self-signed X.509 certificates for
@ -15,6 +15,7 @@
# 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.
@ -26,19 +27,21 @@
progname="$(basename -- "$0")"
usage() {
echo "usage: $progname [-fh] [-d destdir] [-D days] hostname" >&2
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:fh" flag; do
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 ;;
@ -76,13 +79,19 @@ if [ -f "$pem" -o -f "$key" ]; then
fi
fi
openssl req -x509 \
-newkey rsa:4096 \
-out "${pem}" \
-keyout "${key}" \
-days "${days}" \
-nodes \
-subj "/CN=$hostname"
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

View File

@ -6,8 +6,6 @@ Wants=network-online.target
[Service]
Type=simple
User=gmid
Group=nobody
ExecStart=/usr/local/bin/gmid -f -c /etc/gmid.conf
ExecStop=/bin/kill -TERM $MAINPID
ExecReload=/bin/kill -HUP $MAINPID

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

@ -9,3 +9,5 @@ setlocal indentexpr=
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

View File

@ -13,22 +13,44 @@ setlocal iskeyword+=-
" Value Types: {{{2
" ============
syn keyword gmidBoolean on contained
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 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 gmidDirectiveDeprecated mime
syn keyword gmidDeprecated ipv6 nextgroup=gmidBoolean skipwhite
" Comments: {{{2
" =========
@ -38,60 +60,161 @@ syn match gmidComment "\s*#.*$" display
" ===============
syn keyword gmidDirective chroot
syn keyword gmidDirective include
syn keyword gmidDirective ipv6 nextgroup=gmidBoolean skipwhite
syn keyword gmidDirective map
syn keyword gmidDirectiveContinuation to-ext
syn keyword gmidDirective port nextgroup=gmidNumber skipwhite
syn keyword gmidDirective prefork nextgroup=gmidNumber skipwhite
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 gmidBlock start="{" end="}" fold transparent
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 keyword gmidDirectiveBlock server
syn keyword gmidDirectiveBlock location
syn region gmidBlockLocation start="location\s\+.\+\s\+{" end="}"
\ fold transparent contained
\ contains=gmidDirectiveHost,
\ gmidDirectiveParamHost,
\ gmidBlockFastcgi,
\ @gmidValues
syn keyword gmidDirectiveBlock location contained containedin=gmidBlockLocation
syn keyword gmidDirective alias
syn match gmidDirective "\<auto\s\+index\>" nextgroup=gmidBoolean skipwhite display
syn keyword gmidDirective block
syn keyword gmidDirectiveContinuation return nextgroup=gmidNumber skipwhite
syn keyword gmidDirective cert
syn keyword gmidDirective cgi
syn match gmidDirective "\<default\s\+type>" display
syn keyword gmidDirective entrypoint
syn keyword gmidDirective env
syn keyword gmidDirective fastcgi
syn keyword gmidDirectiveContinuation tcp
syn keyword gmidDirective index
syn keyword gmidDirective key
syn keyword gmidDirective lang
syn keyword gmidDirective log nextgroup=gmidBoolean skipwhite
syn keyword gmidDirective param
syn keyword gmidDirective ocsp
syn keyword gmidDirective root
syn match gmidDirective "\<require\s\+client\s\+ca\>" display
syn keyword gmidDirective strip nextgroup=gmidNumber skipwhite
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 keyword gmidDirectiveBlock proxy
syn keyword gmidDirectiveContinuation proto
syn keyword gmidDirectiveContinuation for-host
syn region gmidBlockProxy start="proxy\s\+\(.*\s\+\)\?{" end="}"
\ fold transparent contained
\ contains=gmidDirectiveProxy,
\ gmidDirectiveParamProxy,
\ @gmidValues
syn keyword gmidDirectiveBlock proxy contained containedin=gmidBlockProxy
syn keyword gmidDirective relay-to
syn keyword gmidDirective sni
syn keyword gmidDirective use-tls nextgroup=gmidBoolean skipwhite
syn keyword gmidDirective verifyname nextgroup=gmidBoolean skipwhite
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
@ -99,7 +222,7 @@ hi def link gmidMacro Macro
hi def link gmidDirective Keyword
hi def link gmidDirectiveBlock Function
hi def link gmidDirectiveContinuation Type
hi def link gmidDirectiveDeprecated Error
hi def link gmidDirectiveParam Type
hi def link gmidDeprecated Error
let b:current_syntax = "gmid"

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();
}

531
ex.c
View File

@ -1,531 +0,0 @@
/*
* 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 "gmid.h"
#include <sys/un.h>
#include <err.h>
#include <errno.h>
#include <event.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <signal.h>
#include <stdarg.h>
#include <string.h>
static void handle_imsg_cgi_req(struct imsgbuf*, struct imsg*, size_t);
static void handle_imsg_fcgi_req(struct imsgbuf*, struct imsg*, size_t);
static void handle_imsg_conn_req(struct imsgbuf *, struct imsg *, size_t);
static void handle_imsg_quit(struct imsgbuf*, struct imsg*, size_t);
static void handle_dispatch_imsg(int, short, void*);
static imsg_handlerfn *handlers[] = {
[IMSG_FCGI_REQ] = handle_imsg_fcgi_req,
[IMSG_CGI_REQ] = handle_imsg_cgi_req,
[IMSG_CONN_REQ] = handle_imsg_conn_req,
[IMSG_QUIT] = handle_imsg_quit,
};
static inline void
safe_setenv(const char *name, const char *val)
{
if (val == NULL)
val = "";
setenv(name, val, 1);
}
static char *
xasprintf(const char *fmt, ...)
{
va_list ap;
char *s;
va_start(ap, fmt);
if (vasprintf(&s, fmt, ap) == -1)
s = NULL;
va_end(ap);
return s;
}
static void
do_exec(const char *ex, const char *spath, char *query)
{
char **argv, buf[PATH_MAX], *sname, *t;
size_t i, n;
/* restore the default handlers */
signal(SIGPIPE, SIG_DFL);
signal(SIGCHLD, SIG_DFL);
signal(SIGHUP, SIG_DFL);
signal(SIGINT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
strlcpy(buf, spath, sizeof(buf));
sname = basename(buf);
if (query == NULL || strchr(query, '=') != NULL) {
if ((argv = calloc(2, sizeof(char*))) == NULL)
err(1, "calloc");
argv[0] = sname;
execvp(ex, argv);
warn("execvp: %s", argv[0]);
return;
}
n = 1;
for (t = query ;; t++, n++) {
if ((t = strchr(t, '+')) == NULL)
break;
}
if ((argv = calloc(n+2, sizeof(char*))) == NULL)
err(1, "calloc");
argv[0] = sname;
for (i = 0; i < n; ++i) {
t = strchr(query, '+');
if (t != NULL)
*t = '\0';
argv[i+1] = pct_decode_str(query);
query = t+1;
}
execvp(ex, argv);
warn("execvp: %s", argv[0]);
}
static inline void
setenv_time(const char *var, time_t t)
{
char timebuf[21];
struct tm tminfo;
if (t == -1)
return;
strftime(timebuf, sizeof(timebuf), "%FT%TZ",
gmtime_r(&t, &tminfo));
setenv(var, timebuf, 1);
}
/* fd or -1 on error */
static int
launch_cgi(struct iri *iri, struct cgireq *req, struct vhost *vhost,
struct location *loc)
{
int p[2], errp[2]; /* read end, write end */
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, p) == -1)
return -1;
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, errp) == -1)
return -1;
switch (fork()) {
case -1:
log_err(NULL, "fork failed: %s", strerror(errno));
close(p[0]);
close(p[1]);
close(errp[0]);
close(errp[1]);
return -1;
case 0: { /* child */
char *ex, *pwd;
char iribuf[GEMINI_URL_LEN];
char path[PATH_MAX];
struct envlist *e;
close(p[0]);
if (dup2(p[1], 1) == -1)
goto childerr;
close(errp[0]);
if (dup2(errp[1], 2) == -1)
goto childerr;
ex = xasprintf("%s/%s", loc->dir, req->spath);
serialize_iri(iri, iribuf, sizeof(iribuf));
safe_setenv("GATEWAY_INTERFACE", "CGI/1.1");
safe_setenv("GEMINI_DOCUMENT_ROOT", loc->dir);
safe_setenv("GEMINI_SCRIPT_FILENAME",
xasprintf("%s/%s", loc->dir, req->spath));
safe_setenv("GEMINI_URL", iribuf);
strlcpy(path, "/", sizeof(path));
strlcat(path, req->spath, sizeof(path));
safe_setenv("GEMINI_URL_PATH", path);
if (*req->relpath != '\0') {
strlcpy(path, "/", sizeof(path));
strlcat(path, req->relpath, sizeof(path));
safe_setenv("PATH_INFO", path);
strlcpy(path, loc->dir, sizeof(path));
strlcat(path, "/", sizeof(path));
strlcat(path, req->relpath, sizeof(path));
safe_setenv("PATH_TRANSLATED", path);
}
safe_setenv("QUERY_STRING", iri->query);
safe_setenv("REMOTE_ADDR", req->addr);
safe_setenv("REMOTE_HOST", req->addr);
safe_setenv("REQUEST_METHOD", "");
strlcpy(path, "/", sizeof(path));
strlcat(path, req->spath, sizeof(path));
safe_setenv("SCRIPT_NAME", path);
safe_setenv("SERVER_NAME", iri->host);
snprintf(path, sizeof(path), "%d", conf.port);
safe_setenv("SERVER_PORT", path);
safe_setenv("SERVER_PROTOCOL", "GEMINI");
safe_setenv("SERVER_SOFTWARE", GMID_VERSION);
if (*req->subject != '\0')
safe_setenv("AUTH_TYPE", "Certificate");
else
safe_setenv("AUTH_TYPE", "");
safe_setenv("REMOTE_USER", req->subject);
safe_setenv("TLS_CLIENT_ISSUER", req->issuer);
safe_setenv("TLS_CLIENT_HASH", req->hash);
safe_setenv("TLS_VERSION", req->version);
safe_setenv("TLS_CIPHER", req->cipher);
snprintf(path, sizeof(path), "%d", req->cipher_strength);
safe_setenv("TLS_CIPHER_STRENGTH", path);
setenv_time("TLS_CLIENT_NOT_AFTER", req->notafter);
setenv_time("TLS_CLIENT_NOT_BEFORE", req->notbefore);
TAILQ_FOREACH(e, &vhost->env, envs) {
safe_setenv(e->name, e->value);
}
strlcpy(path, ex, sizeof(path));
pwd = dirname(path);
if (chdir(pwd)) {
warn("chdir");
goto childerr;
}
do_exec(ex, req->spath, iri->query);
goto childerr;
}
default:
close(p[1]);
close(errp[0]);
close(errp[1]);
mark_nonblock(p[0]);
return p[0];
}
childerr:
dprintf(p[1], "%d internal server error\r\n", TEMP_FAILURE);
_exit(1);
}
static struct vhost *
host_nth(size_t n)
{
struct vhost *h;
TAILQ_FOREACH(h, &hosts, vhosts) {
if (n == 0)
return h;
n--;
}
return NULL;
}
static struct location *
loc_nth(struct vhost *vhost, size_t n)
{
struct location *loc;
TAILQ_FOREACH(loc, &vhost->locations, locations) {
if (n == 0)
return loc;
n--;
}
return NULL;
}
static void
handle_imsg_cgi_req(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen)
{
struct vhost *h;
struct location *l;
struct cgireq req;
struct iri iri;
int fd;
if (datalen != sizeof(req))
abort();
memcpy(&req, imsg->data, sizeof(req));
iri.schema = req.iri_schema_off + req.buf;
iri.host = req.iri_host_off + req.buf;
iri.port = req.iri_port_off + req.buf;
iri.path = req.iri_path_off + req.buf;
iri.query = req.iri_query_off + req.buf;
iri.fragment = req.iri_fragment_off + req.buf;
/* patch the query, otherwise do_exec will always pass "" as
* first argument to the script. */
if (*iri.query == '\0')
iri.query = NULL;
if ((h = host_nth(req.host_off)) == NULL)
abort();
if ((l = loc_nth(h, req.loc_off)) == NULL)
abort();
fd = launch_cgi(&iri, &req, h, l);
imsg_compose(ibuf, IMSG_CGI_RES, imsg->hdr.peerid, 0, fd, NULL, 0);
imsg_flush(ibuf);
}
static int
fcgi_open_prog(struct fcgi *f)
{
int s[2];
pid_t p;
/* XXX! */
if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, s) == -1)
err(1, "socketpair");
switch (p = fork()) {
case -1:
err(1, "fork");
case 0:
close(s[0]);
if (dup2(s[1], 0) == -1)
err(1, "dup2");
execl(f->prog, f->prog, NULL);
err(1, "execl %s", f->prog);
default:
close(s[1]);
return s[0];
}
}
static int
fcgi_open_sock(struct fcgi *f)
{
struct sockaddr_un addr;
int fd;
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
log_err(NULL, "socket: %s", strerror(errno));
return -1;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strlcpy(addr.sun_path, f->path, sizeof(addr.sun_path));
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
log_warn(NULL, "failed to connect to %s: %s", f->path,
strerror(errno));
close(fd);
return -1;
}
return fd;
}
static int
fcgi_open_conn(struct fcgi *f)
{
struct addrinfo hints, *servinfo, *p;
int r, sock;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_ADDRCONFIG;
if ((r = getaddrinfo(f->path, f->port, &hints, &servinfo)) != 0) {
log_warn(NULL, "getaddrinfo %s:%s: %s", f->path, f->port,
gai_strerror(r));
return -1;
}
for (p = servinfo; p != NULL; p = p->ai_next) {
sock = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (sock == -1)
continue;
if (connect(sock, p->ai_addr, p->ai_addrlen) == -1) {
close(sock);
continue;
}
break;
}
if (p == NULL) {
log_warn(NULL, "couldn't connect to %s:%s", f->path, f->port);
sock = -1;
}
freeaddrinfo(servinfo);
return sock;
}
static void
handle_imsg_fcgi_req(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen)
{
struct fcgi *f;
int id, fd;
if (datalen != sizeof(id))
abort();
memcpy(&id, imsg->data, sizeof(id));
if (id > FCGI_MAX || (fcgi[id].path == NULL && fcgi[id].prog == NULL))
abort();
f = &fcgi[id];
if (f->prog != NULL)
fd = fcgi_open_prog(f);
else if (f->port != NULL)
fd = fcgi_open_conn(f);
else
fd = fcgi_open_sock(f);
imsg_compose(ibuf, IMSG_FCGI_FD, imsg->hdr.peerid, 0, fd, NULL, 0);
imsg_flush(ibuf);
}
static void
handle_imsg_conn_req(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen)
{
struct addrinfo hints, *res, *res0;
struct connreq req;
int r, sock;
if (datalen != sizeof(req))
abort();
memcpy(&req, imsg->data, sizeof(req));
req.flag = 0;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
/* XXX: do this asynchronously if possible */
r = getaddrinfo(req.host, req.port, &hints, &res0);
if (r != 0) {
log_warn(NULL, "getaddrinfo(\"%s\", \"%s\"): %s",
req.host, req.port, gai_strerror(r));
goto err;
}
for (res = res0; res; res = res->ai_next) {
sock = socket(res->ai_family, res->ai_socktype,
res->ai_protocol);
if (sock == -1)
continue;
if (connect(sock, res->ai_addr, res->ai_addrlen) == -1) {
close(sock);
sock = -1;
continue;
}
break;
}
freeaddrinfo(res0);
if (sock == -1) {
log_warn(NULL, "can't connect to %s:%s", req.host,
req.port);
goto err;
}
imsg_compose(ibuf, IMSG_CONN_FD, imsg->hdr.peerid, 0, sock, NULL, 0);
imsg_flush(ibuf);
return;
err:
imsg_compose(ibuf, IMSG_CONN_FD, imsg->hdr.peerid, 0, -1, NULL, 0);
imsg_flush(ibuf);
}
static void
handle_imsg_quit(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen)
{
int i;
for (i = 0; i < conf.prefork; ++i) {
imsg_compose(&servibuf[i], IMSG_QUIT, 0, 0, -1, NULL, 0);
imsg_flush(&exibuf);
close(servibuf[i].fd);
}
event_loopbreak();
}
static void
handle_dispatch_imsg(int fd, short ev, void *d)
{
struct imsgbuf *ibuf = d;
dispatch_imsg(ibuf, handlers, sizeof(handlers));
}
int
executor_main(struct imsgbuf *ibuf)
{
struct event evs[PROC_MAX], imsgev;
int i;
event_init();
if (ibuf != NULL) {
event_set(&imsgev, ibuf->fd, EV_READ | EV_PERSIST,
handle_dispatch_imsg, ibuf);
event_add(&imsgev, NULL);
}
for (i = 0; i < conf.prefork; ++i) {
event_set(&evs[i], servibuf[i].fd, EV_READ | EV_PERSIST,
handle_dispatch_imsg, &servibuf[i]);
event_add(&evs[i], NULL);
}
sandbox_executor_process();
event_dispatch();
return 1;
}

246
fcgi.c
View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021 Omar Polo <op@omarpolo.com>
* 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
@ -17,27 +17,11 @@
#include "gmid.h"
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
/*
* Sometimes it can be useful to inspect the fastcgi traffic as
* received by gmid.
*
* This will make gmid connect to a `debug.sock' socket (that must
* exists) in the current directory and send there a copy of what gets
* read. The socket can be created and monitored e.g. with
*
* rm -f debug.sock ; nc -Ulk ./debug.sock | hexdump -C
*
* NB: the sandbox must be disabled for this to work.
*/
#define DEBUG_FCGI 0
#if DEBUG_FCGI
# include <sys/un.h>
static int debug_socket = -1;
#endif
#include "log.h"
struct fcgi_header {
unsigned char version;
@ -228,6 +212,65 @@ 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)
{
@ -237,31 +280,14 @@ fcgi_read(struct bufferevent *bev, void *d)
struct fcgi_end_req_body end;
size_t len;
#if DEBUG_FCGI
if (debug_socket == -1) {
struct sockaddr_un addr;
if ((debug_socket = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
err(1, "socket");
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strlcpy(addr.sun_path, "./debug.sock", sizeof(addr.sun_path));
if (connect(debug_socket, (struct sockaddr*)&addr, sizeof(addr))
== -1)
err(1, "connect");
}
#endif
for (;;) {
while (c->type != REQUEST_DONE) {
if (EVBUFFER_LENGTH(src) < sizeof(hdr))
return;
memcpy(&hdr, EVBUFFER_DATA(src), sizeof(hdr));
if (recid(&hdr) != 1) {
log_err(NULL,
"got invalid client id %d from fcgi backend",
log_warnx("got invalid client id %d from fcgi backend",
recid(&hdr));
goto err;
}
@ -271,25 +297,19 @@ fcgi_read(struct bufferevent *bev, void *d)
if (EVBUFFER_LENGTH(src) < sizeof(hdr) + len + hdr.padding)
return;
#if DEBUG_FCGI
write(debug_socket, EVBUFFER_DATA(src),
sizeof(hdr) + len + hdr.padding);
#endif
evbuffer_drain(src, sizeof(hdr));
switch (hdr.type) {
case FCGI_END_REQUEST:
if (len != sizeof(end)) {
log_err(NULL,
"got invalid end request record size");
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;
client_write(c->bev, c);
break;
case FCGI_STDERR:
@ -298,12 +318,11 @@ fcgi_read(struct bufferevent *bev, void *d)
break;
case FCGI_STDOUT:
bufferevent_write(c->bev, EVBUFFER_DATA(src), len);
evbuffer_drain(src, len);
fcgi_handle_stdout(c, src, len);
break;
default:
log_err(NULL, "got invalid fcgi record (type=%d)",
log_warnx("got invalid fcgi record (type=%d)",
hdr.type);
goto err;
}
@ -313,6 +332,7 @@ fcgi_read(struct bufferevent *bev, void *d)
err:
fcgi_error(bev, EVBUFFER_ERROR, c);
client_write(c->bev, c);
}
void
@ -329,45 +349,128 @@ fcgi_error(struct bufferevent *bev, short err, void *d)
{
struct client *c = d;
if (!(err & (EVBUFFER_ERROR|EVBUFFER_EOF)))
log_warn(NULL, "unknown event error (%x): %s",
err, strerror(errno));
/*
* 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;
if (c->code != 0)
client_close(c);
}
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
start_reply(c, CGI_ERROR, "CGI error");
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)
fcgi_req(struct client *c, struct location *loc)
{
char addr[NI_MAXHOST], buf[22];
int e;
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;
e = getnameinfo((struct sockaddr*)&c->addr, sizeof(c->addr),
addr, sizeof(addr),
NULL, 0,
NI_NUMERICHOST);
if (e != 0)
fatal("getnameinfo failed: %s (%s)",
gai_strerror(e), strerror(errno));
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, "GEMINI_URL_PATH", c->iri.path);
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", addr);
fcgi_send_param(c->cgibev, "REMOTE_HOST", addr);
fcgi_send_param(c->cgibev, "REQUEST_METHOD", "");
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",
@ -395,9 +498,6 @@ fcgi_req(struct client *c)
gmtime_r(&tim, &tminfo));
fcgi_send_param(c->cgibev, "TLS_CLIENT_NOT_AFTER", buf);
TAILQ_FOREACH(p, &c->host->params, envs) {
fcgi_send_param(c->cgibev, p->name, p->value);
}
} else
fcgi_send_param(c->cgibev, "AUTH_TYPE", "");

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 .

35
gg.1
View File

@ -11,7 +11,7 @@
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.Dd $Mdocdate: January 30 2022$
.Dd $Mdocdate: October 19 2023$
.Dt GG 1
.Os
.Sh NAME
@ -20,7 +20,7 @@
.Sh SYNOPSIS
.Nm
.Bk -words
.Op Fl 23Nnv
.Op Fl 23Nn
.Op Fl C Ar cert
.Op Fl d Ar mode
.Op Fl H Ar sni
@ -37,9 +37,9 @@ fetches the given gemini page and prints it to standard output.
The options are as follows:
.Bl -tag -width Ds
.It Fl 2
Accept only TLSv1.2.
Use TLSv1.2.
.It Fl 3
Accept only TLSv1.3.
Use TLSv1.3.
.It Fl C Ar certificate
Use the given client
.Ar certificate .
@ -50,15 +50,15 @@ should print.
.Ar mode
can be one of:
.Bl -tag -width header -compact
.It none
print only the body of the reply
.It code
print only the response code
.It header
print only the response header
.It meta
print only the response meta
.It all
.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
@ -73,7 +73,7 @@ is used.
.It Fl N
Disables the server name verification.
.It Fl n
Check that the given IRI is valid, but don't make any requests.
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
@ -92,12 +92,17 @@ after
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
@ -106,7 +111,7 @@ utility was written by
.An Omar Polo Aq Mt op@omarpolo.com .
.Sh CAVEATS
.Nm
doesn't do any TOFU
doesn't perform TOFU
.Pq Trust On First Use
or any X.509 certificate validation beyond the name verification.
.Pp

87
gg.c
View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021 Omar Polo <op@omarpolo.com>
* 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
@ -21,7 +21,9 @@
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <locale.h>
#include <string.h>
#include <wchar.h>
enum debug {
DEBUG_NONE,
@ -39,7 +41,6 @@ int flag3;
int nop;
int redirects = 5;
int timer;
int verbose;
const char *cert;
const char *key;
const char *proxy_host;
@ -157,7 +158,7 @@ doreq(struct tls *ctx, const char *buf)
}
static size_t
dorep(struct tls *ctx, void *buf, size_t len)
dorep(struct tls *ctx, uint8_t *buf, size_t len)
{
ssize_t w;
size_t tot = 0;
@ -181,6 +182,26 @@ dorep(struct tls *ctx, void *buf, size_t len)
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)
{
@ -189,7 +210,7 @@ get(const char *r)
int foundhdr = 0, code = -1, od;
char iribuf[GEMINI_URL_LEN];
char req[GEMINI_URL_LEN];
char buf[2048];
uint8_t buf[2048];
const char *parse_err, *host, *port;
if (strlcpy(iribuf, r, sizeof(iribuf)) >= sizeof(iribuf))
@ -231,18 +252,15 @@ get(const char *r)
}
}
if (verbose)
printf("%s", req);
doreq(ctx, req);
for (;;) {
char *t;
uint8_t *t;
size_t len;
len = dorep(ctx, buf, sizeof(buf));
if (len == 0)
goto close;
break;
if (foundhdr) {
write(1, buf, len);
@ -252,14 +270,16 @@ get(const char *r)
if (memmem(buf, len, "\r\n", 2) == NULL)
errx(1, "invalid reply: no \\r\\n");
if (!isdigit(buf[0]) || !isdigit(buf[1]) || buf[2] != ' ')
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);
goto close;
break;
}
if (debug == DEBUG_HEADER) {
@ -267,7 +287,7 @@ get(const char *r)
assert(t != NULL);
*t = '\0';
printf("%s\n", buf);
goto close;
break;
}
if (debug == DEBUG_META) {
@ -275,7 +295,7 @@ get(const char *r)
assert(t != NULL);
*t = '\0';
printf("%s\n", buf+3);
goto close;
break;
}
if (debug == DEBUG_ALL) {
@ -286,25 +306,36 @@ get(const char *r)
/* skip the header */
t = memmem(buf, len, "\r\n", 2);
assert(t != NULL);
if (code < 20 || code >= 30) {
*t = '\0';
fprintf(stderr, "Server says: ");
safeprint(stderr, buf + 3); /* skip return code */
}
t += 2; /* skip \r\n */
len -= t - buf;
write(1, t, len);
}
close:
od = tls_close(ctx);
if (od == TLS_WANT_POLLIN || od == TLS_WANT_POLLOUT)
goto close;
tls_close(ctx);
tls_free(ctx);
return code;
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, "usage: %s [-23Nnv] [-C cert] [-d mode] [-H sni] "
fprintf(stderr, "version: " GG_STRING "\n");
fprintf(stderr, "usage: %s [-23Nn] [-C cert] [-d mode] [-H sni] "
"[-K key] [-P host[:port]]\n",
getprogname());
fprintf(stderr, " [-T seconds] gemini://...\n");
@ -352,7 +383,9 @@ main(int argc, char **argv)
int ch, code;
const char *errstr;
while ((ch = getopt(argc, argv, "23C:d:H:K:NP:T:v")) != -1) {
setlocale(LC_CTYPE, "");
while ((ch = getopt(argc, argv, "23C:d:H:K:NP:T:")) != -1) {
switch (ch) {
case '2':
flag2 = 1;
@ -390,9 +423,6 @@ main(int argc, char **argv)
signal(SIGALRM, timeout);
alarm(timer);
break;
case 'v':
verbose++;
break;
default:
usage();
}
@ -424,6 +454,7 @@ main(int argc, char **argv)
#endif
code = get(*argv);
return code < 20 || code >= 30;
if (code >= 20 && code < 30)
return 0;
return code;
}

View File

@ -1,4 +1,4 @@
.\" Copyright (c) 2021, 2022 Omar Polo <op@omarpolo.com>
.\" 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
@ -11,44 +11,37 @@
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.Dd $Mdocdate: April 7 2022$
.Dt GMID 1
.Dd April 27, 2024
.Dt GMID 8
.Os
.Sh NAME
.Nm gmid
.Nd simple and secure Gemini server
.Nd Gemini server
.Sh SYNOPSIS
.Nm
.Bk -words
.Op Fl fnv
.Op Fl fhnVv
.Op Fl c Ar config
.Op Fl D Ar macro Ns = Ns Ar value
.Op Fl P Ar pidfile
.Ek
.Nm
.Bk -words
.Op Fl 6hVv
.Op Fl d Ar certs-dir
.Op Fl H Ar hostname
.Op Fl p Ar port
.Op Fl x Ar cgi
.Op Ar dir
.Ek
.Sh DESCRIPTION
.Nm
is a simple and minimal gemini server that can serve static files,
execute CGI scripts and talk to FastCGI applications.
It can run without a configuration file with a limited set of features
available.
talk to FastCGI applications and act as a gemini reverse proxy.
.Pp
.Nm
rereads the configuration file when it receives
.Dv SIGHUP .
.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
Specify the configuration file.
Specifies the configuration file.
The default is
.Pa /etc/gmid.conf .
.It Fl D Ar macro Ns = Ns Ar value
Define
.Ar macro
@ -59,7 +52,10 @@ Overrides the definition of
.Ar macro
in the config file if present.
.It Fl f
Stays and logs on the foreground.
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
@ -71,120 +67,15 @@ will also act as lock: if another process is holding a lock on that
file,
.Nm
will refuse to start.
.El
.Pp
If no configuration file is given,
.Nm
runs in
.Dq config-less mode
.Pq i.e. runs in the foreground to serve a directory from the shell
and looks for the following options
.Bl -tag -width 14m
.It Fl 6
Enable IPv6.
.It Fl d Ar certs-path
Directory where certificates for the config-less mode are stored.
By default it is
.Pa $XDG_DATA_HOME/gmid ,
i.e.
.Pa ~/.local/share/gmid .
.It Fl H Ar hostname
The hostname
.Po
.Ar localhost
by default
.Pc .
Certificates for the given
.Ar hostname
are searched inside the
.Ar certs-dir
directory given with the
.Fl d
option.
They have the form
.Pa hostname.cert.pem
and
.Pa hostname.key.pem .
If a certificate or a key doesn't exist for a given hostname, they
will be generated automatically.
.It Fl h , Fl -help
Print the usage and exit.
.It Fl p Ar port
The port to listen on, by default 1965.
.It Fl V , Fl -version
Print the version and exit.
.It Fl v
Verbose mode.
Multiple
.Fl v
options increase the verbosity.
.It Fl x Ar path
Enable execution of
.Sx CGI
scripts.
See the description of the
.Ic cgi
option in the
.Sq Servers
section below to learn how
.Ar path
is processed.
Cannot be provided more than once.
.It Ar dir
The root directory to serve.
By default the current working directory is assumed.
.El
.Sh LOGGING
Messages and requests are logged by
.Xr syslog 3
using the
.Dv DAEMON
facility or printed on
.Em stderr .
.Pp
Requests are logged with the
.Dv NOTICE
severity.
Each request log entry has the following fields, separated by
whitespace:
.Pp
.Bl -bullet -compact
.It
Client IP address and the source port number, separated by a colon
.It
.Sy GET
keyword
.It
Request URL
.It
Response status
.It
Response meta
.El
.Sh EXAMPLES
Serve the current directory
.Bd -literal -offset indent
$ gmid .
.Ed
.Pp
To serve the directory
.Pa docs
and enable CGI scripts inside
.Pa docs/cgi
.Bd -literal -offset indent
$ mkdir docs/cgi
$ cat <<EOF > docs/cgi/hello
#!/bin/sh
printf "20 text/plain\er\en"
echo "hello world"
EOF
$ chmod +x docs/cgi/hello
$ gmid -x '/cgi/*' docs
.Ed
.Pp
To run
.Nm
as a deamon a configuration file and a X.509 certificate must be provided.
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 :
@ -204,6 +95,8 @@ can be started with
# gmid -c /etc/gmid.conf
.Ed
.Sh SEE ALSO
.Xr gemexp 1 ,
.Xr gg 1 ,
.Xr gmid.conf 5
.Sh ACKNOWLEDGEMENTS
.Nm
@ -220,10 +113,10 @@ program was written by
.Sh CAVEATS
.Bl -bullet
.It
All the root directories are opened during the daemon startup; if a
root directory is deleted and then re-created,
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 restart.
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

1097
gmid.c

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
.\" Copyright (c) 2022 Omar Polo <op@omarpolo.com>
.\" 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
@ -11,7 +11,7 @@
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.Dd $Mdocdate: April 7 2022$
.Dd April 4, 2024
.Dt GMID.CONF 5
.Os
.Sh NAME
@ -20,26 +20,26 @@
.Sh DESCRIPTION
.Nm
is the configuration file format for the
.Xr gmid 1
.Xr gmid 8
Gemini server.
.Pp
The configuration file is divided into three sections:
.Bl -tag -width xxxx
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
.Nm .
.It Sy Servers
Virtual hosts definition.
.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 # ,
.Pq Sq #
and extend to the end of the current line.
A boolean is either the symbol
.Sq on
@ -99,14 +99,16 @@ 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" {
root $dir "/foo" # -> /var/gemini/foo
cert $certdir "/foo.pem" # -> /etc/keys/foo.pem
key $certdir "/foo.key" # -> /etc/keys/foo.key
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
@ -118,26 +120,94 @@ 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, so privileges can be dropped.
Note that
.Nm
will enter the chroot after loading the TLS keys, but before opening
the virtual host root directories.
It's recommended to keep the TLS keys outside the chroot.
Future version of
.Nm
may enforce this.
.It Ic ipv6 Ar bool
Enable or disable IPv6 support, off by default.
.It Ic port Ar portno
The port to listen on.
1965 by default.
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.
When not in config-less mode,
.Nm
.Xr gmid 8
runs 3 server processes by default.
The maximum number allowed is 16.
.It Ic protocols Ar string
@ -151,6 +221,9 @@ Use
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
@ -161,7 +234,7 @@ block:
Match the server name using shell globbing rules.
It can be an explicit name,
.Ar www.example.com ,
or a name including a wildcards,
or a name including wildcards,
.Ar *.example.com .
.El
.Pp
@ -209,31 +282,72 @@ Path to the certificate to use for this server.
.Ar file
should contain a PEM encoded certificate.
This option is mandatory.
.It Ic cgi Ar path
Execute
.Sx CGI
scripts that matches
.Ar path
using shell globbing rules.
.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 CGI scripts are executed in the directory they reside and inherit
the environment from
.Nm gmid
with these additional variables set:
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 GEMINI_DOCUMENT_ROOT
The root directory of the virtual host.
.It Ev GEMINI_SCRIPT_FILENAME
Full path to the CGI script being executed.
.It Ev GEMINI_URL
The full IRI of the request.
.It Ev GEMINI_URL_PATH
The path of the request.
.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 the part that identifies the script itself.
path hierarchy following
.Ev SCRIPT_NAME .
Can be unset.
.It Ev PATH_TRANSLATED
Present if and only if
@ -246,16 +360,22 @@ builds this by appending the
.Ev PATH_INFO
to the virtual host directory root.
.It Ev QUERY_STRING
The decoded 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 the empty string.
It's always set to
.Dq GET .
.It Ev SCRIPT_NAME
The part of the
.Ev GEMINI_URL_PATH
that identifies the current CGI script.
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
@ -264,10 +384,7 @@ The port the server is listening on.
.Dq GEMINI
.It Ev SERVER_SOFTWARE
The name and version of the server, i.e.
.Dq gmid/1.8.4
.It Ev AUTH_TYPE
The string "Certificate" if the client used a certificate, otherwise
unset.
.Dq gmid/2.0.2
.It Ev REMOTE_USER
The subject of the client certificate if provided, otherwise unset.
.It Ev TLS_CLIENT_ISSUER
@ -292,50 +409,8 @@ certificate in the ISO 8601 format
The time corresponding to the start of the validity period of the peer
certificate in the ISO 8601 format.
.El
.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 entrypoint Ar path
Handle all the requests for the current virtual host using the
.Sx CGI
script at
.Ar path ,
relative to the current document root.
.It Ic env Ar name Cm = Ar value
Set the environment variable
.Ar name
to
.Ar value
when executing CGI scripts.
Can be provided more than once.
.\" don't document the "spawn <prog>" form because it probably won't
.\" be kept.
.It Ic fastcgi Oo Ic tcp Oc Ar socket Oo Cm port Ar port Oc
Enable
.Sx FastCGI
instead of serving files.
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
.Nm .
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 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
@ -350,6 +425,25 @@ 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
@ -363,58 +457,11 @@ A
.Ic location
section may include most of the server configuration rules
except
.Ic alias , Ic cert , Ic cgi , Ic entrypoint , Ic env , Ic key ,
.Ic location , Ic param No and Ic proxy .
.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 param Ar name Cm = Ar value
Set the param
.Ar name
to
.Ar value
for FastCGI.
By default the following variables
.Pq parameters
are sent, and carry the same semantics as with CGI:
.Pp
.Bl -bullet -compact
.It
GATEWAY_INTERFACE
.It
GEMINI_URL_PATH
.It
QUERY_STRING
.It
REMOTE_ADDR
.It
REMOTE_HOST
.It
REQUEST_METHOD
.It
SERVER_NAME
.It
SERVER_PROTOCOL
.It
SERVER_SOFTWARE
.It
AUTH_TYPE
.It
REMOTE_USER
.It
TLS_CLIENT_ISSUER
.It
TLS_CLIENT_HASH
.It
TLS_VERSION
.It
TLS_CIPHER
.It
TLS_CIPHER_STRENGTH
.It
TLS_CLIENT_NOT_BEFORE
.It
TLS_CLIENT_NOT_AFTER
.El
.It Ic ocsp Ar file
Specify an OCSP response to be stapled during TLS handshakes
with this server.
@ -428,7 +475,7 @@ 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 : Ns Oo Ar port Oc Oc Brq ...
.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
@ -462,7 +509,7 @@ 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 : Ns Op Ar port
.It Ic relay-to Ar host Op Cm port Ar port
Relay the request to the given
.Ar host
at the given
@ -514,7 +561,7 @@ The
section must include one or more lines of the following syntax, enclosed
in curly brances:
.Bl -tag -width Ds
.It Ar type/subtype Ar name Op Ar name ...
.It Ar type Ns / Ns Ar subtype Ar name Op Ar name ...
Set the media
.Ar type
and
@ -533,6 +580,7 @@ By default
uses the following mapping if no
.Ic types
block is defined:
.Pp
.Bl -tag -offset indent -width 15m -compact
.It application/pdf
pdf
@ -569,8 +617,6 @@ 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
ipv6 on # enable ipv6
protocols "tlsv1.3"
types {
@ -578,19 +624,18 @@ 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"
# execute cgi scripts inside "cgi-bin"
cgi "/cgi-bin/*"
# set the language for text/gemini files
lang "it"
}
@ -606,6 +651,8 @@ 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"
@ -622,8 +669,26 @@ server "example.com" {
}
}
.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 1 ,
.Xr gmid 8 ,
.Xr slowcgi 8
.Sh AUTHORS
.An -nosplit

379
gmid.h
View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2021, 2022 Omar Polo <op@omarpolo.com>
* 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
@ -50,7 +50,13 @@
# include <event.h>
#endif
#define GMID_STRING "gmid " VERSION
#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 */
@ -70,17 +76,25 @@
#define DOMAIN_NAME_LEN (253+1)
#define LABEL_LEN (63+1)
#define FCGI_MAX 32
#define PROC_MAX 16
#define MEDIATYPE_NAMEMAX 128 /* file name extension */
#define MEDIATYPE_TYPEMAX 128 /* length of type/subtype */
struct iri {
char *schema;
char *host;
char *port;
uint16_t port_no;
char *path;
char *query;
char *fragment;
#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 {
@ -89,30 +103,66 @@ struct parser {
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;
/* 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;
char *port;
char *prog;
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;
};
extern struct fcgi fcgi[FCGI_MAX];
TAILQ_HEAD(proxyhead, proxy);
struct proxy {
char *match_proto;
char *match_host;
const char *match_port;
char match_proto[32];
char match_host[HOST_NAME_MAX + 1];
char match_port[32];
char *host;
const char *port;
char *sni;
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;
@ -120,48 +170,48 @@ struct proxy {
TAILQ_HEAD(lochead, location);
struct location {
const char *match;
const char *lang;
const char *default_mime;
const char *index;
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;
const char *block_fmt;
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;
const char *dir;
char dir[PATH_MAX];
int dirfd;
TAILQ_ENTRY(location) locations;
};
TAILQ_HEAD(envhead, envlist);
struct envlist {
char *name;
char *value;
TAILQ_ENTRY(envlist) envs;
};
TAILQ_HEAD(aliashead, alist);
struct alist {
char *alias;
TAILQ_ENTRY(alist) aliases;
};
extern TAILQ_HEAD(vhosthead, vhost) hosts;
TAILQ_HEAD(vhosthead, vhost);
struct vhost {
const char *domain;
const char *cert;
const char *key;
const char *ocsp;
const char *cgi;
const char *entrypoint;
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
@ -169,51 +219,53 @@ struct vhost {
*/
struct lochead locations;
struct envhead env;
struct envhead params;
struct aliashead aliases;
struct proxyhead proxies;
};
struct etm { /* extension to mime */
char *mime;
char *ext;
char mime[MEDIATYPE_TYPEMAX];
char ext[MEDIATYPE_NAMEMAX];
};
struct mime {
struct etm *t;
size_t len;
size_t cap;
};
/*
* Backward compatibility: types override the built-in list,
* but the deprecated `mime' and `map' don't. It's still too
* early to remove `mime' and `map' from the config parser.
*/
int skip_defaults;
TAILQ_HEAD(pkihead, pki);
struct pki {
char *hash;
EVP_PKEY *pkey;
TAILQ_ENTRY(pki) pkis;
};
struct conf {
/* from command line */
int foreground;
int verbose;
/* in the config */
int port;
int ipv6;
struct privsep *ps;
uint32_t protos;
struct mime mime;
char *chroot;
char *user;
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 struct conf conf;
extern struct imsgbuf logibuf, exibuf, servibuf[PROC_MAX];
extern int servpipes[PROC_MAX];
extern int servpipes[PROC_MAX_INSTANCES];
extern int privsep_process;
typedef void (imsg_handlerfn)(struct imsgbuf*, struct imsg*, size_t);
@ -221,24 +273,22 @@ enum {
REQUEST_UNDECIDED,
REQUEST_FILE,
REQUEST_DIR,
REQUEST_CGI,
REQUEST_FCGI,
REQUEST_PROXY,
REQUEST_DONE,
};
#define IS_INTERNAL_REQUEST(x) \
((x) != REQUEST_CGI && \
(x) != REQUEST_FCGI && \
(x) != REQUEST_PROXY)
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;
@ -262,9 +312,11 @@ struct client {
/* big enough to store STATUS + SPACE + META + CRLF */
char sbuf[1029];
ssize_t len, off;
size_t soff;
struct sockaddr_storage raddr;
socklen_t raddrlen;
struct sockaddr_storage addr;
struct vhost *host; /* host they're talking to */
size_t loc; /* location matched */
@ -273,97 +325,74 @@ struct client {
SPLAY_HEAD(client_tree_id, client);
extern struct client_tree_id clients;
struct cgireq {
char buf[GEMINI_URL_LEN];
size_t iri_schema_off;
size_t iri_host_off;
size_t iri_port_off;
size_t iri_path_off;
size_t iri_query_off;
size_t iri_fragment_off;
int iri_portno;
char spath[PATH_MAX+1];
char relpath[PATH_MAX+1];
char addr[NI_MAXHOST+1];
/* AFAIK there isn't an upper limit for these two fields. */
char subject[64+1];
char issuer[64+1];
char hash[128+1];
char version[8];
char cipher[32];
int cipher_strength;
time_t notbefore;
time_t notafter;
size_t host_off;
size_t loc_off;
};
struct connreq {
char host[NI_MAXHOST];
char port[NI_MAXSERV];
int flag;
};
enum {
FILE_EXISTS,
FILE_EXECUTABLE,
FILE_DIRECTORY,
FILE_MISSING,
};
enum imsg_type {
IMSG_CGI_REQ,
IMSG_CGI_RES,
IMSG_FCGI_REQ,
IMSG_FCGI_FD,
IMSG_CONN_REQ,
IMSG_CONN_FD,
IMSG_LOG,
IMSG_LOG_REQUEST,
IMSG_LOG_TYPE,
IMSG_QUIT,
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(const char*, const char*);
void load_vhosts(void);
int make_socket(int, int);
void setup_tls(void);
void init_config(void);
void free_config(void);
void drop_priv(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*, ...);
void parse_conf(const char*);
void print_conf(void);
int parse_conf(struct conf *, const char*);
int cmdline_symset(char *);
/* log.c */
void fatal(const char*, ...)
__attribute__((format (printf, 1, 2)))
__attribute__((__noreturn__));
#define LOG_ATTR_FMT __attribute__((format (printf, 2, 3)))
void log_err(struct client*, const char*, ...) LOG_ATTR_FMT;
void log_warn(struct client*, const char*, ...) LOG_ATTR_FMT;
void log_notice(struct client*, const char*, ...) LOG_ATTR_FMT;
void log_info(struct client*, const char*, ...) LOG_ATTR_FMT;
void log_debug(struct client*, const char*, ...) LOG_ATTR_FMT;
void log_request(struct client*, char*, size_t);
int logger_main(int, struct imsgbuf*);
/* 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 vhost*, const char*);
const char *mime(struct conf *, struct vhost*, const char*);
void free_mime(struct mime *);
/* server.c */
@ -373,7 +402,7 @@ 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**);
int vhost_fastcgi(struct vhost*, 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*);
@ -381,10 +410,12 @@ int vhost_disable_log(struct vhost*, const char*);
void mark_nonblock(int);
void client_write(struct bufferevent *, void *);
void start_reply(struct client*, int, const char*);
int start_reply(struct client*, int, const char*);
void client_close(struct client *);
struct client *client_by_id(int);
void loop(struct tls*, int, int, struct imsgbuf*);
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);
@ -395,40 +426,24 @@ int scandir_fd(int, struct dirent***, int(*)(const struct dirent*),
int select_non_dot(const struct dirent*);
int select_non_dotdot(const struct dirent*);
/* ex.c */
int send_string(int, const char*);
int recv_string(int, char**);
int send_iri(int, struct iri*);
int recv_iri(int, struct iri*);
void free_recvd_iri(struct iri*);
int send_vhost(int, struct vhost*);
int recv_vhost(int, struct vhost**);
int send_time(int, time_t);
int recv_time(int, time_t*);
int send_fd(int, int);
int recv_fd(int);
int executor_main(struct imsgbuf*);
/* 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 *);
void fcgi_req(struct client *, struct location *);
/* sandbox.c */
void sandbox_main_process(void);
void sandbox_server_process(void);
void sandbox_executor_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);
/* iri.c */
int parse_iri(char*, struct iri*, const char**);
int serialize_iri(struct iri*, char*, size_t);
int encode_path(char *, size_t, const char *);
char *pct_decode_str(char *);
/* logger.c */
void logger(struct privsep *, struct privsep_proc *);
/* proxy.c */
int proxy_init(struct client *);
@ -437,17 +452,19 @@ int proxy_init(struct client *);
int puny_decode(const char*, char*, size_t, const char**);
/* utils.c */
void block_signals(void);
void unblock_signals(void);
int starts_with(const char*, const char*);
const char *strip_path(const char *, int);
int ends_with(const char*, const char*);
ssize_t filesize(int);
char *absolutify_path(const char*);
char *xstrdup(const char*);
void *xcalloc(size_t, size_t);
void gen_certificate(const char*, const char*, const char*);
X509_STORE *load_ca(const char*);
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 dispatch_imsg(struct imsgbuf*, imsg_handlerfn**, 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/getentropy.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 <stdio.h>
#include <unistd.h>
int
main(void)
{
char buf[1024];
return getentropy(buf, sizeof(buf));
}

View File

@ -24,7 +24,8 @@ int
main(void)
{
struct imsgbuf buf;
struct imsg imsg;
imsg_init(&buf, -1);
return 0;
return imsg_get_fd(&imsg);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021 Omar Polo <op@omarpolo.com>
* 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
@ -14,7 +14,21 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "../landlock_shim.h"
#include <linux/landlock.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <stddef.h>
#ifndef landlock_create_ruleset
static inline int
landlock_create_ruleset(const struct landlock_ruleset_attr *attr, size_t size,
__u32 flags)
{
return syscall(__NR_landlock_create_ruleset, attr, size, flags);
}
#endif
int
main(void)

28
have/machine_endian.c Normal file
View File

@ -0,0 +1,28 @@
/*
* 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 <machine/endian.h>
#include <libkern/OSByteOrder.h>
#include <stdint.h>
int
main(void)
{
uint16_t x;
x = 42;
return (OSSwapHostToBigInt16(x));
}

27
have/memmem.c Normal file
View File

@ -0,0 +1,27 @@
/*
* Copyright (c) 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.
*/
#include <string.h>
int
main(void)
{
char big[33], little[2];
memset(big, 0, sizeof(big));
memset(little, 0, sizeof(little));
return (memmem(big, sizeof(big), little, sizeof(little)) == NULL);
}

8
have/setresgid.c Normal file
View File

@ -0,0 +1,8 @@
#include <sys/types.h>
#include <unistd.h>
int
main(void)
{
return setresgid(-1, -1, -1) == -1;
}

8
have/setresuid.c Normal file
View File

@ -0,0 +1,8 @@
#include <sys/types.h>
#include <unistd.h>
int
main(void)
{
return setresuid(-1, -1, -1) == -1;
}

27
have/sys_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 <sys/endian.h>
#include <stdint.h>
int
main(void)
{
uint16_t x;
x = 42;
return (htobe16(x));
}

26
have/timingsafe_memcmp.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 <string.h>
int
main(void)
{
const char *a = "foo";
const char *b = "bar";
return timingsafe_memcmp(a, b, 3);
}

26
have/vis.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 <vis.h>
int
main(void)
{
char buf[128];
return strnvis(buf, "Hello, world!\n", sizeof(buf), 0);
}

23
have/wait_any.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 <sys/wait.h>
int
main(void)
{
return WAIT_ANY;
}

15
iri.c
View File

@ -22,7 +22,7 @@
static inline int
unreserved(int p)
{
return isalnum(p)
return isalnum((unsigned char)p)
|| p == '-'
|| p == '.'
|| p == '_'
@ -51,7 +51,8 @@ valid_pct_enc_string(char *s)
if (*s != '%')
return 1;
if (!isxdigit(s[1]) || !isxdigit(s[2]))
if (!isxdigit((unsigned char)s[1]) ||
!isxdigit((unsigned char)s[2]))
return 0;
if (s[1] == '0' && s[2] == '0')
@ -108,7 +109,7 @@ parse_scheme(struct parser *p)
{
p->parsed->schema = p->iri;
if (!isalpha(*p->iri)) {
if (!isalpha((unsigned char)*p->iri)) {
p->err = "illegal character in scheme";
return 0;
}
@ -125,7 +126,7 @@ parse_scheme(struct parser *p)
*/
*p->iri = tolower(*p->iri);
p->iri++;
} while (isalnum(*p->iri)
} while (isalnum((unsigned char)*p->iri)
|| *p->iri == '+'
|| *p->iri == '-'
|| *p->iri == '.');
@ -153,7 +154,7 @@ parse_port(struct parser *p)
p->parsed->port = p->iri;
for (; isdigit(*p->iri); p->iri++) {
for (; isdigit((unsigned char)*p->iri); p->iri++) {
i = i * 10 + *p->iri - '0';
if (i > UINT16_MAX) {
p->err = "port number too large";
@ -487,7 +488,9 @@ pct_decode_str(char *s)
char *t;
for (t = s; *t; ++t) {
if (*t == '%' && valid_pct_enc_string(t))
if (*t == '+')
*t = ' ';
else if (*t == '%' && valid_pct_enc_string(t))
pct_decode(t);
}

30
iri.h Normal file
View File

@ -0,0 +1,30 @@
/*
* 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.
*/
struct iri {
char *schema;
char *host;
char *port;
uint16_t port_no;
char *path;
char *query;
char *fragment;
};
int parse_iri(char*, struct iri*, const char**);
int serialize_iri(struct iri*, char*, size_t);
int encode_path(char *, size_t, const char *);
char *pct_decode_str(char *);

10
keys/Makefile Normal file
View File

@ -0,0 +1,10 @@
DISTFILES = Makefile gmid-1.7.pub gmid-1.8.pub gmid-2.0.pub
all: false
dist: ${DISTFILES}
mkdir -p ${DESTDIR}
${INSTALL} -m 0644 ${DISTFILES} ${DESTDIR}/
.PHONY: all dist
include ../config.mk

2
keys/gmid-1.7.pub Normal file
View File

@ -0,0 +1,2 @@
untrusted comment: signify public key
RWSK+qgSqgu20CEZZQTAExCxaGaOwGO7AWqru6BKLqQhQDy8Iz1tjXNE

2
keys/gmid-1.8.pub Normal file
View File

@ -0,0 +1,2 @@
untrusted comment: signify public key
RWTy3UJQzpxBUAymBwb2EGLLm0b3H/1n8hzhaC9HYFYzNuTavGt9QSwC

2
keys/gmid-2.0.pub Normal file
View File

@ -0,0 +1,2 @@
untrusted comment: signify public key
RWQ+Bm0F0FtPLtTnpRe09x/Z6Fiodk4toTZe2TJ4yCqDZ6l0c5wiU9te

View File

@ -1,64 +0,0 @@
/*
* 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.
*/
/*
* What's the deal with landlock? While distro with linux >= 5.13
* have the struct declarations, libc wrappers are missing. The
* sample landlock code provided by the authors includes these "shims"
* in their example for the landlock API until libc provides them.
*
* Linux is such a mess sometimes. /rant
*/
#ifndef LANDLOCK_SHIM_H
#define LANDLOCK_SHIM_H
#include <linux/landlock.h>
#include <linux/prctl.h>
#include <sys/prctl.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <unistd.h>
#ifndef landlock_create_ruleset
static inline int
landlock_create_ruleset(const struct landlock_ruleset_attr *attr, size_t size,
__u32 flags)
{
return syscall(__NR_landlock_create_ruleset, attr, size, flags);
}
#endif
#ifndef landlock_add_rule
static inline int
landlock_add_rule(int ruleset_fd, enum landlock_rule_type type,
const void *attr, __u32 flags)
{
return syscall(__NR_landlock_add_rule, ruleset_fd, type, attr, flags);
}
#endif
#ifndef landlock_restrict_self
static inline int
landlock_restrict_self(int ruleset_fd, __u32 flags)
{
return syscall(__NR_landlock_restrict_self, ruleset_fd, flags);
}
#endif
#endif /* LANDLOCK_SHIM_H */

462
log.c
View File

@ -1,5 +1,7 @@
/* $OpenBSD: log.c,v 1.1 2018/07/10 16:39:54 florian Exp $ */
/*
* Copyright (c) 2021 Omar Polo <op@omarpolo.com>
* 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
@ -16,336 +18,182 @@
#include "gmid.h"
#include <sys/types.h>
#include <sys/uio.h>
#include <errno.h>
#include <event.h>
#include <imsg.h>
#include <netdb.h>
#include <poll.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <syslog.h>
#include <errno.h>
#include <time.h>
static struct event imsgev;
#include "log.h"
static FILE *log;
static int debug;
static int verbose;
static const char *log_procname;
static void handle_imsg_quit(struct imsgbuf*, struct imsg*, size_t);
static void handle_imsg_log(struct imsgbuf*, struct imsg*, size_t);
static void handle_imsg_log_type(struct imsgbuf*, struct imsg*, size_t);
static void handle_dispatch_imsg(int, short, void*);
static imsg_handlerfn *handlers[] = {
[IMSG_QUIT] = handle_imsg_quit,
[IMSG_LOG] = handle_imsg_log,
[IMSG_LOG_REQUEST] = handle_imsg_log,
[IMSG_LOG_TYPE] = handle_imsg_log_type,
};
static inline void
print_date(FILE *f)
void
log_init(int n_debug, int facility)
{
struct tm tminfo;
time_t t;
char buf[20];
debug = n_debug;
verbose = n_debug;
log_procinit(getprogname());
time(&t);
strftime(buf, sizeof(buf), "%F %T",
localtime_r(&t, &tminfo));
fprintf(f, "[%s] ", buf);
}
if (!debug)
openlog(getprogname(), LOG_PID | LOG_NDELAY, facility);
static inline int
should_log(int priority)
{
switch (priority) {
case LOG_ERR:
return 1;
case LOG_WARNING:
return 1;
case LOG_NOTICE:
return conf.verbose >= 1;
case LOG_INFO:
return conf.verbose >= 2;
case LOG_DEBUG:
return conf.verbose >= 3;
default:
return 0;
}
}
static inline void
send_log(int type, int priority, const char *msg, size_t len)
{
imsg_compose(&logibuf, type, priority, 0, -1, msg, len);
imsg_flush(&logibuf);
tzset();
}
void
fatal(const char *fmt, ...)
log_procinit(const char *procname)
{
struct pollfd pfd;
va_list ap;
int r;
char *fmted;
if (procname != NULL)
log_procname = procname;
}
void
log_setverbose(int v)
{
verbose = v;
}
int
log_getverbose(void)
{
return (verbose);
}
void
logit(int pri, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if ((r = vasprintf(&fmted, fmt, ap)) != -1) {
send_log(IMSG_LOG, LOG_CRIT, fmted, r+1);
free(fmted);
vlog(pri, fmt, ap);
va_end(ap);
}
/* wait for the logger process to shut down */
pfd.fd = logibuf.fd;
pfd.events = POLLIN;
poll(&pfd, 1, 1000);
void
vlog(int pri, const char *fmt, va_list ap)
{
char *nfmt;
int saved_errno = errno;
if (debug) {
/* best effort in out of mem situations */
if (asprintf(&nfmt, "%s\n", fmt) == -1) {
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
} else {
vfprintf(stderr, nfmt, ap);
free(nfmt);
}
fflush(stderr);
} else
vsyslog(pri, fmt, ap);
errno = saved_errno;
}
void
log_warn(const char *emsg, ...)
{
char *nfmt;
va_list ap;
int saved_errno = errno;
/* best effort to even work in out of memory situations */
if (emsg == NULL)
logit(LOG_ERR, "%s", strerror(saved_errno));
else {
va_start(ap, emsg);
if (asprintf(&nfmt, "%s: %s", emsg,
strerror(saved_errno)) == -1) {
/* we tried it... */
vlog(LOG_ERR, emsg, ap);
logit(LOG_ERR, "%s", strerror(saved_errno));
} else {
vlog(LOG_ERR, nfmt, ap);
free(nfmt);
}
va_end(ap);
}
errno = saved_errno;
}
void
log_warnx(const char *emsg, ...)
{
va_list ap;
va_start(ap, emsg);
vlog(LOG_ERR, emsg, ap);
va_end(ap);
}
void
log_info(const char *emsg, ...)
{
va_list ap;
va_start(ap, emsg);
vlog(LOG_INFO, emsg, ap);
va_end(ap);
}
void
log_debug(const char *emsg, ...)
{
va_list ap;
if (verbose) {
va_start(ap, emsg);
vlog(LOG_DEBUG, emsg, ap);
va_end(ap);
}
}
static void
vfatalc(int code, const char *emsg, va_list ap)
{
static char s[BUFSIZ];
const char *sep;
if (emsg != NULL) {
(void)vsnprintf(s, sizeof(s), emsg, ap);
sep = ": ";
} else {
s[0] = '\0';
sep = "";
}
if (code)
logit(LOG_CRIT, "fatal in %s: %s%s%s",
log_procname, s, sep, strerror(code));
else
logit(LOG_CRIT, "fatal in %s%s%s", log_procname, sep, s);
}
void
fatal(const char *emsg, ...)
{
va_list ap;
va_start(ap, emsg);
vfatalc(errno, emsg, ap);
va_end(ap);
exit(1);
}
static inline void
vlog(int priority, struct client *c,
const char *fmt, va_list ap)
{
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
char *fmted, *s;
size_t len;
int ec;
if (!should_log(priority))
return;
if (c != NULL) {
len = sizeof(c->addr);
ec = getnameinfo((struct sockaddr*)&c->addr, len,
hbuf, sizeof(hbuf),
sbuf, sizeof(sbuf),
NI_NUMERICHOST | NI_NUMERICSERV);
if (ec != 0)
fatal("getnameinfo: %s: %s",
gai_strerror(ec), strerror(errno));
}
if (vasprintf(&fmted, fmt, ap) == -1)
fatal("vasprintf: %s", strerror(errno));
if (c == NULL)
ec = asprintf(&s, "internal: %s", fmted);
else
ec = asprintf(&s, "%s:%s %s", hbuf, sbuf, fmted);
if (ec < 0)
fatal("asprintf: %s", strerror(errno));
send_log(IMSG_LOG, priority, s, ec+1);
free(fmted);
free(s);
}
void
log_err(struct client *c, const char *fmt, ...)
fatalx(const char *emsg, ...)
{
va_list ap;
va_list ap;
va_start(ap, fmt);
vlog(LOG_ERR, c, fmt, ap);
va_start(ap, emsg);
vfatalc(0, emsg, ap);
va_end(ap);
}
void
log_warn(struct client *c, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vlog(LOG_WARNING, c, fmt, ap);
va_end(ap);
}
void
log_notice(struct client *c, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vlog(LOG_NOTICE, c, fmt, ap);
va_end(ap);
}
void
log_info(struct client *c, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vlog(LOG_INFO, c, fmt, ap);
va_end(ap);
}
void
log_debug(struct client *c, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vlog(LOG_DEBUG, c, fmt, ap);
va_end(ap);
}
/* strchr, but with a bound */
static char *
gmid_strnchr(char *s, int c, size_t len)
{
size_t i;
for (i = 0; i < len; ++i)
if (s[i] == c)
return &s[i];
return NULL;
}
void
log_request(struct client *c, char *meta, size_t l)
{
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV], b[GEMINI_URL_LEN];
char *fmted;
const char *t;
size_t len;
int ec;
len = sizeof(c->addr);
ec = getnameinfo((struct sockaddr*)&c->addr, len,
hbuf, sizeof(hbuf),
sbuf, sizeof(sbuf),
NI_NUMERICHOST | NI_NUMERICSERV);
if (ec != 0)
fatal("getnameinfo: %s", gai_strerror(ec));
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 ((t = gmid_strnchr(meta, '\r', l)) == NULL)
t = meta + len;
ec = asprintf(&fmted, "%s:%s GET %s %.*s", hbuf, sbuf, b,
(int)(t-meta), meta);
if (ec < 0)
err(1, "asprintf");
send_log(IMSG_LOG_REQUEST, LOG_NOTICE, fmted, ec+1);
free(fmted);
}
static void
do_log(int type, int priority, const char *msg)
{
int quit = 0;
if (priority == LOG_CRIT) {
quit = 1;
priority = LOG_ERR;
}
if (log != NULL) {
if (type != IMSG_LOG_REQUEST)
print_date(log);
fprintf(log, "%s\n", msg);
} else
syslog(LOG_DAEMON | priority, "%s", msg);
if (quit)
exit(1);
}
static void
handle_imsg_quit(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen)
{
event_loopbreak();
}
static void
handle_imsg_log(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen)
{
int priority;
char *msg;
msg = imsg->data;
msg[datalen-1] = '\0';
priority = imsg->hdr.peerid;
do_log(imsg->hdr.type, priority, msg);
}
static void
handle_imsg_log_type(struct imsgbuf *ibuf, struct imsg *imsg, size_t datalen)
{
if (log != NULL && log != stderr) {
fflush(log);
fclose(log);
}
log = NULL;
if (imsg->fd != -1) {
if ((log = fdopen(imsg->fd, "a")) == NULL) {
syslog(LOG_DAEMON | LOG_ERR, "fdopen: %s",
strerror(errno));
exit(1);
}
}
}
static void
handle_dispatch_imsg(int fd, short ev, void *d)
{
struct imsgbuf *ibuf = d;
dispatch_imsg(ibuf, handlers, sizeof(handlers));
}
int
logger_main(int fd, struct imsgbuf *ibuf)
{
log = stderr;
openlog(getprogname(), LOG_NDELAY, LOG_DAEMON);
tzset();
event_init();
event_set(&imsgev, fd, EV_READ | EV_PERSIST, &handle_dispatch_imsg, ibuf);
event_add(&imsgev, NULL);
sandbox_logger_process();
event_dispatch();
closelog();
return 0;
exit(1);
}

45
log.h Normal file
View File

@ -0,0 +1,45 @@
/* $OpenBSD: log.h,v 1.2 2021/12/13 18:28:40 deraadt Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef LOG_H
#define LOG_H
#include <stdarg.h>
void log_init(int, int);
void log_procinit(const char *);
void log_setverbose(int);
int log_getverbose(void);
void log_warn(const char *, ...)
__attribute__((__format__ (printf, 1, 2)));
void log_warnx(const char *, ...)
__attribute__((__format__ (printf, 1, 2)));
void log_info(const char *, ...)
__attribute__((__format__ (printf, 1, 2)));
void log_debug(const char *, ...)
__attribute__((__format__ (printf, 1, 2)));
void logit(int, const char *, ...)
__attribute__((__format__ (printf, 2, 3)));
void vlog(int, const char *, va_list)
__attribute__((__format__ (printf, 2, 0)));
__dead void fatal(const char *, ...)
__attribute__((__format__ (printf, 1, 2)));
__dead void fatalx(const char *, ...)
__attribute__((__format__ (printf, 1, 2)));
#endif /* LOG_H */

128
logger.c Normal file
View File

@ -0,0 +1,128 @@
/*
* 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/types.h>
#include <sys/uio.h>
#include <errno.h>
#include <event.h>
#include <imsg.h>
#include <netdb.h>
#include <poll.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <time.h>
#include "log.h"
#include "proc.h"
#ifndef nitems
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#endif
static int logfd = -1;
static int log_to_syslog = 1;
static int facility = LOG_DAEMON;
static void logger_init(struct privsep *, struct privsep_proc *, void *);
static void logger_shutdown(void);
static int logger_dispatch_parent(int, struct privsep_proc *, struct imsg *);
static int logger_dispatch_server(int, struct privsep_proc *, struct imsg *);
static struct privsep_proc procs[] = {
{ "parent", PROC_PARENT, logger_dispatch_parent },
{ "server", PROC_SERVER, logger_dispatch_server },
};
void
logger(struct privsep *ps, struct privsep_proc *p)
{
proc_run(ps, p, procs, nitems(procs), logger_init, NULL);
}
static void
logger_init(struct privsep *ps, struct privsep_proc *p, void *arg)
{
p->p_shutdown = logger_shutdown;
openlog(getprogname(), LOG_NDELAY, LOG_DAEMON);
tzset();
sandbox_logger_process();
}
static void
logger_shutdown(void)
{
closelog();
if (logfd != -1)
close(logfd);
}
static int
logger_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
{
switch (imsg_get_type(imsg)) {
case IMSG_LOG_FACILITY:
if (imsg_get_data(imsg, &facility, sizeof(facility)) == -1)
fatal("corrupted IMSG_LOG_SYSLOG");
break;
case IMSG_LOG_SYSLOG:
if (imsg_get_data(imsg, &log_to_syslog,
sizeof(log_to_syslog)) == -1)
fatal("corrupted IMSG_LOG_SYSLOG");
break;
case IMSG_LOG_ACCESS:
if (logfd != -1)
close(logfd);
logfd = imsg_get_fd(imsg);
break;
default:
return -1;
}
return 0;
}
static int
logger_dispatch_server(int fd, struct privsep_proc *p, struct imsg *imsg)
{
char *msg;
size_t datalen = 0;
struct ibuf ibuf;
switch (imsg_get_type(imsg)) {
case IMSG_LOG_REQUEST:
if (imsg_get_ibuf(imsg, &ibuf) == -1 ||
(datalen = ibuf_size(&ibuf)) == 0)
fatal("got invalid IMSG_LOG_REQUEST");
msg = ibuf_data(&ibuf);
msg[datalen - 1] = '\0';
if (logfd != -1)
dprintf(logfd, "%s\n", msg);
if (log_to_syslog)
syslog(facility | LOG_NOTICE, "%s", msg);
break;
default:
return -1;
}
return 0;
}

32
mime.c
View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2022 Omar Polo <op@omarpolo.com>
* 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
@ -20,6 +20,8 @@
#include <stdlib.h>
#include <string.h>
#include "log.h"
void
init_mime(struct mime *mime)
{
@ -28,14 +30,13 @@ init_mime(struct mime *mime)
mime->t = calloc(mime->cap, sizeof(struct etm));
if (mime->t == NULL)
fatal("calloc: %s", strerror(errno));
fatal("calloc");
}
/* register mime for the given extension */
int
add_mime(struct mime *mime, const char *mt, const char *ext)
{
char *mimetype, *extension;
struct etm *t;
size_t newcap;
@ -49,15 +50,11 @@ add_mime(struct mime *mime, const char *mt, const char *ext)
mime->cap = newcap;
}
if ((mimetype = strdup(mt)) == NULL)
t = &mime->t[mime->len];
if (strlcpy(t->mime, mt, sizeof(t->mime)) >= sizeof(t->mime))
return -1;
if ((extension = strdup(ext)) == NULL) {
free(mimetype);
if (strlcpy(t->ext, ext, sizeof(t->ext)) >= sizeof(t->ext))
return -1;
}
mime->t[mime->len].mime = mimetype;
mime->t[mime->len].ext = extension;
mime->len++;
return 0;
}
@ -87,6 +84,10 @@ load_default_mime(struct mime *mime)
{NULL, NULL}
}, *i;
/* don't load the default if `types' was used. */
if (mime->len != 0)
return 0;
for (i = m; i->mime != NULL; ++i) {
if (add_mime(mime, i->mime, i->ext) == -1)
return -1;
@ -135,7 +136,7 @@ mime_find(const void *a, const void *b)
}
const char *
mime(struct vhost *host, const char *path)
mime(struct conf *conf, struct vhost *host, const char *path)
{
const char *def, *ext;
struct etm *t;
@ -145,7 +146,7 @@ mime(struct vhost *host, const char *path)
if ((ext = path_ext(path)) == NULL)
return def;
t = bsearch(ext, conf.mime.t, conf.mime.len, sizeof(*conf.mime.t),
t = bsearch(ext, conf->mime.t, conf->mime.len, sizeof(*conf->mime.t),
mime_find);
if (t != NULL)
return t->mime;
@ -157,12 +158,5 @@ mime(struct vhost *host, const char *path)
void
free_mime(struct mime *m)
{
struct etm *t;
for (t = m->t; t->mime != NULL; ++t) {
free(t->mime);
free(t->ext);
}
free(m->t);
}

719
parse.y

File diff suppressed because it is too large Load Diff

841
proc.c Normal file
View File

@ -0,0 +1,841 @@
/* $OpenBSD: proc.c,v 1.41 2021/12/04 06:52:58 florian Exp $ */
/*
* Copyright (c) 2010 - 2016 Reyk Floeter <reyk@openbsd.org>
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@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 "gmid.h"
#include <sys/types.h>
/* #include <sys/queue.h> XXX provided by gmid.h */
/* #include <sys/tree.h> XXX provided by gmid.h */
#include <sys/socket.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <grp.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <paths.h>
#include <pwd.h>
/* #include <event.h> XXX provided by gmid.h */
/* #include <imsg.h> XXX provided by gmid.h */
#include "log.h"
#include "proc.h"
void proc_exec(struct privsep *, struct privsep_proc *, unsigned int, int,
int, char **);
void proc_setup(struct privsep *, struct privsep_proc *, unsigned int);
void proc_open(struct privsep *, int, int);
void proc_accept(struct privsep *, int, enum privsep_procid,
unsigned int);
void proc_close(struct privsep *);
void proc_shutdown(struct privsep_proc *);
void proc_sig_handler(int, short, void *);
void proc_range(struct privsep *, enum privsep_procid, int *, int *);
int proc_dispatch_null(int, struct privsep_proc *, struct imsg *);
enum privsep_procid
proc_getid(struct privsep_proc *procs, unsigned int nproc,
const char *proc_name)
{
struct privsep_proc *p;
unsigned int proc;
for (proc = 0; proc < nproc; proc++) {
p = &procs[proc];
if (strcmp(p->p_title, proc_name))
continue;
return (p->p_id);
}
return (PROC_MAX);
}
void
proc_exec(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc,
int debug, int argc, char **argv)
{
unsigned int proc, nargc, i, proc_i, proc_X = 0;
const char **nargv;
struct privsep_proc *p;
char num[32];
int fd;
/* Prepare the new process argv. */
nargv = calloc(argc + 9, sizeof(char *));
if (nargv == NULL)
fatal("%s: calloc", __func__);
/* Copy call argument first. */
nargc = 0;
nargv[nargc++] = argv[0];
/* Set process name argument and save the position. */
nargv[nargc++] = "-T";
proc_i = nargc;
nargc++;
/* Set user and chroot */
if (ps->ps_pw != NULL) {
nargv[nargc++] = "-U";
nargv[nargc++] = ps->ps_pw->pw_name;
nargv[nargc++] = "-X";
proc_X = nargc;
nargc++;
}
/* Point process instance arg to stack and copy the original args. */
nargv[nargc++] = "-I";
nargv[nargc++] = num;
for (i = 1; i < (unsigned int) argc; i++)
nargv[nargc++] = argv[i];
nargv[nargc] = NULL;
for (proc = 0; proc < nproc; proc++) {
p = &procs[proc];
/* Update args with process title and chroot. */
nargv[proc_i] = (char *)(uintptr_t)p->p_title;
if (proc_X)
nargv[proc_X] = p->p_chroot;
/* Fire children processes. */
for (i = 0; i < ps->ps_instances[p->p_id]; i++) {
/* Update the process instance number. */
snprintf(num, sizeof(num), "%u", i);
fd = ps->ps_pipes[p->p_id][i].pp_pipes[PROC_PARENT][0];
ps->ps_pipes[p->p_id][i].pp_pipes[PROC_PARENT][0] = -1;
switch (fork()) {
case -1:
fatal("%s: fork", __func__);
break;
case 0:
/* First create a new session */
if (setsid() == -1)
fatal("setsid");
/* Prepare parent socket. */
if (fd != PROC_PARENT_SOCK_FILENO) {
if (dup2(fd, PROC_PARENT_SOCK_FILENO)
== -1)
fatal("dup2");
} else if (fcntl(fd, F_SETFD, 0) == -1)
fatal("fcntl");
/* Daemons detach from terminal. */
if (!debug && (fd =
open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
(void)dup2(fd, STDIN_FILENO);
(void)dup2(fd, STDOUT_FILENO);
(void)dup2(fd, STDERR_FILENO);
if (fd > 2)
(void)close(fd);
}
/* obnoxious casts */
execvp(argv[0], (char *const *)nargv);
fatal("%s: execvp", __func__);
break;
default:
/* Close child end. */
close(fd);
break;
}
}
}
free(nargv);
}
void
proc_connect(struct privsep *ps)
{
struct imsgev *iev;
unsigned int src, dst, inst;
/* Don't distribute any sockets if we are not really going to run. */
if (ps->ps_noaction)
return;
for (dst = 0; dst < PROC_MAX; dst++) {
/* We don't communicate with ourselves. */
if (dst == PROC_PARENT)
continue;
for (inst = 0; inst < ps->ps_instances[dst]; inst++) {
iev = &ps->ps_ievs[dst][inst];
imsg_init(&iev->ibuf, ps->ps_pp->pp_pipes[dst][inst]);
event_set(&iev->ev, iev->ibuf.fd, iev->events,
iev->handler, iev->data);
event_add(&iev->ev, NULL);
}
}
/* Distribute the socketpair()s for everyone. */
for (src = 0; src < PROC_MAX; src++)
for (dst = src; dst < PROC_MAX; dst++) {
/* Parent already distributed its fds. */
if (src == PROC_PARENT || dst == PROC_PARENT)
continue;
proc_open(ps, src, dst);
}
}
void
proc_init(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc,
int debug, int argc, char **argv, enum privsep_procid proc_id)
{
struct privsep_proc *p = NULL;
struct privsep_pipes *pa, *pb;
unsigned int proc;
unsigned int dst;
int fds[2];
/* Don't initiate anything if we are not really going to run. */
if (ps->ps_noaction)
return;
if (proc_id == PROC_PARENT) {
privsep_process = PROC_PARENT;
proc_setup(ps, procs, nproc);
/*
* Create the children sockets so we can use them
* to distribute the rest of the socketpair()s using
* proc_connect() later.
*/
for (dst = 0; dst < PROC_MAX; dst++) {
/* Don't create socket for ourselves. */
if (dst == PROC_PARENT)
continue;
for (proc = 0; proc < ps->ps_instances[dst]; proc++) {
pa = &ps->ps_pipes[PROC_PARENT][0];
pb = &ps->ps_pipes[dst][proc];
if (socketpair(AF_UNIX,
SOCK_STREAM,
PF_UNSPEC, fds) == -1)
fatal("%s: socketpair", __func__);
mark_nonblock(fds[0]);
mark_nonblock(fds[1]);
if (fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 ||
fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1)
fatal("%s: fcntl F_SETFD", __func__);
pa->pp_pipes[dst][proc] = fds[0];
pb->pp_pipes[PROC_PARENT][0] = fds[1];
}
}
/* Engage! */
proc_exec(ps, procs, nproc, debug, argc, argv);
return;
}
/* Initialize a child */
for (proc = 0; proc < nproc; proc++) {
if (procs[proc].p_id != proc_id)
continue;
p = &procs[proc];
break;
}
if (p == NULL || p->p_init == NULL)
fatalx("%s: process %d missing process initialization",
__func__, proc_id);
p->p_init(ps, p);
fatalx("failed to initiate child process");
}
void
proc_accept(struct privsep *ps, int fd, enum privsep_procid dst,
unsigned int n)
{
struct privsep_pipes *pp = ps->ps_pp;
struct imsgev *iev;
if (ps->ps_ievs[dst] == NULL) {
#if DEBUG > 1
log_debug("%s: %s src %d %d to dst %d %d not connected",
__func__, ps->ps_title[privsep_process],
privsep_process, ps->ps_instance + 1,
dst, n + 1);
#endif
close(fd);
return;
}
if (pp->pp_pipes[dst][n] != -1) {
log_warnx("%s: duplicated descriptor", __func__);
close(fd);
return;
} else
pp->pp_pipes[dst][n] = fd;
iev = &ps->ps_ievs[dst][n];
imsg_init(&iev->ibuf, fd);
event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
event_add(&iev->ev, NULL);
}
void
proc_setup(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc)
{
unsigned int i, j, src, dst, id;
struct privsep_pipes *pp;
/* Initialize parent title, ps_instances and procs. */
ps->ps_title[PROC_PARENT] = "parent";
for (src = 0; src < PROC_MAX; src++)
/* Default to 1 process instance */
if (ps->ps_instances[src] < 1)
ps->ps_instances[src] = 1;
for (src = 0; src < nproc; src++) {
procs[src].p_ps = ps;
if (procs[src].p_cb == NULL)
procs[src].p_cb = proc_dispatch_null;
id = procs[src].p_id;
ps->ps_title[id] = procs[src].p_title;
if ((ps->ps_ievs[id] = calloc(ps->ps_instances[id],
sizeof(struct imsgev))) == NULL)
fatal("%s: calloc", __func__);
/* With this set up, we are ready to call imsg_init(). */
for (i = 0; i < ps->ps_instances[id]; i++) {
ps->ps_ievs[id][i].handler = proc_dispatch;
ps->ps_ievs[id][i].events = EV_READ;
ps->ps_ievs[id][i].proc = &procs[src];
ps->ps_ievs[id][i].data = &ps->ps_ievs[id][i];
}
}
/*
* Allocate pipes for all process instances (incl. parent)
*
* - ps->ps_pipes: N:M mapping
* N source processes connected to M destination processes:
* [src][instances][dst][instances], for example
* [PROC_RELAY][3][PROC_CA][3]
*
* - ps->ps_pp: per-process 1:M part of ps->ps_pipes
* Each process instance has a destination array of socketpair fds:
* [dst][instances], for example
* [PROC_PARENT][0]
*/
for (src = 0; src < PROC_MAX; src++) {
/* Allocate destination array for each process */
if ((ps->ps_pipes[src] = calloc(ps->ps_instances[src],
sizeof(struct privsep_pipes))) == NULL)
fatal("%s: calloc", __func__);
for (i = 0; i < ps->ps_instances[src]; i++) {
pp = &ps->ps_pipes[src][i];
for (dst = 0; dst < PROC_MAX; dst++) {
/* Allocate maximum fd integers */
if ((pp->pp_pipes[dst] =
calloc(ps->ps_instances[dst],
sizeof(int))) == NULL)
fatal("%s: calloc", __func__);
/* Mark fd as unused */
for (j = 0; j < ps->ps_instances[dst]; j++)
pp->pp_pipes[dst][j] = -1;
}
}
}
ps->ps_pp = &ps->ps_pipes[privsep_process][ps->ps_instance];
}
void
proc_kill(struct privsep *ps)
{
char *cause;
pid_t pid;
int len, status;
if (privsep_process != PROC_PARENT)
return;
proc_close(ps);
do {
pid = waitpid(WAIT_ANY, &status, 0);
if (pid <= 0)
continue;
if (WIFSIGNALED(status)) {
len = asprintf(&cause, "terminated; signal %d",
WTERMSIG(status));
} else if (WIFEXITED(status)) {
if (WEXITSTATUS(status) != 0)
len = asprintf(&cause, "exited abnormally");
else
len = 0;
} else
len = -1;
if (len == 0) {
/* child exited OK, don't print a warning message */
} else if (len != -1) {
log_warnx("lost child: pid %u %s", pid, cause);
free(cause);
} else
log_warnx("lost child: pid %u", pid);
} while (pid != -1 || errno == EINTR);
}
void
proc_open(struct privsep *ps, int src, int dst)
{
struct privsep_pipes *pa, *pb;
struct privsep_fd pf;
int fds[2];
unsigned int i, j;
/* Exchange pipes between process. */
for (i = 0; i < ps->ps_instances[src]; i++) {
for (j = 0; j < ps->ps_instances[dst]; j++) {
/* Don't create sockets for ourself. */
if (src == dst && i == j)
continue;
/* Servers don't talk to each other. */
if (src == PROC_SERVER && dst == PROC_SERVER)
continue;
pa = &ps->ps_pipes[src][i];
pb = &ps->ps_pipes[dst][j];
if (socketpair(AF_UNIX,
SOCK_STREAM,
PF_UNSPEC, fds) == -1)
fatal("%s: socketpair", __func__);
mark_nonblock(fds[0]);
mark_nonblock(fds[1]);
if (fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 ||
fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1)
fatal("%s: fcntl F_SETFD", __func__);
pa->pp_pipes[dst][j] = fds[0];
pb->pp_pipes[src][i] = fds[1];
pf.pf_procid = src;
pf.pf_instance = i;
if (proc_compose_imsg(ps, dst, j, IMSG_CTL_PROCFD,
-1, pb->pp_pipes[src][i], &pf, sizeof(pf)) == -1)
fatal("%s: proc_compose_imsg", __func__);
pf.pf_procid = dst;
pf.pf_instance = j;
if (proc_compose_imsg(ps, src, i, IMSG_CTL_PROCFD,
-1, pa->pp_pipes[dst][j], &pf, sizeof(pf)) == -1)
fatal("%s: proc_compose_imsg", __func__);
/*
* We have to flush to send the descriptors and close
* them to avoid the fd ramp on startup.
*/
if (proc_flush_imsg(ps, src, i) == -1 ||
proc_flush_imsg(ps, dst, j) == -1)
fatal("%s: imsg_flush", __func__);
}
}
}
void
proc_close(struct privsep *ps)
{
unsigned int dst, n;
struct privsep_pipes *pp;
if (ps == NULL)
return;
pp = ps->ps_pp;
for (dst = 0; dst < PROC_MAX; dst++) {
if (ps->ps_ievs[dst] == NULL)
continue;
for (n = 0; n < ps->ps_instances[dst]; n++) {
if (pp->pp_pipes[dst][n] == -1)
continue;
/* Cancel the fd, close and invalidate the fd */
event_del(&(ps->ps_ievs[dst][n].ev));
imsg_clear(&(ps->ps_ievs[dst][n].ibuf));
close(pp->pp_pipes[dst][n]);
pp->pp_pipes[dst][n] = -1;
}
free(ps->ps_ievs[dst]);
}
}
void
proc_shutdown(struct privsep_proc *p)
{
struct privsep *ps = p->p_ps;
if (p->p_shutdown != NULL)
(*p->p_shutdown)();
proc_close(ps);
log_info("%s exiting, pid %d", p->p_title, getpid());
exit(0);
}
void
proc_sig_handler(int sig, short event, void *arg)
{
struct privsep_proc *p = arg;
switch (sig) {
case SIGINT:
case SIGTERM:
proc_shutdown(p);
break;
case SIGCHLD:
case SIGHUP:
/* ignore */
break;
default:
fatalx("%s: unexpected signal", __func__);
/* NOTREACHED */
}
}
void
proc_run(struct privsep *ps, struct privsep_proc *p,
struct privsep_proc *procs, unsigned int nproc,
void (*run)(struct privsep *, struct privsep_proc *, void *), void *arg)
{
struct passwd *pw;
const char *root;
log_procinit(p->p_title);
setproctitle("%s", p->p_title);
privsep_process = p->p_id;
if (ps->ps_pw == NULL)
goto init;
/* Set the process group of the current process */
setpgid(0, 0);
/* Use non-standard user */
if (p->p_pw != NULL)
pw = p->p_pw;
else
pw = ps->ps_pw;
/* Change root directory */
if (p->p_chroot != NULL)
root = p->p_chroot;
else
root = pw->pw_dir;
if (chroot(root) == -1)
fatal("%s: chroot", __func__);
if (chdir("/") == -1)
fatal("%s: chdir(\"/\")", __func__);
if (setgroups(1, &pw->pw_gid) ||
setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
fatal("%s: cannot drop privileges", __func__);
init:
event_init();
signal(SIGPIPE, SIG_IGN);
signal_set(&ps->ps_evsigint, SIGINT, proc_sig_handler, p);
signal_set(&ps->ps_evsigterm, SIGTERM, proc_sig_handler, p);
signal_set(&ps->ps_evsigchld, SIGCHLD, proc_sig_handler, p);
signal_set(&ps->ps_evsighup, SIGHUP, proc_sig_handler, p);
signal_add(&ps->ps_evsigint, NULL);
signal_add(&ps->ps_evsigterm, NULL);
signal_add(&ps->ps_evsigchld, NULL);
signal_add(&ps->ps_evsighup, NULL);
proc_setup(ps, procs, nproc);
proc_accept(ps, PROC_PARENT_SOCK_FILENO, PROC_PARENT, 0);
log_debug("%s: %s %d/%d, pid %d", __func__, p->p_title,
ps->ps_instance + 1, ps->ps_instances[p->p_id], getpid());
if (run != NULL)
run(ps, p, arg);
event_dispatch();
proc_shutdown(p);
}
void
proc_dispatch(int fd, short event, void *arg)
{
struct imsgev *iev = arg;
struct privsep_proc *p = iev->proc;
struct privsep *ps = p->p_ps;
struct imsgbuf *ibuf;
struct imsg imsg;
ssize_t n;
const char *title;
struct privsep_fd pf;
title = ps->ps_title[privsep_process];
ibuf = &iev->ibuf;
if (event & EV_READ) {
if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
fatal("%s: imsg_read", __func__);
if (n == 0) {
/* this pipe is dead, so remove the event handler */
event_del(&iev->ev);
event_loopexit(NULL);
return;
}
}
if (event & EV_WRITE) {
if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
fatal("%s: msgbuf_write", __func__);
if (n == 0) {
/* this pipe is dead, so remove the event handler */
event_del(&iev->ev);
event_loopexit(NULL);
return;
}
}
for (;;) {
if ((n = imsg_get(ibuf, &imsg)) == -1)
fatal("%s: imsg_get", __func__);
if (n == 0)
break;
#if DEBUG > 1
log_debug("%s: %s %d got imsg %d peerid %d from %s %d",
__func__, title, ps->ps_instance + 1,
imsg.hdr.type, imsg.hdr.peerid, p->p_title, imsg.hdr.pid);
#endif
/*
* Check the message with the program callback
*/
if ((p->p_cb)(fd, p, &imsg) == 0) {
/* Message was handled by the callback, continue */
imsg_free(&imsg);
continue;
}
/*
* Generic message handling
*/
switch (imsg.hdr.type) {
case IMSG_CTL_PROCFD:
if (imsg_get_data(&imsg, &pf, sizeof(pf)))
fatalx("bad length imsg CTL_PROCFD");
proc_accept(ps, imsg_get_fd(&imsg), pf.pf_procid,
pf.pf_instance);
break;
default:
fatalx("%s: %s %d got invalid imsg %d peerid %d "
"from %s %d",
__func__, title, ps->ps_instance + 1,
imsg.hdr.type, imsg.hdr.peerid,
p->p_title, imsg.hdr.pid);
}
imsg_free(&imsg);
}
imsg_event_add(iev);
}
int
proc_dispatch_null(int fd, struct privsep_proc *p, struct imsg *imsg)
{
return (-1);
}
/*
* imsg helper functions
*/
void
imsg_event_add(struct imsgev *iev)
{
if (iev->handler == NULL) {
imsg_flush(&iev->ibuf);
return;
}
iev->events = EV_READ;
if (iev->ibuf.w.queued)
iev->events |= EV_WRITE;
event_del(&iev->ev);
event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
event_add(&iev->ev, NULL);
}
int
imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
pid_t pid, int fd, void *data, uint16_t datalen)
{
int ret;
if ((ret = imsg_compose(&iev->ibuf, type, peerid,
pid, fd, data, datalen)) == -1)
return (ret);
imsg_event_add(iev);
return (ret);
}
int
imsg_composev_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
pid_t pid, int fd, const struct iovec *iov, int iovcnt)
{
int ret;
if ((ret = imsg_composev(&iev->ibuf, type, peerid,
pid, fd, iov, iovcnt)) == -1)
return (ret);
imsg_event_add(iev);
return (ret);
}
void
proc_range(struct privsep *ps, enum privsep_procid id, int *n, int *m)
{
if (*n == -1) {
/* Use a range of all target instances */
*n = 0;
*m = ps->ps_instances[id];
} else {
/* Use only a single slot of the specified peer process */
*m = *n + 1;
}
}
int
proc_compose_imsg(struct privsep *ps, enum privsep_procid id, int n,
uint16_t type, uint32_t peerid, int fd, void *data, uint16_t datalen)
{
int m;
proc_range(ps, id, &n, &m);
for (; n < m; n++) {
if (imsg_compose_event(&ps->ps_ievs[id][n],
type, peerid, ps->ps_instance + 1, fd, data, datalen) == -1)
return (-1);
}
return (0);
}
int
proc_compose(struct privsep *ps, enum privsep_procid id,
uint16_t type, void *data, uint16_t datalen)
{
return (proc_compose_imsg(ps, id, -1, type, -1, -1, data, datalen));
}
int
proc_composev_imsg(struct privsep *ps, enum privsep_procid id, int n,
uint16_t type, uint32_t peerid, int fd, const struct iovec *iov, int iovcnt)
{
int m;
proc_range(ps, id, &n, &m);
for (; n < m; n++)
if (imsg_composev_event(&ps->ps_ievs[id][n],
type, peerid, ps->ps_instance + 1, fd, iov, iovcnt) == -1)
return (-1);
return (0);
}
int
proc_composev(struct privsep *ps, enum privsep_procid id,
uint16_t type, const struct iovec *iov, int iovcnt)
{
return (proc_composev_imsg(ps, id, -1, type, -1, -1, iov, iovcnt));
}
struct imsgbuf *
proc_ibuf(struct privsep *ps, enum privsep_procid id, int n)
{
int m;
proc_range(ps, id, &n, &m);
return (&ps->ps_ievs[id][n].ibuf);
}
struct imsgev *
proc_iev(struct privsep *ps, enum privsep_procid id, int n)
{
int m;
proc_range(ps, id, &n, &m);
return (&ps->ps_ievs[id][n]);
}
/* This function should only be called with care as it breaks async I/O */
int
proc_flush_imsg(struct privsep *ps, enum privsep_procid id, int n)
{
struct imsgbuf *ibuf;
int m, ret = 0;
proc_range(ps, id, &n, &m);
for (; n < m; n++) {
if ((ibuf = proc_ibuf(ps, id, n)) == NULL)
return (-1);
do {
ret = imsg_flush(ibuf);
} while (ret == -1 && errno == EAGAIN);
if (ret == -1)
break;
imsg_event_add(&ps->ps_ievs[id][n]);
}
return (ret);
}

123
proc.h Normal file
View File

@ -0,0 +1,123 @@
/*
* Copyright (c) 2010-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.
*/
/* imsg */
struct imsgev {
struct imsgbuf ibuf;
void (*handler)(int, short, void *);
struct event ev;
struct privsep_proc *proc;
void *data;
short events;
};
#define IMSG_SIZE_CHECK(imsg, p) do { \
if (IMSG_DATA_SIZE(imsg) < sizeof(*p)) \
fatalx("bad length imsg received (%s)", #p); \
} while (0)
#define IMSG_DATA_SIZE(imsg) ((imsg)->hdr.len - IMSG_HEADER_SIZE)
#define PROC_PARENT_SOCK_FILENO 3
/* privsep */
enum privsep_procid {
PROC_PARENT,
PROC_SERVER,
PROC_CRYPTO,
PROC_LOGGER,
PROC_MAX,
};
#define CONFIG_RELOAD 0x00
#define CONFIG_SOCKS 0x01
#define CONFIG_ALL 0xff
struct privsep_pipes {
int *pp_pipes[PROC_MAX];
};
struct privsep {
struct privsep_pipes *ps_pipes[PROC_MAX];
struct privsep_pipes *ps_pp;
struct imsgev *ps_ievs[PROC_MAX];
const char *ps_title[PROC_MAX];
uint8_t ps_what[PROC_MAX];
struct passwd *ps_pw;
int ps_noaction;
unsigned int ps_instances[PROC_MAX];
unsigned int ps_instance;
/* Event and signal handlers */
struct event ps_evsigint;
struct event ps_evsigterm;
struct event ps_evsigchld;
struct event ps_evsighup;
struct event ps_evsigusr1;
void *ps_env;
};
struct privsep_proc {
const char *p_title;
enum privsep_procid p_id;
int (*p_cb)(int, struct privsep_proc *,
struct imsg *);
void (*p_init)(struct privsep *,
struct privsep_proc *);
void (*p_shutdown)(void);
const char *p_chroot;
struct passwd *p_pw;
struct privsep *p_ps;
};
struct privsep_fd {
enum privsep_procid pf_procid;
unsigned int pf_instance;
};
/* proc.c */
void proc_init(struct privsep *, struct privsep_proc *, unsigned int,
int, int, char **, enum privsep_procid);
void proc_kill(struct privsep *);
void proc_connect(struct privsep *ps);
void proc_dispatch(int, short event, void *);
void proc_range(struct privsep *, enum privsep_procid, int *, int *);
void proc_run(struct privsep *, struct privsep_proc *,
struct privsep_proc *, unsigned int,
void (*)(struct privsep *, struct privsep_proc *, void *), void *);
void imsg_event_add(struct imsgev *);
int imsg_compose_event(struct imsgev *, uint16_t, uint32_t,
pid_t, int, void *, uint16_t);
int imsg_composev_event(struct imsgev *, uint16_t, uint32_t,
pid_t, int, const struct iovec *, int);
int proc_compose_imsg(struct privsep *, enum privsep_procid, int,
uint16_t, uint32_t, int, void *, uint16_t);
int proc_compose(struct privsep *, enum privsep_procid,
uint16_t, void *data, uint16_t);
int proc_composev_imsg(struct privsep *, enum privsep_procid, int,
uint16_t, uint32_t, int, const struct iovec *, int);
int proc_composev(struct privsep *, enum privsep_procid,
uint16_t, const struct iovec *, int);
struct imsgbuf *
proc_ibuf(struct privsep *, enum privsep_procid, int);
struct imsgev *
proc_iev(struct privsep *, enum privsep_procid, int);
enum privsep_procid
proc_getid(struct privsep_proc *, unsigned int, const char *);
int proc_flush_imsg(struct privsep *, enum privsep_procid, int);

29
proxy.c
View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2021, 2022 Omar Polo <op@omarpolo.com>
* 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
@ -20,6 +20,8 @@
#include <errno.h>
#include <string.h>
#include "log.h"
#define MIN(a, b) ((a) < (b) ? (a) : (b))
static const struct timeval handshake_timeout = { 5, 0 };
@ -150,7 +152,7 @@ proxy_read(struct bufferevent *bev, void *d)
if (hdr == NULL) {
/* max reply + \r\n */
if (EVBUFFER_LENGTH(src) > 1029) {
log_warn(c, "upstream server is trying to "
log_warnx("upstream server is trying to "
"send a header that's too long.");
proxy_error(bev, EVBUFFER_READ, c);
}
@ -160,11 +162,11 @@ proxy_read(struct bufferevent *bev, void *d)
}
if (len < 3 || len > 1029 ||
!isdigit(hdr[0]) ||
!isdigit(hdr[1]) ||
!isspace(hdr[2])) {
!isdigit((unsigned char)hdr[0]) ||
!isdigit((unsigned char)hdr[1]) ||
!isspace((unsigned char)hdr[2])) {
free(hdr);
log_warn(c, "upstream server is trying to send a "
log_warnx("upstream server is trying to send a "
"header that's too long.");
proxy_error(bev, EVBUFFER_READ, c);
return;
@ -174,15 +176,14 @@ proxy_read(struct bufferevent *bev, void *d)
code = (hdr[0] - '0') * 10 + (hdr[1] - '0');
if (code < 10 || code >= 70) {
log_warn(c, "upstream server is trying to send an "
log_warnx("upstream server is trying to send an "
"invalid reply code: %d", code);
proxy_error(bev, EVBUFFER_READ, c);
return;
}
start_reply(c, code, hdr + 3);
if (c->code < 20 || c->code > 29) {
if (start_reply(c, code, hdr + 3) == -1 ||
c->code < 20 || c->code > 29) {
proxy_error(bev, EVBUFFER_EOF, c);
return;
}
@ -208,7 +209,7 @@ proxy_error(struct bufferevent *bev, short error, void *d)
/*
* If we're here it means that some kind of non-recoverable
* error appened.
* error happened.
*/
bufferevent_free(bev);
@ -240,7 +241,7 @@ proxy_enqueue_req(struct client *c)
c->proxybev = bufferevent_new(c->pfd, proxy_read, proxy_write,
proxy_error, c);
if (c->proxybev == NULL)
fatal("can't allocate bufferevent: %s", strerror(errno));
fatal("can't allocate bufferevent");
if (!p->notls) {
event_set(&c->proxybev->ev_read, c->pfd, EV_READ,
@ -282,7 +283,7 @@ proxy_handshake(int fd, short event, void *d)
event_add(&c->proxyev, &handshake_timeout);
return;
case -1:
log_warn(c, "handshake with proxy failed: %s",
log_warnx("handshake with proxy failed: %s",
tls_error(c->proxyctx));
start_reply(c, PROXY_ERROR, "handshake failed");
return;
@ -326,7 +327,7 @@ proxy_setup_tls(struct client *c)
if (tls_configure(c->proxyctx, conf) == -1)
goto err;
if ((hn = p->sni) == NULL)
if (*(hn = p->sni) == '\0')
hn = p->host;
if (tls_connect_socket(c->proxyctx, c->pfd, hn) == -1)
goto err;

2
puny.c
View File

@ -151,7 +151,7 @@ decode(const char *str, char *out, size_t len, const char **err)
unsigned int numpoints;
const char *s;
if (!starts_with(str, "xn--")) {
if (strncmp(str, "xn--", 4) != 0) {
strncpy(out, str, len);
return 1;
}

View File

@ -2,43 +2,63 @@
# all.
TESTS=
include ../Makefile.local
GENCERT_FLAGS=
COMPAT= ${COBJS:%=../%}
# host to bind to during regress
REGRESS_HOST = localhost
.PHONY: all data clean regress
DISTFILES = Makefile \
env \
err \
example.mime.types \
fcgi-test.c \
fill-file.c \
hello \
invalid \
iri_test.c \
lib.sh \
max-length-reply \
puny-test.c \
regress \
serve-bigfile \
slow \
tests.sh \
valid.ext
include ../config.mk
COBJS = ${COMPATS:.c=.o}
REG_COMPATS = ${COBJS:%=../%}
PUNY_SRCS = puny-test.c ../puny.c ../utf8.c ../utils.c ../log.c
PUNY_OBJS = ${PUNY_SRCS:.c=.o} ${REG_COMPATS}
IRI_SRCS = iri_test.c ../iri.c ../utf8.c ../log.c
IRI_OBJS = ${IRI_SRCS:.c=.o} ${REG_COMPATS}
.PHONY: all data clean dist
all: data puny-test iri_test fcgi-test
./regress ${TESTS}
env REGRESS_HOST="${REGRESS_HOST}" ./regress ${TESTS}
data: testdata cert.pem testca.pem valid.crt invalid.cert.pem
data: testdata localhost.pem testca.pem valid.crt invalid.pem
puny-test: puny-test.o ../puny.o ../utf8.o ../utils.o ../log.o ${COMPAT}
${CC} puny-test.o ../puny.o ../utf8.o ../utils.o ../log.o ${COMPAT} \
-o puny-test ${LDFLAGS}
puny-test: ${PUNY_OBJS}
${CC} ${PUNY_OBJS} -o puny-test ${LIBS} ${LDFLAGS}
iri_test: iri_test.o ../iri.o ../utf8.o ${COMPAT}
${CC} iri_test.o ../iri.o ../utf8.o ${COMPAT} -o $@ ${LDFLAGS}
iri_test: ${IRI_OBJS}
${CC} ${IRI_OBJS} -o $@ ${LIBS} ${LDFLAGS}
fill-file: fill-file.o
${CC} fill-file.o -o $@ ${LDFLAGS}
${CC} fill-file.o -o $@ ${LIBS} ${LDFLAGS}
fcgi-test: fcgi-test.o
${CC} fcgi-test.o ${COMPAT} -o fcgi-test ${LDFLAGS}
${CC} fcgi-test.o ../log.o ${REG_COMPATS} -o fcgi-test ${LIBS} ${LDFLAGS}
key.pem: cert.pem
localhost.key: localhost.pem
# XXX: key size is NOT GOOD. This is only for testing. Smaller keys
# are quicker to generate. DON'T DO THIS AT HOME.
cert.pem:
openssl req -x509 -newkey rsa:2048 \
-keyout key.pem \
-out cert.pem \
-days 365 -nodes \
-subj "/CN=localhost"
ln -s cert.pem localhost.cert.pem
ln -s key.pem localhost.key.pem
@echo
localhost.pem:
./../contrib/gencert ${GENCERT_FLAGS} localhost >/dev/null
testca.pem:
openssl genrsa -out testca.key 2048
@ -63,23 +83,21 @@ valid.crt: testca.pem
-days 365 \
-sha256 -extfile valid.ext
invalid.cert.pem: cert.pem
cp cert.pem invalid.cert.pem
cp key.pem invalid.key.pem
invalid.pem: localhost.pem
cp localhost.pem invalid.pem
cp localhost.key invalid.key
clean:
rm -f *.o iri_test cert.pem key.pem
rm -f localhost.cert.pem localhost.key.pem
rm -f testca.* valid.csr valid.key valid.crt invalid.*pem
rm -f *.o iri_test localhost.pem localhost.key
rm -f localhost.pem localhost.key
rm -f testca.* valid.csr valid.key valid.crt invalid.pem invalid.key
rm -rf testdata fill-file puny-test fcgi-test
rm -f gmid.pid
rm -f gmid.pid fcgi.sock
testdata: fill-file
mkdir testdata
./fill-file testdata/bigfile
./sha testdata/bigfile testdata/bigfile.sha
printf "# hello world\n" > testdata/index.gmi
./sha testdata/index.gmi testdata/index.gmi.sha
cp hello slow err invalid serve-bigfile env testdata/
cp max-length-reply testdata
mkdir testdata/dir
@ -87,3 +105,9 @@ testdata: fill-file
cp hello testdata/dir
cp testdata/index.gmi testdata/dir/foo.gmi
touch testdata/test.m3u8 testdata/foo.1
dist: ${DISTFILES}
mkdir -p ${DESTDIR}/
${INSTALL} -m 0644 ${DISTFILES} ${DESTDIR}/
cd ${DESTDIR}/ && chmod +x env err hello invalid \
max-length-reply regress slow

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