mirror of https://github.com/omar-polo/gmid.git
Compare commits
1361 Commits
Author | SHA1 | Date |
---|---|---|
Omar Polo | c9ea70a36f | |
Omar Polo | 7c723cf05f | |
Omar Polo | b5dd7091ad | |
Omar Polo | 5b549c2805 | |
Omar Polo | b00f71ba97 | |
Omar Polo | 6ff8de1f8a | |
Omar Polo | 9f675805d0 | |
Omar Polo | a91b0892bf | |
Omar Polo | 610a4666cd | |
Omar Polo | 2f4926259f | |
Omar Polo | cd12ad1132 | |
Omar Polo | b2782022c9 | |
Omar Polo | 1ef0cd0cdb | |
Omar Polo | 42e2af25ae | |
Omar Polo | 89dca7ab54 | |
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 | |
Omar Polo | 1a04137e18 | |
Omar Polo | ea27eaaa83 | |
Omar Polo | 6084a9a5ba | |
Omar Polo | 62a46b03c6 | |
Omar Polo | da613aba4b | |
Omar Polo | 57d2fca4b4 | |
Omar Polo | 3fdc457c8d | |
Omar Polo | 409a2599b3 | |
Omar Polo | 91971201e5 | |
Omar Polo | 894e998423 | |
Omar Polo | c7949fd545 | |
Omar Polo | e5d82d9472 | |
Omar Polo | 9db5e7051e | |
Omar Polo | 5f966fbe41 | |
Omar Polo | a00e67f1bf | |
Omar Polo | efd54f4f8a | |
Nakaya | 3c04ffc096 | |
Nakaya | bff125b842 | |
Omar Polo | 12fcba2f80 | |
Omar Polo | cd3e28ffe4 | |
Omar Polo | 74994ae7e5 | |
Omar Polo | e0bf950dff | |
Omar Polo | fb1212266f | |
Omar Polo | ee219d702e | |
Omar Polo | 88971f9a4e | |
Omar Polo | d98ae929b2 | |
Omar Polo | ed78e81b9c | |
Omar Polo | c273bc0e41 | |
Omar Polo | 4f0e893cd3 | |
Omar Polo | 94c5f99ab0 | |
Omar Polo | 67347fb021 | |
Tobias Berger | d0e0be1e43 | |
Omar Polo | c6ae2561a0 | |
Omar Polo | b4b432e78e | |
Omar Polo | f2dbf375a0 | |
Omar Polo | 98c6f8de41 | |
Anna “CyberTailor” | be88c5d657 | |
Anna “CyberTailor” | 1f1f381068 | |
Anna “CyberTailor” | e20755180f | |
Anna “CyberTailor” | c1a72dab7a | |
Anna “CyberTailor” | 1ca7a0f3bf | |
Omar Polo | f525aa55b8 | |
Omar Polo | a14b9f779f | |
Omar Polo | de721c431c | |
Omar Polo | 135307cfb0 | |
Omar Polo | d78dbe8db5 | |
Omar Polo | 5266f50060 | |
Omar Polo | 7ac42a178b | |
Omar Polo | d23cd12c68 | |
Omar Polo | c586dfead7 | |
Omar Polo | 1cdea97b6c | |
Omar Polo | 1b626eae83 | |
Omar Polo | 151af94eba | |
Omar Polo | 12a5e730e9 | |
Omar Polo | 4e8ca48c85 | |
Omar Polo | 21dc379479 | |
Omar Polo | ac42bb6c7f | |
Omar Polo | 964686ce0b | |
Omar Polo | e89f473904 | |
Omar Polo | e0f6dc646d | |
Omar Polo | d28bd963c2 | |
Omar Polo | 89efa81bcc | |
Omar Polo | b9b77f5344 | |
Omar Polo | b3602923d0 | |
Omar Polo | febfcde82b | |
Omar Polo | e2534fd11f | |
Omar Polo | 901905e0cf | |
Omar Polo | 876a417023 | |
Omar Polo | c57dc42b31 | |
Omar Polo | 14cee92646 | |
Omar Polo | 7c2374af2c | |
Omar Polo | d474a97922 | |
Omar Polo | ba94a608a8 | |
Omar Polo | 280fd79b8f | |
Omar Polo | 718c0d6c71 | |
Omar Polo | 7c956fefc2 | |
Omar Polo | 613c827165 | |
Omar Polo | 8dfacf0242 | |
Omar Polo | 92a9f41d3d | |
Omar Polo | 7b7def0a8f | |
Omar Polo | dd208aa2cf | |
Omar Polo | 6effb78141 | |
Omar Polo | 5df593f3a6 | |
Omar Polo | 207dc0f9f6 | |
Omar Polo | b7967bc1f6 | |
Omar Polo | e2f167afb3 | |
Omar Polo | 8e7e7cacf2 | |
Omar Polo | e19f21f5a6 | |
Omar Polo | ec5c5ced4a | |
Omar Polo | 53884ec00a | |
Omar Polo | 593e412b49 | |
Omar Polo | 294a57275a | |
Omar Polo | ee68c36198 | |
Omar Polo | 3c4b712bb2 | |
Omar Polo | c7c8ef448b | |
Omar Polo | 5128c0b0e3 | |
Omar Polo | 7bdcc91ec7 | |
Omar Polo | bd5f79542c | |
Omar Polo | a9cb709d44 | |
Omar Polo | c064f3de1d | |
Omar Polo | e6ab6b25fc | |
Omar Polo | d49093c105 | |
Omar Polo | da2185f37f | |
Omar Polo | cc1c8f548c | |
Omar Polo | c3eb759a79 | |
Omar Polo | 6a6b4a2a98 | |
Omar Polo | 4b5b1e82a9 | |
Omar Polo | c143dcfa85 | |
Omar Polo | 72b033ef18 | |
Omar Polo | 054387bb26 | |
Omar Polo | a62c63f261 | |
Omar Polo | 7a4ae106ec | |
Omar Polo | ce09569f7b | |
Omar Polo | 5c7abf0151 | |
Omar Polo | fe903d30e7 | |
Max | 63bf54b646 | |
Omar Polo | 2d6b9b53ac | |
Omar Polo | 79c3a02152 | |
Omar Polo | 52c92ef680 | |
Omar Polo | 34c4ca6ebc | |
Omar Polo | ae0d3cdbbe | |
Omar Polo | e5285d5422 | |
Omar Polo | a721c233ee | |
Omar Polo | 5741561d57 | |
Omar Polo | ea47a245aa | |
Omar Polo | ebf3373d66 | |
Omar Polo | aa76c675eb | |
Omar Polo | eb77afa8d3 | |
Omar Polo | 193380eaa4 | |
Omar Polo | dcfdb969a2 | |
Omar Polo | d65fa58c1b | |
Omar Polo | eaa02ac8c7 | |
Omar Polo | a81dd6cda6 | |
Omar Polo | 93edd35305 | |
Omar Polo | 4842c72d9f | |
Omar Polo | f8bcc13822 | |
Omar Polo | e84ee6a729 | |
Omar Polo | 818d6aeeb6 | |
Omar Polo | 7fa6717647 | |
Stephen Gregoratto | ff05125eb8 | |
Omar Polo | 387b976b99 | |
Omar Polo | 8044493865 | |
Omar Polo | 33c4c3a5ba | |
Omar Polo | 2ae10bb4ee | |
Omar Polo | c62a411f4f | |
Omar Polo | 8af884dff4 | |
Omar Polo | 7c6bf71dc8 | |
Omar Polo | fc4b58d450 | |
Omar Polo | c0f81ed39e | |
Omar Polo | 536026c565 | |
Omar Polo | f0a01fc742 | |
Omar Polo | 5eb3fc905f | |
Omar Polo | c6bcc919c6 | |
Omar Polo | c7b79c9eed | |
Omar Polo | 1298e4307f | |
Omar Polo | 679d5d1a61 | |
Omar Polo | 35340c9f1e | |
Omar Polo | 540d05dedc | |
Omar Polo | 0111ad5d26 | |
Omar Polo | a83ba84448 | |
Omar Polo | d7e2e22c58 | |
Omar Polo | 9bb2f62e24 | |
Omar Polo | 807869c14e | |
Omar Polo | 492a274fd7 | |
Omar Polo | 207b3e80d8 | |
Omar Polo | 4cd2520965 | |
Omar Polo | 3096da4ef4 | |
Omar Polo | e4daebe44a | |
Omar Polo | 807a80cb9e | |
Omar Polo | b4c6cd9768 | |
Omar Polo | 9212cf1ba9 | |
Omar Polo | eb82dcfbf4 | |
Omar Polo | 12866f1911 | |
Omar Polo | ae6870fa3b | |
Omar Polo | 568419b2c1 | |
Omar Polo | 6e0f14d51e | |
Omar Polo | 2072343d6b | |
Omar Polo | 260becda9c | |
Omar Polo | d046e4d6b5 | |
Omar Polo | 4a2a525d7c | |
Omar Polo | c1272f63e4 | |
Omar Polo | 99c91b4a51 | |
Omar Polo | 176179b2a9 | |
Omar Polo | c28994868e | |
Omar Polo | a49800c86a | |
Omar Polo | acafce5b7d | |
Omar Polo | efb6210d77 | |
Omar Polo | d0071d8321 | |
Omar Polo | 827cc37cff | |
Omar Polo | fa0299a26d | |
Omar Polo | efe7d18029 | |
Omar Polo | 403c422041 | |
Omar Polo | b618111a68 | |
Omar Polo | 5f37f9c20d | |
Omar Polo | c016b65ca9 | |
Omar Polo | f7ee799023 | |
Omar Polo | 0c66b6ad55 | |
Omar Polo | 6f27d2595a | |
Omar Polo | 2a44a2ab6e | |
Omar Polo | 741b69be96 | |
Omar Polo | 83fe545a2b | |
Omar Polo | cb28978f0a | |
Omar Polo | 775ef04f82 | |
Omar Polo | 0d9a5b7a18 | |
Omar Polo | 3571854e94 | |
Omar Polo | 353e3c8ebe | |
Omar Polo | e15fc95736 | |
Omar Polo | 81e0f00078 | |
Omar Polo | df0c2926cc | |
Omar Polo | a91ad7f2ff | |
Omar Polo | 79288c8b60 | |
Martin | 2b38d395cd | |
Omar Polo | 36d2d7b4b3 | |
Omar Polo | b0be065390 | |
Omar Polo | 0ea22af280 | |
Omar Polo | 67c49bc5c7 | |
Omar Polo | 3499ce5a9a | |
Omar Polo | d85aa60208 | |
Omar Polo | 4c31de2915 | |
Omar Polo | fba809b5c7 | |
Omar Polo | ca3d8bfde6 | |
Omar Polo | 39c5bfd64b | |
Omar Polo | e30e5385f1 | |
Omar Polo | b03b7d5bae | |
Omar Polo | db623d7551 | |
Omar Polo | f2478b332f | |
Omar Polo | 2e31927606 | |
Omar Polo | 83272dfee9 | |
Omar Polo | ef352ec3b8 | |
Omar Polo | 03747a6c51 | |
Omar Polo | 2bb1776bac | |
Anna “CyberTailor” | 301e039d24 | |
Anna “CyberTailor” | e308526cf6 | |
Anna “CyberTailor” | e58a447a28 | |
Omar Polo | 2887c631ee | |
Omar Polo | efb48052dc | |
Omar Polo | a8e1e8d738 | |
Omar Polo | 21f7d24699 | |
Omar Polo | af5f9b271e | |
Omar Polo | 3e0164f4c3 | |
Omar Polo | 735336b21e | |
Omar Polo | abc8801d60 | |
Omar Polo | c74c70304d | |
Omar Polo | 9a818eebc1 | |
Omar Polo | 41395640e0 | |
Omar Polo | 2b5e2a5f04 | |
Omar Polo | 9ffdade26c | |
Omar Polo | a7da55710e | |
Anna “CyberTailor” | f3966209e5 | |
Omar Polo | a556718a24 | |
la-ninpre | caf4e5fb22 | |
Omar Polo | dbbfd0fb9f | |
Omar Polo | 3b0eff06ff | |
Omar Polo | 02ccd9d940 | |
Omar Polo | e8b8003098 | |
Omar Polo | e2d3a8f222 | |
Anna “CyberTailor” | 1a115616cb | |
Omar Polo | 8068d2ff33 | |
Omar Polo | 094bdacd40 | |
Omar Polo | ad15003be8 | |
Anna “CyberTailor” | 2d06ae62ae | |
Anna “CyberTailor” | 8518014cb9 | |
Omar Polo | b79b2d811b | |
Omar Polo | f361f79979 | |
Omar Polo | a82d4dfa25 | |
Omar Polo | 9f075bd689 | |
Omar Polo | 391825e360 | |
Omar Polo | e3b2a0f8de | |
Omar Polo | 1bd706dc41 | |
Omar Polo | 528f37cd2d | |
Omar Polo | 67f494057a | |
Omar Polo | c39be742cf | |
Omar Polo | a5d822e542 | |
Omar Polo | be52e954c1 | |
Omar Polo | 3d132b2833 | |
Omar Polo | 2b520ad595 | |
Omar Polo | efacb859a7 | |
Omar Polo | 762b9b991f | |
Omar Polo | ff954a3e76 | |
Omar Polo | d19951cf03 | |
Omar Polo | d93c819182 | |
Omar Polo | bffa7daab8 | |
Anna “CyberTailor” | 8c9b8f487e | |
Anna “CyberTailor” | 9c84a04f9c | |
Anna “CyberTailor” | 9797799ad4 | |
Omar Polo | 2e2e189b01 | |
Omar Polo | e7c6502bf3 | |
Omar Polo | a8a1f43921 | |
Omar Polo | 68ef117092 | |
Omar Polo | 8a50fc0330 | |
Omar Polo | d336aeafd4 | |
Omar Polo | 489e1655a0 | |
Omar Polo | 1a572d4221 | |
Omar Polo | af15474448 | |
Omar Polo | 321341f6c0 | |
Omar Polo | 6edcfca97f | |
Omar Polo | eb877bffaa | |
Omar Polo | ca3f67564c | |
Omar Polo | fb4102a5ff | |
Omar Polo | 3759d3eb56 | |
Omar Polo | 7700ab07d3 | |
Omar Polo | 59c7ee13b4 | |
Omar Polo | 090b8a89fa | |
Omar Polo | ea976e8743 | |
Omar Polo | ef945cf415 | |
Omar Polo | 4aa1dd553a | |
Omar Polo | b24021d4a2 | |
Omar Polo | 7322a054f5 | |
Omar Polo | 8bb8cf2ad4 | |
Omar Polo | 137def5ff4 | |
Omar Polo | 4f9a27603c | |
Omar Polo | 2c16dbd548 | |
Omar Polo | 4ede0fe495 | |
Omar Polo | eaf7f776f1 | |
Omar Polo | bfb076ed7e | |
Omar Polo | 7252049dd7 | |
Omar Polo | 98f5217822 | |
Omar Polo | 8b743ddacb | |
Omar Polo | 6b86655a10 | |
Omar Polo | f98e9045ae | |
Omar Polo | 3b21cca385 | |
Omar Polo | fafc684957 | |
Omar Polo | 8235a81c8f | |
Omar Polo | 0be2a537e6 | |
Omar Polo | ba65dcc8fa | |
Omar Polo | fdb43a4c0e | |
Omar Polo | 0233b0f6b9 | |
Omar Polo | 9327bc045a | |
Omar Polo | 5777923bb0 | |
Omar Polo | 2c43dcceb6 | |
Omar Polo | 1b78bd563a | |
Omar Polo | 80fbf1e934 | |
Omar Polo | 2d383cbd5f | |
Omar Polo | 9e659275b0 | |
Omar Polo | ef129b08ef | |
Omar Polo | 74f0778b9a | |
Omar Polo | 984c46a82e | |
Omar Polo | 89c110fe7b | |
Omar Polo | e952c5052a | |
Omar Polo | 0f2124e291 | |
Omar Polo | 03fcfb79d6 | |
Omar Polo | 24d362cd67 | |
Omar Polo | 89c88caa3c | |
Omar Polo | e18b070da8 | |
Omar Polo | ab1e0169b9 | |
Omar Polo | 3b33eab3ad | |
Omar Polo | a6c809551e | |
Omar Polo | fd1ae83d20 | |
Omar Polo | ada16f8731 | |
Omar Polo | c92b802b6a | |
Omar Polo | f740b61b03 | |
Omar Polo | 6957a8c27f | |
Omar Polo | 0c7a1d3cf3 | |
Omar Polo | 3a1bd9ab49 | |
Omar Polo | f48e3b85a9 | |
Omar Polo | 0d047efcb4 | |
Omar Polo | 8ff40039e8 | |
Omar Polo | ce2c9edbc2 | |
Omar Polo | d1051bfaa0 | |
Omar Polo | 1feaf2a618 | |
Omar Polo | 91b9f2a8f9 | |
Omar Polo | 571d20fbb3 | |
Omar Polo | 0773413c1d | |
Omar Polo | 1eb3631d10 | |
Omar Polo | 58d51f2c11 | |
Omar Polo | 23a5aab4ce | |
Omar Polo | 84821dc0ac | |
Omar Polo | 8ad1c57024 | |
Omar Polo | 50310aff33 | |
Omar Polo | 419e32b05e | |
Omar Polo | c705ecb1ba | |
Omar Polo | 7b2d743260 | |
Omar Polo | 7aa9258633 | |
Omar Polo | 737a6b50c5 | |
Omar Polo | fdea6aa0bc | |
Omar Polo | adbe6a6493 | |
Omar Polo | 17a04409b5 | |
Omar Polo | 8070ffa70f | |
Omar Polo | 7a4f742657 | |
Omar Polo | 08ce6f5273 | |
Omar Polo | d06d6f4bba | |
Omar Polo | 6a5e709beb | |
Omar Polo | cc8c2901ad | |
Omar Polo | 8e8b2e252c | |
Omar Polo | 2ef7f631db | |
Omar Polo | 48b69cb2dc | |
Omar Polo | d89a9060f8 | |
Omar Polo | 42447f673f | |
Omar Polo | 9cc630aa63 | |
Omar Polo | e6ca8eb156 | |
Omar Polo | 419a423520 | |
Omar Polo | c79b63f580 | |
Omar Polo | 1b333122a9 | |
Omar Polo | 578f13e4b2 | |
Omar Polo | b5fdc32e75 | |
Omar Polo | 5aba63956a | |
Omar Polo | e76f2c74b8 | |
Omar Polo | 11c986679a | |
Omar Polo | bb4be662f1 | |
Omar Polo | 072e3557c4 | |
Omar Polo | 0bfdcb1799 | |
Omar Polo | 4d820b6cd2 | |
Omar Polo | 5d1474a561 | |
Omar Polo | 3841a36930 | |
Omar Polo | 74c0c7e4ce | |
Omar Polo | 3f47867e1d | |
Omar Polo | b7a9906151 | |
Omar Polo | dafaf46734 | |
Omar Polo | 1de922891b | |
Omar Polo | 548c2849fa | |
Omar Polo | b33425827e | |
Omar Polo | 89541eeec0 | |
Omar Polo | b211d0f7d9 | |
Omar Polo | 9d092b607a | |
Omar Polo | f2522b4313 | |
Omar Polo | b8e64ccd44 | |
Omar Polo | e0ebdf2d94 | |
Omar Polo | 7e1df73d7d | |
Omar Polo | 33b455ada3 | |
Omar Polo | 2ddc927113 | |
Omar Polo | 8b462a5d17 | |
Omar Polo | c836cdfadb | |
omar-polo | 071dce449d | |
Korneliusz Łapacz | c928dcc7ba | |
Omar Polo | c8249bada3 | |
Omar Polo | b890467e60 | |
Omar Polo | 94be0bf03b | |
Omar Polo | 8e09ee1d50 | |
Omar Polo | b9c9123b8e | |
Omar Polo | e3d81f49cc | |
Omar Polo | d632468df5 | |
Omar Polo | 7e3aaf3752 | |
Omar Polo | 052c9d2403 | |
Omar Polo | 1d3eb470b0 | |
Omar Polo | 62e001b067 | |
Omar Polo | ad5301d1a0 | |
Omar Polo | bc99d868bc | |
Omar Polo | 1fbac5ba7c | |
Omar Polo | dbe262a45d | |
Omar Polo | de62c38d81 | |
Omar Polo | 2c3e53dac6 | |
Omar Polo | fda7b99fc7 | |
Omar Polo | 290b5baa97 | |
Omar Polo | 9899a837af | |
Omar Polo | 17458f11e1 | |
Omar Polo | 376a540764 | |
Omar Polo | aa627c91fc | |
Omar Polo | 4f8128452c | |
Omar Polo | a3c5f7451a | |
Omar Polo | 6b78d24c31 | |
Omar Polo | 3c56e99ab0 | |
Omar Polo | 4604dc9671 | |
Omar Polo | 793835cb26 | |
Omar Polo | fd9a486925 | |
Omar Polo | 6b191ed52a | |
Omar Polo | d278a0c3c5 | |
Omar Polo | c39b26d308 | |
Omar Polo | eecad7a3ca | |
Omar Polo | dfa6d77b70 | |
Omar Polo | 5b6dc93c8a | |
Omar Polo | 7689871446 | |
Omar Polo | 52418c8d82 | |
Omar Polo | 3cb3dd4d42 | |
Omar Polo | 9356f61a63 | |
Omar Polo | 5e3285d52e | |
Omar Polo | 98ee8406aa | |
Omar Polo | a4188b7865 | |
Omar Polo | 27b2fa9ae5 | |
Omar Polo | a6e689d745 | |
Omar Polo | e1b4612359 | |
Omar Polo | a72cc6be31 | |
Omar Polo | 4d7043e33c | |
Omar Polo | 1606927ea1 | |
Omar Polo | 8e56d6adc4 | |
Omar Polo | 2a911637be | |
Omar Polo | 0b040b9909 | |
Omar Polo | 4ad0601d18 | |
Omar Polo | 6827d2781e | |
Omar Polo | 4c857c0afc | |
Omar Polo | aa37287565 | |
Omar Polo | 49b73ba1ab | |
Omar Polo | 2898780aea | |
Omar Polo | 4283d65fc1 | |
Omar Polo | f6b9a079e3 | |
Omar Polo | 2d34f732f1 | |
Omar Polo | 002a84a123 | |
Omar Polo | d3f073c9ed | |
Omar Polo | eb59f87e67 | |
Omar Polo | 02be96c6dd | |
Omar Polo | 2ff026b09b | |
Omar Polo | 31b3662c54 | |
Omar Polo | 57ec3e776e | |
Omar Polo | 747b35d0da | |
Omar Polo | b9231167fb | |
Omar Polo | c214d1ab67 | |
Omar Polo | df58efff26 | |
Omar Polo | d090dc8491 | |
Omar Polo | 319b7fa909 | |
Omar Polo | 1516fd4dad | |
Omar Polo | 337c74209b | |
Omar Polo | 0b8133c264 | |
Omar Polo | abc007d2b3 | |
Omar Polo | b63e30ff44 | |
Omar Polo | 9f006a2127 | |
Omar Polo | a13739138b | |
Omar Polo | 7e9b7812b3 | |
Omar Polo | 3077ce5bee | |
Omar Polo | a64959c99a | |
Omar Polo | 3abf91b0b4 | |
Omar Polo | a709ddf5eb | |
Omar Polo | cfb8a77fd4 | |
Omar Polo | 2564eddee4 | |
Omar Polo | 809c4bd0f8 | |
Omar Polo | eef0492e35 | |
Omar Polo | 60dbafbf2f | |
Omar Polo | 1487e11eda | |
Omar Polo | 2b92a4477a | |
Omar Polo | 3704b36bda | |
Omar Polo | 6c6f242a54 | |
Omar Polo | e3ddf39095 | |
Omar Polo | afc025ff60 | |
Omar Polo | cd76162494 | |
Omar Polo | 6abda252e9 | |
Omar Polo | daac4a9452 | |
Omar Polo | 4125c94fda | |
Omar Polo | 52053e1a61 | |
Omar Polo | 19e7bd00a3 | |
Omar Polo | 8404ec301f | |
Omar Polo | 709f4c9447 | |
Omar Polo | 102d2e9cce | |
Omar Polo | 9543e3fbaf | |
Omar Polo | 38d4db740d | |
Omar Polo | fe90d24fd0 | |
Omar Polo | 86edc4f400 | |
Omar Polo | 3007f56596 | |
Omar Polo | ca21e10043 | |
Omar Polo | 1e3ef7ab4f | |
Omar Polo | e824d03efa | |
Omar Polo | 4e2e2ab1d3 | |
Omar Polo | d672b8fba1 | |
Omar Polo | 9b8f5ed2c0 | |
Omar Polo | 9edb828251 | |
Omar Polo | 4ee08bd148 | |
Omar Polo | 346f28eeaa | |
Omar Polo | fe40638928 | |
Omar Polo | 87f2b68b58 | |
Omar Polo | 5b4a641893 | |
Omar Polo | b1d52c3105 | |
Omar Polo | 5f715ce43f | |
Omar Polo | 3c680bddab | |
Omar Polo | 35744950aa | |
Omar Polo | b06f80cdf4 | |
Omar Polo | 92da82858b | |
Omar Polo | 112802ea31 | |
Omar Polo | 1e7591a922 | |
Omar Polo | ad58806e30 | |
Omar Polo | d5f4d615cf | |
Omar Polo | 3e5418099f | |
Omar Polo | ee655e64d7 | |
Omar Polo | 38f8a798b1 | |
Omar Polo | 67528c1f82 | |
Omar Polo | 2fafa2d23e | |
Omar Polo | e17642a7bb | |
Omar Polo | bcf5d929e6 | |
Omar Polo | 6ff23c6739 | |
Omar Polo | 7808978667 | |
Omar Polo | 57d0d0adba | |
Omar Polo | ee29b5a63b | |
Omar Polo | 8503a1431d | |
Omar Polo | 91d7870bb7 | |
Omar Polo | 1b246dbd3c | |
Omar Polo | 39a7b61145 | |
Omar Polo | e99923477a | |
Omar Polo | b59f3cdd27 | |
Omar Polo | 6016a593a3 | |
Omar Polo | 601bc1cc37 | |
Omar Polo | 714685c10c | |
Omar Polo | e7c7f19c4e | |
Omar Polo | cef60084e6 | |
Omar Polo | dceca3b69d | |
Omar Polo | a8d4a89770 | |
Omar Polo | 4a3ab60928 | |
Omar Polo | 117ac52cdd | |
Omar Polo | a2fd801327 | |
Omar Polo | 22a742e4cb | |
Omar Polo | 32693ee6db | |
Omar Polo | 415ac7a229 | |
Omar Polo | 75fbb1789e | |
Omar Polo | 90cb9eea8a | |
Omar Polo | 8ef09de3d0 | |
Omar Polo | cbeee4cad1 | |
Omar Polo | 9a672b3712 | |
Omar Polo | 35cf19e3f3 | |
Omar Polo | 473e4531d5 | |
Omar Polo | d2b941f34b | |
Omar Polo | ef0f2ae88c | |
Omar Polo | 8904fa0e64 | |
Omar Polo | 08a2fee055 | |
Omar Polo | 747f13af33 | |
Omar Polo | 13ed2fb63f | |
Omar Polo | 40dc608f1f | |
Omar Polo | fe5967cd02 | |
Omar Polo | 0cd6675437 | |
Omar Polo | b736ca35f2 | |
Omar Polo | 44ee1bac8b | |
Omar Polo | 22c6d6334d | |
Omar Polo | caad03081b | |
Omar Polo | c4f682f855 | |
Omar Polo | 72bbed9136 | |
Omar Polo | 58173ca20e | |
Omar Polo | 286c4f40a5 | |
Omar Polo | 46af8c6cfe | |
Omar Polo | 7957cbd9aa | |
Omar Polo | 42650adec0 | |
Omar Polo | 909ea500a4 | |
Omar Polo | 3300cbe06a | |
Omar Polo | 390a611893 | |
Omar Polo | 2d3f837ac5 | |
Omar Polo | 333a671107 | |
Omar Polo | 2b15ad2860 | |
Omar Polo | 1078229283 | |
Omar Polo | 2d3cc76f6d | |
Omar Polo | 6c117838be | |
Omar Polo | 0a0e6ba7f9 | |
Omar Polo | 2f09adb085 | |
Omar Polo | 71cf39757d | |
Omar Polo | a5d04ea066 | |
Omar Polo | f28d96d399 | |
Omar Polo | 8443bff77a | |
Omar Polo | 0b00962d37 | |
Omar Polo | 145ba43e16 | |
Omar Polo | ae08ec7da5 | |
Omar Polo | 2030e31486 | |
Omar Polo | f88311e534 | |
Omar Polo | 252908e6bb | |
Omar Polo | c8b7433918 | |
Omar Polo | 501e489c90 | |
Omar Polo | 8d6ae384be | |
Omar Polo | 7b31a6386d | |
Omar Polo | 28ec617809 | |
Omar Polo | ba7b2faa86 | |
Omar Polo | 07b0a14218 | |
Omar Polo | 0baf6bed2a | |
Omar Polo | a87f662565 | |
Omar Polo | a7b9bb4d24 | |
Omar Polo | 05748e49fe | |
Omar Polo | e7a2a99b5a | |
Omar Polo | 9adde3d8b2 | |
Omar Polo | 6cdecad88d | |
Omar Polo | f62aab517d | |
Omar Polo | 2349b02b26 | |
Omar Polo | c6b9ddecf6 | |
Omar Polo | ce06cdc5bd | |
Omar Polo | 31a4993a94 | |
Omar Polo | d760973a5b | |
Omar Polo | 497977d501 | |
Omar Polo | 3309ef975c | |
Omar Polo | 45b4aa6e57 | |
Omar Polo | 1a49166de4 | |
Omar Polo | e29dbd7217 | |
Omar Polo | 7f740a1440 | |
Omar Polo | e0e15c30d7 | |
Omar Polo | dd8cc7d3ce | |
Omar Polo | c421bd46ed | |
Omar Polo | 609fc9f9d9 | |
Omar Polo | 99f95f7762 | |
Omar Polo | 7e26256394 | |
Omar Polo | 3527b7a58a | |
Omar Polo | 30008a68d3 | |
Omar Polo | 5c2e310ede | |
Omar Polo | f890c8c54d | |
Omar Polo | 36162ed86d | |
Omar Polo | 42bbdc7978 | |
Omar Polo | f77a8c867e | |
Omar Polo | d1ca3911d2 | |
Omar Polo | 06f233ad8a | |
Omar Polo | 0ac2dceccc | |
Omar Polo | 2d7abb7017 | |
Omar Polo | ea28a44931 | |
Omar Polo | 95210bb396 | |
Omar Polo | a5a09e44b2 | |
Omar Polo | 5c342d059f | |
Omar Polo | b2a6b61371 | |
Omar Polo | 20c9ff466b | |
Omar Polo | 33ac26a0e5 | |
Omar Polo | 642b37d5ac | |
Omar Polo | ac69e83aab | |
Omar Polo | fb2c20c301 | |
Omar Polo | 86939cb45b | |
Omar Polo | f980545b58 | |
Omar Polo | 04397b3293 | |
Omar Polo | 8f0da58068 | |
Omar Polo | 597ddf6ca8 | |
Omar Polo | 12042ad700 | |
Omar Polo | 338f06f4e5 | |
Omar Polo | 32befb74a9 | |
Omar Polo | 1213c735bf | |
Omar Polo | de27389e83 | |
Omar Polo | b4d409cf29 | |
Omar Polo | 15209ad9ed | |
Omar Polo | ce79c944bc | |
Omar Polo | 0ab65593e2 | |
Omar Polo | 0be51733ef | |
Omar Polo | 61f8d630c8 | |
Omar Polo | f2b3a5193f | |
Omar Polo | 3c0375e405 | |
Omar Polo | de4f713184 | |
Omar Polo | 298e4b96dc | |
Omar Polo | 2debfe2b1f | |
Omar Polo | 710a71b2bb | |
Omar Polo | 6119e13e8a | |
Omar Polo | df79b4c1d5 | |
Omar Polo | 05c23a54ea | |
Omar Polo | 17b09e3cb7 | |
Omar Polo | 94a79035ec | |
Omar Polo | 5bc29bc915 | |
Omar Polo | d89aa7630d | |
Omar Polo | e7ecf9a528 | |
Omar Polo | eb6997835a | |
Omar Polo | a010b0ddc5 | |
Omar Polo | 982069a120 | |
Omar Polo | 0fbe79b33c | |
Omar Polo | 132cae8c6f | |
Omar Polo | 568a09c220 | |
Omar Polo | f87bf597ea | |
Omar Polo | c0224a266c | |
Omar Polo | d3a08f4d17 | |
Omar Polo | 5f564d23e9 | |
Omar Polo | 65fba1d570 | |
Omar Polo | 771d8f281b | |
Omar Polo | 0170ba023f | |
Omar Polo | b3927be446 | |
Omar Polo | 364f10dbcd | |
Omar Polo | 76fd55f437 | |
Omar Polo | 7a11e8a5d3 | |
Omar Polo | ea58dab1a7 | |
Omar Polo | c2e39fcfed | |
Omar Polo | 71b7eb2f8c | |
Omar Polo | 7146dd552f | |
Omar Polo | d939d0f09e | |
Omar Polo | 881dc835d0 | |
Omar Polo | 881a9dd9c2 | |
Omar Polo | bd726b55be | |
Omar Polo | 98c8f038ac | |
Omar Polo | 7edc455ab4 | |
Omar Polo | 5bc3c98ed4 | |
Omar Polo | 8696c5ea24 | |
Omar Polo | b777bf4b2b | |
Omar Polo | f7b816dc39 | |
Omar Polo | dafb57b8af | |
Omar Polo | 4ad11931ec | |
Omar Polo | 6214c12719 | |
Omar Polo | e4d82becb7 | |
Omar Polo | 1590277007 | |
Omar Polo | 4d2ec6d705 | |
Omar Polo | 9939ddcf9d | |
Omar Polo | de428fff65 | |
Omar Polo | 9862b637c2 | |
Omar Polo | 0d7a38c4ce | |
Omar Polo | 796c6e75d7 | |
Omar Polo | 7b1d979032 | |
Omar Polo | 6a9ae70773 | |
Omar Polo | 3c1cf9d07c | |
Omar Polo | 28778244d6 | |
Omar Polo | b9220ca4de | |
Omar Polo | 85dff1f9c3 | |
Omar Polo | 33756bd235 | |
Omar Polo | 709d6e5ead | |
Omar Polo | 80bbcad5f2 | |
Omar Polo | 945d22d1f2 | |
Omar Polo | 9b374f41ee | |
Omar Polo | 579e21b35a | |
Omar Polo | ef04b55160 | |
Omar Polo | 578ba2d81b | |
Omar Polo | 16ace04d45 | |
Omar Polo | 4c4167393a | |
Omar Polo | 6c6c7a0ed7 | |
Omar Polo | b3deee7b38 | |
Omar Polo | 3d9a1c73de | |
Omar Polo | 4a28dd014a | |
Omar Polo | 5a40216f37 | |
Omar Polo | 6d91731552 | |
Omar Polo | f722f3c5aa | |
Omar Polo | 60ff035518 | |
Omar Polo | 00781742c5 | |
Omar Polo | df6ca41da3 | |
Omar Polo | 043acc97b1 | |
Omar Polo | 33d32d1fd6 | |
Omar Polo | d5aba4c791 | |
Omar Polo | 488f059ac4 | |
Omar Polo | 83000e2dd4 | |
Omar Polo | 51d876f030 | |
Omar Polo | 4f12842dfe | |
Omar Polo | 231bfcdc03 | |
Omar Polo | dd3e110a70 | |
Omar Polo | d7802bb44a | |
Omar Polo | 677afbd3f8 | |
Omar Polo | c603f1260d | |
Omar Polo | dd3895586f | |
Omar Polo | 7cd6880b84 | |
Omar Polo | ae2ad03ec0 | |
Omar Polo | 721e232529 | |
Omar Polo | d431188c66 | |
Omar Polo | a5d310bc0d | |
Omar Polo | 92e66347ed | |
Omar Polo | 0ed5656795 | |
Omar Polo | 72f653b652 | |
Omar Polo | f65ed01841 | |
Omar Polo | 120381c93d | |
Omar Polo | aff8d19010 | |
Omar Polo | 60ba426e7e | |
Omar Polo | 75d233f00c | |
Omar Polo | aa0fe0cf24 | |
Omar Polo | 3c19febb01 | |
Omar Polo | e8cac16e03 | |
Omar Polo | 72342dc960 | |
Omar Polo | 2c3a40faf8 | |
Omar Polo | 932b001a3f | |
Omar Polo | 20f688381a | |
Omar Polo | dd080e1f75 | |
Omar Polo | 19f68393e6 | |
Omar Polo | 0cf902af62 | |
Omar Polo | 9468027ba7 | |
Omar Polo | 4f6eb772cb | |
Omar Polo | 2b897a2cea | |
Omar Polo | f28f931139 | |
Omar Polo | 9c56b0a78a | |
Omar Polo | 10fed44c79 | |
Omar Polo | 09a1b97896 |
|
@ -0,0 +1,46 @@
|
|||
# gcc' -Werror=use-after-free gets tripped by vis.c: it sees a use
|
||||
# after free where it's not possible and breaks the CI.
|
||||
|
||||
# seems that inside the CI it's not currently possible to bind to ::1
|
||||
# so set HAVE_IPV6=no.
|
||||
|
||||
linux_amd64_task:
|
||||
container:
|
||||
image: alpine:latest
|
||||
test_script:
|
||||
- apk add alpine-sdk linux-headers bison libretls-dev libevent-dev
|
||||
- ./configure CFLAGS='-O2 -pipe -Wno-deprecated-declarations -Wno-use-after-free' -Werror
|
||||
- make
|
||||
- make regress REGRESS_HOST="*" HAVE_IPV6=no
|
||||
|
||||
linux_arm_task:
|
||||
arm_container:
|
||||
image: alpine:latest
|
||||
test_script:
|
||||
- apk add alpine-sdk linux-headers bison libretls-dev libevent-dev
|
||||
- ./configure CFLAGS='-O2 -pipe -Wno-deprecated-declarations -Wno-use-after-free' -Werror
|
||||
- make
|
||||
- make regress REGRESS_HOST="*" HAVE_IPV6=no
|
||||
|
||||
freebsd_14_task:
|
||||
freebsd_instance:
|
||||
image_family: freebsd-14-0
|
||||
install_script: pkg install -y libevent libressl pkgconf
|
||||
script:
|
||||
- ./configure CFLAGS='-O2 -pipe -Wno-deprecated-declarations' -Werror
|
||||
- make
|
||||
- make regress HAVE_IPV6=no
|
||||
|
||||
#
|
||||
# There are some issues with imsg fd passing on macos at the moment that
|
||||
# seem to be triggered only in applications that do a heavy use of them,
|
||||
# like gmid or opensmtpd. Still, keep macos to ensure gmid builds here.
|
||||
#
|
||||
mac_task:
|
||||
macos_instance:
|
||||
image: ghcr.io/cirruslabs/macos-sonoma-xcode:latest
|
||||
test_script:
|
||||
- brew install libevent openssl libretls
|
||||
- PKG_CONFIG_PATH="$(brew --prefix openssl)/lib/pkgconfig" ./configure CFLAGS='-O2 -pipe -Wno-deprecated-declarations' -Werror
|
||||
- make
|
||||
- SKIP_RUNTIME_TESTS=1 make regress
|
|
@ -0,0 +1,11 @@
|
|||
**
|
||||
!*.c
|
||||
!*.h
|
||||
!*.y
|
||||
!*.[1-9]
|
||||
!compat
|
||||
!have/*.c
|
||||
!Makefile
|
||||
!configure
|
||||
!contrib/Docker.gmid.conf
|
||||
!contrib/gencert
|
|
@ -0,0 +1,33 @@
|
|||
name: release docker image
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
env:
|
||||
IMAGE_NAME: "gmid"
|
||||
jobs:
|
||||
build:
|
||||
permissions: write-all
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
|
||||
- name: build the image
|
||||
run: docker build -f contrib/Dockerfile -t gmid:alpine .
|
||||
|
||||
- name: login to ghcr.io
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: push the image
|
||||
run: |
|
||||
IMAGE_ID=ghcr.io/${{ github.repository_owner }}/$IMAGE_NAME
|
||||
IMAGE_ID=$(echo $IMAGE_ID | tr A-Z a-z)
|
||||
# strip git ref prefix from version
|
||||
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
|
||||
echo IMAGE_ID=$IMAGE_ID
|
||||
echo VERSION=$VERSION
|
||||
docker tag gmid:alpine $IMAGE_ID:$VERSION
|
||||
docker push $IMAGE_ID:$VERSION
|
|
@ -1,6 +1,37 @@
|
|||
cert.pem
|
||||
key.pem
|
||||
*~
|
||||
*.pem
|
||||
TAGS
|
||||
gmid
|
||||
*.o
|
||||
gg
|
||||
gemexp
|
||||
titan
|
||||
**/*.[do]
|
||||
*.swp
|
||||
docs
|
||||
y.tab.*
|
||||
compile_flags.txt
|
||||
config.h
|
||||
config.h.old
|
||||
config.log
|
||||
config.log.old
|
||||
config.mk
|
||||
configure.local
|
||||
!contrib/gmid
|
||||
!contrib/vim/ale_linters/gmid
|
||||
!contrib/vim/syntax_checkers/gmid
|
||||
regress/testdata
|
||||
regress/*.pem
|
||||
regress/*.key
|
||||
regress/*.crt
|
||||
regress/*.csr
|
||||
regress/*.srl
|
||||
regress/reg.conf
|
||||
regress/fcgi-test
|
||||
regress/fcgi.sock
|
||||
regress/fill-file
|
||||
regress/iri_test
|
||||
regress/puny-test
|
||||
regress/gmid.pid
|
||||
|
||||
site/gemini
|
||||
site/www
|
||||
|
|
|
@ -0,0 +1,686 @@
|
|||
2024-04-03 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* configure: improve function checking in the configure
|
||||
* have/landlock.c: fix landlock test
|
||||
* gmid.c (main_print_conf): fix config dumping with -nn
|
||||
|
||||
2024-03-03 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gmid.c: fix `log access path' with a chroot
|
||||
|
||||
2024-01-30 Anna “CyberTailor”
|
||||
|
||||
* contrib/vim/indent/gmid.vim: fix indent
|
||||
|
||||
2024-01-30 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y: don't make log styles reserved keywords. Unbreaks the
|
||||
example in the manpage with `common = ...'.
|
||||
|
||||
2024-01-26 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y: rework grammar to allow the semicolon after
|
||||
variables/macros definition and top-level options
|
||||
|
||||
2024-01-24 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* configure (VERSION): release 2.0.1
|
||||
|
||||
2024-01-21 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* convert gmid to the new imsg API
|
||||
|
||||
2024-01-14 Anna “CyberTailor”
|
||||
|
||||
* configure: fix --mandir handling
|
||||
|
||||
2024-01-11 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* configure (VERSION): release 2.0
|
||||
|
||||
2024-01-09 Anna “CyberTailor”
|
||||
|
||||
* contrib/vim/syntax/gmid.vim: update Vim syntax file
|
||||
* contrib/vim/ale_listers/gmid/gmid.vim: add ALE linter
|
||||
|
||||
2023-10-18 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* ge.c (load_local_cert): generate EC certs by default, use -R to keep generating RSA ones.
|
||||
* utils.c (gencert): generate EC too
|
||||
* gg.c (get): print the response header for non-2x replies to standard error
|
||||
|
||||
2023-10-15 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gg.c (main): exit with the gemini response code (unless it's 2x)
|
||||
|
||||
2023-08-29 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* ge.c (data_dir): use $XDG_DATA_HOME/gemexp instead of /gmid for the certificates.
|
||||
|
||||
2023-08-18 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* fcgi.c (fcgi_req): sync the parameters with RFC3875 (CGI)
|
||||
(fcgi_req): send "GET" as REQUEST_METHOD
|
||||
|
||||
2023-08-08 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* fcgi.c (fcgi_req): implement SCRIPT_NAME / PATH_INFO splitting for fastcgi
|
||||
(fcgi_req): add `fastcgi strip'
|
||||
|
||||
2023-08-07 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* logger.c (logger_dispatch_server): allow to change the syslog(3) facility.
|
||||
|
||||
* gmid.c (main): attempt to load TLS certificates and load mimes and virtual hosts root as part of configtest (-n) rather than verifying the syntax of the configuration only.
|
||||
|
||||
2023-07-25 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gmid.c (log_request): allow to change the logging style.
|
||||
|
||||
2023-07-24 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y: add ability to log to files with `log access <path>'.
|
||||
|
||||
2023-07-23 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y: revamp fastcgi configuration; make it per-location
|
||||
|
||||
2023-07-22 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* titan.c (main): add titan(1), a simple titan client
|
||||
|
||||
* gg.c (get): warn when the server doesn't use TLS' close notify
|
||||
|
||||
2023-07-01 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* fcgi.c (fcgi_handle_stdout): parse and log the fastcgi reply
|
||||
|
||||
2023-06-24 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* server.c (handle_handshake): correctly handle TLS handshake failures.
|
||||
|
||||
* server.c (client_close_ev): plug memory leak
|
||||
|
||||
2023-06-23 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y: implement `listen on'
|
||||
|
||||
2023-06-13 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* regress/sha: remove regress/sha; sha256/sha256sum is no more required for the regress suite.
|
||||
|
||||
2023-06-12 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* regress/lib.sh (run_test): use the default prefork in tests
|
||||
|
||||
2023-06-11 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* crypto.c: add a privsep crypto engine (enabled only on OpenBSD)
|
||||
|
||||
* configure: add -Wpointer-sign to the mix, adjust the code to cope.
|
||||
|
||||
2023-05-05 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* contrib/gencert: add -e to generate EC keys
|
||||
|
||||
2023-05-08 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* sandbox.c: drop landlock, seccomp and capsicum support
|
||||
|
||||
2022-09-10 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y (string): retire the deprecated `mime' and `map' config options
|
||||
|
||||
2022-09-07 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* ge.c (main): add `gemexp': small program to quickly export a directory over Gemini.
|
||||
|
||||
2022-09-06 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* server.c: drop CGI support.
|
||||
|
||||
2022-07-07 Omar Polo <op@omarpolo.com>
|
||||
|
||||
Included as part of gmid 1.8.5:
|
||||
|
||||
* log.c (logger_main): fix timestamps in log files. Reported by Karl Jeacle, thanks!
|
||||
|
||||
* dirs.c (scandir_fd): drop d_reclen; it's not available on DragonflyBSD (at least.)
|
||||
|
||||
2022-07-07 Anna “CyberTailor”
|
||||
|
||||
* Makefile: include contrib/ in dist
|
||||
|
||||
2022-07-04 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* contrib/gmid: remove OpenBSD' rc file: it's now maintained in the ports tree
|
||||
|
||||
* configure (VERSION): 1.8.4 tagged
|
||||
|
||||
* server.c (check_path): log when it fails to open a file because of permissions
|
||||
(client_write): encode file names in the directory index, spotted by cage
|
||||
(client_write): add a trailing / for dirs in the directory index.
|
||||
|
||||
* iri.c (parse_path): allow '@' and ':' in paths
|
||||
|
||||
2022-04-08 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* mime.c (mime): do a binary search to match the MIME time
|
||||
|
||||
* gmid.c (listener_main): don't load the default mime listing when
|
||||
`types' is used. (text/gemini -> gmi/gemini is still hardcoded)
|
||||
|
||||
* mime.c (add_mime): fix memory leak in the mime handling
|
||||
|
||||
2022-04-07 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gmid.conf.5: move config file documentation into a new man page
|
||||
|
||||
* parse.y (option): deprecate `map' rule in favour of the `types' block
|
||||
|
||||
2022-03-27 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* configure (VERSION): 1.8.3 tagged
|
||||
|
||||
* server.c (start_cgi): fix a possible out-of-bound access in the
|
||||
CGI handling.
|
||||
|
||||
2022-03-26 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* configure (VERSION): 1.8.2 tagged
|
||||
|
||||
* server.c (handle_imsg_cgi_res): fix a CGI timing issue: if a
|
||||
connection handled by a CGI scripts is interrupted with the right
|
||||
timing it causes the server process to exit with "fatal in
|
||||
client_by_id: invalid id X"
|
||||
|
||||
2022-02-26 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y: add the types block
|
||||
|
||||
2022-02-13 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* sandbox.c (filter): tightens seccomp policy wrt openat: allow
|
||||
only with the O_RDONLY flag.
|
||||
|
||||
2022-02-13 Tobias Berger <tobi.berger13@gmail.com>
|
||||
|
||||
* sandbox.c (filter): allow fstatat64, llseek and sigreturn;
|
||||
needed by glibc on armv7
|
||||
|
||||
2022-02-10 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* configure (VERSION): 1.8.1 tagged
|
||||
|
||||
* sandbox.c (open_landlock): fix landlock usage
|
||||
|
||||
2022-01-30 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* configure (VERSION): 1.8 “Lightbulb Sun” tagged
|
||||
|
||||
* proxy.c (proxy_setup_tls): allow to specify a custom hostname as SNI for the TLS handshake with the proxied host.
|
||||
|
||||
2022-01-02 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* server.c (matched_proxy): allow to specify multiple proxy blocks and add matching rules
|
||||
|
||||
* parse.y (servbody): relax the strict ordering of options, location and proxy blocks
|
||||
|
||||
2022-01-01 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* proxy.c (proxy_init): add support for client certificate when proxying
|
||||
|
||||
2021-12-29 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* proxy.c (proxy_init): add proxying support via `proxy relay-to'
|
||||
|
||||
2021-12-11 Max <vdrummer@posteo.net>
|
||||
|
||||
* sandbox.c (filter): [seccomp]: allow ugetrlimit(2), needed by glibc on armv7l
|
||||
|
||||
2021-12-09 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* server.c (client_read): don't check if the port in the request is the same we're listening on. Suggested by Allen Sobot, thanks!
|
||||
* configure: add --prefix=... long argument.
|
||||
|
||||
2021-11-16 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* configure (singletest): fix the cross-compilation: don't run the test binaries just built. Suggested by Nikolay Korotkiy (@sikmir), thanks!
|
||||
|
||||
2021-10-24 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* log.c (log_request): fix "double slash" in logs: gmid ended up printing two slashes between the hostname and the path when logging the request IRI.
|
||||
|
||||
2021-10-15 Stephen Gregoratto <dev@sgregoratto.me>
|
||||
|
||||
* gmid.c (add_keypair): implement OCSP stapling support
|
||||
|
||||
2021-10-13 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* server.c (do_accept): don't die on ECONNABORTED (``Software caused connection abort'')
|
||||
|
||||
2021-10-11 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* contrib/renew-certs: add script to automatically renew self-signed certificates
|
||||
|
||||
2021-10-09 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y (print_conf): multiple -n to dump the parsed configuration
|
||||
|
||||
* contrib/gencert: add gencert, a simple script to generate self-signed certs
|
||||
|
||||
2021-10-04 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* regress/lib.sh (raw): reduced the timeout time for single checks from 30 to 10 seconds
|
||||
|
||||
* regress/runtime: regression test restructured. still implemented as a set of POSIX-scripts
|
||||
|
||||
2021-10-02 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* server.c (client_read): reworked the internal structure. Now we leverage libevent' bufferevents more.
|
||||
(cgi_read): be more strict with CGI scripts: don't pass through illegal CGI responses.
|
||||
|
||||
2021-09-26 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* fcgi.c (fcgi_req): the FastCGI implementation is fully asynchronous
|
||||
|
||||
2021-09-24 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* configure (VERSION): 1.7.4 tagged
|
||||
|
||||
* server.c (check_for_cgi): fix out-of-bound access, found with lots of help from cage, thanks!
|
||||
|
||||
2021-09-19 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* sandbox.c (gmid_create_landlock_rs): [linux] use landlock on the server and logger process to reduce the fs access
|
||||
|
||||
* configure (VERSION): 1.7.3 tagged
|
||||
|
||||
2021-09-17 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* log.c (logger_main): fix syslog logging on FreeBSD
|
||||
|
||||
2021-08-24 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gmid.c (main): don't crash on -n without -c
|
||||
|
||||
2021-07-29 Anna “CyberTailor”
|
||||
|
||||
* gmid.1: document logging, openssl key generation example, spelling/grammar and crosslinks
|
||||
|
||||
2021-07-27 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* server.c (check_path): allow symlinks
|
||||
|
||||
2021-07-23 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* sandbox.c: seccomp allow fstat64 (used by glibc an aarch64). Reported by pine, thanks!
|
||||
|
||||
2021-07-19 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gmid.c (setup_configless): unbreak configless mode
|
||||
|
||||
2021-07-13 Anna “CyberTailor”
|
||||
|
||||
* parse.y (yyerror): error and warning messages are prefixed with "error:" and "warning:"
|
||||
(yywarn): fix off-by-one line number in warnings
|
||||
|
||||
2021-07-11 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* configure (VERSION): 1.7.1 tagged
|
||||
|
||||
2021-07-11 Anna “CyberTailor”
|
||||
|
||||
* gmid.1 (common): fix misleading example in man page: macros names may not be reserved words
|
||||
|
||||
2021-07-10 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* configure (VERSION): 1.7 tagged
|
||||
|
||||
2021-07-09 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y (STRING): add `include' directive
|
||||
(yylex): add @-macros (real macros)
|
||||
|
||||
2021-07-08 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y (option): rename `mime MIME EXT' to `map MIME to-ext EXT', but retain the old `mime' for compatibility.
|
||||
|
||||
2021-07-06 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* regress/gg.c (main): add -T timeout
|
||||
|
||||
* configure (guessing_cflags): try to preserve CFLAGS/LDFLAGS
|
||||
|
||||
2021-07-02 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* sandbox.c (filter): seccomp filter reworked: now it should work on x86 and possibly other arches too!
|
||||
|
||||
2021-06-29 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y (conf): don't require the strict order macro > options > servers
|
||||
c-like strings: when two or more strings are next to each others, concat them
|
||||
|
||||
* gmid.c (main): add -D to define macros from the cmd line
|
||||
|
||||
* parse.y (yylex): allow to define macros in the config file
|
||||
|
||||
* gmid.c (main): use getopt_long, add --help as synonym of -h and -V/--version
|
||||
|
||||
2021-06-17 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gmid.1: document `log' option
|
||||
|
||||
2021-06-16 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y (yylex): drop the dependency on lex by implementing
|
||||
yylex by ourselves (the actual implementation is based off doas'
|
||||
parse.y). This gave us various benefits, like cleaner code, \ to
|
||||
break long lines, better handling of quotes etc...
|
||||
|
||||
2021-06-11 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y (servopt): add `param' keyword
|
||||
|
||||
* fcgi.c (send_fcgi_req): send GATEWAY_INTERFACE, AUTH_TYPE,
|
||||
REMOTE_USER, TLS_CLIENT_ISSUER, TLS_CLIENT_HASH, TLS_VERSION,
|
||||
TLS_CIPHER, TLS_CIPHER_STRENGTH and TLS_CLIENT_NOT_BEFORE/AFTER.
|
||||
(send_fcgi_req): support a custom list of params
|
||||
|
||||
2021-05-24 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gg.c: move `gg' to regress, as it's only used for the tests
|
||||
|
||||
2021-05-12 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* utils.c (gen_certificate): don't add gmid as organisation when generating the certificate, and now it set the version 3, so it's compatible with java/android
|
||||
|
||||
2021-05-09 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* server.c (apply_fastcgi): added fastcgi support!
|
||||
|
||||
2021-05-03 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y: errors on duplicate values instead of silently using only the last value.
|
||||
|
||||
2021-04-30 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* server.c (fmt_sbuf): ensure %p (path) is always absolute
|
||||
|
||||
* gmid.c (load_vhosts): allow ``root'' rule to be specified per-location block
|
||||
|
||||
2021-04-29 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y (servopt): added ``alias'' option to define hostname aliases for a server
|
||||
|
||||
2021-04-28 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gmid.c (main): pidfile support with `-P pidfile'
|
||||
|
||||
2021-04-27 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y (servopt): added ``env'' option to define environment vars for CGI scripts
|
||||
|
||||
* log.c (fatal): lower the log priority for fatal errors from CRIT to ERR
|
||||
|
||||
2021-04-25 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* server.c (open_dir): sort the auto index alphabetically
|
||||
|
||||
2021-04-21 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* mime.c (load_default_mime): use `text/x-patch' for .patch and .diff files
|
||||
|
||||
2021-04-14 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* log.c (handle_imsg_log): print the datetime when logging to stderr
|
||||
|
||||
2021-04-13 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* ex.c (launch_cgi): define TLS_VERSION, TLS_CIPHER and TLS_CIPHER_STRENGTH for CGI scripts
|
||||
|
||||
2021-04-12 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* 1.6.1 tagged
|
||||
|
||||
* iri.c (path_clean): fix possible infinite loop in the IRI parsing code.
|
||||
|
||||
2021-03-31 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gmid.h (struct vhost): remove limits on the number of vhosts and location blocks
|
||||
|
||||
* gmid.c (mkdirs): fix recursive mkdirs for configless mode
|
||||
|
||||
2021-03-29 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* Makefile (static): fixed `static' target
|
||||
|
||||
2021-03-29 kornellapacz @ github
|
||||
|
||||
* Dockerfile: add missing libevent-dev dependency, thanks!
|
||||
|
||||
2021-03-27 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gmid.h (struct client): correctly handle CGI scripts that replies with the maximum header length allowed
|
||||
|
||||
2021-03-20 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* 1.6 tagged
|
||||
|
||||
* sandbox.c (sandbox_logger_process): add capsicum to the logger process
|
||||
|
||||
2021-03-19 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gmid.c (main): use imsg for all IPC; only the main process
|
||||
listens for SIGHUP: this means that finally the config reload will
|
||||
work with OpenBSD' rc (and probably other service manager too)
|
||||
|
||||
2021-02-22 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* log.c (log_request): add `log on/off' to enable/disable logs per-server or per-location
|
||||
|
||||
2021-02-09 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y (locopt): add `require client ca' rule to require client certs signed by a specified CA
|
||||
|
||||
2021-02-07 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* ex.c (do_exec): [cgi] split the query in words if needed and add them to the argv
|
||||
(launch_cgi): define TLS_CLIENT_NOT_BEFORE/NOT_AFTER in CGI scripts
|
||||
|
||||
* parse.y (option): added prefork option
|
||||
|
||||
2021-02-06 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y (locopt): added ``block return'' and ``strip'' options
|
||||
(servopt): add the ``entrypoint'' option
|
||||
|
||||
2021-02-05 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* iri.c (parse_query): don't %-decode the query part. This affects the value of QUERY_STRING for CGI scripts too, since that must be %-encoded and we're currently shipping it decoded.
|
||||
|
||||
2021-02-04 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gmid.c (main): reload configuration on SIGHUP, without disconnecting the clients
|
||||
|
||||
2021-02-02 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* server.c (handle_dirlist_head): print the header in the directory listing
|
||||
(open_file): cgi follows globbing rules, just like location and hostname matching
|
||||
|
||||
2021-02-01 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y (servopt): require absolute paths in config file
|
||||
|
||||
2021-01-31 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gmid.c (main): cgi scripts now have only std{in,out,err} open
|
||||
|
||||
2021-01-30 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* 1.5 tagged
|
||||
* server.c: change precedence of location rules
|
||||
|
||||
2021-01-29 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* iri.c (parse_authority): accept a wider range of unicode
|
||||
codepoints while parsing the host name.
|
||||
|
||||
2021-01-26 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* puny.c (puny_decode): initial punycode support!
|
||||
|
||||
2021-01-25 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gmid.1: manpage improvements (example and usage)
|
||||
|
||||
* gmid.c (main): Dropping -C, -K, -f, changing the meaning of -d:
|
||||
now it's the certificate directory. Serve the directory given as
|
||||
positional argument (or the current one) when running in
|
||||
config-less mode.
|
||||
(gen_certificate): automatically generate a certificate
|
||||
|
||||
* parse.y (option): added chroot and user options
|
||||
|
||||
2021-01-24 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* server.c (open_dir): add directory listing (disabled by default)
|
||||
|
||||
* parse.y (vhost): added support for location blocks
|
||||
|
||||
* server.c (send_dir): make the directory index customizable
|
||||
|
||||
2021-01-23 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gg.c (main): added gg, a barebone gemini client. used by the
|
||||
regress suite
|
||||
|
||||
2021-01-21 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* configure: added a configure script
|
||||
|
||||
* server.c (handle_handshake): glob for vhost domain
|
||||
|
||||
* gmid.c (log_request): logs the full IRI and the response code (even for CGI)
|
||||
|
||||
2021-01-19 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y (servopt): add "lang" server option
|
||||
(servopt): moving "default type" from global options to server options
|
||||
|
||||
* Dockerfile: add a dockerfile
|
||||
|
||||
2021-01-18 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* parse.y (option): add mime and "default type" options for media types.
|
||||
|
||||
2021-01-17 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* sandbox.c (sandbox): added initial seccomp(2) support
|
||||
|
||||
2021-01-15 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* cgi.c (start_cgi): set SERVER_NAME to the vhost when executing CGI scripts
|
||||
|
||||
* parse.y (option): add ability to specify the tls versions with "protocols"
|
||||
|
||||
* gmid.c (handle_open_conn): ensure the port number of the request matches
|
||||
|
||||
* sandbox.c (sandbox): sandbox on OpenBSD (pledge/unveil, as before) and on FreeBSD (capsicum) too
|
||||
|
||||
* sample.conf: added sample configuration
|
||||
|
||||
* gmid.c (main): changed behaviour: daemon off by default
|
||||
(main): changed -c in -C (cert option)
|
||||
(main): changed -k in -K (key option, for consistency with -C)
|
||||
(main): added -c to load a configuration
|
||||
(main): certs, key and doc (-C -K and -d) doesn't have a default value anymore
|
||||
(handle_handshake): add vhosts support
|
||||
|
||||
2021-01-13 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* iri.c (parse_scheme): normalize scheme while parsing, so we're
|
||||
RFC3986 compliant.
|
||||
|
||||
2021-01-11 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* 1.4 and 1.4.1 tagged
|
||||
|
||||
* gmid.c (main): ipv6 disabled by default and -6 flag to enable it
|
||||
(handle): reject non-gemini protocols with 53
|
||||
|
||||
2021-01-10 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gmid.c (logs): log also the port of the client
|
||||
(loop): accept both ipv4 and ipv6
|
||||
|
||||
2020-12-26 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* uri.c (parse_uri): IRI support
|
||||
|
||||
2020-12-21 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gmid.c (main): -d supports relative paths
|
||||
|
||||
2020-12-02 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gmid.c: logging reworked and daemonize. The -l option was
|
||||
removed: now it logs on syslog if -f (foreground) is not passed.
|
||||
|
||||
2020-11-18 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* 1.3.2 tagged
|
||||
|
||||
* gmid.c (url_after_proto): fix bug introduced in last version
|
||||
regarding full URLs with explicit gemini protocol.
|
||||
|
||||
* 1.3.1 tagged
|
||||
|
||||
* gmid.c (url_after_proto): correct url parsing: accept URLs
|
||||
without explicit protocol
|
||||
(url_start_of_request): correct handling of relative URLs
|
||||
|
||||
2020-11-17 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gmid.c (main): add flag -p to change the port
|
||||
|
||||
2020-11-10 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* ChangeLog: 1.3 tagged, fixed ChangeLog format
|
||||
|
||||
* gmid.c (open_file): added support for path parameters for CGI
|
||||
scripts
|
||||
|
||||
2020-11-06 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gmid.1: great improvements to the documentation
|
||||
|
||||
* gmid.c (url_after_proto): ensure that the requested protocol is
|
||||
“gemini” and not something else that’s long 6 bytes.
|
||||
|
||||
* gmid.c (loop): added support for cgi scripts (can handle multiple
|
||||
concurrently)
|
||||
|
||||
2020-11-06 Omar Polo <op@venera>
|
||||
|
||||
* gmid.1: added option to log to a file
|
||||
|
||||
2020-11-05 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* gmid.c (filetypes): add MIME type for xml files
|
||||
|
||||
2020-11-03 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* 1.2 tagged
|
||||
|
||||
* gmid.c (main): ignore SIGPIPE
|
||||
|
||||
2020-10-14 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* 1.1 tagged
|
||||
|
||||
* switching to mmap() based file handling
|
||||
|
||||
2020-10-07 Omar Polo <op@omarpolo.com>
|
||||
|
||||
* 1.0 tagged
|
43
INSTALL.gmi
43
INSTALL.gmi
|
@ -1,43 +0,0 @@
|
|||
# Installing gmid
|
||||
|
||||
## Dependencies
|
||||
|
||||
gmid depends on libtls and a C compiler. It's reported to compile with gcc 4.2, so it should work pretty everywhere now.
|
||||
|
||||
The compilation is as easy as
|
||||
|
||||
```
|
||||
make
|
||||
```
|
||||
|
||||
Note that there isn't an install target yet.
|
||||
|
||||
If you're a packager, don't forget to install also the manpage gmid.1
|
||||
|
||||
### etags
|
||||
|
||||
during the build process, etags(1) is invoked to generate a TAGS file. This is done to aid the development. Anyway, etags(1) is NOT NEEDED as dependency. So, if you get something like
|
||||
|
||||
```
|
||||
$ make
|
||||
...
|
||||
etags gmid.c
|
||||
make: exec(etags) failed (No such file or directory)
|
||||
*** Error code 1 (ignored)
|
||||
```
|
||||
|
||||
you don't need to worry, it is OK.
|
||||
|
||||
## Notes for specific OSes
|
||||
|
||||
### OpenBSD
|
||||
|
||||
Everything you need is in base, just run make.
|
||||
|
||||
### FreeBSD
|
||||
|
||||
You need to install security/libressl and then
|
||||
|
||||
```
|
||||
make CFLAGS=-I/usr/local/include LDFLAGS='-L/usr/local/lib -ltls'
|
||||
```
|
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
|
||||
|
|
195
Makefile
195
Makefile
|
@ -1,19 +1,190 @@
|
|||
CC = cc
|
||||
CFLAGS = -Wall -Wextra -g
|
||||
LDFLAGS = -ltls
|
||||
# Copyright (c) 2022 Omar Polo <op@omarpolo.com>
|
||||
# Copyright (c) 2011, 2013-2022 Ingo Schwarze <schwarze@openbsd.org>
|
||||
# Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
.PHONY: all clean
|
||||
# tests to run as a port of the regression suite. Leave empty to run
|
||||
# all.
|
||||
TESTS=
|
||||
|
||||
all: gmid TAGS README.md
|
||||
# host to bind to during regress
|
||||
REGRESS_HOST = localhost
|
||||
|
||||
gmid: gmid.o
|
||||
${CC} gmid.o -o gmid ${LDFLAGS}
|
||||
# -- build-related variables --
|
||||
|
||||
TAGS: gmid.c
|
||||
-etags gmid.c
|
||||
COBJS = ${COMPATS:.c=.o}
|
||||
|
||||
README.md: gmid.1
|
||||
mandoc -Tmarkdown gmid.1 | sed -e '1d' -e '$$d' > README.md
|
||||
GMID_SRCS = gmid.c config.c crypto.c dirs.c fcgi.c iri.c log.c \
|
||||
logger.c mime.c proc.c proxy.c puny.c sandbox.c \
|
||||
server.c utf8.c utils.c y.tab.c
|
||||
|
||||
GMID_OBJS = ${GMID_SRCS:.c=.o} ${COBJS}
|
||||
|
||||
GEMEXP_SRCS = ge.c config.c crypto.c dirs.c fcgi.c iri.c log.c mime.c \
|
||||
proc.c proxy.c puny.c sandbox.c server.c utf8.c utils.c
|
||||
|
||||
GEMEXP_OBJS = ${GEMEXP_SRCS:.c=.o} ${COBJS}
|
||||
|
||||
GG_SRCS = gg.c iri.c log.c utf8.c
|
||||
|
||||
GG_OBJS = ${GG_SRCS:.c=.o} ${COBJS}
|
||||
|
||||
TITAN_SRCS = titan.c iri.c log.c utf8.c
|
||||
TITAN_OBJS = ${TITAN_SRCS:.c=.o} ${COBJS}
|
||||
|
||||
SRCS = gmid.h iri.h log.h parse.y proc.h \
|
||||
${GMID_SRCS} ${GEMEXP_SRCS} ${GG_SRCS} ${TITAN_SRCS}
|
||||
|
||||
DISTNAME = gmid-${VERSION}
|
||||
|
||||
# -- public targets --
|
||||
|
||||
all: config.mk gmid gemexp gg titan
|
||||
.PHONY: all tags clean cleanall test regress install
|
||||
|
||||
config.mk config.h: configure
|
||||
@echo "$@ is out of date; please run ./configure"
|
||||
@exit 1
|
||||
|
||||
include config.mk
|
||||
|
||||
clean:
|
||||
rm -f gmid.o gmid
|
||||
rm -f gmid gemexp gg
|
||||
rm -f *.[do] compat/*.[do] compat/libtls/*.[do]
|
||||
rm -f y.tab.c y.tab.h y.output
|
||||
rm -f compile_flags.txt
|
||||
${MAKE} -C regress clean
|
||||
|
||||
distclean: clean
|
||||
rm -f config.h config.h.old config.log config.log.old config.mk
|
||||
|
||||
test: regress
|
||||
regress: all
|
||||
${MAKE} 'TESTS=${TESTS}' -C regress all
|
||||
|
||||
install: gmid gg gemexp
|
||||
mkdir -p ${DESTDIR}${BINDIR}
|
||||
mkdir -p ${DESTDIR}${MANDIR}/man1
|
||||
mkdir -p ${DESTDIR}${MANDIR}/man5
|
||||
mkdir -p ${DESTDIR}${MANDIR}/man8
|
||||
${INSTALL_PROGRAM} gemexp ${DESTDIR}${BINDIR}
|
||||
${INSTALL_PROGRAM} gg ${DESTDIR}${BINDIR}
|
||||
${INSTALL_PROGRAM} gmid ${DESTDIR}${BINDIR}
|
||||
${INSTALL_PROGRAM} titan ${DESTDIR}${BINDIR}
|
||||
${INSTALL_MAN} gemexp.1 ${DESTDIR}${MANDIR}/man1
|
||||
${INSTALL_MAN} gg.1 ${DESTDIR}${MANDIR}/man1
|
||||
${INSTALL_MAN} titan.1 ${DESTDIR}${MANDIR}/man1
|
||||
${INSTALL_MAN} gmid.conf.5 ${DESTDIR}${MANDIR}/man5
|
||||
${INSTALL_MAN} gmid.8 ${DESTDIR}${MANDIR}/man8
|
||||
|
||||
uninstall:
|
||||
rm ${DESTDIR}${BINDIR}/gemexp
|
||||
rm ${DESTDIR}${BINDIR}/gg
|
||||
rm ${DESTDIR}${BINDIR}/gmid
|
||||
rm ${DESTDIR}${BINDIR}/titan
|
||||
rm ${DESTDIR}${MANDIR}/man1/gemexp.1
|
||||
rm ${DESTDIR}${MANDIR}/man1/gg.1
|
||||
rm ${DESTDIR}${MANDIR}/man1/titan.1
|
||||
rm ${DESTDIR}${MANDIR}/man5/gmid.conf.5
|
||||
rm ${DESTDIR}${MANDIR}/man8/gmid.8
|
||||
|
||||
tags:
|
||||
ctags ${SRCS}
|
||||
|
||||
# --internal build targets --
|
||||
|
||||
gmid: ${GMID_OBJS}
|
||||
${CC} ${GMID_OBJS} -o $@ ${LDFLAGS} ${LIBS}
|
||||
|
||||
gemexp: ${GEMEXP_OBJS}
|
||||
${CC} ${GEMEXP_OBJS} -o $@ ${LDFLAGS} ${LIBS}
|
||||
|
||||
gg: ${GG_OBJS}
|
||||
${CC} ${GG_OBJS} -o $@ ${LDFLAGS} ${LIBS}
|
||||
|
||||
titan: ${TITAN_OBJS}
|
||||
${CC} ${TITAN_OBJS} -o $@ ${LDFLAGS} ${LIBS}
|
||||
|
||||
y.tab.c: parse.y
|
||||
${YACC} -b y parse.y
|
||||
|
||||
# make sure we pass -o to ${CC}. OpenBSD default suffix rule doesn't
|
||||
.SUFFIXES: .c .o
|
||||
.c.o:
|
||||
${CC} ${CFLAGS} -c $< -o $@
|
||||
|
||||
# -- maintainer targets --
|
||||
|
||||
.PHONY: lint release dist
|
||||
|
||||
lint:
|
||||
man -Tlint -Wstyle -l gmid.8 gmid.conf.5 gemexp.1 gg.1 titan.1
|
||||
|
||||
PUBKEY = keys/gmid-2.0.pub
|
||||
PRIVKEY = set-PRIVKEY
|
||||
DISTFILES = .cirrus.yml .dockerignore .gitignore ChangeLog LICENSE \
|
||||
Makefile README.md config.c configure crypto.c dirs.c fcgi.c \
|
||||
ge.c gemexp.1 gg.1 gg.c gmid.8 gmid.c gmid.conf.5 gmid.h \
|
||||
iri.c iri.h log.c log.h logger.c mime.c parse.y proc.c \
|
||||
proc.h proxy.c puny.c sandbox.c server.c titan.1 titan.c \
|
||||
utf8.c utils.c y.tab.c
|
||||
|
||||
release:
|
||||
sed -i -e '/^RELEASE=/s/no/yes/' configure
|
||||
${MAKE} dist
|
||||
sed -i -e '/^RELEASE=/s/yes/no/' configure
|
||||
signify -S -e -m ${DISTNAME}.sha256 -s ${PRIVKEY}
|
||||
|
||||
verify-release:
|
||||
signify -C -p ${PUBKEY} -x ${DISTNAME}.sha256.sig
|
||||
|
||||
dist: ${DISTNAME}.sha256
|
||||
|
||||
${DISTNAME}.sha256: ${DISTNAME}.tar.gz
|
||||
sha256 ${DISTNAME}.tar.gz > $@
|
||||
|
||||
${DISTNAME}.tar.gz: ${DISTFILES}
|
||||
mkdir -p .dist/${DISTNAME}/
|
||||
${INSTALL} -m 0644 ${DISTFILES} .dist/${DISTNAME}/
|
||||
cd .dist/${DISTNAME} && chmod 755 configure
|
||||
${MAKE} -C compat DESTDIR=${PWD}/.dist/${DISTNAME}/compat dist
|
||||
${MAKE} -C contrib DESTDIR=${PWD}/.dist/${DISTNAME}/contrib dist
|
||||
${MAKE} -C have DESTDIR=${PWD}/.dist/${DISTNAME}/have dist
|
||||
${MAKE} -C keys DESTDIR=${PWD}/.dist/${DISTNAME}/keys dist
|
||||
${MAKE} -C regress DESTDIR=${PWD}/.dist/${DISTNAME}/regress dist
|
||||
cd .dist/ && tar zcf ../$@ ${DISTNAME}
|
||||
rm -rf .dist/
|
||||
|
||||
# -- dependencies --
|
||||
|
||||
-include config.d
|
||||
-include crypto.d
|
||||
-include dirs.d
|
||||
-include fcgi.d
|
||||
-include ge.d
|
||||
-include gg.d
|
||||
-include gmid.d
|
||||
-include iri.d
|
||||
-include log.d
|
||||
-include logger.d
|
||||
-include mime.d
|
||||
-include proc.d
|
||||
-include proxy.d
|
||||
-include puny.d
|
||||
-include sandbox.d
|
||||
-include server.d
|
||||
-include titan.d
|
||||
-include utf8.d
|
||||
-include utils.d
|
||||
-include y.tab.d
|
||||
|
|
192
README.md
192
README.md
|
@ -1,97 +1,147 @@
|
|||
# gmid
|
||||
|
||||
# NAME
|
||||
gmid is a full-featured Gemini server written with security in mind.
|
||||
It can serve static files, has optional FastCGI and proxying support,
|
||||
and a rich configuration syntax.
|
||||
|
||||
**gmid** - dead simple zero configuration gemini server
|
||||
A few helper programs are shipped as part of gmid:
|
||||
|
||||
# SYNOPSIS
|
||||
- `gg` is a simple command-line Gemini client.
|
||||
|
||||
**gmid**
|
||||
\[**-h**]
|
||||
\[**-c** *cert.pem*]
|
||||
\[**-d** *docs*]
|
||||
\[**-k** *key.pem*]
|
||||
- `gemexp` is a stripped-down config-less version of gmid to quickly
|
||||
serve a directory from the command line.
|
||||
|
||||
# DESCRIPTION
|
||||
- `titan` is a command-line titan client.
|
||||
|
||||
**gmid**
|
||||
is a very simple and minimal gemini server.
|
||||
It only supports serving static content, and strive to be as simple as
|
||||
possible.
|
||||
|
||||
**gmid**
|
||||
will strip any sequence of
|
||||
*../*
|
||||
or trailing
|
||||
*..*
|
||||
in the requests made by clients, so it's impossible to serve content
|
||||
outside the
|
||||
*docs*
|
||||
directory by mistake, and will also refuse to follow symlink.
|
||||
Furthermore, on
|
||||
OpenBSD,
|
||||
pledge(2)
|
||||
and
|
||||
unveil(2)
|
||||
are used to ensure that
|
||||
**gmid**
|
||||
dosen't do anything else than read files from the given directory and
|
||||
accept network connections.
|
||||
## Internationalisation (IRIs, UNICODE, punycode, all that stuff)
|
||||
|
||||
It should be noted that
|
||||
**gmid**
|
||||
is very simple in its implementation, and so it may not be appropriate
|
||||
for serving site with lots of users.
|
||||
After all, the code is single threaded and use a single process.
|
||||
Even thought the current Gemini specification doesn't mention anything
|
||||
in this regard, I do think these are important things and so I tried
|
||||
to implement them in the most user-friendly way I could think of.
|
||||
|
||||
If a user request path is a directory,
|
||||
**gmid**
|
||||
will try to serve a
|
||||
*index.gmi*
|
||||
file inside that directory.
|
||||
If not found, it will return an error 51 (not found) to the user.
|
||||
For starters, gmid has full support for IRI (RFC3987 —
|
||||
Internationalized Resource Identifiers). IRIs are a superset of URIs,
|
||||
so there aren't incompatibilities with URI-only clients.
|
||||
|
||||
The options are as follows:
|
||||
There is full support also for punycode. In theory, the user doesn't
|
||||
even need to know that punycode is a thing. The hostname in the
|
||||
configuration file can (and must be) in the decoded form (e.g. `naïve`
|
||||
and not `xn--nave-6pa`), gmid will do the rest.
|
||||
|
||||
**-c** *cert.pem*
|
||||
The only missing piece is UNICODE normalisation of the IRI path: gmid
|
||||
doesn't do that (yet).
|
||||
|
||||
> The certificate to use, by default is
|
||||
> *cert.pem*.
|
||||
|
||||
**-d** *docs*
|
||||
## Configuration
|
||||
|
||||
> The root directory to serve.
|
||||
> **gmid**
|
||||
> won't serve any file that is outside that directory.
|
||||
[httpd]: https://man.openbsd.org/httpd.8
|
||||
|
||||
**-h**
|
||||
gmid has a rich configuration file, heavily inspired by OpenBSD'
|
||||
[httpd(8)][httpd], with every detail carefully documented in the
|
||||
manpage. Here's a minimal example of a config file:
|
||||
|
||||
> Print the usage and exit.
|
||||
```conf
|
||||
server "example.com" {
|
||||
listen on * port 1965
|
||||
cert "/path/to/cert.pem"
|
||||
key "/path/to/key.pem"
|
||||
root "/var/gemini/example.com"
|
||||
}
|
||||
```
|
||||
|
||||
**-k** *key.pem*
|
||||
and a slightly more complex one
|
||||
|
||||
> The key for the certificate, by default is
|
||||
> *key.pem*.
|
||||
```conf
|
||||
cert_root = "/path/to/keys"
|
||||
|
||||
# EXAMPLES
|
||||
server "example.com" {
|
||||
listen on * port 1965
|
||||
|
||||
To quickly getting started
|
||||
alias "foobar.com"
|
||||
|
||||
$ # generate a cert and a key
|
||||
$ openssl req -x509 -newkey rsa:4096 -keyout key.pem \
|
||||
-out cert.pem -days 365 -nodes
|
||||
$ mkdir docs
|
||||
$ cat <<EOF > docs/index.gmi
|
||||
# Hello world
|
||||
test paragraph...
|
||||
EOF
|
||||
$ gmid -c cert.pem -k key.pem -d docs
|
||||
cert $cert_root "/example.com.crt"
|
||||
key $cert_root "/example.com.pem"
|
||||
root "/var/gemini/example.com"
|
||||
|
||||
now you can visit gemini://localhost/ with your preferred gemini client.
|
||||
# lang for text/gemini files
|
||||
lang "en"
|
||||
|
||||
# CAVEATS
|
||||
# only for locations that matches /files/*
|
||||
location "/files/*" {
|
||||
# generate directory listings
|
||||
auto index on
|
||||
}
|
||||
|
||||
* it doesn't support virtual hosts: the host part of the request URL is
|
||||
completely ignored.
|
||||
location "/repo/*" {
|
||||
# change the index file name
|
||||
index "README.gmi"
|
||||
lang "it"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
* it doesn't fork in the background or anything like that.
|
||||
|
||||
## Building
|
||||
|
||||
gmid depends on libevent2, LibreSSL or OpenSSL, and yacc or GNU bison.
|
||||
|
||||
The build is as simple as
|
||||
|
||||
$ ./configure
|
||||
$ make
|
||||
|
||||
If the configure scripts fails to pick up something, please open an
|
||||
issue or notify me via email.
|
||||
|
||||
To install execute:
|
||||
|
||||
# make install
|
||||
|
||||
Please keep in mind that the master branch, from time to time, may be
|
||||
accidentally broken on some platforms. gmid is developed primarily on
|
||||
OpenBSD/amd64 and commits on the master branch don't get always tested
|
||||
in other OSes. Before tagging a release however, a comprehensive
|
||||
testing on various platform is done to ensure that everything is
|
||||
working as intended.
|
||||
|
||||
|
||||
### Testing
|
||||
|
||||
Execute
|
||||
|
||||
$ make regress
|
||||
|
||||
to start the suite. Keep in mind that the regression tests needs to
|
||||
create a few file inside the `regress` directory and bind the 10965
|
||||
port.
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
Any form of contribution is welcome, not only patches or bug reports.
|
||||
If you have a sample configuration for some specific use-case, a
|
||||
script or anything that could be useful to others, consider adding it
|
||||
to the `contrib` directory.
|
||||
|
||||
|
||||
## Architecture/Security considerations
|
||||
|
||||
The internal architecture was revisited for the 2.0 release. For
|
||||
earlier releases, please refer to previous revision of this file.
|
||||
|
||||
gmid has a privsep design, where the operations done by the daemon are
|
||||
splitted into multiple processes:
|
||||
|
||||
- main: the main process is the only one that keeps the original
|
||||
privileges. It opens the TLS certificates on the behalf of the
|
||||
`server` and `crypto` processes, reloads the configuration upon
|
||||
`SIGHUP` and re-opens the log files upon `SIGUSR1`.
|
||||
|
||||
- logger: handles the logging with syslog and/or local files.
|
||||
|
||||
- server: listens for connections and serves the request. It also
|
||||
speaks FastCGI and do the proxying.
|
||||
|
||||
- crypto: holds the TLS private keys to avoid a compromised `server`
|
||||
process to disclose them.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Omar Polo <op@omarpolo.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
static void vwarn_impl(const char*, va_list);
|
||||
static void vwarnx_impl(const char*, va_list);
|
||||
|
||||
static void
|
||||
vwarn_impl(const char *fmt, va_list ap)
|
||||
{
|
||||
fprintf(stderr, "%s: ", getprogname());
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, ": %s\n", strerror(errno));
|
||||
}
|
||||
|
||||
static void
|
||||
vwarnx_impl(const char *fmt, va_list ap)
|
||||
{
|
||||
fprintf(stderr, "%s: ", getprogname());
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
void
|
||||
err(int ret, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vwarn_impl(fmt, ap);
|
||||
va_end(ap);
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
void
|
||||
errx(int ret, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vwarnx_impl(fmt, ap);
|
||||
va_end(ap);
|
||||
exit(ret);
|
||||
}
|
||||
|
||||
void
|
||||
warn(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vwarn_impl(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
void
|
||||
warnx(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vwarnx_impl(fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* Public domain.
|
||||
* Written by Matthew Dempsky.
|
||||
*/
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
void
|
||||
explicit_bzero(void *buf, size_t len)
|
||||
{
|
||||
memset(buf, 0, len);
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Omar Polo <op@omarpolo.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void
|
||||
freezero(void *ptr, size_t len)
|
||||
{
|
||||
if (ptr == NULL)
|
||||
return;
|
||||
|
||||
memset(ptr, 0, len);
|
||||
free(ptr);
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Omar Polo <op@omarpolo.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* XXX: on linux it's possible to glob("/proc/$pid/fd/ *") to know the
|
||||
* dtablecount. */
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
int
|
||||
getdtablecount(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Omar Polo <op@omarpolo.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
int
|
||||
getdtablesize(void)
|
||||
{
|
||||
return sysconf(_SC_OPEN_MAX);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Omar Polo <op@omarpolo.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#if HAVE_PROGRAM_INVOCATION_SHORT_NAME
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
extern char *program_invocation_short_name;
|
||||
|
||||
const char *
|
||||
getprogname(void)
|
||||
{
|
||||
return program_invocation_short_name;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
const char *
|
||||
getprogname(void)
|
||||
{
|
||||
return "gmid";
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,697 @@
|
|||
/* $OpenBSD: imsg-buffer.c,v 1.18 2023/12/12 15:47:41 claudio Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
|
||||
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "imsg.h"
|
||||
|
||||
static int ibuf_realloc(struct ibuf *, size_t);
|
||||
static void ibuf_enqueue(struct msgbuf *, struct ibuf *);
|
||||
static void ibuf_dequeue(struct msgbuf *, struct ibuf *);
|
||||
static void msgbuf_drain(struct msgbuf *, size_t);
|
||||
|
||||
struct ibuf *
|
||||
ibuf_open(size_t len)
|
||||
{
|
||||
struct ibuf *buf;
|
||||
|
||||
if (len == 0) {
|
||||
errno = EINVAL;
|
||||
return (NULL);
|
||||
}
|
||||
if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
|
||||
return (NULL);
|
||||
if ((buf->buf = calloc(len, 1)) == NULL) {
|
||||
free(buf);
|
||||
return (NULL);
|
||||
}
|
||||
buf->size = buf->max = len;
|
||||
buf->fd = -1;
|
||||
|
||||
return (buf);
|
||||
}
|
||||
|
||||
struct ibuf *
|
||||
ibuf_dynamic(size_t len, size_t max)
|
||||
{
|
||||
struct ibuf *buf;
|
||||
|
||||
if (max == 0 || max < len) {
|
||||
errno = EINVAL;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if ((buf = calloc(1, sizeof(struct ibuf))) == NULL)
|
||||
return (NULL);
|
||||
if (len > 0) {
|
||||
if ((buf->buf = calloc(len, 1)) == NULL) {
|
||||
free(buf);
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
buf->size = len;
|
||||
buf->max = max;
|
||||
buf->fd = -1;
|
||||
|
||||
return (buf);
|
||||
}
|
||||
|
||||
static int
|
||||
ibuf_realloc(struct ibuf *buf, size_t len)
|
||||
{
|
||||
unsigned char *b;
|
||||
|
||||
/* on static buffers max is eq size and so the following fails */
|
||||
if (len > SIZE_MAX - buf->wpos || buf->wpos + len > buf->max) {
|
||||
errno = ERANGE;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
b = recallocarray(buf->buf, buf->size, buf->wpos + len, 1);
|
||||
if (b == NULL)
|
||||
return (-1);
|
||||
buf->buf = b;
|
||||
buf->size = buf->wpos + len;
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void *
|
||||
ibuf_reserve(struct ibuf *buf, size_t len)
|
||||
{
|
||||
void *b;
|
||||
|
||||
if (len > SIZE_MAX - buf->wpos || buf->max == 0) {
|
||||
errno = ERANGE;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if (buf->wpos + len > buf->size)
|
||||
if (ibuf_realloc(buf, len) == -1)
|
||||
return (NULL);
|
||||
|
||||
b = buf->buf + buf->wpos;
|
||||
buf->wpos += len;
|
||||
return (b);
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_add(struct ibuf *buf, const void *data, size_t len)
|
||||
{
|
||||
void *b;
|
||||
|
||||
if ((b = ibuf_reserve(buf, len)) == NULL)
|
||||
return (-1);
|
||||
|
||||
memcpy(b, data, len);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_add_ibuf(struct ibuf *buf, const struct ibuf *from)
|
||||
{
|
||||
return ibuf_add(buf, ibuf_data(from), ibuf_size(from));
|
||||
}
|
||||
|
||||
/* remove after tree is converted */
|
||||
int
|
||||
ibuf_add_buf(struct ibuf *buf, const struct ibuf *from)
|
||||
{
|
||||
return ibuf_add_ibuf(buf, from);
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_add_n8(struct ibuf *buf, uint64_t value)
|
||||
{
|
||||
uint8_t v;
|
||||
|
||||
if (value > UINT8_MAX) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
v = value;
|
||||
return ibuf_add(buf, &v, sizeof(v));
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_add_n16(struct ibuf *buf, uint64_t value)
|
||||
{
|
||||
uint16_t v;
|
||||
|
||||
if (value > UINT16_MAX) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
v = htobe16(value);
|
||||
return ibuf_add(buf, &v, sizeof(v));
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_add_n32(struct ibuf *buf, uint64_t value)
|
||||
{
|
||||
uint32_t v;
|
||||
|
||||
if (value > UINT32_MAX) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
v = htobe32(value);
|
||||
return ibuf_add(buf, &v, sizeof(v));
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_add_n64(struct ibuf *buf, uint64_t value)
|
||||
{
|
||||
value = htobe64(value);
|
||||
return ibuf_add(buf, &value, sizeof(value));
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_add_h16(struct ibuf *buf, uint64_t value)
|
||||
{
|
||||
uint16_t v;
|
||||
|
||||
if (value > UINT16_MAX) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
v = value;
|
||||
return ibuf_add(buf, &v, sizeof(v));
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_add_h32(struct ibuf *buf, uint64_t value)
|
||||
{
|
||||
uint32_t v;
|
||||
|
||||
if (value > UINT32_MAX) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
v = value;
|
||||
return ibuf_add(buf, &v, sizeof(v));
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_add_h64(struct ibuf *buf, uint64_t value)
|
||||
{
|
||||
return ibuf_add(buf, &value, sizeof(value));
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_add_zero(struct ibuf *buf, size_t len)
|
||||
{
|
||||
void *b;
|
||||
|
||||
if ((b = ibuf_reserve(buf, len)) == NULL)
|
||||
return (-1);
|
||||
memset(b, 0, len);
|
||||
return (0);
|
||||
}
|
||||
|
||||
void *
|
||||
ibuf_seek(struct ibuf *buf, size_t pos, size_t len)
|
||||
{
|
||||
/* only allow seeking between rpos and wpos */
|
||||
if (ibuf_size(buf) < pos || SIZE_MAX - pos < len ||
|
||||
ibuf_size(buf) < pos + len) {
|
||||
errno = ERANGE;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
return (buf->buf + buf->rpos + pos);
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_set(struct ibuf *buf, size_t pos, const void *data, size_t len)
|
||||
{
|
||||
void *b;
|
||||
|
||||
if ((b = ibuf_seek(buf, pos, len)) == NULL)
|
||||
return (-1);
|
||||
|
||||
memcpy(b, data, len);
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_set_n8(struct ibuf *buf, size_t pos, uint64_t value)
|
||||
{
|
||||
uint8_t v;
|
||||
|
||||
if (value > UINT8_MAX) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
v = value;
|
||||
return (ibuf_set(buf, pos, &v, sizeof(v)));
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_set_n16(struct ibuf *buf, size_t pos, uint64_t value)
|
||||
{
|
||||
uint16_t v;
|
||||
|
||||
if (value > UINT16_MAX) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
v = htobe16(value);
|
||||
return (ibuf_set(buf, pos, &v, sizeof(v)));
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_set_n32(struct ibuf *buf, size_t pos, uint64_t value)
|
||||
{
|
||||
uint32_t v;
|
||||
|
||||
if (value > UINT32_MAX) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
v = htobe32(value);
|
||||
return (ibuf_set(buf, pos, &v, sizeof(v)));
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_set_n64(struct ibuf *buf, size_t pos, uint64_t value)
|
||||
{
|
||||
value = htobe64(value);
|
||||
return (ibuf_set(buf, pos, &value, sizeof(value)));
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_set_h16(struct ibuf *buf, size_t pos, uint64_t value)
|
||||
{
|
||||
uint16_t v;
|
||||
|
||||
if (value > UINT16_MAX) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
v = value;
|
||||
return (ibuf_set(buf, pos, &v, sizeof(v)));
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_set_h32(struct ibuf *buf, size_t pos, uint64_t value)
|
||||
{
|
||||
uint32_t v;
|
||||
|
||||
if (value > UINT32_MAX) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
v = value;
|
||||
return (ibuf_set(buf, pos, &v, sizeof(v)));
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_set_h64(struct ibuf *buf, size_t pos, uint64_t value)
|
||||
{
|
||||
return (ibuf_set(buf, pos, &value, sizeof(value)));
|
||||
}
|
||||
|
||||
void *
|
||||
ibuf_data(const struct ibuf *buf)
|
||||
{
|
||||
return (buf->buf + buf->rpos);
|
||||
}
|
||||
|
||||
size_t
|
||||
ibuf_size(const struct ibuf *buf)
|
||||
{
|
||||
return (buf->wpos - buf->rpos);
|
||||
}
|
||||
|
||||
size_t
|
||||
ibuf_left(const struct ibuf *buf)
|
||||
{
|
||||
if (buf->max == 0)
|
||||
return (0);
|
||||
return (buf->max - buf->wpos);
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_truncate(struct ibuf *buf, size_t len)
|
||||
{
|
||||
if (ibuf_size(buf) >= len) {
|
||||
buf->wpos = buf->rpos + len;
|
||||
return (0);
|
||||
}
|
||||
if (buf->max == 0) {
|
||||
/* only allow to truncate down */
|
||||
errno = ERANGE;
|
||||
return (-1);
|
||||
}
|
||||
return ibuf_add_zero(buf, len - ibuf_size(buf));
|
||||
}
|
||||
|
||||
void
|
||||
ibuf_rewind(struct ibuf *buf)
|
||||
{
|
||||
buf->rpos = 0;
|
||||
}
|
||||
|
||||
void
|
||||
ibuf_close(struct msgbuf *msgbuf, struct ibuf *buf)
|
||||
{
|
||||
ibuf_enqueue(msgbuf, buf);
|
||||
}
|
||||
|
||||
void
|
||||
ibuf_from_buffer(struct ibuf *buf, void *data, size_t len)
|
||||
{
|
||||
memset(buf, 0, sizeof(*buf));
|
||||
buf->buf = data;
|
||||
buf->size = buf->wpos = len;
|
||||
buf->fd = -1;
|
||||
}
|
||||
|
||||
void
|
||||
ibuf_from_ibuf(struct ibuf *buf, const struct ibuf *from)
|
||||
{
|
||||
ibuf_from_buffer(buf, ibuf_data(from), ibuf_size(from));
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_get(struct ibuf *buf, void *data, size_t len)
|
||||
{
|
||||
if (ibuf_size(buf) < len) {
|
||||
errno = EBADMSG;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
memcpy(data, ibuf_data(buf), len);
|
||||
buf->rpos += len;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_get_ibuf(struct ibuf *buf, size_t len, struct ibuf *new)
|
||||
{
|
||||
if (ibuf_size(buf) < len) {
|
||||
errno = EBADMSG;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
ibuf_from_buffer(new, ibuf_data(buf), len);
|
||||
buf->rpos += len;
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_get_n8(struct ibuf *buf, uint8_t *value)
|
||||
{
|
||||
return ibuf_get(buf, value, sizeof(*value));
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_get_n16(struct ibuf *buf, uint16_t *value)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = ibuf_get(buf, value, sizeof(*value));
|
||||
*value = be16toh(*value);
|
||||
return (rv);
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_get_n32(struct ibuf *buf, uint32_t *value)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = ibuf_get(buf, value, sizeof(*value));
|
||||
*value = be32toh(*value);
|
||||
return (rv);
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_get_n64(struct ibuf *buf, uint64_t *value)
|
||||
{
|
||||
int rv;
|
||||
|
||||
rv = ibuf_get(buf, value, sizeof(*value));
|
||||
*value = be64toh(*value);
|
||||
return (rv);
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_get_h16(struct ibuf *buf, uint16_t *value)
|
||||
{
|
||||
return ibuf_get(buf, value, sizeof(*value));
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_get_h32(struct ibuf *buf, uint32_t *value)
|
||||
{
|
||||
return ibuf_get(buf, value, sizeof(*value));
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_get_h64(struct ibuf *buf, uint64_t *value)
|
||||
{
|
||||
return ibuf_get(buf, value, sizeof(*value));
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_skip(struct ibuf *buf, size_t len)
|
||||
{
|
||||
if (ibuf_size(buf) < len) {
|
||||
errno = EBADMSG;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
buf->rpos += len;
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
ibuf_free(struct ibuf *buf)
|
||||
{
|
||||
if (buf == NULL)
|
||||
return;
|
||||
if (buf->max == 0) /* if buf lives on the stack */
|
||||
abort(); /* abort before causing more harm */
|
||||
if (buf->fd != -1)
|
||||
close(buf->fd);
|
||||
freezero(buf->buf, buf->size);
|
||||
free(buf);
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_fd_avail(struct ibuf *buf)
|
||||
{
|
||||
return (buf->fd != -1);
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_fd_get(struct ibuf *buf)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = buf->fd;
|
||||
buf->fd = -1;
|
||||
return (fd);
|
||||
}
|
||||
|
||||
void
|
||||
ibuf_fd_set(struct ibuf *buf, int fd)
|
||||
{
|
||||
if (buf->max == 0) /* if buf lives on the stack */
|
||||
abort(); /* abort before causing more harm */
|
||||
if (buf->fd != -1)
|
||||
close(buf->fd);
|
||||
buf->fd = fd;
|
||||
}
|
||||
|
||||
int
|
||||
ibuf_write(struct msgbuf *msgbuf)
|
||||
{
|
||||
struct iovec iov[IOV_MAX];
|
||||
struct ibuf *buf;
|
||||
unsigned int i = 0;
|
||||
ssize_t n;
|
||||
|
||||
memset(&iov, 0, sizeof(iov));
|
||||
TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
|
||||
if (i >= IOV_MAX)
|
||||
break;
|
||||
iov[i].iov_base = ibuf_data(buf);
|
||||
iov[i].iov_len = ibuf_size(buf);
|
||||
i++;
|
||||
}
|
||||
|
||||
again:
|
||||
if ((n = writev(msgbuf->fd, iov, i)) == -1) {
|
||||
if (errno == EINTR)
|
||||
goto again;
|
||||
if (errno == ENOBUFS)
|
||||
errno = EAGAIN;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (n == 0) { /* connection closed */
|
||||
errno = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
msgbuf_drain(msgbuf, n);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
void
|
||||
msgbuf_init(struct msgbuf *msgbuf)
|
||||
{
|
||||
msgbuf->queued = 0;
|
||||
msgbuf->fd = -1;
|
||||
TAILQ_INIT(&msgbuf->bufs);
|
||||
}
|
||||
|
||||
static void
|
||||
msgbuf_drain(struct msgbuf *msgbuf, size_t n)
|
||||
{
|
||||
struct ibuf *buf, *next;
|
||||
|
||||
for (buf = TAILQ_FIRST(&msgbuf->bufs); buf != NULL && n > 0;
|
||||
buf = next) {
|
||||
next = TAILQ_NEXT(buf, entry);
|
||||
if (n >= ibuf_size(buf)) {
|
||||
n -= ibuf_size(buf);
|
||||
ibuf_dequeue(msgbuf, buf);
|
||||
} else {
|
||||
buf->rpos += n;
|
||||
n = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
msgbuf_clear(struct msgbuf *msgbuf)
|
||||
{
|
||||
struct ibuf *buf;
|
||||
|
||||
while ((buf = TAILQ_FIRST(&msgbuf->bufs)) != NULL)
|
||||
ibuf_dequeue(msgbuf, buf);
|
||||
}
|
||||
|
||||
int
|
||||
msgbuf_write(struct msgbuf *msgbuf)
|
||||
{
|
||||
struct iovec iov[IOV_MAX];
|
||||
struct ibuf *buf, *buf0 = NULL;
|
||||
unsigned int i = 0;
|
||||
ssize_t n;
|
||||
struct msghdr msg;
|
||||
struct cmsghdr *cmsg;
|
||||
union {
|
||||
struct cmsghdr hdr;
|
||||
char buf[CMSG_SPACE(sizeof(int))];
|
||||
} cmsgbuf;
|
||||
|
||||
memset(&iov, 0, sizeof(iov));
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
memset(&cmsgbuf, 0, sizeof(cmsgbuf));
|
||||
TAILQ_FOREACH(buf, &msgbuf->bufs, entry) {
|
||||
if (i >= IOV_MAX)
|
||||
break;
|
||||
if (i > 0 && buf->fd != -1)
|
||||
break;
|
||||
iov[i].iov_base = ibuf_data(buf);
|
||||
iov[i].iov_len = ibuf_size(buf);
|
||||
i++;
|
||||
if (buf->fd != -1)
|
||||
buf0 = buf;
|
||||
}
|
||||
|
||||
msg.msg_iov = iov;
|
||||
msg.msg_iovlen = i;
|
||||
|
||||
if (buf0 != NULL) {
|
||||
msg.msg_control = (caddr_t)&cmsgbuf.buf;
|
||||
msg.msg_controllen = sizeof(cmsgbuf.buf);
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
*(int *)CMSG_DATA(cmsg) = buf0->fd;
|
||||
}
|
||||
|
||||
again:
|
||||
if ((n = sendmsg(msgbuf->fd, &msg, 0)) == -1) {
|
||||
if (errno == EINTR)
|
||||
goto again;
|
||||
if (errno == ENOBUFS)
|
||||
errno = EAGAIN;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (n == 0) { /* connection closed */
|
||||
errno = 0;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* assumption: fd got sent if sendmsg sent anything
|
||||
* this works because fds are passed one at a time
|
||||
*/
|
||||
if (buf0 != NULL) {
|
||||
close(buf0->fd);
|
||||
buf0->fd = -1;
|
||||
}
|
||||
|
||||
msgbuf_drain(msgbuf, n);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
msgbuf_queuelen(struct msgbuf *msgbuf)
|
||||
{
|
||||
return (msgbuf->queued);
|
||||
}
|
||||
|
||||
static void
|
||||
ibuf_enqueue(struct msgbuf *msgbuf, struct ibuf *buf)
|
||||
{
|
||||
if (buf->max == 0) /* if buf lives on the stack */
|
||||
abort(); /* abort before causing more harm */
|
||||
TAILQ_INSERT_TAIL(&msgbuf->bufs, buf, entry);
|
||||
msgbuf->queued++;
|
||||
}
|
||||
|
||||
static void
|
||||
ibuf_dequeue(struct msgbuf *msgbuf, struct ibuf *buf)
|
||||
{
|
||||
TAILQ_REMOVE(&msgbuf->bufs, buf, entry);
|
||||
msgbuf->queued--;
|
||||
ibuf_free(buf);
|
||||
}
|
|
@ -0,0 +1,449 @@
|
|||
/* $OpenBSD: imsg.c,v 1.23 2023/12/12 15:47:41 claudio Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
|
||||
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "imsg.h"
|
||||
|
||||
struct imsg_fd {
|
||||
TAILQ_ENTRY(imsg_fd) entry;
|
||||
int fd;
|
||||
};
|
||||
|
||||
int imsg_fd_overhead = 0;
|
||||
|
||||
static int imsg_dequeue_fd(struct imsgbuf *);
|
||||
|
||||
void
|
||||
imsg_init(struct imsgbuf *imsgbuf, int fd)
|
||||
{
|
||||
msgbuf_init(&imsgbuf->w);
|
||||
memset(&imsgbuf->r, 0, sizeof(imsgbuf->r));
|
||||
imsgbuf->fd = fd;
|
||||
imsgbuf->w.fd = fd;
|
||||
imsgbuf->pid = getpid();
|
||||
TAILQ_INIT(&imsgbuf->fds);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
imsg_read(struct imsgbuf *imsgbuf)
|
||||
{
|
||||
struct msghdr msg;
|
||||
struct cmsghdr *cmsg;
|
||||
union {
|
||||
struct cmsghdr hdr;
|
||||
char buf[CMSG_SPACE(sizeof(int) * 1)];
|
||||
} cmsgbuf;
|
||||
struct iovec iov;
|
||||
ssize_t n = -1;
|
||||
int fd;
|
||||
struct imsg_fd *ifd;
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
memset(&cmsgbuf, 0, sizeof(cmsgbuf));
|
||||
|
||||
iov.iov_base = imsgbuf->r.buf + imsgbuf->r.wpos;
|
||||
iov.iov_len = sizeof(imsgbuf->r.buf) - imsgbuf->r.wpos;
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
msg.msg_control = &cmsgbuf.buf;
|
||||
msg.msg_controllen = sizeof(cmsgbuf.buf);
|
||||
|
||||
if ((ifd = calloc(1, sizeof(struct imsg_fd))) == NULL)
|
||||
return (-1);
|
||||
|
||||
again:
|
||||
if (getdtablecount() + imsg_fd_overhead +
|
||||
(int)((CMSG_SPACE(sizeof(int))-CMSG_SPACE(0))/sizeof(int))
|
||||
>= getdtablesize()) {
|
||||
errno = EAGAIN;
|
||||
free(ifd);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if ((n = recvmsg(imsgbuf->fd, &msg, 0)) == -1) {
|
||||
if (errno == EINTR)
|
||||
goto again;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
imsgbuf->r.wpos += n;
|
||||
|
||||
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
|
||||
cmsg = CMSG_NXTHDR(&msg, cmsg)) {
|
||||
if (cmsg->cmsg_level == SOL_SOCKET &&
|
||||
cmsg->cmsg_type == SCM_RIGHTS) {
|
||||
int i;
|
||||
int j;
|
||||
|
||||
/*
|
||||
* We only accept one file descriptor. Due to C
|
||||
* padding rules, our control buffer might contain
|
||||
* more than one fd, and we must close them.
|
||||
*/
|
||||
j = ((char *)cmsg + cmsg->cmsg_len -
|
||||
(char *)CMSG_DATA(cmsg)) / sizeof(int);
|
||||
for (i = 0; i < j; i++) {
|
||||
fd = ((int *)CMSG_DATA(cmsg))[i];
|
||||
if (ifd != NULL) {
|
||||
ifd->fd = fd;
|
||||
TAILQ_INSERT_TAIL(&imsgbuf->fds, ifd,
|
||||
entry);
|
||||
ifd = NULL;
|
||||
} else
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
/* we do not handle other ctl data level */
|
||||
}
|
||||
|
||||
fail:
|
||||
free(ifd);
|
||||
return (n);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
imsg_get(struct imsgbuf *imsgbuf, struct imsg *imsg)
|
||||
{
|
||||
struct imsg m;
|
||||
size_t av, left, datalen;
|
||||
|
||||
av = imsgbuf->r.wpos;
|
||||
|
||||
if (IMSG_HEADER_SIZE > av)
|
||||
return (0);
|
||||
|
||||
memcpy(&m.hdr, imsgbuf->r.buf, sizeof(m.hdr));
|
||||
if (m.hdr.len < IMSG_HEADER_SIZE ||
|
||||
m.hdr.len > MAX_IMSGSIZE) {
|
||||
errno = ERANGE;
|
||||
return (-1);
|
||||
}
|
||||
if (m.hdr.len > av)
|
||||
return (0);
|
||||
|
||||
m.fd = -1;
|
||||
m.buf = NULL;
|
||||
m.data = NULL;
|
||||
|
||||
datalen = m.hdr.len - IMSG_HEADER_SIZE;
|
||||
imsgbuf->r.rptr = imsgbuf->r.buf + IMSG_HEADER_SIZE;
|
||||
if (datalen != 0) {
|
||||
if ((m.buf = ibuf_open(datalen)) == NULL)
|
||||
return (-1);
|
||||
if (ibuf_add(m.buf, imsgbuf->r.rptr, datalen) == -1) {
|
||||
/* this should never fail */
|
||||
ibuf_free(m.buf);
|
||||
return (-1);
|
||||
}
|
||||
m.data = ibuf_data(m.buf);
|
||||
}
|
||||
|
||||
if (m.hdr.flags & IMSGF_HASFD)
|
||||
m.fd = imsg_dequeue_fd(imsgbuf);
|
||||
|
||||
if (m.hdr.len < av) {
|
||||
left = av - m.hdr.len;
|
||||
memmove(&imsgbuf->r.buf, imsgbuf->r.buf + m.hdr.len, left);
|
||||
imsgbuf->r.wpos = left;
|
||||
} else
|
||||
imsgbuf->r.wpos = 0;
|
||||
|
||||
*imsg = m;
|
||||
return (datalen + IMSG_HEADER_SIZE);
|
||||
}
|
||||
|
||||
int
|
||||
imsg_get_ibuf(struct imsg *imsg, struct ibuf *ibuf)
|
||||
{
|
||||
if (imsg->buf == NULL) {
|
||||
errno = EBADMSG;
|
||||
return (-1);
|
||||
}
|
||||
return ibuf_get_ibuf(imsg->buf, ibuf_size(imsg->buf), ibuf);
|
||||
}
|
||||
|
||||
int
|
||||
imsg_get_data(struct imsg *imsg, void *data, size_t len)
|
||||
{
|
||||
if (len == 0) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
if (imsg->buf == NULL || ibuf_size(imsg->buf) != len) {
|
||||
errno = EBADMSG;
|
||||
return (-1);
|
||||
}
|
||||
return ibuf_get(imsg->buf, data, len);
|
||||
}
|
||||
|
||||
int
|
||||
imsg_get_fd(struct imsg *imsg)
|
||||
{
|
||||
int fd = imsg->fd;
|
||||
|
||||
imsg->fd = -1;
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
imsg_get_id(struct imsg *imsg)
|
||||
{
|
||||
return (imsg->hdr.peerid);
|
||||
}
|
||||
|
||||
size_t
|
||||
imsg_get_len(struct imsg *imsg)
|
||||
{
|
||||
if (imsg->buf == NULL)
|
||||
return 0;
|
||||
return ibuf_size(imsg->buf);
|
||||
}
|
||||
|
||||
pid_t
|
||||
imsg_get_pid(struct imsg *imsg)
|
||||
{
|
||||
return (imsg->hdr.pid);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
imsg_get_type(struct imsg *imsg)
|
||||
{
|
||||
return (imsg->hdr.type);
|
||||
}
|
||||
|
||||
int
|
||||
imsg_compose(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
|
||||
int fd, const void *data, size_t datalen)
|
||||
{
|
||||
struct ibuf *wbuf;
|
||||
|
||||
if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
|
||||
return (-1);
|
||||
|
||||
if (imsg_add(wbuf, data, datalen) == -1)
|
||||
return (-1);
|
||||
|
||||
ibuf_fd_set(wbuf, fd);
|
||||
imsg_close(imsgbuf, wbuf);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
int
|
||||
imsg_composev(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
|
||||
int fd, const struct iovec *iov, int iovcnt)
|
||||
{
|
||||
struct ibuf *wbuf;
|
||||
int i;
|
||||
size_t datalen = 0;
|
||||
|
||||
for (i = 0; i < iovcnt; i++)
|
||||
datalen += iov[i].iov_len;
|
||||
|
||||
if ((wbuf = imsg_create(imsgbuf, type, id, pid, datalen)) == NULL)
|
||||
return (-1);
|
||||
|
||||
for (i = 0; i < iovcnt; i++)
|
||||
if (imsg_add(wbuf, iov[i].iov_base, iov[i].iov_len) == -1)
|
||||
return (-1);
|
||||
|
||||
ibuf_fd_set(wbuf, fd);
|
||||
imsg_close(imsgbuf, wbuf);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Enqueue imsg with payload from ibuf buf. fd passing is not possible
|
||||
* with this function.
|
||||
*/
|
||||
int
|
||||
imsg_compose_ibuf(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id,
|
||||
pid_t pid, struct ibuf *buf)
|
||||
{
|
||||
struct ibuf *hdrbuf = NULL;
|
||||
struct imsg_hdr hdr;
|
||||
int save_errno;
|
||||
|
||||
if (ibuf_size(buf) + IMSG_HEADER_SIZE > MAX_IMSGSIZE) {
|
||||
errno = ERANGE;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hdr.type = type;
|
||||
hdr.len = ibuf_size(buf) + IMSG_HEADER_SIZE;
|
||||
hdr.flags = 0;
|
||||
hdr.peerid = id;
|
||||
if ((hdr.pid = pid) == 0)
|
||||
hdr.pid = imsgbuf->pid;
|
||||
|
||||
if ((hdrbuf = ibuf_open(IMSG_HEADER_SIZE)) == NULL)
|
||||
goto fail;
|
||||
if (imsg_add(hdrbuf, &hdr, sizeof(hdr)) == -1)
|
||||
goto fail;
|
||||
|
||||
ibuf_close(&imsgbuf->w, hdrbuf);
|
||||
ibuf_close(&imsgbuf->w, buf);
|
||||
return (1);
|
||||
|
||||
fail:
|
||||
save_errno = errno;
|
||||
ibuf_free(buf);
|
||||
ibuf_free(hdrbuf);
|
||||
errno = save_errno;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Forward imsg to another channel. Any attached fd is closed.
|
||||
*/
|
||||
int
|
||||
imsg_forward(struct imsgbuf *imsgbuf, struct imsg *msg)
|
||||
{
|
||||
struct ibuf *wbuf;
|
||||
size_t len = 0;
|
||||
|
||||
if (msg->fd != -1) {
|
||||
close(msg->fd);
|
||||
msg->fd = -1;
|
||||
}
|
||||
|
||||
if (msg->buf != NULL) {
|
||||
ibuf_rewind(msg->buf);
|
||||
len = ibuf_size(msg->buf);
|
||||
}
|
||||
|
||||
if ((wbuf = imsg_create(imsgbuf, msg->hdr.type, msg->hdr.peerid,
|
||||
msg->hdr.pid, len)) == NULL)
|
||||
return (-1);
|
||||
|
||||
if (msg->buf != NULL) {
|
||||
if (ibuf_add_buf(wbuf, msg->buf) == -1) {
|
||||
ibuf_free(wbuf);
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
imsg_close(imsgbuf, wbuf);
|
||||
return (1);
|
||||
}
|
||||
|
||||
struct ibuf *
|
||||
imsg_create(struct imsgbuf *imsgbuf, uint32_t type, uint32_t id, pid_t pid,
|
||||
size_t datalen)
|
||||
{
|
||||
struct ibuf *wbuf;
|
||||
struct imsg_hdr hdr;
|
||||
|
||||
datalen += IMSG_HEADER_SIZE;
|
||||
if (datalen > MAX_IMSGSIZE) {
|
||||
errno = ERANGE;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
hdr.type = type;
|
||||
hdr.flags = 0;
|
||||
hdr.peerid = id;
|
||||
if ((hdr.pid = pid) == 0)
|
||||
hdr.pid = imsgbuf->pid;
|
||||
if ((wbuf = ibuf_dynamic(datalen, MAX_IMSGSIZE)) == NULL) {
|
||||
return (NULL);
|
||||
}
|
||||
if (imsg_add(wbuf, &hdr, sizeof(hdr)) == -1)
|
||||
return (NULL);
|
||||
|
||||
return (wbuf);
|
||||
}
|
||||
|
||||
int
|
||||
imsg_add(struct ibuf *msg, const void *data, size_t datalen)
|
||||
{
|
||||
if (datalen)
|
||||
if (ibuf_add(msg, data, datalen) == -1) {
|
||||
ibuf_free(msg);
|
||||
return (-1);
|
||||
}
|
||||
return (datalen);
|
||||
}
|
||||
|
||||
void
|
||||
imsg_close(struct imsgbuf *imsgbuf, struct ibuf *msg)
|
||||
{
|
||||
struct imsg_hdr *hdr;
|
||||
|
||||
hdr = (struct imsg_hdr *)msg->buf;
|
||||
|
||||
hdr->flags &= ~IMSGF_HASFD;
|
||||
if (ibuf_fd_avail(msg))
|
||||
hdr->flags |= IMSGF_HASFD;
|
||||
hdr->len = ibuf_size(msg);
|
||||
|
||||
ibuf_close(&imsgbuf->w, msg);
|
||||
}
|
||||
|
||||
void
|
||||
imsg_free(struct imsg *imsg)
|
||||
{
|
||||
ibuf_free(imsg->buf);
|
||||
}
|
||||
|
||||
static int
|
||||
imsg_dequeue_fd(struct imsgbuf *imsgbuf)
|
||||
{
|
||||
int fd;
|
||||
struct imsg_fd *ifd;
|
||||
|
||||
if ((ifd = TAILQ_FIRST(&imsgbuf->fds)) == NULL)
|
||||
return (-1);
|
||||
|
||||
fd = ifd->fd;
|
||||
TAILQ_REMOVE(&imsgbuf->fds, ifd, entry);
|
||||
free(ifd);
|
||||
|
||||
return (fd);
|
||||
}
|
||||
|
||||
int
|
||||
imsg_flush(struct imsgbuf *imsgbuf)
|
||||
{
|
||||
while (imsgbuf->w.queued)
|
||||
if (msgbuf_write(&imsgbuf->w) <= 0)
|
||||
return (-1);
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
imsg_clear(struct imsgbuf *imsgbuf)
|
||||
{
|
||||
int fd;
|
||||
|
||||
msgbuf_clear(&imsgbuf->w);
|
||||
while ((fd = imsg_dequeue_fd(imsgbuf)) != -1)
|
||||
close(fd);
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
/* $OpenBSD: imsg.h,v 1.8 2023/12/12 15:47:41 claudio Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2023 Claudio Jeker <claudio@openbsd.org>
|
||||
* Copyright (c) 2006, 2007 Pierre-Yves Ritschard <pyr@openbsd.org>
|
||||
* Copyright (c) 2006, 2007, 2008 Reyk Floeter <reyk@openbsd.org>
|
||||
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _IMSG_H_
|
||||
#define _IMSG_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#define IBUF_READ_SIZE 65535
|
||||
#define IMSG_HEADER_SIZE sizeof(struct imsg_hdr)
|
||||
#define MAX_IMSGSIZE 16384
|
||||
|
||||
struct ibuf {
|
||||
TAILQ_ENTRY(ibuf) entry;
|
||||
unsigned char *buf;
|
||||
size_t size;
|
||||
size_t max;
|
||||
size_t wpos;
|
||||
size_t rpos;
|
||||
int fd;
|
||||
};
|
||||
|
||||
struct msgbuf {
|
||||
TAILQ_HEAD(, ibuf) bufs;
|
||||
uint32_t queued;
|
||||
int fd;
|
||||
};
|
||||
|
||||
struct ibuf_read {
|
||||
unsigned char buf[IBUF_READ_SIZE];
|
||||
unsigned char *rptr;
|
||||
size_t wpos;
|
||||
};
|
||||
|
||||
struct imsg_fd;
|
||||
struct imsgbuf {
|
||||
TAILQ_HEAD(, imsg_fd) fds;
|
||||
struct ibuf_read r;
|
||||
struct msgbuf w;
|
||||
int fd;
|
||||
pid_t pid;
|
||||
};
|
||||
|
||||
#define IMSGF_HASFD 1
|
||||
|
||||
struct imsg_hdr {
|
||||
uint32_t type;
|
||||
uint16_t len;
|
||||
uint16_t flags;
|
||||
uint32_t peerid;
|
||||
uint32_t pid;
|
||||
};
|
||||
|
||||
struct imsg {
|
||||
struct imsg_hdr hdr;
|
||||
int fd;
|
||||
void *data;
|
||||
struct ibuf *buf;
|
||||
};
|
||||
|
||||
struct iovec;
|
||||
|
||||
/* imsg-buffer.c */
|
||||
struct ibuf *ibuf_open(size_t);
|
||||
struct ibuf *ibuf_dynamic(size_t, size_t);
|
||||
int ibuf_add(struct ibuf *, const void *, size_t);
|
||||
int ibuf_add_buf(struct ibuf *, const struct ibuf *);
|
||||
int ibuf_add_ibuf(struct ibuf *, const struct ibuf *);
|
||||
int ibuf_add_zero(struct ibuf *, size_t);
|
||||
int ibuf_add_n8(struct ibuf *, uint64_t);
|
||||
int ibuf_add_n16(struct ibuf *, uint64_t);
|
||||
int ibuf_add_n32(struct ibuf *, uint64_t);
|
||||
int ibuf_add_n64(struct ibuf *, uint64_t);
|
||||
int ibuf_add_h16(struct ibuf *, uint64_t);
|
||||
int ibuf_add_h32(struct ibuf *, uint64_t);
|
||||
int ibuf_add_h64(struct ibuf *, uint64_t);
|
||||
void *ibuf_reserve(struct ibuf *, size_t);
|
||||
void *ibuf_seek(struct ibuf *, size_t, size_t);
|
||||
int ibuf_set(struct ibuf *, size_t, const void *, size_t);
|
||||
int ibuf_set_n8(struct ibuf *, size_t, uint64_t);
|
||||
int ibuf_set_n16(struct ibuf *, size_t, uint64_t);
|
||||
int ibuf_set_n32(struct ibuf *, size_t, uint64_t);
|
||||
int ibuf_set_n64(struct ibuf *, size_t, uint64_t);
|
||||
int ibuf_set_h16(struct ibuf *, size_t, uint64_t);
|
||||
int ibuf_set_h32(struct ibuf *, size_t, uint64_t);
|
||||
int ibuf_set_h64(struct ibuf *, size_t, uint64_t);
|
||||
void *ibuf_data(const struct ibuf *);
|
||||
size_t ibuf_size(const struct ibuf *);
|
||||
size_t ibuf_left(const struct ibuf *);
|
||||
int ibuf_truncate(struct ibuf *, size_t);
|
||||
void ibuf_rewind(struct ibuf *);
|
||||
void ibuf_close(struct msgbuf *, struct ibuf *);
|
||||
void ibuf_from_buffer(struct ibuf *, void *, size_t);
|
||||
void ibuf_from_ibuf(struct ibuf *, const struct ibuf *);
|
||||
int ibuf_get(struct ibuf *, void *, size_t);
|
||||
int ibuf_get_ibuf(struct ibuf *, size_t, struct ibuf *);
|
||||
int ibuf_get_n8(struct ibuf *, uint8_t *);
|
||||
int ibuf_get_n16(struct ibuf *, uint16_t *);
|
||||
int ibuf_get_n32(struct ibuf *, uint32_t *);
|
||||
int ibuf_get_n64(struct ibuf *, uint64_t *);
|
||||
int ibuf_get_h16(struct ibuf *, uint16_t *);
|
||||
int ibuf_get_h32(struct ibuf *, uint32_t *);
|
||||
int ibuf_get_h64(struct ibuf *, uint64_t *);
|
||||
int ibuf_skip(struct ibuf *, size_t);
|
||||
void ibuf_free(struct ibuf *);
|
||||
int ibuf_fd_avail(struct ibuf *);
|
||||
int ibuf_fd_get(struct ibuf *);
|
||||
void ibuf_fd_set(struct ibuf *, int);
|
||||
int ibuf_write(struct msgbuf *);
|
||||
void msgbuf_init(struct msgbuf *);
|
||||
void msgbuf_clear(struct msgbuf *);
|
||||
uint32_t msgbuf_queuelen(struct msgbuf *);
|
||||
int msgbuf_write(struct msgbuf *);
|
||||
|
||||
/* imsg.c */
|
||||
void imsg_init(struct imsgbuf *, int);
|
||||
ssize_t imsg_read(struct imsgbuf *);
|
||||
ssize_t imsg_get(struct imsgbuf *, struct imsg *);
|
||||
int imsg_get_ibuf(struct imsg *, struct ibuf *);
|
||||
int imsg_get_data(struct imsg *, void *, size_t);
|
||||
int imsg_get_fd(struct imsg *);
|
||||
uint32_t imsg_get_id(struct imsg *);
|
||||
size_t imsg_get_len(struct imsg *);
|
||||
pid_t imsg_get_pid(struct imsg *);
|
||||
uint32_t imsg_get_type(struct imsg *);
|
||||
int imsg_forward(struct imsgbuf *, struct imsg *);
|
||||
int imsg_compose(struct imsgbuf *, uint32_t, uint32_t, pid_t, int,
|
||||
const void *, size_t);
|
||||
int imsg_composev(struct imsgbuf *, uint32_t, uint32_t, pid_t, int,
|
||||
const struct iovec *, int);
|
||||
int imsg_compose_ibuf(struct imsgbuf *, uint32_t, uint32_t, pid_t,
|
||||
struct ibuf *);
|
||||
struct ibuf *imsg_create(struct imsgbuf *, uint32_t, uint32_t, pid_t, size_t);
|
||||
int imsg_add(struct ibuf *, const void *, size_t);
|
||||
void imsg_close(struct imsgbuf *, struct ibuf *);
|
||||
void imsg_free(struct imsg *);
|
||||
int imsg_flush(struct imsgbuf *);
|
||||
void imsg_clear(struct imsgbuf *);
|
||||
|
||||
#endif
|
|
@ -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,534 @@
|
|||
/* $OpenBSD: queue.h,v 1.45 2018/07/12 14:22:54 sashan Exp $ */
|
||||
/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1991, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* @(#)queue.h 8.5 (Berkeley) 8/20/94
|
||||
*/
|
||||
|
||||
#ifndef _SYS_QUEUE_H_
|
||||
#define _SYS_QUEUE_H_
|
||||
|
||||
/*
|
||||
* This file defines five types of data structures: singly-linked lists,
|
||||
* lists, simple queues, tail queues and XOR simple queues.
|
||||
*
|
||||
*
|
||||
* A singly-linked list is headed by a single forward pointer. The elements
|
||||
* are singly linked for minimum space and pointer manipulation overhead at
|
||||
* the expense of O(n) removal for arbitrary elements. New elements can be
|
||||
* added to the list after an existing element or at the head of the list.
|
||||
* Elements being removed from the head of the list should use the explicit
|
||||
* macro for this purpose for optimum efficiency. A singly-linked list may
|
||||
* only be traversed in the forward direction. Singly-linked lists are ideal
|
||||
* for applications with large datasets and few or no removals or for
|
||||
* implementing a LIFO queue.
|
||||
*
|
||||
* A list is headed by a single forward pointer (or an array of forward
|
||||
* pointers for a hash table header). The elements are doubly linked
|
||||
* so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before
|
||||
* or after an existing element or at the head of the list. A list
|
||||
* may only be traversed in the forward direction.
|
||||
*
|
||||
* A simple queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are singly
|
||||
* linked to save space, so elements can only be removed from the
|
||||
* head of the list. New elements can be added to the list before or after
|
||||
* an existing element, at the head of the list, or at the end of the
|
||||
* list. A simple queue may only be traversed in the forward direction.
|
||||
*
|
||||
* A tail queue is headed by a pair of pointers, one to the head of the
|
||||
* list and the other to the tail of the list. The elements are doubly
|
||||
* linked so that an arbitrary element can be removed without a need to
|
||||
* traverse the list. New elements can be added to the list before or
|
||||
* after an existing element, at the head of the list, or at the end of
|
||||
* the list. A tail queue may be traversed in either direction.
|
||||
*
|
||||
* An XOR simple queue is used in the same way as a regular simple queue.
|
||||
* The difference is that the head structure also includes a "cookie" that
|
||||
* is XOR'd with the queue pointer (first, last or next) to generate the
|
||||
* real pointer value.
|
||||
*
|
||||
* For details on the use of these macros, see the queue(3) manual page.
|
||||
*/
|
||||
|
||||
#if defined(QUEUE_MACRO_DEBUG) || (defined(_KERNEL) && defined(DIAGNOSTIC))
|
||||
#define _Q_INVALID ((void *)-1)
|
||||
#define _Q_INVALIDATE(a) (a) = _Q_INVALID
|
||||
#else
|
||||
#define _Q_INVALIDATE(a)
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Singly-linked List definitions.
|
||||
*/
|
||||
#define SLIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *slh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define SLIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define SLIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sle_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Singly-linked List access methods.
|
||||
*/
|
||||
#define SLIST_FIRST(head) ((head)->slh_first)
|
||||
#define SLIST_END(head) NULL
|
||||
#define SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head))
|
||||
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
|
||||
|
||||
#define SLIST_FOREACH(var, head, field) \
|
||||
for((var) = SLIST_FIRST(head); \
|
||||
(var) != SLIST_END(head); \
|
||||
(var) = SLIST_NEXT(var, field))
|
||||
|
||||
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = SLIST_FIRST(head); \
|
||||
(var) && ((tvar) = SLIST_NEXT(var, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
/*
|
||||
* Singly-linked List functions.
|
||||
*/
|
||||
#define SLIST_INIT(head) { \
|
||||
SLIST_FIRST(head) = SLIST_END(head); \
|
||||
}
|
||||
|
||||
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
|
||||
(elm)->field.sle_next = (slistelm)->field.sle_next; \
|
||||
(slistelm)->field.sle_next = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_INSERT_HEAD(head, elm, field) do { \
|
||||
(elm)->field.sle_next = (head)->slh_first; \
|
||||
(head)->slh_first = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_AFTER(elm, field) do { \
|
||||
(elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE_HEAD(head, field) do { \
|
||||
(head)->slh_first = (head)->slh_first->field.sle_next; \
|
||||
} while (0)
|
||||
|
||||
#define SLIST_REMOVE(head, elm, type, field) do { \
|
||||
if ((head)->slh_first == (elm)) { \
|
||||
SLIST_REMOVE_HEAD((head), field); \
|
||||
} else { \
|
||||
struct type *curelm = (head)->slh_first; \
|
||||
\
|
||||
while (curelm->field.sle_next != (elm)) \
|
||||
curelm = curelm->field.sle_next; \
|
||||
curelm->field.sle_next = \
|
||||
curelm->field.sle_next->field.sle_next; \
|
||||
} \
|
||||
_Q_INVALIDATE((elm)->field.sle_next); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* List definitions.
|
||||
*/
|
||||
#define LIST_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *lh_first; /* first element */ \
|
||||
}
|
||||
|
||||
#define LIST_HEAD_INITIALIZER(head) \
|
||||
{ NULL }
|
||||
|
||||
#define LIST_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *le_next; /* next element */ \
|
||||
struct type **le_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* List access methods.
|
||||
*/
|
||||
#define LIST_FIRST(head) ((head)->lh_first)
|
||||
#define LIST_END(head) NULL
|
||||
#define LIST_EMPTY(head) (LIST_FIRST(head) == LIST_END(head))
|
||||
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
|
||||
|
||||
#define LIST_FOREACH(var, head, field) \
|
||||
for((var) = LIST_FIRST(head); \
|
||||
(var)!= LIST_END(head); \
|
||||
(var) = LIST_NEXT(var, field))
|
||||
|
||||
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = LIST_FIRST(head); \
|
||||
(var) && ((tvar) = LIST_NEXT(var, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
/*
|
||||
* List functions.
|
||||
*/
|
||||
#define LIST_INIT(head) do { \
|
||||
LIST_FIRST(head) = LIST_END(head); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
|
||||
if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \
|
||||
(listelm)->field.le_next->field.le_prev = \
|
||||
&(elm)->field.le_next; \
|
||||
(listelm)->field.le_next = (elm); \
|
||||
(elm)->field.le_prev = &(listelm)->field.le_next; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.le_prev = (listelm)->field.le_prev; \
|
||||
(elm)->field.le_next = (listelm); \
|
||||
*(listelm)->field.le_prev = (elm); \
|
||||
(listelm)->field.le_prev = &(elm)->field.le_next; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.le_next = (head)->lh_first) != NULL) \
|
||||
(head)->lh_first->field.le_prev = &(elm)->field.le_next;\
|
||||
(head)->lh_first = (elm); \
|
||||
(elm)->field.le_prev = &(head)->lh_first; \
|
||||
} while (0)
|
||||
|
||||
#define LIST_REMOVE(elm, field) do { \
|
||||
if ((elm)->field.le_next != NULL) \
|
||||
(elm)->field.le_next->field.le_prev = \
|
||||
(elm)->field.le_prev; \
|
||||
*(elm)->field.le_prev = (elm)->field.le_next; \
|
||||
_Q_INVALIDATE((elm)->field.le_prev); \
|
||||
_Q_INVALIDATE((elm)->field.le_next); \
|
||||
} while (0)
|
||||
|
||||
#define LIST_REPLACE(elm, elm2, field) do { \
|
||||
if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \
|
||||
(elm2)->field.le_next->field.le_prev = \
|
||||
&(elm2)->field.le_next; \
|
||||
(elm2)->field.le_prev = (elm)->field.le_prev; \
|
||||
*(elm2)->field.le_prev = (elm2); \
|
||||
_Q_INVALIDATE((elm)->field.le_prev); \
|
||||
_Q_INVALIDATE((elm)->field.le_next); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Simple queue definitions.
|
||||
*/
|
||||
#define SIMPLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *sqh_first; /* first element */ \
|
||||
struct type **sqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define SIMPLEQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).sqh_first }
|
||||
|
||||
#define SIMPLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sqe_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple queue access methods.
|
||||
*/
|
||||
#define SIMPLEQ_FIRST(head) ((head)->sqh_first)
|
||||
#define SIMPLEQ_END(head) NULL
|
||||
#define SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head))
|
||||
#define SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next)
|
||||
|
||||
#define SIMPLEQ_FOREACH(var, head, field) \
|
||||
for((var) = SIMPLEQ_FIRST(head); \
|
||||
(var) != SIMPLEQ_END(head); \
|
||||
(var) = SIMPLEQ_NEXT(var, field))
|
||||
|
||||
#define SIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = SIMPLEQ_FIRST(head); \
|
||||
(var) && ((tvar) = SIMPLEQ_NEXT(var, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
/*
|
||||
* Simple queue functions.
|
||||
*/
|
||||
#define SIMPLEQ_INIT(head) do { \
|
||||
(head)->sqh_first = NULL; \
|
||||
(head)->sqh_last = &(head)->sqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
(head)->sqh_first = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.sqe_next = NULL; \
|
||||
*(head)->sqh_last = (elm); \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
(listelm)->field.sqe_next = (elm); \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_REMOVE_HEAD(head, field) do { \
|
||||
if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \
|
||||
(head)->sqh_last = &(head)->sqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
|
||||
if (((elm)->field.sqe_next = (elm)->field.sqe_next->field.sqe_next) \
|
||||
== NULL) \
|
||||
(head)->sqh_last = &(elm)->field.sqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define SIMPLEQ_CONCAT(head1, head2) do { \
|
||||
if (!SIMPLEQ_EMPTY((head2))) { \
|
||||
*(head1)->sqh_last = (head2)->sqh_first; \
|
||||
(head1)->sqh_last = (head2)->sqh_last; \
|
||||
SIMPLEQ_INIT((head2)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* XOR Simple queue definitions.
|
||||
*/
|
||||
#define XSIMPLEQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *sqx_first; /* first element */ \
|
||||
struct type **sqx_last; /* addr of last next element */ \
|
||||
unsigned long sqx_cookie; \
|
||||
}
|
||||
|
||||
#define XSIMPLEQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *sqx_next; /* next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* XOR Simple queue access methods.
|
||||
*/
|
||||
#define XSIMPLEQ_XOR(head, ptr) ((__typeof(ptr))((head)->sqx_cookie ^ \
|
||||
(unsigned long)(ptr)))
|
||||
#define XSIMPLEQ_FIRST(head) XSIMPLEQ_XOR(head, ((head)->sqx_first))
|
||||
#define XSIMPLEQ_END(head) NULL
|
||||
#define XSIMPLEQ_EMPTY(head) (XSIMPLEQ_FIRST(head) == XSIMPLEQ_END(head))
|
||||
#define XSIMPLEQ_NEXT(head, elm, field) XSIMPLEQ_XOR(head, ((elm)->field.sqx_next))
|
||||
|
||||
|
||||
#define XSIMPLEQ_FOREACH(var, head, field) \
|
||||
for ((var) = XSIMPLEQ_FIRST(head); \
|
||||
(var) != XSIMPLEQ_END(head); \
|
||||
(var) = XSIMPLEQ_NEXT(head, var, field))
|
||||
|
||||
#define XSIMPLEQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = XSIMPLEQ_FIRST(head); \
|
||||
(var) && ((tvar) = XSIMPLEQ_NEXT(head, var, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
/*
|
||||
* XOR Simple queue functions.
|
||||
*/
|
||||
#define XSIMPLEQ_INIT(head) do { \
|
||||
arc4random_buf(&(head)->sqx_cookie, sizeof((head)->sqx_cookie)); \
|
||||
(head)->sqx_first = XSIMPLEQ_XOR(head, NULL); \
|
||||
(head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
|
||||
} while (0)
|
||||
|
||||
#define XSIMPLEQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.sqx_next = (head)->sqx_first) == \
|
||||
XSIMPLEQ_XOR(head, NULL)) \
|
||||
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
|
||||
(head)->sqx_first = XSIMPLEQ_XOR(head, (elm)); \
|
||||
} while (0)
|
||||
|
||||
#define XSIMPLEQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.sqx_next = XSIMPLEQ_XOR(head, NULL); \
|
||||
*(XSIMPLEQ_XOR(head, (head)->sqx_last)) = XSIMPLEQ_XOR(head, (elm)); \
|
||||
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
|
||||
} while (0)
|
||||
|
||||
#define XSIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if (((elm)->field.sqx_next = (listelm)->field.sqx_next) == \
|
||||
XSIMPLEQ_XOR(head, NULL)) \
|
||||
(head)->sqx_last = XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
|
||||
(listelm)->field.sqx_next = XSIMPLEQ_XOR(head, (elm)); \
|
||||
} while (0)
|
||||
|
||||
#define XSIMPLEQ_REMOVE_HEAD(head, field) do { \
|
||||
if (((head)->sqx_first = XSIMPLEQ_XOR(head, \
|
||||
(head)->sqx_first)->field.sqx_next) == XSIMPLEQ_XOR(head, NULL)) \
|
||||
(head)->sqx_last = XSIMPLEQ_XOR(head, &(head)->sqx_first); \
|
||||
} while (0)
|
||||
|
||||
#define XSIMPLEQ_REMOVE_AFTER(head, elm, field) do { \
|
||||
if (((elm)->field.sqx_next = XSIMPLEQ_XOR(head, \
|
||||
(elm)->field.sqx_next)->field.sqx_next) \
|
||||
== XSIMPLEQ_XOR(head, NULL)) \
|
||||
(head)->sqx_last = \
|
||||
XSIMPLEQ_XOR(head, &(elm)->field.sqx_next); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/*
|
||||
* Tail queue definitions.
|
||||
*/
|
||||
#define TAILQ_HEAD(name, type) \
|
||||
struct name { \
|
||||
struct type *tqh_first; /* first element */ \
|
||||
struct type **tqh_last; /* addr of last next element */ \
|
||||
}
|
||||
|
||||
#define TAILQ_HEAD_INITIALIZER(head) \
|
||||
{ NULL, &(head).tqh_first }
|
||||
|
||||
#define TAILQ_ENTRY(type) \
|
||||
struct { \
|
||||
struct type *tqe_next; /* next element */ \
|
||||
struct type **tqe_prev; /* address of previous next element */ \
|
||||
}
|
||||
|
||||
/*
|
||||
* Tail queue access methods.
|
||||
*/
|
||||
#define TAILQ_FIRST(head) ((head)->tqh_first)
|
||||
#define TAILQ_END(head) NULL
|
||||
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
|
||||
#define TAILQ_LAST(head, headname) \
|
||||
(*(((struct headname *)((head)->tqh_last))->tqh_last))
|
||||
/* XXX */
|
||||
#define TAILQ_PREV(elm, headname, field) \
|
||||
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
|
||||
#define TAILQ_EMPTY(head) \
|
||||
(TAILQ_FIRST(head) == TAILQ_END(head))
|
||||
|
||||
#define TAILQ_FOREACH(var, head, field) \
|
||||
for((var) = TAILQ_FIRST(head); \
|
||||
(var) != TAILQ_END(head); \
|
||||
(var) = TAILQ_NEXT(var, field))
|
||||
|
||||
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
|
||||
for ((var) = TAILQ_FIRST(head); \
|
||||
(var) != TAILQ_END(head) && \
|
||||
((tvar) = TAILQ_NEXT(var, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
|
||||
for((var) = TAILQ_LAST(head, headname); \
|
||||
(var) != TAILQ_END(head); \
|
||||
(var) = TAILQ_PREV(var, headname, field))
|
||||
|
||||
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
|
||||
for ((var) = TAILQ_LAST(head, headname); \
|
||||
(var) != TAILQ_END(head) && \
|
||||
((tvar) = TAILQ_PREV(var, headname, field), 1); \
|
||||
(var) = (tvar))
|
||||
|
||||
/*
|
||||
* Tail queue functions.
|
||||
*/
|
||||
#define TAILQ_INIT(head) do { \
|
||||
(head)->tqh_first = NULL; \
|
||||
(head)->tqh_last = &(head)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
|
||||
if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \
|
||||
(head)->tqh_first->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(head)->tqh_first = (elm); \
|
||||
(elm)->field.tqe_prev = &(head)->tqh_first; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
|
||||
(elm)->field.tqe_next = NULL; \
|
||||
(elm)->field.tqe_prev = (head)->tqh_last; \
|
||||
*(head)->tqh_last = (elm); \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
|
||||
if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
&(elm)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm)->field.tqe_next; \
|
||||
(listelm)->field.tqe_next = (elm); \
|
||||
(elm)->field.tqe_prev = &(listelm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
|
||||
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
|
||||
(elm)->field.tqe_next = (listelm); \
|
||||
*(listelm)->field.tqe_prev = (elm); \
|
||||
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_REMOVE(head, elm, field) do { \
|
||||
if (((elm)->field.tqe_next) != NULL) \
|
||||
(elm)->field.tqe_next->field.tqe_prev = \
|
||||
(elm)->field.tqe_prev; \
|
||||
else \
|
||||
(head)->tqh_last = (elm)->field.tqe_prev; \
|
||||
*(elm)->field.tqe_prev = (elm)->field.tqe_next; \
|
||||
_Q_INVALIDATE((elm)->field.tqe_prev); \
|
||||
_Q_INVALIDATE((elm)->field.tqe_next); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_REPLACE(head, elm, elm2, field) do { \
|
||||
if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \
|
||||
(elm2)->field.tqe_next->field.tqe_prev = \
|
||||
&(elm2)->field.tqe_next; \
|
||||
else \
|
||||
(head)->tqh_last = &(elm2)->field.tqe_next; \
|
||||
(elm2)->field.tqe_prev = (elm)->field.tqe_prev; \
|
||||
*(elm2)->field.tqe_prev = (elm2); \
|
||||
_Q_INVALIDATE((elm)->field.tqe_prev); \
|
||||
_Q_INVALIDATE((elm)->field.tqe_next); \
|
||||
} while (0)
|
||||
|
||||
#define TAILQ_CONCAT(head1, head2, field) do { \
|
||||
if (!TAILQ_EMPTY(head2)) { \
|
||||
*(head1)->tqh_last = (head2)->tqh_first; \
|
||||
(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
|
||||
(head1)->tqh_last = (head2)->tqh_last; \
|
||||
TAILQ_INIT((head2)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#endif /* !_SYS_QUEUE_H_ */
|
|
@ -0,0 +1,40 @@
|
|||
/* $OpenBSD: reallocarray.c,v 1.3 2015/09/13 08:31:47 guenther Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008 Otto Moerbeek <otto@drijf.net>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
* This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
|
||||
* if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
|
||||
*/
|
||||
#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
|
||||
|
||||
void *
|
||||
reallocarray(void *optr, size_t nmemb, size_t size)
|
||||
{
|
||||
if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
|
||||
nmemb > 0 && SIZE_MAX / nmemb < size) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
return realloc(optr, size * nmemb);
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
/* $OpenBSD: recallocarray.c,v 1.2 2021/03/18 11:16:58 claudio Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2008, 2017 Otto Moerbeek <otto@drijf.net>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/*
|
||||
* This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
|
||||
* if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
|
||||
*/
|
||||
#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
|
||||
|
||||
/*
|
||||
* Even though specified in POSIX, the PAGESIZE and PAGE_SIZE
|
||||
* macros have very poor portability. Since we only use this
|
||||
* to avoid free() overhead for small shrinking, simply pick
|
||||
* an arbitrary number.
|
||||
*/
|
||||
#define getpagesize() (1UL << 12)
|
||||
|
||||
void *
|
||||
recallocarray(void *ptr, size_t oldnmemb, size_t newnmemb, size_t size)
|
||||
{
|
||||
size_t oldsize, newsize;
|
||||
void *newptr;
|
||||
|
||||
if (ptr == NULL)
|
||||
return calloc(newnmemb, size);
|
||||
|
||||
if ((newnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
|
||||
newnmemb > 0 && SIZE_MAX / newnmemb < size) {
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
newsize = newnmemb * size;
|
||||
|
||||
if ((oldnmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
|
||||
oldnmemb > 0 && SIZE_MAX / oldnmemb < size) {
|
||||
errno = EINVAL;
|
||||
return NULL;
|
||||
}
|
||||
oldsize = oldnmemb * size;
|
||||
|
||||
/*
|
||||
* Don't bother too much if we're shrinking just a bit,
|
||||
* we do not shrink for series of small steps, oh well.
|
||||
*/
|
||||
if (newsize <= oldsize) {
|
||||
size_t d = oldsize - newsize;
|
||||
|
||||
if (d < oldsize / 2 && d < (size_t)getpagesize()) {
|
||||
memset((char *)ptr + newsize, 0, d);
|
||||
return ptr;
|
||||
}
|
||||
}
|
||||
|
||||
newptr = malloc(newsize);
|
||||
if (newptr == NULL)
|
||||
return NULL;
|
||||
|
||||
if (newsize > oldsize) {
|
||||
memcpy(newptr, ptr, oldsize);
|
||||
memset((char *)newptr + oldsize, 0, newsize - oldsize);
|
||||
} else
|
||||
memcpy(newptr, ptr, newsize);
|
||||
|
||||
explicit_bzero(ptr, oldsize);
|
||||
free(ptr);
|
||||
|
||||
return newptr;
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
||||
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#if HAVE_PR_SET_NAME
|
||||
|
||||
#include <sys/prctl.h>
|
||||
|
||||
void
|
||||
setproctitle(const char *fmt, ...)
|
||||
{
|
||||
char title[16], name[16], *cp;
|
||||
va_list ap;
|
||||
int used;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf(title, sizeof title, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
used = snprintf(name, sizeof name, "%s: %s", getprogname(), title);
|
||||
if (used >= (int)sizeof name) {
|
||||
cp = strrchr(name, ' ');
|
||||
if (cp != NULL)
|
||||
*cp = '\0';
|
||||
}
|
||||
prctl(PR_SET_NAME, name);
|
||||
}
|
||||
#else
|
||||
void
|
||||
setproctitle(const char *fmt, ...)
|
||||
{
|
||||
(void)fmt;
|
||||
}
|
||||
#endif
|
|
@ -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,55 @@
|
|||
/*
|
||||
* Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Appends src to string dst of size dsize (unlike strncat, dsize is the
|
||||
* full size of dst, not space left). At most dsize-1 characters
|
||||
* will be copied. Always NUL terminates (unless dsize <= strlen(dst)).
|
||||
* Returns strlen(src) + MIN(dsize, strlen(initial dst)).
|
||||
* If retval >= dsize, truncation occurred.
|
||||
*/
|
||||
size_t
|
||||
strlcat(char *dst, const char *src, size_t dsize)
|
||||
{
|
||||
const char *odst = dst;
|
||||
const char *osrc = src;
|
||||
size_t n = dsize;
|
||||
size_t dlen;
|
||||
|
||||
/* Find the end of dst and adjust bytes left but don't go past end. */
|
||||
while (n-- != 0 && *dst != '\0')
|
||||
dst++;
|
||||
dlen = dst - odst;
|
||||
n = dsize - dlen;
|
||||
|
||||
if (n-- == 0)
|
||||
return(dlen + strlen(src));
|
||||
while (*src != '\0') {
|
||||
if (n != 0) {
|
||||
*dst++ = *src;
|
||||
n--;
|
||||
}
|
||||
src++;
|
||||
}
|
||||
*dst = '\0';
|
||||
|
||||
return(dlen + (src - osrc)); /* count does not include NUL */
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 1998, 2015 Todd C. Miller <millert@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Copy string src to buffer dst of size dsize. At most dsize-1
|
||||
* chars will be copied. Always NUL terminates (unless dsize == 0).
|
||||
* Returns strlen(src); if retval >= dsize, truncation occurred.
|
||||
*/
|
||||
size_t
|
||||
strlcpy(char *dst, const char *src, size_t dsize)
|
||||
{
|
||||
const char *osrc = src;
|
||||
size_t nleft = dsize;
|
||||
|
||||
/* Copy as many bytes as will fit. */
|
||||
if (nleft != 0) {
|
||||
while (--nleft != 0) {
|
||||
if ((*dst++ = *src++) == '\0')
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not enough room in dst, add NUL and traverse rest of src. */
|
||||
if (nleft == 0) {
|
||||
if (dsize != 0)
|
||||
*dst = '\0'; /* NUL-terminate dst */
|
||||
while (*src++)
|
||||
;
|
||||
}
|
||||
|
||||
return(src - osrc - 1); /* count does not include NUL */
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (c) 2004 Ted Unangst and Todd Miller
|
||||
* All rights reserved.
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define INVALID 1
|
||||
#define TOOSMALL 2
|
||||
#define TOOLARGE 3
|
||||
|
||||
long long
|
||||
strtonum(const char *numstr, long long minval, long long maxval,
|
||||
const char **errstrp)
|
||||
{
|
||||
long long ll = 0;
|
||||
int error = 0;
|
||||
char *ep;
|
||||
struct errval {
|
||||
const char *errstr;
|
||||
int err;
|
||||
} ev[4] = {
|
||||
{ NULL, 0 },
|
||||
{ "invalid", EINVAL },
|
||||
{ "too small", ERANGE },
|
||||
{ "too large", ERANGE },
|
||||
};
|
||||
|
||||
ev[0].err = errno;
|
||||
errno = 0;
|
||||
if (minval > maxval) {
|
||||
error = INVALID;
|
||||
} else {
|
||||
ll = strtoll(numstr, &ep, 10);
|
||||
if (numstr == ep || *ep != '\0')
|
||||
error = INVALID;
|
||||
else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval)
|
||||
error = TOOSMALL;
|
||||
else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval)
|
||||
error = TOOLARGE;
|
||||
}
|
||||
if (errstrp != NULL)
|
||||
*errstrp = ev[error].errstr;
|
||||
errno = ev[error].err;
|
||||
if (error)
|
||||
ll = 0;
|
||||
|
||||
return (ll);
|
||||
}
|
|
@ -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);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,48 @@
|
|||
/* from mandoc, with tweaks */
|
||||
/*
|
||||
* Copyright (c) 2015 Ingo Schwarze <schwarze@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*
|
||||
* This fallback implementation is not efficient:
|
||||
* It does the formatting twice.
|
||||
* Short of fiddling with the unknown internals of the system's
|
||||
* printf(3) or completely reimplementing printf(3), i can't think
|
||||
* of another portable solution.
|
||||
*/
|
||||
|
||||
#include "../config.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int
|
||||
vasprintf(char **ret, const char *format, va_list ap)
|
||||
{
|
||||
char buf[2];
|
||||
va_list ap2;
|
||||
int sz;
|
||||
|
||||
va_copy(ap2, ap);
|
||||
sz = vsnprintf(buf, sizeof(buf), format, ap2);
|
||||
va_end(ap2);
|
||||
|
||||
if (sz != -1 && (*ret = malloc(sz + 1)) != NULL) {
|
||||
if (vsnprintf(*ret, sz + 1, format, ap) == sz)
|
||||
return sz;
|
||||
free(*ret);
|
||||
}
|
||||
*ret = NULL;
|
||||
return -1;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,691 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Copyright (c) 2021, 2022, 2023 Omar Polo <op@omarpolo.com>
|
||||
# Copyright (c) 2011, 2013-2022 Ingo Schwarze <schwarze@openbsd.org>
|
||||
# Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
set -e
|
||||
|
||||
RELEASE=no
|
||||
VERSION=2.0.2-current
|
||||
|
||||
usage()
|
||||
{
|
||||
echo "usage: $0 [--help] [--prefix=prefix] [OPTION=VALUE...]" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
if command -v yacc 2>/dev/null >&2; then
|
||||
YACC=yacc
|
||||
elif command -v bison 2>/dev/null >&2; then
|
||||
YACC=bison
|
||||
else
|
||||
# assume yacc by default. Make will fail building parse.y if
|
||||
# not from a release tarball, but at least it'll have a decent
|
||||
# error message.
|
||||
YACC=yacc
|
||||
fi
|
||||
|
||||
pkgconfig=
|
||||
|
||||
CC=${CC:-cc}
|
||||
|
||||
if [ "$RELEASE" = no ]; then
|
||||
CFLAGS=${CFLAGS:--O0 -g3}
|
||||
else
|
||||
CFLAGS=${CFLAGS:--O2 -pipe}
|
||||
fi
|
||||
|
||||
INSTALL=${INSTALL-install}
|
||||
PREFIX=${PREFIX-/usr/local}
|
||||
SYSCONFDIR=${SYSCONFDIR-/etc}
|
||||
|
||||
CDIAGFLAGS=
|
||||
CDIAGFLAGS="${CDIAGFLAGS} -W -Wall -Wextra -Wpointer-arith -Wuninitialized"
|
||||
CDIAGFLAGS="${CDIAGFLAGS} -Wstrict-prototypes -Wmissing-prototypes -Wunused"
|
||||
CDIAGFLAGS="${CDIAGFLAGS} -Wsign-compare -Wno-unused-parameter" # -Wshadow
|
||||
CDIAGFLAGS="${CDIAGFLAGS} -Wno-missing-field-initializers"
|
||||
CDIAGFLAGS="${CDIAGFLAGS} -Wno-pointer-sign"
|
||||
|
||||
# On all OSes except OpenBSD use the bundled one. It may crash at
|
||||
# runtime otherwise since we depend on the libtls internals for the
|
||||
# privsep crypto engine.
|
||||
# See <https://codeberg.org/op/gmid/issues/2>.
|
||||
LIBTLS=bundled # or system
|
||||
if [ "$(uname || true)" = OpenBSD ]; then
|
||||
LIBTLS=system
|
||||
fi
|
||||
|
||||
while [ $# -gt 0 ]; do
|
||||
key="${1%%=*}"
|
||||
val="${1#*=}"
|
||||
|
||||
if [ "$key" = --help ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
if [ "$key" = -Werror ]; then
|
||||
CDIAGFLAGS="$CDIAGFLAGS -Werror"
|
||||
shift
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "$key" = --enable-sandbox ]; then
|
||||
key=DISABLE_SANDBOX
|
||||
val=0
|
||||
fi
|
||||
|
||||
if [ "$key" = --disable-sandbox ]; then
|
||||
key=DISABLE_SANDBOX
|
||||
val=1
|
||||
fi
|
||||
|
||||
if [ "$key" = "$1" ]; then
|
||||
# if no --xy=, look at the next arg
|
||||
if ! shift 2>/dev/null; then
|
||||
echo "$0: missing value for $key" >&2
|
||||
exit 1
|
||||
fi
|
||||
val="$1"
|
||||
fi
|
||||
|
||||
case "$key" in
|
||||
--bindir) key=BINDIR ;;
|
||||
--mandir) key=MANDIR ;;
|
||||
--prefix) key=PREFIX ;;
|
||||
--sysconfdir) key=SYSCONFDIR ;;
|
||||
--with-libtls) key=LIBTLS ;;
|
||||
esac
|
||||
|
||||
case "$key" in
|
||||
LIBTLS)
|
||||
case "$val" in
|
||||
bundled) LIBTLS=bundled ;;
|
||||
system) LIBTLS=system ;;
|
||||
*) usage ;;
|
||||
esac
|
||||
;;
|
||||
BINDIR) BINDIR="$val" ;;
|
||||
CC) CC="$val" ;;
|
||||
CFLAGS) CFLAGS="$val" ;;
|
||||
CDIAGFLAGS) CDIAGFLAGS="$val" ;;
|
||||
DISABLE_SANDBOX) DISABLE_SANDBOX="$val" ;;
|
||||
INSTALL) INSTALL="$val" ;;
|
||||
LDFLAGS) LDFLAGS="$val" ;;
|
||||
MANDIR) MANDIR="$val" ;;
|
||||
PKG_CONFIG) PKG_CONFIG="$val" ;;
|
||||
PREFIX) PREFIX="$val" ;;
|
||||
SYSCONFDIR) SYSCONFDIR="$val" ;;
|
||||
YACC) YACC="$val" ;;
|
||||
*) usage
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
[ -w config.log ] && mv config.log config.log.old
|
||||
[ -w config.h ] && mv config.h config.h.old
|
||||
|
||||
exec 3> config.log
|
||||
echo "file config.log: writing..."
|
||||
|
||||
NEED_GNU_SOURCE=0
|
||||
NEED_OPENBSD_SOURCE=0
|
||||
NEED_LIBBSD_OPENBSD_VIS=0
|
||||
|
||||
COMPATS=
|
||||
COMP="${CC} ${CFLAGS} -Werror=implicit-function-declaration"
|
||||
|
||||
# singletest name var extra-cflags extra-libs msg
|
||||
singletest() {
|
||||
msg="$5"
|
||||
if [ -z "$msg" ]; then
|
||||
if [ -n "$3" ]; then
|
||||
msg=" ($3)"
|
||||
elif [ -n "$4" ]; then
|
||||
msg=" ($4)"
|
||||
fi
|
||||
elif [ "$msg" = no ]; then
|
||||
msg=""
|
||||
fi
|
||||
|
||||
cat >&3 <<EOF
|
||||
${1}: testing...
|
||||
$COMP have/${1}.c $3 -o test-$1 $LDFLAGS $4
|
||||
EOF
|
||||
if $COMP have/${1}.c $3 -o test-$1 $LDFLAGS $4 >&3 2>&3; then
|
||||
rm -f test-${1} test-${1}.d
|
||||
|
||||
echo "${1}: $CC$msg succeeded" >&3
|
||||
echo "${1}$msg: yes"
|
||||
echo >&3
|
||||
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "${1}: $CC$msg failed $?" >&3
|
||||
echo "${1}$msg: no"
|
||||
echo >&3
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# deptest name var
|
||||
deptest() {
|
||||
if singletest "$1" "$2" "${CFLAGS}" "${LIBS}" no; then
|
||||
eval HAVE_${2}=1
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -f compat/${1}.c ]; then
|
||||
COMPATS="compat/$1.c $COMPATS"
|
||||
fi
|
||||
|
||||
eval HAVE_${2}=0
|
||||
return 1
|
||||
}
|
||||
|
||||
# runtest name var extra-cflags extra-libs pkgconfig-name
|
||||
runtest() {
|
||||
if singletest "$1" "$2" "" ""; then
|
||||
eval HAVE_${2}=1
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ -n "$3" -o -n "$4" ]; then
|
||||
echo "retrying with ${3+$3 }$4" >&3
|
||||
if singletest "$1" "$2" "$3" "$4"; then
|
||||
eval HAVE_${2}=1
|
||||
if [ "$3" = -D_GNU_SOURCE ]; then
|
||||
NEED_GNU_SOURCE=1
|
||||
return 0
|
||||
fi
|
||||
if [ "$4" = -D_OPENBSD_SOURCE ]; then
|
||||
NEED_OPENBSD_SOURCE=1
|
||||
return 0
|
||||
fi
|
||||
if [ "$4" = -DLIBBSD_OPENBSD_VIS ]; then
|
||||
NEED_LIBBSD_OPENBSD_VIS=1
|
||||
return 0
|
||||
fi
|
||||
if [ -n "$3" ]; then
|
||||
CFLAGS="$CFLAGS $3"
|
||||
fi
|
||||
if [ -n "$4" ]; then
|
||||
LIBS="$LIBS $4"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "$5" -a -n "$pkgconfig" ]; then
|
||||
if $pkgconfig $5; then
|
||||
cflags="$($pkgconfig --cflags $5)"
|
||||
ldflags="$($pkgconfig --libs $5)"
|
||||
echo "retrying with pkg-config" >&3
|
||||
if singletest "$1" "$2" "$cflags" "$ldflags"; then
|
||||
CFLAGS="$CFLAGS $cflags"
|
||||
LIBS="$LIBS $ldflags"
|
||||
eval HAVE_$2=1
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -f compat/$1.c ]; then
|
||||
COMPATS="compat/$1.c $COMPATS"
|
||||
fi
|
||||
|
||||
eval HAVE_$2=0
|
||||
return 1
|
||||
}
|
||||
|
||||
if [ "$PKG_CONFIG" = no ]; then
|
||||
echo "pkg-config: disabled"
|
||||
elif [ -n "$PKG_CONFIG" ]; then
|
||||
pkgconfig="$PKG_CONFIG"
|
||||
echo "pkg-config: (manual) $PKG_CONFIG"
|
||||
elif command -v pkg-config 2>/dev/null >&2; then
|
||||
pkgconfig="pkg-config"
|
||||
echo "pkg-config: (auto) pkg-config"
|
||||
else
|
||||
echo "pkg-config: not found"
|
||||
fi
|
||||
|
||||
if singletest noop MMD -MMD; then
|
||||
CFLAGS="${CFLAGS} -MMD"
|
||||
fi
|
||||
|
||||
if ! runtest wait_any WAIT_ANY; then
|
||||
CFLAGS="${CFLAGS} -DWAIT_ANY=-1"
|
||||
fi
|
||||
|
||||
HAVE_ENDIAN_H=0
|
||||
HAVE_SYS_ENDIAN_H=0
|
||||
HAVE_MACHINE_ENDIAN=0
|
||||
|
||||
runtest endian_h ENDIAN_H || \
|
||||
runtest sys_endian_h SYS_ENDIAN_H || \
|
||||
runtest machine_endian MACHINE_ENDIAN || true
|
||||
|
||||
if [ ${HAVE_ENDIAN_H} -eq 0 -a \
|
||||
${HAVE_SYS_ENDIAN_H} -eq 0 -a \
|
||||
${HAVE_MACHINE_ENDIAN} -eq 0 ]; then
|
||||
echo "FATAL: no endian header found" 1>&2
|
||||
echo "FATAL: no endian header found" 1>&3
|
||||
exit 1
|
||||
fi
|
||||
|
||||
runtest arc4random ARC4RANDOM || true
|
||||
runtest arc4random_buf ARC4RANDOM_BUF || true
|
||||
runtest err ERR || true
|
||||
runtest explicit_bzero EXPLICIT_BZERO || true
|
||||
runtest freezero FREEZERO || true
|
||||
runtest getdtablecount GETDTABLECOUNT || true
|
||||
runtest getdtablesize GETDTABLESIZE || true
|
||||
runtest getprogname GETPROGNAME || true
|
||||
runtest imsg IMSG "" -lutil libimsg || true
|
||||
runtest landlock LANDLOCK || true
|
||||
runtest libevent LIBEVENT "" -levent libevent_core|| true
|
||||
runtest memmem MEMMEM -D_GNU_SOURCE || true
|
||||
runtest openssl OPENSSL "" '-lcrypto -lssl' 'libcrypto libssl' || true
|
||||
runtest pr_set_name PR_SET_NAME || true
|
||||
runtest program_invocation_short_name PROGRAM_INVOCATION_SHORT_NAME -D_GNU_SOURCE || true
|
||||
runtest queue_h QUEUE_H || true
|
||||
runtest reallocarray REALLOCARRAY -D_OPENBSD_SOURCE || true
|
||||
runtest recallocarray RECALLOCARRAY || true
|
||||
runtest setproctitle SETPROCTITLE || true
|
||||
runtest setresgid SETRESGID -D_GNU_SOURCE || true
|
||||
runtest setresuid SETRESUID -D_GNU_SOURCE || true
|
||||
runtest strlcat STRLCAT || true
|
||||
runtest strlcpy STRLCPY || true
|
||||
runtest strtonum STRTONUM -D_OPENBSD_SOURCE || true
|
||||
runtest timingsafe_memcmp TIMINGSAFE_MEMCMP || true
|
||||
runtest tree_h TREE_H || true
|
||||
runtest vasprintf VASPRINTF -D_GNU_SOURCE || true
|
||||
runtest vis VIS -DLIBBSD_OPENBSD_VIS || true
|
||||
|
||||
if [ ${HAVE_ARC4RANDOM} -eq 1 -a ${HAVE_ARC4RANDOM_BUF} -eq 0 ]; then
|
||||
COMPATS="compat/arc4random.c ${COMPATS}"
|
||||
fi
|
||||
|
||||
if [ ${HAVE_ARC4RANDOM} -eq 0 ]; then
|
||||
runtest getentropy GETENTROPY || true
|
||||
else
|
||||
# fake it
|
||||
HAVE_GETENTROPY=1
|
||||
fi
|
||||
|
||||
if [ ${HAVE_ARC4RANDOM} -eq 0 -a ${HAVE_GETENTROPY} -eq 1 ]; then
|
||||
COMPATS="compat/getentropy.c ${COMPATS}"
|
||||
fi
|
||||
|
||||
if [ "${LIBTLS}" = system ]; then
|
||||
runtest libtls LIBTLS "" -ltls libtls || true
|
||||
|
||||
# not actually needed
|
||||
HAVE_ASN1_TIME_TM_CMP=1
|
||||
HAVE_ASN1_TIME_TM_CLAMP_NOTAFTER=1
|
||||
HAVE_ASN1_TIME_PARSE=1
|
||||
HAVE_SSL_CTX_UCCM=1
|
||||
HAVE_SSL_CTX_LVM=1
|
||||
HAVE_X509_LOOKUP_MEM=1
|
||||
else
|
||||
# use bundled one
|
||||
HAVE_LIBTLS=1
|
||||
for f in compat/libtls/*.c; do
|
||||
COMPATS="$f ${COMPATS}"
|
||||
done
|
||||
|
||||
CFLAGS="-Icompat/libtls -I../compat/libtls ${CFLAGS}"
|
||||
|
||||
deptest ASN1_time_tm_cmp ASN1_TIME_TM_CMP || true
|
||||
deptest ASN1_time_tm_clamp_notafter ASN1_TIME_TM_CLAMP_NOTAFTER || true
|
||||
deptest ASN1_time_parse ASN1_TIME_PARSE || true
|
||||
deptest SSL_CTX_use_certificate_chain_mem SSL_CTX_UCCM || true
|
||||
deptest SSL_CTX_load_verify_mem SSL_CTX_LVM || true
|
||||
deptest X509_LOOKUP_mem X509_LOOKUP_MEM || true
|
||||
fi
|
||||
|
||||
deptest libevent2 LIBEVENT2 || true
|
||||
|
||||
if [ ${HAVE_LIBTLS} -eq 0 ]; then
|
||||
echo "FATAL: openssl not found" 1>&2
|
||||
echo "FATAL: openssl not found" 1>&3
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ${HAVE_OPENSSL} -eq 0 ]; then
|
||||
echo "FATAL: openssl not found" 1>&2
|
||||
echo "FATAL: openssl not found" 1>&3
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ${HAVE_LIBEVENT} -eq 0 ]; then
|
||||
echo "FATAL: libevent not found" 1>&2
|
||||
echo "FATAL: libevent not found" 1>&3
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ${HAVE_IMSG} -eq 0 ]; then
|
||||
# compat/imsg.c is implicitly added
|
||||
COMPATS="$COMPATS compat/imsg-buffer.c"
|
||||
fi
|
||||
|
||||
if [ ${HAVE_QUEUE_H} -eq 0 -o ${HAVE_IMSG} -eq 0 -o ${HAVE_TREE_H} -eq 0 ]; then
|
||||
CFLAGS="${CFLAGS} -I ${PWD}/compat"
|
||||
fi
|
||||
|
||||
if [ ${HAVE_VIS} -eq 0 ]; then
|
||||
CFLAGS="${CFLAGS} -I ${PWD}/compat/vis"
|
||||
fi
|
||||
|
||||
if [ $HAVE_LIBEVENT2 -eq 1 ]; then
|
||||
CFLAGS="$CFLAGS -DHAVE_LIBEVENT2=1"
|
||||
fi
|
||||
|
||||
if [ $NEED_GNU_SOURCE = 1 ]; then
|
||||
CFLAGS="$CFLAGS -D_GNU_SOURCE"
|
||||
fi
|
||||
if [ $NEED_OPENBSD_SOURCE = 1 ]; then
|
||||
CFLAGS="$CFLAGS -D_OPENBSD_SOURCE"
|
||||
fi
|
||||
if [ $NEED_LIBBSD_OPENBSD_VIS = 1 ]; then
|
||||
CFLAGS="$CFLAGS -DLIBBSD_OPENBSD_VIS"
|
||||
fi
|
||||
|
||||
CFLAGS="-I. ${CFLAGS} ${CDIAGFLAGS}"
|
||||
|
||||
exec > config.h
|
||||
echo "config.h: writing.." >&2
|
||||
|
||||
cat <<__HEREDOC__
|
||||
#ifdef __cplusplus
|
||||
#error "Do not use C++."
|
||||
#endif
|
||||
__HEREDOC__
|
||||
|
||||
[ ${HAVE_STRLCAT} -eq 0 -o ${HAVE_STRLCPY} -eq 0 -o ${HAVE_IMSG} -eq 0 ] \
|
||||
&& echo "#include <sys/types.h>"
|
||||
[ ${HAVE_VASPRINTF} -eq 0 ] && echo "#include <stdarg.h>"
|
||||
|
||||
if [ ${HAVE_QUEUE_H} -eq 1 ]; then
|
||||
echo "#include <sys/queue.h>"
|
||||
else
|
||||
echo "#include <queue.h>"
|
||||
fi
|
||||
|
||||
if [ ${HAVE_TREE_H} -eq 1 ]; then
|
||||
echo "#include <sys/tree.h>"
|
||||
else
|
||||
echo "#include <tree.h>"
|
||||
fi
|
||||
|
||||
echo "#include <sys/types.h>"
|
||||
echo "#include <sys/uio.h>"
|
||||
echo "#include <stdint.h>"
|
||||
echo "#include <imsg.h>"
|
||||
echo "#include <limits.h>"
|
||||
|
||||
cat <<__HEREDOC__
|
||||
|
||||
#define VERSION "${VERSION}"
|
||||
#define DISABLE_SANDBOX ${DISABLE_SANDBOX}
|
||||
#define HAVE_LANDLOCK ${HAVE_LANDLOCK}
|
||||
|
||||
#ifndef SYSCONFDIR
|
||||
# define SYSCONFDIR "${SYSCONFDIR}"
|
||||
#endif
|
||||
|
||||
__HEREDOC__
|
||||
|
||||
if [ ${HAVE_ENDIAN_H} -eq 1 ]; then
|
||||
echo "#include <endian.h>"
|
||||
elif [ ${HAVE_SYS_ENDIAN_H} -eq 1 ]; then
|
||||
echo "#include <sys/endian.h>"
|
||||
elif [ ${HAVE_MACHINE_ENDIAN} -eq 1 ]; then
|
||||
cat <<__HEREDOC__
|
||||
#include <machine/endian.h>
|
||||
#include <libkern/OSByteOrder.h>
|
||||
|
||||
# define htobe16(x) OSSwapHostToBigInt16(x)
|
||||
# define htole16(x) OSSwapHostToLittleInt16(x)
|
||||
# define be16toh(x) OSSwapBigToHostInt16(x)
|
||||
# define le16toh(x) OSSwapLittleToHostInt16(x)
|
||||
|
||||
# define htobe32(x) OSSwapHostToBigInt32(x)
|
||||
# define htole32(x) OSSwapHostToLittleInt32(x)
|
||||
# define be32toh(x) OSSwapBigToHostInt32(x)
|
||||
# define le32toh(x) OSSwapLittleToHostInt32(x)
|
||||
|
||||
# define htobe64(x) OSSwapHostToBigInt64(x)
|
||||
# define htole64(x) OSSwapHostToLittleInt64(x)
|
||||
# define be64toh(x) OSSwapBigToHostInt64(x)
|
||||
# define le64toh(x) OSSwapLittleToHostInt64(x)
|
||||
__HEREDOC__
|
||||
fi
|
||||
|
||||
[ ${HAVE_ARC4RANDOM_BUF} -eq 0 -o \
|
||||
${HAVE_ASN1_TIME_PARSE} -eq 0 -o \
|
||||
${HAVE_EXPLICIT_BZERO} -eq 0 -o \
|
||||
${HAVE_FREEZERO} -eq 0 -o \
|
||||
${HAVE_GETENTROPY} -eq 0 -o \
|
||||
${HAVE_REALLOCARRAY} -eq 0 -o \
|
||||
${HAVE_RECALLOCARRAY} -eq 0 -o \
|
||||
${HAVE_STRLCAT} -eq 0 -o \
|
||||
${HAVE_STRLCPY} -eq 0 -o \
|
||||
${HAVE_STRTONUM} -eq 0 -o \
|
||||
${HAVE_TIMINGSAFE_MEMCMP} -eq 0 ] && echo "#include <stddef.h>"
|
||||
|
||||
[ ${HAVE_ARC4RANDOM} -eq 0 ] && echo "#include <stdint.h>"
|
||||
|
||||
[ ${HAVE_SETRESGID} -eq 0 -o \
|
||||
${HAVE_SETRESUID} -eq 0 ] && echo "#include <unistd.h>"
|
||||
|
||||
if [ ${HAVE_GETENTROPY} -eq 1 ]; then
|
||||
echo "#define HAVE_GETENTROPY 1"
|
||||
else
|
||||
echo "#define WITH_OPENSSL 1"
|
||||
echo "#define OPENSSL_PRNG_ONLY 1"
|
||||
fi
|
||||
|
||||
if [ ${HAVE_ARC4RANDOM} -eq 0 ]; then
|
||||
echo "extern uint32_t arc4random(void);"
|
||||
else
|
||||
echo "#define HAVE_ARC4RANDOM 1"
|
||||
fi
|
||||
if [ ${HAVE_ARC4RANDOM_BUF} -eq 0 ]; then
|
||||
echo "extern void arc4random_buf(void *, size_t);"
|
||||
else
|
||||
echo "#define HAVE_ARC4RANDOM_BUF 1"
|
||||
fi
|
||||
|
||||
if [ ${HAVE_ASN1_TIME_TM_CMP} -eq 0 ]; then
|
||||
echo "struct tm;"
|
||||
echo "extern int ASN1_time_tm_cmp(struct tm *, struct tm *);"
|
||||
else
|
||||
echo "#define HAVE_ASN1_TIME_TM_CMP 1"
|
||||
fi
|
||||
|
||||
if [ ${HAVE_ASN1_TIME_TM_CLAMP_NOTAFTER} -eq 0 ]; then
|
||||
echo "struct tm;"
|
||||
echo "extern int ASN1_time_tm_clamp_notafter(struct tm *);"
|
||||
else
|
||||
echo "#define HAVE_ASN1_TIME_TM_CLAMP_NOTAFTER 1"
|
||||
fi
|
||||
|
||||
if [ ${HAVE_ASN1_TIME_PARSE} -eq 0 ]; then
|
||||
echo "struct tm;"
|
||||
echo "extern int ASN1_time_parse(const char *, size_t, struct tm *, int);"
|
||||
else
|
||||
echo "#define HAVE_ASN1_TIME_PARSE 1"
|
||||
fi
|
||||
|
||||
if [ ${HAVE_ERR} -eq 0 ]; then
|
||||
echo "extern void err(int, const char*, ...);"
|
||||
echo "extern void errx(int, const char*, ...);"
|
||||
echo "extern void warn(const char*, ...);"
|
||||
echo "extern void warnx(const char*, ...);"
|
||||
else
|
||||
echo "#include <err.h>"
|
||||
fi
|
||||
if [ ${HAVE_EXPLICIT_BZERO} -eq 0 ]; then
|
||||
echo "extern void explicit_bzero(void*, size_t);"
|
||||
fi
|
||||
if [ ${HAVE_FREEZERO} -eq 0 ]; then
|
||||
echo "extern void freezero(void*, size_t);"
|
||||
fi
|
||||
if [ ${HAVE_GETDTABLECOUNT} -eq 0 ]; then
|
||||
echo "extern int getdtablecount(void);"
|
||||
fi
|
||||
if [ ${HAVE_GETDTABLESIZE} -eq 0 ]; then
|
||||
echo "extern int getdtablesize(void);"
|
||||
fi
|
||||
if [ ${HAVE_GETENTROPY} -eq 0 ]; then
|
||||
echo "extern int getentropy(void *, size_t);"
|
||||
fi
|
||||
if [ ${HAVE_GETPROGNAME} -eq 0 ]; then
|
||||
echo "extern const char *getprogname(void);"
|
||||
fi
|
||||
if [ ${HAVE_MEMMEM} -eq 0 ]; then
|
||||
echo "extern void *memmem(const void *, size_t, const void *, size_t);"
|
||||
fi
|
||||
if [ ${HAVE_REALLOCARRAY} -eq 0 ]; then
|
||||
echo "extern void *reallocarray(void*, size_t, size_t);"
|
||||
fi
|
||||
if [ ${HAVE_RECALLOCARRAY} -eq 0 ]; then
|
||||
echo "extern void *recallocarray(void*, size_t, size_t, size_t);"
|
||||
fi
|
||||
if [ ${HAVE_SETPROCTITLE} -eq 0 ]; then
|
||||
echo "extern void setproctitle(const char *fmt, ...);"
|
||||
fi
|
||||
if [ ${HAVE_SETRESGID} -eq 0 ]; then
|
||||
echo "extern int setresgid(gid_t, gid_t, gid_t);"
|
||||
fi
|
||||
if [ ${HAVE_SETRESUID} -eq 0 ]; then
|
||||
echo "extern int setresuid(uid_t, uid_t, uid_t);"
|
||||
fi
|
||||
if [ ${HAVE_STRLCAT} -eq 0 ]; then
|
||||
echo "extern size_t strlcat(char*, const char*, size_t);"
|
||||
fi
|
||||
if [ ${HAVE_STRLCPY} -eq 0 ]; then
|
||||
echo "extern size_t strlcpy(char*, const char*, size_t);"
|
||||
fi
|
||||
if [ ${HAVE_STRTONUM} -eq 0 ]; then
|
||||
echo "extern long long strtonum(const char*, long long, long long, const char**);"
|
||||
fi
|
||||
if [ ${HAVE_TIMINGSAFE_MEMCMP} -eq 0 ]; then
|
||||
echo "extern int timingsafe_memcmp(const void *, const void *, size_t);"
|
||||
fi
|
||||
if [ ${HAVE_VASPRINTF} -eq 0 ]; then
|
||||
echo "extern int vasprintf(char**, const char*, va_list);"
|
||||
fi
|
||||
|
||||
if [ ${HAVE_ASN1_TIME_TM_CMP} -eq 0 ]; then
|
||||
echo "#include <openssl/asn1.h>"
|
||||
echo "struct tm;"
|
||||
echo "int ASN1_time_tm_cmp(struct tm *, struct tm *);"
|
||||
else
|
||||
echo "#define HAVE_ASN1_TIME_TM_CMP 1"
|
||||
fi
|
||||
|
||||
if [ ${HAVE_SSL_CTX_UCCM} -eq 0 -o ${HAVE_SSL_CTX_LVM} -eq 0 ]; then
|
||||
echo "#include <openssl/ssl.h>"
|
||||
fi
|
||||
|
||||
if [ ${HAVE_SSL_CTX_UCCM} -eq 0 ]; then
|
||||
echo "int SSL_CTX_use_certificate_chain_mem(SSL_CTX *, void *, int);"
|
||||
else
|
||||
echo "#define HAVE_SSL_CTX_USE_CERTIFICATE_CHAIN_MEM 1"
|
||||
fi
|
||||
|
||||
if [ ${HAVE_SSL_CTX_LVM} -eq 0 ]; then
|
||||
echo "int SSL_CTX_load_verify_mem(SSL_CTX *, void *, int);"
|
||||
else
|
||||
echo "#define HAVE_SSL_CTX_LOAD_VERIFY_MEM 1"
|
||||
fi
|
||||
|
||||
if [ ${HAVE_X509_LOOKUP_MEM} -eq 0 ]; then
|
||||
echo "#include <openssl/x509_vfy.h>"
|
||||
echo "X509_LOOKUP_METHOD *X509_LOOKUP_mem(void);"
|
||||
else
|
||||
echo "#define HAVE_X509_LOOKUP_MEM 1"
|
||||
fi
|
||||
|
||||
cat <<__HEREDOC__
|
||||
|
||||
#ifndef __dead
|
||||
#define __dead __attribute__((noreturn))
|
||||
#endif
|
||||
|
||||
/* Linux and OpenBSD have LOGIN_NAME_MAX, FreeBSD MAXLOGNAME. */
|
||||
#ifndef LOGIN_NAME_MAX
|
||||
# if defined(MAXLOGNAME)
|
||||
# define LOGIN_NAME_MAX MAXLOGNAME
|
||||
# elif defined(_POSIX_LOGIN_NAME_MAX)
|
||||
# define LOGIN_NAME_MAX _POSIX_LOGIN_NAME_MAX
|
||||
# else
|
||||
# define LOGIN_NAME_MAX 32
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef HOST_NAME_MAX
|
||||
# if defined(_POSIX_HOST_NAME_MAX)
|
||||
# define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
|
||||
# else
|
||||
# define HOST_NAME_MAX 255
|
||||
# endif
|
||||
#endif
|
||||
__HEREDOC__
|
||||
|
||||
echo "file config.h: written" 1>&2
|
||||
echo "file config.h: written" 1>&3
|
||||
|
||||
exec > config.mk
|
||||
|
||||
[ -z "${BINDIR}" ] && BINDIR="\${PREFIX}/bin"
|
||||
[ -z "${MANDIR}" ] && MANDIR="\${PREFIX}/man"
|
||||
|
||||
cat << __HEREDOC__
|
||||
CC = ${CC}
|
||||
CFLAGS = ${CFLAGS}
|
||||
LDFLAGS = ${LDFLAGS}
|
||||
LIBS = ${LIBS}
|
||||
YACC = ${YACC}
|
||||
STATIC = ${STATIC}
|
||||
PREFIX = ${PREFIX}
|
||||
BINDIR = ${BINDIR}
|
||||
MANDIR = ${MANDIR}
|
||||
|
||||
INSTALL = ${INSTALL}
|
||||
INSTALL_PROGRAM = \${INSTALL} -m 0555
|
||||
INSTALL_LIB = \${INSTALL} -m 0444
|
||||
INSTALL_MAN = \${INSTALL} -m 0444
|
||||
INSTALL_DATA = \${INSTALL} -m 0444
|
||||
|
||||
COMPATS= ${COMPATS}
|
||||
|
||||
VERSION = ${VERSION}
|
||||
|
||||
__HEREDOC__
|
||||
|
||||
echo "file config.mk: written" 1>&2
|
||||
echo "file config.mk: written" 1>&3
|
||||
|
||||
echo >&2
|
||||
echo "Now run \`make' to compile." >&2
|
||||
echo >&2
|
||||
|
||||
exit 0
|
|
@ -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 "/"
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
FROM alpine
|
||||
WORKDIR /build
|
||||
RUN apk update && \
|
||||
apk upgrade && \
|
||||
apk add --repository=https://dl-cdn.alpinelinux.org/alpine/edge/main \
|
||||
alpine-sdk \
|
||||
linux-headers \
|
||||
bison \
|
||||
libevent-dev \
|
||||
openssl-dev
|
||||
COPY . .
|
||||
RUN ./configure && make && make install
|
||||
RUN adduser -H -S -s /sbin/nologin gmid
|
||||
RUN mkdir /var/gemini
|
||||
RUN ./contrib/gencert -e localhost && \
|
||||
mv localhost.pem /etc/ssl && \
|
||||
mv localhost.key /etc/ssl/private
|
||||
RUN mv contrib/Docker.gmid.conf /etc/gmid.conf
|
||||
ENTRYPOINT ["gmid", "-f"]
|
|
@ -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
|
|
@ -0,0 +1,38 @@
|
|||
This directory is for additional contributed files which may be
|
||||
useful.
|
||||
|
||||
Dockerfile
|
||||
|
||||
Sample Dockerfile to build alpine-based gmid images.
|
||||
|
||||
gencert
|
||||
|
||||
Simple shell script to generate self-signed certificates.
|
||||
|
||||
gmid.service
|
||||
|
||||
Simple systemd service file.
|
||||
|
||||
gmid.sysusers
|
||||
|
||||
Sample systemd-sysusers' config file.
|
||||
|
||||
mime.types
|
||||
|
||||
A copy of OpenBSD' /usr/share/misc/mime.types to be included
|
||||
within a `types' block.
|
||||
|
||||
renew-certs
|
||||
|
||||
Flexible script meant to be run in a cronjob to watch for cert
|
||||
expiration. It can optionally regen the (self-signed)
|
||||
certificate in place and restart the server too.
|
||||
|
||||
vim
|
||||
|
||||
Syntax highlighting of gmid configuration for vim, to be
|
||||
placed into ~/.vim/ or /usr/share/vim/vimfiles/.
|
||||
|
||||
To enable Syntastic checker, put this line in your vimrc:
|
||||
|
||||
let g:syntastic_gmid_checkers = ['gmid']
|
|
@ -0,0 +1,104 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# NAME
|
||||
# gencert - generate certificates
|
||||
#
|
||||
# SYNOPSIS
|
||||
# ./gencert [-efh] [-D days] [-d destdir] hostname
|
||||
#
|
||||
# DESCRIPTION
|
||||
# A simple script to generate self-signed X.509 certificates for
|
||||
# gmid.
|
||||
#
|
||||
# The option are as follows:
|
||||
# -D Specify the number of days the certificate
|
||||
# will be valid for. Use 365 (a year) by default.
|
||||
# -d Save the certificates to the given directory.
|
||||
# By default the current directory is used.
|
||||
# -e Use an EC key instead of RSA.
|
||||
# -f Forcefully overwrite existing certificates
|
||||
# without prompting.
|
||||
# -h Display usage and exit.
|
||||
#
|
||||
# SEE ALSO
|
||||
# openssl(1)
|
||||
#
|
||||
|
||||
progname="$(basename -- "$0")"
|
||||
|
||||
usage() {
|
||||
echo "usage: $progname [-fhe] [-d destdir] [-D days] hostname" >&2
|
||||
echo "Please read the comment at the top of $0 for the usage." >&2
|
||||
exit $1
|
||||
}
|
||||
|
||||
ec=no
|
||||
force=no
|
||||
destdir=.
|
||||
days=365
|
||||
|
||||
while getopts "D:d:efh" flag; do
|
||||
case $flag in
|
||||
D) days="$OPTARG" ;;
|
||||
d) destdir="${OPTARG%/}" ;;
|
||||
e) ec=yes ;;
|
||||
f) force=yes ;;
|
||||
h) usage 0 ;;
|
||||
?) usage 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
shift $(($OPTIND - 1))
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
usage 1
|
||||
fi
|
||||
|
||||
if [ ! -d "${destdir}" ]; then
|
||||
echo "${progname}: ${destdir} is not a directory." >&2
|
||||
usage 1
|
||||
fi
|
||||
|
||||
hostname="${1}"
|
||||
pem="${destdir}/${hostname}.pem"
|
||||
key="${destdir}/${hostname}.key"
|
||||
|
||||
if [ -f "$pem" -o -f "$key" ]; then
|
||||
if [ $force = no ]; then
|
||||
while :; do
|
||||
printf "Overwrite existing certificate $pem? [y/n] "
|
||||
if ! read -r reply; then
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
case "$reply" in
|
||||
[yY]) echo "overwriting"; break ;;
|
||||
[nN]) echo "quitting"; exit 0 ;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $ec = yes ]; then
|
||||
openssl ecparam -name secp384r1 -genkey -noout -out "${key}" && \
|
||||
openssl req -new -x509 -key "${key}" -out "${pem}" -days "${days}" \
|
||||
-nodes -subj "/CN=$hostname"
|
||||
else
|
||||
openssl req -x509 \
|
||||
-newkey rsa:4096 \
|
||||
-out "${pem}" \
|
||||
-keyout "${key}" \
|
||||
-days "${days}" \
|
||||
-nodes \
|
||||
-subj "/CN=$hostname"
|
||||
fi
|
||||
|
||||
e=$?
|
||||
if [ $e -ne 0 ]; then
|
||||
exit $e
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "Generated files:"
|
||||
echo " $pem : certificate"
|
||||
echo " $key : private key"
|
|
@ -0,0 +1,19 @@
|
|||
[Unit]
|
||||
Description=The gmid Gemini server
|
||||
Documentation=https://gmid.omarpolo.com/
|
||||
After=network-online.target syslog.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart=/usr/local/bin/gmid -f -c /etc/gmid.conf
|
||||
ExecStop=/bin/kill -TERM $MAINPID
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
Restart=on-failure
|
||||
RestartSec=30
|
||||
StandardOutput=journal
|
||||
StandardError=inherit
|
||||
SyslogIdentifier=gmid
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -0,0 +1 @@
|
|||
u gmid - "gmid Gemini server" - -
|
|
@ -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,200 @@
|
|||
#!/usr/bin/env perl
|
||||
#
|
||||
# Copyright (c) 2021 Omar Polo <op@omarpolo.com>
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
# You can read the documentation for this script using
|
||||
#
|
||||
# $ perldoc renew-certs
|
||||
#
|
||||
|
||||
use v5.10;
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Getopt::Std;
|
||||
use Time::Piece;
|
||||
|
||||
my $auto = 0;
|
||||
my $conf = '/etc/gmid.conf';
|
||||
my $days = 365;
|
||||
my $gmid = 'gmid';
|
||||
my $restart = 0;
|
||||
my $threshold = 24 * 60 * 60;
|
||||
|
||||
my %options = ();
|
||||
getopts("ac:d:g:r", \%options);
|
||||
|
||||
foreach my $flag (keys %options) {
|
||||
if ($flag eq 'a') {
|
||||
$auto = 1;
|
||||
} elsif ($flag eq 'c') {
|
||||
$conf = $options{c};
|
||||
} elsif ($flag eq 'd') {
|
||||
$days = int($options{d}) or exit 1;
|
||||
} elsif ($flag eq 'g') {
|
||||
$gmid = $options{g};
|
||||
} elsif ($flag eq 'r') {
|
||||
$auto = 1;
|
||||
$restart = 1;
|
||||
} elsif ($flag eq 't') {
|
||||
$threshold = int($options{t}) or exit 1;
|
||||
}
|
||||
}
|
||||
|
||||
my $now = localtime()->epoch + $threshold;
|
||||
my $found_one = 0;
|
||||
|
||||
my $c = `$gmid -nn -c $conf @ARGV 2>/dev/null`;
|
||||
die "$gmid failed to parse $conf" if $? != 0;
|
||||
|
||||
while ($c =~ /server \"(.*)\"/g) {
|
||||
my $server = $1;
|
||||
|
||||
$c =~ /cert \"(.*)\"/gc;
|
||||
my $cert = $1;
|
||||
|
||||
$c =~ /key \"(.*)\"/gc;
|
||||
my $key = $1;
|
||||
|
||||
if (expired($cert)) {
|
||||
$found_one = 1;
|
||||
if ($auto) {
|
||||
renew($server, $cert, $key);
|
||||
} else {
|
||||
say $server;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($found_one && $restart) {
|
||||
my @cmd = ("pkill", "-HUP", $gmid);
|
||||
system(@cmd);
|
||||
}
|
||||
|
||||
exit !$found_one;
|
||||
|
||||
sub expired {
|
||||
my ($cert) = @_;
|
||||
|
||||
my $exp = `openssl x509 -noout -enddate -in $cert`;
|
||||
die 'failed to execute openssl' if $? != 0;
|
||||
chomp $exp;
|
||||
|
||||
my $d = Time::Piece->strptime($exp, "notAfter=%b %e %T %Y %Z");
|
||||
return $d->epoch < $now;
|
||||
}
|
||||
|
||||
sub renew {
|
||||
my ($hostname, $cert, $key) = @_;
|
||||
my @cmd = (
|
||||
"openssl", "req", "-x509",
|
||||
"-newkey", "rsa:4096",
|
||||
"-out", $cert,
|
||||
"-keyout", $key,
|
||||
"-days", $days,
|
||||
"-nodes",
|
||||
"-subj", "/CN=".$hostname,
|
||||
);
|
||||
|
||||
system(@cmd) == 0
|
||||
or die "system @cmd failed: $?";
|
||||
}
|
||||
|
||||
__END__
|
||||
|
||||
=head1 NAME
|
||||
|
||||
B<renew-certs> - automatically renew gmid certificates
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
B<renew-certs> [-ar] [-c I<conf>] [-d I<days>] [-g I<gmid>] [-t I<threshold>] [-- I<gmid flags...>]
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
B<renew-certs> attempts to renew the certificates used by gmid if they
|
||||
are close to the expiration date and can optionally restart the
|
||||
server. It's meant to be used in a crontab(5) file.
|
||||
|
||||
B<renew-certs> needs at least B<gmid> 1.8.
|
||||
|
||||
The arguments are as follows:
|
||||
|
||||
=over
|
||||
|
||||
=item -a
|
||||
|
||||
Automatically generate a new set of certificates.
|
||||
|
||||
=item -c I<conf>
|
||||
|
||||
Path to the gmid configuration. By default is F</etc/gmid.conf>.
|
||||
|
||||
=item -d I<days>
|
||||
|
||||
Number of I<days> the newly generated certificates will be valid for;
|
||||
365 by default.
|
||||
|
||||
=item -g I<gmid>
|
||||
|
||||
Path to the gmid(1) executable.
|
||||
|
||||
=item -r
|
||||
|
||||
Restart B<gmid> after re-generating the certificates by killing it
|
||||
with SIGHUP. Implies -a.
|
||||
|
||||
=item -t I<threshold>
|
||||
|
||||
Tweak the expiring I<threshold>. Certificates whose I<notAfter> field
|
||||
ends before I<threshold> seconds will be considered outdated. By
|
||||
default is 86400, or 24 * 60 * 60, 24 hours.
|
||||
|
||||
=item I<gmid flags>
|
||||
|
||||
Additional flags to be passed to gmid(1).
|
||||
|
||||
=back
|
||||
|
||||
=head1 EXIT STATUS
|
||||
|
||||
The B<renew-certs> utility exits on 0 when at least one certificate is
|
||||
about to expire and >0 otherwise, or if an error occurs.
|
||||
|
||||
=head1 EXAMPLES
|
||||
|
||||
Some examples of how to use B<renew-certs> in a crontab(5) file
|
||||
follows:
|
||||
|
||||
# automatically renew and restart gmid
|
||||
0 0 * * * renew-certs -r
|
||||
|
||||
# like the previous, but pass a custom flag to gmid
|
||||
0 0 * * * renew-certs -r -- -Dcerts=/etc/ssl/
|
||||
|
||||
# automatically renew the certs but use a custom
|
||||
# command (rcctl in this case) to restart the server
|
||||
0 0 * * * renew-certs -a && rcctl restart gmid
|
||||
|
||||
# only check for expiration. `cmd' can read the names of the
|
||||
# servers with an expiring certificate from stdin, one per
|
||||
# line
|
||||
0 0 * * * renew-certs | cmd
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
crontab(1) gmid(1) openssl(1) crontab(5)
|
||||
|
||||
=cut
|
|
@ -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:
|
|
@ -0,0 +1,7 @@
|
|||
" Vim filetype detection file
|
||||
" Language: gmid(1) configuration files
|
||||
" Licence: ISC
|
||||
|
||||
au BufNewFile,BufRead *.gmid set filetype=gmid
|
||||
au BufNewFile,BufRead */etc/gmid/* set filetype=gmid
|
||||
au BufNewFile,BufRead gmid.conf set filetype=gmid
|
|
@ -0,0 +1,10 @@
|
|||
if exists("b:did_ftplugin")
|
||||
finish
|
||||
endif
|
||||
let b:did_ftplugin = 1
|
||||
let b:undo_ftplugin = "setl cms< sua<"
|
||||
|
||||
setlocal suffixesadd+=.conf,.gmid
|
||||
|
||||
" vim-commentary support
|
||||
setlocal commentstring=#\ %s
|
|
@ -0,0 +1,13 @@
|
|||
if exists("b:did_indent")
|
||||
finish
|
||||
endif
|
||||
let b:did_indent = 1
|
||||
|
||||
setlocal indentexpr=
|
||||
|
||||
" cindent actually works for simple file structure
|
||||
setlocal cindent
|
||||
" Just make sure that the comments are not reset as defs would be.
|
||||
setlocal cinkeys-=0#
|
||||
" And indentation works correctly without semicolons.
|
||||
setlocal cinoptions=+0
|
|
@ -0,0 +1,228 @@
|
|||
" Vim syntax file
|
||||
" Language: gmid(1) configuration files
|
||||
" Licence: ISC
|
||||
|
||||
if exists("b:current_syntax")
|
||||
finish
|
||||
endif
|
||||
|
||||
" Syntax Definition: {{{1
|
||||
" ==================
|
||||
syn case match
|
||||
setlocal iskeyword+=-
|
||||
|
||||
" Value Types: {{{2
|
||||
" ============
|
||||
syn keyword gmidBoolean on contained
|
||||
syn keyword gmidBoolean off contained
|
||||
|
||||
syn match gmidNumber "\<\d\+\>" display
|
||||
|
||||
syn keyword gmidStyle common contained
|
||||
syn keyword gmidStyle combined contained
|
||||
syn keyword gmidStyle legacy contained
|
||||
|
||||
syn keyword gmidFacility daemon contained
|
||||
syn keyword gmidFacility ftp contained
|
||||
syn keyword gmidFacility local0 contained
|
||||
syn keyword gmidFacility local1 contained
|
||||
syn keyword gmidFacility local2 contained
|
||||
syn keyword gmidFacility local3 contained
|
||||
syn keyword gmidFacility local4 contained
|
||||
syn keyword gmidFacility local5 contained
|
||||
syn keyword gmidFacility local6 contained
|
||||
syn keyword gmidFacility local7 contained
|
||||
syn keyword gmidFacility user contained
|
||||
|
||||
syn region gmidQuotedString start=+"+ end=+"+ skip=+\\"+
|
||||
syn region gmidQuotedString start=+'+ end=+'+ skip=+\\'+
|
||||
|
||||
syn match gmidVariable "\$\w\w*" display
|
||||
syn match gmidMacro "@\w\w*" display
|
||||
|
||||
syn cluster gmidValues contains=gmidNumber,
|
||||
\ gmidQuotedString,
|
||||
\ gmidVariable,
|
||||
\ gmidMacro,
|
||||
\ gmidDeprecated
|
||||
|
||||
" Errors: {{{2
|
||||
" ============
|
||||
" TODO: write comprehensive syntax rules so it can be checked with:
|
||||
" syn match gmidError '.'
|
||||
syn keyword gmidDeprecated ipv6 nextgroup=gmidBoolean skipwhite
|
||||
|
||||
" Comments: {{{2
|
||||
" =========
|
||||
syn match gmidComment "\s*#.*$" display
|
||||
|
||||
" Global Options: {{{2
|
||||
" ===============
|
||||
syn keyword gmidDirective chroot
|
||||
syn keyword gmidDirective include
|
||||
syn keyword gmidDirective prefork nextgroup=gmidNumber skipwhite
|
||||
syn keyword gmidDirective protocols
|
||||
syn keyword gmidDirective user
|
||||
|
||||
" Logging options
|
||||
syn match gmidDirective "\<log\s\+access\>" display
|
||||
syn match gmidDirective "\<log\s\+style\>" display
|
||||
\ nextgroup=gmidStyle skipwhite
|
||||
syn match gmidDirective "\<log\s\+syslog\>" display
|
||||
\ nextgroup=gmidBoolean skipwhite
|
||||
syn match gmidDirective "\<log\s\+syslog\s\+facility\>" display
|
||||
\ nextgroup=gmidFacility skipwhite
|
||||
|
||||
" Global Log Blocks: {{{3
|
||||
" ==================
|
||||
syn region gmidBlockLog start="log\s\+{" end="}"
|
||||
\ fold transparent
|
||||
\ contains=gmidDirectiveLog,
|
||||
\ @gmidValues
|
||||
syn keyword gmidDirectiveBlock log contained containedin=gmidBlockLog
|
||||
|
||||
syn keyword gmidDirectiveLog access contained
|
||||
syn keyword gmidDirectiveLog style contained nextgroup=gmidStyle skipwhite
|
||||
syn keyword gmidDirectiveLog syslog contained nextgroup=gmidBoolean skipwhite
|
||||
syn match gmidDirectiveLog "\<syslog\s\+facility\>" display
|
||||
\ contained nextgroup=gmidFacility skipwhite
|
||||
|
||||
" Server Blocks: {{{2
|
||||
" ==============
|
||||
syn region gmidBlockServer start="server\s\+.\+\s\+{" end="}"
|
||||
\ fold transparent
|
||||
\ contains=gmidDirectiveServer,
|
||||
\ gmidDirectiveParamServer,
|
||||
\ gmidDirectiveHost,
|
||||
\ gmidDirectiveParamHost,
|
||||
\ gmidBlockLocation,
|
||||
\ gmidBlockFastcgi,
|
||||
\ gmidBlockProxy,
|
||||
\ @gmidValues
|
||||
syn keyword gmidDirectiveBlock server contained containedin=gmidBlockServer
|
||||
|
||||
syn region gmidBlockLocation start="location\s\+.\+\s\+{" end="}"
|
||||
\ fold transparent contained
|
||||
\ contains=gmidDirectiveHost,
|
||||
\ gmidDirectiveParamHost,
|
||||
\ gmidBlockFastcgi,
|
||||
\ @gmidValues
|
||||
syn keyword gmidDirectiveBlock location contained containedin=gmidBlockLocation
|
||||
|
||||
syn match gmidDirectiveHost "\<auto\s\+index\>" display
|
||||
\ contained nextgroup=gmidBoolean skipwhite
|
||||
syn keyword gmidDirectiveHost block contained
|
||||
syn keyword gmidDirectiveParamHost return contained nextgroup=gmidNumber skipwhite
|
||||
syn match gmidDirectiveHost "\<default\s\+type\>" display contained
|
||||
syn keyword gmidDirectiveHost index contained
|
||||
syn keyword gmidDirectiveHost lang contained
|
||||
syn keyword gmidDirectiveHost log contained nextgroup=gmidBoolean skipwhite
|
||||
syn keyword gmidDirectiveHost ocsp contained
|
||||
syn keyword gmidDirectiveHost root contained
|
||||
syn match gmidDirectiveHost "\<require\s\+client\s\+ca\>" display contained
|
||||
syn keyword gmidDirectiveHost strip contained nextgroup=gmidNumber skipwhite
|
||||
|
||||
" FastCGI options
|
||||
syn match gmidDirectiveHost "\<fastcgi\s\+off\>" display contained
|
||||
syn match gmidDirectiveHost "\<fastcgi\s\+socket\>" display contained
|
||||
syn keyword gmidDirectiveParamHost tcp contained
|
||||
syn match gmidDirectiveHost "\<fastcgi\s\+strip\>" display
|
||||
\ contained nextgroup=gmidNumber skipwhite
|
||||
|
||||
" Options unavailable for `location`
|
||||
syn keyword gmidDirectiveServer alias contained
|
||||
syn keyword gmidDirectiveServer cert contained
|
||||
syn keyword gmidDirectiveServer key contained
|
||||
syn match gmidDirectiveServer "\<listen\s\+on\>" display contained
|
||||
|
||||
" Ambiguos, can be used both in `listen on` and `fastcgi socket`
|
||||
syn keyword gmidDirectiveParamHost port contained nextgroup=gmidNumber skipwhite
|
||||
|
||||
" FastCGI Blocks: {{{3
|
||||
" ===============
|
||||
syn region gmidBlockFastcgi start="fastcgi\s\+{" end="}"
|
||||
\ fold transparent contained
|
||||
\ contains=gmidDirectiveFastcgi,
|
||||
\ gmidDirectiveParamFastcgi,
|
||||
\ @gmidValues
|
||||
syn keyword gmidDirectiveBlock fastcgi contained containedin=gmidBlockFastcgi
|
||||
|
||||
syn keyword gmidDirectiveFastcgi param contained
|
||||
syn keyword gmidDirectiveFastcgi socket contained
|
||||
syn keyword gmidDirectiveParamFastcgi tcp contained
|
||||
syn keyword gmidDirectiveParamFastcgi port contained nextgroup=gmidNumber skipwhite
|
||||
syn keyword gmidDirectiveFastcgi strip contained nextgroup=gmidNumber skipwhite
|
||||
|
||||
" Proxy Blocks: {{{3
|
||||
" =============
|
||||
syn region gmidBlockProxy start="proxy\s\+\(.*\s\+\)\?{" end="}"
|
||||
\ fold transparent contained
|
||||
\ contains=gmidDirectiveProxy,
|
||||
\ gmidDirectiveParamProxy,
|
||||
\ @gmidValues
|
||||
syn keyword gmidDirectiveBlock proxy contained containedin=gmidBlockProxy
|
||||
|
||||
syn keyword gmidDirectiveParamProxy proto contained
|
||||
syn keyword gmidDirectiveParamProxy for-host contained
|
||||
|
||||
syn keyword gmidDirectiveProxy cert contained
|
||||
syn keyword gmidDirectiveProxy key contained
|
||||
syn keyword gmidDirectiveProxy protocols contained
|
||||
syn keyword gmidDirectiveProxy relay-to contained
|
||||
syn match gmidDirectiveProxy "\<require\s\+client\s\+ca\>" display contained
|
||||
syn keyword gmidDirectiveProxy sni contained
|
||||
syn keyword gmidDirectiveProxy use-tls contained nextgroup=gmidBoolean skipwhite
|
||||
syn keyword gmidDirectiveProxy verifyname contained nextgroup=gmidBoolean skipwhite
|
||||
|
||||
" Ambiguos, can be used both in `proxy` and `relay-to`
|
||||
syn keyword gmidDirectiveParamProxy port contained nextgroup=gmidNumber skipwhite
|
||||
|
||||
" Types Blocks: {{{2
|
||||
" =============
|
||||
syn region gmidBlockTypes start="types\s\+{" end="}"
|
||||
\ fold transparent
|
||||
\ contains=gmidDirectiveTypes,
|
||||
\ @gmidValues
|
||||
syn keyword gmidDirectiveBlock types contained containedin=gmidBlockTypes
|
||||
|
||||
syn keyword gmidDirectiveTypes include contained
|
||||
|
||||
" Highlighting Settings: {{{1
|
||||
" ======================
|
||||
|
||||
" Create aliases
|
||||
|
||||
hi def link gmidDirectiveLog gmidDirective
|
||||
hi def link gmidDirectiveTypes gmidDirective
|
||||
|
||||
hi def link gmidDirectiveServer gmidDirective
|
||||
hi def link gmidDirectiveParamServer gmidDirectiveParam
|
||||
|
||||
hi def link gmidDirectiveHost gmidDirective
|
||||
hi def link gmidDirectiveParamHost gmidDirectiveParam
|
||||
|
||||
hi def link gmidDirectiveFastcgi gmidDirective
|
||||
hi def link gmidDirectiveParamFastcgi gmidDirectiveParam
|
||||
|
||||
hi def link gmidDirectiveProxy gmidDirective
|
||||
hi def link gmidDirectiveParamProxy gmidDirectiveParam
|
||||
|
||||
" Map to standard types
|
||||
|
||||
hi def link gmidComment Comment
|
||||
|
||||
hi def link gmidBoolean Boolean
|
||||
hi def link gmidNumber Number
|
||||
hi def link gmidStyle Constant
|
||||
hi def link gmidFacility Constant
|
||||
hi def link gmidQuotedString String
|
||||
|
||||
hi def link gmidVariable Identifier
|
||||
hi def link gmidMacro Macro
|
||||
|
||||
hi def link gmidDirective Keyword
|
||||
hi def link gmidDirectiveBlock Function
|
||||
hi def link gmidDirectiveParam Type
|
||||
hi def link gmidDeprecated Error
|
||||
|
||||
let b:current_syntax = "gmid"
|
|
@ -0,0 +1,36 @@
|
|||
" Syntax checking plugin for syntastic
|
||||
" Language: gmid(1) configuration file
|
||||
" Licence: ISC
|
||||
|
||||
if exists('g:loaded_syntastic_gmid_gmid_checker')
|
||||
finish
|
||||
endif
|
||||
let g:loaded_syntastic_gmid_gmid_checker = 1
|
||||
|
||||
let s:save_cpo = &cpo
|
||||
set cpo&vim
|
||||
|
||||
function! SyntaxCheckers_gmid_gmid_GetLocList() dict
|
||||
let makeprg = self.makeprgBuild({ 'args': '-nc' })
|
||||
|
||||
let errorformat =
|
||||
\ '%-Gconfig OK,' .
|
||||
\ '%f:%l %tarning: %m,' .
|
||||
\ '%f:%l %trror: %m'
|
||||
|
||||
return SyntasticMake({
|
||||
\ 'makeprg': makeprg,
|
||||
\ 'errorformat': errorformat,
|
||||
\ 'defaults': {'type': 'E'},
|
||||
\ 'returns': [0, 1] })
|
||||
endfunction
|
||||
|
||||
call g:SyntasticRegistry.CreateAndRegisterChecker({
|
||||
\ 'filetype': 'gmid',
|
||||
\ 'name': 'gmid',
|
||||
\ 'exec': 'gmid'})
|
||||
|
||||
let &cpo = s:save_cpo
|
||||
unlet s:save_cpo
|
||||
|
||||
" vim: set sw=4 sts=4 et fdm=marker:
|
|
@ -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();
|
||||
}
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
/* $OpenBSD: scandir.c,v 1.21 2019/06/28 13:32:41 deraadt Exp $ */
|
||||
|
||||
/*
|
||||
* This is a modified verision of scandir that takes an explicit
|
||||
* directory file descriptor as parameter.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 1983, 1993
|
||||
* The Regents of the University of California. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. Neither the name of the University nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Scan the directory fd calling select to make a list of selected
|
||||
* directory entries then sort using qsort and compare routine dcomp.
|
||||
* Returns the number of entries and a pointer to a list of pointers to
|
||||
* struct dirent (through namelist). Returns -1 if there were any errors.
|
||||
*/
|
||||
|
||||
#include "gmid.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define MAXIMUM(a, b) (((a) > (b)) ? (a) : (b))
|
||||
|
||||
/*
|
||||
* The DIRSIZ macro is the minimum record length which will hold the directory
|
||||
* entry. This requires the amount of space in struct dirent without the
|
||||
* d_name field, plus enough space for the name and a terminating nul byte
|
||||
* (dp->d_namlen + 1), rounded up to a 4 byte boundary.
|
||||
*/
|
||||
#undef DIRSIZ
|
||||
#define DIRSIZ(dp, namlen) \
|
||||
((sizeof(struct dirent) - sizeof(dp)->d_name) + \
|
||||
((namlen + 1 + 3) &~ 3))
|
||||
|
||||
int
|
||||
scandir_fd(int fd, struct dirent ***namelist,
|
||||
int (*select)(const struct dirent *),
|
||||
int (*dcomp)(const struct dirent **, const struct dirent **))
|
||||
{
|
||||
struct dirent *d, *p, **names = NULL;
|
||||
size_t arraysz, namlen, nitems = 0;
|
||||
struct stat stb;
|
||||
DIR *dirp;
|
||||
|
||||
if ((dirp = fdopendir(fd)) == NULL)
|
||||
return (-1);
|
||||
if (fstat(fd, &stb) == -1)
|
||||
goto fail;
|
||||
|
||||
/*
|
||||
* estimate the array size by taking the size of the directory file
|
||||
* and dividing it by a multiple of the minimum size entry.
|
||||
*/
|
||||
arraysz = MAXIMUM(stb.st_size / 24, 16);
|
||||
if (arraysz > SIZE_MAX / sizeof(struct dirent *)) {
|
||||
errno = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
names = calloc(arraysz, sizeof(struct dirent *));
|
||||
if (names == NULL)
|
||||
goto fail;
|
||||
|
||||
while ((d = readdir(dirp)) != NULL) {
|
||||
if (select != NULL && !(*select)(d))
|
||||
continue; /* just selected names */
|
||||
|
||||
/*
|
||||
* Check to make sure the array has space left and
|
||||
* realloc the maximum size.
|
||||
*/
|
||||
if (nitems >= arraysz) {
|
||||
struct dirent **nnames;
|
||||
|
||||
if (fstat(fd, &stb) == -1)
|
||||
goto fail;
|
||||
|
||||
arraysz *= 2;
|
||||
if (SIZE_MAX / sizeof(struct dirent *) < arraysz)
|
||||
goto fail;
|
||||
nnames = reallocarray(names,
|
||||
arraysz, sizeof(struct dirent *));
|
||||
if (nnames == NULL)
|
||||
goto fail;
|
||||
|
||||
names = nnames;
|
||||
}
|
||||
|
||||
/*
|
||||
* Make a minimum size copy of the data
|
||||
*/
|
||||
namlen = strlen(d->d_name);
|
||||
p = malloc(DIRSIZ(d, namlen));
|
||||
if (p == NULL)
|
||||
goto fail;
|
||||
|
||||
p->d_ino = d->d_ino;
|
||||
p->d_type = d->d_type;
|
||||
memcpy(p->d_name, d->d_name, namlen + 1);
|
||||
names[nitems++] = p;
|
||||
}
|
||||
closedir(dirp);
|
||||
if (nitems && dcomp != NULL)
|
||||
qsort(names, nitems, sizeof(struct dirent *),
|
||||
(int(*)(const void *, const void *))dcomp);
|
||||
*namelist = names;
|
||||
return (nitems);
|
||||
|
||||
fail:
|
||||
while (nitems > 0)
|
||||
free(names[--nitems]);
|
||||
free(names);
|
||||
closedir(dirp);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int
|
||||
select_non_dot(const struct dirent *d)
|
||||
{
|
||||
return strcmp(d->d_name, ".");
|
||||
}
|
||||
|
||||
int
|
||||
select_non_dotdot(const struct dirent *d)
|
||||
{
|
||||
return strcmp(d->d_name, ".") && strcmp(d->d_name, "..");
|
||||
}
|
|
@ -0,0 +1,506 @@
|
|||
/*
|
||||
* Copyright (c) 2021, 2022, 2023 Omar Polo <op@omarpolo.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "gmid.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "log.h"
|
||||
|
||||
struct fcgi_header {
|
||||
unsigned char version;
|
||||
unsigned char type;
|
||||
unsigned char req_id1;
|
||||
unsigned char req_id0;
|
||||
unsigned char content_len1;
|
||||
unsigned char content_len0;
|
||||
unsigned char padding;
|
||||
unsigned char reserved;
|
||||
};
|
||||
|
||||
/*
|
||||
* number of bytes in a FCGI_HEADER. Future version of the protocol
|
||||
* will not reduce this number.
|
||||
*/
|
||||
#define FCGI_HEADER_LEN 8
|
||||
|
||||
/*
|
||||
* values for the version component
|
||||
*/
|
||||
#define FCGI_VERSION_1 1
|
||||
|
||||
/*
|
||||
* values for the type component
|
||||
*/
|
||||
#define FCGI_BEGIN_REQUEST 1
|
||||
#define FCGI_ABORT_REQUEST 2
|
||||
#define FCGI_END_REQUEST 3
|
||||
#define FCGI_PARAMS 4
|
||||
#define FCGI_STDIN 5
|
||||
#define FCGI_STDOUT 6
|
||||
#define FCGI_STDERR 7
|
||||
#define FCGI_DATA 8
|
||||
#define FCGI_GET_VALUES 9
|
||||
#define FCGI_GET_VALUES_RESULT 10
|
||||
#define FCGI_UNKNOWN_TYPE 11
|
||||
#define FCGI_MAXTYPE (FCGI_UNKNOWN_TYPE)
|
||||
|
||||
struct fcgi_begin_req {
|
||||
unsigned char role1;
|
||||
unsigned char role0;
|
||||
unsigned char flags;
|
||||
unsigned char reserved[5];
|
||||
};
|
||||
|
||||
struct fcgi_begin_req_record {
|
||||
struct fcgi_header header;
|
||||
struct fcgi_begin_req body;
|
||||
};
|
||||
|
||||
/*
|
||||
* mask for flags;
|
||||
*/
|
||||
#define FCGI_KEEP_CONN 1
|
||||
|
||||
/*
|
||||
* values for the role
|
||||
*/
|
||||
#define FCGI_RESPONDER 1
|
||||
#define FCGI_AUTHORIZER 2
|
||||
#define FCGI_FILTER 3
|
||||
|
||||
struct fcgi_end_req_body {
|
||||
unsigned char app_status3;
|
||||
unsigned char app_status2;
|
||||
unsigned char app_status1;
|
||||
unsigned char app_status0;
|
||||
unsigned char proto_status;
|
||||
unsigned char reserved[3];
|
||||
};
|
||||
|
||||
/*
|
||||
* values for proto_status
|
||||
*/
|
||||
#define FCGI_REQUEST_COMPLETE 0
|
||||
#define FCGI_CANT_MPX_CONN 1
|
||||
#define FCGI_OVERLOADED 2
|
||||
#define FCGI_UNKNOWN_ROLE 3
|
||||
|
||||
/*
|
||||
* Variable names for FCGI_GET_VALUES / FCGI_GET_VALUES_RESULT
|
||||
* records.
|
||||
*/
|
||||
#define FCGI_MAX_CONNS "FCGI_MAX_CONNS"
|
||||
#define FCGI_MAX_REQS "FCGI_MAX_REQS"
|
||||
#define FCGI_MPXS_CONNS "FCGI_MPXS_CONNS"
|
||||
|
||||
static int
|
||||
prepare_header(struct fcgi_header *h, int type, size_t size,
|
||||
size_t padding)
|
||||
{
|
||||
int id = 1;
|
||||
|
||||
memset(h, 0, sizeof(*h));
|
||||
|
||||
h->version = FCGI_VERSION_1;
|
||||
h->type = type;
|
||||
h->req_id1 = (id >> 8);
|
||||
h->req_id0 = (id & 0xFF);
|
||||
h->content_len1 = (size >> 8);
|
||||
h->content_len0 = (size & 0xFF);
|
||||
h->padding = padding;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fcgi_begin_request(struct bufferevent *bev)
|
||||
{
|
||||
struct fcgi_begin_req_record r;
|
||||
|
||||
memset(&r, 0, sizeof(r));
|
||||
prepare_header(&r.header, FCGI_BEGIN_REQUEST, sizeof(r.body), 0);
|
||||
assert(sizeof(r.body) == FCGI_HEADER_LEN);
|
||||
|
||||
r.body.role1 = 0;
|
||||
r.body.role0 = FCGI_RESPONDER;
|
||||
r.body.flags = FCGI_KEEP_CONN;
|
||||
|
||||
if (bufferevent_write(bev, &r, sizeof(r)) == -1)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fcgi_send_param(struct bufferevent *bev, const char *name,
|
||||
const char *value)
|
||||
{
|
||||
struct fcgi_header h;
|
||||
uint32_t namlen, vallen, padlen;
|
||||
uint8_t s[8];
|
||||
size_t size;
|
||||
const char padding[8] = { 0 };
|
||||
|
||||
namlen = strlen(name);
|
||||
vallen = strlen(value);
|
||||
size = namlen + vallen + 8; /* 4 for the sizes */
|
||||
padlen = (8 - (size & 0x7)) & 0x7;
|
||||
|
||||
s[0] = ( namlen >> 24) | 0x80;
|
||||
s[1] = ((namlen >> 16) & 0xFF);
|
||||
s[2] = ((namlen >> 8) & 0xFF);
|
||||
s[3] = ( namlen & 0xFF);
|
||||
|
||||
s[4] = ( vallen >> 24) | 0x80;
|
||||
s[5] = ((vallen >> 16) & 0xFF);
|
||||
s[6] = ((vallen >> 8) & 0xFF);
|
||||
s[7] = ( vallen & 0xFF);
|
||||
|
||||
prepare_header(&h, FCGI_PARAMS, size, padlen);
|
||||
|
||||
if (bufferevent_write(bev, &h, sizeof(h)) == -1 ||
|
||||
bufferevent_write(bev, s, sizeof(s)) == -1 ||
|
||||
bufferevent_write(bev, name, namlen) == -1 ||
|
||||
bufferevent_write(bev, value, vallen) == -1 ||
|
||||
bufferevent_write(bev, padding, padlen) == -1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
fcgi_end_param(struct bufferevent *bev)
|
||||
{
|
||||
struct fcgi_header h;
|
||||
|
||||
prepare_header(&h, FCGI_PARAMS, 0, 0);
|
||||
if (bufferevent_write(bev, &h, sizeof(h)) == -1)
|
||||
return -1;
|
||||
|
||||
prepare_header(&h, FCGI_STDIN, 0, 0);
|
||||
if (bufferevent_write(bev, &h, sizeof(h)) == -1)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int
|
||||
recid(struct fcgi_header *h)
|
||||
{
|
||||
return h->req_id0 + (h->req_id1 << 8);
|
||||
}
|
||||
|
||||
static inline int
|
||||
reclen(struct fcgi_header *h)
|
||||
{
|
||||
return h->content_len0 + (h->content_len1 << 8);
|
||||
}
|
||||
|
||||
static void
|
||||
fcgi_handle_stdout(struct client *c, struct evbuffer *src, size_t len)
|
||||
{
|
||||
struct bufferevent *bev = c->cgibev;
|
||||
char *t;
|
||||
size_t l;
|
||||
int code;
|
||||
|
||||
if (c->code == 0) {
|
||||
l = len;
|
||||
if (l > sizeof(c->sbuf) - c->soff)
|
||||
l = sizeof(c->sbuf) - c->soff;
|
||||
|
||||
memcpy(&c->sbuf[c->soff], EVBUFFER_DATA(src), l);
|
||||
c->soff += l;
|
||||
evbuffer_drain(src, l);
|
||||
len -= l;
|
||||
|
||||
if ((t = memmem(c->sbuf, c->soff, "\r\n", 2)) == NULL) {
|
||||
if (c->soff == sizeof(c->sbuf)) {
|
||||
log_warnx("FastCGI application is trying to"
|
||||
" send a header that's too long.");
|
||||
fcgi_error(bev, EVBUFFER_ERROR, c);
|
||||
}
|
||||
|
||||
/* wait a bit */
|
||||
return;
|
||||
}
|
||||
*t = '\0';
|
||||
t += 2; /* skip CRLF */
|
||||
|
||||
if (!isdigit((unsigned char)c->sbuf[0]) ||
|
||||
!isdigit((unsigned char)c->sbuf[1]) ||
|
||||
c->sbuf[2] != ' ') {
|
||||
fcgi_error(bev, EVBUFFER_ERROR, c);
|
||||
return;
|
||||
}
|
||||
|
||||
code = (c->sbuf[0] - '0') * 10 + (c->sbuf[1] - '0');
|
||||
if (code < 10 || code >= 70) {
|
||||
log_warnx("FastCGI application is trying to send an"
|
||||
" invalid reply code: %d", code);
|
||||
fcgi_error(bev, EVBUFFER_ERROR, c);
|
||||
return;
|
||||
}
|
||||
|
||||
if (start_reply(c, code, c->sbuf + 3) == -1 ||
|
||||
c->code < 20 || c->code > 29) {
|
||||
fcgi_error(bev, EVBUFFER_EOF, c);
|
||||
return;
|
||||
}
|
||||
|
||||
bufferevent_write(c->bev, t, &c->sbuf[c->soff] - t);
|
||||
}
|
||||
|
||||
bufferevent_write(c->bev, EVBUFFER_DATA(src), len);
|
||||
evbuffer_drain(src, len);
|
||||
}
|
||||
|
||||
void
|
||||
fcgi_read(struct bufferevent *bev, void *d)
|
||||
{
|
||||
struct client *c = d;
|
||||
struct evbuffer *src = EVBUFFER_INPUT(bev);
|
||||
struct fcgi_header hdr;
|
||||
struct fcgi_end_req_body end;
|
||||
size_t len;
|
||||
|
||||
while (c->type != REQUEST_DONE) {
|
||||
if (EVBUFFER_LENGTH(src) < sizeof(hdr))
|
||||
return;
|
||||
|
||||
memcpy(&hdr, EVBUFFER_DATA(src), sizeof(hdr));
|
||||
|
||||
if (recid(&hdr) != 1) {
|
||||
log_warnx("got invalid client id %d from fcgi backend",
|
||||
recid(&hdr));
|
||||
goto err;
|
||||
}
|
||||
|
||||
len = reclen(&hdr);
|
||||
|
||||
if (EVBUFFER_LENGTH(src) < sizeof(hdr) + len + hdr.padding)
|
||||
return;
|
||||
|
||||
evbuffer_drain(src, sizeof(hdr));
|
||||
|
||||
switch (hdr.type) {
|
||||
case FCGI_END_REQUEST:
|
||||
if (len != sizeof(end)) {
|
||||
log_warnx("got invalid end request"
|
||||
" record size");
|
||||
goto err;
|
||||
}
|
||||
bufferevent_read(bev, &end, sizeof(end));
|
||||
|
||||
/* TODO: do something with the status? */
|
||||
c->type = REQUEST_DONE;
|
||||
break;
|
||||
|
||||
case FCGI_STDERR:
|
||||
/* discard stderr (for now) */
|
||||
evbuffer_drain(src, len);
|
||||
break;
|
||||
|
||||
case FCGI_STDOUT:
|
||||
fcgi_handle_stdout(c, src, len);
|
||||
break;
|
||||
|
||||
default:
|
||||
log_warnx("got invalid fcgi record (type=%d)",
|
||||
hdr.type);
|
||||
goto err;
|
||||
}
|
||||
|
||||
evbuffer_drain(src, hdr.padding);
|
||||
}
|
||||
|
||||
err:
|
||||
fcgi_error(bev, EVBUFFER_ERROR, c);
|
||||
client_write(c->bev, c);
|
||||
}
|
||||
|
||||
void
|
||||
fcgi_write(struct bufferevent *bev, void *d)
|
||||
{
|
||||
/*
|
||||
* There's no much use for the write callback.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
fcgi_error(struct bufferevent *bev, short err, void *d)
|
||||
{
|
||||
struct client *c = d;
|
||||
|
||||
/*
|
||||
* If we're here it means that some kind of non-recoverable
|
||||
* error happened.
|
||||
*
|
||||
* Don't free bev as we might be called by a function that
|
||||
* still uses it.
|
||||
*/
|
||||
|
||||
bufferevent_disable(bev, EVBUFFER_READ);
|
||||
|
||||
close(c->pfd);
|
||||
c->pfd = -1;
|
||||
|
||||
/* EOF and no header */
|
||||
if (c->code == 0) {
|
||||
start_reply(c, CGI_ERROR, "CGI error");
|
||||
return;
|
||||
}
|
||||
|
||||
c->type = REQUEST_DONE;
|
||||
}
|
||||
|
||||
static void
|
||||
path_translate(const char *path, struct location *loc, struct location *rloc,
|
||||
char *buf, size_t len)
|
||||
{
|
||||
const char *root, *sufx;
|
||||
|
||||
buf[0] = '\0';
|
||||
|
||||
if (*loc->dir != '\0')
|
||||
root = loc->dir;
|
||||
else if (*rloc->dir != '\0')
|
||||
root = rloc->dir;
|
||||
else
|
||||
return;
|
||||
|
||||
sufx = "";
|
||||
if (*root != '\0')
|
||||
sufx = root[strlen(root) - 1] == '/' ? "" : "/";
|
||||
|
||||
while (*path == '/')
|
||||
path++;
|
||||
|
||||
snprintf(buf, len, "%s%s%s", root, sufx, path);
|
||||
}
|
||||
|
||||
void
|
||||
fcgi_req(struct client *c, struct location *loc)
|
||||
{
|
||||
char buf[22], path[GEMINI_URL_LEN], path_tr[PATH_MAX];
|
||||
char *scriptname, *qs;
|
||||
const char *stripped, *port;
|
||||
size_t l;
|
||||
time_t tim;
|
||||
struct tm tminfo;
|
||||
struct envlist *p;
|
||||
|
||||
fcgi_begin_request(c->cgibev);
|
||||
|
||||
stripped = strip_path(c->iri.path, loc->fcgi_strip);
|
||||
if (*stripped != '/')
|
||||
snprintf(path, sizeof(path), "/%s", stripped);
|
||||
else
|
||||
strlcpy(path, stripped, sizeof(path));
|
||||
|
||||
port = c->iri.host;
|
||||
if (port == NULL || *port == '\0')
|
||||
port = "1965";
|
||||
|
||||
scriptname = "";
|
||||
TAILQ_FOREACH(p, &loc->params, envs) {
|
||||
if (!strcmp(p->name, "SCRIPT_NAME")) {
|
||||
scriptname = p->value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
l = strlen(scriptname);
|
||||
while (l > 0 && scriptname[l - 1] == '/')
|
||||
l--;
|
||||
if (!strncmp(scriptname, path, l) && (path[l] == '/' ||
|
||||
path[l] == '\0')) {
|
||||
fcgi_send_param(c->cgibev, "PATH_INFO", &path[l]);
|
||||
path_translate(&path[l], loc, TAILQ_FIRST(&c->host->locations),
|
||||
path_tr, sizeof(path_tr));
|
||||
path[l] = '\0';
|
||||
fcgi_send_param(c->cgibev, "SCRIPT_NAME", path);
|
||||
} else {
|
||||
path_translate(stripped, loc, TAILQ_FIRST(&c->host->locations),
|
||||
path_tr, sizeof(path_tr));
|
||||
fcgi_send_param(c->cgibev, "PATH_INFO", stripped);
|
||||
fcgi_send_param(c->cgibev, "SCRIPT_NAME", scriptname);
|
||||
}
|
||||
|
||||
fcgi_send_param(c->cgibev, "GATEWAY_INTERFACE", "CGI/1.1");
|
||||
fcgi_send_param(c->cgibev, "PATH_TRANSLATED", path_tr);
|
||||
fcgi_send_param(c->cgibev, "QUERY_STRING", c->iri.query);
|
||||
fcgi_send_param(c->cgibev, "REMOTE_ADDR", c->rhost);
|
||||
fcgi_send_param(c->cgibev, "REMOTE_HOST", c->rhost);
|
||||
fcgi_send_param(c->cgibev, "REQUEST_METHOD", "GET");
|
||||
fcgi_send_param(c->cgibev, "SERVER_NAME", c->iri.host);
|
||||
fcgi_send_param(c->cgibev, "SERVER_PORT", port);
|
||||
fcgi_send_param(c->cgibev, "SERVER_PROTOCOL", "GEMINI");
|
||||
fcgi_send_param(c->cgibev, "SERVER_SOFTWARE", GMID_VERSION);
|
||||
|
||||
fcgi_send_param(c->cgibev, "GEMINI_URL_PATH", c->iri.path);
|
||||
|
||||
if (*c->iri.query != '\0' &&
|
||||
strchr(c->iri.query, '=') == NULL &&
|
||||
(qs = strdup(c->iri.query)) != NULL) {
|
||||
pct_decode_str(qs);
|
||||
fcgi_send_param(c->cgibev, "GEMINI_SEARCH_STRING", qs);
|
||||
free(qs);
|
||||
}
|
||||
|
||||
TAILQ_FOREACH(p, &loc->params, envs) {
|
||||
if (!strcmp(p->name, "SCRIPT_NAME"))
|
||||
continue;
|
||||
fcgi_send_param(c->cgibev, p->name, p->value);
|
||||
}
|
||||
|
||||
if (tls_peer_cert_provided(c->ctx)) {
|
||||
fcgi_send_param(c->cgibev, "AUTH_TYPE", "CERTIFICATE");
|
||||
fcgi_send_param(c->cgibev, "REMOTE_USER",
|
||||
tls_peer_cert_subject(c->ctx));
|
||||
fcgi_send_param(c->cgibev, "TLS_CLIENT_ISSUER",
|
||||
tls_peer_cert_issuer(c->ctx));
|
||||
fcgi_send_param(c->cgibev, "TLS_CLIENT_HASH",
|
||||
tls_peer_cert_hash(c->ctx));
|
||||
fcgi_send_param(c->cgibev, "TLS_VERSION",
|
||||
tls_conn_version(c->ctx));
|
||||
fcgi_send_param(c->cgibev, "TLS_CIPHER",
|
||||
tls_conn_cipher(c->ctx));
|
||||
|
||||
snprintf(buf, sizeof(buf), "%d",
|
||||
tls_conn_cipher_strength(c->ctx));
|
||||
fcgi_send_param(c->cgibev, "TLS_CIPHER_STRENGTH", buf);
|
||||
|
||||
tim = tls_peer_cert_notbefore(c->ctx);
|
||||
strftime(buf, sizeof(buf), "%FT%TZ",
|
||||
gmtime_r(&tim, &tminfo));
|
||||
fcgi_send_param(c->cgibev, "TLS_CLIENT_NOT_BEFORE", buf);
|
||||
|
||||
tim = tls_peer_cert_notafter(c->ctx);
|
||||
strftime(buf, sizeof(buf), "%FT%TZ",
|
||||
gmtime_r(&tim, &tminfo));
|
||||
fcgi_send_param(c->cgibev, "TLS_CLIENT_NOT_AFTER", buf);
|
||||
|
||||
} else
|
||||
fcgi_send_param(c->cgibev, "AUTH_TYPE", "");
|
||||
|
||||
if (fcgi_end_param(c->cgibev) == -1)
|
||||
fcgi_error(c->cgibev, EVBUFFER_ERROR, c);
|
||||
}
|
|
@ -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 .
|
|
@ -0,0 +1,121 @@
|
|||
.\" Copyright (c) 2021, 2022 Omar Polo <op@omarpolo.com>
|
||||
.\"
|
||||
.\" Permission to use, copy, modify, and distribute this software for any
|
||||
.\" purpose with or without fee is hereby granted, provided that the above
|
||||
.\" copyright notice and this permission notice appear in all copies.
|
||||
.\"
|
||||
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.Dd $Mdocdate: October 19 2023$
|
||||
.Dt GG 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm gg
|
||||
.Nd gemini client
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Bk -words
|
||||
.Op Fl 23Nnq
|
||||
.Op Fl C Ar cert
|
||||
.Op Fl d Ar mode
|
||||
.Op Fl H Ar sni
|
||||
.Op Fl K Ar key
|
||||
.Op Fl P Ar host Ns Oo : Ns Ar port Oc
|
||||
.Op Fl T Ar seconds
|
||||
.Ar gemini://...
|
||||
.Ek
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
.Pq gemini get
|
||||
fetches the given gemini page and prints it to standard output.
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width Ds
|
||||
.It Fl 2
|
||||
Use TLSv1.2.
|
||||
.It Fl 3
|
||||
Use TLSv1.3.
|
||||
.It Fl C Ar certificate
|
||||
Use the given client
|
||||
.Ar certificate .
|
||||
.It Fl d Ar mode
|
||||
Specify what
|
||||
.Nm
|
||||
should print.
|
||||
.Ar mode
|
||||
can be one of:
|
||||
.Bl -tag -width header -compact
|
||||
.It Ic none
|
||||
print only the body of the reply, the default.
|
||||
.It Ic code
|
||||
print only the response code.
|
||||
.It Ic header
|
||||
print only the response header.
|
||||
.It Ic meta
|
||||
print only the response meta.
|
||||
.It Ic all
|
||||
print the whole response as-is.
|
||||
.El
|
||||
.It Fl H Ar sni
|
||||
Use the given
|
||||
.Ar sni
|
||||
host name instead of the one deducted by the IRI or proxy.
|
||||
.It Fl K Ar key
|
||||
Specify the key for the certificate.
|
||||
It's mandatory if
|
||||
.Fl C
|
||||
is used.
|
||||
.It Fl N
|
||||
Disables the server name verification.
|
||||
.It Fl n
|
||||
Check the given IRI for validity, but don't issue any requests.
|
||||
.It Fl P Ar host Ns Oo : Ns Ar port Oc
|
||||
Connect to the given
|
||||
.Ar host
|
||||
and
|
||||
.Ar port
|
||||
to do the request instead of the ones extracted by the IRI.
|
||||
.Ar port
|
||||
is by default 1965.
|
||||
.It Fl q
|
||||
Don't print server error messages to standard error.
|
||||
.It Fl T Ar seconds
|
||||
Kill
|
||||
.Nm
|
||||
after
|
||||
.Ar seconds .
|
||||
.El
|
||||
.Sh EXIT STATUS
|
||||
The
|
||||
.Nm
|
||||
utility exits with zero if the response code was in the 2x range.
|
||||
If a failure occurs, it exits with status code 1.
|
||||
Otherwise, the error code reflects the Gemini response code.
|
||||
.Sh ACKNOWLEDGEMENTS
|
||||
.Nm
|
||||
uses the
|
||||
.Dq Flexible and Economical
|
||||
UTF-8 decoder written by
|
||||
.An Bjoern Hoehrmann .
|
||||
.Sh SEE ALSO
|
||||
.Xr ftp 1 ,
|
||||
.Xr titan 1
|
||||
.Sh AUTHORS
|
||||
.An -nosplit
|
||||
The
|
||||
.Nm
|
||||
utility was written by
|
||||
.An Omar Polo Aq Mt op@omarpolo.com .
|
||||
.Sh CAVEATS
|
||||
.Nm
|
||||
doesn't perform TOFU
|
||||
.Pq Trust On First Use
|
||||
or any X.509 certificate validation beyond the name verification.
|
||||
.Pp
|
||||
.Nm
|
||||
doesn't follow redirects.
|
|
@ -0,0 +1,467 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2023 Omar Polo <op@omarpolo.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "gmid.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <locale.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
enum debug {
|
||||
DEBUG_NONE,
|
||||
DEBUG_CODE,
|
||||
DEBUG_HEADER,
|
||||
DEBUG_META,
|
||||
DEBUG_ALL,
|
||||
};
|
||||
|
||||
/* flags */
|
||||
int debug;
|
||||
int dont_verify_name;
|
||||
int flag2;
|
||||
int flag3;
|
||||
int nop;
|
||||
int redirects = 5;
|
||||
int timer;
|
||||
int quiet;
|
||||
const char *cert;
|
||||
const char *key;
|
||||
const char *proxy_host;
|
||||
const char *proxy_port;
|
||||
const char *sni;
|
||||
|
||||
/* state */
|
||||
struct tls_config *tls_conf;
|
||||
|
||||
static void
|
||||
timeout(int signo)
|
||||
{
|
||||
dprintf(2, "%s: timer expired\n", getprogname());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void
|
||||
load_tls_conf(void)
|
||||
{
|
||||
if ((tls_conf = tls_config_new()) == NULL)
|
||||
err(1, "tls_config_new");
|
||||
|
||||
tls_config_insecure_noverifycert(tls_conf);
|
||||
if (dont_verify_name)
|
||||
tls_config_insecure_noverifyname(tls_conf);
|
||||
|
||||
if (flag2 &&
|
||||
tls_config_set_protocols(tls_conf, TLS_PROTOCOL_TLSv1_2) == -1)
|
||||
errx(1, "can't set TLSv1.2");
|
||||
if (flag3 &&
|
||||
tls_config_set_protocols(tls_conf, TLS_PROTOCOL_TLSv1_3) == -1)
|
||||
errx(1, "can't set TLSv1.3");
|
||||
|
||||
if (cert != NULL &&
|
||||
tls_config_set_keypair_file(tls_conf, cert, key) == -1)
|
||||
errx(1, "can't load client certificate %s", cert);
|
||||
}
|
||||
|
||||
static void
|
||||
connectto(struct tls *ctx, const char *host, const char *port)
|
||||
{
|
||||
struct addrinfo hints, *res, *res0;
|
||||
int error;
|
||||
int saved_errno;
|
||||
int s;
|
||||
const char *cause = NULL;
|
||||
const char *sname;
|
||||
|
||||
if (proxy_host != NULL) {
|
||||
host = proxy_host;
|
||||
port = proxy_port;
|
||||
}
|
||||
|
||||
if ((sname = sni) == NULL)
|
||||
sname = host;
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
error = getaddrinfo(host, port, &hints, &res0);
|
||||
if (error)
|
||||
errx(1, "%s", gai_strerror(error));
|
||||
|
||||
s = -1;
|
||||
for (res = res0; res != NULL; res = res->ai_next) {
|
||||
s = socket(res->ai_family, res->ai_socktype,
|
||||
res->ai_protocol);
|
||||
if (s == -1) {
|
||||
cause = "socket";
|
||||
continue;
|
||||
}
|
||||
|
||||
if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
|
||||
cause = "connect";
|
||||
saved_errno = errno;
|
||||
close(s);
|
||||
errno = saved_errno;
|
||||
s = -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (s == -1)
|
||||
err(1, "%s: can't connect to %s:%s", cause,
|
||||
host, port);
|
||||
|
||||
freeaddrinfo(res0);
|
||||
|
||||
if (tls_connect_socket(ctx, s, sname) == -1)
|
||||
errx(1, "tls_connect_socket: %s", tls_error(ctx));
|
||||
}
|
||||
|
||||
static void
|
||||
doreq(struct tls *ctx, const char *buf)
|
||||
{
|
||||
size_t s;
|
||||
ssize_t w;
|
||||
|
||||
s = strlen(buf);
|
||||
while (s != 0) {
|
||||
switch (w = tls_write(ctx, buf, s)) {
|
||||
case 0:
|
||||
case -1:
|
||||
errx(1, "tls_write: %s", tls_error(ctx));
|
||||
case TLS_WANT_POLLIN:
|
||||
case TLS_WANT_POLLOUT:
|
||||
continue;
|
||||
}
|
||||
|
||||
s -= w;
|
||||
buf += w;
|
||||
}
|
||||
}
|
||||
|
||||
static size_t
|
||||
dorep(struct tls *ctx, uint8_t *buf, size_t len)
|
||||
{
|
||||
ssize_t w;
|
||||
size_t tot = 0;
|
||||
|
||||
while (len != 0) {
|
||||
switch (w = tls_read(ctx, buf, len)) {
|
||||
case 0:
|
||||
return tot;
|
||||
case -1:
|
||||
errx(1, "tls_write: %s", tls_error(ctx));
|
||||
case TLS_WANT_POLLIN:
|
||||
case TLS_WANT_POLLOUT:
|
||||
continue;
|
||||
}
|
||||
|
||||
len -= w;
|
||||
buf += w;
|
||||
tot += w;
|
||||
}
|
||||
|
||||
return tot;
|
||||
}
|
||||
|
||||
static void
|
||||
safeprint(FILE *fp, const char *str)
|
||||
{
|
||||
int len;
|
||||
wchar_t wc;
|
||||
|
||||
for (; *str != '\0'; str += len) {
|
||||
if ((len = mbtowc(&wc, str, MB_CUR_MAX)) == -1) {
|
||||
mbtowc(NULL, NULL, MB_CUR_MAX);
|
||||
fputc('?', fp);
|
||||
len = 1;
|
||||
} else if (wcwidth(wc) == -1) {
|
||||
fputc('?', fp);
|
||||
} else if (wc != L'\n')
|
||||
putwc(wc, fp);
|
||||
}
|
||||
|
||||
fputc('\n', fp);
|
||||
}
|
||||
|
||||
static int
|
||||
get(const char *r)
|
||||
{
|
||||
struct tls *ctx;
|
||||
struct iri iri;
|
||||
int foundhdr = 0, code = -1, od;
|
||||
char iribuf[GEMINI_URL_LEN];
|
||||
char req[GEMINI_URL_LEN];
|
||||
uint8_t buf[2048];
|
||||
const char *parse_err, *host, *port;
|
||||
|
||||
if (strlcpy(iribuf, r, sizeof(iribuf)) >= sizeof(iribuf))
|
||||
errx(1, "iri too long: %s", r);
|
||||
|
||||
if (strlcpy(req, r, sizeof(req)) >= sizeof(req))
|
||||
errx(1, "iri too long: %s", r);
|
||||
|
||||
if (strlcat(req, "\r\n", sizeof(req)) >= sizeof(req))
|
||||
errx(1, "iri too long: %s", r);
|
||||
|
||||
if (!parse_iri(iribuf, &iri, &parse_err))
|
||||
errx(1, "invalid IRI: %s", parse_err);
|
||||
|
||||
if (nop)
|
||||
errx(0, "IRI OK");
|
||||
|
||||
if ((ctx = tls_client()) == NULL)
|
||||
errx(1, "can't create tls context");
|
||||
|
||||
if (tls_configure(ctx, tls_conf) == -1)
|
||||
errx(1, "tls_configure: %s", tls_error(ctx));
|
||||
|
||||
host = iri.host;
|
||||
port = "1965";
|
||||
if (*iri.port != '\0')
|
||||
port = iri.port;
|
||||
|
||||
connectto(ctx, host, port);
|
||||
|
||||
od = 0;
|
||||
while (!od) {
|
||||
switch (tls_handshake(ctx)) {
|
||||
case 0:
|
||||
od = 1;
|
||||
break;
|
||||
case -1:
|
||||
errx(1, "handshake: %s", tls_error(ctx));
|
||||
}
|
||||
}
|
||||
|
||||
doreq(ctx, req);
|
||||
|
||||
for (;;) {
|
||||
uint8_t *t;
|
||||
size_t len;
|
||||
|
||||
len = dorep(ctx, buf, sizeof(buf));
|
||||
if (len == 0)
|
||||
break;
|
||||
|
||||
if (foundhdr) {
|
||||
write(1, buf, len);
|
||||
continue;
|
||||
}
|
||||
foundhdr = 1;
|
||||
|
||||
if (memmem(buf, len, "\r\n", 2) == NULL)
|
||||
errx(1, "invalid reply: no \\r\\n");
|
||||
if (!isdigit((unsigned char)buf[0]) ||
|
||||
!isdigit((unsigned char)buf[1]) ||
|
||||
buf[2] != ' ')
|
||||
errx(1, "invalid reply: invalid response format");
|
||||
|
||||
code = (buf[0] - '0') * 10 + buf[1] - '0';
|
||||
|
||||
if (debug == DEBUG_CODE) {
|
||||
printf("%d\n", code);
|
||||
break;
|
||||
}
|
||||
|
||||
if (debug == DEBUG_HEADER) {
|
||||
t = memmem(buf, len, "\r\n", 2);
|
||||
assert(t != NULL);
|
||||
*t = '\0';
|
||||
printf("%s\n", buf);
|
||||
break;
|
||||
}
|
||||
|
||||
if (debug == DEBUG_META) {
|
||||
t = memmem(buf, len, "\r\n", 2);
|
||||
assert(t != NULL);
|
||||
*t = '\0';
|
||||
printf("%s\n", buf+3);
|
||||
break;
|
||||
}
|
||||
|
||||
if (debug == DEBUG_ALL) {
|
||||
write(1, buf, len);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* skip the header */
|
||||
t = memmem(buf, len, "\r\n", 2);
|
||||
assert(t != NULL);
|
||||
if (code < 20 || code >= 30) {
|
||||
*t = '\0';
|
||||
if (!quiet) {
|
||||
fprintf(stderr, "Server says: ");
|
||||
/* skip return code */
|
||||
safeprint(stderr, buf + 3);
|
||||
}
|
||||
}
|
||||
t += 2; /* skip \r\n */
|
||||
len -= t - buf;
|
||||
write(1, t, len);
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
switch (tls_close(ctx)) {
|
||||
case TLS_WANT_POLLIN:
|
||||
case TLS_WANT_POLLOUT:
|
||||
continue;
|
||||
case -1:
|
||||
warnx("tls_close: %s", tls_error(ctx));
|
||||
/* fallthrough */
|
||||
default:
|
||||
tls_free(ctx);
|
||||
return code;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void __attribute__((noreturn))
|
||||
usage(void)
|
||||
{
|
||||
fprintf(stderr, "version: " GG_STRING "\n");
|
||||
fprintf(stderr, "usage: %s [-23Nnq] [-C cert] [-d mode] [-H sni] "
|
||||
"[-K key] [-P host[:port]]\n",
|
||||
getprogname());
|
||||
fprintf(stderr, " [-T seconds] gemini://...\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static int
|
||||
parse_debug(const char *arg)
|
||||
{
|
||||
if (!strcmp(arg, "none"))
|
||||
return DEBUG_NONE;
|
||||
if (!strcmp(arg, "code"))
|
||||
return DEBUG_CODE;
|
||||
if (!strcmp(arg, "header"))
|
||||
return DEBUG_HEADER;
|
||||
if (!strcmp(arg, "meta"))
|
||||
return DEBUG_META;
|
||||
if (!strcmp(arg, "all"))
|
||||
return DEBUG_ALL;
|
||||
usage();
|
||||
}
|
||||
|
||||
static void
|
||||
parse_proxy(const char *arg)
|
||||
{
|
||||
char *at;
|
||||
|
||||
if ((proxy_host = strdup(arg)) == NULL)
|
||||
err(1, "strdup");
|
||||
|
||||
proxy_port = "1965";
|
||||
|
||||
if ((at = strchr(proxy_host, ':')) == NULL)
|
||||
return;
|
||||
*at = '\0';
|
||||
proxy_port = ++at;
|
||||
|
||||
if (strchr(proxy_port, ':') != NULL)
|
||||
errx(1, "invalid port %s", proxy_port);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int ch, code;
|
||||
const char *errstr;
|
||||
|
||||
setlocale(LC_CTYPE, "");
|
||||
|
||||
while ((ch = getopt(argc, argv, "23C:d:H:K:nNP:qT:")) != -1) {
|
||||
switch (ch) {
|
||||
case '2':
|
||||
flag2 = 1;
|
||||
break;
|
||||
case '3':
|
||||
flag3 = 1;
|
||||
break;
|
||||
case 'C':
|
||||
cert = optarg;
|
||||
break;
|
||||
case 'd':
|
||||
debug = parse_debug(optarg);
|
||||
break;
|
||||
case 'H':
|
||||
sni = optarg;
|
||||
break;
|
||||
case 'K':
|
||||
key = optarg;
|
||||
break;
|
||||
case 'N':
|
||||
dont_verify_name = 1;
|
||||
break;
|
||||
case 'n':
|
||||
nop = 1;
|
||||
break;
|
||||
case 'P':
|
||||
parse_proxy(optarg);
|
||||
dont_verify_name = 1;
|
||||
break;
|
||||
case 'q':
|
||||
quiet = 1;
|
||||
break;
|
||||
case 'T':
|
||||
timer = strtonum(optarg, 1, 1000, &errstr);
|
||||
if (errstr != NULL)
|
||||
errx(1, "timeout is %s: %s",
|
||||
errstr, optarg);
|
||||
signal(SIGALRM, timeout);
|
||||
alarm(timer);
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
if (flag2 + flag3 > 1) {
|
||||
warnx("only -2 or -3 can be specified at the same time");
|
||||
usage();
|
||||
}
|
||||
|
||||
if ((cert != NULL && key == NULL) ||
|
||||
(cert == NULL && key != NULL)) {
|
||||
warnx("cert or key is missing");
|
||||
usage();
|
||||
}
|
||||
|
||||
if (argc != 1)
|
||||
usage();
|
||||
|
||||
load_tls_conf();
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
#ifdef __OpenBSD__
|
||||
if (pledge("stdio inet dns", NULL) == -1)
|
||||
err(1, "pledge");
|
||||
#endif
|
||||
|
||||
code = get(*argv);
|
||||
if (code >= 20 && code < 30)
|
||||
return 0;
|
||||
return code;
|
||||
}
|
103
gmid.1
103
gmid.1
|
@ -1,103 +0,0 @@
|
|||
.\" Copyright (c) 2020 Omar Polo <op@omarpolo.com>
|
||||
.\"
|
||||
.\" Permission to use, copy, modify, and distribute this software for any
|
||||
.\" purpose with or without fee is hereby granted, provided that the above
|
||||
.\" copyright notice and this permission notice appear in all copies.
|
||||
.\"
|
||||
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.Dd $Mdocdate: October 2 2020$
|
||||
.Dt GMIND 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm gmid
|
||||
.Nd dead simple zero configuration gemini server
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Bk -words
|
||||
.Op Fl h
|
||||
.Op Fl c Ar cert.pem
|
||||
.Op Fl d Ar docs
|
||||
.Op Fl k Ar key.pem
|
||||
.Ek
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is a very simple and minimal gemini server.
|
||||
It only supports serving static content, and strive to be as simple as
|
||||
possible.
|
||||
.Pp
|
||||
.Nm
|
||||
will strip any sequence of
|
||||
.Pa ../
|
||||
or trailing
|
||||
.Pa ..
|
||||
in the requests made by clients, so it's impossible to serve content
|
||||
outside the
|
||||
.Pa docs
|
||||
directory by mistake, and will also refuse to follow symlink.
|
||||
Furthermore, on
|
||||
.Ox ,
|
||||
.Xr pledge 2
|
||||
and
|
||||
.Xr unveil 2
|
||||
are used to ensure that
|
||||
.Nm
|
||||
dosen't do anything else than read files from the given directory and
|
||||
accept network connections.
|
||||
.Pp
|
||||
It should be noted that
|
||||
.Nm
|
||||
is very simple in its implementation, and so it may not be appropriate
|
||||
for serving site with lots of users.
|
||||
After all, the code is single threaded and use a single process.
|
||||
.Pp
|
||||
If a user request path is a directory,
|
||||
.Nm
|
||||
will try to serve a
|
||||
.Pa index.gmi
|
||||
file inside that directory.
|
||||
If not found, it will return an error 51 (not found) to the user.
|
||||
.Pp
|
||||
The options are as follows:
|
||||
.Bl -tag -width 12m
|
||||
.It Fl c Ar cert.pem
|
||||
The certificate to use, by default is
|
||||
.Pa cert.pem .
|
||||
.It Fl d Ar docs
|
||||
The root directory to serve.
|
||||
.Nm
|
||||
won't serve any file that is outside that directory.
|
||||
.It Fl h
|
||||
Print the usage and exit.
|
||||
.It Fl k Ar key.pem
|
||||
The key for the certificate, by default is
|
||||
.Pa key.pem .
|
||||
.El
|
||||
.Sh EXAMPLES
|
||||
To quickly getting started
|
||||
.Bd -literal -offset indent
|
||||
$ # generate a cert and a key
|
||||
$ openssl req -x509 -newkey rsa:4096 -keyout key.pem \\
|
||||
-out cert.pem -days 365 -nodes
|
||||
$ mkdir docs
|
||||
$ cat <<EOF > docs/index.gmi
|
||||
# Hello world
|
||||
test paragraph...
|
||||
EOF
|
||||
$ gmid -c cert.pem -k key.pem -d docs
|
||||
.Ed
|
||||
.Pp
|
||||
now you can visit gemini://localhost/ with your preferred gemini client.
|
||||
.Sh CAVEATS
|
||||
.Bl -bullet
|
||||
.It
|
||||
it doesn't support virtual hosts: the host part of the request URL is
|
||||
completely ignored.
|
||||
.It
|
||||
it doesn't fork in the background or anything like that.
|
||||
.El
|
|
@ -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
|
|
@ -0,0 +1,698 @@
|
|||
.\" Copyright (c) 2022, 2023, 2024 Omar Polo <op@omarpolo.com>
|
||||
.\"
|
||||
.\" Permission to use, copy, modify, and distribute this software for any
|
||||
.\" purpose with or without fee is hereby granted, provided that the above
|
||||
.\" copyright notice and this permission notice appear in all copies.
|
||||
.\"
|
||||
.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.Dd April 4, 2024
|
||||
.Dt GMID.CONF 5
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm gmid.conf
|
||||
.Nd gmid Gemini server configuration file
|
||||
.Sh DESCRIPTION
|
||||
.Nm
|
||||
is the configuration file format for the
|
||||
.Xr gmid 8
|
||||
Gemini server.
|
||||
.Pp
|
||||
The configuration file is divided into the following sections:
|
||||
.Bl -tag -width Ds
|
||||
.It Sy Macros
|
||||
User-defined variables may be defined and used later, simplifying the
|
||||
configuration file.
|
||||
.It Sy Global Options
|
||||
Global settings for
|
||||
.Xr gmid 8 .
|
||||
.It Sy Types
|
||||
Media types and extensions.
|
||||
.It Sy Servers
|
||||
Virtual hosts definition.
|
||||
.El
|
||||
.Pp
|
||||
Within the sections, empty lines are ignored and comments can be put
|
||||
anywhere in the file using a hash mark
|
||||
.Pq Sq #
|
||||
and extend to the end of the current line.
|
||||
A boolean is either the symbol
|
||||
.Sq on
|
||||
or
|
||||
.Sq off .
|
||||
A string is a sequence of characters wrapped in double quotes,
|
||||
.Dq like this .
|
||||
Multiple strings one next to the other are joined into a single
|
||||
string:
|
||||
.Bd -literal -offset indent
|
||||
# equivalent to "temporary-failure"
|
||||
block return 40 "temporary" "-" "failure"
|
||||
.Ed
|
||||
.Pp
|
||||
Furthermore, quoting is necessary only when a string needs to contain
|
||||
special characters
|
||||
.Pq like spaces or punctuation ,
|
||||
something that looks like a number or a reserved keyword.
|
||||
The last example could have been written also as:
|
||||
.Bd -literal -offset indent
|
||||
block return 40 temporary "-" failure
|
||||
.Ed
|
||||
.Pp
|
||||
Strict ordering of the sections is not enforced, so that is possible
|
||||
to mix macros, options and
|
||||
.Ic server
|
||||
blocks.
|
||||
However, defining all the
|
||||
.Ic server
|
||||
blocks after the macros and the global options is recommended.
|
||||
.Pp
|
||||
Newlines are often optional, except around top-level instructions, and
|
||||
semicolons
|
||||
.Dq \&;
|
||||
can also be optionally used to separate options.
|
||||
.Pp
|
||||
Additional configuration files can be included with the
|
||||
.Ic include
|
||||
keyword, for example:
|
||||
.Bd -literal -offset indent
|
||||
include "/etc/gmid.conf.local"
|
||||
.Ed
|
||||
.Ss Macros
|
||||
Macros can be defined that will later be expanded in context.
|
||||
Macro names must start with a letter, digit or underscore and may
|
||||
contain any of those characters.
|
||||
Macros names may not be reserved words.
|
||||
Macros are not expanded inside quotes.
|
||||
.Pp
|
||||
Two kinds of macros are supported: variable-like and proper macros.
|
||||
When a macro is invoked with a
|
||||
.Dq $
|
||||
before its name its expanded as a string, whereas when it's invoked
|
||||
with a
|
||||
.Dq @
|
||||
its expanded in-place.
|
||||
.Pp
|
||||
For example:
|
||||
.Bd -literal -offset indent
|
||||
ext_ip = "10.0.0.1"
|
||||
dir = "/var/gemini"
|
||||
certdir = "/etc/keys"
|
||||
common = "lang it; auto index on"
|
||||
|
||||
server "foo" {
|
||||
listen on $ext_ip
|
||||
root $dir "/foo" # "/var/gemini/foo"
|
||||
cert $certdir "/foo.pem" # "/etc/keys/foo.pem"
|
||||
key $certdir "/foo.key" # "/etc/keys/foo.key"
|
||||
@common
|
||||
}
|
||||
.Ed
|
||||
.Ss Global Options
|
||||
.Bl -tag -width 12m
|
||||
.It Ic chroot Ar path
|
||||
.Xr chroot 2
|
||||
the process to the given
|
||||
.Ar path .
|
||||
The daemon has to be run with root privileges and thus the option
|
||||
.Ic user
|
||||
needs to be provided too, so privileges can be dropped afterwards.
|
||||
All the paths in the configuration file are relative to the chroot
|
||||
directory, except for the
|
||||
.Ic cert ,
|
||||
.Ic key
|
||||
and
|
||||
.Ic ocsp
|
||||
paths.
|
||||
Defaults to the
|
||||
.Ic user
|
||||
home directory, if provided.
|
||||
.It Ic log Ar options
|
||||
Specify logging options.
|
||||
Multiple options may be provided within curly braces.
|
||||
The available options are as follows:
|
||||
.Bl -tag -width Ds
|
||||
.It Ic access Ar file
|
||||
Log the requests to
|
||||
.Ar file .
|
||||
The path is relative to the
|
||||
.Ic chroot .
|
||||
.It Ic style Ar style
|
||||
Set the logging style, defaults to
|
||||
.Ic legacy .
|
||||
The
|
||||
.Ar style
|
||||
can be one of:
|
||||
.Bl -tag -width Ds
|
||||
.It Ic common
|
||||
Attempt to be compatible with the default Apache httpd log format.
|
||||
Each line is formatted as follows: the matching host name,
|
||||
the remote IP address, one dash
|
||||
.Sq - ,
|
||||
Common Name of the client certificate
|
||||
.Pq if provided, '-' otherwise ,
|
||||
the timestamp of the request, the request URI wrapped in double quotes,
|
||||
the response code and the size of the response.
|
||||
.It Ic combined
|
||||
Attempt to be compatible with the default nginx log format.
|
||||
Each line is formatted as follows: the remote IP address, one dash
|
||||
.Sq - ,
|
||||
Common Name of the client certificate
|
||||
.Pq if provided, '-' otherwise ,
|
||||
the timestamp wrapped in square brackets, the request URI wrapped in
|
||||
double quotes, the response code, the size of the response, a dash
|
||||
wrapped in double quotes and "".
|
||||
The strangness of these two last fields is because Gemini doesn't have
|
||||
the notion of the
|
||||
.Dq Referer
|
||||
header nor the
|
||||
.Dq User-agent .
|
||||
.\" .It Ic condensed
|
||||
.\" The native
|
||||
.\" .Xr gmid 8
|
||||
.\" format since 2.0.
|
||||
.\" Each line is formatted as follows: RFC 3339 date time,
|
||||
.\" remote IP address, Common Name of the client certificate
|
||||
.\" .Pq if provided, '-' otherwise ,
|
||||
.\" the matching host name, the request URI, the size of the request,
|
||||
.\" the size of the response, the response code and meta.
|
||||
.It Ic legacy
|
||||
Each line is formatted as follows: the remote IP address and port, the
|
||||
.Sq GET
|
||||
keyword, the request URI, the response code and meta.
|
||||
.El
|
||||
.It Ic syslog Op Ic off
|
||||
Log to syslog.
|
||||
It is enabled by default, use the
|
||||
.Ic off
|
||||
argument to disable.
|
||||
.It Ic syslog facility Ar facility
|
||||
Log to
|
||||
.Xr syslog 3
|
||||
using specified
|
||||
.Ar facility .
|
||||
Available facilities are as follows: daemon, ftp, local0 through local7 and
|
||||
user.
|
||||
These are case insensitive and can be prefixed with
|
||||
.Sq LOG_ .
|
||||
Not all level may be available on all operating systems.
|
||||
The default facility is
|
||||
.Ev LOG_DAEMON .
|
||||
.El
|
||||
.It Ic prefork Ar number
|
||||
Run the specified number of server processes.
|
||||
This increases the performance and prevents delays when connecting to
|
||||
a server.
|
||||
.Xr gmid 8
|
||||
runs 3 server processes by default.
|
||||
The maximum number allowed is 16.
|
||||
.It Ic protocols Ar string
|
||||
Specify the TLS protocols to enable.
|
||||
Refer to
|
||||
.Xr tls_config_parse_protocols 3
|
||||
for the valid protocol string values.
|
||||
By default, both TLSv1.3 and TLSv1.2 are enabled.
|
||||
Use
|
||||
.Dq tlsv1.3
|
||||
to enable only TLSv1.3.
|
||||
.It Ic user Ar string
|
||||
Run the daemon as the given user.
|
||||
Mandatory if the
|
||||
.Ic chroot
|
||||
option is used.
|
||||
.El
|
||||
.Ss Servers
|
||||
Every virtual host is defined by a
|
||||
.Ic server
|
||||
block:
|
||||
.Bl -tag -width Ds
|
||||
.It Ic server Ar hostname Brq ...
|
||||
Match the server name using shell globbing rules.
|
||||
It can be an explicit name,
|
||||
.Ar www.example.com ,
|
||||
or a name including wildcards,
|
||||
.Ar *.example.com .
|
||||
.El
|
||||
.Pp
|
||||
Followed by a block of options that is enclosed in curly brackets:
|
||||
.Bl -tag -width Ds
|
||||
.It Ic alias Ar name
|
||||
Specify an additional alias
|
||||
.Ar name
|
||||
for this server.
|
||||
.It Ic auto Ic index Ar bool
|
||||
If no index file is found, automatically generate a directory listing.
|
||||
Disabled by default.
|
||||
.It Ic block Op Ic return Ar code Op Ar meta
|
||||
Send a reply and close the connection;
|
||||
by default
|
||||
.Ar code
|
||||
is 40
|
||||
and
|
||||
.Ar meta
|
||||
is
|
||||
.Dq temporary failure .
|
||||
If
|
||||
.Ar code
|
||||
is in the 3x range, then
|
||||
.Ar meta
|
||||
is mandatory.
|
||||
Inside
|
||||
.Ar meta ,
|
||||
the following special sequences are supported:
|
||||
.Bl -tag -width Ds -compact
|
||||
.It \&%\&%
|
||||
is replaced with a single
|
||||
.Sq \&% .
|
||||
.It \&%p
|
||||
is replaced with the request path.
|
||||
.It \&%q
|
||||
is replaced with the query string of the request.
|
||||
.It \&%P
|
||||
is replaced with the server port.
|
||||
.It \&%N
|
||||
is replaced with the server name.
|
||||
.El
|
||||
.It Ic cert Ar file
|
||||
Path to the certificate to use for this server.
|
||||
.Ar file
|
||||
should contain a PEM encoded certificate.
|
||||
This option is mandatory.
|
||||
.It Ic default type Ar string
|
||||
Set the default media type that is used if the media type for a
|
||||
specified extension is not found.
|
||||
If not specified, the
|
||||
.Ic default type
|
||||
is set to
|
||||
.Dq application/octet-stream .
|
||||
.It Ic fastcgi Ar option
|
||||
Enable FastCGI instead of serving files.
|
||||
Multiple options may be specified within curly braces.
|
||||
Valid options are:
|
||||
.Bl -tag -width Ds
|
||||
.It Ic param Ar name Cm = Ar value
|
||||
Set the param
|
||||
.Ar name
|
||||
to
|
||||
.Ar value .
|
||||
.It Ic socket Oo Ic tcp Oc Ar socket Oo Cm port Ar port Oc
|
||||
The
|
||||
.Ar socket
|
||||
can either be a UNIX-domain socket or a TCP socket.
|
||||
If the FastCGI application is listening on a UNIX domain socket,
|
||||
.Ar socket
|
||||
is a local path name within the
|
||||
.Xr chroot 2
|
||||
root directory of
|
||||
.Xr gmid 8 .
|
||||
Otherwise, the
|
||||
.Ic tcp
|
||||
keyword must be provided and
|
||||
.Ar socket
|
||||
is interpreted as a hostname or an IP address.
|
||||
.Ar port
|
||||
can be either a port number or the name of a service enclosed in
|
||||
double quotes.
|
||||
If not specified defaults to 9000.
|
||||
.It Ic strip Ar number
|
||||
Strip
|
||||
.Ar number
|
||||
leading path components from the request URL before splitting it in
|
||||
.Ev SCRIPT_NAME
|
||||
and
|
||||
.Ev PATH_INFO .
|
||||
.El
|
||||
.Pp
|
||||
The FastCGI handler will be given the following variables by default:
|
||||
.Bl -tag -width 24m
|
||||
.\" .It Ev GEMINI_DOCUMENT_ROOT
|
||||
.\" The root directory of the virtual host.
|
||||
.It Ev GEMINI_URL_PATH
|
||||
Full path of the request.
|
||||
.It Ev GEMINI_SEARCH_STRING
|
||||
The decoded
|
||||
.Ev QUERY_STRING
|
||||
if defined in the request and if it doesn't contain any unencoded
|
||||
.Sq =
|
||||
characters, otherwise unset.
|
||||
.It Ev GATEWAY_INTERFACE
|
||||
.Dq CGI/1.1
|
||||
.It Ev AUTH_TYPE
|
||||
The string "Certificate" if the client used a certificate, otherwise
|
||||
unset.
|
||||
.It Ev PATH_INFO
|
||||
The portion of the requested path that is derived from the the IRI
|
||||
path hierarchy following
|
||||
.Ev SCRIPT_NAME .
|
||||
Can be unset.
|
||||
.It Ev PATH_TRANSLATED
|
||||
Present if and only if
|
||||
.Ev PATH_INFO
|
||||
is set.
|
||||
It represent the translation of the
|
||||
.Ev PATH_INFO .
|
||||
.Nm gmid
|
||||
builds this by appending the
|
||||
.Ev PATH_INFO
|
||||
to the virtual host directory root.
|
||||
.It Ev QUERY_STRING
|
||||
The URL-encoded search or parameter string.
|
||||
.It Ev REMOTE_ADDR , Ev REMOTE_HOST
|
||||
Textual representation of the client IP.
|
||||
.It Ev REQUEST_METHOD
|
||||
This is present only for RFC3875 (CGI) compliance.
|
||||
It's always set to
|
||||
.Dq GET .
|
||||
.It Ev SCRIPT_NAME
|
||||
The virtual URI path to the script.
|
||||
Since it's impossible to determine in all cases the correct
|
||||
.Ev SCRIPT_NAME
|
||||
programmatically
|
||||
.Nm gmid
|
||||
assumes it's the empty string.
|
||||
It is recommended to manually specify this parameter when serving a
|
||||
sub-tree of a virtual host via FastCGI.
|
||||
.It Ev SERVER_NAME
|
||||
The name of the server
|
||||
.It Ev SERVER_PORT
|
||||
The port the server is listening on.
|
||||
.It Ev SERVER_PROTOCOL
|
||||
.Dq GEMINI
|
||||
.It Ev SERVER_SOFTWARE
|
||||
The name and version of the server, i.e.
|
||||
.Dq gmid/2.0.2
|
||||
.It Ev REMOTE_USER
|
||||
The subject of the client certificate if provided, otherwise unset.
|
||||
.It Ev TLS_CLIENT_ISSUER
|
||||
The is the issuer of the client certificate if provided, otherwise
|
||||
unset.
|
||||
.It Ev TLS_CLIENT_HASH
|
||||
The hash of the client certificate if provided, otherwise unset.
|
||||
The format is
|
||||
.Dq ALGO:HASH .
|
||||
.It Ev TLS_VERSION
|
||||
The TLS version negotiated with the peer.
|
||||
.It Ev TLS_CIPHER
|
||||
The cipher suite negotiated with the peer.
|
||||
.It Ev TLS_CIPHER_STRENGTH
|
||||
The strength in bits for the symmetric cipher that is being used with
|
||||
the peer.
|
||||
.It Ev TLS_CLIENT_NOT_AFTER
|
||||
The time corresponding to the end of the validity period of the peer
|
||||
certificate in the ISO 8601 format
|
||||
.Pq e.g. Dq 2021-02-07T20:17:41Z .
|
||||
.It Ev TLS_CLIENT_NOT_BEFORE
|
||||
The time corresponding to the start of the validity period of the peer
|
||||
certificate in the ISO 8601 format.
|
||||
.El
|
||||
.It Ic fastcgi off
|
||||
Disable FastCGI handling in the current location.
|
||||
.It Ic index Ar string
|
||||
Set the directory index file.
|
||||
If not specified, it defaults to
|
||||
.Pa index.gmi .
|
||||
.It Ic key Ar file
|
||||
Specify the private key to use for this server.
|
||||
.Ar file
|
||||
should contain a PEM encoded private key.
|
||||
This option is mandatory.
|
||||
.It Ic lang Ar string
|
||||
Specify the language tag for the text/gemini content served.
|
||||
If not specified, no
|
||||
.Dq lang
|
||||
parameter will be added in the response.
|
||||
.It Ic listen on Ar address Op Ic port Ar number
|
||||
Set the listen
|
||||
.Ar address
|
||||
and
|
||||
.Ar port
|
||||
which defaults to
|
||||
.Sq 1965 .
|
||||
This statement can be specified multiple times.
|
||||
If
|
||||
.Ar address
|
||||
is
|
||||
.Sq *
|
||||
then
|
||||
.Xr gmid 8
|
||||
will listen on all IPv4 and IPv6 addresses.
|
||||
.Ar 0.0.0.0
|
||||
can be used to listen on all IPv4 addresses and
|
||||
.Ar ::
|
||||
on all IPv6 addresses.
|
||||
.It Ic location Ar path Brq ...
|
||||
Specify server configuration rules for a specific location.
|
||||
.Ar path
|
||||
argument will be matched against the request path with shell globbing
|
||||
rules.
|
||||
In case of multiple location statements in the same context, the first
|
||||
matching location will be put into effect and the later ones ignored.
|
||||
Therefore is advisable to match for more specific paths first and for
|
||||
generic ones later on.
|
||||
A
|
||||
.Ic location
|
||||
section may include most of the server configuration rules
|
||||
except
|
||||
.Ic alias , Ic cert , Ic key , Ic listen , Ic location
|
||||
and
|
||||
.Ic proxy .
|
||||
.It Ic log Ar bool
|
||||
Enable or disable the logging for the current server or location block.
|
||||
.It Ic ocsp Ar file
|
||||
Specify an OCSP response to be stapled during TLS handshakes
|
||||
with this server.
|
||||
The
|
||||
.Ar file
|
||||
should contain a DER-format OCSP response retrieved from an
|
||||
OCSP server for the
|
||||
.Ic cert
|
||||
in use.
|
||||
If the OCSP response in
|
||||
.Ar file
|
||||
is empty, OCSP stapling will not be used.
|
||||
The default is to not use OCSP stapling.
|
||||
.It Ic proxy Oo Cm proto Ar name Oc Oo Cm for-host Ar host Oo Cm port Ar port Oc Oc Brq ...
|
||||
Set up a reverse proxy.
|
||||
The optional matching rules
|
||||
.Cm proto
|
||||
and
|
||||
.Cm for-host
|
||||
can be used to enable proxying only for protocols matching
|
||||
.Ar name
|
||||
.Po Dq gemini
|
||||
by default
|
||||
.Pc
|
||||
and/or whose request IRI matches
|
||||
.Ar host
|
||||
and
|
||||
.Ar port
|
||||
.Pq 1965 by default .
|
||||
Matching happens using shell globbing rules.
|
||||
.Pp
|
||||
In case of multiple matching proxy blocks in the same context, the
|
||||
first matching proxy will be put into effect and the later ones
|
||||
ignored.
|
||||
.Pp
|
||||
Valid options are:
|
||||
.Bl -tag -width Ds
|
||||
.It Ic cert Ar file
|
||||
Specify the client certificate to use when making requests.
|
||||
.It Ic key Ar file
|
||||
Specify the client certificate key to use when making requests.
|
||||
.It Ic protocols Ar string
|
||||
Specify the TLS protocols allowed when making remote requests.
|
||||
Refer to the
|
||||
.Xr tls_config_parse_protocols 3
|
||||
function for the valid protocol string values.
|
||||
By default, both TLSv1.2 and TLSv1.3 are enabled.
|
||||
.It Ic relay-to Ar host Op Cm port Ar port
|
||||
Relay the request to the given
|
||||
.Ar host
|
||||
at the given
|
||||
.Ar port ,
|
||||
1965 by default.
|
||||
This is the only mandatory option in a
|
||||
.Ic proxy
|
||||
block.
|
||||
.It Ic require Ic client Ic ca Ar file
|
||||
Allow the proxying only from clients that provide a certificate
|
||||
signed by the CA certificate in
|
||||
.Ar file .
|
||||
.It Ic sni Ar hostname
|
||||
Use the given
|
||||
.Ar hostname
|
||||
instead of the one extracted from the
|
||||
.Ic relay-to
|
||||
rule for the TLS handshake with the proxied gemini server.
|
||||
.It Ic use-tls Ar bool
|
||||
Specify whether to use TLS when connecting to the proxied host.
|
||||
Enabled by default.
|
||||
.It Ic verifyname Ar bool
|
||||
Enable or disable the TLS server name verification.
|
||||
Enabled by default.
|
||||
.El
|
||||
.It Ic root Ar directory
|
||||
Specify the root directory for this server
|
||||
.Pq alas the current Dq document root .
|
||||
It's relative to the chroot if enabled.
|
||||
.It Ic require Ic client Ic ca Ar path
|
||||
Allow requests only from clients that provide a certificate signed by
|
||||
the CA certificate in
|
||||
.Ar path .
|
||||
It needs to be a PEM-encoded certificate and it's not relative to the
|
||||
chroot.
|
||||
.It Ic strip Ar number
|
||||
Strip
|
||||
.Ar number
|
||||
components from the beginning of the path before doing a lookup in the
|
||||
root directory.
|
||||
It's also considered for the
|
||||
.Ar meta
|
||||
parameter in the scope of a
|
||||
.Ic block return .
|
||||
.El
|
||||
.Ss Types
|
||||
The
|
||||
.Ic types
|
||||
section must include one or more lines of the following syntax, enclosed
|
||||
in curly brances:
|
||||
.Bl -tag -width Ds
|
||||
.It Ar type Ns / Ns Ar subtype Ar name Op Ar name ...
|
||||
Set the media
|
||||
.Ar type
|
||||
and
|
||||
.Ar subtype
|
||||
to the specified extension
|
||||
.Ar name .
|
||||
One or more names can be specified per line.
|
||||
Earch line may end with an optional semicolon.
|
||||
.It Ic include Ar file
|
||||
Include types definition from an external file, for example
|
||||
.Pa /usr/share/misc/mime.types .
|
||||
.El
|
||||
.Pp
|
||||
By default
|
||||
.Nm gmid
|
||||
uses the following mapping if no
|
||||
.Ic types
|
||||
block is defined:
|
||||
.Pp
|
||||
.Bl -tag -offset indent -width 15m -compact
|
||||
.It application/pdf
|
||||
pdf
|
||||
.It image/gif
|
||||
gif
|
||||
.It image/jpeg
|
||||
jpg jpeg
|
||||
.It image/png
|
||||
png
|
||||
.It image/svg+xml
|
||||
svg
|
||||
.It text/gemini
|
||||
gemini gmi
|
||||
.It text/markdown
|
||||
markdown md
|
||||
.It text/x-patch
|
||||
diff patch
|
||||
.It text/xml
|
||||
xml
|
||||
.El
|
||||
.Pp
|
||||
As an exception,
|
||||
.Nm gmid
|
||||
uses the MIME type
|
||||
.Ar text/gemini
|
||||
for file extensions
|
||||
.Ar gemini
|
||||
or
|
||||
.Ar gmi
|
||||
if no mapping was found.
|
||||
.Sh EXAMPLES
|
||||
The following is an example of a possible configuration for a site
|
||||
that enables only TLSv1.3, adds the MIME types mapping from
|
||||
.Pa /usr/share/misc/mime.types
|
||||
and defines two virtual host:
|
||||
.Bd -literal -offset indent
|
||||
protocols "tlsv1.3"
|
||||
|
||||
types {
|
||||
include "/usr/share/misc/mime.types"
|
||||
}
|
||||
|
||||
server "example.com" {
|
||||
listen on * port 1965
|
||||
cert "/etc/ssl/example.com.pem"
|
||||
key "/etc/ssl/private/example.com.key"
|
||||
root "/var/gemini/example.com"
|
||||
}
|
||||
|
||||
server "example.it" {
|
||||
listen on * port 1965
|
||||
cert "/etc/ssl/example.it.pem"
|
||||
key "/etc/ssl/private/example.it.key"
|
||||
root "/var/gemini/example.it"
|
||||
|
||||
# set the language for text/gemini files
|
||||
lang "it"
|
||||
}
|
||||
.Ed
|
||||
.Pp
|
||||
Yet another example, showing how to enable a
|
||||
.Ic chroot
|
||||
and use
|
||||
.Ic location
|
||||
rule
|
||||
.Bd -literal -offset indent
|
||||
chroot "/var/gemini"
|
||||
user "_gmid"
|
||||
|
||||
server "example.com" {
|
||||
listen on * port 1965
|
||||
|
||||
# absolute paths:
|
||||
cert "/etc/ssl/example.com.pem"
|
||||
key "/etc/ssl/private/example.com.key"
|
||||
|
||||
# relative to the chroot:
|
||||
root "/example.com"
|
||||
|
||||
location "/static/*" {
|
||||
# load the following rules only for
|
||||
# requests that matches "/static/*"
|
||||
|
||||
auto index on
|
||||
index "index.gemini"
|
||||
}
|
||||
}
|
||||
.Ed
|
||||
.Pp
|
||||
This shows how to set up a reverse proxy: all request for
|
||||
.Sq example.com
|
||||
will be forwarded to 10.0.0.6 transparently.
|
||||
Proxying establish a new TLS connection, so any client-certificates used
|
||||
to connect to
|
||||
.Xr gmid 8
|
||||
cannot be provided to the proxied server as well.
|
||||
.Bd -literal -offset indent
|
||||
server "example.com" {
|
||||
listen on * port 1965
|
||||
cert "/etc/ssl/example.com.pem"
|
||||
key "/etc/ssl/private/example.com.key"
|
||||
proxy {
|
||||
relay-to 10.0.0.6 port 1965
|
||||
}
|
||||
}
|
||||
.Ed
|
||||
.Sh SEE ALSO
|
||||
.Xr gmid 8 ,
|
||||
.Xr slowcgi 8
|
||||
.Sh AUTHORS
|
||||
.An -nosplit
|
||||
The
|
||||
.Nm gmid
|
||||
program was written by
|
||||
.An Omar Polo Aq Mt op@omarpolo.com .
|
|
@ -0,0 +1,473 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2021, 2022, 2023 Omar Polo <op@omarpolo.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef GMID_H
|
||||
#define GMID_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <dirent.h>
|
||||
#include <limits.h>
|
||||
#include <netdb.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <tls.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <openssl/x509.h>
|
||||
|
||||
#if HAVE_EVENT2
|
||||
# include <event2/event.h>
|
||||
# include <event2/event_compat.h>
|
||||
# include <event2/event_struct.h>
|
||||
# include <event2/buffer.h>
|
||||
# include <event2/buffer_compat.h>
|
||||
# include <event2/bufferevent.h>
|
||||
# include <event2/bufferevent_struct.h>
|
||||
# include <event2/bufferevent_compat.h>
|
||||
#else
|
||||
# include <event.h>
|
||||
#endif
|
||||
|
||||
#include "iri.h"
|
||||
|
||||
#define VERSION_STR(n) n " " VERSION
|
||||
#define GEMEXP_STRING VERSION_STR("gemexp")
|
||||
#define GG_STRING VERSION_STR("gg")
|
||||
#define GMID_STRING VERSION_STR("gmid")
|
||||
|
||||
#define GMID_VERSION "gmid/" VERSION
|
||||
|
||||
#define GEMINI_URL_LEN (1024+3) /* URL max len + \r\n + \0 */
|
||||
|
||||
#define SUCCESS 20
|
||||
#define TEMP_REDIRECT 30
|
||||
#define TEMP_FAILURE 40
|
||||
#define CGI_ERROR 42
|
||||
#define PROXY_ERROR 43
|
||||
#define NOT_FOUND 51
|
||||
#define PROXY_REFUSED 53
|
||||
#define BAD_REQUEST 59
|
||||
#define CLIENT_CERT_REQ 60
|
||||
#define CERT_NOT_AUTH 61
|
||||
|
||||
/* maximum hostname and label length, +1 for the NUL-terminator */
|
||||
#define DOMAIN_NAME_LEN (253+1)
|
||||
#define LABEL_LEN (63+1)
|
||||
|
||||
#define MEDIATYPE_NAMEMAX 128 /* file name extension */
|
||||
#define MEDIATYPE_TYPEMAX 128 /* length of type/subtype */
|
||||
|
||||
#define FCGI_NAME_MAX 511
|
||||
#define FCGI_VAL_MAX 511
|
||||
|
||||
#define PROC_MAX_INSTANCES 16
|
||||
|
||||
#define TLS_CERT_HASH_SIZE 128
|
||||
|
||||
/* forward declaration */
|
||||
struct privsep;
|
||||
struct privsep_proc;
|
||||
|
||||
enum log_format {
|
||||
LOG_FORMAT_CONDENSED,
|
||||
LOG_FORMAT_COMMON,
|
||||
LOG_FORMAT_COMBINED,
|
||||
LOG_FORMAT_LEGACY,
|
||||
};
|
||||
|
||||
struct parser {
|
||||
char *iri;
|
||||
struct iri *parsed;
|
||||
const char *err;
|
||||
};
|
||||
|
||||
struct conf;
|
||||
TAILQ_HEAD(addrhead, address);
|
||||
struct address {
|
||||
int ai_flags;
|
||||
int ai_family;
|
||||
int ai_socktype;
|
||||
int ai_protocol;
|
||||
struct sockaddr_storage ss;
|
||||
socklen_t slen;
|
||||
int16_t port;
|
||||
|
||||
/* pretty-printed version of `ss' */
|
||||
char pp[NI_MAXHOST];
|
||||
|
||||
/* used in the server */
|
||||
struct conf *conf;
|
||||
int sock;
|
||||
struct event evsock; /* set if sock != -1 */
|
||||
struct tls *ctx;
|
||||
|
||||
TAILQ_ENTRY(address) addrs;
|
||||
};
|
||||
|
||||
TAILQ_HEAD(fcgihead, fcgi);
|
||||
struct fcgi {
|
||||
int id;
|
||||
char path[PATH_MAX];
|
||||
char port[32];
|
||||
TAILQ_ENTRY(fcgi) fcgi;
|
||||
};
|
||||
|
||||
TAILQ_HEAD(envhead, envlist);
|
||||
struct envlist {
|
||||
char name[FCGI_NAME_MAX];
|
||||
char value[FCGI_VAL_MAX];
|
||||
TAILQ_ENTRY(envlist) envs;
|
||||
};
|
||||
|
||||
TAILQ_HEAD(aliashead, alist);
|
||||
struct alist {
|
||||
char alias[HOST_NAME_MAX + 1];
|
||||
TAILQ_ENTRY(alist) aliases;
|
||||
};
|
||||
|
||||
TAILQ_HEAD(proxyhead, proxy);
|
||||
struct proxy {
|
||||
char match_proto[32];
|
||||
char match_host[HOST_NAME_MAX + 1];
|
||||
char match_port[32];
|
||||
|
||||
char host[HOST_NAME_MAX + 1];
|
||||
char port[32];
|
||||
char sni[HOST_NAME_MAX];
|
||||
int notls;
|
||||
uint32_t protocols;
|
||||
int noverifyname;
|
||||
char *cert_path;
|
||||
uint8_t *cert;
|
||||
size_t certlen;
|
||||
char *key_path;
|
||||
uint8_t *key;
|
||||
size_t keylen;
|
||||
char *reqca_path;
|
||||
X509_STORE *reqca;
|
||||
|
||||
TAILQ_ENTRY(proxy) proxies;
|
||||
};
|
||||
|
||||
TAILQ_HEAD(lochead, location);
|
||||
struct location {
|
||||
char match[128];
|
||||
char lang[32];
|
||||
char default_mime[MEDIATYPE_TYPEMAX];
|
||||
char index[PATH_MAX];
|
||||
int auto_index; /* 0 auto, -1 off, 1 on */
|
||||
int block_code;
|
||||
char block_fmt[GEMINI_URL_LEN];
|
||||
int strip;
|
||||
char *reqca_path;
|
||||
X509_STORE *reqca;
|
||||
int disable_log;
|
||||
int fcgi;
|
||||
int nofcgi;
|
||||
int fcgi_strip;
|
||||
struct envhead params;
|
||||
|
||||
char dir[PATH_MAX];
|
||||
int dirfd;
|
||||
|
||||
TAILQ_ENTRY(location) locations;
|
||||
};
|
||||
|
||||
TAILQ_HEAD(vhosthead, vhost);
|
||||
struct vhost {
|
||||
char domain[HOST_NAME_MAX + 1];
|
||||
char *cert_path;
|
||||
char *key_path;
|
||||
char *ocsp_path;
|
||||
|
||||
uint8_t *cert;
|
||||
size_t certlen;
|
||||
|
||||
uint8_t *key;
|
||||
size_t keylen;
|
||||
|
||||
uint8_t *ocsp;
|
||||
size_t ocsplen;
|
||||
|
||||
TAILQ_ENTRY(vhost) vhosts;
|
||||
|
||||
struct addrhead addrs;
|
||||
|
||||
/*
|
||||
* the first location rule is always '*' and holds the default
|
||||
* settings for the vhost, then follows the "real" location
|
||||
* rules as specified in the configuration.
|
||||
*/
|
||||
struct lochead locations;
|
||||
|
||||
struct aliashead aliases;
|
||||
struct proxyhead proxies;
|
||||
};
|
||||
|
||||
struct etm { /* extension to mime */
|
||||
char mime[MEDIATYPE_TYPEMAX];
|
||||
char ext[MEDIATYPE_NAMEMAX];
|
||||
};
|
||||
|
||||
struct mime {
|
||||
struct etm *t;
|
||||
size_t len;
|
||||
size_t cap;
|
||||
};
|
||||
|
||||
TAILQ_HEAD(pkihead, pki);
|
||||
struct pki {
|
||||
char *hash;
|
||||
EVP_PKEY *pkey;
|
||||
TAILQ_ENTRY(pki) pkis;
|
||||
};
|
||||
|
||||
struct conf {
|
||||
struct privsep *ps;
|
||||
uint32_t protos;
|
||||
struct mime mime;
|
||||
char chroot[PATH_MAX];
|
||||
char user[LOGIN_NAME_MAX];
|
||||
int prefork;
|
||||
int reload;
|
||||
int log_syslog;
|
||||
int log_facility;
|
||||
char *log_access;
|
||||
enum log_format log_format;
|
||||
int use_privsep_crypto;
|
||||
int conftest;
|
||||
|
||||
struct fcgihead fcgi;
|
||||
struct vhosthead hosts;
|
||||
struct pkihead pkis;
|
||||
struct addrhead addrs;
|
||||
};
|
||||
|
||||
extern const char *config_path;
|
||||
|
||||
extern int servpipes[PROC_MAX_INSTANCES];
|
||||
extern int privsep_process;
|
||||
|
||||
typedef void (imsg_handlerfn)(struct imsgbuf*, struct imsg*, size_t);
|
||||
|
||||
enum {
|
||||
REQUEST_UNDECIDED,
|
||||
REQUEST_FILE,
|
||||
REQUEST_DIR,
|
||||
REQUEST_FCGI,
|
||||
REQUEST_PROXY,
|
||||
REQUEST_DONE,
|
||||
};
|
||||
|
||||
struct client {
|
||||
struct conf *conf;
|
||||
struct address *addr;
|
||||
uint32_t id;
|
||||
struct tls *ctx;
|
||||
char *req;
|
||||
size_t reqlen;
|
||||
struct iri iri;
|
||||
char domain[DOMAIN_NAME_LEN];
|
||||
char rhost[NI_MAXHOST];
|
||||
char rserv[NI_MAXSERV];
|
||||
|
||||
struct bufferevent *bev;
|
||||
|
||||
int type;
|
||||
|
||||
struct bufferevent *cgibev;
|
||||
|
||||
struct proxy *proxy;
|
||||
struct bufferevent *proxybev;
|
||||
struct tls *proxyctx;
|
||||
int proxyevset;
|
||||
struct event proxyev;
|
||||
|
||||
char *header;
|
||||
|
||||
int code;
|
||||
const char *meta;
|
||||
int fd, pfd;
|
||||
struct dirent **dir;
|
||||
int dirlen, diroff;
|
||||
|
||||
/* big enough to store STATUS + SPACE + META + CRLF */
|
||||
char sbuf[1029];
|
||||
size_t soff;
|
||||
|
||||
struct sockaddr_storage raddr;
|
||||
socklen_t raddrlen;
|
||||
|
||||
struct vhost *host; /* host they're talking to */
|
||||
size_t loc; /* location matched */
|
||||
|
||||
SPLAY_ENTRY(client) entry;
|
||||
};
|
||||
SPLAY_HEAD(client_tree_id, client);
|
||||
extern struct client_tree_id clients;
|
||||
|
||||
struct connreq {
|
||||
char host[NI_MAXHOST];
|
||||
char port[NI_MAXSERV];
|
||||
int flag;
|
||||
};
|
||||
|
||||
enum imsg_type {
|
||||
IMSG_LOG_REQUEST,
|
||||
IMSG_LOG_ACCESS,
|
||||
IMSG_LOG_SYSLOG,
|
||||
IMSG_LOG_FACILITY,
|
||||
|
||||
IMSG_RECONF_START,
|
||||
IMSG_RECONF_LOG_FMT,
|
||||
IMSG_RECONF_MIME,
|
||||
IMSG_RECONF_PROTOS,
|
||||
IMSG_RECONF_SOCK,
|
||||
IMSG_RECONF_FCGI,
|
||||
IMSG_RECONF_HOST,
|
||||
IMSG_RECONF_CERT,
|
||||
IMSG_RECONF_KEY,
|
||||
IMSG_RECONF_OCSP,
|
||||
IMSG_RECONF_HOST_ADDR,
|
||||
IMSG_RECONF_LOC,
|
||||
IMSG_RECONF_ENV,
|
||||
IMSG_RECONF_ALIAS,
|
||||
IMSG_RECONF_PROXY,
|
||||
IMSG_RECONF_PROXY_CERT,
|
||||
IMSG_RECONF_PROXY_KEY,
|
||||
IMSG_RECONF_END,
|
||||
IMSG_RECONF_DONE,
|
||||
|
||||
IMSG_CRYPTO_RSA_PRIVENC,
|
||||
IMSG_CRYPTO_RSA_PRIVDEC,
|
||||
IMSG_CRYPTO_ECDSA_SIGN,
|
||||
|
||||
IMSG_CTL_PROCFD,
|
||||
};
|
||||
|
||||
/* gmid.c */
|
||||
char *data_dir(void);
|
||||
void load_local_cert(struct vhost*, const char*, const char*);
|
||||
|
||||
/* gmid.c / ge.c */
|
||||
void log_request(struct client *, int, const char *);
|
||||
|
||||
/* config.c */
|
||||
struct conf *config_new(void);
|
||||
void config_purge(struct conf *);
|
||||
int config_send(struct conf *);
|
||||
int config_recv(struct conf *, struct imsg *);
|
||||
int config_test(struct conf *);
|
||||
|
||||
/* crypto.c */
|
||||
void crypto(struct privsep *, struct privsep_proc *);
|
||||
void crypto_engine_init(struct conf *);
|
||||
|
||||
/* parse.y */
|
||||
void yyerror(const char*, ...);
|
||||
int parse_conf(struct conf *, const char*);
|
||||
int cmdline_symset(char *);
|
||||
|
||||
/* mime.c */
|
||||
void init_mime(struct mime*);
|
||||
int add_mime(struct mime*, const char*, const char*);
|
||||
int load_default_mime(struct mime*);
|
||||
void sort_mime(struct mime *);
|
||||
const char *mime(struct conf *, struct vhost*, const char*);
|
||||
void free_mime(struct mime *);
|
||||
|
||||
/* server.c */
|
||||
extern int shutting_down;
|
||||
const char *vhost_lang(struct vhost*, const char*);
|
||||
const char *vhost_default_mime(struct vhost*, const char*);
|
||||
const char *vhost_index(struct vhost*, const char*);
|
||||
int vhost_auto_index(struct vhost*, const char*);
|
||||
int vhost_block_return(struct vhost*, const char*, int*, const char**);
|
||||
struct location *vhost_fastcgi(struct vhost*, const char*);
|
||||
int vhost_dirfd(struct vhost*, const char*, size_t*);
|
||||
int vhost_strip(struct vhost*, const char*);
|
||||
X509_STORE *vhost_require_ca(struct vhost*, const char*);
|
||||
int vhost_disable_log(struct vhost*, const char*);
|
||||
|
||||
void mark_nonblock(int);
|
||||
void client_write(struct bufferevent *, void *);
|
||||
int start_reply(struct client*, int, const char*);
|
||||
void client_close(struct client *);
|
||||
void server_accept(int, short, void *);
|
||||
void server_init(struct privsep *, struct privsep_proc *, void *);
|
||||
int server_configure_done(struct conf *);
|
||||
void server(struct privsep *ps, struct privsep_proc *);
|
||||
|
||||
int client_tree_cmp(struct client *, struct client *);
|
||||
SPLAY_PROTOTYPE(client_tree_id, client, entry, client_tree_cmp);
|
||||
|
||||
/* dirs.c */
|
||||
int scandir_fd(int, struct dirent***, int(*)(const struct dirent*),
|
||||
int(*)(const struct dirent**, const struct dirent**));
|
||||
int select_non_dot(const struct dirent*);
|
||||
int select_non_dotdot(const struct dirent*);
|
||||
|
||||
/* fcgi.c */
|
||||
void fcgi_read(struct bufferevent *, void *);
|
||||
void fcgi_write(struct bufferevent *, void *);
|
||||
void fcgi_error(struct bufferevent *, short, void *);
|
||||
void fcgi_req(struct client *, struct location *);
|
||||
|
||||
/* sandbox.c */
|
||||
void sandbox_main_process(void);
|
||||
void sandbox_server_process(void);
|
||||
void sandbox_crypto_process(void);
|
||||
void sandbox_logger_process(void);
|
||||
|
||||
/* utf8.c */
|
||||
int valid_multibyte_utf8(struct parser*);
|
||||
char *utf8_nth(char*, size_t);
|
||||
|
||||
/* logger.c */
|
||||
void logger(struct privsep *, struct privsep_proc *);
|
||||
|
||||
/* proxy.c */
|
||||
int proxy_init(struct client *);
|
||||
|
||||
/* puny.c */
|
||||
int puny_decode(const char*, char*, size_t, const char**);
|
||||
|
||||
/* utils.c */
|
||||
const char *strip_path(const char *, int);
|
||||
int ends_with(const char*, const char*);
|
||||
char *absolutify_path(const char*);
|
||||
char *xstrdup(const char*);
|
||||
void *xcalloc(size_t, size_t);
|
||||
void gencert(const char *, const char *, const char *, int);
|
||||
X509_STORE *load_ca(uint8_t *, size_t);
|
||||
int validate_against_ca(X509_STORE*, const uint8_t*, size_t);
|
||||
void ssl_error(const char *);
|
||||
char *ssl_pubkey_hash(const uint8_t *, size_t);
|
||||
EVP_PKEY *ssl_load_pkey(const uint8_t *, size_t);
|
||||
struct vhost *new_vhost(void);
|
||||
struct location *new_location(void);
|
||||
struct proxy *new_proxy(void);
|
||||
|
||||
#endif
|
|
@ -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) 2021 Omar Polo <op@omarpolo.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <err.h>
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
warnx("%d. warnx", 1);
|
||||
warn("%d. warn", 2);
|
||||
err(0, "%d. err", 3);
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#include <string.h>
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
char buf[] = "hello world";
|
||||
|
||||
explicit_bzero(buf, sizeof(buf));
|
||||
return strcmp(buf, "");
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Omar Polo <op@omarpolo.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
freezero(NULL, 0);
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Omar Polo <op@omarpolo.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
return getdtablecount() == 0;
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (c) 2021 Omar Polo <op@omarpolo.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
return getdtablesize() == 0;
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue