mirror of
https://github.com/clearml/dropbear
synced 2025-06-26 18:17:32 +00:00
Compare commits
154 Commits
DROPBEAR_2
...
DROPBEAR_2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4318631dd6 | ||
|
|
7bc6280613 | ||
|
|
e9231f73c2 | ||
|
|
4fd40e61f7 | ||
|
|
71f818262c | ||
|
|
07f790db5a | ||
|
|
78a3388b98 | ||
|
|
6fecc91d10 | ||
|
|
8b4f60a7a1 | ||
|
|
01cd1bd11f | ||
|
|
f6df3e1fec | ||
|
|
553087b7a6 | ||
|
|
16fb0b0d53 | ||
|
|
493ab8fd81 | ||
|
|
0afcfafbb9 | ||
|
|
0af22aa8e4 | ||
|
|
2fd3b9f560 | ||
|
|
2de3bc9353 | ||
|
|
28b6111db0 | ||
|
|
a0aa274981 | ||
|
|
b016ebedfd | ||
|
|
cb945f9f67 | ||
|
|
df0f1297eb | ||
|
|
fe992bf4ea | ||
|
|
ffde4a524f | ||
|
|
7e03e4d443 | ||
|
|
02ffdd09dc | ||
|
|
6f6ef4834c | ||
|
|
d2e71ade72 | ||
|
|
892c5fc1c8 | ||
|
|
e7504b3311 | ||
|
|
ad3eacf3d6 | ||
|
|
52adbb34c3 | ||
|
|
90f04384ee | ||
|
|
195934be96 | ||
|
|
d740dc5489 | ||
|
|
76933e6c0a | ||
|
|
38c9408cf8 | ||
|
|
397af3e6a6 | ||
|
|
933bc5f8a7 | ||
|
|
ba94bcd2e8 | ||
|
|
56855744b8 | ||
|
|
b4b11c8155 | ||
|
|
f82933108d | ||
|
|
27828c742c | ||
|
|
ed4c38ba46 | ||
|
|
e9edbe8bb2 | ||
|
|
4fd3160179 | ||
|
|
a60725740b | ||
|
|
5d065258da | ||
|
|
149b21d7cf | ||
|
|
145fb96989 | ||
|
|
084ff9b4c8 | ||
|
|
6b05aa4275 | ||
|
|
366fc8f335 | ||
|
|
35f479bd87 | ||
|
|
9d11cad5dc | ||
|
|
0233dcebb4 | ||
|
|
f7dedab4a7 | ||
|
|
129c440362 | ||
|
|
abee8093b3 | ||
|
|
2583b180c9 | ||
|
|
1e1e477d85 | ||
|
|
85eda7d943 | ||
|
|
4b36e24735 | ||
|
|
a5eac0a065 | ||
|
|
7f15910541 | ||
|
|
625b38d7af | ||
|
|
b9edf939f4 | ||
|
|
257bba00ac | ||
|
|
8d0b48f165 | ||
|
|
ce1f9cdf7c | ||
|
|
e612aec5d9 | ||
|
|
5bd0c0d25a | ||
|
|
26ad6853d2 | ||
|
|
8a4f7fe4f8 | ||
|
|
294e98c397 | ||
|
|
c1a2dcb25d | ||
|
|
a0972e0ac6 | ||
|
|
8062a4e8d6 | ||
|
|
a0ab5e86b5 | ||
|
|
a1dcaf82a0 | ||
|
|
78e17f6ee9 | ||
|
|
5ebc48b3f2 | ||
|
|
1ae4237920 | ||
|
|
5ca12d8332 | ||
|
|
7f8702d3d6 | ||
|
|
5f2447edbb | ||
|
|
c658b275fd | ||
|
|
9bbce01e1b | ||
|
|
bbe02dc3cf | ||
|
|
3d76aecaa6 | ||
|
|
5df73215f8 | ||
|
|
573838a027 | ||
|
|
05f4e29a52 | ||
|
|
dbc0520992 | ||
|
|
dcb41e91eb | ||
|
|
7e8094d53a | ||
|
|
f7a664f127 | ||
|
|
89bdf3b0b9 | ||
|
|
597f7eb5e9 | ||
|
|
5cd003d9e6 | ||
|
|
4b7105dfea | ||
|
|
6ac5ea2a9f | ||
|
|
17a9b8802f | ||
|
|
9b6f7fc9af | ||
|
|
5ac4a71000 | ||
|
|
b967dc1fa5 | ||
|
|
2cbe70ba34 | ||
|
|
8e1ea0f27b | ||
|
|
e3246ceb7e | ||
|
|
88ce30beb6 | ||
|
|
723ec19eed | ||
|
|
937e6cb91e | ||
|
|
81b64ea0b5 | ||
|
|
ea0e23c172 | ||
|
|
4d07aa315b | ||
|
|
2e298b25e4 | ||
|
|
25607c04a7 | ||
|
|
cbd5be1b82 | ||
|
|
f24d93d4e4 | ||
|
|
65baa71b58 | ||
|
|
b8fa712847 | ||
|
|
095b067857 | ||
|
|
87c4586d61 | ||
|
|
b17254925d | ||
|
|
cf2c4f44a2 | ||
|
|
2bc55ff428 | ||
|
|
ee5769f31f | ||
|
|
3a8517b06f | ||
|
|
0363d3c32e | ||
|
|
a582c4cdb6 | ||
|
|
a43b6b0323 | ||
|
|
84a143a605 | ||
|
|
114438e669 | ||
|
|
50bde9976b | ||
|
|
18ea116827 | ||
|
|
30d3ccd419 | ||
|
|
fb8fb7fed0 | ||
|
|
1abd239b9d | ||
|
|
9f1c8b2f8f | ||
|
|
c169423051 | ||
|
|
fdc6f32392 | ||
|
|
e7cdb2ebe5 | ||
|
|
4dae8edb76 | ||
|
|
a3e01b8884 | ||
|
|
6d33a2b0bb | ||
|
|
beaff53a79 | ||
|
|
b9b308f2fe | ||
|
|
a7bfd792f7 | ||
|
|
06fd9e3771 | ||
|
|
fb719e3d0b | ||
|
|
9f24cdf74c | ||
|
|
d7471c4f87 |
2
.hgsigs
2
.hgsigs
@@ -23,3 +23,5 @@ fd1981f41c626a969f07b4823848deaefef3c8aa 0 iQIcBAABCgAGBQJW4W2TAAoJEESTFJTynGdzu
|
||||
70705edee9dd29cd3d410f19fbd15cc3489313e2 0 iQIcBAABCgAGBQJW7CQRAAoJEESTFJTynGdzTj0QAJL38CKSZthBAeI9c6B+IlwIeT6kPZaPqk1pkycCTWOe87NiNU9abrsF+JrjTuRQiO1EpM2IvfQEIXTijUcMxvld3PnzrZDDv6UvBLtOkn3i++HSVRO0MOuTKI8gFDEPUxRtcaCKXEbqYnf1OTK25FT09Vb//qP9mK1thvlLJmbV+D2a9MkMK66rom1d1h+347IsuwsM+ycHjB80VVAQLA7VYLC5YIwmL17dSmcQLvetfikAMwwmUE+KES4qiLSaqOcAWcKcU67RZzgMMv5o0rESlQmv1nj0mHZtHoUR71sd21emPaRXLOr0oT5YogWUphKq2qVthRn2B06+vd3hPdtn92CmJw9j7zT2jl4OeSjNm9qfAajsRzHIANssFxkGAb7w/LxcMoO29JC+01iUUJMdOVm+4Ns6wGI7qxssWPKdB+VbQUDlHrXLR+sopO524uhkYoWB6DVfTj4R6tImaHtj5/VXON0lsYaLGj8cSH60emL6nNQ0lYV/bSlk6l0s+0x3uXGZnp9oKA+vqMzHfG3vJeMm6KUqtFVjUsYx+q8nHm5/SlWxj1EwnkH8s8ELKZAUXjd76nWEwJ7JFRNRSQWvjOUh3/rsOo4JopzZXPsjCjm+Vql9TG0X6hB21noai32oD5RvfhtR/NX6sXNS5TKZz/j/cMsMnAAsSKb6W7Jm
|
||||
9030ffdbe5625e35ed7189ab84a41dfc8d413e9c 0 iQIcBAABCgAGBQJXkOg0AAoJEESTFJTynGdzc1kP/3vSKCnhOOvjCjnpTQadYcCUq8vTNnfLHYVu0R4ItPa/jT6RmxoaYP+lZnLnnBx9+aX7kzwHsa9BUX3MbMEyLrOzX2I+bDJbNPhQyupyCuPYlf5Q9KVcO9YlpbsC4q5XBzCn3j2+pT8kSfi9uD8fgY3TgE4w9meINrfQAealfjwMLT8S/I49/ni0r+usSfk/dnSShJYDUO7Ja0VWbJea/GkkZTu30bCnMUZPjRApipU3hPP63WFjkSMT1rp2mAXbWqyr9lf8z32yxzM9nMSjq4ViRFzFlkGtE3EVRJ4PwkO7JuiWAMPJpiQcEr+r52cCsmWhiGyHuINo01MwoMO9/n6uL1WVa3mJcE9se3xBOvfgDu2FRFGCAdm1tef+AGVo9EG1uJXi0sX2yUc6DMeuYaRWrXMMlZh7zp9cuNU9Y/lLui9RFmq66yeXG3Z2B72doju3Ig5QGrNNw2AOsSzeHdAtOp6ychqPcl9QfIeJQG18KyPSefZKM3G8YRKBRIwXFEH6iZJe5ZIP4iXrHDMn2JqtTRtDqKR8VNDAgb9z4Ffx8QRxFyj5JzTTMM1GddHb9udLvTQlO0ULYG7hCSMRNzvUBE2aTw8frjLRyfyyg3QpDu/hz8op8s1ecE8rTCD8RuX9DiiylNozypPtGNS+UDbAmkc1PCWaRpPVl+9K6787
|
||||
5c9207ceedaea794f958224c19214d66af6e2d56 0 iQIzBAABCgAdFiEE9zR+8u4uB6JnYoypRJMUlPKcZ3MFAlkdtooACgkQRJMUlPKcZ3P6ZxAAmLy/buZB/d96DJF/pViRWt/fWdjQFC4MqWfeSLW02OZ8Qkm1vPL3ln6WPHC2thy3xZWVg2uan3pLk/XXnsIFu8Q7r1EAfFFpvlMUmdl7asE8V6ilaeqmiI7bIvGMFbf4cZkQliLjiFkJX56tFHRCNi+rb7WgRuru3/GzPXUq2AvXZvFpFJgik0B72TxVlmCKeBRZq1FvP0UhAH48RJWYJksdEyzh2paMfjX9ZO5Q2SFFrmPw6k2ArdJFC1AYcgceZC84y06RKJ0WiSntUPlEUXgQbQVVWbtQDhjfJXMr/beuroNdT/vsRraLVkAzvhaDXNnHlAJNLQxci+AcLpnzZhxMW+ax7RRtrpXGxRN4cs0lBGUcSkaDybFqMYXwEjXAE8w6fdJRWCIlxctkAW/iNEO4kAG97hI2Qwcw5oU2Ymnv09zyGR+XJE35pJqPulJHExdwanJHvmjH0QF7TNFS82yxS5dKnP954cj3Lu9SWGYWjxQJRmLtOwb+lqqol4VTxG7Ois4uef9/Tpp9skeMZXVeNlpn2wrp6iFcX3uiiVDg9VKkl3ig6UqCiqQSuiIN87RXwUOeHXlCnW3adz3Xei0ziBrwLSql7lBIHGEAlUUNmJ3CrR8IwQtcynGEMKfNIeZ/XK+uNlm9cJIqZf1fzqc8KexlyS9AS0i/kiYZTr4=
|
||||
2f0c3f3361d3ea4eb9129ed8810699fda7e7a8ee 0 iQIzBAABCgAdFiEE9zR+8u4uB6JnYoypRJMUlPKcZ3MFAlqVb+IACgkQRJMUlPKcZ3OENA//R9HsOUJQB2QZjRgAvqgLn2AMLUvmWb2etTZEc3Nps957Fw1F4kjh6VGfIpWuytfsDx1W8qRx09ikTdb3YteMWCuX8/aFreSPrioYmzrAEcxkZdA7B/jciqU0iXuHiJ9saKk5TR70aNp+iRy0hjAgiYEsVMF9YKHzULOJcHr70x9XVKquubQkwNqJA+/b2JbK2j46wM5nVK/alGSI2kMmEzXmAHQxsvf1OLMvgH8ou/l0xsg/CuFEK299XKfZAbsFEXrjuoWZ1aSa6rTeOWsWli5T+czyyJHI4Eu0Sz/gaR8+MPhJSYes8YjvzEdv32rRMDVOdBq4e+HoTgFt/THYABP6/R1H5fX3Lm4K8u9F9SwJbb/YKRAIrfWDob8ApnGFHk2dyYO20Fskbbg6b1pC7ulDWsufu8lYkQyMlTc3dR6P4eTB6mKO4x+gMG6tIYZ60fiULoEnMJCgegPtevmz+TG1rzdjh3ljiw9Dxz5lNtL+W7sBKKHwhyG0u+bavgmvBMKNL/rdHEM+0yCIz1U6Lb8sVaST1E4zbdm7cWHbSozBij3G0GBSkLFEq7ZLlh8wco9rELRh0Y9fFsWY9j6H/PTOu0GfHrYluFb9WGywHAquQY8j2croRx+MrvTbR1wZrbevPNm9gqk3vgOiDWu7KwxLLqcj+dEQ7tccptVYtbM=
|
||||
07b0d56d186d7eeef4106137a3eba554959ba0e3 0 iQIzBAABCgAdFiEE9zR+8u4uB6JnYoypRJMUlPKcZ3MFAlyWOo8ACgkQRJMUlPKcZ3O+MQ//c5oeDUvZuFiI4FHZqfIK/59YAciTP+9TQmoWDVSuOdkd9ZYJA7b7DCusqP2TWFEIl9M7i5hTLTMD21xuEQQtfOSP6EXpUw6JNdh/lsJs7EDlFANtwkdEozAQozFKnXbJEV3y9WldEWUlmPFjt4fJQIuG10SU7MTJHcSaQddJCh3I1//F4EvgRe+OqyrFwKekGiFdvfjcIFN3lQmk6K1Sc0MgyIO/VVZm/AQpBi0Dlg0yOl+EDcxxlmeSInbvLceWSP6op35I4dE5YWH1UetjzIsr5AIM15/k3viAKDDefY1EMAzK9b7YAF4BLw0a6XoQu0apvcWaALE/bJzWNSg/QbCm2JAZzk21WLLvR+AELzPfKXrHX3o0h51lpQ4rs7EWKUm43dJPoWkcFNOU+BDsNzffcJgChbRs48ut89DYLiGmSxhRxE77VPbA+klgTGdctOTLd8psseRlGYCuGe8zeota80bV9fUZ9WJZHwNgEWGowKUoTjy6l5k9OH3iQuQX3OXoy78ufRgWDulE7noVTMhXurQ8a0Jf2k/MW9dcnqGVkWitCFKPEvZwVmWyW2AWsdMcBJnFFGzDsNSxWTtCF9XcxieDO1IB8vGwYcb1TwEVuVzvR/wwvc3PgVikF+4Qv2NqdoQc1yn2PkocY2hwXyIZUAwz7erNumlTbeC/JK8=
|
||||
|
||||
2
.hgtags
2
.hgtags
@@ -55,3 +55,5 @@ cbd674d63cd4f3781464a8d4056a5506c8ae926f DROPBEAR_2015.67
|
||||
309e1c4a87682b6ca7d80b8555a1db416c3cb7ac DROPBEAR_2016.73
|
||||
0ed3d2bbf956cb8a9bf0f4b5a86b7dd9688205cb DROPBEAR_2016.74
|
||||
c31276613181c5cff7854e7ef586ace03424e55e DROPBEAR_2017.75
|
||||
1c66ca4f3791c82501c88e7637312182c7294978 DROPBEAR_2018.76
|
||||
6d1bbe7d5fa5827c7eae28bca044d691f7efa785 DROPBEAR_2019.77
|
||||
|
||||
24
.travis.yml
24
.travis.yml
@@ -9,20 +9,26 @@ matrix:
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
env: WEXTRAFLAGS=-Werror
|
||||
sudo: false
|
||||
- env: MULTI=1 WEXTRAFLAGS=-Werror
|
||||
# libtom has some warnings, so no WEXTRAFLAGS
|
||||
- env: BUNDLEDLIBTOM=--enable-bundled-libtom WEXTRAFLAGS=""
|
||||
- env: CONFIGURE_FLAGS=--enable-bundled-libtom WEXTRAFLAGS=""
|
||||
- env: NOWRITEV=1 WEXTRAFLAGS=-Werror
|
||||
# libtomcrypt 1.18.1 fixes clang problems, distro doesn't have that yet
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env: BUNDLEDLIBTOM=--enable-bundled-libtom WEXTRAFLAGS=""
|
||||
env: CONFIGURE_FLAGS=--enable-bundled-libtom WEXTRAFLAGS=""
|
||||
- os: osx
|
||||
compiler: clang
|
||||
env: WEXTRAFLAGS=""
|
||||
|
||||
# Note: the fuzzing malloc wrapper doesn't replace free() in system libtomcrypt, so need bundled.
|
||||
- env: DO_FUZZ=1 CONFIGURE_FLAGS="--enable-fuzz --disable-harden --enable-bundled-libtom" WEXTRAFLAGS="" LDFLAGS=-fsanitize=address EXTRACFLAGS=-fsanitize=address CXX=clang++
|
||||
compiler: clang
|
||||
# sanitizers need ptrace which is privileged https://github.com/travis-ci/travis-ci/issues/9033
|
||||
sudo: required
|
||||
|
||||
# container-based builds
|
||||
sudo: false
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
@@ -30,24 +36,28 @@ addons:
|
||||
- zlib1g-dev
|
||||
- libtomcrypt-dev
|
||||
- libtommath-dev
|
||||
|
||||
- mercurial
|
||||
|
||||
before_install:
|
||||
- if [ "$CC" = "clang" ]; then WEXTRAFLAGS="$WEXTRAFLAGS -Wno-error=incompatible-library-redeclaration" ; fi # workaround
|
||||
|
||||
script:
|
||||
- autoconf && autoheader && ./configure "$BUNDLEDLIBTOM" CFLAGS="-O2 -Wall -Wno-pointer-sign $WEXTRAFLAGS" --prefix="$HOME/inst"
|
||||
install:
|
||||
- autoconf
|
||||
- autoheader
|
||||
- ./configure $CONFIGURE_FLAGS CFLAGS="-O2 -Wall -Wno-pointer-sign $WEXTRAFLAGS $EXTRACFLAGS" --prefix="$HOME/inst" || (cat config.log; exit 1)
|
||||
- if [ "$NOWRITEV" = "1" ]; then sed -i -e s/HAVE_WRITEV/DONT_HAVE_WRITEV/ config.h ; fi
|
||||
- make -j3
|
||||
- test -z $DO_FUZZ || make fuzzstandalone
|
||||
# avoid concurrent install, osx/freebsd is racey (https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=208093)
|
||||
- make install
|
||||
|
||||
after_success:
|
||||
script:
|
||||
- ~/inst/bin/dropbearkey -t rsa -f testrsa
|
||||
- ~/inst/bin/dropbearkey -t dss -f testdss
|
||||
- ~/inst/bin/dropbearkey -t ecdsa -f testec256 -s 256
|
||||
- ~/inst/bin/dropbearkey -t ecdsa -f testec384 -s 384
|
||||
- ~/inst/bin/dropbearkey -t ecdsa -f testec521 -s 521
|
||||
- test -z $DO_FUZZ || ./fuzzers_test.sh
|
||||
|
||||
branches:
|
||||
only:
|
||||
|
||||
49
CHANGES
49
CHANGES
@@ -1,13 +1,58 @@
|
||||
2019.78 - 27 March 2019
|
||||
|
||||
- Fix dbclient regression in 2019.77. After exiting the terminal would be left
|
||||
in a bad state. Reported by Ryan Woodsmall
|
||||
|
||||
2019.77 - 23 March 2019
|
||||
|
||||
- Fix server -R option with ECDSA - only advertise one key size which will be accepted.
|
||||
Reported by Peter Krefting, 2018.76 regression.
|
||||
|
||||
- Fix server regression in 2018.76 where multiple client -R forwards were all forwarded
|
||||
to the first destination. Reported by Iddo Samet.
|
||||
|
||||
- Make failure delay more consistent to avoid revealing valid usernames, set server password
|
||||
limit of 100 characters. Problem reported by usd responsible disclosure team
|
||||
|
||||
- Change handling of failed authentication to avoid disclosing valid usernames,
|
||||
CVE-2018-15599.
|
||||
|
||||
- Fix dbclient to reliably return the exit code from the remote server.
|
||||
Reported by W. Mike Petullo
|
||||
|
||||
- Fix export of 521-bit ECDSA keys, from Christian Hohnstädt
|
||||
|
||||
- Add -o Port=xxx option to work with sshfs, from xcko
|
||||
|
||||
- Merged fuzzing code, see FUZZER-NOTES.md
|
||||
|
||||
- Add a DROPBEAR_SVR_MULTIUSER=0 compile option to run on
|
||||
single-user Linux kernels (CONFIG_MULTIUSER disabled). From Patrick Stewart
|
||||
|
||||
- Increase allowed username to 100 characters, reported by W. Mike Petullo
|
||||
|
||||
- Update config.sub and config.guess, should now work with RISC-V
|
||||
|
||||
- Cygwin compile fix from karel-m
|
||||
|
||||
- Don't require GNU sed (accidentally in 2018.76), reported by Samuel Hsu
|
||||
|
||||
- Fix for IRIX and writev(), reported by Kazuo Kuroi
|
||||
|
||||
- Other fixes and cleanups from François Perrad, Andre McCurdy, Konstantin Demin,
|
||||
Michael Jones, Pawel Rapkiewicz
|
||||
|
||||
|
||||
2018.76 - 27 February 2018
|
||||
|
||||
> > > Configuration/compatibility changes
|
||||
IMPORTANT
|
||||
Custom configuration is now specified in local_options.h rather than options.h
|
||||
Custom configuration is now specified in localoptions.h rather than options.h
|
||||
Available options and defaults can be seen in default_options.h
|
||||
|
||||
To migrate your configuration, compare your customised options.h against the
|
||||
upstream options.h from your relevant version. Any customised options should
|
||||
be put in localoptions.h
|
||||
be put in localoptions.h in the build directory.
|
||||
|
||||
- "configure --enable-static" should now be used instead of "make STATIC=1"
|
||||
This will avoid 'hardened build' flags that conflict with static binaries
|
||||
|
||||
74
FUZZER-NOTES.md
Normal file
74
FUZZER-NOTES.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# Fuzzing Dropbear
|
||||
|
||||
Dropbear is process-per-session so it assumes calling `dropbear_exit()`
|
||||
is fine at any point to clean up. This makes fuzzing a bit trickier.
|
||||
A few pieces of wrapping infrastructure are used to work around this.
|
||||
|
||||
The [libfuzzer](http://llvm.org/docs/LibFuzzer.html#fuzz-target) harness
|
||||
expects a long running process to continually run a test function with
|
||||
a string of crafted input. That process should not leak resources or exit.
|
||||
|
||||
## longjmp
|
||||
|
||||
When dropbear runs in fuzz mode it sets up a
|
||||
[`setjmp()`](http://man7.org/linux/man-pages/man3/setjmp.3.html) target prior
|
||||
to launching the code to be fuzzed, and then [`dropbear_exit()`](dbutil.c#L125)
|
||||
calls `longjmp()` back there. This avoids exiting though it doesn't free
|
||||
memory or other resources.
|
||||
|
||||
## malloc Wrapper
|
||||
|
||||
Dropbear normally uses a [`m_malloc()`](dbmalloc.c) function that is the same as `malloc()` but
|
||||
exits if allocation fails. In fuzzing mode this is replaced with a tracking allocator
|
||||
that stores all allocations in a linked list. After the `longjmp()` occurs the fuzzer target
|
||||
calls [`m_malloc_free_epoch(1, 1)`](dbmalloc.c) to clean up any unreleased memory.
|
||||
|
||||
If the fuzz target runs to completion it calls `m_malloc_free_epoch(1, 0)` which will reset
|
||||
the tracked allocations but will not free memory - that allows libfuzzer's leak checking
|
||||
to detect leaks in normal operation.
|
||||
|
||||
## File Descriptor Input
|
||||
|
||||
As a network process Dropbear reads and writes from a socket. The wrappers for
|
||||
`read()`/`write()`/`select()` in [fuzz-wrapfd.c](fuzz-wrapfd.c) will read from the
|
||||
fuzzer input that has been set up with `wrapfd_add()`. `write()` output is
|
||||
currently discarded.
|
||||
These also test error paths such as EINTR and short reads with certain probabilities.
|
||||
|
||||
This allows running the entire dropbear server process with network input provided by the
|
||||
fuzzer, without many modifications to the main code. At the time of writing this
|
||||
only runs the pre-authentication stages, though post-authentication could be run similarly.
|
||||
|
||||
## Encryption and Randomness
|
||||
|
||||
When running in fuzzing mode Dropbear uses a [fixed seed](dbrandom.c#L185)
|
||||
every time so that failures can be reproduced.
|
||||
|
||||
Since the fuzzer cannot generate valid encrypted input the packet decryption and
|
||||
message authentication calls are disabled, see [packet.c](packet.c).
|
||||
MAC failures are set to occur with a low probability to test that error path.
|
||||
|
||||
## Fuzzers
|
||||
|
||||
Current fuzzers are
|
||||
|
||||
- [fuzzer-preauth](fuzzer-preauth.c) - the fuzzer input is treated as a stream of session input. This will
|
||||
test key exchange, packet ordering, authentication attempts etc.
|
||||
|
||||
- [fuzzer-preauth_nomaths](fuzzer-preauth_nomaths.c) - the same as fuzzer-preauth but with asymmetric crypto
|
||||
routines replaced with dummies for faster runtime. corpora are shared
|
||||
between fuzzers by [oss-fuzz](https://github.com/google/oss-fuzz) so this
|
||||
will help fuzzer-preauth too.
|
||||
|
||||
- [fuzzer-verify](fuzzer-verify.c) - read a key and signature from fuzzer input and verify that signature.
|
||||
It would not be expected to pass, though some keys with bad parameters are
|
||||
able to validate with a trivial signature - extra checks are added for that.
|
||||
|
||||
- [fuzzer-pubkey](fuzzer-pubkey.c) - test parsing of an `authorized_keys` line.
|
||||
|
||||
- [fuzzer-kexdh](fuzzer-kexdh.c) - test Diffie-Hellman key exchange where the fuzz input is the
|
||||
ephemeral public key that would be received over the network. This is testing `mp_expt_mod()`
|
||||
and and other libtommath routines.
|
||||
|
||||
- [fuzzer-kexecdh](fuzzer-kexecdh.c) - test Elliptic Curve Diffie-Hellman key exchange like fuzzer-kexdh.
|
||||
This is testing libtommath ECC routines.
|
||||
2
INSTALL
2
INSTALL
@@ -3,6 +3,8 @@ Basic Dropbear build instructions:
|
||||
- Edit localoptions.h to set which features you want. Available options
|
||||
are described in default_options.h, these will be overridden by
|
||||
anything set in localoptions.h
|
||||
localoptions.h should be located in the build directory if you are
|
||||
building out of tree.
|
||||
|
||||
- If using a Mercurial or Git checkout, "autoconf; autoheader"
|
||||
|
||||
|
||||
82
Makefile.in
82
Makefile.in
@@ -36,6 +36,7 @@ COMMONOBJS=dbutil.o buffer.o dbhelpers.o \
|
||||
queue.o \
|
||||
atomicio.o compat.o fake-rfc2553.o \
|
||||
ltc_prng.o ecc.o ecdsa.o crypto_desc.o \
|
||||
dbmalloc.o \
|
||||
gensignkey.o gendss.o genrsa.o
|
||||
|
||||
SVROBJS=svr-kex.o svr-auth.o sshpty.o \
|
||||
@@ -59,11 +60,25 @@ CONVERTOBJS=dropbearconvert.o keyimport.o
|
||||
|
||||
SCPOBJS=scp.o progressmeter.o atomicio.o scpmisc.o compat.o
|
||||
|
||||
dropbearobjs=$(COMMONOBJS) $(CLISVROBJS) $(SVROBJS)
|
||||
dbclientobjs=$(COMMONOBJS) $(CLISVROBJS) $(CLIOBJS)
|
||||
dropbearkeyobjs=$(COMMONOBJS) $(KEYOBJS)
|
||||
dropbearconvertobjs=$(COMMONOBJS) $(CONVERTOBJS)
|
||||
scpobjs=$(SCPOBJS)
|
||||
ifeq (@DROPBEAR_FUZZ@, 1)
|
||||
allobjs = $(COMMONOBJS) fuzz-common.o fuzz-wrapfd.o $(CLISVROBJS) $(CLIOBJS) $(SVROBJS) @CRYPTLIB@
|
||||
allobjs:=$(subst svr-main.o, ,$(allobjs))
|
||||
allobjs:=$(subst cli-main.o, ,$(allobjs))
|
||||
allobjs:=$(sort $(allobjs))
|
||||
|
||||
dropbearobjs=$(allobjs) svr-main.o
|
||||
dbclientobjs=$(allobjs) cli-main.o
|
||||
dropbearkeyobjs=$(allobjs) $(KEYOBJS)
|
||||
dropbearconvertobjs=$(allobjs) $(CONVERTOBJS)
|
||||
# CXX only set when fuzzing
|
||||
CXX=@CXX@
|
||||
else
|
||||
dropbearobjs=$(COMMONOBJS) $(CLISVROBJS) $(SVROBJS)
|
||||
dbclientobjs=$(COMMONOBJS) $(CLISVROBJS) $(CLIOBJS)
|
||||
dropbearkeyobjs=$(COMMONOBJS) $(KEYOBJS)
|
||||
dropbearconvertobjs=$(COMMONOBJS) $(CONVERTOBJS)
|
||||
scpobjs=$(SCPOBJS)
|
||||
endif
|
||||
|
||||
VPATH=@srcdir@
|
||||
srcdir=@srcdir@
|
||||
@@ -180,7 +195,7 @@ dbclient: $(HEADERS) $(LIBTOM_DEPS) Makefile
|
||||
$(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS) $(LIBS)
|
||||
|
||||
dropbearkey dropbearconvert: $(HEADERS) $(LIBTOM_DEPS) Makefile
|
||||
$(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS)
|
||||
$(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBTOM_LIBS) $(LIBS)
|
||||
|
||||
# scp doesn't use the libs so is special.
|
||||
scp: $(SCPOBJS) $(HEADERS) Makefile
|
||||
@@ -236,3 +251,58 @@ distclean: clean tidy
|
||||
|
||||
tidy:
|
||||
-rm -f *~ *.gcov */*~
|
||||
|
||||
## Fuzzing targets
|
||||
|
||||
# list of fuzz targets
|
||||
FUZZ_TARGETS=fuzzer-preauth fuzzer-pubkey fuzzer-verify fuzzer-preauth_nomaths fuzzer-kexdh fuzzer-kexecdh
|
||||
|
||||
FUZZER_OPTIONS = $(addsuffix .options, $(FUZZ_TARGETS))
|
||||
|
||||
list-fuzz-targets:
|
||||
@echo $(FUZZ_TARGETS)
|
||||
|
||||
# fuzzers that don't use libfuzzer, just a standalone harness that feeds inputs
|
||||
fuzzstandalone: FUZZLIB=fuzz-harness.o
|
||||
fuzzstandalone: fuzz-harness.o fuzz-targets
|
||||
|
||||
# exclude svr-main.o to avoid duplicate main
|
||||
svrfuzzobjs=$(subst svr-main.o, ,$(dropbearobjs))
|
||||
|
||||
# build all the fuzzers. This will require fail to link unless built with
|
||||
# make fuzz-targets FUZZLIB=-lFuzzer.a
|
||||
# or similar - the library provides main().
|
||||
fuzz-targets: $(FUZZ_TARGETS) $(FUZZER_OPTIONS)
|
||||
|
||||
fuzzer-preauth: fuzzer-preauth.o $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobjs)
|
||||
$(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
|
||||
|
||||
fuzzer-preauth_nomaths: fuzzer-preauth_nomaths.o $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobjs)
|
||||
$(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
|
||||
|
||||
fuzzer-pubkey: fuzzer-pubkey.o $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobjs)
|
||||
$(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
|
||||
|
||||
fuzzer-verify: fuzzer-verify.o $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobjs)
|
||||
$(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
|
||||
|
||||
fuzzer-kexdh: fuzzer-kexdh.o $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobjs)
|
||||
$(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
|
||||
|
||||
fuzzer-kexecdh: fuzzer-kexecdh.o $(HEADERS) $(LIBTOM_DEPS) Makefile $(svrfuzzobjs)
|
||||
$(CXX) $(CXXFLAGS) $@.o $(LDFLAGS) $(svrfuzzobjs) -o $@$(EXEEXT) $(LIBTOM_LIBS) $(LIBS) $(FUZZLIB) @CRYPTLIB@
|
||||
|
||||
fuzzer-%.options: Makefile
|
||||
echo "[libfuzzer]" > $@
|
||||
echo "max_len = 50000" >> $@
|
||||
|
||||
# run this to update hardcoded hostkeys for for fuzzing.
|
||||
# hostkeys.c is checked in to hg.
|
||||
fuzz-hostkeys:
|
||||
dropbearkey -t rsa -f keyr
|
||||
dropbearkey -t dss -f keyd
|
||||
dropbearkey -t ecdsa -size 256 -f keye
|
||||
echo > hostkeys.c
|
||||
/usr/bin/xxd -i -a keyr >> hostkeys.c
|
||||
/usr/bin/xxd -i -a keye >> hostkeys.c
|
||||
/usr/bin/xxd -i -a keyd >> hostkeys.c
|
||||
|
||||
2
README
2
README
@@ -51,7 +51,7 @@ dropbearkey's '-y' option.
|
||||
|
||||
============================================================================
|
||||
|
||||
To run the server, you need to server keys, this is one-off:
|
||||
To run the server, you need to generate server keys, this is one-off:
|
||||
./dropbearkey -t rsa -f dropbear_rsa_host_key
|
||||
./dropbearkey -t dss -f dropbear_dss_host_key
|
||||
./dropbearkey -t ecdsa -f dropbear_ecdsa_host_key
|
||||
|
||||
11
auth.h
11
auth.h
@@ -37,9 +37,9 @@ void recv_msg_userauth_request(void);
|
||||
void send_msg_userauth_failure(int partial, int incrfail);
|
||||
void send_msg_userauth_success(void);
|
||||
void send_msg_userauth_banner(const buffer *msg);
|
||||
void svr_auth_password(void);
|
||||
void svr_auth_pubkey(void);
|
||||
void svr_auth_pam(void);
|
||||
void svr_auth_password(int valid_user);
|
||||
void svr_auth_pubkey(int valid_user);
|
||||
void svr_auth_pam(int valid_user);
|
||||
|
||||
#if DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT
|
||||
int svr_pubkey_allows_agentfwd(void);
|
||||
@@ -78,7 +78,7 @@ char* getpass_or_cancel(const char* prompt);
|
||||
void cli_auth_pubkey_cleanup(void);
|
||||
|
||||
|
||||
#define MAX_USERNAME_LEN 25 /* arbitrary for the moment */
|
||||
#define MAX_USERNAME_LEN 100 /* arbitrary for the moment */
|
||||
|
||||
#define AUTH_TYPE_NONE 1
|
||||
#define AUTH_TYPE_PUBKEY (1 << 1)
|
||||
@@ -108,11 +108,14 @@ struct AuthState {
|
||||
unsigned int authdone; /* 0 if we haven't authed, 1 if we have. Applies for
|
||||
client and server (though has differing
|
||||
meanings). */
|
||||
|
||||
unsigned int perm_warn; /* Server only, set if bad permissions on
|
||||
~/.ssh/authorized_keys have already been
|
||||
logged. */
|
||||
unsigned int checkusername_failed; /* Server only, set if checkusername
|
||||
has already failed */
|
||||
struct timespec auth_starttime; /* Server only, time of receiving current
|
||||
SSH_MSG_USERAUTH_REQUEST */
|
||||
|
||||
/* These are only used for the server */
|
||||
uid_t pw_uid;
|
||||
|
||||
4
buffer.c
4
buffer.c
@@ -209,6 +209,7 @@ char* buf_getstring(buffer* buf, unsigned int *retlen) {
|
||||
|
||||
unsigned int len;
|
||||
char* ret;
|
||||
void* src = NULL;
|
||||
len = buf_getint(buf);
|
||||
if (len > MAX_STRING_LEN) {
|
||||
dropbear_exit("String too long");
|
||||
@@ -217,8 +218,9 @@ char* buf_getstring(buffer* buf, unsigned int *retlen) {
|
||||
if (retlen != NULL) {
|
||||
*retlen = len;
|
||||
}
|
||||
src = buf_getptr(buf, len);
|
||||
ret = m_malloc(len+1);
|
||||
memcpy(ret, buf_getptr(buf, len), len);
|
||||
memcpy(ret, src, len);
|
||||
buf_incrpos(buf, len);
|
||||
ret[len] = '\0';
|
||||
|
||||
|
||||
11
channel.h
11
channel.h
@@ -69,10 +69,6 @@ struct Channel {
|
||||
int sent_close, recv_close;
|
||||
int recv_eof, sent_eof;
|
||||
|
||||
/* Set after running the ChanType-specific close hander
|
||||
* to ensure we don't run it twice (nor type->checkclose()). */
|
||||
int close_handler_done;
|
||||
|
||||
struct dropbear_progress_connection *conn_pending;
|
||||
int initconn; /* used for TCP forwarding, whether the channel has been
|
||||
fully initialised */
|
||||
@@ -95,10 +91,17 @@ struct ChanType {
|
||||
|
||||
int sepfds; /* Whether this channel has separate pipes for in/out or not */
|
||||
const char *name;
|
||||
/* Sets up the channel */
|
||||
int (*inithandler)(struct Channel*);
|
||||
/* Called to check whether a channel should close, separately from the FD being closed.
|
||||
Used for noticing process exiting */
|
||||
int (*check_close)(const struct Channel*);
|
||||
/* Handler for ssh_msg_channel_request */
|
||||
void (*reqhandler)(struct Channel*);
|
||||
/* Called prior to sending ssh_msg_channel_close, used for sending exit status */
|
||||
void (*closehandler)(const struct Channel*);
|
||||
/* Frees resources, called just prior to channel being removed */
|
||||
void (*cleanup)(const struct Channel*);
|
||||
};
|
||||
|
||||
/* Callback for connect_remote */
|
||||
|
||||
@@ -52,6 +52,7 @@ const struct ChanType cli_chan_agent = {
|
||||
new_agent_chan,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ const struct ChanType clichansess = {
|
||||
NULL, /* checkclosehandler */
|
||||
cli_chansessreq, /* reqhandler */
|
||||
cli_closechansess, /* closehandler */
|
||||
NULL, /* cleanup */
|
||||
};
|
||||
|
||||
static void cli_chansessreq(struct Channel *channel) {
|
||||
@@ -387,7 +388,8 @@ static const struct ChanType cli_chan_netcat = {
|
||||
cli_init_netcat, /* inithandler */
|
||||
NULL,
|
||||
NULL,
|
||||
cli_closechansess
|
||||
cli_closechansess,
|
||||
NULL,
|
||||
};
|
||||
|
||||
void cli_send_netcat_request() {
|
||||
|
||||
@@ -214,7 +214,7 @@ void cli_getopts(int argc, char ** argv) {
|
||||
cli_opts.always_accept_key = 1;
|
||||
break;
|
||||
case 'p': /* remoteport */
|
||||
next = &cli_opts.remoteport;
|
||||
next = (char**)&cli_opts.remoteport;
|
||||
break;
|
||||
#if DROPBEAR_CLI_PUBKEY_AUTH
|
||||
case 'i': /* an identityfile */
|
||||
@@ -891,6 +891,7 @@ static void add_extendedopt(const char* origstr) {
|
||||
#ifndef DISABLE_SYSLOG
|
||||
"\tUseSyslog\n"
|
||||
#endif
|
||||
"\tPort\n"
|
||||
);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
@@ -909,5 +910,10 @@ static void add_extendedopt(const char* origstr) {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (match_extendedopt(&optstr, "Port") == DROPBEAR_SUCCESS) {
|
||||
cli_opts.remoteport = optstr;
|
||||
return;
|
||||
}
|
||||
|
||||
dropbear_log(LOG_WARNING, "Ignoring unknown configuration option '%s'", origstr);
|
||||
}
|
||||
|
||||
@@ -356,6 +356,7 @@ static void cli_session_cleanup(void) {
|
||||
}
|
||||
|
||||
static void cli_finished() {
|
||||
TRACE(("cli_finised()"))
|
||||
|
||||
session_cleanup();
|
||||
fprintf(stderr, "Connection to %s@%s:%s closed.\n", cli_opts.username,
|
||||
|
||||
@@ -40,6 +40,7 @@ const struct ChanType cli_chan_tcpremote = {
|
||||
newtcpforwarded,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
#endif
|
||||
@@ -55,6 +56,7 @@ static const struct ChanType cli_chan_tcplocal = {
|
||||
tcp_prio_inithandler,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
#endif
|
||||
@@ -135,7 +137,7 @@ static int cli_localtcp(const char* listenaddr,
|
||||
tcpinfo->chantype = &cli_chan_tcplocal;
|
||||
tcpinfo->tcp_type = direct;
|
||||
|
||||
ret = listen_tcpfwd(tcpinfo);
|
||||
ret = listen_tcpfwd(tcpinfo, NULL);
|
||||
|
||||
if (ret == DROPBEAR_FAILURE) {
|
||||
m_free(tcpinfo);
|
||||
|
||||
@@ -144,7 +144,6 @@ static struct Channel* newchannel(unsigned int remotechan,
|
||||
newchan->index = i;
|
||||
newchan->sent_close = newchan->recv_close = 0;
|
||||
newchan->sent_eof = newchan->recv_eof = 0;
|
||||
newchan->close_handler_done = 0;
|
||||
|
||||
newchan->remotechan = remotechan;
|
||||
newchan->transwindow = transwindow;
|
||||
@@ -286,7 +285,7 @@ static void check_close(struct Channel *channel) {
|
||||
channel->extrabuf ? cbuf_getused(channel->extrabuf) : 0))
|
||||
|
||||
if (!channel->flushing
|
||||
&& !channel->close_handler_done
|
||||
&& !channel->sent_close
|
||||
&& channel->type->check_close
|
||||
&& channel->type->check_close(channel))
|
||||
{
|
||||
@@ -298,7 +297,7 @@ static void check_close(struct Channel *channel) {
|
||||
channel, to ensure that the shell has exited (and the exit status
|
||||
retrieved) before we close things up. */
|
||||
if (!channel->type->check_close
|
||||
|| channel->close_handler_done
|
||||
|| channel->sent_close
|
||||
|| channel->type->check_close(channel)) {
|
||||
close_allowed = 1;
|
||||
}
|
||||
@@ -385,10 +384,8 @@ void channel_connect_done(int result, int sock, void* user_data, const char* UNU
|
||||
static void send_msg_channel_close(struct Channel *channel) {
|
||||
|
||||
TRACE(("enter send_msg_channel_close %p", (void*)channel))
|
||||
if (channel->type->closehandler
|
||||
&& !channel->close_handler_done) {
|
||||
if (channel->type->closehandler) {
|
||||
channel->type->closehandler(channel);
|
||||
channel->close_handler_done = 1;
|
||||
}
|
||||
|
||||
CHECKCLEARTOWRITE();
|
||||
@@ -661,10 +658,8 @@ static void remove_channel(struct Channel * channel) {
|
||||
m_close(channel->errfd);
|
||||
}
|
||||
|
||||
if (!channel->close_handler_done
|
||||
&& channel->type->closehandler) {
|
||||
channel->type->closehandler(channel);
|
||||
channel->close_handler_done = 1;
|
||||
if (channel->type->cleanup) {
|
||||
channel->type->cleanup(channel);
|
||||
}
|
||||
|
||||
if (channel->conn_pending) {
|
||||
@@ -690,13 +685,7 @@ void recv_msg_channel_request() {
|
||||
|
||||
TRACE(("enter recv_msg_channel_request %p", (void*)channel))
|
||||
|
||||
if (channel->sent_close) {
|
||||
TRACE(("leave recv_msg_channel_request: already closed channel"))
|
||||
return;
|
||||
}
|
||||
|
||||
if (channel->type->reqhandler
|
||||
&& !channel->close_handler_done) {
|
||||
if (channel->type->reqhandler) {
|
||||
channel->type->reqhandler(channel);
|
||||
} else {
|
||||
int wantreply;
|
||||
@@ -1011,6 +1000,11 @@ cleanup:
|
||||
void send_msg_channel_failure(const struct Channel *channel) {
|
||||
|
||||
TRACE(("enter send_msg_channel_failure"))
|
||||
|
||||
if (channel->sent_close) {
|
||||
TRACE(("Skipping sending msg_channel_failure for closed channel"))
|
||||
return;
|
||||
}
|
||||
CHECKCLEARTOWRITE();
|
||||
|
||||
buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_FAILURE);
|
||||
@@ -1024,6 +1018,10 @@ void send_msg_channel_failure(const struct Channel *channel) {
|
||||
void send_msg_channel_success(const struct Channel *channel) {
|
||||
|
||||
TRACE(("enter send_msg_channel_success"))
|
||||
if (channel->sent_close) {
|
||||
TRACE(("Skipping sending msg_channel_success for closed channel"))
|
||||
return;
|
||||
}
|
||||
CHECKCLEARTOWRITE();
|
||||
|
||||
buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_SUCCESS);
|
||||
|
||||
29
common-kex.c
29
common-kex.c
@@ -48,7 +48,6 @@ static void read_kex_algos(void);
|
||||
/* helper function for gen_new_keys */
|
||||
static void hashkeys(unsigned char *out, unsigned int outlen,
|
||||
const hash_state * hs, const unsigned char X);
|
||||
static void finish_kexhashbuf(void);
|
||||
|
||||
|
||||
/* Send our list of algorithms we can use */
|
||||
@@ -391,6 +390,14 @@ int is_compress_recv() {
|
||||
&& ses.keys->recv.algo_comp == DROPBEAR_COMP_ZLIB_DELAY);
|
||||
}
|
||||
|
||||
static void* dropbear_zalloc(void* UNUSED(opaque), uInt items, uInt size) {
|
||||
return m_calloc(items, size);
|
||||
}
|
||||
|
||||
static void dropbear_zfree(void* UNUSED(opaque), void* ptr) {
|
||||
m_free(ptr);
|
||||
}
|
||||
|
||||
/* Set up new zlib compression streams, close the old ones. Only
|
||||
* called from gen_new_keys() */
|
||||
static void gen_new_zstream_recv() {
|
||||
@@ -399,8 +406,8 @@ static void gen_new_zstream_recv() {
|
||||
if (ses.newkeys->recv.algo_comp == DROPBEAR_COMP_ZLIB
|
||||
|| ses.newkeys->recv.algo_comp == DROPBEAR_COMP_ZLIB_DELAY) {
|
||||
ses.newkeys->recv.zstream = (z_streamp)m_malloc(sizeof(z_stream));
|
||||
ses.newkeys->recv.zstream->zalloc = Z_NULL;
|
||||
ses.newkeys->recv.zstream->zfree = Z_NULL;
|
||||
ses.newkeys->recv.zstream->zalloc = dropbear_zalloc;
|
||||
ses.newkeys->recv.zstream->zfree = dropbear_zfree;
|
||||
|
||||
if (inflateInit(ses.newkeys->recv.zstream) != Z_OK) {
|
||||
dropbear_exit("zlib error");
|
||||
@@ -423,8 +430,8 @@ static void gen_new_zstream_trans() {
|
||||
if (ses.newkeys->trans.algo_comp == DROPBEAR_COMP_ZLIB
|
||||
|| ses.newkeys->trans.algo_comp == DROPBEAR_COMP_ZLIB_DELAY) {
|
||||
ses.newkeys->trans.zstream = (z_streamp)m_malloc(sizeof(z_stream));
|
||||
ses.newkeys->trans.zstream->zalloc = Z_NULL;
|
||||
ses.newkeys->trans.zstream->zfree = Z_NULL;
|
||||
ses.newkeys->trans.zstream->zalloc = dropbear_zalloc;
|
||||
ses.newkeys->trans.zstream->zfree = dropbear_zfree;
|
||||
|
||||
if (deflateInit2(ses.newkeys->trans.zstream, Z_DEFAULT_COMPRESSION,
|
||||
Z_DEFLATED, DROPBEAR_ZLIB_WINDOW_BITS,
|
||||
@@ -687,6 +694,9 @@ void kexecdh_comb_key(struct kex_ecdh_param *param, buffer *pub_them,
|
||||
/* K, the shared secret */
|
||||
buf_putmpint(ses.kexhashbuf, ses.dh_K);
|
||||
|
||||
ecc_free(Q_them);
|
||||
m_free(Q_them);
|
||||
|
||||
/* calculate the hash H to sign */
|
||||
finish_kexhashbuf();
|
||||
}
|
||||
@@ -761,8 +771,7 @@ void kexcurve25519_comb_key(const struct kex_curve25519_param *param, const buff
|
||||
#endif /* DROPBEAR_CURVE25519 */
|
||||
|
||||
|
||||
|
||||
static void finish_kexhashbuf(void) {
|
||||
void finish_kexhashbuf(void) {
|
||||
hash_state hs;
|
||||
const struct ltc_hash_descriptor *hash_desc = ses.newkeys->algo_kex->hash_desc;
|
||||
|
||||
@@ -943,6 +952,12 @@ static void read_kex_algos() {
|
||||
ses.newkeys->trans.algo_comp = s2c_comp_algo->val;
|
||||
}
|
||||
|
||||
#if DROPBEAR_FUZZ
|
||||
if (fuzz.fuzzing) {
|
||||
fuzz_kex_fakealgos();
|
||||
}
|
||||
#endif
|
||||
|
||||
/* reserved for future extensions */
|
||||
buf_getint(ses.payload);
|
||||
|
||||
|
||||
@@ -68,6 +68,16 @@ void common_session_init(int sock_in, int sock_out) {
|
||||
/* Sets it to lowdelay */
|
||||
update_channel_prio();
|
||||
|
||||
#if !DROPBEAR_SVR_MULTIUSER
|
||||
/* A sanity check to prevent an accidental configuration option
|
||||
leaving multiuser systems exposed */
|
||||
errno = 0;
|
||||
getuid();
|
||||
if (errno != ENOSYS) {
|
||||
dropbear_exit("Non-multiuser Dropbear requires a non-multiuser kernel");
|
||||
}
|
||||
#endif
|
||||
|
||||
now = monotonic_now();
|
||||
ses.connect_time = now;
|
||||
ses.last_packet_time_keepalive_recv = now;
|
||||
@@ -75,14 +85,18 @@ void common_session_init(int sock_in, int sock_out) {
|
||||
ses.last_packet_time_any_sent = 0;
|
||||
ses.last_packet_time_keepalive_sent = 0;
|
||||
|
||||
#if DROPBEAR_FUZZ
|
||||
if (!fuzz.fuzzing)
|
||||
#endif
|
||||
{
|
||||
if (pipe(ses.signal_pipe) < 0) {
|
||||
dropbear_exit("Signal pipe failed");
|
||||
}
|
||||
setnonblocking(ses.signal_pipe[0]);
|
||||
setnonblocking(ses.signal_pipe[1]);
|
||||
|
||||
ses.maxfd = MAX(ses.maxfd, ses.signal_pipe[0]);
|
||||
ses.maxfd = MAX(ses.maxfd, ses.signal_pipe[1]);
|
||||
}
|
||||
|
||||
ses.writepayload = buf_new(TRANS_MAX_PAYLOAD_LEN);
|
||||
ses.transseq = 0;
|
||||
@@ -148,13 +162,19 @@ void session_loop(void(*loophandler)(void)) {
|
||||
|
||||
timeout.tv_sec = select_timeout();
|
||||
timeout.tv_usec = 0;
|
||||
FD_ZERO(&writefd);
|
||||
FD_ZERO(&readfd);
|
||||
DROPBEAR_FD_ZERO(&writefd);
|
||||
DROPBEAR_FD_ZERO(&readfd);
|
||||
|
||||
dropbear_assert(ses.payload == NULL);
|
||||
|
||||
/* We get woken up when signal handlers write to this pipe.
|
||||
SIGCHLD in svr-chansession is the only one currently. */
|
||||
#if DROPBEAR_FUZZ
|
||||
if (!fuzz.fuzzing)
|
||||
#endif
|
||||
{
|
||||
FD_SET(ses.signal_pipe[0], &readfd);
|
||||
}
|
||||
|
||||
/* set up for channels which can be read/written */
|
||||
setchannelfds(&readfd, &writefd, writequeue_has_space);
|
||||
@@ -195,8 +215,8 @@ void session_loop(void(*loophandler)(void)) {
|
||||
* want to iterate over channels etc for reading, to handle
|
||||
* server processes exiting etc.
|
||||
* We don't want to read/write FDs. */
|
||||
FD_ZERO(&writefd);
|
||||
FD_ZERO(&readfd);
|
||||
DROPBEAR_FD_ZERO(&writefd);
|
||||
DROPBEAR_FD_ZERO(&readfd);
|
||||
}
|
||||
|
||||
/* We'll just empty out the pipe if required. We don't do
|
||||
@@ -298,6 +318,16 @@ void session_cleanup() {
|
||||
buf_free(dequeue(&ses.writequeue));
|
||||
}
|
||||
|
||||
m_free(ses.newkeys);
|
||||
#ifndef DISABLE_ZLIB
|
||||
if (ses.keys->recv.zstream != NULL) {
|
||||
if (inflateEnd(ses.keys->recv.zstream) == Z_STREAM_ERROR) {
|
||||
dropbear_exit("Crypto error");
|
||||
}
|
||||
m_free(ses.keys->recv.zstream);
|
||||
}
|
||||
#endif
|
||||
|
||||
m_free(ses.remoteident);
|
||||
m_free(ses.authstate.pw_dir);
|
||||
m_free(ses.authstate.pw_name);
|
||||
@@ -327,7 +357,7 @@ void session_cleanup() {
|
||||
void send_session_identification() {
|
||||
buffer *writebuf = buf_new(strlen(LOCAL_IDENT "\r\n") + 1);
|
||||
buf_putbytes(writebuf, (const unsigned char *) LOCAL_IDENT "\r\n", strlen(LOCAL_IDENT "\r\n"));
|
||||
writebuf_enqueue(writebuf, 0);
|
||||
writebuf_enqueue(writebuf);
|
||||
}
|
||||
|
||||
static void read_session_identification() {
|
||||
@@ -387,7 +417,7 @@ static int ident_readln(int fd, char* buf, int count) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
FD_ZERO(&fds);
|
||||
DROPBEAR_FD_ZERO(&fds);
|
||||
|
||||
/* select since it's a non-blocking fd */
|
||||
|
||||
|
||||
939
config.guess
vendored
939
config.guess
vendored
File diff suppressed because it is too large
Load Diff
2752
config.sub
vendored
2752
config.sub
vendored
File diff suppressed because it is too large
Load Diff
31
configure.ac
31
configure.ac
@@ -9,6 +9,13 @@ AC_PREREQ(2.59)
|
||||
AC_INIT
|
||||
AC_CONFIG_SRCDIR(buffer.c)
|
||||
|
||||
# Record which revision is being built
|
||||
if test -s "`which hg`" && test -d "$srcdir/.hg"; then
|
||||
hgrev=`hg id -i -R "$srcdir"`
|
||||
AC_MSG_NOTICE([Source directory Mercurial base revision $hgrev])
|
||||
fi
|
||||
|
||||
ORIGCFLAGS="$CFLAGS"
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
|
||||
@@ -29,7 +36,7 @@ AC_DEFUN(DB_TRYADDCFLAGS,
|
||||
}])
|
||||
|
||||
# set compile flags prior to other tests
|
||||
if test -z "$OLDCFLAGS" && test "$GCC" = "yes"; then
|
||||
if test -z "$ORIGCFLAGS" && test "$GCC" = "yes"; then
|
||||
AC_MSG_NOTICE(No \$CFLAGS set... using "-Os -W -Wall" for GCC)
|
||||
CFLAGS="-Os -W -Wall"
|
||||
fi
|
||||
@@ -315,7 +322,24 @@ AC_ARG_ENABLE(shadow,
|
||||
AC_MSG_NOTICE(Using shadow passwords if available)
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
AC_ARG_ENABLE(fuzz,
|
||||
[ --enable-fuzz Build fuzzing. Not recommended for deployment.],
|
||||
[
|
||||
AC_DEFINE(DROPBEAR_FUZZ, 1, Fuzzing)
|
||||
AC_MSG_NOTICE(Enabling fuzzing)
|
||||
DROPBEAR_FUZZ=1
|
||||
# libfuzzer needs linking with c++ libraries
|
||||
AC_PROG_CXX
|
||||
],
|
||||
[
|
||||
AC_DEFINE(DROPBEAR_FUZZ, 0, Fuzzing)
|
||||
DROPBEAR_FUZZ=0
|
||||
]
|
||||
|
||||
)
|
||||
AC_SUBST(DROPBEAR_FUZZ)
|
||||
AC_SUBST(CXX)
|
||||
|
||||
# Checks for header files.
|
||||
AC_HEADER_STDC
|
||||
@@ -473,6 +497,9 @@ AC_CHECK_FUNCS(endutxent getutxent getutxid getutxline pututxline )
|
||||
AC_CHECK_FUNCS(setutxent utmpxname)
|
||||
AC_CHECK_FUNCS(logout updwtmp logwtmp)
|
||||
|
||||
# POSIX monotonic time
|
||||
AC_CHECK_FUNCS(clock_gettime)
|
||||
|
||||
# OS X monotonic time
|
||||
AC_CHECK_HEADERS([mach/mach_time.h])
|
||||
AC_CHECK_FUNCS(mach_absolute_time)
|
||||
|
||||
13
dbhelpers.c
13
dbhelpers.c
@@ -9,16 +9,9 @@ void m_burn(void *data, unsigned int len) {
|
||||
#elif defined(HAVE_EXPLICIT_BZERO)
|
||||
explicit_bzero(data, len);
|
||||
#else
|
||||
/* Based on the method in David Wheeler's
|
||||
* "Secure Programming for Linux and Unix HOWTO". May not be safe
|
||||
* against link-time optimisation. */
|
||||
volatile char *p = data;
|
||||
|
||||
if (data == NULL)
|
||||
return;
|
||||
while (len--) {
|
||||
*p++ = 0x0;
|
||||
}
|
||||
/* This must be volatile to avoid compiler optimisation */
|
||||
volatile void *p = data;
|
||||
memset((void*)p, 0x0, len);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
182
dbmalloc.c
Normal file
182
dbmalloc.c
Normal file
@@ -0,0 +1,182 @@
|
||||
#include "dbmalloc.h"
|
||||
#include "dbutil.h"
|
||||
|
||||
|
||||
void * m_calloc(size_t nmemb, size_t size) {
|
||||
if (SIZE_T_MAX / nmemb < size) {
|
||||
dropbear_exit("m_calloc failed");
|
||||
}
|
||||
return m_malloc(nmemb*size);
|
||||
}
|
||||
|
||||
void * m_strdup(const char * str) {
|
||||
char* ret;
|
||||
unsigned int len;
|
||||
len = strlen(str);
|
||||
|
||||
ret = m_malloc(len+1);
|
||||
if (ret == NULL) {
|
||||
dropbear_exit("m_strdup failed");
|
||||
}
|
||||
memcpy(ret, str, len+1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if !DROPBEAR_TRACKING_MALLOC
|
||||
|
||||
/* Simple wrappers around malloc etc */
|
||||
void * m_malloc(size_t size) {
|
||||
|
||||
void* ret;
|
||||
|
||||
if (size == 0) {
|
||||
dropbear_exit("m_malloc failed");
|
||||
}
|
||||
ret = calloc(1, size);
|
||||
if (ret == NULL) {
|
||||
dropbear_exit("m_malloc failed");
|
||||
}
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
void * m_realloc(void* ptr, size_t size) {
|
||||
|
||||
void *ret;
|
||||
|
||||
if (size == 0) {
|
||||
dropbear_exit("m_realloc failed");
|
||||
}
|
||||
ret = realloc(ptr, size);
|
||||
if (ret == NULL) {
|
||||
dropbear_exit("m_realloc failed");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#else
|
||||
|
||||
/* For fuzzing */
|
||||
|
||||
struct dbmalloc_header {
|
||||
unsigned int epoch;
|
||||
struct dbmalloc_header *prev;
|
||||
struct dbmalloc_header *next;
|
||||
};
|
||||
|
||||
static void put_alloc(struct dbmalloc_header *header);
|
||||
static void remove_alloc(struct dbmalloc_header *header);
|
||||
|
||||
/* end of the linked list */
|
||||
static struct dbmalloc_header* staple;
|
||||
|
||||
unsigned int current_epoch = 0;
|
||||
|
||||
void m_malloc_set_epoch(unsigned int epoch) {
|
||||
current_epoch = epoch;
|
||||
}
|
||||
|
||||
void m_malloc_free_epoch(unsigned int epoch, int dofree) {
|
||||
struct dbmalloc_header* header;
|
||||
struct dbmalloc_header* nextheader = NULL;
|
||||
struct dbmalloc_header* oldstaple = staple;
|
||||
staple = NULL;
|
||||
/* free allocations from this epoch, create a new staple-anchored list from
|
||||
the remainder */
|
||||
for (header = oldstaple; header; header = nextheader)
|
||||
{
|
||||
nextheader = header->next;
|
||||
if (header->epoch == epoch) {
|
||||
if (dofree) {
|
||||
free(header);
|
||||
}
|
||||
} else {
|
||||
header->prev = NULL;
|
||||
header->next = NULL;
|
||||
put_alloc(header);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void put_alloc(struct dbmalloc_header *header) {
|
||||
assert(header->next == NULL);
|
||||
assert(header->prev == NULL);
|
||||
if (staple) {
|
||||
staple->prev = header;
|
||||
}
|
||||
header->next = staple;
|
||||
staple = header;
|
||||
}
|
||||
|
||||
static void remove_alloc(struct dbmalloc_header *header) {
|
||||
if (header->prev) {
|
||||
header->prev->next = header->next;
|
||||
}
|
||||
if (header->next) {
|
||||
header->next->prev = header->prev;
|
||||
}
|
||||
if (staple == header) {
|
||||
staple = header->next;
|
||||
}
|
||||
header->prev = NULL;
|
||||
header->next = NULL;
|
||||
}
|
||||
|
||||
static struct dbmalloc_header* get_header(void* ptr) {
|
||||
char* bptr = ptr;
|
||||
return (struct dbmalloc_header*)&bptr[-sizeof(struct dbmalloc_header)];
|
||||
}
|
||||
|
||||
void * m_malloc(size_t size) {
|
||||
char* mem = NULL;
|
||||
struct dbmalloc_header* header = NULL;
|
||||
|
||||
if (size == 0 || size > 1e9) {
|
||||
dropbear_exit("m_malloc failed");
|
||||
}
|
||||
|
||||
size = size + sizeof(struct dbmalloc_header);
|
||||
|
||||
mem = calloc(1, size);
|
||||
if (mem == NULL) {
|
||||
dropbear_exit("m_malloc failed");
|
||||
}
|
||||
header = (struct dbmalloc_header*)mem;
|
||||
put_alloc(header);
|
||||
header->epoch = current_epoch;
|
||||
return &mem[sizeof(struct dbmalloc_header)];
|
||||
}
|
||||
|
||||
void * m_realloc(void* ptr, size_t size) {
|
||||
char* mem = NULL;
|
||||
struct dbmalloc_header* header = NULL;
|
||||
if (size == 0 || size > 1e9) {
|
||||
dropbear_exit("m_realloc failed");
|
||||
}
|
||||
|
||||
header = get_header(ptr);
|
||||
remove_alloc(header);
|
||||
|
||||
size = size + sizeof(struct dbmalloc_header);
|
||||
mem = realloc(header, size);
|
||||
if (mem == NULL) {
|
||||
dropbear_exit("m_realloc failed");
|
||||
}
|
||||
|
||||
header = (struct dbmalloc_header*)mem;
|
||||
put_alloc(header);
|
||||
return &mem[sizeof(struct dbmalloc_header)];
|
||||
}
|
||||
|
||||
void m_free_direct(void* ptr) {
|
||||
struct dbmalloc_header* header = NULL;
|
||||
if (!ptr) {
|
||||
return;
|
||||
}
|
||||
header = get_header(ptr);
|
||||
remove_alloc(header);
|
||||
free(header);
|
||||
}
|
||||
|
||||
#endif /* DROPBEAR_TRACKING_MALLOC */
|
||||
27
dbmalloc.h
Normal file
27
dbmalloc.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef DBMALLOC_H_
|
||||
#define DBMALLOC_H_
|
||||
|
||||
#include "stdint.h"
|
||||
#include "stdlib.h"
|
||||
#include "options.h"
|
||||
|
||||
void * m_malloc(size_t size);
|
||||
void * m_calloc(size_t nmemb, size_t size);
|
||||
void * m_strdup(const char * str);
|
||||
void * m_realloc(void* ptr, size_t size);
|
||||
|
||||
#if DROPBEAR_TRACKING_MALLOC
|
||||
void m_free_direct(void* ptr);
|
||||
void m_malloc_set_epoch(unsigned int epoch);
|
||||
void m_malloc_free_epoch(unsigned int epoch, int dofree);
|
||||
|
||||
#else
|
||||
/* plain wrapper */
|
||||
#define m_free_direct free
|
||||
|
||||
#endif
|
||||
|
||||
#define m_free(X) do {m_free_direct(X); (X) = NULL;} while (0)
|
||||
|
||||
|
||||
#endif /* DBMALLOC_H_ */
|
||||
34
dbrandom.c
34
dbrandom.c
@@ -27,7 +27,7 @@
|
||||
#include "dbutil.h"
|
||||
#include "bignum.h"
|
||||
#include "dbrandom.h"
|
||||
|
||||
#include "runopts.h"
|
||||
|
||||
/* this is used to generate unique output from the same hashpool */
|
||||
static uint32_t counter = 0;
|
||||
@@ -88,7 +88,7 @@ process_file(hash_state *hs, const char *filename,
|
||||
timeout.tv_sec = 2;
|
||||
timeout.tv_usec = 0;
|
||||
|
||||
FD_ZERO(&read_fds);
|
||||
DROPBEAR_FD_ZERO(&read_fds);
|
||||
FD_SET(readfd, &read_fds);
|
||||
res = select(readfd + 1, &read_fds, NULL, NULL, &timeout);
|
||||
if (res == 0)
|
||||
@@ -145,6 +145,12 @@ void addrandom(const unsigned char * buf, unsigned int len)
|
||||
{
|
||||
hash_state hs;
|
||||
|
||||
#if DROPBEAR_FUZZ
|
||||
if (fuzz.fuzzing) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* hash in the new seed data */
|
||||
sha1_init(&hs);
|
||||
/* existing state (zeroes on startup) */
|
||||
@@ -157,6 +163,11 @@ void addrandom(const unsigned char * buf, unsigned int len)
|
||||
|
||||
static void write_urandom()
|
||||
{
|
||||
#if DROPBEAR_FUZZ
|
||||
if (fuzz.fuzzing) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
#if !DROPBEAR_USE_PRNGD
|
||||
/* This is opportunistic, don't worry about failure */
|
||||
unsigned char buf[INIT_SEED_SIZE];
|
||||
@@ -170,6 +181,18 @@ static void write_urandom()
|
||||
#endif
|
||||
}
|
||||
|
||||
#if DROPBEAR_FUZZ
|
||||
void fuzz_seed(void) {
|
||||
hash_state hs;
|
||||
sha1_init(&hs);
|
||||
sha1_process(&hs, "fuzzfuzzfuzz", strlen("fuzzfuzzfuzz"));
|
||||
sha1_done(&hs, hashpool);
|
||||
|
||||
counter = 0;
|
||||
donerandinit = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Initialise the prng from /dev/urandom or prngd. This function can
|
||||
* be called multiple times */
|
||||
void seedrandom() {
|
||||
@@ -180,8 +203,15 @@ void seedrandom() {
|
||||
struct timeval tv;
|
||||
clock_t clockval;
|
||||
|
||||
#if DROPBEAR_FUZZ
|
||||
if (fuzz.fuzzing) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* hash in the new seed data */
|
||||
sha1_init(&hs);
|
||||
|
||||
/* existing state */
|
||||
sha1_process(&hs, (void*)hashpool, sizeof(hashpool));
|
||||
|
||||
|
||||
143
dbutil.c
143
dbutil.c
@@ -120,6 +120,13 @@ static void generic_dropbear_exit(int exitcode, const char* format,
|
||||
|
||||
_dropbear_log(LOG_INFO, fmtbuf, param);
|
||||
|
||||
#if DROPBEAR_FUZZ
|
||||
/* longjmp before cleaning up svr_opts */
|
||||
if (fuzz.do_jmp) {
|
||||
longjmp(fuzz.jmp, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
exit(exitcode);
|
||||
}
|
||||
|
||||
@@ -392,6 +399,7 @@ void printhex(const char * label, const unsigned char * buf, int len) {
|
||||
void printmpint(const char *label, mp_int *mp) {
|
||||
buffer *buf = buf_new(1000);
|
||||
buf_putmpint(buf, mp);
|
||||
fprintf(stderr, "%d bits ", mp_count_bits(mp));
|
||||
printhex(label, buf->data, buf->len);
|
||||
buf_free(buf);
|
||||
|
||||
@@ -520,57 +528,26 @@ void m_close(int fd) {
|
||||
}
|
||||
}
|
||||
|
||||
void * m_malloc(size_t size) {
|
||||
|
||||
void* ret;
|
||||
|
||||
if (size == 0) {
|
||||
dropbear_exit("m_malloc failed");
|
||||
}
|
||||
ret = calloc(1, size);
|
||||
if (ret == NULL) {
|
||||
dropbear_exit("m_malloc failed");
|
||||
}
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
void * m_strdup(const char * str) {
|
||||
char* ret;
|
||||
|
||||
ret = strdup(str);
|
||||
if (ret == NULL) {
|
||||
dropbear_exit("m_strdup failed");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void * m_realloc(void* ptr, size_t size) {
|
||||
|
||||
void *ret;
|
||||
|
||||
if (size == 0) {
|
||||
dropbear_exit("m_realloc failed");
|
||||
}
|
||||
ret = realloc(ptr, size);
|
||||
if (ret == NULL) {
|
||||
dropbear_exit("m_realloc failed");
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void setnonblocking(int fd) {
|
||||
|
||||
TRACE(("setnonblocking: %d", fd))
|
||||
|
||||
#if DROPBEAR_FUZZ
|
||||
if (fuzz.fuzzing) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
|
||||
if (errno == ENODEV) {
|
||||
/* Some devices (like /dev/null redirected in)
|
||||
* can't be set to non-blocking */
|
||||
TRACE(("ignoring ENODEV for setnonblocking"))
|
||||
} else {
|
||||
{
|
||||
dropbear_exit("Couldn't set nonblocking");
|
||||
}
|
||||
}
|
||||
}
|
||||
TRACE(("leave setnonblocking"))
|
||||
}
|
||||
@@ -628,61 +605,67 @@ int constant_time_memcmp(const void* a, const void *b, size_t n)
|
||||
return c;
|
||||
}
|
||||
|
||||
#if defined(__linux__) && defined(SYS_clock_gettime)
|
||||
/* CLOCK_MONOTONIC_COARSE was added in Linux 2.6.32 but took a while to
|
||||
reach userspace include headers */
|
||||
#ifndef CLOCK_MONOTONIC_COARSE
|
||||
#define CLOCK_MONOTONIC_COARSE 6
|
||||
/* higher-resolution monotonic timestamp, falls back to gettimeofday */
|
||||
void gettime_wrapper(struct timespec *now) {
|
||||
struct timeval tv;
|
||||
#if DROPBEAR_FUZZ
|
||||
if (fuzz.fuzzing) {
|
||||
/* time stands still when fuzzing */
|
||||
now->tv_sec = 5;
|
||||
now->tv_nsec = 0;
|
||||
}
|
||||
#endif
|
||||
/* Some old toolchains know SYS_clock_gettime but not CLOCK_MONOTONIC */
|
||||
#ifndef CLOCK_MONOTONIC
|
||||
#define CLOCK_MONOTONIC 1
|
||||
|
||||
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
|
||||
/* POSIX monotonic clock. Newer Linux, BSD, MacOSX >10.12 */
|
||||
if (clock_gettime(CLOCK_MONOTONIC, now) == 0) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
static clockid_t get_linux_clock_source() {
|
||||
struct timespec ts;
|
||||
if (syscall(SYS_clock_gettime, CLOCK_MONOTONIC_COARSE, &ts) == 0) {
|
||||
return CLOCK_MONOTONIC_COARSE;
|
||||
}
|
||||
|
||||
if (syscall(SYS_clock_gettime, CLOCK_MONOTONIC, &ts) == 0) {
|
||||
return CLOCK_MONOTONIC;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
time_t monotonic_now() {
|
||||
#if defined(__linux__) && defined(SYS_clock_gettime)
|
||||
static clockid_t clock_source = -2;
|
||||
|
||||
if (clock_source == -2) {
|
||||
/* First run, find out which one works.
|
||||
-1 will fall back to time() */
|
||||
clock_source = get_linux_clock_source();
|
||||
}
|
||||
|
||||
if (clock_source >= 0) {
|
||||
struct timespec ts;
|
||||
if (syscall(SYS_clock_gettime, clock_source, &ts) != 0) {
|
||||
/* Intermittent clock failures should not happen */
|
||||
dropbear_exit("Clock broke");
|
||||
{
|
||||
/* Old linux toolchain - kernel might support it but not the build headers */
|
||||
/* Also glibc <2.17 requires -lrt which we neglect to add */
|
||||
static int linux_monotonic_failed = 0;
|
||||
if (!linux_monotonic_failed) {
|
||||
/* CLOCK_MONOTONIC isn't in some headers */
|
||||
int clock_source_monotonic = 1;
|
||||
if (syscall(SYS_clock_gettime, clock_source_monotonic, now) == 0) {
|
||||
return;
|
||||
} else {
|
||||
/* Don't try again */
|
||||
linux_monotonic_failed = 1;
|
||||
}
|
||||
return ts.tv_sec;
|
||||
}
|
||||
#endif /* linux clock_gettime */
|
||||
}
|
||||
#endif /* linux fallback clock_gettime */
|
||||
|
||||
#if defined(HAVE_MACH_ABSOLUTE_TIME)
|
||||
/* OS X, see https://developer.apple.com/library/mac/qa/qa1398/_index.html */
|
||||
{
|
||||
/* OS X pre 10.12, see https://developer.apple.com/library/mac/qa/qa1398/_index.html */
|
||||
static mach_timebase_info_data_t timebase_info;
|
||||
uint64_t scaled_time;
|
||||
if (timebase_info.denom == 0) {
|
||||
mach_timebase_info(&timebase_info);
|
||||
}
|
||||
return mach_absolute_time() * timebase_info.numer / timebase_info.denom
|
||||
/ 1e9;
|
||||
scaled_time = mach_absolute_time() * timebase_info.numer / timebase_info.denom;
|
||||
now->tv_sec = scaled_time / 1000000000;
|
||||
now->tv_nsec = scaled_time % 1000000000;
|
||||
}
|
||||
#endif /* osx mach_absolute_time */
|
||||
|
||||
/* Fallback for everything else - this will sometimes go backwards */
|
||||
return time(NULL);
|
||||
gettimeofday(&tv, NULL);
|
||||
now->tv_sec = tv.tv_sec;
|
||||
now->tv_nsec = 1000*tv.tv_usec;
|
||||
}
|
||||
|
||||
/* second-resolution monotonic timestamp */
|
||||
time_t monotonic_now() {
|
||||
struct timespec ts;
|
||||
gettime_wrapper(&ts);
|
||||
return ts.tv_sec;
|
||||
}
|
||||
|
||||
void fsync_parent_dir(const char* fn) {
|
||||
@@ -700,6 +683,6 @@ void fsync_parent_dir(const char* fn) {
|
||||
TRACE(("error opening directory %s for fsync: %s", dir, strerror(errno)))
|
||||
}
|
||||
|
||||
free(fn_dir);
|
||||
m_free(fn_dir);
|
||||
#endif
|
||||
}
|
||||
|
||||
14
dbutil.h
14
dbutil.h
@@ -30,6 +30,7 @@
|
||||
#include "buffer.h"
|
||||
#include "queue.h"
|
||||
#include "dbhelpers.h"
|
||||
#include "dbmalloc.h"
|
||||
|
||||
#ifndef DISABLE_SYSLOG
|
||||
void startsyslog(const char *ident);
|
||||
@@ -66,10 +67,6 @@ int buf_readfile(buffer* buf, const char* filename);
|
||||
int buf_getline(buffer * line, FILE * authfile);
|
||||
|
||||
void m_close(int fd);
|
||||
void * m_malloc(size_t size);
|
||||
void * m_strdup(const char * str);
|
||||
void * m_realloc(void* ptr, size_t size);
|
||||
#define m_free(X) do {free(X); (X) = NULL;} while (0)
|
||||
void setnonblocking(int fd);
|
||||
void disallow_core(void);
|
||||
int m_str_to_uint(const char* str, unsigned int *val);
|
||||
@@ -86,9 +83,18 @@ int constant_time_memcmp(const void* a, const void *b, size_t n);
|
||||
/* Returns a time in seconds that doesn't go backwards - does not correspond to
|
||||
a real-world clock */
|
||||
time_t monotonic_now(void);
|
||||
/* Higher resolution clock_gettime(CLOCK_MONOTONIC) wrapper */
|
||||
void gettime_wrapper(struct timespec *now);
|
||||
|
||||
char * expand_homedir_path(const char *inpath);
|
||||
|
||||
void fsync_parent_dir(const char* fn);
|
||||
|
||||
#if DROPBEAR_MSAN
|
||||
/* FD_ZERO seems to leave some memory uninitialized. clear it to avoid false positives */
|
||||
#define DROPBEAR_FD_ZERO(fds) do { memset((fds), 0x0, sizeof(fd_set)); FD_ZERO(fds); } while(0)
|
||||
#else
|
||||
#define DROPBEAR_FD_ZERO(fds) FD_ZERO(fds)
|
||||
#endif
|
||||
|
||||
#endif /* DROPBEAR_DBUTIL_H_ */
|
||||
|
||||
12
debian/changelog
vendored
12
debian/changelog
vendored
@@ -1,3 +1,15 @@
|
||||
dropbear (2019.78-0.1) unstable; urgency=low
|
||||
|
||||
* New upstream release.
|
||||
|
||||
-- Matt Johnston <matt@ucc.asn.au> Wed, 27 Mar 2019 22:51:57 +0800
|
||||
|
||||
dropbear (2019.77-0.1) unstable; urgency=low
|
||||
|
||||
* New upstream release.
|
||||
|
||||
-- Matt Johnston <matt@ucc.asn.au> Sat, 23 Mar 2019 22:51:57 +0800
|
||||
|
||||
dropbear (2018.76-0.1) unstable; urgency=low
|
||||
|
||||
* New upstream release.
|
||||
|
||||
3
debug.h
3
debug.h
@@ -39,7 +39,9 @@
|
||||
/*#define CHECKCLEARTOWRITE() assert(ses.writepayload->len == 0 && \
|
||||
ses.writepayload->pos == 0)*/
|
||||
|
||||
#ifndef CHECKCLEARTOWRITE
|
||||
#define CHECKCLEARTOWRITE()
|
||||
#endif
|
||||
|
||||
/* Define this, compile with -pg and set GMON_OUT_PREFIX=gmon to get gmon
|
||||
* output when Dropbear forks. This will allow it gprof to be used.
|
||||
@@ -54,6 +56,7 @@
|
||||
|
||||
/* you don't need to touch this block */
|
||||
#if DEBUG_TRACE
|
||||
extern int debug_trace;
|
||||
#define TRACE(X) dropbear_trace X;
|
||||
#define TRACE2(X) dropbear_trace2 X;
|
||||
#else /*DEBUG_TRACE*/
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
default_options.h documents compile-time options, and provides default values.
|
||||
|
||||
Local customisation should be added to localoptions.h which is
|
||||
used if it exists. Options defined there will override any options in this
|
||||
file.
|
||||
used if it exists in the build directory. Options defined there will override
|
||||
any options in this file.
|
||||
|
||||
Options can also be defined with -DDROPBEAR_XXX=[0,1] in Makefile CFLAGS
|
||||
|
||||
@@ -196,6 +196,11 @@ group1 in Dropbear server too */
|
||||
* authorized_keys file into account */
|
||||
#define DROPBEAR_SVR_PUBKEY_OPTIONS 1
|
||||
|
||||
/* Set this to 0 if your system does not have multiple user support.
|
||||
(Linux kernel CONFIG_MULTIUSER option)
|
||||
The resulting binary will not run on a normal system. */
|
||||
#define DROPBEAR_SVR_MULTIUSER 1
|
||||
|
||||
/* Client authentication options */
|
||||
#define DROPBEAR_CLI_PASSWORD_AUTH 1
|
||||
#define DROPBEAR_CLI_PUBKEY_AUTH 1
|
||||
|
||||
25
dss.c
25
dss.c
@@ -73,6 +73,18 @@ int buf_get_dss_pub_key(buffer* buf, dropbear_dss_key *key) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* test 1 < g < p */
|
||||
if (mp_cmp_d(key->g, 1) != MP_GT) {
|
||||
dropbear_log(LOG_WARNING, "Bad DSS g");
|
||||
ret = DROPBEAR_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
if (mp_cmp(key->g, key->p) != MP_LT) {
|
||||
dropbear_log(LOG_WARNING, "Bad DSS g");
|
||||
ret = DROPBEAR_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = DROPBEAR_SUCCESS;
|
||||
TRACE(("leave buf_get_dss_pub_key: success"))
|
||||
out:
|
||||
@@ -172,6 +184,13 @@ int buf_dss_verify(buffer* buf, const dropbear_dss_key *key, const buffer *data_
|
||||
goto out;
|
||||
}
|
||||
|
||||
#if DEBUG_DSS_VERIFY
|
||||
printmpint("dss verify p", key->p);
|
||||
printmpint("dss verify q", key->q);
|
||||
printmpint("dss verify g", key->g);
|
||||
printmpint("dss verify y", key->y);
|
||||
#endif
|
||||
|
||||
/* hash the data */
|
||||
sha1_init(&hs);
|
||||
sha1_process(&hs, data_buf->data, data_buf->len);
|
||||
@@ -181,6 +200,9 @@ int buf_dss_verify(buffer* buf, const dropbear_dss_key *key, const buffer *data_
|
||||
/* w = (s')-1 mod q */
|
||||
/* let val1 = s' */
|
||||
bytes_to_mp(&val1, (const unsigned char*) &string[SHA1_HASH_SIZE], SHA1_HASH_SIZE);
|
||||
#if DEBUG_DSS_VERIFY
|
||||
printmpint("dss verify s'", &val1);
|
||||
#endif
|
||||
|
||||
if (mp_cmp(&val1, key->q) != MP_LT) {
|
||||
TRACE(("verify failed, s' >= q"))
|
||||
@@ -198,6 +220,9 @@ int buf_dss_verify(buffer* buf, const dropbear_dss_key *key, const buffer *data_
|
||||
/* u1 = ((SHA(M')w) mod q */
|
||||
/* let val1 = SHA(M') = msghash */
|
||||
bytes_to_mp(&val1, msghash, SHA1_HASH_SIZE);
|
||||
#if DEBUG_DSS_VERIFY
|
||||
printmpint("dss verify r'", &val1);
|
||||
#endif
|
||||
|
||||
/* let val3 = u1 = ((SHA(M')w) mod q */
|
||||
if (mp_mulmod(&val1, &val2, key->q, &val3) != MP_OKAY) {
|
||||
|
||||
2
ecdsa.h
2
ecdsa.h
@@ -16,7 +16,7 @@
|
||||
#elif DROPBEAR_ECC_521
|
||||
#define ECDSA_DEFAULT_SIZE 521
|
||||
#else
|
||||
#define ECDSA_DEFAULT_SIZE 0
|
||||
#error ECDSA cannot be enabled without enabling at least one size (256, 384, 521)
|
||||
#endif
|
||||
|
||||
ecc_key *gen_ecdsa_priv_key(unsigned int bit_size);
|
||||
|
||||
201
fuzz-common.c
Normal file
201
fuzz-common.c
Normal file
@@ -0,0 +1,201 @@
|
||||
#include "includes.h"
|
||||
|
||||
#include "includes.h"
|
||||
#include "fuzz.h"
|
||||
#include "dbutil.h"
|
||||
#include "runopts.h"
|
||||
#include "crypto_desc.h"
|
||||
#include "session.h"
|
||||
#include "dbrandom.h"
|
||||
#include "bignum.h"
|
||||
#include "fuzz-wrapfd.h"
|
||||
|
||||
struct dropbear_fuzz_options fuzz;
|
||||
|
||||
static void fuzz_dropbear_log(int UNUSED(priority), const char* format, va_list param);
|
||||
static void load_fixed_hostkeys(void);
|
||||
|
||||
void fuzz_common_setup(void) {
|
||||
fuzz.fuzzing = 1;
|
||||
fuzz.wrapfds = 1;
|
||||
fuzz.do_jmp = 1;
|
||||
fuzz.input = m_malloc(sizeof(buffer));
|
||||
_dropbear_log = fuzz_dropbear_log;
|
||||
crypto_init();
|
||||
fuzz_seed();
|
||||
/* let any messages get flushed */
|
||||
setlinebuf(stdout);
|
||||
}
|
||||
|
||||
int fuzz_set_input(const uint8_t *Data, size_t Size) {
|
||||
|
||||
fuzz.input->data = (unsigned char*)Data;
|
||||
fuzz.input->size = Size;
|
||||
fuzz.input->len = Size;
|
||||
fuzz.input->pos = 0;
|
||||
|
||||
memset(&ses, 0x0, sizeof(ses));
|
||||
memset(&svr_ses, 0x0, sizeof(svr_ses));
|
||||
wrapfd_setup();
|
||||
|
||||
fuzz_seed();
|
||||
|
||||
return DROPBEAR_SUCCESS;
|
||||
}
|
||||
|
||||
#if DEBUG_TRACE
|
||||
static void fuzz_dropbear_log(int UNUSED(priority), const char* format, va_list param) {
|
||||
if (debug_trace) {
|
||||
char printbuf[1024];
|
||||
vsnprintf(printbuf, sizeof(printbuf), format, param);
|
||||
fprintf(stderr, "%s\n", printbuf);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static void fuzz_dropbear_log(int UNUSED(priority), const char* UNUSED(format), va_list UNUSED(param)) {
|
||||
/* No print */
|
||||
}
|
||||
#endif /* DEBUG_TRACE */
|
||||
|
||||
void fuzz_svr_setup(void) {
|
||||
fuzz_common_setup();
|
||||
|
||||
_dropbear_exit = svr_dropbear_exit;
|
||||
|
||||
char *argv[] = {
|
||||
"-E",
|
||||
};
|
||||
|
||||
int argc = sizeof(argv) / sizeof(*argv);
|
||||
svr_getopts(argc, argv);
|
||||
|
||||
/* user lookups might be slow, cache it */
|
||||
fuzz.pw_name = m_strdup("person");
|
||||
fuzz.pw_dir = m_strdup("/tmp");
|
||||
fuzz.pw_shell = m_strdup("/bin/zsh");
|
||||
fuzz.pw_passwd = m_strdup("!!zzznope");
|
||||
|
||||
load_fixed_hostkeys();
|
||||
}
|
||||
|
||||
static void load_fixed_hostkeys(void) {
|
||||
#include "fuzz-hostkeys.c"
|
||||
|
||||
buffer *b = buf_new(3000);
|
||||
enum signkey_type type;
|
||||
|
||||
TRACE(("load fixed hostkeys"))
|
||||
|
||||
svr_opts.hostkey = new_sign_key();
|
||||
|
||||
buf_setlen(b, 0);
|
||||
buf_putbytes(b, keyr, keyr_len);
|
||||
buf_setpos(b, 0);
|
||||
type = DROPBEAR_SIGNKEY_RSA;
|
||||
if (buf_get_priv_key(b, svr_opts.hostkey, &type) == DROPBEAR_FAILURE) {
|
||||
dropbear_exit("failed fixed rsa hostkey");
|
||||
}
|
||||
|
||||
buf_setlen(b, 0);
|
||||
buf_putbytes(b, keyd, keyd_len);
|
||||
buf_setpos(b, 0);
|
||||
type = DROPBEAR_SIGNKEY_DSS;
|
||||
if (buf_get_priv_key(b, svr_opts.hostkey, &type) == DROPBEAR_FAILURE) {
|
||||
dropbear_exit("failed fixed dss hostkey");
|
||||
}
|
||||
|
||||
buf_setlen(b, 0);
|
||||
buf_putbytes(b, keye, keye_len);
|
||||
buf_setpos(b, 0);
|
||||
type = DROPBEAR_SIGNKEY_ECDSA_NISTP256;
|
||||
if (buf_get_priv_key(b, svr_opts.hostkey, &type) == DROPBEAR_FAILURE) {
|
||||
dropbear_exit("failed fixed ecdsa hostkey");
|
||||
}
|
||||
|
||||
buf_free(b);
|
||||
}
|
||||
|
||||
void fuzz_kex_fakealgos(void) {
|
||||
ses.newkeys->recv.crypt_mode = &dropbear_mode_none;
|
||||
}
|
||||
|
||||
void fuzz_get_socket_address(int UNUSED(fd), char **local_host, char **local_port,
|
||||
char **remote_host, char **remote_port, int UNUSED(host_lookup)) {
|
||||
if (local_host) {
|
||||
*local_host = m_strdup("fuzzlocalhost");
|
||||
}
|
||||
if (local_port) {
|
||||
*local_port = m_strdup("1234");
|
||||
}
|
||||
if (remote_host) {
|
||||
*remote_host = m_strdup("fuzzremotehost");
|
||||
}
|
||||
if (remote_port) {
|
||||
*remote_port = m_strdup("9876");
|
||||
}
|
||||
}
|
||||
|
||||
/* cut down version of svr_send_msg_kexdh_reply() that skips slow maths. Still populates structures */
|
||||
void fuzz_fake_send_kexdh_reply(void) {
|
||||
assert(!ses.dh_K);
|
||||
m_mp_alloc_init_multi(&ses.dh_K, NULL);
|
||||
mp_set_int(ses.dh_K, 12345678);
|
||||
finish_kexhashbuf();
|
||||
}
|
||||
|
||||
int fuzz_run_preauth(const uint8_t *Data, size_t Size, int skip_kexmaths) {
|
||||
static int once = 0;
|
||||
if (!once) {
|
||||
fuzz_svr_setup();
|
||||
fuzz.skip_kexmaths = skip_kexmaths;
|
||||
once = 1;
|
||||
}
|
||||
|
||||
if (fuzz_set_input(Data, Size) == DROPBEAR_FAILURE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
get prefix. input format is
|
||||
string prefix
|
||||
uint32 wrapfd seed
|
||||
... to be extended later
|
||||
[bytes] ssh input stream
|
||||
*/
|
||||
|
||||
/* be careful to avoid triggering buffer.c assertions */
|
||||
if (fuzz.input->len < 8) {
|
||||
return 0;
|
||||
}
|
||||
size_t prefix_size = buf_getint(fuzz.input);
|
||||
if (prefix_size != 4) {
|
||||
return 0;
|
||||
}
|
||||
uint32_t wrapseed = buf_getint(fuzz.input);
|
||||
wrapfd_setseed(wrapseed);
|
||||
|
||||
int fakesock = 20;
|
||||
wrapfd_add(fakesock, fuzz.input, PLAIN);
|
||||
|
||||
m_malloc_set_epoch(1);
|
||||
if (setjmp(fuzz.jmp) == 0) {
|
||||
svr_session(fakesock, fakesock);
|
||||
m_malloc_free_epoch(1, 0);
|
||||
} else {
|
||||
m_malloc_free_epoch(1, 1);
|
||||
TRACE(("dropbear_exit longjmped"))
|
||||
/* dropbear_exit jumped here */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const void* fuzz_get_algo(const algo_type *algos, const char* name) {
|
||||
const algo_type *t;
|
||||
for (t = algos; t->name; t++) {
|
||||
if (strcmp(t->name, name) == 0) {
|
||||
return t->data;
|
||||
}
|
||||
}
|
||||
assert(0);
|
||||
}
|
||||
48
fuzz-harness.c
Normal file
48
fuzz-harness.c
Normal file
@@ -0,0 +1,48 @@
|
||||
#include "includes.h"
|
||||
#include "buffer.h"
|
||||
#include "dbutil.h"
|
||||
|
||||
extern int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size);
|
||||
|
||||
int main(int argc, char ** argv) {
|
||||
int i;
|
||||
buffer *input = buf_new(100000);
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
printf("arg %s\n", argv[i]);
|
||||
#if DEBUG_TRACE
|
||||
if (strcmp(argv[i], "-v") == 0) {
|
||||
debug_trace = 1;
|
||||
TRACE(("debug printing on"))
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int old_fuzz_wrapfds = 0;
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (argv[i][0] == '-') {
|
||||
/* ignore arguments */
|
||||
continue;
|
||||
}
|
||||
|
||||
char* fn = argv[i];
|
||||
buf_setlen(input, 0);
|
||||
buf_readfile(input, fn);
|
||||
buf_setpos(input, 0);
|
||||
|
||||
fuzz.wrapfds = old_fuzz_wrapfds;
|
||||
printf("Running %s once \n", fn);
|
||||
LLVMFuzzerTestOneInput(input->data, input->len);
|
||||
printf("Running %s twice \n", fn);
|
||||
LLVMFuzzerTestOneInput(input->data, input->len);
|
||||
printf("Done %s\n", fn);
|
||||
|
||||
/* Disable wrapfd so it won't interfere with buf_readfile() above */
|
||||
old_fuzz_wrapfds = fuzz.wrapfds;
|
||||
fuzz.wrapfds = 0;
|
||||
}
|
||||
|
||||
printf("Finished\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
129
fuzz-hostkeys.c
Normal file
129
fuzz-hostkeys.c
Normal file
@@ -0,0 +1,129 @@
|
||||
|
||||
unsigned char keyr[] = {
|
||||
0x00, 0x00, 0x00, 0x07, 0x73, 0x73, 0x68, 0x2d, 0x72, 0x73, 0x61, 0x00,
|
||||
0x00, 0x00, 0x03, 0x01, 0x00, 0x01, 0x00, 0x00, 0x01, 0x01, 0x00, 0xb1,
|
||||
0x06, 0x95, 0xc9, 0xa8, 0x38, 0xb9, 0x99, 0x91, 0xb5, 0x17, 0x39, 0xb9,
|
||||
0xfa, 0xa4, 0x49, 0xf8, 0x2a, 0x4c, 0x14, 0xbd, 0xb6, 0x85, 0xdb, 0x38,
|
||||
0x99, 0x44, 0xfa, 0xd6, 0xaa, 0x67, 0xef, 0x00, 0x75, 0x2b, 0x6a, 0x5c,
|
||||
0x1b, 0x50, 0xa8, 0x52, 0xf9, 0xa7, 0xee, 0xe2, 0xb3, 0x80, 0x38, 0x92,
|
||||
0x20, 0x86, 0x7c, 0xe5, 0x89, 0xb3, 0x06, 0xe4, 0x3b, 0xd1, 0xe2, 0x45,
|
||||
0xea, 0xc1, 0xd5, 0x8e, 0x05, 0xfb, 0x90, 0x29, 0xd9, 0x41, 0xb3, 0x05,
|
||||
0x31, 0x1e, 0xcc, 0xeb, 0x89, 0xdc, 0xd2, 0x6a, 0x99, 0x23, 0xbd, 0x7a,
|
||||
0xbe, 0x8c, 0xe3, 0x3f, 0xa1, 0xe8, 0xf5, 0xb4, 0x51, 0x40, 0xb4, 0xb1,
|
||||
0xc1, 0x16, 0x9f, 0x07, 0xbb, 0x99, 0xaa, 0x4b, 0x8f, 0x11, 0x19, 0x3c,
|
||||
0x18, 0xbd, 0x6e, 0xce, 0x14, 0x54, 0x2c, 0x16, 0x4a, 0x5f, 0x89, 0xe4,
|
||||
0x6b, 0x9f, 0x55, 0x68, 0xcc, 0x09, 0x8e, 0x4b, 0x92, 0xc8, 0x87, 0xfe,
|
||||
0x09, 0xed, 0x53, 0x6e, 0xff, 0x5f, 0x15, 0x0d, 0x19, 0x9d, 0xa6, 0x54,
|
||||
0xd2, 0xea, 0x59, 0x4f, 0xa1, 0x7c, 0xf6, 0xf5, 0x7f, 0x32, 0x23, 0xed,
|
||||
0x72, 0xa8, 0x96, 0x17, 0x87, 0x06, 0xf2, 0xc7, 0xcd, 0xda, 0x4a, 0x10,
|
||||
0xd1, 0xfd, 0xb8, 0xf1, 0xaf, 0x25, 0x55, 0x32, 0x45, 0x39, 0x95, 0xec,
|
||||
0x0c, 0xa9, 0xf0, 0x47, 0x8b, 0x66, 0xe0, 0xb7, 0xa2, 0xf6, 0x35, 0x50,
|
||||
0x27, 0xe7, 0x2f, 0x90, 0x35, 0x5b, 0xd5, 0x62, 0x19, 0xb4, 0x41, 0xd4,
|
||||
0x52, 0xe7, 0x7f, 0x97, 0xfc, 0x5b, 0x4a, 0x5b, 0x19, 0x06, 0x65, 0x2d,
|
||||
0x23, 0x29, 0x15, 0x8b, 0x05, 0xaf, 0xbe, 0xd3, 0x4a, 0x27, 0x5b, 0xc9,
|
||||
0xc0, 0xd0, 0xd2, 0xba, 0x8b, 0x00, 0x7a, 0x2f, 0x39, 0xa0, 0x13, 0xb9,
|
||||
0xe6, 0xf5, 0x4b, 0x21, 0x54, 0x57, 0xb3, 0xf9, 0x6c, 0x6f, 0xd0, 0x17,
|
||||
0xf4, 0x50, 0x9d, 0x00, 0x00, 0x01, 0x00, 0x01, 0xf2, 0xda, 0x5f, 0xfb,
|
||||
0xe2, 0xda, 0xfc, 0xe0, 0xdf, 0x3a, 0x0e, 0x14, 0x18, 0xc1, 0xd9, 0x1f,
|
||||
0x43, 0xe3, 0x65, 0x3e, 0x07, 0xe7, 0x8d, 0xdc, 0x1d, 0x11, 0xc1, 0xd6,
|
||||
0xc0, 0xd8, 0xda, 0x53, 0xf5, 0x04, 0x73, 0x51, 0x1b, 0x26, 0xef, 0x4e,
|
||||
0xf5, 0xce, 0x3d, 0x77, 0x21, 0x94, 0xd0, 0xc7, 0xc1, 0xda, 0x19, 0x7d,
|
||||
0xf8, 0xc5, 0x4c, 0xc8, 0xee, 0x7d, 0xd1, 0xbb, 0x02, 0x90, 0x2b, 0xff,
|
||||
0x4e, 0x4d, 0xd7, 0x9d, 0x72, 0x0c, 0x60, 0x0f, 0x4b, 0x83, 0xf5, 0xc2,
|
||||
0x26, 0xd6, 0x22, 0xb8, 0x60, 0x3a, 0xf9, 0x2f, 0x92, 0x2a, 0x2e, 0x14,
|
||||
0xa7, 0x56, 0x1c, 0x56, 0x05, 0x41, 0x92, 0xac, 0xb1, 0x4e, 0x44, 0x1e,
|
||||
0x70, 0x42, 0xda, 0xc7, 0xc8, 0x9c, 0xae, 0x29, 0x2d, 0x0c, 0x3a, 0xff,
|
||||
0x9b, 0xb6, 0xad, 0xb4, 0xfb, 0x49, 0x28, 0x96, 0x74, 0xf5, 0x94, 0x74,
|
||||
0xb7, 0x40, 0x93, 0x2b, 0x34, 0x29, 0xd2, 0x8a, 0xf3, 0x99, 0xf9, 0xe9,
|
||||
0xd8, 0xcc, 0x48, 0x1d, 0x3e, 0xc1, 0x82, 0x35, 0x4f, 0xef, 0xb1, 0x81,
|
||||
0x3c, 0xe1, 0xa1, 0x03, 0x65, 0xac, 0x21, 0x21, 0x40, 0x61, 0xfb, 0xd3,
|
||||
0x54, 0xac, 0xa1, 0xf2, 0xf0, 0x61, 0xd9, 0x01, 0x4e, 0xc2, 0x28, 0xb1,
|
||||
0x7c, 0x27, 0x6e, 0x56, 0x68, 0x69, 0x8f, 0xc5, 0xfd, 0xca, 0x39, 0x6e,
|
||||
0x22, 0x09, 0xf1, 0xb4, 0xd5, 0xac, 0xb8, 0xe0, 0x1b, 0x21, 0x86, 0xf4,
|
||||
0xc8, 0x15, 0xc6, 0x1f, 0x21, 0xae, 0xcb, 0xab, 0x5a, 0x09, 0x30, 0x9e,
|
||||
0xdd, 0x6c, 0x38, 0x59, 0xec, 0x59, 0x3a, 0x08, 0xee, 0x46, 0x7b, 0x78,
|
||||
0x23, 0xbc, 0xfc, 0xe2, 0xda, 0xe8, 0x1a, 0x65, 0xe6, 0xe0, 0x78, 0xd3,
|
||||
0xb0, 0x03, 0x2e, 0xf1, 0xb8, 0xca, 0x8e, 0x90, 0x75, 0xaf, 0xf7, 0xa8,
|
||||
0x48, 0xed, 0x82, 0xc9, 0xcf, 0x44, 0x56, 0xfc, 0x05, 0xfd, 0x6b, 0x00,
|
||||
0x00, 0x00, 0x81, 0x00, 0xfc, 0x94, 0xdf, 0x42, 0xc7, 0x9a, 0xa2, 0xff,
|
||||
0x32, 0xdf, 0x06, 0xb6, 0x4d, 0x90, 0x31, 0x28, 0x28, 0xdb, 0x03, 0xf9,
|
||||
0xa6, 0xb3, 0xa2, 0x91, 0x4c, 0xdf, 0x6e, 0xf6, 0xb9, 0x44, 0x3b, 0xdd,
|
||||
0x17, 0xc1, 0xc8, 0x1d, 0xd1, 0xc0, 0xc0, 0x30, 0x22, 0xbe, 0x24, 0x2e,
|
||||
0x0e, 0xdf, 0xe0, 0x18, 0x37, 0x3e, 0xb8, 0x7f, 0xb2, 0x50, 0x34, 0xc4,
|
||||
0x08, 0x5e, 0x69, 0x1f, 0xd5, 0xc9, 0xce, 0x47, 0x7d, 0x75, 0x5e, 0x3b,
|
||||
0x87, 0xdd, 0x46, 0x35, 0x01, 0x0f, 0x17, 0x8a, 0xf1, 0xf1, 0xc4, 0xa9,
|
||||
0x94, 0xa7, 0x6e, 0xce, 0x80, 0xe3, 0x17, 0x2e, 0xb0, 0xef, 0x63, 0xa7,
|
||||
0x11, 0x86, 0x96, 0x4a, 0x63, 0x2d, 0x9e, 0x92, 0x62, 0x43, 0x43, 0x72,
|
||||
0xa5, 0xdc, 0xa0, 0xcd, 0x19, 0x93, 0xd7, 0xe0, 0x80, 0x41, 0x27, 0xea,
|
||||
0xe4, 0xe8, 0xc1, 0x91, 0x9e, 0x13, 0xb3, 0x9c, 0xd1, 0xed, 0xcb, 0xbf,
|
||||
0x00, 0x00, 0x00, 0x81, 0x00, 0xb3, 0x6b, 0xee, 0xa4, 0x70, 0x4e, 0xfb,
|
||||
0xf9, 0x7e, 0x2e, 0x74, 0x5d, 0x3e, 0x8b, 0x3f, 0xff, 0x8c, 0xde, 0x68,
|
||||
0x38, 0xda, 0xce, 0xc0, 0x66, 0x4b, 0xca, 0x35, 0xc3, 0x97, 0xa8, 0xf0,
|
||||
0x00, 0x8e, 0xb3, 0x46, 0x60, 0xd0, 0x4d, 0x7e, 0x7b, 0xdf, 0x17, 0x7b,
|
||||
0x2f, 0xc4, 0x16, 0xee, 0x45, 0xdb, 0xa5, 0x5d, 0xc0, 0x72, 0xe9, 0xc6,
|
||||
0x91, 0x0f, 0xd9, 0x30, 0x74, 0x6c, 0xde, 0x93, 0xb5, 0xb6, 0xaf, 0x52,
|
||||
0x53, 0x3c, 0x08, 0x55, 0xea, 0xb8, 0x66, 0x07, 0xbe, 0xce, 0xf9, 0x80,
|
||||
0x8d, 0xe0, 0xca, 0xdc, 0x63, 0xe8, 0x58, 0x94, 0x22, 0x4f, 0x08, 0x66,
|
||||
0x13, 0x9e, 0x63, 0x2e, 0x92, 0x7a, 0xb6, 0x66, 0x94, 0x9b, 0x71, 0x66,
|
||||
0xd3, 0x08, 0xc9, 0x89, 0xea, 0x78, 0x35, 0x0d, 0xf2, 0x25, 0x55, 0xd4,
|
||||
0xb0, 0x9b, 0xea, 0x18, 0x77, 0xf6, 0x25, 0x02, 0xb4, 0x5e, 0x71, 0xea,
|
||||
0xa3
|
||||
};
|
||||
unsigned int keyr_len = 805;
|
||||
unsigned char keye[] = {
|
||||
0x00, 0x00, 0x00, 0x13, 0x65, 0x63, 0x64, 0x73, 0x61, 0x2d, 0x73, 0x68,
|
||||
0x61, 0x32, 0x2d, 0x6e, 0x69, 0x73, 0x74, 0x70, 0x32, 0x35, 0x36, 0x00,
|
||||
0x00, 0x00, 0x08, 0x6e, 0x69, 0x73, 0x74, 0x70, 0x32, 0x35, 0x36, 0x00,
|
||||
0x00, 0x00, 0x41, 0x04, 0x0a, 0x00, 0x6c, 0x7c, 0x1c, 0xc4, 0x03, 0x44,
|
||||
0x46, 0x70, 0xba, 0x00, 0x7c, 0x79, 0x89, 0x7b, 0xc3, 0xd6, 0x32, 0x98,
|
||||
0x34, 0xe7, 0x1c, 0x60, 0x04, 0x73, 0xd9, 0xb5, 0x7e, 0x94, 0x04, 0x04,
|
||||
0xea, 0xc8, 0xb8, 0xfb, 0xd4, 0x70, 0x9f, 0x29, 0xa7, 0x8d, 0x9a, 0x64,
|
||||
0x3a, 0x8c, 0x45, 0x23, 0x37, 0x5a, 0x2b, 0x4f, 0x54, 0x91, 0x80, 0xf1,
|
||||
0xac, 0x3a, 0xf5, 0x6d, 0xfa, 0xe8, 0x76, 0x20, 0x00, 0x00, 0x00, 0x21,
|
||||
0x00, 0xc2, 0xaf, 0xbe, 0xdc, 0x06, 0xff, 0x3d, 0x08, 0x9b, 0x73, 0xe0,
|
||||
0x3c, 0x58, 0x28, 0x70, 0x9b, 0x23, 0x39, 0x51, 0xd7, 0xbc, 0xa7, 0x1a,
|
||||
0xf5, 0xb4, 0x23, 0xd3, 0xf6, 0x17, 0xa6, 0x9c, 0x02
|
||||
};
|
||||
unsigned int keye_len = 141;
|
||||
unsigned char keyd[] = {
|
||||
0x00, 0x00, 0x00, 0x07, 0x73, 0x73, 0x68, 0x2d, 0x64, 0x73, 0x73, 0x00,
|
||||
0x00, 0x00, 0x81, 0x00, 0xb0, 0x02, 0x19, 0x8b, 0xf3, 0x46, 0xf9, 0xc5,
|
||||
0x47, 0x78, 0x3d, 0x7f, 0x04, 0x10, 0x0a, 0x43, 0x8e, 0x00, 0x9e, 0xa4,
|
||||
0x30, 0xfd, 0x47, 0xb9, 0x05, 0x9e, 0x95, 0xaa, 0x37, 0x9a, 0x91, 0xbf,
|
||||
0xf8, 0xb9, 0xe0, 0x8d, 0x97, 0x49, 0x87, 0xe2, 0xe6, 0x90, 0xc1, 0xe4,
|
||||
0x61, 0x57, 0x77, 0xfd, 0x91, 0x1d, 0xe1, 0x4b, 0xa0, 0xb2, 0xbc, 0xa1,
|
||||
0x6a, 0x6a, 0xdd, 0x31, 0xda, 0xe7, 0x54, 0x03, 0xfd, 0x48, 0x62, 0x8a,
|
||||
0x1d, 0x1d, 0xe2, 0x26, 0x76, 0x29, 0x08, 0xab, 0x65, 0x88, 0x74, 0x02,
|
||||
0x1e, 0xa9, 0x29, 0x1b, 0x69, 0x3b, 0xb4, 0x5f, 0x62, 0x80, 0xa3, 0xa6,
|
||||
0x4b, 0xc3, 0x0e, 0x89, 0x24, 0xe4, 0x8a, 0x31, 0xae, 0x89, 0x7a, 0x7a,
|
||||
0x58, 0x44, 0x46, 0x77, 0x62, 0x33, 0xa2, 0x5d, 0x17, 0x0e, 0x0b, 0x64,
|
||||
0xee, 0x1a, 0x02, 0xbd, 0xf8, 0x27, 0x86, 0xe1, 0x87, 0x92, 0x84, 0xc7,
|
||||
0x00, 0x00, 0x00, 0x15, 0x00, 0xb3, 0x8b, 0x81, 0x39, 0x9c, 0xba, 0xe1,
|
||||
0x1d, 0x9a, 0x8b, 0x89, 0xb3, 0x08, 0x9b, 0x12, 0xa8, 0x7b, 0xea, 0x25,
|
||||
0x8d, 0x00, 0x00, 0x00, 0x80, 0x76, 0x3f, 0x72, 0xb2, 0xef, 0xc3, 0x16,
|
||||
0xd8, 0x09, 0x36, 0x23, 0x03, 0xf9, 0x5c, 0xac, 0x8b, 0x51, 0x35, 0x2e,
|
||||
0x36, 0xba, 0x39, 0xd0, 0x57, 0x19, 0x4f, 0x14, 0x8b, 0xea, 0x32, 0xfc,
|
||||
0x86, 0x41, 0xea, 0x85, 0x71, 0x4d, 0x52, 0x0c, 0xff, 0xc1, 0xd3, 0xd5,
|
||||
0xcd, 0x2e, 0x37, 0xcc, 0xe1, 0xcc, 0x22, 0x38, 0xa8, 0x47, 0x16, 0x34,
|
||||
0x3b, 0x32, 0x9c, 0x2f, 0x0f, 0xcd, 0x5f, 0x7f, 0x06, 0x64, 0x89, 0xc5,
|
||||
0x02, 0x4f, 0x9a, 0x70, 0x11, 0xf0, 0xaa, 0xe1, 0x7a, 0x75, 0x49, 0x8d,
|
||||
0x0f, 0x8d, 0x5b, 0x54, 0xe2, 0xe7, 0x10, 0x6e, 0xe5, 0xbd, 0xb7, 0x62,
|
||||
0xf7, 0x40, 0x59, 0x39, 0x31, 0xd9, 0x13, 0x7b, 0xa3, 0xdf, 0x0d, 0x31,
|
||||
0x52, 0x43, 0xe0, 0xaf, 0x19, 0x12, 0x15, 0x12, 0x34, 0x01, 0x6f, 0xcf,
|
||||
0x62, 0x21, 0xe4, 0xc8, 0x34, 0x69, 0xc9, 0x85, 0xe3, 0xde, 0xd7, 0x0c,
|
||||
0xac, 0x00, 0x00, 0x00, 0x80, 0x41, 0xa3, 0xc5, 0xa4, 0x89, 0x86, 0xc8,
|
||||
0x17, 0xf3, 0x8e, 0x68, 0x72, 0xbe, 0x13, 0x8b, 0x63, 0xe3, 0x07, 0xe3,
|
||||
0xd5, 0xa4, 0xa2, 0xd3, 0x2c, 0x2f, 0xbe, 0x16, 0x71, 0xc9, 0x79, 0x64,
|
||||
0x5a, 0x1e, 0x19, 0x82, 0x07, 0xe2, 0x93, 0xda, 0x22, 0xcf, 0x6d, 0xdd,
|
||||
0x38, 0xcb, 0x6e, 0x6b, 0x0f, 0x95, 0x8d, 0xfa, 0x3f, 0xbb, 0xb8, 0x6a,
|
||||
0x7d, 0xc3, 0x22, 0x1e, 0x49, 0xcf, 0x98, 0x73, 0x05, 0x5d, 0x97, 0xfa,
|
||||
0x4c, 0xf2, 0x82, 0x3d, 0x98, 0x61, 0x4e, 0x96, 0x80, 0x26, 0x79, 0xda,
|
||||
0x24, 0xf8, 0xa1, 0x9c, 0x71, 0x82, 0xe6, 0xc7, 0xdc, 0xc2, 0xa5, 0xd0,
|
||||
0xf4, 0x36, 0xba, 0xaa, 0xee, 0xd3, 0x43, 0x46, 0x1d, 0xaa, 0x53, 0xea,
|
||||
0x85, 0x2c, 0x1b, 0xc8, 0x7c, 0x3c, 0xe7, 0x06, 0x44, 0xab, 0x16, 0xad,
|
||||
0xc6, 0x54, 0x91, 0x9a, 0xb9, 0xc0, 0xeb, 0x93, 0x8c, 0xca, 0x39, 0xcf,
|
||||
0x6f, 0x00, 0x00, 0x00, 0x15, 0x00, 0x90, 0x26, 0x0a, 0xfc, 0x15, 0x99,
|
||||
0x7b, 0xac, 0xaa, 0x0c, 0xa2, 0xca, 0x7b, 0xa8, 0xd4, 0xdf, 0x68, 0x56,
|
||||
0xf9, 0x39
|
||||
};
|
||||
unsigned int keyd_len = 458;
|
||||
246
fuzz-wrapfd.c
Normal file
246
fuzz-wrapfd.c
Normal file
@@ -0,0 +1,246 @@
|
||||
#define FUZZ_SKIP_WRAP 1
|
||||
#include "includes.h"
|
||||
#include "fuzz-wrapfd.h"
|
||||
|
||||
#include "dbutil.h"
|
||||
|
||||
#include "fuzz.h"
|
||||
|
||||
#define IOWRAP_MAXFD (FD_SETSIZE-1)
|
||||
static const int MAX_RANDOM_IN = 50000;
|
||||
static const double CHANCE_CLOSE = 1.0 / 600;
|
||||
static const double CHANCE_INTR = 1.0 / 900;
|
||||
static const double CHANCE_READ1 = 0.96;
|
||||
static const double CHANCE_READ2 = 0.5;
|
||||
static const double CHANCE_WRITE1 = 0.96;
|
||||
static const double CHANCE_WRITE2 = 0.5;
|
||||
|
||||
struct fdwrap {
|
||||
enum wrapfd_mode mode;
|
||||
buffer *buf;
|
||||
int closein;
|
||||
int closeout;
|
||||
};
|
||||
|
||||
static struct fdwrap wrap_fds[IOWRAP_MAXFD+1];
|
||||
/* for quick selection of in-use descriptors */
|
||||
static int wrap_used[IOWRAP_MAXFD+1];
|
||||
static unsigned int nused;
|
||||
static unsigned short rand_state[3];
|
||||
|
||||
void wrapfd_setup(void) {
|
||||
TRACE(("wrapfd_setup"))
|
||||
nused = 0;
|
||||
memset(wrap_fds, 0x0, sizeof(wrap_fds));
|
||||
memset(wrap_used, 0x0, sizeof(wrap_used));
|
||||
|
||||
memset(rand_state, 0x0, sizeof(rand_state));
|
||||
wrapfd_setseed(50);
|
||||
}
|
||||
|
||||
void wrapfd_setseed(uint32_t seed) {
|
||||
memcpy(rand_state, &seed, sizeof(seed));
|
||||
nrand48(rand_state);
|
||||
}
|
||||
|
||||
void wrapfd_add(int fd, buffer *buf, enum wrapfd_mode mode) {
|
||||
TRACE(("wrapfd_add %d buf %p mode %d", fd, buf, mode))
|
||||
assert(fd >= 0);
|
||||
assert(fd <= IOWRAP_MAXFD);
|
||||
assert(wrap_fds[fd].mode == UNUSED);
|
||||
assert(buf || mode == RANDOMIN);
|
||||
|
||||
wrap_fds[fd].mode = mode;
|
||||
wrap_fds[fd].buf = buf;
|
||||
wrap_fds[fd].closein = 0;
|
||||
wrap_fds[fd].closeout = 0;
|
||||
wrap_used[nused] = fd;
|
||||
|
||||
nused++;
|
||||
}
|
||||
|
||||
void wrapfd_remove(int fd) {
|
||||
unsigned int i, j;
|
||||
TRACE(("wrapfd_remove %d", fd))
|
||||
assert(fd >= 0);
|
||||
assert(fd <= IOWRAP_MAXFD);
|
||||
assert(wrap_fds[fd].mode != UNUSED);
|
||||
wrap_fds[fd].mode = UNUSED;
|
||||
|
||||
|
||||
/* remove from used list */
|
||||
for (i = 0, j = 0; i < nused; i++) {
|
||||
if (wrap_used[i] != fd) {
|
||||
wrap_used[j] = wrap_used[i];
|
||||
j++;
|
||||
}
|
||||
}
|
||||
nused--;
|
||||
}
|
||||
|
||||
int wrapfd_close(int fd) {
|
||||
if (fd >= 0 && fd <= IOWRAP_MAXFD && wrap_fds[fd].mode != UNUSED) {
|
||||
wrapfd_remove(fd);
|
||||
return 0;
|
||||
} else {
|
||||
return close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
int wrapfd_read(int fd, void *out, size_t count) {
|
||||
size_t maxread;
|
||||
buffer *buf;
|
||||
|
||||
if (!fuzz.wrapfds) {
|
||||
return read(fd, out, count);
|
||||
}
|
||||
|
||||
if (fd < 0 || fd > IOWRAP_MAXFD || wrap_fds[fd].mode == UNUSED) {
|
||||
/* XXX - assertion failure? */
|
||||
TRACE(("Bad read descriptor %d\n", fd))
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(count != 0);
|
||||
|
||||
if (wrap_fds[fd].closein || erand48(rand_state) < CHANCE_CLOSE) {
|
||||
wrap_fds[fd].closein = 1;
|
||||
errno = ECONNRESET;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (erand48(rand_state) < CHANCE_INTR) {
|
||||
errno = EINTR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
buf = wrap_fds[fd].buf;
|
||||
if (buf) {
|
||||
maxread = MIN(buf->len - buf->pos, count);
|
||||
/* returns 0 if buf is EOF, as intended */
|
||||
if (maxread > 0) {
|
||||
maxread = nrand48(rand_state) % maxread + 1;
|
||||
}
|
||||
memcpy(out, buf_getptr(buf, maxread), maxread);
|
||||
buf_incrpos(buf, maxread);
|
||||
return maxread;
|
||||
}
|
||||
|
||||
maxread = MIN(MAX_RANDOM_IN, count);
|
||||
maxread = nrand48(rand_state) % maxread + 1;
|
||||
memset(out, 0xef, maxread);
|
||||
return maxread;
|
||||
}
|
||||
|
||||
int wrapfd_write(int fd, const void* in, size_t count) {
|
||||
unsigned const volatile char* volin = in;
|
||||
unsigned int i;
|
||||
|
||||
if (!fuzz.wrapfds) {
|
||||
return write(fd, in, count);
|
||||
}
|
||||
|
||||
if (fd < 0 || fd > IOWRAP_MAXFD || wrap_fds[fd].mode == UNUSED) {
|
||||
/* XXX - assertion failure? */
|
||||
TRACE(("Bad read descriptor %d\n", fd))
|
||||
errno = EBADF;
|
||||
return -1;
|
||||
}
|
||||
|
||||
assert(count != 0);
|
||||
|
||||
/* force read to exercise sanitisers */
|
||||
for (i = 0; i < count; i++) {
|
||||
(void)volin[i];
|
||||
}
|
||||
|
||||
if (wrap_fds[fd].closeout || erand48(rand_state) < CHANCE_CLOSE) {
|
||||
wrap_fds[fd].closeout = 1;
|
||||
errno = ECONNRESET;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (erand48(rand_state) < CHANCE_INTR) {
|
||||
errno = EINTR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return nrand48(rand_state) % (count+1);
|
||||
}
|
||||
|
||||
int wrapfd_select(int nfds, fd_set *readfds, fd_set *writefds,
|
||||
fd_set *exceptfds, struct timeval *timeout) {
|
||||
int i, nset, sel;
|
||||
int ret = 0;
|
||||
int fdlist[IOWRAP_MAXFD+1];
|
||||
|
||||
memset(fdlist, 0x0, sizeof(fdlist));
|
||||
|
||||
if (!fuzz.wrapfds) {
|
||||
return select(nfds, readfds, writefds, exceptfds, timeout);
|
||||
}
|
||||
|
||||
assert(nfds <= IOWRAP_MAXFD+1);
|
||||
|
||||
if (erand48(rand_state) < CHANCE_INTR) {
|
||||
errno = EINTR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* read */
|
||||
if (readfds != NULL && erand48(rand_state) < CHANCE_READ1) {
|
||||
for (i = 0, nset = 0; i < nfds; i++) {
|
||||
if (FD_ISSET(i, readfds)) {
|
||||
assert(wrap_fds[i].mode != UNUSED);
|
||||
fdlist[nset] = i;
|
||||
nset++;
|
||||
}
|
||||
}
|
||||
DROPBEAR_FD_ZERO(readfds);
|
||||
|
||||
if (nset > 0) {
|
||||
/* set one */
|
||||
sel = fdlist[nrand48(rand_state) % nset];
|
||||
FD_SET(sel, readfds);
|
||||
ret++;
|
||||
|
||||
if (erand48(rand_state) < CHANCE_READ2) {
|
||||
sel = fdlist[nrand48(rand_state) % nset];
|
||||
if (!FD_ISSET(sel, readfds)) {
|
||||
FD_SET(sel, readfds);
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* write */
|
||||
if (writefds != NULL && erand48(rand_state) < CHANCE_WRITE1) {
|
||||
for (i = 0, nset = 0; i < nfds; i++) {
|
||||
if (FD_ISSET(i, writefds)) {
|
||||
assert(wrap_fds[i].mode != UNUSED);
|
||||
fdlist[nset] = i;
|
||||
nset++;
|
||||
}
|
||||
}
|
||||
DROPBEAR_FD_ZERO(writefds);
|
||||
|
||||
/* set one */
|
||||
if (nset > 0) {
|
||||
sel = fdlist[nrand48(rand_state) % nset];
|
||||
FD_SET(sel, writefds);
|
||||
ret++;
|
||||
|
||||
if (erand48(rand_state) < CHANCE_WRITE2) {
|
||||
sel = fdlist[nrand48(rand_state) % nset];
|
||||
if (!FD_ISSET(sel, writefds)) {
|
||||
FD_SET(sel, writefds);
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
25
fuzz-wrapfd.h
Normal file
25
fuzz-wrapfd.h
Normal file
@@ -0,0 +1,25 @@
|
||||
#ifndef FUZZ_WRAPFD_H
|
||||
#define FUZZ_WRAPFD_H
|
||||
|
||||
#include "buffer.h"
|
||||
|
||||
enum wrapfd_mode {
|
||||
UNUSED = 0,
|
||||
PLAIN,
|
||||
INPROGRESS,
|
||||
RANDOMIN
|
||||
};
|
||||
|
||||
void wrapfd_setup(void);
|
||||
void wrapfd_setseed(uint32_t seed);
|
||||
// doesn't take ownership of buf. buf is optional.
|
||||
void wrapfd_add(int fd, buffer *buf, enum wrapfd_mode mode);
|
||||
|
||||
// called via #defines for read/write/select
|
||||
int wrapfd_read(int fd, void *out, size_t count);
|
||||
int wrapfd_write(int fd, const void* in, size_t count);
|
||||
int wrapfd_select(int nfds, fd_set *readfds, fd_set *writefds,
|
||||
fd_set *exceptfds, struct timeval *timeout);
|
||||
int wrapfd_close(int fd);
|
||||
|
||||
#endif // FUZZ_WRAPFD_H
|
||||
72
fuzz.h
Normal file
72
fuzz.h
Normal file
@@ -0,0 +1,72 @@
|
||||
#ifndef DROPBEAR_FUZZ_H
|
||||
#define DROPBEAR_FUZZ_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#if DROPBEAR_FUZZ
|
||||
|
||||
#include "includes.h"
|
||||
#include "buffer.h"
|
||||
#include "algo.h"
|
||||
#include "fuzz-wrapfd.h"
|
||||
|
||||
// once per process
|
||||
void fuzz_common_setup(void);
|
||||
void fuzz_svr_setup(void);
|
||||
|
||||
// must be called once per fuzz iteration.
|
||||
// returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE
|
||||
int fuzz_set_input(const uint8_t *Data, size_t Size);
|
||||
|
||||
int fuzz_run_preauth(const uint8_t *Data, size_t Size, int skip_kexmaths);
|
||||
const void* fuzz_get_algo(const algo_type *algos, const char* name);
|
||||
|
||||
// fuzzer functions that intrude into general code
|
||||
void fuzz_kex_fakealgos(void);
|
||||
int fuzz_checkpubkey_line(buffer* line, int line_num, char* filename,
|
||||
const char* algo, unsigned int algolen,
|
||||
const unsigned char* keyblob, unsigned int keybloblen);
|
||||
extern const char * const * fuzz_signkey_names;
|
||||
void fuzz_seed(void);
|
||||
void fuzz_get_socket_address(int fd, char **local_host, char **local_port,
|
||||
char **remote_host, char **remote_port, int host_lookup);
|
||||
void fuzz_fake_send_kexdh_reply(void);
|
||||
|
||||
// fake IO wrappers
|
||||
#ifndef FUZZ_SKIP_WRAP
|
||||
#define select(nfds, readfds, writefds, exceptfds, timeout) \
|
||||
wrapfd_select(nfds, readfds, writefds, exceptfds, timeout)
|
||||
#define write(fd, buf, count) wrapfd_write(fd, buf, count)
|
||||
#define read(fd, buf, count) wrapfd_read(fd, buf, count)
|
||||
#define close(fd) wrapfd_close(fd)
|
||||
#endif // FUZZ_SKIP_WRAP
|
||||
|
||||
struct dropbear_fuzz_options {
|
||||
int fuzzing;
|
||||
|
||||
// fuzzing input
|
||||
buffer *input;
|
||||
struct dropbear_cipher recv_cipher;
|
||||
struct dropbear_hash recv_mac;
|
||||
int wrapfds;
|
||||
|
||||
// whether to skip slow bignum maths
|
||||
int skip_kexmaths;
|
||||
|
||||
// dropbear_exit() jumps back
|
||||
int do_jmp;
|
||||
sigjmp_buf jmp;
|
||||
|
||||
uid_t pw_uid;
|
||||
gid_t pw_gid;
|
||||
char* pw_name;
|
||||
char* pw_dir;
|
||||
char* pw_shell;
|
||||
char* pw_passwd;
|
||||
};
|
||||
|
||||
extern struct dropbear_fuzz_options fuzz;
|
||||
|
||||
#endif // DROPBEAR_FUZZ
|
||||
|
||||
#endif /* DROPBEAR_FUZZ_H */
|
||||
76
fuzzer-kexdh.c
Normal file
76
fuzzer-kexdh.c
Normal file
@@ -0,0 +1,76 @@
|
||||
#include "fuzz.h"
|
||||
#include "session.h"
|
||||
#include "fuzz-wrapfd.h"
|
||||
#include "debug.h"
|
||||
#include "runopts.h"
|
||||
#include "algo.h"
|
||||
#include "bignum.h"
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
static int once = 0;
|
||||
static struct key_context* keep_newkeys = NULL;
|
||||
/* number of generated parameters is limited by the timeout for the first run.
|
||||
TODO move this to the libfuzzer initialiser function instead if the timeout
|
||||
doesn't apply there */
|
||||
#define NUM_PARAMS 20
|
||||
static struct kex_dh_param *dh_params[NUM_PARAMS];
|
||||
|
||||
if (!once) {
|
||||
fuzz_common_setup();
|
||||
fuzz_svr_setup();
|
||||
|
||||
keep_newkeys = (struct key_context*)m_malloc(sizeof(struct key_context));
|
||||
keep_newkeys->algo_kex = fuzz_get_algo(sshkex, "diffie-hellman-group14-sha256");
|
||||
keep_newkeys->algo_hostkey = DROPBEAR_SIGNKEY_ECDSA_NISTP256;
|
||||
ses.newkeys = keep_newkeys;
|
||||
|
||||
/* Pre-generate parameters */
|
||||
int i;
|
||||
for (i = 0; i < NUM_PARAMS; i++) {
|
||||
dh_params[i] = gen_kexdh_param();
|
||||
}
|
||||
|
||||
once = 1;
|
||||
}
|
||||
|
||||
if (fuzz_set_input(Data, Size) == DROPBEAR_FAILURE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
m_malloc_set_epoch(1);
|
||||
|
||||
if (setjmp(fuzz.jmp) == 0) {
|
||||
/* Based on recv_msg_kexdh_init()/send_msg_kexdh_reply()
|
||||
with DROPBEAR_KEX_NORMAL_DH */
|
||||
ses.newkeys = keep_newkeys;
|
||||
|
||||
/* Choose from the collection of ecdh params */
|
||||
unsigned int e = buf_getint(fuzz.input);
|
||||
struct kex_dh_param * dh_param = dh_params[e % NUM_PARAMS];
|
||||
|
||||
DEF_MP_INT(dh_e);
|
||||
m_mp_init(&dh_e);
|
||||
if (buf_getmpint(fuzz.input, &dh_e) != DROPBEAR_SUCCESS) {
|
||||
dropbear_exit("Bad kex value");
|
||||
}
|
||||
|
||||
ses.kexhashbuf = buf_new(KEXHASHBUF_MAX_INTS);
|
||||
kexdh_comb_key(dh_param, &dh_e, svr_opts.hostkey);
|
||||
|
||||
mp_clear(ses.dh_K);
|
||||
m_free(ses.dh_K);
|
||||
mp_clear(&dh_e);
|
||||
|
||||
buf_free(ses.hash);
|
||||
buf_free(ses.session_id);
|
||||
/* kexhashbuf is freed in kexdh_comb_key */
|
||||
|
||||
m_malloc_free_epoch(1, 0);
|
||||
} else {
|
||||
m_malloc_free_epoch(1, 1);
|
||||
TRACE(("dropbear_exit longjmped"))
|
||||
/* dropbear_exit jumped here */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
82
fuzzer-kexecdh.c
Normal file
82
fuzzer-kexecdh.c
Normal file
@@ -0,0 +1,82 @@
|
||||
#include "fuzz.h"
|
||||
#include "session.h"
|
||||
#include "fuzz-wrapfd.h"
|
||||
#include "debug.h"
|
||||
#include "runopts.h"
|
||||
#include "algo.h"
|
||||
#include "bignum.h"
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
static int once = 0;
|
||||
static const struct dropbear_kex *ecdh[3]; /* 256, 384, 521 */
|
||||
static struct key_context* keep_newkeys = NULL;
|
||||
/* number of generated parameters is limited by the timeout for the first run */
|
||||
#define NUM_PARAMS 80
|
||||
static struct kex_ecdh_param *ecdh_params[NUM_PARAMS];
|
||||
|
||||
if (!once) {
|
||||
fuzz_common_setup();
|
||||
fuzz_svr_setup();
|
||||
|
||||
/* ses gets zeroed by fuzz_set_input */
|
||||
keep_newkeys = (struct key_context*)m_malloc(sizeof(struct key_context));
|
||||
ecdh[0] = fuzz_get_algo(sshkex, "ecdh-sha2-nistp256");
|
||||
ecdh[1] = fuzz_get_algo(sshkex, "ecdh-sha2-nistp384");
|
||||
ecdh[2] = fuzz_get_algo(sshkex, "ecdh-sha2-nistp521");
|
||||
assert(ecdh[0]);
|
||||
assert(ecdh[1]);
|
||||
assert(ecdh[2]);
|
||||
keep_newkeys->algo_hostkey = DROPBEAR_SIGNKEY_ECDSA_NISTP256;
|
||||
ses.newkeys = keep_newkeys;
|
||||
|
||||
/* Pre-generate parameters */
|
||||
int i;
|
||||
for (i = 0; i < NUM_PARAMS; i++) {
|
||||
ses.newkeys->algo_kex = ecdh[i % 3];
|
||||
ecdh_params[i] = gen_kexecdh_param();
|
||||
}
|
||||
|
||||
once = 1;
|
||||
}
|
||||
|
||||
if (fuzz_set_input(Data, Size) == DROPBEAR_FAILURE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
m_malloc_set_epoch(1);
|
||||
|
||||
if (setjmp(fuzz.jmp) == 0) {
|
||||
/* Based on recv_msg_kexdh_init()/send_msg_kexdh_reply()
|
||||
with DROPBEAR_KEX_ECDH */
|
||||
ses.newkeys = keep_newkeys;
|
||||
|
||||
/* random choice of ecdh 256, 384, 521 */
|
||||
unsigned char b = buf_getbyte(fuzz.input);
|
||||
ses.newkeys->algo_kex = ecdh[b % 3];
|
||||
|
||||
/* Choose from the collection of ecdh params */
|
||||
unsigned int e = buf_getint(fuzz.input);
|
||||
struct kex_ecdh_param *ecdh_param = ecdh_params[e % NUM_PARAMS];
|
||||
|
||||
buffer * ecdh_qs = buf_getstringbuf(fuzz.input);
|
||||
|
||||
ses.kexhashbuf = buf_new(KEXHASHBUF_MAX_INTS);
|
||||
kexecdh_comb_key(ecdh_param, ecdh_qs, svr_opts.hostkey);
|
||||
|
||||
mp_clear(ses.dh_K);
|
||||
m_free(ses.dh_K);
|
||||
buf_free(ecdh_qs);
|
||||
|
||||
buf_free(ses.hash);
|
||||
buf_free(ses.session_id);
|
||||
/* kexhashbuf is freed in kexdh_comb_key */
|
||||
|
||||
m_malloc_free_epoch(1, 0);
|
||||
} else {
|
||||
m_malloc_free_epoch(1, 1);
|
||||
TRACE(("dropbear_exit longjmped"))
|
||||
/* dropbear_exit jumped here */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
6
fuzzer-preauth.c
Normal file
6
fuzzer-preauth.c
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "fuzz.h"
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
return fuzz_run_preauth(Data, Size, 0);
|
||||
}
|
||||
|
||||
6
fuzzer-preauth_nomaths.c
Normal file
6
fuzzer-preauth_nomaths.c
Normal file
@@ -0,0 +1,6 @@
|
||||
#include "fuzz.h"
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
return fuzz_run_preauth(Data, Size, 1);
|
||||
}
|
||||
|
||||
54
fuzzer-pubkey.c
Normal file
54
fuzzer-pubkey.c
Normal file
@@ -0,0 +1,54 @@
|
||||
#include "fuzz.h"
|
||||
#include "session.h"
|
||||
#include "fuzz-wrapfd.h"
|
||||
#include "debug.h"
|
||||
|
||||
static void setup_fuzzer(void) {
|
||||
fuzz_common_setup();
|
||||
}
|
||||
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
static int once = 0;
|
||||
if (!once) {
|
||||
setup_fuzzer();
|
||||
once = 1;
|
||||
}
|
||||
|
||||
if (fuzz_set_input(Data, Size) == DROPBEAR_FAILURE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
m_malloc_set_epoch(1);
|
||||
|
||||
if (setjmp(fuzz.jmp) == 0) {
|
||||
buffer *line = buf_getstringbuf(fuzz.input);
|
||||
buffer *keyblob = buf_getstringbuf(fuzz.input);
|
||||
|
||||
unsigned int algolen;
|
||||
char* algoname = buf_getstring(keyblob, &algolen);
|
||||
|
||||
if (have_algo(algoname, algolen, sshhostkey) == DROPBEAR_FAILURE) {
|
||||
dropbear_exit("fuzzer imagined a bogus algorithm");
|
||||
}
|
||||
|
||||
int ret = fuzz_checkpubkey_line(line, 5, "/home/me/authorized_keys",
|
||||
algoname, algolen,
|
||||
keyblob->data, keyblob->len);
|
||||
|
||||
if (ret == DROPBEAR_SUCCESS) {
|
||||
/* fuzz_checkpubkey_line() should have cleaned up for failure */
|
||||
svr_pubkey_options_cleanup();
|
||||
}
|
||||
|
||||
buf_free(line);
|
||||
buf_free(keyblob);
|
||||
m_free(algoname);
|
||||
m_malloc_free_epoch(1, 0);
|
||||
} else {
|
||||
m_malloc_free_epoch(1, 1);
|
||||
TRACE(("dropbear_exit longjmped"))
|
||||
/* dropbear_exit jumped here */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
64
fuzzer-verify.c
Normal file
64
fuzzer-verify.c
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "fuzz.h"
|
||||
#include "session.h"
|
||||
#include "fuzz-wrapfd.h"
|
||||
#include "debug.h"
|
||||
|
||||
static void setup_fuzzer(void) {
|
||||
fuzz_common_setup();
|
||||
}
|
||||
|
||||
static buffer *verifydata;
|
||||
|
||||
/* Tests reading a public key and verifying a signature */
|
||||
int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
|
||||
static int once = 0;
|
||||
if (!once) {
|
||||
setup_fuzzer();
|
||||
verifydata = buf_new(30);
|
||||
buf_putstring(verifydata, "x", 1);
|
||||
once = 1;
|
||||
}
|
||||
|
||||
if (fuzz_set_input(Data, Size) == DROPBEAR_FAILURE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
m_malloc_set_epoch(1);
|
||||
|
||||
if (setjmp(fuzz.jmp) == 0) {
|
||||
sign_key *key = new_sign_key();
|
||||
enum signkey_type type = DROPBEAR_SIGNKEY_ANY;
|
||||
if (buf_get_pub_key(fuzz.input, key, &type) == DROPBEAR_SUCCESS) {
|
||||
if (buf_verify(fuzz.input, key, verifydata) == DROPBEAR_SUCCESS) {
|
||||
/* The fuzzer is capable of generating keys with a signature to match.
|
||||
We don't want false positives if the key is bogus, since a client/server
|
||||
wouldn't be trusting a bogus key anyway */
|
||||
int boguskey = 0;
|
||||
|
||||
if (type == DROPBEAR_SIGNKEY_DSS) {
|
||||
/* So far have seen dss keys with bad p/q/g domain parameters */
|
||||
int pprime, qprime;
|
||||
assert(mp_prime_is_prime(key->dsskey->p, 5, &pprime) == MP_OKAY);
|
||||
assert(mp_prime_is_prime(key->dsskey->q, 18, &qprime) == MP_OKAY);
|
||||
boguskey = !(pprime && qprime);
|
||||
/* Could also check g**q mod p == 1 */
|
||||
}
|
||||
|
||||
if (!boguskey) {
|
||||
printf("Random key/signature managed to verify!\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
sign_key_free(key);
|
||||
m_malloc_free_epoch(1, 0);
|
||||
} else {
|
||||
m_malloc_free_epoch(1, 1);
|
||||
TRACE(("dropbear_exit longjmped"))
|
||||
/* dropbear_exit jumped here */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
12
fuzzers_test.sh
Executable file
12
fuzzers_test.sh
Executable file
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
|
||||
# runs fuzz corpus with standalone fuzzers
|
||||
|
||||
result=0
|
||||
|
||||
hg clone https://secure.ucc.asn.au/hg/dropbear-fuzzcorpus fuzzcorpus || exit 1
|
||||
for f in `make list-fuzz-targets`; do
|
||||
./$f fuzzcorpus/$f/* || result=1
|
||||
done
|
||||
|
||||
exit $result
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
# Wrap all "#define X Y" with a #ifndef X...#endif"
|
||||
|
||||
sed -E 's/^( *#define ([^ ]+) .*)/#ifndef \2\
|
||||
sed 's/^\( *#define \([^ ][^ ]*\) .*\)/#ifndef \2\
|
||||
\1\
|
||||
#endif/'
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
#include <stdarg.h>
|
||||
#include <dirent.h>
|
||||
#include <time.h>
|
||||
#include <setjmp.h>
|
||||
|
||||
#ifdef HAVE_UTMP_H
|
||||
#include <utmp.h>
|
||||
@@ -131,7 +132,6 @@
|
||||
#include <tommath.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "compat.h"
|
||||
|
||||
#ifndef HAVE_U_INT8_T
|
||||
@@ -155,6 +155,10 @@ typedef unsigned int u_int32_t;
|
||||
typedef u_int32_t uint32_t;
|
||||
#endif /* HAVE_UINT32_T */
|
||||
|
||||
#ifndef SIZE_T_MAX
|
||||
#define SIZE_T_MAX ULONG_MAX
|
||||
#endif /* SIZE_T_MAX */
|
||||
|
||||
#ifdef HAVE_LINUX_PKT_SCHED_H
|
||||
#include <linux/types.h>
|
||||
#include <linux/pkt_sched.h>
|
||||
@@ -162,6 +166,8 @@ typedef u_int32_t uint32_t;
|
||||
|
||||
#include "fake-rfc2553.h"
|
||||
|
||||
#include "fuzz.h"
|
||||
|
||||
#ifndef LOG_AUTHPRIV
|
||||
#define LOG_AUTHPRIV LOG_AUTH
|
||||
#endif
|
||||
|
||||
4
kex.h
4
kex.h
@@ -34,6 +34,7 @@ void recv_msg_kexinit(void);
|
||||
void send_msg_newkeys(void);
|
||||
void recv_msg_newkeys(void);
|
||||
void kexfirstinitialise(void);
|
||||
void finish_kexhashbuf(void);
|
||||
|
||||
struct kex_dh_param *gen_kexdh_param(void);
|
||||
void free_kexdh_param(struct kex_dh_param *param);
|
||||
@@ -105,7 +106,4 @@ struct kex_curve25519_param {
|
||||
int curve25519_donna(unsigned char *out, const unsigned char *secret, const unsigned char *other);
|
||||
#endif
|
||||
|
||||
|
||||
#define MAX_KEXHASHBUF 2000
|
||||
|
||||
#endif /* DROPBEAR_KEX_H_ */
|
||||
|
||||
@@ -1097,7 +1097,9 @@ static int openssh_write(const char *filename, sign_key *key,
|
||||
buf_putbytes(seq_buf, curve_oid, curve_oid_len);
|
||||
|
||||
buf_incrwritepos(seq_buf,
|
||||
ber_write_id_len(buf_getwriteptr(seq_buf, 10), 1, 2+1+pubkey_size, 0xa0));
|
||||
ber_write_id_len(buf_getwriteptr(seq_buf, 10), 1,
|
||||
(pubkey_size +1 < 128 ? 2 : 3 ) +1 +pubkey_size, 0xa0));
|
||||
|
||||
buf_incrwritepos(seq_buf,
|
||||
ber_write_id_len(buf_getwriteptr(seq_buf, 10), 3, 1+pubkey_size, 0));
|
||||
buf_putbyte(seq_buf, 0);
|
||||
|
||||
@@ -12,6 +12,12 @@
|
||||
|
||||
#include "tomcrypt_dropbear.h"
|
||||
|
||||
#include "dbmalloc.h"
|
||||
#define XMALLOC m_malloc
|
||||
#define XFREE m_free_direct
|
||||
#define XREALLOC m_realloc
|
||||
#define XCALLOC m_calloc
|
||||
|
||||
/* macros for various libc functions you can change for embedded targets */
|
||||
#ifndef XMALLOC
|
||||
#define XMALLOC malloc
|
||||
|
||||
@@ -87,7 +87,7 @@ int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
|
||||
{
|
||||
mp_digit *tmpc;
|
||||
tmpc = c->dp;
|
||||
for (ix = 0; ix < (pa + 1); ix++) {
|
||||
for (ix = 0; ix < pa; ix++) {
|
||||
/* now extract the previous digit [below the carry] */
|
||||
*tmpc++ = W[ix];
|
||||
}
|
||||
|
||||
@@ -1062,6 +1062,12 @@
|
||||
#undef BN_MP_TOOM_MUL_C
|
||||
#undef BN_MP_TOOM_SQR_C
|
||||
|
||||
#include "dbmalloc.h"
|
||||
#define XMALLOC m_malloc
|
||||
#define XFREE m_free_direct
|
||||
#define XREALLOC m_realloc
|
||||
#define XCALLOC m_calloc
|
||||
|
||||
/* $Source$ */
|
||||
/* $Revision$ */
|
||||
/* $Date$ */
|
||||
|
||||
30
netio.c
30
netio.c
@@ -224,7 +224,6 @@ void remove_connect_pending() {
|
||||
|
||||
void set_connect_fds(fd_set *writefd) {
|
||||
m_list_elem *iter;
|
||||
TRACE(("enter set_connect_fds"))
|
||||
iter = ses.conn_pending.first;
|
||||
while (iter) {
|
||||
m_list_elem *next_iter = iter->next;
|
||||
@@ -249,7 +248,6 @@ void set_connect_fds(fd_set *writefd) {
|
||||
|
||||
void handle_connect_fds(const fd_set *writefd) {
|
||||
m_list_elem *iter;
|
||||
TRACE(("enter handle_connect_fds"))
|
||||
for (iter = ses.conn_pending.first; iter; iter = iter->next) {
|
||||
int val;
|
||||
socklen_t vallen = sizeof(val);
|
||||
@@ -283,7 +281,6 @@ void handle_connect_fds(const fd_set *writefd) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
TRACE(("leave handle_connect_fds - end iter"))
|
||||
}
|
||||
|
||||
void connect_set_writequeue(struct dropbear_progress_connection *c, struct Queue *writequeue) {
|
||||
@@ -297,7 +294,11 @@ void packet_queue_to_iovec(const struct Queue *queue, struct iovec *iov, unsigne
|
||||
buffer *writebuf;
|
||||
|
||||
#ifndef IOV_MAX
|
||||
#define IOV_MAX UIO_MAXIOV
|
||||
#if defined(__CYGWIN__) && !defined(UIO_MAXIOV)
|
||||
#define IOV_MAX 1024
|
||||
#else
|
||||
#define IOV_MAX UIO_MAXIOV
|
||||
#endif
|
||||
#endif
|
||||
|
||||
*iov_count = MIN(MIN(queue->count, IOV_MAX), *iov_count);
|
||||
@@ -305,10 +306,10 @@ void packet_queue_to_iovec(const struct Queue *queue, struct iovec *iov, unsigne
|
||||
for (l = queue->head, i = 0; i < *iov_count; l = l->link, i++)
|
||||
{
|
||||
writebuf = (buffer*)l->item;
|
||||
len = writebuf->len - 1 - writebuf->pos;
|
||||
len = writebuf->len - writebuf->pos;
|
||||
dropbear_assert(len > 0);
|
||||
TRACE2(("write_packet writev #%d type %d len %d/%d", i, writebuf->data[writebuf->len-1],
|
||||
len, writebuf->len-1))
|
||||
TRACE2(("write_packet writev #%d len %d/%d", i,
|
||||
len, writebuf->len))
|
||||
iov[i].iov_base = buf_getptr(writebuf, len);
|
||||
iov[i].iov_len = len;
|
||||
}
|
||||
@@ -319,7 +320,7 @@ void packet_queue_consume(struct Queue *queue, ssize_t written) {
|
||||
int len;
|
||||
while (written > 0) {
|
||||
writebuf = (buffer*)examine(queue);
|
||||
len = writebuf->len - 1 - writebuf->pos;
|
||||
len = writebuf->len - writebuf->pos;
|
||||
if (len > written) {
|
||||
/* partial buffer write */
|
||||
buf_incrpos(writebuf, written);
|
||||
@@ -360,6 +361,12 @@ void set_sock_priority(int sock, enum dropbear_prio prio) {
|
||||
int so_prio_val = 0;
|
||||
#endif
|
||||
|
||||
#if DROPBEAR_FUZZ
|
||||
if (fuzz.fuzzing) {
|
||||
TRACE(("fuzzing skips set_sock_prio"))
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Don't log ENOTSOCK errors so that this can harmlessly be called
|
||||
* on a client '-J' proxy pipe */
|
||||
@@ -584,6 +591,13 @@ void get_socket_address(int fd, char **local_host, char **local_port,
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
socklen_t addrlen;
|
||||
|
||||
#if DROPBEAR_FUZZ
|
||||
if (fuzz.fuzzing) {
|
||||
fuzz_get_socket_address(fd, local_host, local_port, remote_host, remote_port, host_lookup);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (local_host || local_port) {
|
||||
addrlen = sizeof(addr);
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
> > > Don't edit this file any more! < < <
|
||||
|
||||
Local compile-time configuration should be defined in localoptions.h
|
||||
in the build directory.
|
||||
See default_options.h.in for a description of the available options.
|
||||
*/
|
||||
|
||||
|
||||
47
packet.c
47
packet.c
@@ -35,6 +35,7 @@
|
||||
#include "auth.h"
|
||||
#include "channel.h"
|
||||
#include "netio.h"
|
||||
#include "runopts.h"
|
||||
|
||||
static int read_packet_init(void);
|
||||
static void make_mac(unsigned int seqno, const struct key_context_directional * key_state,
|
||||
@@ -57,14 +58,13 @@ static void buf_compress(buffer * dest, buffer * src, unsigned int len);
|
||||
void write_packet() {
|
||||
|
||||
ssize_t written;
|
||||
#ifdef HAVE_WRITEV
|
||||
#if defined(HAVE_WRITEV) && (defined(IOV_MAX) || defined(UIO_MAXIOV))
|
||||
/* 50 is somewhat arbitrary */
|
||||
unsigned int iov_count = 50;
|
||||
struct iovec iov[50];
|
||||
#else
|
||||
int len;
|
||||
buffer* writebuf;
|
||||
int packet_type;
|
||||
#endif
|
||||
|
||||
TRACE2(("enter write_packet"))
|
||||
@@ -76,6 +76,15 @@ void write_packet() {
|
||||
/* This may return EAGAIN. The main loop sometimes
|
||||
calls write_packet() without bothering to test with select() since
|
||||
it's likely to be necessary */
|
||||
#if DROPBEAR_FUZZ
|
||||
if (fuzz.fuzzing) {
|
||||
/* pretend to write one packet at a time */
|
||||
/* TODO(fuzz): randomise amount written based on the fuzz input */
|
||||
written = iov[0].iov_len;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
written = writev(ses.sock_out, iov, iov_count);
|
||||
if (written < 0) {
|
||||
if (errno == EINTR || errno == EAGAIN) {
|
||||
@@ -85,6 +94,7 @@ void write_packet() {
|
||||
dropbear_exit("Error writing: %s", strerror(errno));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
packet_queue_consume(&ses.writequeue, written);
|
||||
ses.writequeue_len -= written;
|
||||
@@ -94,15 +104,13 @@ void write_packet() {
|
||||
}
|
||||
|
||||
#else /* No writev () */
|
||||
#if DROPBEAR_FUZZ
|
||||
_Static_assert(0, "No fuzzing code for no-writev writes");
|
||||
#endif
|
||||
/* Get the next buffer in the queue of encrypted packets to write*/
|
||||
writebuf = (buffer*)examine(&ses.writequeue);
|
||||
|
||||
/* The last byte of the buffer is not to be transmitted, but is
|
||||
* a cleartext packet_type indicator */
|
||||
packet_type = writebuf->data[writebuf->len-1];
|
||||
len = writebuf->len - 1 - writebuf->pos;
|
||||
TRACE2(("write_packet type %d len %d/%d", packet_type,
|
||||
len, writebuf->len-1))
|
||||
len = writebuf->len - writebuf->pos;
|
||||
dropbear_assert(len > 0);
|
||||
/* Try to write as much as possible */
|
||||
written = write(ses.sock_out, buf_getptr(writebuf, len), len);
|
||||
@@ -352,6 +360,20 @@ static int checkmac() {
|
||||
buf_setpos(ses.readbuf, 0);
|
||||
make_mac(ses.recvseq, &ses.keys->recv, ses.readbuf, contents_len, mac_bytes);
|
||||
|
||||
#if DROPBEAR_FUZZ
|
||||
if (fuzz.fuzzing) {
|
||||
/* fail 1 in 2000 times to test error path. */
|
||||
unsigned int value = 0;
|
||||
if (mac_size > sizeof(value)) {
|
||||
memcpy(&value, mac_bytes, sizeof(value));
|
||||
}
|
||||
if (value % 2000 == 99) {
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
return DROPBEAR_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* compare the hash */
|
||||
buf_setpos(ses.readbuf, contents_len);
|
||||
if (constant_time_memcmp(mac_bytes, buf_getptr(ses.readbuf, mac_size), mac_size) != 0) {
|
||||
@@ -578,7 +600,7 @@ void encrypt_packet() {
|
||||
/* Update counts */
|
||||
ses.kexstate.datatrans += writebuf->len;
|
||||
|
||||
writebuf_enqueue(writebuf, packet_type);
|
||||
writebuf_enqueue(writebuf);
|
||||
|
||||
/* Update counts */
|
||||
ses.transseq++;
|
||||
@@ -598,14 +620,11 @@ void encrypt_packet() {
|
||||
TRACE2(("leave encrypt_packet()"))
|
||||
}
|
||||
|
||||
void writebuf_enqueue(buffer * writebuf, unsigned char packet_type) {
|
||||
/* The last byte of the buffer stores the cleartext packet_type. It is not
|
||||
* transmitted but is used for transmit timeout purposes */
|
||||
buf_putbyte(writebuf, packet_type);
|
||||
void writebuf_enqueue(buffer * writebuf) {
|
||||
/* enqueue the packet for sending. It will get freed after transmission. */
|
||||
buf_setpos(writebuf, 0);
|
||||
enqueue(&ses.writequeue, (void*)writebuf);
|
||||
ses.writequeue_len += writebuf->len-1;
|
||||
ses.writequeue_len += writebuf->len;
|
||||
}
|
||||
|
||||
|
||||
|
||||
2
packet.h
2
packet.h
@@ -35,7 +35,7 @@ void read_packet(void);
|
||||
void decrypt_packet(void);
|
||||
void encrypt_packet(void);
|
||||
|
||||
void writebuf_enqueue(buffer * writebuf, unsigned char packet_type);
|
||||
void writebuf_enqueue(buffer * writebuf);
|
||||
|
||||
void process_packet(void);
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ rm "$RELDIR/.hgtags"
|
||||
(cd "$RELDIR/.." && tar cjf $ARCHIVE `basename "$RELDIR"`) || exit 2
|
||||
|
||||
ls -l $ARCHIVE
|
||||
openssl sha -sha256 $ARCHIVE
|
||||
openssl sha256 $ARCHIVE
|
||||
echo Done to
|
||||
echo "$ARCHIVE"
|
||||
echo Sign it with
|
||||
|
||||
@@ -136,7 +136,7 @@ typedef struct cli_runopts {
|
||||
|
||||
char *progname;
|
||||
char *remotehost;
|
||||
char *remoteport;
|
||||
const char *remoteport;
|
||||
|
||||
char *own_user;
|
||||
char *username;
|
||||
|
||||
@@ -102,7 +102,7 @@ xstrdup(const char *str)
|
||||
|
||||
len = strlen(str) + 1;
|
||||
cp = xmalloc(len);
|
||||
strncpy(cp, str, len);
|
||||
strlcpy(cp, str, len);
|
||||
return cp;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,8 @@ struct passwd *pwcopy(struct passwd *);
|
||||
typedef struct arglist arglist;
|
||||
struct arglist {
|
||||
char **list;
|
||||
int num;
|
||||
int nalloc;
|
||||
u_int num;
|
||||
u_int nalloc;
|
||||
};
|
||||
void addargs(arglist *, char *, ...);
|
||||
void replacearg(arglist *, u_int, char *, ...);
|
||||
|
||||
@@ -580,6 +580,10 @@ int cmp_base64_key(const unsigned char* keyblob, unsigned int keybloblen,
|
||||
|
||||
/* now we have the actual data */
|
||||
len = line->len - line->pos;
|
||||
if (len == 0) {
|
||||
/* base64_decode doesn't like NULL argument */
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
decodekeylen = len * 2; /* big to be safe */
|
||||
decodekey = buf_new(decodekeylen);
|
||||
|
||||
@@ -623,3 +627,8 @@ out:
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DROPBEAR_FUZZ
|
||||
const char * const * fuzz_signkey_names = signkey_names;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -151,6 +151,7 @@ void svr_agentcleanup(struct ChanSess * chansess) {
|
||||
|
||||
if (chansess->agentfile != NULL && chansess->agentdir != NULL) {
|
||||
|
||||
#if DROPBEAR_SVR_MULTIUSER
|
||||
/* Remove the dir as the user. That way they can't cause problems except
|
||||
* for themselves */
|
||||
uid = getuid();
|
||||
@@ -159,6 +160,7 @@ void svr_agentcleanup(struct ChanSess * chansess) {
|
||||
(seteuid(ses.authstate.pw_uid)) < 0) {
|
||||
dropbear_exit("Failed to set euid");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 2 for "/" and "\0" */
|
||||
len = strlen(chansess->agentdir) + strlen(chansess->agentfile) + 2;
|
||||
@@ -170,10 +172,12 @@ void svr_agentcleanup(struct ChanSess * chansess) {
|
||||
|
||||
rmdir(chansess->agentdir);
|
||||
|
||||
#if DROPBEAR_SVR_MULTIUSER
|
||||
if ((seteuid(uid)) < 0 ||
|
||||
(setegid(gid)) < 0) {
|
||||
dropbear_exit("Failed to revert euid");
|
||||
}
|
||||
#endif
|
||||
|
||||
m_free(chansess->agentfile);
|
||||
m_free(chansess->agentdir);
|
||||
@@ -187,6 +191,7 @@ static const struct ChanType chan_svr_agent = {
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -208,13 +213,14 @@ static int bindagent(int fd, struct ChanSess * chansess) {
|
||||
|
||||
struct sockaddr_un addr;
|
||||
unsigned int prefix;
|
||||
char path[sizeof(addr.sun_path)], sockfile[sizeof(addr.sun_path)];
|
||||
char path[(sizeof(addr.sun_path)-1)/2], sockfile[(sizeof(addr.sun_path)-1)/2];
|
||||
mode_t mode;
|
||||
int i;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
int ret = DROPBEAR_FAILURE;
|
||||
|
||||
#if DROPBEAR_SVR_MULTIUSER
|
||||
/* drop to user privs to make the dir/file */
|
||||
uid = getuid();
|
||||
gid = getgid();
|
||||
@@ -222,6 +228,7 @@ static int bindagent(int fd, struct ChanSess * chansess) {
|
||||
(seteuid(ses.authstate.pw_uid)) < 0) {
|
||||
dropbear_exit("Failed to set euid");
|
||||
}
|
||||
#endif
|
||||
|
||||
memset((void*)&addr, 0x0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
@@ -261,10 +268,12 @@ bindsocket:
|
||||
|
||||
|
||||
out:
|
||||
#if DROPBEAR_SVR_MULTIUSER
|
||||
if ((seteuid(uid)) < 0 ||
|
||||
(setegid(gid)) < 0) {
|
||||
dropbear_exit("Failed to revert euid");
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
73
svr-auth.c
73
svr-auth.c
@@ -79,6 +79,9 @@ void recv_msg_userauth_request() {
|
||||
|
||||
TRACE(("enter recv_msg_userauth_request"))
|
||||
|
||||
/* for compensating failure delay */
|
||||
gettime_wrapper(&ses.authstate.auth_starttime);
|
||||
|
||||
/* ignore packets if auth is already done */
|
||||
if (ses.authstate.authdone == 1) {
|
||||
TRACE(("leave recv_msg_userauth_request: authdone already"))
|
||||
@@ -149,10 +152,8 @@ void recv_msg_userauth_request() {
|
||||
if (methodlen == AUTH_METHOD_PASSWORD_LEN &&
|
||||
strncmp(methodname, AUTH_METHOD_PASSWORD,
|
||||
AUTH_METHOD_PASSWORD_LEN) == 0) {
|
||||
if (valid_user) {
|
||||
svr_auth_password();
|
||||
goto out;
|
||||
}
|
||||
svr_auth_password(valid_user);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -164,10 +165,8 @@ void recv_msg_userauth_request() {
|
||||
if (methodlen == AUTH_METHOD_PASSWORD_LEN &&
|
||||
strncmp(methodname, AUTH_METHOD_PASSWORD,
|
||||
AUTH_METHOD_PASSWORD_LEN) == 0) {
|
||||
if (valid_user) {
|
||||
svr_auth_pam();
|
||||
goto out;
|
||||
}
|
||||
svr_auth_pam(valid_user);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -177,12 +176,7 @@ void recv_msg_userauth_request() {
|
||||
if (methodlen == AUTH_METHOD_PUBKEY_LEN &&
|
||||
strncmp(methodname, AUTH_METHOD_PUBKEY,
|
||||
AUTH_METHOD_PUBKEY_LEN) == 0) {
|
||||
if (valid_user) {
|
||||
svr_auth_pubkey();
|
||||
} else {
|
||||
/* pubkey has no failure delay */
|
||||
send_msg_userauth_failure(0, 0);
|
||||
}
|
||||
svr_auth_pubkey(valid_user);
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
@@ -282,7 +276,7 @@ static int checkusername(const char *username, unsigned int userlen) {
|
||||
|
||||
/* check if we are running as non-root, and login user is different from the server */
|
||||
uid = geteuid();
|
||||
if (uid != 0 && uid != ses.authstate.pw_uid) {
|
||||
if (!(DROPBEAR_SVR_MULTIUSER && uid == 0) && uid != ses.authstate.pw_uid) {
|
||||
TRACE(("running as nonroot, only server uid is allowed"))
|
||||
dropbear_log(LOG_WARNING,
|
||||
"Login attempt with wrong user %s from %s",
|
||||
@@ -312,7 +306,7 @@ static int checkusername(const char *username, unsigned int userlen) {
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
}
|
||||
#endif HAVE_GETGROUPLIST
|
||||
#endif /* HAVE_GETGROUPLIST */
|
||||
|
||||
TRACE(("shell is %s", ses.authstate.pw_shell))
|
||||
|
||||
@@ -391,11 +385,48 @@ void send_msg_userauth_failure(int partial, int incrfail) {
|
||||
encrypt_packet();
|
||||
|
||||
if (incrfail) {
|
||||
unsigned int delay;
|
||||
genrandom((unsigned char*)&delay, sizeof(delay));
|
||||
/* We delay for 300ms +- 50ms */
|
||||
delay = 250000 + (delay % 100000);
|
||||
usleep(delay);
|
||||
/* The SSH_MSG_AUTH_FAILURE response is delayed to attempt to
|
||||
avoid user enumeration and slow brute force attempts.
|
||||
The delay is adjusted by the time already spent in processing
|
||||
authentication (ses.authstate.auth_starttime timestamp). */
|
||||
|
||||
/* Desired total delay 300ms +-50ms (in nanoseconds).
|
||||
Beware of integer overflow if increasing these values */
|
||||
const unsigned int mindelay = 250000000;
|
||||
const unsigned int vardelay = 100000000;
|
||||
unsigned int rand_delay;
|
||||
struct timespec delay;
|
||||
|
||||
gettime_wrapper(&delay);
|
||||
delay.tv_sec -= ses.authstate.auth_starttime.tv_sec;
|
||||
delay.tv_nsec -= ses.authstate.auth_starttime.tv_nsec;
|
||||
|
||||
/* carry */
|
||||
if (delay.tv_nsec < 0) {
|
||||
delay.tv_nsec += 1000000000;
|
||||
delay.tv_sec -= 1;
|
||||
}
|
||||
|
||||
genrandom((unsigned char*)&rand_delay, sizeof(rand_delay));
|
||||
rand_delay = mindelay + (rand_delay % vardelay);
|
||||
|
||||
if (delay.tv_sec == 0 && delay.tv_nsec <= mindelay) {
|
||||
/* Compensate for elapsed time */
|
||||
delay.tv_nsec = rand_delay - delay.tv_nsec;
|
||||
} else {
|
||||
/* No time left or time went backwards, just delay anyway */
|
||||
delay.tv_sec = 0;
|
||||
delay.tv_nsec = rand_delay;
|
||||
}
|
||||
|
||||
|
||||
#if DROPBEAR_FUZZ
|
||||
if (!fuzz.fuzzing)
|
||||
#endif
|
||||
{
|
||||
while (nanosleep(&delay, &delay) == -1 && errno == EINTR) { /* Go back to sleep */ }
|
||||
}
|
||||
|
||||
ses.authstate.failcount++;
|
||||
}
|
||||
|
||||
|
||||
@@ -178,13 +178,14 @@ pamConvFunc(int num_msg,
|
||||
* Keyboard interactive would be a lot nicer, but since PAM is synchronous, it
|
||||
* gets very messy trying to send the interactive challenges, and read the
|
||||
* interactive responses, over the network. */
|
||||
void svr_auth_pam() {
|
||||
void svr_auth_pam(int valid_user) {
|
||||
|
||||
struct UserDataS userData = {NULL, NULL};
|
||||
struct pam_conv pamConv = {
|
||||
pamConvFunc,
|
||||
&userData /* submitted to pamvConvFunc as appdata_ptr */
|
||||
};
|
||||
const char* printable_user = NULL;
|
||||
|
||||
pam_handle_t* pamHandlep = NULL;
|
||||
|
||||
@@ -204,12 +205,23 @@ void svr_auth_pam() {
|
||||
|
||||
password = buf_getstring(ses.payload, &passwordlen);
|
||||
|
||||
/* We run the PAM conversation regardless of whether the username is valid
|
||||
in case the conversation function has an inherent delay.
|
||||
Use ses.authstate.username rather than ses.authstate.pw_name.
|
||||
After PAM succeeds we then check the valid_user flag too */
|
||||
|
||||
/* used to pass data to the PAM conversation function - don't bother with
|
||||
* strdup() etc since these are touched only by our own conversation
|
||||
* function (above) which takes care of it */
|
||||
userData.user = ses.authstate.pw_name;
|
||||
userData.user = ses.authstate.username;
|
||||
userData.passwd = password;
|
||||
|
||||
if (ses.authstate.pw_name) {
|
||||
printable_user = ses.authstate.pw_name;
|
||||
} else {
|
||||
printable_user = "<invalid username>";
|
||||
}
|
||||
|
||||
/* Init pam */
|
||||
if ((rc = pam_start("sshd", NULL, &pamConv, &pamHandlep)) != PAM_SUCCESS) {
|
||||
dropbear_log(LOG_WARNING, "pam_start() failed, rc=%d, %s",
|
||||
@@ -242,7 +254,7 @@ void svr_auth_pam() {
|
||||
rc, pam_strerror(pamHandlep, rc));
|
||||
dropbear_log(LOG_WARNING,
|
||||
"Bad PAM password attempt for '%s' from %s",
|
||||
ses.authstate.pw_name,
|
||||
printable_user,
|
||||
svr_ses.addrstring);
|
||||
send_msg_userauth_failure(0, 1);
|
||||
goto cleanup;
|
||||
@@ -253,12 +265,19 @@ void svr_auth_pam() {
|
||||
rc, pam_strerror(pamHandlep, rc));
|
||||
dropbear_log(LOG_WARNING,
|
||||
"Bad PAM password attempt for '%s' from %s",
|
||||
ses.authstate.pw_name,
|
||||
printable_user,
|
||||
svr_ses.addrstring);
|
||||
send_msg_userauth_failure(0, 1);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!valid_user) {
|
||||
/* PAM auth succeeded but the username isn't allowed in for another reason
|
||||
(checkusername() failed) */
|
||||
send_msg_userauth_failure(0, 1);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* successful authentication */
|
||||
dropbear_log(LOG_NOTICE, "PAM password auth succeeded for '%s' from %s",
|
||||
ses.authstate.pw_name,
|
||||
|
||||
@@ -48,22 +48,14 @@ static int constant_time_strcmp(const char* a, const char* b) {
|
||||
|
||||
/* Process a password auth request, sending success or failure messages as
|
||||
* appropriate */
|
||||
void svr_auth_password() {
|
||||
void svr_auth_password(int valid_user) {
|
||||
|
||||
char * passwdcrypt = NULL; /* the crypt from /etc/passwd or /etc/shadow */
|
||||
char * testcrypt = NULL; /* crypt generated from the user's password sent */
|
||||
char * password;
|
||||
char * password = NULL;
|
||||
unsigned int passwordlen;
|
||||
|
||||
unsigned int changepw;
|
||||
|
||||
passwdcrypt = ses.authstate.pw_passwd;
|
||||
|
||||
#ifdef DEBUG_HACKCRYPT
|
||||
/* debugging crypt for non-root testing with shadows */
|
||||
passwdcrypt = DEBUG_HACKCRYPT;
|
||||
#endif
|
||||
|
||||
/* check if client wants to change password */
|
||||
changepw = buf_getbool(ses.payload);
|
||||
if (changepw) {
|
||||
@@ -73,12 +65,30 @@ void svr_auth_password() {
|
||||
}
|
||||
|
||||
password = buf_getstring(ses.payload, &passwordlen);
|
||||
|
||||
/* the first bytes of passwdcrypt are the salt */
|
||||
testcrypt = crypt(password, passwdcrypt);
|
||||
if (valid_user && passwordlen <= DROPBEAR_MAX_PASSWORD_LEN) {
|
||||
/* the first bytes of passwdcrypt are the salt */
|
||||
passwdcrypt = ses.authstate.pw_passwd;
|
||||
testcrypt = crypt(password, passwdcrypt);
|
||||
}
|
||||
m_burn(password, passwordlen);
|
||||
m_free(password);
|
||||
|
||||
/* After we have got the payload contents we can exit if the username
|
||||
is invalid. Invalid users have already been logged. */
|
||||
if (!valid_user) {
|
||||
send_msg_userauth_failure(0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (passwordlen > DROPBEAR_MAX_PASSWORD_LEN) {
|
||||
dropbear_log(LOG_WARNING,
|
||||
"Too-long password attempt for '%s' from %s",
|
||||
ses.authstate.pw_name,
|
||||
svr_ses.addrstring);
|
||||
send_msg_userauth_failure(0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (testcrypt == NULL) {
|
||||
/* crypt() with an invalid salt like "!!" */
|
||||
dropbear_log(LOG_WARNING, "User account '%s' is locked",
|
||||
|
||||
@@ -79,7 +79,7 @@ static int checkfileperm(char * filename);
|
||||
|
||||
/* process a pubkey auth request, sending success or failure message as
|
||||
* appropriate */
|
||||
void svr_auth_pubkey() {
|
||||
void svr_auth_pubkey(int valid_user) {
|
||||
|
||||
unsigned char testkey; /* whether we're just checking if a key is usable */
|
||||
char* algo = NULL; /* pubkey algo */
|
||||
@@ -102,6 +102,15 @@ void svr_auth_pubkey() {
|
||||
keybloblen = buf_getint(ses.payload);
|
||||
keyblob = buf_getptr(ses.payload, keybloblen);
|
||||
|
||||
if (!valid_user) {
|
||||
/* Return failure once we have read the contents of the packet
|
||||
required to validate a public key.
|
||||
Avoids blind user enumeration though it isn't possible to prevent
|
||||
testing for user existence if the public key is known */
|
||||
send_msg_userauth_failure(0, 0);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* check if the key is valid */
|
||||
if (checkpubkey(algo, algolen, keyblob, keybloblen) == DROPBEAR_FAILURE) {
|
||||
send_msg_userauth_failure(0, 0);
|
||||
@@ -167,6 +176,10 @@ out:
|
||||
sign_key_free(key);
|
||||
key = NULL;
|
||||
}
|
||||
/* Retain pubkey options only if auth succeeded */
|
||||
if (!ses.authstate.authdone) {
|
||||
svr_pubkey_options_cleanup();
|
||||
}
|
||||
TRACE(("leave pubkeyauth"))
|
||||
}
|
||||
|
||||
@@ -197,7 +210,12 @@ static int checkpubkey_line(buffer* line, int line_num, const char* filename,
|
||||
|
||||
if (line->len < MIN_AUTHKEYS_LINE || line->len > MAX_AUTHKEYS_LINE) {
|
||||
TRACE(("checkpubkey_line: bad line length %d", line->len))
|
||||
return DROPBEAR_FAILURE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (memchr(line->data, 0x0, line->len) != NULL) {
|
||||
TRACE(("checkpubkey_line: bad line has null char"))
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* compare the algorithm. +3 so we have enough bytes to read a space and some base64 characters too. */
|
||||
@@ -329,6 +347,7 @@ static int checkpubkey(const char* algo, unsigned int algolen,
|
||||
snprintf(filename, len + 22, "%s/.ssh/authorized_keys",
|
||||
ses.authstate.pw_dir);
|
||||
|
||||
#if DROPBEAR_SVR_MULTIUSER
|
||||
/* open the file as the authenticating user. */
|
||||
origuid = getuid();
|
||||
origgid = getgid();
|
||||
@@ -336,13 +355,16 @@ static int checkpubkey(const char* algo, unsigned int algolen,
|
||||
(seteuid(ses.authstate.pw_uid)) < 0) {
|
||||
dropbear_exit("Failed to set euid");
|
||||
}
|
||||
#endif
|
||||
|
||||
authfile = fopen(filename, "r");
|
||||
|
||||
#if DROPBEAR_SVR_MULTIUSER
|
||||
if ((seteuid(origuid)) < 0 ||
|
||||
(setegid(origgid)) < 0) {
|
||||
dropbear_exit("Failed to revert euid");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (authfile == NULL) {
|
||||
goto out;
|
||||
@@ -406,8 +428,9 @@ static int checkpubkeyperms() {
|
||||
|
||||
/* allocate max required pathname storage,
|
||||
* = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */
|
||||
filename = m_malloc(len + 22);
|
||||
strncpy(filename, ses.authstate.pw_dir, len+1);
|
||||
len += 22;
|
||||
filename = m_malloc(len);
|
||||
strlcpy(filename, ses.authstate.pw_dir, len);
|
||||
|
||||
/* check ~ */
|
||||
if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
|
||||
@@ -415,13 +438,13 @@ static int checkpubkeyperms() {
|
||||
}
|
||||
|
||||
/* check ~/.ssh */
|
||||
strncat(filename, "/.ssh", 5); /* strlen("/.ssh") == 5 */
|
||||
strlcat(filename, "/.ssh", len);
|
||||
if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* now check ~/.ssh/authorized_keys */
|
||||
strncat(filename, "/authorized_keys", 16);
|
||||
strlcat(filename, "/authorized_keys", len);
|
||||
if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
|
||||
goto out;
|
||||
}
|
||||
@@ -473,4 +496,12 @@ static int checkfileperm(char * filename) {
|
||||
return DROPBEAR_SUCCESS;
|
||||
}
|
||||
|
||||
#if DROPBEAR_FUZZ
|
||||
int fuzz_checkpubkey_line(buffer* line, int line_num, char* filename,
|
||||
const char* algo, unsigned int algolen,
|
||||
const unsigned char* keyblob, unsigned int keybloblen) {
|
||||
return checkpubkey_line(line, line_num, filename, algo, algolen, keyblob, keybloblen);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -113,7 +113,6 @@ void svr_pubkey_options_cleanup() {
|
||||
m_free(ses.authstate.pubkey_options->forced_command);
|
||||
}
|
||||
m_free(ses.authstate.pubkey_options);
|
||||
ses.authstate.pubkey_options = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,6 +168,12 @@ int svr_add_pubkey_options(buffer *options_buf, int line_num, const char* filena
|
||||
if (match_option(options_buf, "command=\"") == DROPBEAR_SUCCESS) {
|
||||
int escaped = 0;
|
||||
const unsigned char* command_start = buf_getptr(options_buf, 0);
|
||||
|
||||
if (ses.authstate.pubkey_options->forced_command) {
|
||||
/* multiple command= options */
|
||||
goto bad_option;
|
||||
}
|
||||
|
||||
while (options_buf->pos < options_buf->len) {
|
||||
const char c = buf_getbyte(options_buf);
|
||||
if (!escaped && c == '"') {
|
||||
|
||||
@@ -51,6 +51,7 @@ static void execchild(const void *user_data_chansess);
|
||||
static void addchildpid(struct ChanSess *chansess, pid_t pid);
|
||||
static void sesssigchild_handler(int val);
|
||||
static void closechansess(const struct Channel *channel);
|
||||
static void cleanupchansess(const struct Channel *channel);
|
||||
static int newchansess(struct Channel *channel);
|
||||
static void chansessionrequest(struct Channel *channel);
|
||||
static int sesscheckclose(const struct Channel *channel);
|
||||
@@ -69,6 +70,7 @@ const struct ChanType svrchansess = {
|
||||
sesscheckclose, /* checkclosehandler */
|
||||
chansessionrequest, /* reqhandler */
|
||||
closechansess, /* closehandler */
|
||||
cleanupchansess /* cleanup */
|
||||
};
|
||||
|
||||
/* required to clear environment */
|
||||
@@ -91,7 +93,7 @@ void svr_chansess_checksignal(void) {
|
||||
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
|
||||
unsigned int i;
|
||||
struct exitinfo *ex = NULL;
|
||||
TRACE(("sigchld handler: pid %d", pid))
|
||||
TRACE(("svr_chansess_checksignal : pid %d", pid))
|
||||
|
||||
ex = NULL;
|
||||
/* find the corresponding chansess */
|
||||
@@ -285,8 +287,25 @@ chansess_login_alloc(const struct ChanSess *chansess) {
|
||||
return li;
|
||||
}
|
||||
|
||||
/* clean a session channel */
|
||||
/* send exit status message before the channel is closed */
|
||||
static void closechansess(const struct Channel *channel) {
|
||||
struct ChanSess *chansess;
|
||||
|
||||
TRACE(("enter closechansess"))
|
||||
|
||||
chansess = (struct ChanSess*)channel->typedata;
|
||||
|
||||
if (chansess == NULL) {
|
||||
TRACE(("leave closechansess: chansess == NULL"))
|
||||
return;
|
||||
}
|
||||
|
||||
send_exitsignalstatus(channel);
|
||||
TRACE(("leave closechansess"))
|
||||
}
|
||||
|
||||
/* clean a session channel */
|
||||
static void cleanupchansess(const struct Channel *channel) {
|
||||
|
||||
struct ChanSess *chansess;
|
||||
unsigned int i;
|
||||
@@ -301,8 +320,6 @@ static void closechansess(const struct Channel *channel) {
|
||||
return;
|
||||
}
|
||||
|
||||
send_exitsignalstatus(channel);
|
||||
|
||||
m_free(chansess->cmd);
|
||||
m_free(chansess->term);
|
||||
|
||||
@@ -932,6 +949,7 @@ static void execchild(const void *user_data) {
|
||||
#endif /* HAVE_CLEARENV */
|
||||
#endif /* DEBUG_VALGRIND */
|
||||
|
||||
#if DROPBEAR_SVR_MULTIUSER
|
||||
/* We can only change uid/gid as root ... */
|
||||
if (getuid() == 0) {
|
||||
|
||||
@@ -955,6 +973,7 @@ static void execchild(const void *user_data) {
|
||||
dropbear_exit("Couldn't change user as non-root");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* set env vars */
|
||||
addnewvar("USER", ses.authstate.pw_name);
|
||||
|
||||
@@ -179,6 +179,13 @@ static void send_msg_kexdh_reply(mp_int *dh_e, buffer *ecdh_qs) {
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DROPBEAR_FUZZ
|
||||
if (fuzz.fuzzing && fuzz.skip_kexmaths) {
|
||||
fuzz_fake_send_kexdh_reply();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_REPLY);
|
||||
buf_put_pub_key(ses.writepayload, svr_opts.hostkey,
|
||||
ses.newkeys->algo_hostkey);
|
||||
|
||||
@@ -178,7 +178,7 @@ static void main_noinetd() {
|
||||
/* incoming connection select loop */
|
||||
for(;;) {
|
||||
|
||||
FD_ZERO(&fds);
|
||||
DROPBEAR_FD_ZERO(&fds);
|
||||
|
||||
/* listening sockets */
|
||||
for (i = 0; i < listensockcount; i++) {
|
||||
|
||||
@@ -523,10 +523,13 @@ static void addhostkey(const char *keyfile) {
|
||||
svr_opts.num_hostkey_files++;
|
||||
}
|
||||
|
||||
|
||||
void load_all_hostkeys() {
|
||||
int i;
|
||||
int disable_unset_keys = 1;
|
||||
int any_keys = 0;
|
||||
#ifdef DROPBEAR_ECDSA
|
||||
int loaded_any_ecdsa = 0;
|
||||
#endif
|
||||
|
||||
svr_opts.hostkey = new_sign_key();
|
||||
|
||||
@@ -551,14 +554,8 @@ void load_all_hostkeys() {
|
||||
#endif
|
||||
}
|
||||
|
||||
#if DROPBEAR_DELAY_HOSTKEY
|
||||
if (svr_opts.delay_hostkey) {
|
||||
disable_unset_keys = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DROPBEAR_RSA
|
||||
if (disable_unset_keys && !svr_opts.hostkey->rsakey) {
|
||||
if (!svr_opts.delay_hostkey && !svr_opts.hostkey->rsakey) {
|
||||
disablekey(DROPBEAR_SIGNKEY_RSA);
|
||||
} else {
|
||||
any_keys = 1;
|
||||
@@ -566,39 +563,54 @@ void load_all_hostkeys() {
|
||||
#endif
|
||||
|
||||
#if DROPBEAR_DSS
|
||||
if (disable_unset_keys && !svr_opts.hostkey->dsskey) {
|
||||
if (!svr_opts.delay_hostkey && !svr_opts.hostkey->dsskey) {
|
||||
disablekey(DROPBEAR_SIGNKEY_DSS);
|
||||
} else {
|
||||
any_keys = 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if DROPBEAR_ECDSA
|
||||
/* We want to advertise a single ecdsa algorithm size.
|
||||
- If there is a ecdsa hostkey at startup we choose that that size.
|
||||
- If we generate at runtime we choose the default ecdsa size.
|
||||
- Otherwise no ecdsa keys will be advertised */
|
||||
|
||||
/* check if any keys were loaded at startup */
|
||||
loaded_any_ecdsa =
|
||||
0
|
||||
#if DROPBEAR_ECC_256
|
||||
if ((disable_unset_keys || ECDSA_DEFAULT_SIZE != 256)
|
||||
&& !svr_opts.hostkey->ecckey256) {
|
||||
disablekey(DROPBEAR_SIGNKEY_ECDSA_NISTP256);
|
||||
} else {
|
||||
any_keys = 1;
|
||||
}
|
||||
|| svr_opts.hostkey->ecckey256
|
||||
#endif
|
||||
|
||||
#if DROPBEAR_ECC_384
|
||||
if ((disable_unset_keys || ECDSA_DEFAULT_SIZE != 384)
|
||||
&& !svr_opts.hostkey->ecckey384) {
|
||||
disablekey(DROPBEAR_SIGNKEY_ECDSA_NISTP384);
|
||||
} else {
|
||||
any_keys = 1;
|
||||
|| svr_opts.hostkey->ecckey384
|
||||
#endif
|
||||
#if DROPBEAR_ECC_521
|
||||
|| svr_opts.hostkey->ecckey521
|
||||
#endif
|
||||
;
|
||||
any_keys |= loaded_any_ecdsa;
|
||||
|
||||
/* Or an ecdsa key could be generated at runtime */
|
||||
any_keys |= svr_opts.delay_hostkey;
|
||||
|
||||
/* At most one ecdsa key size will be left enabled */
|
||||
#if DROPBEAR_ECC_256
|
||||
if (!svr_opts.hostkey->ecckey256
|
||||
&& (!svr_opts.delay_hostkey || loaded_any_ecdsa || ECDSA_DEFAULT_SIZE != 256 )) {
|
||||
disablekey(DROPBEAR_SIGNKEY_ECDSA_NISTP256);
|
||||
}
|
||||
#endif
|
||||
#if DROPBEAR_ECC_384
|
||||
if (!svr_opts.hostkey->ecckey384
|
||||
&& (!svr_opts.delay_hostkey || loaded_any_ecdsa || ECDSA_DEFAULT_SIZE != 384 )) {
|
||||
disablekey(DROPBEAR_SIGNKEY_ECDSA_NISTP384);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if DROPBEAR_ECC_521
|
||||
if ((disable_unset_keys || ECDSA_DEFAULT_SIZE != 521)
|
||||
&& !svr_opts.hostkey->ecckey521) {
|
||||
if (!svr_opts.hostkey->ecckey521
|
||||
&& (!svr_opts.delay_hostkey || loaded_any_ecdsa || ECDSA_DEFAULT_SIZE != 521 )) {
|
||||
disablekey(DROPBEAR_SIGNKEY_ECDSA_NISTP521);
|
||||
} else {
|
||||
any_keys = 1;
|
||||
}
|
||||
#endif
|
||||
#endif /* DROPBEAR_ECDSA */
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "auth.h"
|
||||
#include "runopts.h"
|
||||
#include "crypto_desc.h"
|
||||
#include "fuzz.h"
|
||||
|
||||
static void svr_remoteclosed(void);
|
||||
static void svr_algos_initialise(void);
|
||||
@@ -184,6 +185,13 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) {
|
||||
session_cleanup();
|
||||
}
|
||||
|
||||
#if DROPBEAR_FUZZ
|
||||
/* longjmp before cleaning up svr_opts */
|
||||
if (fuzz.do_jmp) {
|
||||
longjmp(fuzz.jmp, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (svr_opts.hostkey) {
|
||||
sign_key_free(svr_opts.hostkey);
|
||||
svr_opts.hostkey = NULL;
|
||||
@@ -193,6 +201,7 @@ void svr_dropbear_exit(int exitcode, const char* format, va_list param) {
|
||||
m_free(svr_opts.ports[i]);
|
||||
}
|
||||
|
||||
|
||||
exit(exitcode);
|
||||
|
||||
}
|
||||
@@ -238,7 +247,9 @@ void svr_dropbear_log(int priority, const char* format, va_list param) {
|
||||
static void svr_remoteclosed() {
|
||||
|
||||
m_close(ses.sock_in);
|
||||
m_close(ses.sock_out);
|
||||
if (ses.sock_in != ses.sock_out) {
|
||||
m_close(ses.sock_out);
|
||||
}
|
||||
ses.sock_in = -1;
|
||||
ses.sock_out = -1;
|
||||
dropbear_close("Exited normally");
|
||||
|
||||
@@ -57,6 +57,7 @@ static const struct ChanType svr_chan_tcpremote = {
|
||||
tcp_prio_inithandler,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
@@ -168,6 +169,7 @@ static int svr_remotetcpreq(int *allocated_listen_port) {
|
||||
unsigned int addrlen;
|
||||
struct TCPListener *tcpinfo = NULL;
|
||||
unsigned int port;
|
||||
struct Listener *listener = NULL;
|
||||
|
||||
TRACE(("enter remotetcpreq"))
|
||||
|
||||
@@ -208,9 +210,9 @@ static int svr_remotetcpreq(int *allocated_listen_port) {
|
||||
tcpinfo->listenaddr = m_strdup(request_addr);
|
||||
}
|
||||
|
||||
ret = listen_tcpfwd(tcpinfo);
|
||||
ret = listen_tcpfwd(tcpinfo, &listener);
|
||||
if (DROPBEAR_SUCCESS == ret) {
|
||||
tcpinfo->listenport = get_sock_port(ses.listeners[0]->socks[0]);
|
||||
tcpinfo->listenport = get_sock_port(listener->socks[0]);
|
||||
*allocated_listen_port = tcpinfo->listenport;
|
||||
}
|
||||
|
||||
@@ -237,7 +239,8 @@ const struct ChanType svr_chan_tcpdirect = {
|
||||
newtcpdirect, /* init */
|
||||
NULL, /* checkclose */
|
||||
NULL, /* reqhandler */
|
||||
NULL /* closehandler */
|
||||
NULL, /* closehandler */
|
||||
NULL /* cleanup */
|
||||
};
|
||||
|
||||
/* Called upon creating a new direct tcp channel (ie we connect out to an
|
||||
|
||||
@@ -216,7 +216,8 @@ static const struct ChanType chan_x11 = {
|
||||
x11_inithandler, /* inithandler */
|
||||
NULL, /* checkclose */
|
||||
NULL, /* reqhandler */
|
||||
NULL /* closehandler */
|
||||
NULL, /* closehandler */
|
||||
NULL /* cleanup */
|
||||
};
|
||||
|
||||
|
||||
|
||||
19
sysoptions.h
19
sysoptions.h
@@ -4,7 +4,7 @@
|
||||
*******************************************************************/
|
||||
|
||||
#ifndef DROPBEAR_VERSION
|
||||
#define DROPBEAR_VERSION "2018.76"
|
||||
#define DROPBEAR_VERSION "2019.78"
|
||||
#endif
|
||||
|
||||
#define LOCAL_IDENT "SSH-2.0-dropbear_" DROPBEAR_VERSION
|
||||
@@ -86,6 +86,8 @@
|
||||
/* Required for pubkey auth */
|
||||
#define DROPBEAR_SIGNKEY_VERIFY ((DROPBEAR_SVR_PUBKEY_AUTH) || (DROPBEAR_CLIENT))
|
||||
|
||||
#define DROPBEAR_MAX_PASSWORD_LEN 100
|
||||
|
||||
#define SHA1_HASH_SIZE 20
|
||||
#define MD5_HASH_SIZE 16
|
||||
#define MAX_HASH_SIZE 64 /* sha512 */
|
||||
@@ -225,7 +227,7 @@ If you test it please contact the Dropbear author */
|
||||
#define DROPBEAR_ZLIB_MEM_LEVEL 8
|
||||
|
||||
#if (DROPBEAR_SVR_PASSWORD_AUTH) && (DROPBEAR_SVR_PAM_AUTH)
|
||||
#error "You can't turn on PASSWORD and PAM auth both at once. Fix it in options.h"
|
||||
#error "You can't turn on PASSWORD and PAM auth both at once. Fix it in localoptions.h"
|
||||
#endif
|
||||
|
||||
/* PAM requires ./configure --enable-pam */
|
||||
@@ -316,4 +318,17 @@ If you test it please contact the Dropbear author */
|
||||
#define DROPBEAR_CLIENT_TCP_FAST_OPEN 0
|
||||
#endif
|
||||
|
||||
#define DROPBEAR_TRACKING_MALLOC (DROPBEAR_FUZZ)
|
||||
|
||||
/* Used to work around Memory Sanitizer false positives */
|
||||
#if defined(__has_feature)
|
||||
# if __has_feature(memory_sanitizer)
|
||||
# define DROPBEAR_MSAN 1
|
||||
# endif
|
||||
#endif
|
||||
#ifndef DROPBEAR_MSAN
|
||||
#define DROPBEAR_MSAN 0
|
||||
#endif
|
||||
|
||||
|
||||
/* no include guard for this file */
|
||||
|
||||
@@ -110,12 +110,12 @@ static void tcp_acceptor(const struct Listener *listener, int sock) {
|
||||
}
|
||||
}
|
||||
|
||||
int listen_tcpfwd(struct TCPListener* tcpinfo) {
|
||||
int listen_tcpfwd(struct TCPListener* tcpinfo, struct Listener **ret_listener) {
|
||||
|
||||
char portstring[NI_MAXSERV];
|
||||
int socks[DROPBEAR_MAX_SOCKS];
|
||||
struct Listener *listener = NULL;
|
||||
int nsocks;
|
||||
struct Listener *listener;
|
||||
char* errstring = NULL;
|
||||
|
||||
TRACE(("enter listen_tcpfwd"))
|
||||
@@ -142,6 +142,10 @@ int listen_tcpfwd(struct TCPListener* tcpinfo) {
|
||||
return DROPBEAR_FAILURE;
|
||||
}
|
||||
|
||||
if (ret_listener) {
|
||||
*ret_listener = listener;
|
||||
}
|
||||
|
||||
TRACE(("leave listen_tcpfwd: success"))
|
||||
return DROPBEAR_SUCCESS;
|
||||
}
|
||||
|
||||
3
tcpfwd.h
3
tcpfwd.h
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "channel.h"
|
||||
#include "list.h"
|
||||
#include "listener.h"
|
||||
|
||||
struct TCPListener {
|
||||
|
||||
@@ -69,7 +70,7 @@ void cli_recv_msg_request_success(void);
|
||||
void cli_recv_msg_request_failure(void);
|
||||
|
||||
/* Common */
|
||||
int listen_tcpfwd(struct TCPListener* tcpinfo);
|
||||
int listen_tcpfwd(struct TCPListener* tcpinfo, struct Listener **ret_listener);
|
||||
int tcp_prio_inithandler(struct Channel* chan);
|
||||
|
||||
/* A random identifier */
|
||||
|
||||
Reference in New Issue
Block a user