mirror of https://github.com/omar-polo/gmid.git
Compare commits
517 Commits
Author | SHA1 | Date |
---|---|---|
Omar Polo | 359c56ce35 | |
Omar Polo | c2dcb5fa6e | |
Omar Polo | 5d12e6a104 | |
Omar Polo | 0d8eb9b60c | |
Omar Polo | 5864f3ce3c | |
Omar Polo | 9536c8ca63 | |
Omar Polo | 40b71b6861 | |
Omar Polo | 42235e3fc2 | |
Omar Polo | f53f5e5fe1 | |
Omar Polo | 40ea7b163e | |
Omar Polo | be265175c6 | |
Omar Polo | 8aba5d8b21 | |
Omar Polo | 7c83689428 | |
Omar Polo | 248fb833f9 | |
Omar Polo | 0ed763b03d | |
Omar Polo | 9f1cce3d0e | |
Omar Polo | 8f543d941e | |
Omar Polo | acf244c516 | |
Anna “CyberTailor” | 53ad458e22 | |
Omar Polo | bb5a25d287 | |
Omar Polo | f862d389ff | |
Omar Polo | 574f71f7a3 | |
Omar Polo | ebe2e54900 | |
Omar Polo | ddb089c157 | |
Omar Polo | 3524375abe | |
Omar Polo | fe37d79200 | |
Omar Polo | 33a5425235 | |
Omar Polo | cd5e264f9d | |
Omar Polo | 0baf7066ac | |
Omar Polo | 83a2644bfb | |
Omar Polo | 4f3b85e6d7 | |
Omar Polo | 2a822b03ba | |
Omar Polo | 3f16db6263 | |
Omar Polo | 561b9f0067 | |
Omar Polo | aa2cb5c274 | |
Omar Polo | 63e6b0bd0c | |
Omar Polo | 6dec2ad700 | |
Omar Polo | b03e976aa2 | |
Anna “CyberTailor” | 6bce8180d9 | |
Omar Polo | 20fa7cded6 | |
Omar Polo | 2865452c40 | |
Omar Polo | 1ee636a45c | |
Omar Polo | eabbdf5a10 | |
Omar Polo | 38a0d7ee8f | |
Omar Polo | ad3bf17681 | |
Omar Polo | 398253f3f5 | |
Omar Polo | d8df67565c | |
Omar Polo | 0d5282b647 | |
Omar Polo | e78e2923ea | |
Omar Polo | a08e0451ed | |
Omar Polo | 89b564c312 | |
Omar Polo | 13f900092d | |
Omar Polo | 84285be948 | |
Omar Polo | 3b9388d8d0 | |
Omar Polo | fa3b459472 | |
Omar Polo | a9092d0ee8 | |
Omar Polo | 8d0573e84a | |
Omar Polo | dd40d59659 | |
Omar Polo | 707ec003e5 | |
Omar Polo | c86654c907 | |
Omar Polo | e3dd9e66af | |
Omar Polo | 14d22e8007 | |
Omar Polo | 432c31e6cc | |
Omar Polo | e371817b34 | |
Omar Polo | ef5057cdec | |
Omar Polo | e6adaaba61 | |
Anna “CyberTailor” | b7e7b9178d | |
Anna “CyberTailor” | 4b77fc3240 | |
Omar Polo | dd3f04b227 | |
Omar Polo | fd48b7c0c4 | |
Omar Polo | ac46710a4b | |
Omar Polo | 104a2059e6 | |
Omar Polo | 822125ca3f | |
Omar Polo | 08e6d6ed7a | |
Omar Polo | 0c5dd8ffe9 | |
Omar Polo | ebfc578491 | |
Omar Polo | 12eb29d878 | |
Omar Polo | b8537c1c54 | |
Omar Polo | 59953b20b2 | |
Omar Polo | acc862155d | |
Omar Polo | dad248f4cf | |
Omar Polo | 6660c0bd41 | |
Omar Polo | ac6c76f8a8 | |
Omar Polo | f522971968 | |
Omar Polo | d1f84895f7 | |
Omar Polo | 5bee9bd73e | |
Omar Polo | 797c527a00 | |
Omar Polo | 36b2905a68 | |
Omar Polo | 91b90ad440 | |
Omar Polo | 4d51ac4f4a | |
Omar Polo | dba9907a71 | |
Omar Polo | 4c12885779 | |
Omar Polo | 4645a68a10 | |
Omar Polo | 33f2a7f735 | |
Omar Polo | 17b7d61fc9 | |
Omar Polo | 07d86a0beb | |
Omar Polo | bab32701fb | |
Omar Polo | adaae5163c | |
Omar Polo | 43b38b2dbb | |
Omar Polo | e8b2d8e3f0 | |
Omar Polo | 7980a5d2a8 | |
Omar Polo | c7bcd4a8a9 | |
Omar Polo | 4db22ea654 | |
Omar Polo | 6e4ba8184d | |
Omar Polo | 60ff0ebcb5 | |
Omar Polo | 23e92df733 | |
Omar Polo | 1218bca611 | |
Omar Polo | 96515efd27 | |
Omar Polo | 80745f0411 | |
Omar Polo | eaca1ed4db | |
Omar Polo | a6d07f09e0 | |
Omar Polo | f31289a8ac | |
Omar Polo | b894573ad9 | |
Omar Polo | c440a0ded9 | |
Omar Polo | 6be41efe33 | |
Omar Polo | a6c8b8051e | |
Omar Polo | 3cba037a11 | |
Omar Polo | 6c86d810fc | |
Omar Polo | 7bbf17a857 | |
Omar Polo | c2c051f28e | |
Omar Polo | 3cb7e8d7ac | |
Omar Polo | 39204b7e48 | |
Omar Polo | ee42fb87e2 | |
Omar Polo | 4f97572edc | |
Omar Polo | ae32f1aa8e | |
Omar Polo | 618b1d4dce | |
Omar Polo | 4d9d3093d4 | |
Omar Polo | cedef5b09c | |
Omar Polo | 01d3961d6f | |
Omar Polo | 74c6900c91 | |
Omar Polo | 21b4a5163c | |
Omar Polo | 258131b1b6 | |
Omar Polo | 8f8d721301 | |
Omar Polo | f9ab77a898 | |
Omar Polo | 9019e55e7e | |
Omar Polo | 7ea8725593 | |
Omar Polo | 9cd81f93d7 | |
Omar Polo | e872053b20 | |
Omar Polo | f5dc7eddd7 | |
Omar Polo | 81634643db | |
Omar Polo | 07ad491025 | |
Omar Polo | 95500a936a | |
Omar Polo | 8bb1b23633 | |
Omar Polo | cf2784df75 | |
Omar Polo | 390d312b22 | |
Omar Polo | 01481c255a | |
Omar Polo | a1e159c917 | |
Omar Polo | 03d671e2aa | |
Omar Polo | 4f7492c36e | |
Omar Polo | b27dc2b0a3 | |
Omar Polo | 08c0f676fd | |
Omar Polo | d98ef73494 | |
Omar Polo | 877b479bcc | |
Omar Polo | 9e6fc13b35 | |
Omar Polo | 36363b011c | |
Omar Polo | d13b044d59 | |
Omar Polo | ddbcd3c13f | |
Omar Polo | 35579431eb | |
Omar Polo | d72ac636bb | |
Omar Polo | 36a98d50e5 | |
Omar Polo | 3b431c09d9 | |
Omar Polo | 9abba172b6 | |
Omar Polo | 3a93c90445 | |
Omar Polo | a250aff257 | |
Omar Polo | f23b708726 | |
Omar Polo | 5d38e5d88d | |
Omar Polo | 11ff7f934e | |
Omar Polo | d671434bc7 | |
Omar Polo | 5905156665 | |
Omar Polo | 1b1a6fb7ee | |
Omar Polo | fcc5a371b1 | |
Omar Polo | 26df50981f | |
Omar Polo | 56054fe197 | |
Omar Polo | f736c9579c | |
Omar Polo | 161651fa5e | |
Omar Polo | c2bcf6a402 | |
Omar Polo | 0eeee6f321 | |
Omar Polo | 603e4dd82f | |
Omar Polo | 564c2ad166 | |
Omar Polo | 547437cc40 | |
Omar Polo | 2c079c9e69 | |
Omar Polo | b5963536c8 | |
Omar Polo | 98827de5ab | |
Omar Polo | 0c39da5145 | |
Omar Polo | 46bcc4ea95 | |
Omar Polo | cba01a8687 | |
Omar Polo | a84492b75f | |
Omar Polo | 471a5250e3 | |
Omar Polo | fdb4572d2f | |
Omar Polo | 82947e8be7 | |
Omar Polo | f8bfba4723 | |
Omar Polo | f59543490d | |
Omar Polo | 857635a107 | |
Omar Polo | aea87ce91f | |
Omar Polo | abd261d25b | |
Omar Polo | 2a28b04424 | |
Omar Polo | e137cb0348 | |
Omar Polo | e075021085 | |
Omar Polo | e5f060f0d2 | |
Omar Polo | 3927336855 | |
Omar Polo | 56d32bb51a | |
Omar Polo | 32b1638ebc | |
Omar Polo | 9888507cd4 | |
Omar Polo | bf9ca5e71c | |
Omar Polo | 4acf495f41 | |
Omar Polo | 3bda540e34 | |
Omar Polo | 60b4efa1e2 | |
Omar Polo | 226f13ece0 | |
Omar Polo | 3826d7de43 | |
Omar Polo | bf7a7fd7b2 | |
Omar Polo | 692a9f5fea | |
Omar Polo | 45c946b37b | |
Omar Polo | 3d56cb5336 | |
Omar Polo | 60f4107da6 | |
Omar Polo | 6a8387e5f5 | |
Omar Polo | fdd67729b4 | |
Omar Polo | a1ba9650a9 | |
Omar Polo | f36ba9be59 | |
Omar Polo | 0995ecdb87 | |
Omar Polo | cf35bdce9a | |
Omar Polo | 88c03474f8 | |
Omar Polo | 67ae3d8e03 | |
Omar Polo | da3fc66ee8 | |
Omar Polo | 800aa93c05 | |
Omar Polo | 2ff1e2a923 | |
Omar Polo | 81bab00246 | |
Omar Polo | 6a996ec20f | |
Omar Polo | bd23307690 | |
Omar Polo | 21617eda73 | |
Omar Polo | 5d2f4b1611 | |
Omar Polo | c9c44c6571 | |
Omar Polo | 71b02f6390 | |
Omar Polo | 2b0b2661ea | |
Omar Polo | 1134ea149a | |
Omar Polo | 2c3810687f | |
Omar Polo | 2247b66842 | |
Omar Polo | 0f7fdd2105 | |
Omar Polo | a6f2cfe792 | |
Omar Polo | e3ce19dcc1 | |
Omar Polo | 80192f4589 | |
Omar Polo | 57ee9057af | |
Omar Polo | 994fc034e5 | |
Omar Polo | e2003e7e30 | |
Omar Polo | 2339a71178 | |
Omar Polo | fef06f06ac | |
Omar Polo | 911156fb95 | |
Omar Polo | c9e878d6a4 | |
Omar Polo | ed164e7221 | |
Omar Polo | da0821b6cb | |
Omar Polo | a452496a96 | |
Omar Polo | 287ab86538 | |
Omar Polo | 0fc92ad2fd | |
Omar Polo | 841633cfec | |
Omar Polo | ddf7a437de | |
Omar Polo | c5edb15740 | |
Omar Polo | 53bdae38fe | |
Omar Polo | 23f0ac49ed | |
Omar Polo | 6a60134c64 | |
Omar Polo | fea6a85623 | |
Omar Polo | 24f644dbb6 | |
Omar Polo | 3a877237a7 | |
Omar Polo | 7edcf2b341 | |
Omar Polo | b3010dbbaf | |
Omar Polo | c3d502d455 | |
Omar Polo | eac9287d29 | |
Omar Polo | df6282815f | |
Omar Polo | aa30aaedc8 | |
Omar Polo | 35dd3fc8ce | |
Omar Polo | e50f85adcb | |
Omar Polo | a0a42860d2 | |
Omar Polo | 9fda962861 | |
Omar Polo | 2c0716fd7d | |
Omar Polo | 2d0a2b21f5 | |
Omar Polo | 5134078414 | |
Omar Polo | 3d0204e7ef | |
Omar Polo | 5a34572282 | |
Omar Polo | a7a998ac9b | |
Omar Polo | 509d0509a5 | |
Omar Polo | 37df23d183 | |
Omar Polo | ab55c7815e | |
Omar Polo | f29d705e04 | |
Omar Polo | abc599e031 | |
Omar Polo | 55b74a9691 | |
Omar Polo | fc2d207c79 | |
Omar Polo | c5ded53a8e | |
Omar Polo | b692d8bd5b | |
Omar Polo | 5dad390015 | |
Omar Polo | 1959cda3d8 | |
Omar Polo | b90faa1605 | |
Omar Polo | cf5cf697a8 | |
Omar Polo | 7604fc903a | |
Omar Polo | 1610f9541d | |
Omar Polo | 10cc819309 | |
Omar Polo | d6d9f9b2a9 | |
Omar Polo | 8af9da9843 | |
Omar Polo | 611dffe816 | |
Omar Polo | 1b9031f1fc | |
Omar Polo | 94893746ae | |
Omar Polo | 2cef5cf42a | |
Omar Polo | 89cfcb4569 | |
Omar Polo | 5d160453f2 | |
Omar Polo | ba290ef3af | |
Omar Polo | 237095fd9a | |
Omar Polo | 1ef09e6313 | |
Omar Polo | b8d68fc8e4 | |
Omar Polo | d1739e3f03 | |
Omar Polo | ec96a0ad3b | |
Omar Polo | 86693a33ab | |
Omar Polo | f81a97b356 | |
Omar Polo | 725457a9e4 | |
Omar Polo | 4ad573d0d5 | |
Omar Polo | 1a99859b35 | |
Omar Polo | 15e60fdf0c | |
Omar Polo | 2e880a57f8 | |
Omar Polo | 892f3a5cf8 | |
Omar Polo | 7fff8aa6cb | |
Omar Polo | 5af19830c3 | |
Omar Polo | 792f302ace | |
Omar Polo | 68368f4c29 | |
Omar Polo | af1dab1870 | |
Omar Polo | e45334e6ae | |
Omar Polo | d273c0648d | |
Omar Polo | fe7cdaa479 | |
Omar Polo | 9adeb26579 | |
Omar Polo | 5d22294a59 | |
Omar Polo | 1962764c62 | |
Omar Polo | cd1ede6dd3 | |
Omar Polo | deadd9e131 | |
Omar Polo | c144b1b6f8 | |
Omar Polo | 309dab3a90 | |
Omar Polo | fc440833ad | |
Omar Polo | 9b89eaeb55 | |
Omar Polo | 9b2587bb33 | |
Omar Polo | 1c6967b33a | |
Omar Polo | 49bd46a150 | |
Omar Polo | 8eeb992206 | |
Omar Polo | f5c8360ade | |
Omar Polo | ca84625a7f | |
Omar Polo | 85a575a444 | |
Omar Polo | cbb7f9fc28 | |
Omar Polo | 797c4609a9 | |
Omar Polo | 3886afceec | |
Omar Polo | 47b0ff105a | |
Omar Polo | 4f4937f06a | |
Omar Polo | fc9cc497e0 | |
Omar Polo | e69e1151f6 | |
Omar Polo | 2b4ef796d7 | |
Omar Polo | 61febd28af | |
Omar Polo | 846842e138 | |
Omar Polo | 68e38f49b2 | |
Omar Polo | 3483609593 | |
Omar Polo | c727f8dd75 | |
Omar Polo | c26f2460e4 | |
Omar Polo | 99f1fbb0c7 | |
Omar Polo | f1f13cb7dc | |
Omar Polo | 9b1750057c | |
Omar Polo | 2dd5994ae1 | |
Omar Polo | bc525c73db | |
Omar Polo | eae52ad493 | |
Omar Polo | 58fae4ea90 | |
Omar Polo | 281a8852b3 | |
Omar Polo | 3dd89fbb44 | |
Omar Polo | 3a8c76eab2 | |
Omar Polo | 4267093e37 | |
Omar Polo | f057c92622 | |
Omar Polo | c68baad22a | |
Omar Polo | 0046c1fe9c | |
Omar Polo | bb595bff79 | |
Omar Polo | 070b32952c | |
Omar Polo | 114e9a4206 | |
Omar Polo | 0ac785a6fa | |
Omar Polo | e1e04caa4f | |
Omar Polo | df5058c919 | |
Omar Polo | a01a91db06 | |
Omar Polo | 9a821f8c0f | |
Omar Polo | ac9f55ba32 | |
Omar Polo | 34886b1e55 | |
Omar Polo | 1e0b974519 | |
Omar Polo | 0b62f4842d | |
Anna “CyberTailor” | c9e97a6ecb | |
Omar Polo | 837156014c | |
Omar Polo | 195f32d3a4 | |
Omar Polo | bd8683d0fd | |
Omar Polo | 06035a0237 | |
Omar Polo | 97b306cbee | |
Omar Polo | 77718c121f | |
Omar Polo | 17493a486c | |
Omar Polo | eb4f96c10a | |
Omar Polo | e92efb0d8e | |
Omar Polo | 872a717687 | |
Omar Polo | b24c6fcc1c | |
Omar Polo | 6130e0eeac | |
Omar Polo | 71cac3a08b | |
Omar Polo | 8295757f96 | |
Omar Polo | 0ab57224fb | |
Omar Polo | a4180f1d0b | |
Omar Polo | 19a8d9fe74 | |
Omar Polo | 4b93be289b | |
Omar Polo | 21cf735f37 | |
Omar Polo | e59b7f30e7 | |
Omar Polo | 8141737318 | |
Omar Polo | fad3441ba9 | |
nytpu | 50a8f9107c | |
Omar Polo | 7b00c8900b | |
Omar Polo | d2da235ad9 | |
Omar Polo | 534afd0ddc | |
Omar Polo | 4ceb570910 | |
Omar Polo | 9715efe6f3 | |
Omar Polo | c5b4db930e | |
Omar Polo | 71ddfd2023 | |
Omar Polo | 83c62ff950 | |
Omar Polo | 5c4855299c | |
Omar Polo | d040746a37 | |
Omar Polo | 24232204eb | |
Omar Polo | 934f957f29 | |
Omar Polo | 7bb80ca90d | |
Omar Polo | edc5ca667d | |
Omar Polo | 2025e96d97 | |
Omar Polo | cd5826b8ba | |
Omar Polo | aa9543b9fd | |
Omar Polo | 7277bb7dc2 | |
Omar Polo | aae8f6bf2b | |
Omar Polo | 3eabd37fe7 | |
Omar Polo | 9b15e4c628 | |
Omar Polo | 7f03b52bd6 | |
Omar Polo | f7c6f7155a | |
Omar Polo | a5fb2593a9 | |
Omar Polo | 32fbc47803 | |
Omar Polo | 0126d91d1d | |
Omar Polo | 7600099513 | |
Omar Polo | 36e6e793a1 | |
Omar Polo | 1ab7c96bb3 | |
Omar Polo | d29a2ee224 | |
Omar Polo | 5df699d1ab | |
Omar Polo | 39ad4b8d7f | |
Omar Polo | bd2eb09520 | |
Omar Polo | 93124fce99 | |
Omar Polo | 9ac625870e | |
Omar Polo | ddd291dd7c | |
Omar Polo | de8dc03c4d | |
Omar Polo | be97b57fc8 | |
Omar Polo | 2573332d7f | |
Anna “CyberTailor” | 07fbbf0610 | |
Omar Polo | 3bba4fc55f | |
Omar Polo | f759119d2f | |
Omar Polo | f3f4543119 | |
Omar Polo | b48eb0db52 | |
Omar Polo | 1f6de7491c | |
Omar Polo | 1670b5f3ae | |
Omar Polo | c6fb44edd8 | |
Omar Polo | d45d5306e3 | |
Omar Polo | 0d823f229d | |
Omar Polo | 543f4a66fe | |
Omar Polo | 5734723f92 | |
Omar Polo | 870063d344 | |
Omar Polo | 7fc52d5104 | |
Omar Polo | 96e3c01314 | |
Omar Polo | 225686d7fd | |
Omar Polo | a3ebbccb8e | |
Omar Polo | a555e0d67b | |
Omar Polo | cb2b85512c | |
Omar Polo | 617ae38546 | |
Omar Polo | 475205fa0f | |
Omar Polo | ddd3c5529f | |
Omar Polo | f2f8eb35c8 | |
Omar Polo | 3bd4a6dea0 | |
Omar Polo | df40c96931 | |
Omar Polo | 5e41063f1b | |
Omar Polo | df9b379081 | |
Omar Polo | db48d276a7 | |
Omar Polo | 7b27af8388 | |
Omar Polo | 2962200607 | |
Omar Polo | 48589d9de9 | |
Omar Polo | 60d8851fec | |
Omar Polo | bc70003f93 | |
Omar Polo | 8addd12c1a | |
Omar Polo | b6c093c99a | |
Omar Polo | 5c4e8e97bd | |
Omar Polo | 93cab6dc78 | |
Omar Polo | 6d362b3556 | |
Omar Polo | 8fb0df4d79 | |
Omar Polo | 99a95569a4 | |
Omar Polo | 18bd83915e | |
Omar Polo | ca44613693 | |
Omar Polo | 5eb842cd1f | |
Omar Polo | 5f03bf170f | |
Omar Polo | 16c391a063 | |
Omar Polo | fde8750859 | |
Omar Polo | 54203115cd | |
Omar Polo | 9b64995359 | |
Omar Polo | 1d6cbdd0df | |
Omar Polo | d8d170aa5e | |
Omar Polo | aa6b8cf8ac | |
Omar Polo | 72e7fbbcb7 | |
Omar Polo | 8036523fff | |
Omar Polo | f731e4a781 | |
Omar Polo | 19243c768e | |
Omar Polo | bf2850b938 | |
Omar Polo | 1595c277ef | |
Omar Polo | 9bcc265f58 | |
Omar Polo | ce264ddc58 | |
Omar Polo | 445816c2cd | |
Omar Polo | ffd92e638c | |
Omar Polo | 9448a01fdb | |
Omar Polo | 6468868fee | |
Omar Polo | 4ee085938f | |
Omar Polo | 381fccdc56 | |
Omar Polo | a9885c6d6b | |
Omar Polo | effbc069ba | |
Omar Polo | 379d2608e9 | |
Omar Polo | a9dc6fc606 | |
Omar Polo | 456a4c6b6e | |
Omar Polo | 2a7f69f4ee | |
Omar Polo | 4252e62cad | |
Omar Polo | a68203f089 | |
Omar Polo | f6a65aaef9 | |
Omar Polo | efe75a7660 |
30
.cirrus.yml
30
.cirrus.yml
|
@ -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
|
||||
|
|
|
@ -2,8 +2,10 @@
|
|||
!*.c
|
||||
!*.h
|
||||
!*.y
|
||||
!compat/*.c
|
||||
!compat/*.h
|
||||
!*.[1-9]
|
||||
!compat
|
||||
!have/*.c
|
||||
!Makefile
|
||||
!configure
|
||||
!contrib/Docker.gmid.conf
|
||||
!contrib/gencert
|
||||
|
|
|
@ -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
|
||||
|
|
196
ChangeLog
196
ChangeLog
|
@ -1,3 +1,199 @@
|
|||
2024-04-03 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* configure: improve function checking in the configure
|
||||
* have/landlock.c: fix landlock test
|
||||
* gmid.c (main_print_conf): fix config dumping with -nn
|
||||
|
||||
2024-03-03 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gmid.c: fix `log access path' with a chroot
|
||||
|
||||
2024-01-30 Anna “CyberTailor”
|
||||
|
||||
* contrib/vim/indent/gmid.vim: fix indent
|
||||
|
||||
2024-01-30 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y: don't make log styles reserved keywords. Unbreaks the
|
||||
example in the manpage with `common = ...'.
|
||||
|
||||
2024-01-26 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y: rework grammar to allow the semicolon after
|
||||
variables/macros definition and top-level options
|
||||
|
||||
2024-01-24 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* configure (VERSION): release 2.0.1
|
||||
|
||||
2024-01-21 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* convert gmid to the new imsg API
|
||||
|
||||
2024-01-14 Anna “CyberTailor”
|
||||
|
||||
* configure: fix --mandir handling
|
||||
|
||||
2024-01-11 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* configure (VERSION): release 2.0
|
||||
|
||||
2024-01-09 Anna “CyberTailor”
|
||||
|
||||
* contrib/vim/syntax/gmid.vim: update Vim syntax file
|
||||
* contrib/vim/ale_listers/gmid/gmid.vim: add ALE linter
|
||||
|
||||
2023-10-18 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* ge.c (load_local_cert): generate EC certs by default, use -R to keep generating RSA ones.
|
||||
* utils.c (gencert): generate EC too
|
||||
* gg.c (get): print the response header for non-2x replies to standard error
|
||||
|
||||
2023-10-15 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gg.c (main): exit with the gemini response code (unless it's 2x)
|
||||
|
||||
2023-08-29 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* ge.c (data_dir): use $XDG_DATA_HOME/gemexp instead of /gmid for the certificates.
|
||||
|
||||
2023-08-18 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* fcgi.c (fcgi_req): sync the parameters with RFC3875 (CGI)
|
||||
(fcgi_req): send "GET" as REQUEST_METHOD
|
||||
|
||||
2023-08-08 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* fcgi.c (fcgi_req): implement SCRIPT_NAME / PATH_INFO splitting for fastcgi
|
||||
(fcgi_req): add `fastcgi strip'
|
||||
|
||||
2023-08-07 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* logger.c (logger_dispatch_server): allow to change the syslog(3) facility.
|
||||
|
||||
* gmid.c (main): attempt to load TLS certificates and load mimes and virtual hosts root as part of configtest (-n) rather than verifying the syntax of the configuration only.
|
||||
|
||||
2023-07-25 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gmid.c (log_request): allow to change the logging style.
|
||||
|
||||
2023-07-24 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y: add ability to log to files with `log access <path>'.
|
||||
|
||||
2023-07-23 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y: revamp fastcgi configuration; make it per-location
|
||||
|
||||
2023-07-22 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* titan.c (main): add titan(1), a simple titan client
|
||||
|
||||
* gg.c (get): warn when the server doesn't use TLS' close notify
|
||||
|
||||
2023-07-01 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* fcgi.c (fcgi_handle_stdout): parse and log the fastcgi reply
|
||||
|
||||
2023-06-24 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* server.c (handle_handshake): correctly handle TLS handshake failures.
|
||||
|
||||
* server.c (client_close_ev): plug memory leak
|
||||
|
||||
2023-06-23 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y: implement `listen on'
|
||||
|
||||
2023-06-13 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* regress/sha: remove regress/sha; sha256/sha256sum is no more required for the regress suite.
|
||||
|
||||
2023-06-12 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* regress/lib.sh (run_test): use the default prefork in tests
|
||||
|
||||
2023-06-11 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* crypto.c: add a privsep crypto engine (enabled only on OpenBSD)
|
||||
|
||||
* configure: add -Wpointer-sign to the mix, adjust the code to cope.
|
||||
|
||||
2023-05-05 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* contrib/gencert: add -e to generate EC keys
|
||||
|
||||
2023-05-08 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* sandbox.c: drop landlock, seccomp and capsicum support
|
||||
|
||||
2022-09-10 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y (string): retire the deprecated `mime' and `map' config options
|
||||
|
||||
2022-09-07 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* ge.c (main): add `gemexp': small program to quickly export a directory over Gemini.
|
||||
|
||||
2022-09-06 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* server.c: drop CGI support.
|
||||
|
||||
2022-07-07 Omar Polo <op@omarpolo.com>
|
||||
|
||||
Included as part of gmid 1.8.5:
|
||||
|
||||
* log.c (logger_main): fix timestamps in log files. Reported by Karl Jeacle, thanks!
|
||||
|
||||
* dirs.c (scandir_fd): drop d_reclen; it's not available on DragonflyBSD (at least.)
|
||||
|
||||
2022-07-07 Anna “CyberTailor”
|
||||
|
||||
* Makefile: include contrib/ in dist
|
||||
|
||||
2022-07-04 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* contrib/gmid: remove OpenBSD' rc file: it's now maintained in the ports tree
|
||||
|
||||
* configure (VERSION): 1.8.4 tagged
|
||||
|
||||
* server.c (check_path): log when it fails to open a file because of permissions
|
||||
(client_write): encode file names in the directory index, spotted by cage
|
||||
(client_write): add a trailing / for dirs in the directory index.
|
||||
|
||||
* iri.c (parse_path): allow '@' and ':' in paths
|
||||
|
||||
2022-04-08 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* mime.c (mime): do a binary search to match the MIME time
|
||||
|
||||
* gmid.c (listener_main): don't load the default mime listing when
|
||||
`types' is used. (text/gemini -> gmi/gemini is still hardcoded)
|
||||
|
||||
* mime.c (add_mime): fix memory leak in the mime handling
|
||||
|
||||
2022-04-07 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gmid.conf.5: move config file documentation into a new man page
|
||||
|
||||
* parse.y (option): deprecate `map' rule in favour of the `types' block
|
||||
|
||||
2022-03-27 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* configure (VERSION): 1.8.3 tagged
|
||||
|
||||
* server.c (start_cgi): fix a possible out-of-bound access in the
|
||||
CGI handling.
|
||||
|
||||
2022-03-26 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* configure (VERSION): 1.8.2 tagged
|
||||
|
||||
* server.c (handle_imsg_cgi_res): fix a CGI timing issue: if a
|
||||
connection handled by a CGI scripts is interrupted with the right
|
||||
timing it causes the server process to exit with "fatal in
|
||||
client_by_id: invalid id X"
|
||||
|
||||
2022-02-26 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y: add the types block
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
|||
Copyright (c) 2020 Omar Polo <op@omarpolo.com>
|
||||
Copyright (c) 2020, 2021, 2022 Omar Polo <op@omarpolo.com>
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
|
|
181
Makefile
181
Makefile
|
@ -1,51 +1,190 @@
|
|||
# Copyright (c) 2022 Omar Polo <op@omarpolo.com>
|
||||
# Copyright (c) 2011, 2013-2022 Ingo Schwarze <schwarze@openbsd.org>
|
||||
# Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
# tests to run as a port of the regression suite. Leave empty to run
|
||||
# all.
|
||||
TESTS=
|
||||
|
||||
.PHONY: all static clean cleanall test regress install
|
||||
# host to bind to during regress
|
||||
REGRESS_HOST = localhost
|
||||
|
||||
all: Makefile.local gmid gg
|
||||
# -- build-related variables --
|
||||
|
||||
Makefile.local: configure
|
||||
./configure
|
||||
COBJS = ${COMPATS:.c=.o}
|
||||
|
||||
include Makefile.local
|
||||
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
|
||||
|
||||
y.tab.c: parse.y
|
||||
${YACC} -b y parse.y
|
||||
GMID_OBJS = ${GMID_SRCS:.c=.o} ${COBJS}
|
||||
|
||||
gmid: ${GMID_OBJS}
|
||||
${CC} ${GMID_OBJS} -o $@ ${LDFLAGS}
|
||||
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
|
||||
|
||||
gg: ${GG_OBJS}
|
||||
${CC} ${GG_OBJS} -o $@ ${LDFLAGS}
|
||||
GEMEXP_OBJS = ${GEMEXP_SRCS:.c=.o} ${COBJS}
|
||||
|
||||
static: ${GMID_OBJS}
|
||||
${CC} ${GMID_OBJS} -o gmid ${LDFLAGS} ${STATIC}
|
||||
GG_SRCS = gg.c iri.c log.c utf8.c
|
||||
|
||||
GG_OBJS = ${GG_SRCS:.c=.o} ${COBJS}
|
||||
|
||||
TITAN_SRCS = titan.c iri.c log.c utf8.c
|
||||
TITAN_OBJS = ${TITAN_SRCS:.c=.o} ${COBJS}
|
||||
|
||||
SRCS = gmid.h iri.h log.h parse.y proc.h \
|
||||
${GMID_SRCS} ${GEMEXP_SRCS} ${GG_SRCS} ${TITAN_SRCS}
|
||||
|
||||
DISTNAME = gmid-${VERSION}
|
||||
|
||||
# -- public targets --
|
||||
|
||||
all: config.mk gmid gemexp gg titan
|
||||
.PHONY: all tags clean cleanall test regress install
|
||||
|
||||
config.mk config.h: configure
|
||||
@echo "$@ is out of date; please run ./configure"
|
||||
@exit 1
|
||||
|
||||
include config.mk
|
||||
|
||||
clean:
|
||||
rm -f *.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
|
||||
|
||||
cleanall: clean
|
||||
${MAKE} -C regress clean
|
||||
|
||||
distclean: clean
|
||||
rm -f config.h config.h.old config.log config.log.old config.mk
|
||||
|
||||
test: regress
|
||||
regress: all
|
||||
${MAKE} 'TESTS=${TESTS}' -C regress all
|
||||
|
||||
install: gmid
|
||||
install: gmid gg gemexp
|
||||
mkdir -p ${DESTDIR}${BINDIR}
|
||||
mkdir -p ${DESTDIR}${MANDIR}/man1
|
||||
${INSTALL_PROGRAM} gmid ${DESTDIR}${BINDIR}
|
||||
mkdir -p ${DESTDIR}${MANDIR}/man5
|
||||
mkdir -p ${DESTDIR}${MANDIR}/man8
|
||||
${INSTALL_PROGRAM} gemexp ${DESTDIR}${BINDIR}
|
||||
${INSTALL_PROGRAM} gg ${DESTDIR}${BINDIR}
|
||||
${INSTALL_MAN} gmid.1 ${DESTDIR}${MANDIR}/man1
|
||||
${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
|
||||
|
||||
compile_flags.txt:
|
||||
printf "%s\n" ${CFLAGS} > compile_flags.txt
|
||||
uninstall:
|
||||
rm ${DESTDIR}${BINDIR}/gemexp
|
||||
rm ${DESTDIR}${BINDIR}/gg
|
||||
rm ${DESTDIR}${BINDIR}/gmid
|
||||
rm ${DESTDIR}${BINDIR}/titan
|
||||
rm ${DESTDIR}${MANDIR}/man1/gemexp.1
|
||||
rm ${DESTDIR}${MANDIR}/man1/gg.1
|
||||
rm ${DESTDIR}${MANDIR}/man1/titan.1
|
||||
rm ${DESTDIR}${MANDIR}/man5/gmid.conf.5
|
||||
rm ${DESTDIR}${MANDIR}/man8/gmid.8
|
||||
|
||||
tags:
|
||||
ctags ${SRCS}
|
||||
|
||||
# --internal build targets --
|
||||
|
||||
gmid: ${GMID_OBJS}
|
||||
${CC} ${GMID_OBJS} -o $@ ${LDFLAGS} ${LIBS}
|
||||
|
||||
gemexp: ${GEMEXP_OBJS}
|
||||
${CC} ${GEMEXP_OBJS} -o $@ ${LDFLAGS} ${LIBS}
|
||||
|
||||
gg: ${GG_OBJS}
|
||||
${CC} ${GG_OBJS} -o $@ ${LDFLAGS} ${LIBS}
|
||||
|
||||
titan: ${TITAN_OBJS}
|
||||
${CC} ${TITAN_OBJS} -o $@ ${LDFLAGS} ${LIBS}
|
||||
|
||||
y.tab.c: parse.y
|
||||
${YACC} -b y parse.y
|
||||
|
||||
# make sure we pass -o to ${CC}. OpenBSD default suffix rule doesn't
|
||||
.SUFFIXES: .c .o
|
||||
.c.o:
|
||||
${CC} ${CFLAGS} -c $< -o $@
|
||||
|
||||
# -- maintainer targets --
|
||||
|
||||
.PHONY: lint release dist
|
||||
|
||||
lint:
|
||||
man -Tlint -Wstyle -l gmid.8 gmid.conf.5 gemexp.1 gg.1 titan.1
|
||||
|
||||
PUBKEY = keys/gmid-2.0.pub
|
||||
PRIVKEY = set-PRIVKEY
|
||||
DISTFILES = .cirrus.yml .dockerignore .gitignore ChangeLog LICENSE \
|
||||
Makefile README.md config.c configure crypto.c dirs.c fcgi.c \
|
||||
ge.c gemexp.1 gg.1 gg.c gmid.8 gmid.c gmid.conf.5 gmid.h \
|
||||
iri.c iri.h log.c log.h logger.c mime.c parse.y proc.c \
|
||||
proc.h proxy.c puny.c sandbox.c server.c titan.1 titan.c \
|
||||
utf8.c utils.c y.tab.c
|
||||
|
||||
release:
|
||||
sed -i -e '/^RELEASE=/s/no/yes/' configure
|
||||
${MAKE} dist
|
||||
sed -i -e '/^RELEASE=/s/yes/no/' configure
|
||||
signify -S -e -m ${DISTNAME}.sha256 -s ${PRIVKEY}
|
||||
|
||||
verify-release:
|
||||
signify -C -p ${PUBKEY} -x ${DISTNAME}.sha256.sig
|
||||
|
||||
dist: ${DISTNAME}.sha256
|
||||
|
||||
${DISTNAME}.sha256: ${DISTNAME}.tar.gz
|
||||
sha256 ${DISTNAME}.tar.gz > $@
|
||||
|
||||
${DISTNAME}.tar.gz: ${DISTFILES}
|
||||
mkdir -p .dist/${DISTNAME}/
|
||||
${INSTALL} -m 0644 ${DISTFILES} .dist/${DISTNAME}/
|
||||
cd .dist/${DISTNAME} && chmod 755 configure
|
||||
${MAKE} -C compat DESTDIR=${PWD}/.dist/${DISTNAME}/compat dist
|
||||
${MAKE} -C contrib DESTDIR=${PWD}/.dist/${DISTNAME}/contrib dist
|
||||
${MAKE} -C have DESTDIR=${PWD}/.dist/${DISTNAME}/have dist
|
||||
${MAKE} -C keys DESTDIR=${PWD}/.dist/${DISTNAME}/keys dist
|
||||
${MAKE} -C regress DESTDIR=${PWD}/.dist/${DISTNAME}/regress dist
|
||||
cd .dist/ && tar zcf ../$@ ${DISTNAME}
|
||||
rm -rf .dist/
|
||||
|
||||
# -- dependencies --
|
||||
|
||||
-include config.d
|
||||
-include crypto.d
|
||||
-include dirs.d
|
||||
-include fcgi.d
|
||||
-include ge.d
|
||||
-include gg.d
|
||||
-include gmid.d
|
||||
-include iri.d
|
||||
-include log.d
|
||||
-include logger.d
|
||||
-include mime.d
|
||||
-include proc.d
|
||||
-include proxy.d
|
||||
-include puny.d
|
||||
-include sandbox.d
|
||||
-include server.d
|
||||
-include titan.d
|
||||
-include utf8.d
|
||||
-include utils.d
|
||||
-include y.tab.d
|
||||
|
|
105
README.md
105
README.md
|
@ -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 a POSIX libc, 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
|
||||
|
@ -121,44 +106,42 @@ testing on various platform is done to ensure that everything is
|
|||
working as intended.
|
||||
|
||||
|
||||
### Docker
|
||||
|
||||
If you have trouble installing LibreSSL or libretls, in `contrib`
|
||||
there's a sample Dockerfile. See [the contrib page][contrib-page] for
|
||||
more information.
|
||||
|
||||
[contrib-page]: https://gmid.omarpolo.com/contrib.html#dockerfile
|
||||
|
||||
### Testing
|
||||
|
||||
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
|
||||
|
||||
Any form of contribution is welcome, not only patches or bug reports.
|
||||
If you have a sample configuration for some specific use-case, a
|
||||
script or anything that could be useful to others, consider adding it
|
||||
to the `contrib` directory.
|
||||
|
||||
|
||||
## Architecture/Security considerations
|
||||
|
||||
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.
|
||||
|
|
|
@ -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
|
|
@ -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) */
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -14,6 +14,8 @@
|
|||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
int
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
#include "../config.h"
|
||||
|
||||
#ifdef HAVE_PROGRAM_INVOCATION_SHORT_NAME
|
||||
#if HAVE_PROGRAM_INVOCATION_SHORT_NAME
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
287
compat/imsg.c
287
compat/imsg.c
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 *);
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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_ */
|
|
@ -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;
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2021 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>
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -14,249 +16,354 @@
|
|||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
# adapted from mandoc configure and oconfigure, thanks schwarze@ and
|
||||
# kristaps@!
|
||||
|
||||
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.3
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
# --------
|
||||
# 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
|
||||
|
||||
if runtest noop MMD -MMD; then
|
||||
if singletest noop MMD -MMD; then
|
||||
CFLAGS="${CFLAGS} -MMD"
|
||||
echo "adding -MMD to CFLAGS" 1>&2
|
||||
echo "adding -MMD to CFLAGS" 1>&3
|
||||
fi
|
||||
|
||||
# --------
|
||||
# tests for config.h
|
||||
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
|
||||
|
||||
|
@ -272,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
|
||||
|
@ -287,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>"
|
||||
|
@ -310,178 +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*, ...);"
|
||||
COMPAT="${COMPAT} compat/err.o"
|
||||
else
|
||||
echo "#include <err.h>"
|
||||
fi
|
||||
if [ ${HAVE_EXPLICIT_BZERO} -eq 0 ]; then
|
||||
echo "extern void explicit_bzero(void*, size_t);"
|
||||
COMPAT="${COMPAT} compat/explicit_bzero.o"
|
||||
fi
|
||||
if [ ${HAVE_FREEZERO} -eq 0 ]; then
|
||||
echo "extern void freezero(void*, size_t);"
|
||||
COMPAT="${COMPAT} compat/freezero.o"
|
||||
fi
|
||||
if [ ${HAVE_GETDTABLECOUNT} -eq 0 ]; then
|
||||
echo "extern int getdtablecount(void);"
|
||||
COMPAT="${COMPAT} compat/getdtablecount.o"
|
||||
fi
|
||||
if [ ${HAVE_GETDTABLESIZE} -eq 0 ]; then
|
||||
echo "extern int getdtablesize(void);"
|
||||
COMPAT="${COMPAT} 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);"
|
||||
COMPAT="${COMPAT} compat/getprogname.o"
|
||||
fi
|
||||
if [ ${HAVE_IMSG} -eq 0 ]; then
|
||||
COMPAT="${COMPAT} 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);"
|
||||
COMPAT="${COMPAT} compat/reallocarray.o"
|
||||
fi
|
||||
if [ ${HAVE_RECALLOCARRAY} -eq 0 ]; then
|
||||
echo "extern void *recallocarray(void*, size_t, size_t, size_t);"
|
||||
COMPAT="${COMPAT} compat/recallocarray.o"
|
||||
fi
|
||||
if [ ${HAVE_SETPROCTITLE} -eq 0 ]; then
|
||||
echo "extern void setproctitle(const char *fmt, ...);"
|
||||
COMPAT="${COMPAT} 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);"
|
||||
COMPAT="${COMPAT} compat/strlcat.o"
|
||||
fi
|
||||
if [ ${HAVE_STRLCPY} -eq 0 ]; then
|
||||
echo "extern size_t strlcpy(char*, const char*, size_t);"
|
||||
COMPAT="${COMPAT} compat/strlcpy.o"
|
||||
fi
|
||||
if [ ${HAVE_STRTONUM} -eq 0 ]; then
|
||||
echo "extern long long strtonum(const char*, long long, long long, const char**);"
|
||||
COMPAT="${COMPAT} 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);"
|
||||
COMPAT="${COMPAT} 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
|
||||
|
||||
GMID_SRCS="dirs.c ex.c fcgi.c gmid.c iri.c log.c mime.c proxy.c puny.c"
|
||||
GMID_SRCS="${GMID_SRCS} sandbox.c server.c utf8.c utils.c y.tab.c"
|
||||
|
||||
GMID_OBJS=
|
||||
for src in ${GMID_SRCS}; do
|
||||
GMID_OBJS="${GMID_OBJS} ${src%.c}.o"
|
||||
done
|
||||
GMID_OBJS="${GMID_OBJS} ${COMPAT}"
|
||||
|
||||
GG_SRCS="gg.c iri.c utf8.c"
|
||||
|
||||
GG_OBJS=
|
||||
for src in ${GG_SRCS}; do
|
||||
GG_OBJS="${GG_OBJS} ${src%.c}.o"
|
||||
done
|
||||
GG_OBJS="${GG_OBJS} ${COMPAT}"
|
||||
|
||||
ALL_SRCS="${GMID_SRCS} gg.c"
|
||||
|
||||
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}
|
||||
|
||||
GMID_OBJS = ${GMID_OBJS}
|
||||
GG_OBJS = ${GG_OBJS}
|
||||
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__
|
||||
|
||||
printf "COMPAT ="
|
||||
for comp in ${COMPAT}; do
|
||||
printf " %s/%s" "$PWD" "$comp"
|
||||
done
|
||||
printf "\n\n"
|
||||
echo "file config.mk: written" 1>&2
|
||||
echo "file config.mk: written" 1>&3
|
||||
|
||||
# Include dependency info
|
||||
for src in ${ALL_SRCS}; do
|
||||
printf "%s\n" "-include ${src%.c}.d"
|
||||
done
|
||||
|
||||
for comp in ${COMPAT}; do
|
||||
printf "%s\n" "-include ${comp%.o}.d"
|
||||
done
|
||||
|
||||
echo "file Makefile.local: written" 1>&2
|
||||
echo "file Makefile.local: written" 1>&3
|
||||
echo >&2
|
||||
echo "Now run \`make' to compile." >&2
|
||||
echo >&2
|
||||
|
||||
exit 0
|
||||
|
|
|
@ -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 "/"
|
||||
}
|
|
@ -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"]
|
||||
|
|
|
@ -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
|
|
@ -9,10 +9,6 @@ gencert
|
|||
|
||||
Simple shell script to generate self-signed certificates.
|
||||
|
||||
gmid
|
||||
|
||||
Sample rc(8) script for OpenBSD, to be placed in /etc/rc.d.
|
||||
|
||||
gmid.service
|
||||
|
||||
Simple systemd service file.
|
||||
|
@ -21,6 +17,11 @@ gmid.sysusers
|
|||
|
||||
Sample systemd-sysusers' config file.
|
||||
|
||||
mime.types
|
||||
|
||||
A copy of OpenBSD' /usr/share/misc/mime.types to be included
|
||||
within a `types' block.
|
||||
|
||||
renew-certs
|
||||
|
||||
Flexible script meant to be run in a cronjob to watch for cert
|
||||
|
|
|
@ -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
|
||||
|
|
14
contrib/gmid
14
contrib/gmid
|
@ -1,14 +0,0 @@
|
|||
#!/bin/ksh
|
||||
#
|
||||
|
||||
daemon="/usr/local/bin/gmid"
|
||||
daemon_flags="-c /etc/gmid.conf"
|
||||
|
||||
. /etc/rc.d/rc.subr
|
||||
|
||||
rc_pre() {
|
||||
# use rcexec here since daemon_flags may contain arguments with spaces
|
||||
${rcexec} "${daemon} -n ${daemon_flags}"
|
||||
}
|
||||
|
||||
rc_cmd $1
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
# $OpenBSD: mime.types,v 1.4 2018/01/06 22:05:03 sthen Exp $
|
||||
|
||||
application/atom+xml atom
|
||||
application/font-woff woff
|
||||
application/java-archive jar war ear
|
||||
application/javascript js
|
||||
application/json json
|
||||
application/mac-binhex40 hqx
|
||||
application/msword doc
|
||||
application/octet-stream bin exe dll
|
||||
application/octet-stream deb
|
||||
application/octet-stream dmg
|
||||
application/octet-stream fs iso img
|
||||
application/octet-stream msi msp msm
|
||||
application/pdf pdf
|
||||
application/postscript ps eps ai
|
||||
application/rss+xml rss
|
||||
application/rtf rtf
|
||||
application/vnd.apple.mpegurl m3u8
|
||||
application/vnd.google-earth.kml+xml kml
|
||||
application/vnd.google-earth.kmz kmz
|
||||
application/vnd.ms-excel xls
|
||||
application/vnd.ms-fontobject eot
|
||||
application/vnd.ms-powerpoint ppt
|
||||
application/vnd.oasis.opendocument.chart odc
|
||||
application/vnd.oasis.opendocument.chart-template otc
|
||||
application/vnd.oasis.opendocument.database odb
|
||||
application/vnd.oasis.opendocument.formula odf
|
||||
application/vnd.oasis.opendocument.formula-template otf
|
||||
application/vnd.oasis.opendocument.graphics odg
|
||||
application/vnd.oasis.opendocument.graphics-template otg
|
||||
application/vnd.oasis.opendocument.image odi
|
||||
application/vnd.oasis.opendocument.image-template oti
|
||||
application/vnd.oasis.opendocument.presentation odp
|
||||
application/vnd.oasis.opendocument.presentation-template otp
|
||||
application/vnd.oasis.opendocument.spreadsheet ods
|
||||
application/vnd.oasis.opendocument.spreadsheet-template ots
|
||||
application/vnd.oasis.opendocument.text odt
|
||||
application/vnd.oasis.opendocument.text-master odm
|
||||
application/vnd.oasis.opendocument.text-template ott
|
||||
application/vnd.oasis.opendocument.text-web oth
|
||||
application/vnd.wap.wmlc wmlc
|
||||
application/x-7z-compressed 7z
|
||||
application/x-cocoa cco
|
||||
application/x-java-archive-diff jardiff
|
||||
application/x-java-jnlp-file jnlp
|
||||
application/x-makeself run
|
||||
application/x-ns-proxy-autoconfig pac
|
||||
application/x-perl pl pm
|
||||
application/x-pilot prc pdb
|
||||
application/x-rar-compressed rar
|
||||
application/x-redhat-package-manager rpm
|
||||
application/x-sea sea
|
||||
application/x-shockwave-flash swf
|
||||
application/x-stuffit sit
|
||||
application/x-tcl tcl tk
|
||||
application/x-x509-ca-cert der pem crt
|
||||
application/x-xpinstall xpi
|
||||
application/xhtml+xml xhtml
|
||||
application/zip zip
|
||||
|
||||
audio/basic au snd
|
||||
audio/midi mid midi kar
|
||||
audio/mpeg mp3
|
||||
audio/ogg ogg
|
||||
audio/x-m4a m4a
|
||||
audio/x-realaudio ra
|
||||
|
||||
image/gif gif
|
||||
image/jpeg jpeg jpg
|
||||
image/png png
|
||||
image/svg+xml svg svgz
|
||||
image/tiff tif tiff
|
||||
image/vnd.wap.wbmp wbmp
|
||||
image/webp webp
|
||||
image/x-icon ico
|
||||
image/x-jng jng
|
||||
image/x-ms-bmp bmp
|
||||
|
||||
text/css css
|
||||
text/html html htm shtml
|
||||
text/mathml mml
|
||||
text/plain txt
|
||||
text/vnd.sun.j2me.app-descriptor jad
|
||||
text/vnd.wap.wml wml
|
||||
text/x-component htc
|
||||
text/xml xml
|
||||
|
||||
video/3gpp 3gpp 3gp
|
||||
video/mp2t ts
|
||||
video/mp4 mp4
|
||||
video/mpeg mpeg mpg
|
||||
video/quicktime mov
|
||||
video/webm webm
|
||||
video/x-flv flv
|
||||
video/x-m4v m4v
|
||||
video/x-matroska mkv
|
||||
video/x-mng mng
|
||||
video/x-ms-asf asx asf
|
||||
video/x-ms-wmv wmv
|
||||
video/x-msvideo avi
|
|
@ -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:
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
1
dirs.c
1
dirs.c
|
@ -128,7 +128,6 @@ scandir_fd(int fd, struct dirent ***namelist,
|
|||
|
||||
p->d_ino = d->d_ino;
|
||||
p->d_type = d->d_type;
|
||||
p->d_reclen = d->d_reclen;
|
||||
memcpy(p->d_name, d->d_name, namlen + 1);
|
||||
names[nitems++] = p;
|
||||
}
|
||||
|
|
531
ex.c
531
ex.c
|
@ -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
246
fcgi.c
|
@ -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", "");
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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
35
gg.1
|
@ -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
87
gg.c
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
.\" Copyright (c) 2021, 2022, 2023, 2024 Omar Polo <op@omarpolo.com>
|
||||
.\"
|
||||
.\" Permission to use, copy, modify, and distribute this software for any
|
||||
.\" purpose with or without fee is hereby granted, provided that the above
|
||||
.\" copyright notice and this permission notice appear in all copies.
|
||||
.\"
|
||||
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.Dd April 27, 2024
|
||||
.Dt GMID 8
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm gmid
|
||||
.Nd Gemini server
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Bk -words
|
||||
.Op Fl fhnVv
|
||||
.Op Fl c Ar config
|
||||
.Op Fl D Ar macro Ns = Ns Ar value
|
||||
.Op Fl P Ar pidfile
|
||||
.Ek
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is a simple and minimal gemini server that can serve static files,
|
||||
talk to FastCGI applications and act as a gemini reverse proxy.
|
||||
.Pp
|
||||
.Nm
|
||||
rereads the configuration file when it receives
|
||||
.Dv SIGHUP
|
||||
and reopens log files when it receives
|
||||
.Dv SIGUSR1 .
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width 14m
|
||||
.It Fl c Ar config
|
||||
Specifies the configuration file.
|
||||
The default is
|
||||
.Pa /etc/gmid.conf .
|
||||
.It Fl D Ar macro Ns = Ns Ar value
|
||||
Define
|
||||
.Ar macro
|
||||
to be set to
|
||||
.Ar value
|
||||
on the command line.
|
||||
Overrides the definition of
|
||||
.Ar macro
|
||||
in the config file if present.
|
||||
.It Fl f
|
||||
Do not daemonize.
|
||||
Stay and log in the foreground.
|
||||
.It Fl h , Fl -help
|
||||
Print the usage and exit.
|
||||
.It Fl n
|
||||
Check that the configuration is valid, but don't start the server.
|
||||
If specified two or more time, dump the configuration in addition to
|
||||
verify it.
|
||||
.It Fl P Ar pidfile
|
||||
Write daemon's pid to the given location.
|
||||
.Ar pidfile
|
||||
will also act as lock: if another process is holding a lock on that
|
||||
file,
|
||||
.Nm
|
||||
will refuse to start.
|
||||
.It Fl V , Fl -version
|
||||
Print the version and exit.
|
||||
.It Fl v
|
||||
Verbose mode.
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
To run
|
||||
.Nm
|
||||
a configuration file and a X.509 certificate must be provided.
|
||||
A self-signed certificate, which are commonly used in the Geminispace,
|
||||
can be generated using for e.g.\&
|
||||
.Xr openssl 1 :
|
||||
.Bd -literal -offset indent
|
||||
# openssl req \-x509 \-newkey rsa:4096 \-nodes \e
|
||||
\-keyout /etc/ssl/private/example.com.key \e
|
||||
\-out /etc/ssl/example.com.pem \e
|
||||
\-days 365 \-subj "/CN=example.com"
|
||||
# chmod 600 /etc/ssl/example.com.crt
|
||||
# chmod 600 /etc/ssl/private/example.com.key
|
||||
.Ed
|
||||
.Pp
|
||||
Then
|
||||
.Nm
|
||||
can be started with
|
||||
.Bd -literal -offset indent
|
||||
# gmid -c /etc/gmid.conf
|
||||
.Ed
|
||||
.Sh SEE ALSO
|
||||
.Xr gemexp 1 ,
|
||||
.Xr gg 1 ,
|
||||
.Xr gmid.conf 5
|
||||
.Sh ACKNOWLEDGEMENTS
|
||||
.Nm
|
||||
uses the
|
||||
.Dq Flexible and Economical
|
||||
UTF-8 decoder written by
|
||||
.An Bjoern Hoehrmann .
|
||||
.Sh AUTHORS
|
||||
.An -nosplit
|
||||
The
|
||||
.Nm
|
||||
program was written by
|
||||
.An Omar Polo Aq Mt op@omarpolo.com .
|
||||
.Sh CAVEATS
|
||||
.Bl -bullet
|
||||
.It
|
||||
All the root directories are opened during the daemon configuration;
|
||||
if a root directory is deleted and then re-created,
|
||||
.Nm
|
||||
won't be able to serve files inside that directory until a reload.
|
||||
This restriction only applies to the root directories and not their
|
||||
content.
|
||||
.It
|
||||
a %2F sequence is indistinguishable from a literal slash: this is not
|
||||
RFC3986-compliant.
|
||||
.It
|
||||
a %00 sequence is treated as invalid character and thus rejected.
|
||||
.El
|
|
@ -1,4 +1,4 @@
|
|||
.\" Copyright (c) 2021, 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,147 +11,35 @@
|
|||
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.Dd $Mdocdate: February 26 2022$
|
||||
.Dt GMID 1
|
||||
.Dd April 4, 2024
|
||||
.Dt GMID.CONF 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm gmid
|
||||
.Nd simple and secure Gemini server
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Bk -words
|
||||
.Op Fl fnv
|
||||
.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
|
||||
.Nm gmid.conf
|
||||
.Nd gmid Gemini server configuration file
|
||||
.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.
|
||||
is the configuration file format for the
|
||||
.Xr gmid 8
|
||||
Gemini server.
|
||||
.Pp
|
||||
.Nm
|
||||
rereads the configuration file when it receives
|
||||
.Dv SIGHUP .
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width 14m
|
||||
.It Fl c Ar config
|
||||
Specify the configuration file.
|
||||
.It Fl D Ar macro Ns = Ns Ar value
|
||||
Define
|
||||
.Ar macro
|
||||
to be set to
|
||||
.Ar value
|
||||
on the command line.
|
||||
Overrides the definition of
|
||||
.Ar macro
|
||||
in the config file if present.
|
||||
.It Fl f
|
||||
Stays and logs on the foreground.
|
||||
.It Fl n
|
||||
Check that the configuration is valid, but don't start the server.
|
||||
If specified two or more time, dump the configuration in addition to
|
||||
verify it.
|
||||
.It Fl P Ar pidfile
|
||||
Write daemon's pid to the given location.
|
||||
.Ar pidfile
|
||||
will also act as lock: if another process is holding a lock on that
|
||||
file,
|
||||
.Nm
|
||||
will refuse to start.
|
||||
.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 CONFIGURATION FILE
|
||||
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
|
||||
|
@ -211,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.crt" # -> /etc/keys/foo.crt
|
||||
key $certdir "/foo.pem" # -> /etc/keys/foo.pem
|
||||
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
|
||||
|
@ -230,32 +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 map Ar mime-type Cm to-ext Ar file-extension
|
||||
Map
|
||||
.Ar mime-type
|
||||
to the given
|
||||
.Ar file-extension .
|
||||
Both argument are strings.
|
||||
.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
|
||||
|
@ -269,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
|
||||
|
@ -279,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
|
||||
|
@ -327,12 +282,6 @@ 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.
|
||||
|
@ -340,25 +289,17 @@ 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
|
||||
.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
|
||||
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.
|
||||
.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.
|
||||
|
@ -367,7 +308,7 @@ If the FastCGI application is listening on a UNIX domain socket,
|
|||
is a local path name within the
|
||||
.Xr chroot 2
|
||||
root directory of
|
||||
.Nm .
|
||||
.Xr gmid 8 .
|
||||
Otherwise, the
|
||||
.Ic tcp
|
||||
keyword must be provided and
|
||||
|
@ -377,6 +318,99 @@ is interpreted as a hostname or an IP address.
|
|||
can be either a port number or the name of a service enclosed in
|
||||
double quotes.
|
||||
If not specified defaults to 9000.
|
||||
.It Ic strip Ar number
|
||||
Strip
|
||||
.Ar number
|
||||
leading path components from the request URL before splitting it in
|
||||
.Ev SCRIPT_NAME
|
||||
and
|
||||
.Ev PATH_INFO .
|
||||
.El
|
||||
.Pp
|
||||
The FastCGI handler will be given the following variables by default:
|
||||
.Bl -tag -width 24m
|
||||
.\" .It Ev GEMINI_DOCUMENT_ROOT
|
||||
.\" The root directory of the virtual host.
|
||||
.It Ev GEMINI_URL_PATH
|
||||
Full path of the request.
|
||||
.It Ev GEMINI_SEARCH_STRING
|
||||
The decoded
|
||||
.Ev QUERY_STRING
|
||||
if defined in the request and if it doesn't contain any unencoded
|
||||
.Sq =
|
||||
characters, otherwise unset.
|
||||
.It Ev GATEWAY_INTERFACE
|
||||
.Dq CGI/1.1
|
||||
.It Ev AUTH_TYPE
|
||||
The string "Certificate" if the client used a certificate, otherwise
|
||||
unset.
|
||||
.It Ev PATH_INFO
|
||||
The portion of the requested path that is derived from the the IRI
|
||||
path hierarchy following
|
||||
.Ev SCRIPT_NAME .
|
||||
Can be unset.
|
||||
.It Ev PATH_TRANSLATED
|
||||
Present if and only if
|
||||
.Ev PATH_INFO
|
||||
is set.
|
||||
It represent the translation of the
|
||||
.Ev PATH_INFO .
|
||||
.Nm gmid
|
||||
builds this by appending the
|
||||
.Ev PATH_INFO
|
||||
to the virtual host directory root.
|
||||
.It Ev QUERY_STRING
|
||||
The URL-encoded search or parameter string.
|
||||
.It Ev REMOTE_ADDR , Ev REMOTE_HOST
|
||||
Textual representation of the client IP.
|
||||
.It Ev REQUEST_METHOD
|
||||
This is present only for RFC3875 (CGI) compliance.
|
||||
It's always set to
|
||||
.Dq GET .
|
||||
.It Ev SCRIPT_NAME
|
||||
The virtual URI path to the script.
|
||||
Since it's impossible to determine in all cases the correct
|
||||
.Ev SCRIPT_NAME
|
||||
programmatically
|
||||
.Nm gmid
|
||||
assumes it's the empty string.
|
||||
It is recommended to manually specify this parameter when serving a
|
||||
sub-tree of a virtual host via FastCGI.
|
||||
.It Ev SERVER_NAME
|
||||
The name of the server
|
||||
.It Ev SERVER_PORT
|
||||
The port the server is listening on.
|
||||
.It Ev SERVER_PROTOCOL
|
||||
.Dq GEMINI
|
||||
.It Ev SERVER_SOFTWARE
|
||||
The name and version of the server, i.e.
|
||||
.Dq gmid/2.0.2
|
||||
.It Ev REMOTE_USER
|
||||
The subject of the client certificate if provided, otherwise unset.
|
||||
.It Ev TLS_CLIENT_ISSUER
|
||||
The is the issuer of the client certificate if provided, otherwise
|
||||
unset.
|
||||
.It Ev TLS_CLIENT_HASH
|
||||
The hash of the client certificate if provided, otherwise unset.
|
||||
The format is
|
||||
.Dq ALGO:HASH .
|
||||
.It Ev TLS_VERSION
|
||||
The TLS version negotiated with the peer.
|
||||
.It Ev TLS_CIPHER
|
||||
The cipher suite negotiated with the peer.
|
||||
.It Ev TLS_CIPHER_STRENGTH
|
||||
The strength in bits for the symmetric cipher that is being used with
|
||||
the peer.
|
||||
.It Ev TLS_CLIENT_NOT_AFTER
|
||||
The time corresponding to the end of the validity period of the peer
|
||||
certificate in the ISO 8601 format
|
||||
.Pq e.g. Dq 2021-02-07T20:17:41Z .
|
||||
.It Ev TLS_CLIENT_NOT_BEFORE
|
||||
The time corresponding to the start of the validity period of the peer
|
||||
certificate in the ISO 8601 format.
|
||||
.El
|
||||
.It Ic fastcgi off
|
||||
Disable FastCGI handling in the current location.
|
||||
.It Ic index Ar string
|
||||
Set the directory index file.
|
||||
If not specified, it defaults to
|
||||
|
@ -391,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
|
||||
|
@ -404,16 +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.
|
||||
.It Ic ocsp Ar file
|
||||
Specify an OCSP response to be stapled during TLS handshakes
|
||||
with this server.
|
||||
|
@ -427,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
|
||||
|
@ -461,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
|
||||
|
@ -513,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
|
||||
|
@ -526,275 +574,67 @@ Earch line may end with an optional semicolon.
|
|||
Include types definition from an external file, for example
|
||||
.Pa /usr/share/misc/mime.types .
|
||||
.El
|
||||
.Sh CGI
|
||||
When a request for an executable file matches the
|
||||
.Ic cgi
|
||||
rule, that file will be executed and its output fed to the client.
|
||||
.Pp
|
||||
The CGI scripts are executed in the directory they reside and inherit
|
||||
the environment from
|
||||
.Nm
|
||||
with these additional variables set:
|
||||
.Bl -tag -width 24m
|
||||
.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 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.
|
||||
Can be unset.
|
||||
.It Ev PATH_TRANSLATED
|
||||
Present if and only if
|
||||
.Ev PATH_INFO
|
||||
is set.
|
||||
It represent the translation of the
|
||||
.Ev PATH_INFO .
|
||||
.Nm
|
||||
builds this by appending the
|
||||
.Ev PATH_INFO
|
||||
to the virtual host directory root.
|
||||
.It Ev QUERY_STRING
|
||||
The decoded query 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 Ev SCRIPT_NAME
|
||||
The part of the
|
||||
.Ev GEMINI_URL_PATH
|
||||
that identifies the current CGI script.
|
||||
.It Ev SERVER_NAME
|
||||
The name of the server
|
||||
.It Ev SERVER_PORT
|
||||
The port the server is listening on.
|
||||
.It Ev SERVER_PROTOCOL
|
||||
.Dq GEMINI
|
||||
.It Ev SERVER_SOFTWARE
|
||||
The name and version of the server, i.e.
|
||||
.Dq gmid/1.8.3
|
||||
.It Ev AUTH_TYPE
|
||||
The string "Certificate" if the client used a certificate, otherwise
|
||||
unset.
|
||||
.It Ev REMOTE_USER
|
||||
The subject of the client certificate if provided, otherwise unset.
|
||||
.It Ev TLS_CLIENT_ISSUER
|
||||
The is the issuer of the client certificate if provided, otherwise
|
||||
unset.
|
||||
.It Ev TLS_CLIENT_HASH
|
||||
The hash of the client certificate if provided, otherwise unset.
|
||||
The format is
|
||||
.Dq ALGO:HASH .
|
||||
.It Ev TLS_VERSION
|
||||
The TLS version negotiated with the peer.
|
||||
.It Ev TLS_CIPHER
|
||||
The cipher suite negotiated with the peer.
|
||||
.It Ev TLS_CIPHER_STRENGTH
|
||||
The strength in bits for the symmetric cipher that is being used with
|
||||
the peer.
|
||||
.It Ev TLS_CLIENT_NOT_AFTER
|
||||
The time corresponding to the end of the validity period of the peer
|
||||
certificate in the ISO 8601 format
|
||||
.Pq e.g. Dq 2021-02-07T20:17:41Z .
|
||||
.It Ev TLS_CLIENT_NOT_BEFORE
|
||||
The time corresponding to the start of the validity period of the peer
|
||||
certificate in the ISO 8601 format.
|
||||
By default
|
||||
.Nm gmid
|
||||
uses the following mapping if no
|
||||
.Ic types
|
||||
block is defined:
|
||||
.Pp
|
||||
.Bl -tag -offset indent -width 15m -compact
|
||||
.It application/pdf
|
||||
pdf
|
||||
.It image/gif
|
||||
gif
|
||||
.It image/jpeg
|
||||
jpg jpeg
|
||||
.It image/png
|
||||
png
|
||||
.It image/svg+xml
|
||||
svg
|
||||
.It text/gemini
|
||||
gemini gmi
|
||||
.It text/markdown
|
||||
markdown md
|
||||
.It text/x-patch
|
||||
diff patch
|
||||
.It text/xml
|
||||
xml
|
||||
.El
|
||||
.Sh FastCGI
|
||||
.Nm
|
||||
optionally supports FastCGI.
|
||||
A
|
||||
.Ic fastcgi
|
||||
rule must be present in a server or location block.
|
||||
Then, all requests matching that server or location will be handled
|
||||
via the specified FastCGI backend.
|
||||
.Pp
|
||||
By default the following variables
|
||||
.Pq parameters
|
||||
are sent, and carry the same semantics as with CGI.
|
||||
More parameters can be added with the
|
||||
.Ic param
|
||||
option.
|
||||
.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
|
||||
.Sh MEDIA TYPES
|
||||
To auto-detect the MIME type of the response
|
||||
.Nm
|
||||
looks at the file extension and consults an internal table.
|
||||
If no MIME is found, the value of
|
||||
.Ic default type
|
||||
matching the file
|
||||
.Ic location
|
||||
will be used, or
|
||||
.Dq application/octet-stream .
|
||||
.Pp
|
||||
By default the following mappings are loaded, but they can be
|
||||
overridden or extended using the
|
||||
.Ic map
|
||||
option or the
|
||||
.Ic type Brq ...
|
||||
block.
|
||||
.Pp
|
||||
.Bl -tag -offset indent -width 14m -compact
|
||||
.It diff
|
||||
text/x-patch
|
||||
.It gemini, gmi
|
||||
text/gemini
|
||||
.It gif
|
||||
image/gif
|
||||
.It jpeg
|
||||
image/jpeg
|
||||
.It jpg
|
||||
image/jpeg
|
||||
.It markdown, md
|
||||
text/markdown
|
||||
.It patch
|
||||
text/x-patch
|
||||
.It pdf
|
||||
application/pdf
|
||||
.It png
|
||||
image/png
|
||||
.It svg
|
||||
image/svg+xml
|
||||
.It txt
|
||||
text/plain
|
||||
.It xml
|
||||
text/xml
|
||||
.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
|
||||
As an exception,
|
||||
.Nm gmid
|
||||
uses the MIME type
|
||||
.Ar text/gemini
|
||||
for file extensions
|
||||
.Ar gemini
|
||||
or
|
||||
.Ar gmi
|
||||
if no mapping was found.
|
||||
.Sh EXAMPLES
|
||||
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
|
||||
An X.509 certificate must be provided to run
|
||||
.Nm
|
||||
using a configuration file.
|
||||
First, the RSA certificate is created using a wildcard common name:
|
||||
.Bd -literal -offset indent
|
||||
# openssl genrsa \-out /etc/ssl/private/example.com.key 4096
|
||||
# openssl req \-new \-x509 \e
|
||||
\-key /etc/ssl/private/example.com.key \e
|
||||
\-out /etc/ssl/example.com.crt \e
|
||||
\-days 36500 \-nodes \e
|
||||
\-subj "/CN=example.com"
|
||||
# chmod 600 /etc/ssl/example.com.crt
|
||||
# chmod 600 /etc/ssl/private/example.com.key
|
||||
.Ed
|
||||
.Pp
|
||||
In the example above, a certificate is valid for one hundred years from
|
||||
the date it was created, which is normal for TOFU.
|
||||
.Pp
|
||||
The following is an example of a possible configuration for a site
|
||||
that enables only TLSv1.3, adds a mime type for the file extension
|
||||
.Qq rtf
|
||||
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"
|
||||
|
||||
map "application/rtf" to-ext "rtf"
|
||||
types {
|
||||
include "/usr/share/misc/mime.types"
|
||||
}
|
||||
|
||||
server "example.com" {
|
||||
cert "/etc/ssl/example.com.crt"
|
||||
listen on * port 1965
|
||||
cert "/etc/ssl/example.com.pem"
|
||||
key "/etc/ssl/private/example.com.key"
|
||||
root "/var/gemini/example.com"
|
||||
}
|
||||
|
||||
server "it.example.com" {
|
||||
cert "/etc/ssl/example.com.crt"
|
||||
key "/etc/ssl/private/example.com.key"
|
||||
root "/var/gemini/it.example.com"
|
||||
|
||||
# enable cgi scripts inside "cgi-bin"
|
||||
cgi "/cgi-bin/*"
|
||||
server "example.it" {
|
||||
listen on * port 1965
|
||||
cert "/etc/ssl/example.it.pem"
|
||||
key "/etc/ssl/private/example.it.key"
|
||||
root "/var/gemini/example.it"
|
||||
|
||||
# set the language for text/gemini files
|
||||
lang "it"
|
||||
|
@ -811,9 +651,14 @@ chroot "/var/gemini"
|
|||
user "_gmid"
|
||||
|
||||
server "example.com" {
|
||||
cert "/path/to/cert.pem" # absolute path
|
||||
key "/path/to/key.pem" # also absolute
|
||||
root "/example.com" # relative to the chroot
|
||||
listen on * port 1965
|
||||
|
||||
# absolute paths:
|
||||
cert "/etc/ssl/example.com.pem"
|
||||
key "/etc/ssl/private/example.com.key"
|
||||
|
||||
# relative to the chroot:
|
||||
root "/example.com"
|
||||
|
||||
location "/static/*" {
|
||||
# load the following rules only for
|
||||
|
@ -824,30 +669,30 @@ server "example.com" {
|
|||
}
|
||||
}
|
||||
.Ed
|
||||
.Sh ACKNOWLEDGEMENTS
|
||||
.Nm
|
||||
uses the
|
||||
.Dq Flexible and Economical
|
||||
UTF-8 decoder written by
|
||||
.An Bjoern Hoehrmann .
|
||||
.Pp
|
||||
This shows how to set up a reverse proxy: all request for
|
||||
.Sq example.com
|
||||
will be forwarded to 10.0.0.6 transparently.
|
||||
Proxying establish a new TLS connection, so any client-certificates used
|
||||
to connect to
|
||||
.Xr gmid 8
|
||||
cannot be provided to the proxied server as well.
|
||||
.Bd -literal -offset indent
|
||||
server "example.com" {
|
||||
listen on * port 1965
|
||||
cert "/etc/ssl/example.com.pem"
|
||||
key "/etc/ssl/private/example.com.key"
|
||||
proxy {
|
||||
relay-to 10.0.0.6 port 1965
|
||||
}
|
||||
}
|
||||
.Ed
|
||||
.Sh SEE ALSO
|
||||
.Xr gmid 8 ,
|
||||
.Xr slowcgi 8
|
||||
.Sh AUTHORS
|
||||
.An -nosplit
|
||||
The
|
||||
.Nm
|
||||
.Nm gmid
|
||||
program was written by
|
||||
.An Omar Polo Aq Mt op@omarpolo.com .
|
||||
.Sh CAVEATS
|
||||
.Bl -bullet
|
||||
.It
|
||||
All the root directories are opened during the daemon startup; if a
|
||||
root directory is deleted and then re-created,
|
||||
.Nm
|
||||
won't be able to serve files inside that directory until a restart.
|
||||
This restriction only applies to the root directories and not their
|
||||
content.
|
||||
.It
|
||||
a %2F sequence is indistinguishable from a literal slash: this is not
|
||||
RFC3986-compliant.
|
||||
.It
|
||||
a %00 sequence is treated as invalid character and thus rejected.
|
||||
.El
|
383
gmid.h
383
gmid.h
|
@ -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,44 +219,53 @@ struct vhost {
|
|||
*/
|
||||
struct lochead locations;
|
||||
|
||||
struct envhead env;
|
||||
struct envhead params;
|
||||
struct aliashead aliases;
|
||||
struct proxyhead proxies;
|
||||
};
|
||||
|
||||
struct etm { /* extension to mime */
|
||||
const char *mime;
|
||||
const char *ext;
|
||||
char mime[MEDIATYPE_TYPEMAX];
|
||||
char ext[MEDIATYPE_NAMEMAX];
|
||||
};
|
||||
|
||||
struct mime {
|
||||
struct etm *t;
|
||||
size_t len;
|
||||
size_t cap;
|
||||
size_t len;
|
||||
size_t cap;
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
|
@ -214,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;
|
||||
|
||||
|
@ -255,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 */
|
||||
|
||||
|
@ -266,96 +325,75 @@ 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*);
|
||||
void add_mime(struct mime*, const char*, const char*);
|
||||
void load_default_mime(struct mime*);
|
||||
const char *mime(struct vhost*, const char*);
|
||||
int add_mime(struct mime*, const char*, const char*);
|
||||
int load_default_mime(struct mime*);
|
||||
void sort_mime(struct mime *);
|
||||
const char *mime(struct conf *, struct vhost*, const char*);
|
||||
void free_mime(struct mime *);
|
||||
|
||||
/* server.c */
|
||||
extern int shutting_down;
|
||||
|
@ -364,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*);
|
||||
|
@ -372,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);
|
||||
|
@ -386,39 +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);
|
||||
char *pct_decode_str(char *);
|
||||
/* logger.c */
|
||||
void logger(struct privsep *, struct privsep_proc *);
|
||||
|
||||
/* proxy.c */
|
||||
int proxy_init(struct client *);
|
||||
|
@ -427,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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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));
|
||||
}
|
|
@ -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));
|
||||
}
|
|
@ -24,7 +24,8 @@ int
|
|||
main(void)
|
||||
{
|
||||
struct imsgbuf buf;
|
||||
struct imsg imsg;
|
||||
|
||||
imsg_init(&buf, -1);
|
||||
return 0;
|
||||
return imsg_get_fd(&imsg);
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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));
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
return setresgid(-1, -1, -1) == -1;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
return setresuid(-1, -1, -1) == -1;
|
||||
}
|
|
@ -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));
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
56
iri.c
56
iri.c
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2020 Omar Polo <op@omarpolo.com>
|
||||
* Copyright (c) 2020, 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
|
||||
|
@ -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";
|
||||
|
@ -352,6 +353,8 @@ parse_path(struct parser *p)
|
|||
|
||||
while (unreserved(*p->iri)
|
||||
|| sub_delimiters(*p->iri)
|
||||
|| *p->iri == '@'
|
||||
|| *p->iri == ':'
|
||||
|| *p->iri == '/'
|
||||
|| parse_pct_encoded(p)
|
||||
|| valid_multibyte_utf8(p))
|
||||
|
@ -442,13 +445,52 @@ serialize_iri(struct iri *i, char *buf, size_t len)
|
|||
return l < len;
|
||||
}
|
||||
|
||||
int
|
||||
encode_path(char *buf, size_t len, const char *path)
|
||||
{
|
||||
char *p = buf;
|
||||
int a, b;
|
||||
|
||||
memset(buf, 0, len);
|
||||
while (*path != '\0') {
|
||||
if (len == 1) /* NUL */
|
||||
return -1;
|
||||
|
||||
if (unreserved(*path) ||
|
||||
sub_delimiters(*path) ||
|
||||
*path == '@' ||
|
||||
*path == ':' ||
|
||||
*path == '/') {
|
||||
*p++ = *path++;
|
||||
len--;
|
||||
} else if (len < 4)
|
||||
return -1;
|
||||
else {
|
||||
a = (*path & 0xF0) >> 4;
|
||||
b = (*path & 0x0F);
|
||||
|
||||
p[0] = '%';
|
||||
p[1] = a <= 9 ? ('0' + a) : ('7' + a);
|
||||
p[2] = b <= 9 ? ('0' + b) : ('7' + b);
|
||||
|
||||
path++;
|
||||
p += 3;
|
||||
len -= 3;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 *);
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
untrusted comment: signify public key
|
||||
RWSK+qgSqgu20CEZZQTAExCxaGaOwGO7AWqru6BKLqQhQDy8Iz1tjXNE
|
|
@ -0,0 +1,2 @@
|
|||
untrusted comment: signify public key
|
||||
RWTy3UJQzpxBUAymBwb2EGLLm0b3H/1n8hzhaC9HYFYzNuTavGt9QSwC
|
|
@ -0,0 +1,2 @@
|
|||
untrusted comment: signify public key
|
||||
RWQ+Bm0F0FtPLtTnpRe09x/Z6Fiodk4toTZe2TJ4yCqDZ6l0c5wiU9te
|
|
@ -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 */
|
461
log.c
461
log.c
|
@ -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,335 +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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
92
mime.c
92
mime.c
|
@ -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
|
||||
|
@ -20,6 +20,8 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
void
|
||||
init_mime(struct mime *mime)
|
||||
{
|
||||
|
@ -28,34 +30,43 @@ 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 */
|
||||
void
|
||||
int
|
||||
add_mime(struct mime *mime, const char *mt, const char *ext)
|
||||
{
|
||||
size_t oldcap;
|
||||
struct etm *t;
|
||||
size_t newcap;
|
||||
|
||||
if (mime->len == mime->cap) {
|
||||
oldcap = mime->cap;
|
||||
mime->cap *= 1.5;
|
||||
mime->t = recallocarray(mime->t, oldcap, mime->cap,
|
||||
newcap = mime->cap * 1.5;
|
||||
t = recallocarray(mime->t, mime->cap, newcap,
|
||||
sizeof(struct etm));
|
||||
if (mime->t == NULL)
|
||||
err(1, "recallocarray");
|
||||
if (t == NULL)
|
||||
return -1;
|
||||
mime->t = t;
|
||||
mime->cap = newcap;
|
||||
}
|
||||
|
||||
mime->t[mime->len].mime = mt;
|
||||
mime->t[mime->len].ext = ext;
|
||||
t = &mime->t[mime->len];
|
||||
if (strlcpy(t->mime, mt, sizeof(t->mime)) >= sizeof(t->mime))
|
||||
return -1;
|
||||
if (strlcpy(t->ext, ext, sizeof(t->ext)) >= sizeof(t->ext))
|
||||
return -1;
|
||||
mime->len++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* load a default set of common mime-extension associations */
|
||||
void
|
||||
int
|
||||
load_default_mime(struct mime *mime)
|
||||
{
|
||||
const struct etm *i, m[] = {
|
||||
const struct mapping {
|
||||
const char *mime;
|
||||
const char *ext;
|
||||
} m[] = {
|
||||
{"application/pdf", "pdf"},
|
||||
{"image/gif", "gif"},
|
||||
{"image/jpeg", "jpg"},
|
||||
|
@ -71,10 +82,18 @@ load_default_mime(struct mime *mime)
|
|||
{"text/x-patch", "patch"},
|
||||
{"text/xml", "xml"},
|
||||
{NULL, NULL}
|
||||
};
|
||||
}, *i;
|
||||
|
||||
for (i = m; i->mime != NULL; ++i)
|
||||
add_mime(mime, i->mime, i->ext);
|
||||
/* 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;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *
|
||||
|
@ -93,8 +112,31 @@ path_ext(const char *path)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
mime_comp(const void *a, const void *b)
|
||||
{
|
||||
const struct etm *x = a, *y = b;
|
||||
|
||||
return strcmp(x->ext, y->ext);
|
||||
}
|
||||
|
||||
void
|
||||
sort_mime(struct mime *m)
|
||||
{
|
||||
qsort(m->t, m->len, sizeof(*m->t), mime_comp);
|
||||
}
|
||||
|
||||
static int
|
||||
mime_find(const void *a, const void *b)
|
||||
{
|
||||
const char *ext = a;
|
||||
const struct etm *x = b;
|
||||
|
||||
return strcmp(ext, x->ext);
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -104,9 +146,17 @@ mime(struct vhost *host, const char *path)
|
|||
if ((ext = path_ext(path)) == NULL)
|
||||
return def;
|
||||
|
||||
for (t = conf.mime.t; t->mime != NULL; ++t)
|
||||
if (!strcmp(ext, t->ext))
|
||||
return t->mime;
|
||||
|
||||
t = bsearch(ext, conf->mime.t, conf->mime.len, sizeof(*conf->mime.t),
|
||||
mime_find);
|
||||
if (t != NULL)
|
||||
return t->mime;
|
||||
if (!strcmp(ext, "gmi") || !strcmp(ext, "gemini"))
|
||||
return "text/gemini";
|
||||
return def;
|
||||
}
|
||||
|
||||
void
|
||||
free_mime(struct mime *m)
|
||||
{
|
||||
free(m->t);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue