diff --git a/crypto/openssh/ChangeLog b/crypto/openssh/ChangeLog index d48aba33cfcf..48f648d78fe3 100644 --- a/crypto/openssh/ChangeLog +++ b/crypto/openssh/ChangeLog @@ -1,10266 +1,9394 @@ -commit 4a354fc231174901f2629437c2a6e924a2dd6772 +commit d38f05dbdd291212bc95ea80648b72b7177e9f4e +Author: Darren Tucker +Date: Mon Mar 20 13:38:27 2017 +1100 + + Add llabs() implementation. + +commit 72536316a219b7394996a74691a5d4ec197480f7 Author: Damien Miller -Date: Mon Dec 19 15:59:26 2016 +1100 +Date: Mon Mar 20 12:23:04 2017 +1100 - crank version numbers for release + crank version numbers -commit 5f8d0bb8413d4d909cc7aa3c616fb0538224c3c9 +commit 3be52bc36bdfd24ded7e0f46999e7db520fb4e3f Author: djm@openbsd.org -Date: Mon Dec 19 04:55:51 2016 +0000 +Date: Mon Mar 20 01:18:59 2017 +0000 upstream commit - openssh-7.4 + openssh-7.5 - Upstream-ID: 1ee404adba6bbe10ae9277cbae3a94abe2867b79 + Upstream-ID: b8b9a4a949427c393cd868215e1724ceb3467ee5 -commit 3a8213ea0ed843523e34e55ab9c852332bab4c7b -Author: djm@openbsd.org -Date: Mon Dec 19 04:55:18 2016 +0000 +commit db84e52fe9cfad57f22e7e23c5fbf00092385129 +Author: Damien Miller +Date: Mon Mar 20 12:07:20 2017 +1100 - upstream commit + I'm a doofus. - remove testcase that depends on exact output and - behaviour of snprintf(..., "%s", NULL) + Unbreak obvious syntax error. + +commit 89f04852db27643717c9c3a2b0dde97ae50099ee +Author: Damien Miller +Date: Mon Mar 20 11:53:34 2017 +1100 + + on Cygwin, check paths from server for backslashes - Upstream-Regress-ID: cab4288531766bd9593cb556613b91a2eeefb56f + Pointed out by Jann Horn of Google Project Zero -commit eae735a82d759054f6ec7b4e887fb7a5692c66d7 -Author: dtucker@openbsd.org -Date: Mon Dec 19 03:32:57 2016 +0000 +commit 7ef1f9bafc2cc8d97ff2fbd4f280002b6e8ea5d9 +Author: Damien Miller +Date: Mon Mar 20 11:48:34 2017 +1100 - upstream commit + Yet another synonym for ASCII: "646" - Use LOGNAME to get current user and fall back to whoami if - not set. Mainly to benefit -portable since some platforms don't have whoami. + Used by NetBSD; this unbreaks mprintf() and friends there for the C + locale (caught by dtucker@ and his menagerie of test systems). + +commit 9165abfea3f68a0c684a6ed2e575e59bc31a3a6b +Author: Damien Miller +Date: Mon Mar 20 09:58:34 2017 +1100 + + create test mux socket in /tmp - Upstream-Regress-ID: e3a16b7836a3ae24dc8f8a4e43fdf8127a60bdfa + Creating the socket in $OBJ could blow past the (quite limited) + path limit for Unix domain sockets. As a bandaid for bz#2660, + reported by Colin Watson; ok dtucker@ -commit 0d2f88428487518eea60602bd593989013831dcf -Author: dtucker@openbsd.org -Date: Fri Dec 16 03:51:19 2016 +0000 +commit 2adbe1e63bc313d03e8e84e652cc623af8ebb163 +Author: markus@openbsd.org +Date: Wed Mar 15 07:07:39 2017 +0000 upstream commit - Add regression test for AllowUsers and DenyUsers. Patch from - Zev Weiss + disallow KEXINIT before NEWKEYS; ok djm; report by + vegard.nossum at oracle.com - Upstream-Regress-ID: 8f1aac24d52728398871dac14ad26ea38b533fb9 + Upstream-ID: 3668852d1f145050e62f1da08917de34cb0c5234 -commit 3bc8180a008929f6fe98af4a56fb37d04444b417 +commit 2fbf91684d76d38b9cf06550b69c9e41bca5a71c Author: Darren Tucker -Date: Fri Dec 16 15:02:24 2016 +1100 +Date: Thu Mar 16 14:05:46 2017 +1100 - Add missing monitor.h include. + Include includes.h for compat bits. + +commit b55f634e96b9c5b0cd991e23a9ca181bec4bdbad +Author: Darren Tucker +Date: Thu Mar 16 13:45:17 2017 +1100 + + Wrap stdint.h in #ifdef HAVE_STDINT_H + +commit 55a1117d7342a0bf8b793250cf314bab6b482b99 +Author: Damien Miller +Date: Thu Mar 16 11:22:42 2017 +1100 + + Adapt Cygwin config script to privsep knob removal - Fixes warning pointed out by Zev Weiss + Patch from Corinna Vinschen. -commit 410681f9015d76cc7b137dd90dac897f673244a0 -Author: djm@openbsd.org -Date: Fri Dec 16 02:48:55 2016 +0000 +commit 1a321bfdb91defe3c4d9cca5651724ae167e5436 +Author: deraadt@openbsd.org +Date: Wed Mar 15 03:52:30 2017 +0000 upstream commit - revert to rev1.2; the new bits in this test depend on changes - to ssh that aren't yet committed + accidents happen to the best of us; ok djm - Upstream-Regress-ID: 828ffc2c7afcf65d50ff2cf3dfc47a073ad39123 + Upstream-ID: b7a9dbd71011ffde95e06f6945fe7197dedd1604 -commit 2f2ffa4fbe4b671bbffa0611f15ba44cff64d58e -Author: dtucker@openbsd.org -Date: Fri Dec 16 01:06:27 2016 +0000 +commit 25f837646be8c2017c914d34be71ca435dfc0e07 +Author: djm@openbsd.org +Date: Wed Mar 15 02:25:09 2017 +0000 upstream commit - Move the "stop sshd" code into its own helper function. - Patch from Zev Weiss , ok djm@ + fix regression in 7.4: deletion of PKCS#11-hosted keys + would fail unless they were specified by full physical pathname. Report and + fix from Jakub Jelen via bz#2682; ok dtucker@ - Upstream-Regress-ID: a113dea77df5bd97fb4633ea31f3d72dbe356329 + Upstream-ID: 5b5bc20ca11cacb5d5eb29c3f93fd18425552268 -commit e15e7152331e3976b35475fd4e9c72897ad0f074 +commit a8c5eeacf032a7d3408957e45dd7603cc1baf55f Author: djm@openbsd.org -Date: Fri Dec 16 01:01:07 2016 +0000 +Date: Wed Mar 15 02:19:09 2017 +0000 upstream commit - regression test for certificates along with private key - with no public half. bz#2617, mostly from Adam Eijdenberg + Fix segfault when sshd attempts to load RSA1 keys (can + only happen when protocol v.1 support is enabled for the client). Reported by + Jakub Jelen in bz#2686; ok dtucker - Upstream-Regress-ID: 2e74dc2c726f4dc839609b3ce045466b69f01115 + Upstream-ID: 8fdaec2ba4b5f65db1d094f6714ce64b25d871d7 -commit 9a70ec085faf6e55db311cd1a329f1a35ad2a500 -Author: dtucker@openbsd.org -Date: Thu Dec 15 23:50:37 2016 +0000 +commit 66705948c0639a7061a0d0753266da7685badfec +Author: djm@openbsd.org +Date: Tue Mar 14 07:19:07 2017 +0000 upstream commit - Use $SUDO to read pidfile in case root's umask is - restricted. From portable. + Mark the sshd_config UsePrivilegeSeparation option as + deprecated, effectively making privsep mandatory in sandboxing mode. ok + markus@ deraadt@ - Upstream-Regress-ID: f6b1c7ffbc5a0dfb7d430adb2883344899174a98 + (note: this doesn't remove the !privsep code paths, though that will + happen eventually). + + Upstream-ID: b4c52666256c4dd865f8ce9431af5d6ce2d74a0a -commit fe06b68f824f8f55670442fb31f2c03526dd326c +commit f86586b03fe6cd8f595289bde200a94bc2c191af +Author: Damien Miller +Date: Tue Mar 14 18:26:29 2017 +1100 + + Make seccomp-bpf sandbox work on Linux/X32 + + Allow clock_gettime syscall with X32 bit masked off. Apparently + this is required for at least some kernel versions. bz#2142 + Patch mostly by Colin Watson. ok dtucker@ + +commit 2429cf78dd2a9741ce27ba25ac41c535274a0af6 +Author: Damien Miller +Date: Tue Mar 14 18:01:52 2017 +1100 + + require OpenSSL >=1.0.1 + +commit e3ea335abeab731c68f2b2141bee85a4b0bf680f +Author: Damien Miller +Date: Tue Mar 14 17:48:43 2017 +1100 + + Remove macro trickery; no binary change + + This stops the SC_ALLOW(), SC_ALLOW_ARG() and SC_DENY() macros + prepending __NR_ to the syscall number parameter and just makes + them explicit in the macro invocations. + + No binary change in stripped object file before/after. + +commit 5f1596e11d55539678c41f68aed358628d33d86f +Author: Damien Miller +Date: Tue Mar 14 13:15:18 2017 +1100 + + support ioctls for ICA crypto card on Linux/s390 + + Based on patch from Eduardo Barretto; ok dtucker@ + +commit b1b22dd0df2668b322dda174e501dccba2cf5c44 +Author: Darren Tucker +Date: Tue Mar 14 14:19:36 2017 +1100 + + Plumb conversion test into makefile. + +commit f57783f1ddfb4cdfbd612c6beb5ec01cb5b9a6b9 Author: dtucker@openbsd.org -Date: Thu Dec 15 21:29:05 2016 +0000 +Date: Tue Mar 14 01:20:29 2017 +0000 upstream commit - Add missing braces in DenyUsers code. Patch from zev at - bewilderbeest.net, ok deraadt@ + Add unit test for convtime(). - Upstream-ID: d747ace338dcf943b077925f90f85f789714b54e + Upstream-Regress-ID: 8717bc0ca4c21120f6dd3a1d3b7a363f707c31e1 -commit dcc7d74242a574fd5c4afbb4224795b1644321e7 +commit 8884b7247d094cd11ff9e39c325ba928c5bdbc6c Author: dtucker@openbsd.org -Date: Thu Dec 15 21:20:41 2016 +0000 +Date: Tue Mar 14 01:10:07 2017 +0000 upstream commit - Fix text in error message. Patch from zev at - bewilderbeest.net. + Add ASSERT_LONG_* helpers. - Upstream-ID: deb0486e175e7282f98f9a15035d76c55c84f7f6 + Upstream-Regress-ID: fe15beaea8f5063c7f21b0660c722648e3d76431 -commit b737e4d7433577403a31cff6614f6a1b0b5e22f4 -Author: djm@openbsd.org -Date: Wed Dec 14 00:36:34 2016 +0000 +commit c6774d21185220c0ba11e8fd204bf0ad1a432071 +Author: dtucker@openbsd.org +Date: Tue Mar 14 00:55:37 2017 +0000 upstream commit - disable Unix-domain socket forwarding when privsep is - disabled + Fix convtime() overflow test on boundary condition, + spotted by & ok djm. - Upstream-ID: ab61516ae0faadad407857808517efa900a0d6d0 + Upstream-ID: 51f14c507ea87a3022e63f574100613ab2ba5708 -commit 08a1e7014d65c5b59416a0e138c1f73f417496eb -Author: djm@openbsd.org -Date: Fri Dec 9 03:04:29 2016 +0000 +commit f5746b40cfe6d767c8e128fe50c43274b31cd594 +Author: dtucker@openbsd.org +Date: Tue Mar 14 00:25:03 2017 +0000 upstream commit - log connections dropped in excess of MaxStartups at - verbose LogLevel; bz#2613 based on diff from Tomas Kuthan; ok dtucker@ + Check for integer overflow when parsing times in + convtime(). Reported by nicolas.iooss at m4x.org, ok djm@ - Upstream-ID: 703ae690dbf9b56620a6018f8a3b2389ce76d92b + Upstream-ID: 35e6a4e98f6fa24df50bfb8ba1307cf70e966f13 -commit 10e290ec00964b2bf70faab15a10a5574bb80527 +commit f5907982f42a8d88a430b8a46752cbb7859ba979 Author: Darren Tucker -Date: Tue Dec 13 13:51:32 2016 +1100 +Date: Tue Mar 14 13:38:15 2017 +1100 - Get default of TEST_SSH_UTF8 from environment. + Add a "unit" target to run only unit tests. -commit b9b8ba3f9ed92c6220b58d70d1e6d8aa3eea1104 -Author: Darren Tucker -Date: Tue Dec 13 12:56:40 2016 +1100 +commit 9e96b41682aed793fadbea5ccd472f862179fb02 +Author: Damien Miller +Date: Tue Mar 14 12:24:47 2017 +1100 - Remove commented-out includes. + Fix weakness in seccomp-bpf sandbox arg inspection - These commented-out includes have "Still needed?" comments. Since - they've been commented out for ~13 years I assert that they're not. + Syscall arguments are passed via an array of 64-bit values in struct + seccomp_data, but we were only inspecting the bottom 32 bits and not + even those correctly for BE systems. + + Fortunately, the only case argument inspection was used was in the + socketcall filtering so using this for sandbox escape seems + impossible. + + ok dtucker -commit 25275f1c9d5f01a0877d39444e8f90521a598ea0 -Author: Darren Tucker -Date: Tue Dec 13 12:54:23 2016 +1100 +commit 8ff3fc3f2f7c13e8968717bc2b895ee32c441275 +Author: djm@openbsd.org +Date: Sat Mar 11 23:44:16 2017 +0000 - Add prototype for strcasestr in compat library. + upstream commit + + regress tests for loading certificates without public keys; + bz#2617 based on patch from Adam Eijdenberg; ok markus@ dtucker@ + + Upstream-Regress-ID: 0145d19328ed995b73fe2d9da33596b17429d0d0 -commit afec07732aa2985142f3e0b9a01eb6391f523dec -Author: Darren Tucker -Date: Tue Dec 13 10:23:03 2016 +1100 +commit 1e24552716194db8f2f620587b876158a9ef56ad +Author: djm@openbsd.org +Date: Sat Mar 11 23:40:26 2017 +0000 - Add strcasestr to compat library. + upstream commit - Fixes build on (at least) Solaris 10. + allow ssh to use certificates accompanied by a private + key file but no corresponding plain *.pub public key. bz#2617 based on patch + from Adam Eijdenberg; ok dtucker@ markus@ + + Upstream-ID: 295668dca2c39505281577217583ddd2bd4b00b9 -commit dda78a03af32e7994f132d923c2046e98b7c56c8 -Author: Damien Miller -Date: Mon Dec 12 13:57:10 2016 +1100 +commit 0fb1a617a07b8df5de188dd5a0c8bf293d4bfc0e +Author: markus@openbsd.org +Date: Sat Mar 11 13:07:35 2017 +0000 - Force Turkish locales back to C/POSIX; bz#2643 + upstream commit - Turkish locales are unique in their handling of the letters 'i' and - 'I' (yes, they are different letters) and OpenSSH isn't remotely - prepared to deal with that. For now, the best we can do is to force - OpenSSH to use the C/POSIX locale and try to preserve the UTF-8 - encoding if possible. + Don't count the initial block twice when computing how + many bytes to discard for the work around for the attacks against CBC-mode. + ok djm@; report from Jean Paul, Kenny, Martin and Torben @ RHUL - ok dtucker@ + Upstream-ID: f445f509a4e0a7ba3b9c0dae7311cb42458dc1e2 -commit c35995048f41239fc8895aadc3374c5f75180554 -Author: Darren Tucker -Date: Fri Dec 9 12:52:02 2016 +1100 +commit ef653dd5bd5777132d9f9ee356225f9ee3379504 +Author: dtucker@openbsd.org +Date: Fri Mar 10 07:18:32 2017 +0000 - exit is in stdlib.h not unistd.h (that's _exit). + upstream commit + + krl.c + + Upstream-ID: fc5e695d5d107d730182e2da7b23f00b489e0ee1 -commit d399a8b914aace62418c0cfa20341aa37a192f98 -Author: Darren Tucker -Date: Fri Dec 9 12:33:25 2016 +1100 +commit d94c1dfef2ea30ca67b1204ada7c3b537c54f4d0 +Author: Damien Miller +Date: Sun Mar 12 10:48:14 2017 +1100 - Include for exit in utf8 locale test. + sync fmt_scaled.c with OpenBSD + + revision 1.13 + date: 2017/03/11 23:37:23; author: djm; state: Exp; lines: +14 -1; commitid: jnFKyHkB3CEiEZ2R; + fix signed integer overflow in scan_scaled. Found by Nicolas Iooss + using AFL against ssh_config. ok deraadt@ millert@ + ---------------------------- + revision 1.12 + date: 2013/11/29 19:00:51; author: deraadt; state: Exp; lines: +6 -5; + fairly simple unsigned char casts for ctype + ok krw + ---------------------------- + revision 1.11 + date: 2012/11/12 14:07:20; author: halex; state: Exp; lines: +4 -2; + make scan_scaled set errno to EINVAL rather than ERANGE if it encounters + an invalid multiplier, like the man page says it should + + "looks sensible" deraadt@, ok ian@ + ---------------------------- + revision 1.10 + date: 2009/06/20 15:00:04; author: martynas; state: Exp; lines: +4 -4; + use llabs instead of the home-grown version; and some comment changes + ok ian@, millert@ + ---------------------------- -commit 47b8c99ab3221188ad3926108dd9d36da3b528ec -Author: Darren Tucker -Date: Thu Dec 8 15:48:34 2016 +1100 +commit 894221a63fa061e52e414ca58d47edc5fe645968 +Author: djm@openbsd.org +Date: Fri Mar 10 05:01:13 2017 +0000 - Check for utf8 local support before testing it. + upstream commit - Check for utf8 local support and if not found, do not attempt to run the - utf8 tests. Suggested by djm@ + When updating hostkeys, accept RSA keys if + HostkeyAlgorithms contains any RSA keytype. Previously, ssh could ignore RSA + keys when any of the ssh-rsa-sha2-* methods was enabled in HostkeyAlgorithms + nit ssh-rsa (SHA1 signatures) was not. bz#2650 reported by Luis Ressel; ok + dtucker@ + + Upstream-ID: c5e8cfee15c42f4a05d126158a0766ea06da79d2 -commit 4089fc1885b3a2822204effbb02b74e3da58240d -Author: Darren Tucker -Date: Thu Dec 8 12:57:24 2016 +1100 +commit dd3e2298663f4cc1a06bc69582d00dcfee27d73c +Author: djm@openbsd.org +Date: Fri Mar 10 04:24:55 2017 +0000 - Use AC_PATH_TOOL for krb5-config. + upstream commit - This will use the host-prefixed version when cross compiling; patch from - david.michael at coreos.com. + make hostname matching really insensitive to case; + bz#2685, reported by Petr Cerny; ok dtucker@ + + Upstream-ID: e467622ff154269e36ba8b6c9e3d105e1c4a9253 -commit b4867e0712c89b93be905220c82f0a15e6865d1e +commit 77a9be9446697fe8b5499fe651f4a82a71a4b51f Author: djm@openbsd.org -Date: Tue Dec 6 07:48:01 2016 +0000 +Date: Fri Mar 10 03:52:48 2017 +0000 upstream commit - make IdentityFile successfully load and use certificates that - have no corresponding bare public key. E.g. just a private id_rsa and - certificate id_rsa-cert.pub (and no id_rsa.pub). - - bz#2617 ok dtucker@ + reword a comment to make it fit 80 columns - Upstream-ID: c1e9699b8c0e3b63cc4189e6972e3522b6292604 + Upstream-ID: 4ef509a66b96c7314bbcc87027c2af71fa9d0ba4 -commit c9792783a98881eb7ed295680013ca97a958f8ac -Author: Damien Miller -Date: Fri Nov 25 14:04:21 2016 +1100 +commit 61b8ef6a66efaec07e023342cb94a10bdc2254dc +Author: djm@openbsd.org +Date: Fri Mar 10 04:27:32 2017 +0000 - Add a gnome-ssh-askpass3 target for GTK+3 version + upstream commit - Based on patch from Colin Watson via bz#2640 + better match sshd config parser behaviour: fatal() if + line is overlong, increase line buffer to match sshd's; bz#2651 reported by + Don Fong; ok dtucker@ + + Upstream-ID: b175ae7e0ba403833f1ee566edf10f67443ccd18 -commit 7be85ae02b9de0993ce0a1d1e978e11329f6e763 -Author: Damien Miller -Date: Fri Nov 25 14:03:53 2016 +1100 +commit db2597207e69912f2592cd86a1de8e948a9d7ffb +Author: djm@openbsd.org +Date: Fri Mar 10 04:26:06 2017 +0000 - Make gnome-ssh-askpass2.c GTK+3-friendly + upstream commit - Patch from Colin Watson via bz#2640 + ensure hostname is lower-case before hashing it; + bz#2591 reported by Griff Miller II; ok dtucker@ + + Upstream-ID: c3b8b93804f376bd00d859b8bcd9fc0d86b4db17 -commit b9844a45c7f0162fd1b5465683879793d4cc4aaa +commit df9936936c695f85c1038bd706d62edf752aca4b Author: djm@openbsd.org -Date: Sun Dec 4 23:54:02 2016 +0000 +Date: Fri Mar 10 04:24:55 2017 +0000 upstream commit - Fix public key authentication when multiple - authentication is in use. Instead of deleting and re-preparing the entire - keys list, just reset the 'used' flags; the keys list is already in a good - order (with already- tried keys at the back) - - Analysis and patch from Vincent Brillault on bz#2642; ok dtucker@ + make hostname matching really insensitive to case; + bz#2685, reported by Petr Cerny; ok dtucker@ - Upstream-ID: 7123f12dc2f3bcaae715853035a97923d7300176 + Upstream-ID: e632b7a9bf0d0558d5ff56dab98b7cca6c3db549 -commit f2398eb774075c687b13af5bc22009eb08889abe +commit 67eed24bfa7645d88fa0b883745fccb22a0e527e Author: dtucker@openbsd.org -Date: Sun Dec 4 22:27:25 2016 +0000 +Date: Fri Mar 10 04:11:00 2017 +0000 upstream commit - Unlink PidFile on SIGHUP and always recreate it when the - new sshd starts. Regression tests (and possibly other things) depend on the - pidfile being recreated after SIGHUP, and unlinking it means it won't contain - a stale pid if sshd fails to restart. ok djm@ markus@ + Remove old null check from config dumper. Patch from + jjelen at redhat.com vi bz#2687, ok djm@ - Upstream-ID: 132dd6dda0c77dd49d2f15b2573b5794f6160870 + Upstream-ID: 824ab71467b78c4bab0dd1b3a38e8bc5f63dd528 -commit 85aa2efeba51a96bf6834f9accf2935d96150296 +commit 183ba55aaaecca0206184b854ad6155df237adbe Author: djm@openbsd.org -Date: Wed Nov 30 03:01:33 2016 +0000 +Date: Fri Mar 10 04:07:20 2017 +0000 upstream commit - test new behaviour of cert force-command restriction vs. - authorized_key/ principals + fix regression in 7.4 server-sig-algs, where we were + accidentally excluding SHA2 RSA signature methods. bz#2680, patch from Nuno + Goncalves; ok dtucker@ - Upstream-Regress-ID: 399efa7469d40c404c0b0a295064ce75d495387c + Upstream-ID: 81ac8bfb30960447740b9b8f6a214dcf322f12e8 -commit 5d333131cd8519d022389cfd3236280818dae1bc -Author: jmc@openbsd.org -Date: Wed Nov 30 06:54:26 2016 +0000 +commit 66be4fe8c4435af5bbc82998501a142a831f1181 +Author: dtucker@openbsd.org +Date: Fri Mar 10 03:53:11 2017 +0000 upstream commit - tweak previous; while here fix up FILES and AUTHORS; + Check for NULL return value from key_new. Patch from + jjelen at redhat.com via bz#2687, ok djm@ - Upstream-ID: 93f6e54086145a75df8d8ec7d8689bdadbbac8fa + Upstream-ID: 059e33cd43cba88dc8caf0b1936fd4dd88fd5b8e -commit 786d5994da79151180cb14a6cf157ebbba61c0cc +commit ec2892b5c7fea199914cb3a6afb3af38f84990bf Author: djm@openbsd.org -Date: Wed Nov 30 03:07:37 2016 +0000 +Date: Fri Mar 10 03:52:48 2017 +0000 upstream commit - add a whitelist of paths from which ssh-agent will load - (via ssh-pkcs11-helper) a PKCS#11 module; ok markus@ + reword a comment to make it fit 80 columns - Upstream-ID: fe79769469d9cd6d26fe0dc15751b83ef2a06e8f + Upstream-ID: b4b48b4487c0821d16e812c40c9b09f03b28e349 -commit 7844f357cdd90530eec81340847783f1f1da010b -Author: djm@openbsd.org -Date: Wed Nov 30 03:00:05 2016 +0000 +commit 7fadbb6da3f4122de689165651eb39985e1cba85 +Author: dtucker@openbsd.org +Date: Fri Mar 10 03:48:57 2017 +0000 upstream commit - Add a sshd_config DisableForwaring option that disables - X11, agent, TCP, tunnel and Unix domain socket forwarding, as well as - anything else we might implement in the future. - - This, like the 'restrict' authorized_keys flag, is intended to be a - simple and future-proof way of restricting an account. Suggested as - a complement to 'restrict' by Jann Horn; ok markus@ + Check for NULL argument to sshkey_read. Patch from + jjelen at redhat.com via bz#2687, ok djm@ - Upstream-ID: 203803f66e533a474086b38a59ceb4cf2410fcf7 + Upstream-ID: c2d00c2ea50c4861d271d0a586f925cc64a87e0e -commit fd6dcef2030d23c43f986d26979f84619c10589d -Author: djm@openbsd.org -Date: Wed Nov 30 02:57:40 2016 +0000 +commit 5a06b9e019e2b0b0f65a223422935b66f3749de3 +Author: dtucker@openbsd.org +Date: Fri Mar 10 03:45:40 2017 +0000 upstream commit - When a forced-command appears in both a certificate and - an authorized keys/principals command= restriction, refuse to accept the - certificate unless they are identical. + Plug some mem leaks mostly on error paths. From jjelen + at redhat.com via bz#2687, ok djm@ - The previous (documented) behaviour of having the certificate forced- - command override the other could be a bit confused and more error-prone. + Upstream-ID: 3fb030149598957a51b7c8beb32bf92cf30c96f2 + +commit f6edbe9febff8121f26835996b1229b5064d31b7 +Author: dtucker@openbsd.org +Date: Fri Mar 10 03:24:48 2017 +0000 + + upstream commit - Pointed out by Jann Horn of Project Zero; ok dtucker@ + Plug mem leak on GLOB_NOMATCH case. From jjelen at + redhat.com via bz#2687, ok djm@ - Upstream-ID: 79d811b6eb6bbe1221bf146dde6928f92d2cd05f + Upstream-ID: 8016a7ae97719d3aa55fb723fc2ad3200058340d -commit 7fc4766ac78abae81ee75b22b7550720bfa28a33 +commit 566b3a46e89a2fda2db46f04f2639e92da64a120 Author: dtucker@openbsd.org -Date: Wed Nov 30 00:28:31 2016 +0000 +Date: Fri Mar 10 03:22:40 2017 +0000 upstream commit - On startup, check to see if sshd is already daemonized - and if so, skip the call to daemon() and do not rewrite the PidFile. This - means that when sshd re-execs itself on SIGHUP the process ID will no longer - change. Should address bz#2641. ok djm@ markus@. + Plug descriptor leaks of auth_sock. From jjelen at + redhat.com via bz#2687, ok djm@ - Upstream-ID: 5ea0355580056fb3b25c1fd6364307d9638a37b9 + Upstream-ID: 248acb99a5ed2fdca37d1aa33c0fcee7be286d88 -commit c9f880c195c65f1dddcbc4ce9d6bfea7747debcc -Author: Damien Miller -Date: Wed Nov 30 13:51:49 2016 +1100 +commit 8a2834454c73dfc1eb96453c0e97690595f3f4c2 +Author: djm@openbsd.org +Date: Fri Mar 10 03:18:24 2017 +0000 - factor out common PRNG reseed before privdrop + upstream commit - Add a call to RAND_poll() to ensure than more than pid+time gets - stirred into child processes states. Prompted by analysis from Jann - Horn at Project Zero. ok dtucker@ + correctly hash hosts with a port number. Reported by Josh + Powers in bz#2692; ok dtucker@ + + Upstream-ID: 468e357ff143e00acc05bdd2803a696b3d4b6442 -commit 79e4829ec81dead1b30999e1626eca589319a47f -Author: dtucker@openbsd.org -Date: Fri Nov 25 03:02:01 2016 +0000 +commit 9747b9c742de409633d4753bf1a752cbd211e2d3 +Author: djm@openbsd.org +Date: Fri Mar 10 03:15:58 2017 +0000 upstream commit - Allow PuTTY interop tests to run unattended. bz#2639, - patch from cjwatson at debian.org. + don't truncate off \r\n from long stderr lines; bz#2688, + reported by Brian Dyson; ok dtucker@ - Upstream-Regress-ID: 4345253558ac23b2082aebabccd48377433b6fe0 + Upstream-ID: cdfdc4ba90639af807397ce996153c88af046ca4 -commit 504c3a9a1bf090f6b27260fc3e8ea7d984d163dc +commit 4a4b75adac862029a1064577eb5af299b1580cdd Author: dtucker@openbsd.org -Date: Fri Nov 25 02:56:49 2016 +0000 +Date: Fri Mar 10 02:59:51 2017 +0000 upstream commit - Reverse args to sshd-log-wrapper. Matches change in - portable, where it allows sshd do be optionally run under Valgrind. + Validate digest arg in ssh_digest_final; from jjelen at + redhat.com via bz#2687, ok djm@ - Upstream-Regress-ID: b438d1c6726dc5caa2a45153e6103a0393faa906 + Upstream-ID: dbe5494dfddfe523fab341a3dab5a79e7338f878 -commit bd13017736ec2f8f9ca498fe109fb0035f322733 -Author: dtucker@openbsd.org -Date: Fri Nov 25 02:49:18 2016 +0000 +commit bee0167be2340d8de4bdc1ab1064ec957c85a447 +Author: Darren Tucker +Date: Fri Mar 10 13:40:18 2017 +1100 - upstream commit + Check for NULL from malloc. - Fix typo in trace message; from portable. + Part of bz#2687, from jjelen at redhat.com. + +commit da39b09d43b137a5a3d071b51589e3efb3701238 +Author: Darren Tucker +Date: Fri Mar 10 13:22:32 2017 +1100 + + If OSX is using launchd, remove screen no. - Upstream-Regress-ID: 4c4a2ba0d37faf5fd230a91b4c7edb5699fbd73a + Check for socket with and without screen number. From Apple and Jakob + Schlyter via bz#2341, with contributions from Ron Frederick, ok djm@ -commit 7da751d8b007c7f3e814fd5737c2351440d78b4c -Author: tb@openbsd.org -Date: Tue Nov 1 13:43:27 2016 +0000 +commit 8fb15311a011517eb2394bb95a467c209b8b336c +Author: djm@openbsd.org +Date: Wed Mar 8 12:07:47 2017 +0000 upstream commit - Clean up MALLOC_OPTIONS. For the unittests, move - MALLOC_OPTIONS and TEST_ENV to unittets/Makefile.inc. - - ok otto + quote [host]:port in generated ProxyJump commandline; the + [ / ] characters can confuse some shells (e.g. zsh). Reported by Lauri + Tirkkonen via bugs@ - Upstream-Regress-ID: 890d497e0a38eeddfebb11cc429098d76cf29f12 + Upstream-ID: 65cdd161460e1351c3d778e974c1c2a4fa4bc182 -commit 36f58e68221bced35e06d1cca8d97c48807a8b71 -Author: tb@openbsd.org -Date: Mon Oct 31 23:45:08 2016 +0000 +commit 18501151cf272a15b5f2c5e777f2e0933633c513 +Author: dtucker@openbsd.org +Date: Mon Mar 6 02:03:20 2017 +0000 upstream commit - Remove the obsolete A and P flags from MALLOC_OPTIONS. - - ok dtucker + Check l->hosts before dereferencing; fixes potential null + pointer deref. ok djm@ - Upstream-Regress-ID: 6cc25024c8174a87e5734a0dc830194be216dd59 + Upstream-ID: 81c0327c6ec361da794b5c680601195cc23d1301 -commit b0899ee26a6630883c0f2350098b6a35e647f512 +commit d072370793f1a20f01ad827ba8fcd3b8f2c46165 Author: dtucker@openbsd.org -Date: Tue Nov 29 03:54:50 2016 +0000 +Date: Mon Mar 6 00:44:51 2017 +0000 upstream commit - Factor out code to disconnect from controlling terminal - into its own function. ok djm@ + linenum is unsigned long so use %lu in log formats. ok + deraadt@ - Upstream-ID: 39fd9e8ebd7222615a837312face5cc7ae962885 + Upstream-ID: 9dc582d9bb887ebe0164e030d619fc20b1a4ea08 -commit 54d022026aae4f53fa74cc636e4a032d9689b64d +commit 12d3767ba4c84c32150cbe6ff6494498780f12c9 Author: djm@openbsd.org -Date: Fri Nov 25 23:24:45 2016 +0000 +Date: Fri Mar 3 06:13:11 2017 +0000 upstream commit - use sshbuf_allocate() to pre-allocate the buffer used for - loading keys. This avoids implicit realloc inside the buffer code, which - might theoretically leave fragments of the key on the heap. This doesn't - appear to happen in practice for normal sized keys, but was observed for - novelty oversize ones. - - Pointed out by Jann Horn of Project Zero; ok markus@ + fix ssh-keygen -H accidentally corrupting known_hosts that + contained already-hashed entries. HKF_MATCH_HOST_HASHED is only set by + hostkeys_foreach() when hostname matching is in use, so we need to look for + the hash marker explicitly. - Upstream-ID: d620e1d46a29fdea56aeadeda120879eddc60ab1 + Upstream-ID: da82ad653b93e8a753580d3cf5cd448bc2520528 -commit a9c746088787549bb5b1ae3add7d06a1b6d93d5e +commit d7abb771bd5a941b26144ba400a34563a1afa589 Author: djm@openbsd.org -Date: Fri Nov 25 23:22:04 2016 +0000 +Date: Tue Feb 28 06:10:08 2017 +0000 upstream commit - split allocation out of sshbuf_reserve() into a separate - sshbuf_allocate() function; ok markus@ + small memleak: free fd_set on connection timeout (though + we are heading to exit anyway). From Tom Rix in bz#2683 - Upstream-ID: 11b8a2795afeeb1418d508a2c8095b3355577ec2 + Upstream-ID: 10e3dadbb8199845b66581473711642d9e6741c4 -commit f0ddedee460486fa0e32fefb2950548009e5026e -Author: markus@openbsd.org -Date: Wed Nov 23 23:14:15 2016 +0000 +commit 78142e3ab3887e53a968d6e199bcb18daaf2436e +Author: jmc@openbsd.org +Date: Mon Feb 27 14:30:33 2017 +0000 upstream commit - allow ClientAlive{Interval,CountMax} in Match; ok dtucker, - djm + errant dot; from klemens nanni - Upstream-ID: 8beb4c1eadd588f1080b58932281983864979f55 + Upstream-ID: 83d93366a5acf47047298c5d3ebc5e7426f37921 -commit 1a6f9d2e2493d445cd9ee496e6e3c2a2f283f66a +commit 8071a6924c12bb51406a9a64a4b2892675112c87 Author: djm@openbsd.org -Date: Tue Nov 8 22:04:34 2016 +0000 +Date: Fri Feb 24 03:16:34 2017 +0000 upstream commit - unbreak DenyUsers; reported by henning@ + might as well set the listener socket CLOEXEC - Upstream-ID: 1c67d4148f5e953c35acdb62e7c08ae8e33f7cb2 + Upstream-ID: 9c538433d6a0ca79f5f21decc5620e46fb68ab57 -commit 010359b32659f455fddd2bd85fd7cc4d7a3b994a +commit d5499190559ebe374bcdfa8805408646ceffad64 Author: djm@openbsd.org -Date: Sun Nov 6 05:46:37 2016 +0000 +Date: Sun Feb 19 00:11:29 2017 +0000 upstream commit - Validate address ranges for AllowUser/DenyUsers at - configuration load time and refuse to accept bad ones. It was previously - possible to specify invalid CIDR address ranges (e.g. djm@127.1.2.3/55) and - these would always match. + add test cases for C locale; ok schwarze@ - Thanks to Laurence Parry for a detailed bug report. ok markus (for - a previous diff version) - - Upstream-ID: 9dfcdd9672b06e65233ea4434c38226680d40bfb + Upstream-Regress-ID: 783d75de35fbc923d46e2a5e6cee30f8f381ba87 -commit efb494e81d1317209256b38b49f4280897c61e69 +commit 011c8ffbb0275281a0cf330054cf21be10c43e37 Author: djm@openbsd.org -Date: Fri Oct 28 03:33:52 2016 +0000 +Date: Sun Feb 19 00:10:57 2017 +0000 upstream commit - Improve pkcs11_add_provider() logging: demote some - excessively verbose error()s to debug()s, include PKCS#11 provider name and - slot in log messages where possible. bz#2610, based on patch from Jakub Jelen + Add a common nl_langinfo(CODESET) alias for US-ASCII + "ANSI_X3.4-1968" that is used by Linux. Fixes mprintf output truncation for + non-UTF-8 locales on Linux spotted by dtucker@; ok deraadt@ schwarze@ - Upstream-ID: 3223ef693cfcbff9079edfc7e89f55bf63e1973d - -commit 5ee3fb5affd7646f141749483205ade5fc54adaf -Author: Darren Tucker -Date: Tue Nov 1 08:12:33 2016 +1100 - - Use ptrace(PT_DENY_ATTACH, ..) on OS X. + Upstream-ID: c6808956ebffd64066f9075d839f74ff0dd60719 -commit 315d2a4e674d0b7115574645cb51f968420ebb34 -Author: Damien Miller -Date: Fri Oct 28 14:34:07 2016 +1100 +commit 0c4430a19b73058a569573492f55e4c9eeaae67b +Author: dtucker@openbsd.org +Date: Tue Feb 7 23:03:11 2017 +0000 - Unbreak AES-CTR ciphers on old (~0.9.8) OpenSSL + upstream commit - ok dtucker@ + Remove deprecated SSH1 options RSAAuthentication and + RhostsRSAAuthentication from regression test sshd_config. + + Upstream-Regress-ID: 8066b753d9dce7cf02ff87af5c727ff680d99491 -commit a9ff3950b8e80ff971b4d44bbce96df27aed28af -Author: Darren Tucker -Date: Fri Oct 28 14:26:58 2016 +1100 +commit 3baa4cdd197c95d972ec3d07f1c0d08f2d7d9199 +Author: dtucker@openbsd.org +Date: Fri Feb 17 02:32:05 2017 +0000 - Move OPENSSL_NO_RIPEMD160 to compat. + upstream commit - Move OPENSSL_NO_RIPEMD160 to compat and add ifdefs to mac.c around the - ripemd160 MACs. + Do not show rsa1 key type in usage when compiled without + SSH1 support. + + Upstream-ID: 068b5c41357a02f319957746fa4e84ea73960f57 -commit bce58885160e5db2adda3054c3b81fe770f7285a -Author: Darren Tucker -Date: Fri Oct 28 13:52:31 2016 +1100 +commit ecc35893715f969e98fee118481f404772de4132 +Author: dtucker@openbsd.org +Date: Fri Feb 17 02:31:14 2017 +0000 - Check if RIPEMD160 is disabled in OpenSSL. + upstream commit + + ifdef out "rsa1" from the list of supported keytypes when + compiled without SSH1 support. Found by kdunlop at guralp.com, ok djm@ + + Upstream-ID: cea93a26433d235bb1d64b1d990f19a9c160a70f -commit d924640d4c355d1b5eca1f4cc60146a9975dbbff -Author: Darren Tucker -Date: Fri Oct 28 13:38:19 2016 +1100 +commit 10577c6d96a55b877a960b2d0b75edef1b9945af +Author: djm@openbsd.org +Date: Fri Feb 17 02:04:15 2017 +0000 - Skip ssh1 specfic ciphers. + upstream commit - cipher-3des1.c and cipher-bf1.c are specific to sshv1 so don't even try - to compile them when Protocol 1 is not enabled. + For ProxyJump/-J, surround host name with brackets to + allow literal IPv6 addresses. From Dick Visser; ok dtucker@ + + Upstream-ID: 3a5d3b0171250daf6a5235e91bce09c1d5746bf1 -commit 79d078e7a49caef746516d9710ec369ba45feab6 +commit b2afdaf1b52231aa23d2153f4a8c5a60a694dda4 Author: jsg@openbsd.org -Date: Tue Oct 25 04:08:13 2016 +0000 +Date: Wed Feb 15 23:38:31 2017 +0000 upstream commit - Fix logic in add_local_forward() that inverted a test - when code was refactored out into bind_permitted(). This broke ssh port - forwarding for non-priv ports as a non root user. + Fix memory leaks in match_filter_list() error paths. - ok dtucker@ 'looks good' deraadt@ + ok dtucker@ markus@ - Upstream-ID: ddb8156ca03cc99997de284ce7777536ff9570c9 + Upstream-ID: c7f96ac0877f6dc9188bbc908100a8d246cc7f0e -commit a903e315dee483e555c8a3a02c2946937f9b4e5d -Author: dtucker@openbsd.org -Date: Mon Oct 24 01:09:17 2016 +0000 +commit 6d5a41b38b55258213ecfaae9df7a758caa752a1 +Author: djm@openbsd.org +Date: Wed Feb 15 01:46:47 2017 +0000 upstream commit - Remove dead breaks, found via opencoverage.net. ok - deraadt@ + fix division by zero crash in "df" output when server + returns zero total filesystem blocks/inodes. Spotted by Guido Vranken; ok + dtucker@ - Upstream-ID: ad9cc655829d67fad219762810770787ba913069 + Upstream-ID: 6fb6c2ae6b289aa07b6232dbc0be54682ef5419f -commit b4e96b4c9bea4182846e4942ba2048e6d708ee54 +commit bd5d7d239525d595ecea92765334af33a45d9d63 Author: Darren Tucker -Date: Wed Oct 26 08:43:25 2016 +1100 +Date: Sun Feb 12 15:45:15 2017 +1100 - Use !=NULL instead of >0 for getdefaultproj. + ifdef out EVP_R_PRIVATE_KEY_DECODE_ERROR - getdefaultproj() returns a pointer so test it for NULL inequality - instead of >0. Fixes compiler warning and is more correct. Patch from - David Binderman. + EVP_R_PRIVATE_KEY_DECODE_ERROR was added in OpenSSL 1.0.0 so ifdef out + for the benefit of OpenSSL versions prior to that. -commit 1c4ef0b808d3d38232aeeb1cebb7e9a43def42c5 -Author: dtucker@openbsd.org -Date: Sun Oct 23 22:04:05 2016 +0000 +commit 155d540d00ff55f063421ec182ec8ff2b7ab6cbe +Author: djm@openbsd.org +Date: Fri Feb 10 04:34:50 2017 +0000 upstream commit - Factor out "can bind to low ports" check into its own function. This will - make it easier for Portable to support platforms with permissions models - other than uid==0 (eg bz#2625). ok djm@, "doesn't offend me too much" - deraadt@. + bring back r1.34 that was backed out for problems loading + public keys: - Upstream-ID: 86213df4183e92b8f189a6d2dac858c994bfface + translate OpenSSL error codes to something more + meaninful; bz#2522 reported by Jakub Jelen, ok dtucker@ + + with additional fix from Jakub Jelen to solve the backout. + bz#2525 bz#2523 re-ok dtucker@ + + Upstream-ID: a9d5bc0306f4473d9b4f4484f880e95f3c1cc031 -commit 0b9ee623d57e5de7e83e66fd61a7ba9a5be98894 -Author: dtucker@openbsd.org -Date: Wed Oct 19 23:21:56 2016 +0000 +commit a287c5ad1e0bf9811c7b9221979b969255076019 +Author: djm@openbsd.org +Date: Fri Feb 10 03:36:40 2017 +0000 upstream commit - When tearing down ControlMaster connecctions, don't - pollute stderr when LogLevel=quiet. Patch from Tim Kuijsten via tech@. + Sanitise escape sequences in key comments sent to printf + but preserve valid UTF-8 when the locale supports it; bz#2520 ok dtucker@ - Upstream-ID: d9b3a68b2a7c2f2fc7f74678e29a4618d55ceced + Upstream-ID: e8eed28712ba7b22d49be534237eed019875bd1e -commit 09e6a7d8354224933febc08ddcbc2010f542284e -Author: Darren Tucker -Date: Mon Oct 24 09:06:18 2016 +1100 +commit e40269be388972848aafcca7060111c70aab5b87 +Author: millert@openbsd.org +Date: Wed Feb 8 20:32:43 2017 +0000 - Wrap stdint.h include in ifdef. + upstream commit + + Avoid printf %s NULL. From semarie@, OK djm@ + + Upstream-ID: 06beef7344da0208efa9275d504d60d2a5b9266c -commit 08d9e9516e587b25127545c029e5464b2e7f2919 -Author: Darren Tucker -Date: Fri Oct 21 09:46:46 2016 +1100 +commit 5b90709ab8704dafdb31e5651073b259d98352bc +Author: djm@openbsd.org +Date: Mon Feb 6 09:22:51 2017 +0000 - Fix formatting. + upstream commit + + Restore \r\n newline sequence for server ident string. The CR + got lost in the flensing of SSHv1. Pointed out by Stef Bon + + Upstream-ID: 5333fd43ce5396bf5999496096fac5536e678fac -commit 461f50e7ab8751d3a55e9158c44c13031db7ba1d -Author: Darren Tucker -Date: Fri Oct 21 06:55:58 2016 +1100 +commit 97c31c46ee2e6b46dfffdfc4f90bbbf188064cbc +Author: djm@openbsd.org +Date: Fri Feb 3 23:01:42 2017 +0000 - Update links to https. + upstream commit - www.openssh.com now supports https and ftp.openbsd.org no longer - supports ftp. Make all links to these https. + unit test for match_filter_list() function; still want a + better name for this... + + Upstream-Regress-ID: 840ad6118552c35111f0a897af9c8d93ab8de92a -commit dd4e7212a6141f37742de97795e79db51e4427ad -Author: Darren Tucker -Date: Fri Oct 21 06:48:46 2016 +1100 +commit f1a193464a7b77646f0d0cedc929068e4a413ab4 +Author: djm@openbsd.org +Date: Fri Feb 3 23:05:57 2017 +0000 - Update host key generation examples. + upstream commit - Remove ssh1 host key generation, add ssh-keygen -A + use ssh_packet_set_log_preamble() to include connection + username in packet log messages, e.g. + + Connection closed by invalid user foo 10.1.1.1 port 44056 [preauth] + + ok markus@ bz#113 + + Upstream-ID: 3591b88bdb5416d6066fb3d49d8fff2375bf1a15 -commit 6d49ae82634c67e9a4d4af882bee20b40bb8c639 -Author: Darren Tucker -Date: Fri Oct 21 05:22:55 2016 +1100 +commit 07edd7e9537ab32aa52abb5fb2a915c350fcf441 +Author: djm@openbsd.org +Date: Fri Feb 3 23:03:33 2017 +0000 - Update links. + upstream commit - Make links to openssh.com HTTPS now that it's supported, point release - notes link to the HTML release notes page, and update a couple of other - links and bits of text. + add ssh_packet_set_log_preamble() to allow inclusion of a + preamble string in disconnect messages; ok markus@ + + Upstream-ID: 34cb41182cd76d414c214ccb01c01707849afead -commit fe0d1ca6ace06376625084b004ee533f2c2ea9d6 -Author: Darren Tucker -Date: Thu Oct 20 03:42:09 2016 +1100 +commit 68bc8cfa7642d3ccbf2cd64281c16b8b9205be59 +Author: djm@openbsd.org +Date: Fri Feb 3 23:01:19 2017 +0000 - Remote channels .orig and .rej files. + upstream commit - These files were incorrectly added during an OpenBSD sync. + support =- for removing methods from algorithms lists, + e.g. Ciphers=-*cbc; suggested by Cristian Ionescu-Idbohrn in bz#2671 "I like + it" markus@ + + Upstream-ID: c78c38f9f81a963b33d0eade559f6048add24a6d -commit 246aa842a4ad368d8ce030495e657ef3a0e1f95c -Author: dtucker@openbsd.org -Date: Tue Oct 18 17:32:54 2016 +0000 +commit c924b2ef941028a1f31e6e94f54dfeeeef462a4e +Author: djm@openbsd.org +Date: Fri Feb 3 05:05:56 2017 +0000 upstream commit - Remove channel_input_port_forward_request(); the only caller - was the recently-removed SSH1 server code so it's now dead code. ok markus@ + allow form-feed characters at EOL; bz#2431 ok dtucker@ - Upstream-ID: 05453983230a1f439562535fec2818f63f297af9 + Upstream-ID: 1f453afaba6da2ae69d6afdf1ae79a917552f1a2 -commit 2c6697c443d2c9c908260eed73eb9143223e3ec9 -Author: millert@openbsd.org -Date: Tue Oct 18 12:41:22 2016 +0000 +commit 523db8540b720c4d21ab0ff6f928476c70c38aab +Author: Damien Miller +Date: Fri Feb 3 16:01:22 2017 +1100 - upstream commit + prefer to use ldns-config to find libldns - Install a signal handler for tty-generated signals and - wait for the ssh child to suspend before suspending sftp. This lets ssh - restore the terminal mode as needed when it is suspended at the password - prompt. OK dtucker@ + Should fix bz#2603 - "Build with ldns and without kerberos support + fails if ldns compiled with kerberos support" by including correct + cflags/libs - Upstream-ID: a31c1f42aa3e2985dcc91e46e6a17bd22e372d69 + ok dtucker@ -commit fd2a8f1033fa2316fff719fd5176968277560158 -Author: jmc@openbsd.org -Date: Sat Oct 15 19:56:25 2016 +0000 +commit c998bf0afa1a01257a53793eba57941182e9e0b7 +Author: dtucker@openbsd.org +Date: Fri Feb 3 02:56:00 2017 +0000 upstream commit - various formatting fixes, specifically removing Dq; + Make ssh_packet_set_rekey_limits take u32 for the number of + seconds until rekeying (negative values are rejected at config parse time). + This allows the removal of some casts and a signed vs unsigned comparison + warning. - Upstream-ID: 81e85df2b8e474f5f93d66e61d9a4419ce87347c + rekey_time is cast to int64 for the comparison which is a no-op + on OpenBSD, but should also do the right thing in -portable on + anything still using 32bit time_t (until the system time actually + wraps, anyway). + + some early guidance deraadt@, ok djm@ + + Upstream-ID: c9f18613afb994a07e7622eb326f49de3d123b6c -commit 8f866d8a57b9a2dc5dd04504e27f593b551618e3 -Author: Darren Tucker -Date: Wed Oct 19 03:26:09 2016 +1100 +commit 3ec5fa4ba97d4c4853620daea26a33b9f1fe3422 +Author: jsg@openbsd.org +Date: Thu Feb 2 10:54:25 2017 +0000 - Import readpassphrase.c rev 1.26. + upstream commit - Author: miller@openbsd.org: - Avoid generate SIGTTOU when restoring the terminal mode. If we get - SIGTTOU it means the process is not in the foreground process group - which, in most cases, means that the shell has taken control of the tty. - Requiring the user the fg the process in this case doesn't make sense - and can result in both SIGTSTP and SIGTTOU being sent which can lead to - the process being suspended again immediately after being brought into - the foreground. + In vasnmprintf() return an error if malloc fails and + don't set a function argument to the address of free'd memory. + + ok djm@ + + Upstream-ID: 1efffffff2f51d53c9141f245b90ac23d33b9779 -commit f901440cc844062c9bab0183d133f7ccc58ac3a5 -Author: Darren Tucker -Date: Wed Oct 19 03:23:16 2016 +1100 +commit 858252fb1d451ebb0969cf9749116c8f0ee42753 +Author: dtucker@openbsd.org +Date: Wed Feb 1 02:59:09 2017 +0000 - Import readpassphrase.c rev 1.25. + upstream commit - Wrap so internal calls go direct and - readpassphrase is weak. + Return true reason for port forwarding failures where + feasible rather than always "administratively prohibited". bz#2674, ok djm@ - (DEF_WEAK is a no-op in portable.) + Upstream-ID: d901d9887951774e604ca970e1827afaaef9e419 -commit 032147b69527e5448a511049b2d43dbcae582624 -Author: Darren Tucker -Date: Sat Oct 15 05:51:12 2016 +1100 +commit 6ba9f893838489add6ec4213c7a997b425e4a9e0 +Author: dtucker@openbsd.org +Date: Mon Jan 30 23:27:39 2017 +0000 - Move DEF_WEAK into defines.h. + upstream commit - As well pull in more recent changes from OpenBSD these will start to - arrive so put it where the definition is shared. + Small correction to the known_hosts section on when it is + updated. Patch from lkppo at free.fr some time ago, pointed out by smallm at + sdf.org + + Upstream-ID: 1834d7af179dea1a12ad2137f84566664af225d5 -commit e0259a82ddd950cfb109ddee86fcebbc09c6bd04 +commit c61d5ec3c11e7ff9779b6127421d9f166cf10915 Author: Darren Tucker -Date: Sat Oct 15 04:34:46 2016 +1100 +Date: Fri Feb 3 14:10:34 2017 +1100 - Remove do_pam_set_tty which is dead code. + Remove _XOPEN_SOURCE from wide char detection. - The callers of do_pam_set_tty were removed in 2008, so this is now dead - code. bz#2604, pointed out by jjelen at redhat.com. + Having _XOPEN_SOURCE unconditionally causes problems on some platforms + and configurations, notably Solaris 64-bit binaries. It was there for + the benefit of Linux put the required bits in the *-*linux* section. + + Patch from yvoinov at gmail.com. -commit ca04de83f210959ad2ed870a30ba1732c3ae00e3 -Author: Damien Miller -Date: Thu Oct 13 18:53:43 2016 +1100 +commit f25ee13b3e81fd80efeb871dc150fe49d7fc8afd +Author: djm@openbsd.org +Date: Mon Jan 30 05:22:14 2017 +0000 - unbreak principals-command test + upstream commit - Undo inconsistetly updated variable name. + fully unbreak: some $SSH invocations did not have -F + specified and could pick up the ~/.ssh/config of the user running the tests + + Upstream-Regress-ID: f362d1892c0d3e66212d5d3fc02d915c58ef6b89 -commit 1723ec92eb485ce06b4cbf49712d21975d873909 +commit 6956e21fb26652887475fe77ea40d2efcf25908b Author: djm@openbsd.org -Date: Tue Oct 11 21:49:54 2016 +0000 +Date: Mon Jan 30 04:54:07 2017 +0000 upstream commit - fix the KEX fuzzer - the previous method of obtaining the - packet contents was broken. This now uses the new per-packet input hook, so - it sees exact post-decrypt packets and doesn't have to pass packet integrity - checks. ok markus@ + partially unbreak: was not specifying hostname on some + $SSH invocations - Upstream-Regress-ID: 402fb6ffabd97de590e8e57b25788949dce8d2fd + Upstream-Regress-ID: bc8a5e98e57bad0a92ef4f34ed91c1d18294e2cc -commit 09f997893f109799cddbfce6d7e67f787045cbb2 -Author: natano@openbsd.org -Date: Thu Oct 6 09:31:38 2016 +0000 +commit 52763dd3fe0a4678dafdf7aeb32286e514130afc +Author: djm@openbsd.org +Date: Mon Jan 30 01:03:00 2017 +0000 upstream commit - Move USER out of the way to unbreak the BUILDUSER - mechanism. ok tb + revise keys/principals command hang fix (bz#2655) to + consume entire output, avoiding sending SIGPIPE to subprocesses early; ok + dtucker@ - Upstream-Regress-ID: 74ab9687417dd071d62316eaadd20ddad1d5af3c + Upstream-ID: 7cb04b31a61f8c78c4e48ceededcd2fd5c4ee1bc -commit 3049a012c482a7016f674db168f23fd524edce27 -Author: bluhm@openbsd.org -Date: Fri Sep 30 11:55:20 2016 +0000 +commit 381a2615a154a82c4c53b787f4a564ef894fe9ac +Author: djm@openbsd.org +Date: Mon Jan 30 00:38:50 2017 +0000 upstream commit - In ssh tests set REGRESS_FAIL_EARLY with ?= so that the - environment can change it. OK djm@ + small cleanup post SSHv1 removal: - Upstream-Regress-ID: 77bcb50e47b68c7209c7f0a5a020d73761e5143b + remove SSHv1-isms in commented examples + + reorder token table to group deprecated and compile-time conditional tokens + better + + fix config dumping code for some compile-time conditional options that + weren't being correctly skipped (SSHv1 and PKCS#11) + + Upstream-ID: f2e96b3cb3158d857c5a91ad2e15925df3060105 -commit 39af7b444db28c1cb01b7ea468a4f574a44f375b +commit 4833d01591b7eb049489d9558b65f5553387ed43 Author: djm@openbsd.org -Date: Tue Oct 11 21:47:45 2016 +0000 +Date: Mon Jan 30 00:34:01 2017 +0000 upstream commit - Add a per-packet input hook that is called with the - decrypted packet contents. This will be used for fuzzing; ok markus@ + some explicit NULL tests when dumping configured + forwardings; from Karsten Weiss - Upstream-ID: a3221cee6b1725dd4ae1dd2c13841b4784cb75dc + Upstream-ID: 40957b8dea69672b0e50df6b4a91a94e3e37f72d -commit ec165c392ca54317dbe3064a8c200de6531e89ad -Author: markus@openbsd.org -Date: Mon Oct 10 19:28:48 2016 +0000 +commit 326e2fae9f2e3e067b5651365eba86b35ee5a6b2 +Author: djm@openbsd.org +Date: Mon Jan 30 00:32:28 2017 +0000 upstream commit - Unregister the KEXINIT handler after message has been - received. Otherwise an unauthenticated peer can repeat the KEXINIT and cause - allocation of up to 128MB -- until the connection is closed. Reported by - shilei-c at 360.cn + misplaced braces in test; from Karsten Weiss - Upstream-ID: 43649ae12a27ef94290db16d1a98294588b75c05 + Upstream-ID: f7b794074d3aae8e35b69a91d211c599c94afaae -commit 29d40319392e6e19deeca9d45468aa1119846e50 -Author: Darren Tucker -Date: Thu Oct 13 04:07:20 2016 +1100 +commit 3e032a95e46bfaea9f9e857678ac8fa5f63997fb +Author: djm@openbsd.org +Date: Mon Jan 30 00:32:03 2017 +0000 - Import rev 1.24 from OpenBSD. + upstream commit - revision 1.24 - date: 2013/11/24 23:51:29; author: deraadt; state: Exp; lines: +4 -4; - most obvious unsigned char casts for ctype - ok jca krw ingo + don't dereference authctxt before testing != NULL, it + causes compilers to make assumptions; from Karsten Weiss + + Upstream-ID: 794243aad1e976ebc717885b7a97a25e00c031b2 -commit 12069e56221de207ed666c2449dedb431a2a7ca2 -Author: Darren Tucker -Date: Thu Oct 13 04:04:44 2016 +1100 +commit 01cfaa2b1cfb84f3cdd32d1bf82b120a8d30e057 +Author: djm@openbsd.org +Date: Fri Jan 6 02:51:16 2017 +0000 - Import rev 1.23 from OpenBSD. Fixes bz#2619. + upstream commit - revision 1.23 - date: 2010/05/14 13:30:34; author: millert; state: Exp; lines: +41 -39; - Defer installing signal handlers until echo is disabled so that we - get suspended normally when not the foreground process. Fix potential - infinite loop when restoring terminal settings if process is in the - background when restore occurs. OK miod@ + use correct ssh-add program; bz#2654, from Colin Watson + + Upstream-Regress-ID: 7042a36e1bdaec6562f6e57e9d047efe9c7a6030 -commit 7508d83eff89af069760b4cc587305588a64e415 -Author: Darren Tucker -Date: Thu Oct 13 03:53:51 2016 +1100 +commit e5c7ec67cdc42ae2584085e0fc5cc5ee91133cf5 +Author: dtucker@openbsd.org +Date: Fri Jan 6 02:26:10 2017 +0000 - If we don't have TCSASOFT, define it to zero. + upstream commit - This makes it a no-op when we use it below, which allows us to re-sync - those lines with the upstream and make future updates easier. + Account for timeouts in the integrity tests as failures. + + If the first test in a series for a given MAC happens to modify the low + bytes of a packet length, then ssh will time out and this will be + interpreted as a test failure. Patch from cjwatson at debian.org via + bz#2658. + + Upstream-Regress-ID: e7467613b0badedaa300bc6fc7495ec2f44e2fb9 -commit aae4dbd4c058d3b1fe1eb5c4e6ddf35827271377 -Author: jmc@openbsd.org -Date: Fri Oct 7 14:41:52 2016 +0000 +commit dbaf599b61bd6e0f8469363a8c8e7f633b334018 +Author: dtucker@openbsd.org +Date: Fri Jan 6 02:09:25 2017 +0000 upstream commit - tidy up the formatting in this file. more specifically, - replace .Dq, which looks appalling, with .Cm, where appropriate; + Make forwarding test less racy by using unix domain + sockets instead of TCP ports where possible. Patch from cjwatson at + debian.org via bz#2659. - Upstream-ID: ff8e90aa0343d9bb56f40a535e148607973cc738 + Upstream-Regress-ID: 4756375aac5916ef9d25452a1c1d5fa9e90299a9 -commit a571dbcc7b7b25371174569b13df5159bc4c6c7a -Author: djm@openbsd.org -Date: Tue Oct 4 21:34:40 2016 +0000 +commit 9390b0031ebd6eb5488d3bc4d4333c528dffc0a6 +Author: dtucker@openbsd.org +Date: Sun Jan 29 21:35:23 2017 +0000 upstream commit - add a comment about implicitly-expected checks to - sshkey_ec_validate_public() + Fix typo in ~C error message for bad port forward + cancellation. bz#2672, from Brad Marshall via Colin Watson and Ubuntu's + bugtracker. - Upstream-ID: 74a7f71c28f7c13a50f89fc78e7863b9cd61713f + Upstream-ID: 0d4a7e5ead6cc59c9a44b4c1e5435ab3aada09af -commit 2f78a2a698f4222f8e05cad57ac6e0c3d1faff00 -Author: djm@openbsd.org -Date: Fri Sep 30 20:24:46 2016 +0000 +commit 4ba15462ca38883b8a61a1eccc093c79462d5414 +Author: guenther@openbsd.org +Date: Sat Jan 21 11:32:04 2017 +0000 upstream commit - fix some -Wpointer-sign warnings in the new mux proxy; ok - markus@ + The POSIX APIs that that sockaddrs all ignore the s*_len + field in the incoming socket, so userspace doesn't need to set it unless it + has its own reasons for tracking the size along with the sockaddr. - Upstream-ID: b1ba7b3769fbc6b7f526792a215b0197f5e55dfd + ok phessler@ deraadt@ florian@ + + Upstream-ID: ca6e49e2f22f2b9e81d6d924b90ecd7e422e7437 -commit ca71c36645fc26fcd739a8cfdc702cec85607761 -Author: bluhm@openbsd.org -Date: Wed Sep 28 20:09:52 2016 +0000 +commit a1187bd3ef3e4940af849ca953a1b849dae78445 +Author: jmc@openbsd.org +Date: Fri Jan 6 16:28:12 2017 +0000 upstream commit - Add a makefile rule to create the ssh library when - regress needs it. This allows to run the ssh regression tests without doing - a "make build" before. Discussed with dtucker@ and djm@; OK djm@ + keep the tokens list sorted; - Upstream-Regress-ID: ce489bd53afcd471225a125b4b94565d4717c025 + Upstream-ID: b96239dae4fb3aa94146bb381afabcc7740a1638 -commit ce44c970f913d2a047903dba8670554ac42fc479 -Author: bluhm@openbsd.org -Date: Mon Sep 26 21:34:38 2016 +0000 +commit b64077f9767634715402014f509e58decf1e140d +Author: djm@openbsd.org +Date: Fri Jan 6 09:27:52 2017 +0000 upstream commit - Allow to run ssh regression tests as root. If the user - is already root, the test should not expect that SUDO is set. If ssh needs - another user, use sudo or doas to switch from root if necessary. OK dtucker@ + fix previous - Upstream-Regress-ID: b464e55185ac4303529e3e6927db41683aaeace2 + Upstream-ID: c107d6a69bc22325d79fbf78a2a62e04bcac6895 -commit 8d0578478586e283e751ca51e7b0690631da139a -Author: markus@openbsd.org -Date: Fri Sep 30 09:19:13 2016 +0000 +commit 5e820e9ea2e949aeb93071fe31c80b0c42f2b2de +Author: djm@openbsd.org +Date: Fri Jan 6 03:53:58 2017 +0000 upstream commit - ssh proxy mux mode (-O proxy; idea from Simon Tatham): - mux - client speaks the ssh-packet protocol directly over unix-domain socket. - mux - server acts as a proxy, translates channel IDs and relays to the server. - no - filedescriptor passing necessary. - combined with unix-domain forwarding it's - even possible to run mux client and server on different machines. feedback - & ok djm@ + show a useful error message when included config files + can't be opened; bz#2653, ok dtucker@ - Upstream-ID: 666a2fb79f58e5c50e246265fb2b9251e505c25b + Upstream-ID: f598b73b5dfe497344cec9efc9386b4e5a3cb95b -commit b7689155f3f5c4999846c07a852b1c7a43b09cec +commit 13bd2e2d622d01dc85d22b94520a5b243d006049 Author: djm@openbsd.org -Date: Wed Sep 28 21:44:52 2016 +0000 +Date: Fri Jan 6 03:45:41 2017 +0000 upstream commit - put back some pre-auth zlib bits that I shouldn't have - removed - they are still used by the client. Spotted by naddy@ + sshd_config is documented to set + GSSAPIStrictAcceptorCheck=yes by default, so actually make it do this. + bz#2637 ok dtucker - Upstream-ID: 80919468056031037d56a1f5b261c164a6f90dc2 + Upstream-ID: 99ef8ac51f17f0f7aec166cb2e34228d4d72a665 -commit 4577adead6a7d600c8e764619d99477a08192c8f +commit f89b928534c9e77f608806a217d39a2960cc7fd0 Author: djm@openbsd.org -Date: Wed Sep 28 20:32:42 2016 +0000 +Date: Fri Jan 6 03:41:58 2017 +0000 upstream commit - restore pre-auth compression support in the client -- the - previous commit was intended to remove it from the server only. - - remove a few server-side pre-auth compression bits that escaped + Avoid confusing error message when attempting to use + ssh-keyscan built without SSH protocol v.1 to scan for v.1 keys; bz#2583 - adjust wording of Compression directive in sshd_config(5) + Upstream-ID: 5d214abd3a21337d67c6dcc5aa6f313298d0d165 + +commit 0999533014784579aa6f01c2d3a06e3e8804b680 +Author: dtucker@openbsd.org +Date: Fri Jan 6 02:34:54 2017 +0000 + + upstream commit - pointed out by naddy@ ok markus@ + Re-add '%k' token for AuthorizedKeysCommand which was + lost during the re-org in rev 1.235. bz#2656, from jboning at gmail.com. - Upstream-ID: d23696ed72a228dacd4839dd9f2dec424ba2016b + Upstream-ID: 2884e203c02764d7b3fe7472710d9c24bdc73e38 -commit 80d1c963b4dc84ffd11d09617b39c4bffda08956 -Author: jmc@openbsd.org -Date: Wed Sep 28 17:59:22 2016 +0000 +commit 51045869fa084cdd016fdd721ea760417c0a3bf3 +Author: djm@openbsd.org +Date: Wed Jan 4 05:37:40 2017 +0000 upstream commit - use a separate TOKENS section, as we've done for - sshd_config(5); help/ok djm + unbreak Unix domain socket forwarding for root; ok + markus@ - Upstream-ID: 640e32b5e4838e4363738cdec955084b3579481d + Upstream-ID: 6649c76eb7a3fa15409373295ca71badf56920a2 -commit 1cfd5c06efb121e58e8b6671548fda77ef4b4455 -Author: Damien Miller -Date: Thu Sep 29 03:19:23 2016 +1000 +commit 58fca12ba967ea5c768653535604e1522d177e44 +Author: Darren Tucker +Date: Mon Jan 16 09:08:32 2017 +1100 - Remove portability support for mmap + Remove LOGIN_PROGRAM. - We no longer need to wrap/replace mmap for portability now that - pre-auth compression has been removed from OpenSSH. + UseLogin is gone, remove leftover. bz#2665, from cjwatson at debian.org -commit 0082fba4efdd492f765ed4c53f0d0fbd3bdbdf7f +commit b108ce92aae0ca0376dce9513d953be60e449ae1 Author: djm@openbsd.org -Date: Wed Sep 28 16:33:06 2016 +0000 +Date: Wed Jan 4 02:21:43 2017 +0000 upstream commit - Remove support for pre-authentication compression. Doing - compression early in the protocol probably seemed reasonable in the 1990s, - but today it's clearly a bad idea in terms of both cryptography (cf. multiple - compression oracle attacks in TLS) and attack surface. - - Moreover, to support it across privilege-separation zlib needed - the assistance of a complex shared-memory manager that made the - required attack surface considerably larger. + relax PKCS#11 whitelist a bit to allow libexec as well as + lib directories. - Prompted by Guido Vranken pointing out a compiler-elided security - check in the shared memory manager found by Stack - (http://css.csail.mit.edu/stack/); ok deraadt@ markus@ + Upstream-ID: cf5617958e2e2d39f8285fd3bc63b557da484702 + +commit c7995f296b9222df2846f56ecf61e5ae13d7a53d +Author: djm@openbsd.org +Date: Tue Jan 3 05:46:51 2017 +0000 + + upstream commit - NB. pre-auth authentication has been disabled by default in sshd - for >10 years. + check number of entries in SSH2_FXP_NAME response; avoids + unreachable overflow later. Reported by Jann Horn - Upstream-ID: 32af9771788d45a0779693b41d06ec199d849caf + Upstream-ID: b6b2b434a6d6035b1644ca44f24cd8104057420f -commit 27c3a9c2aede2184856b5de1e6eca414bb751c38 +commit ddd3d34e5c7979ca6f4a3a98a7d219a4ed3d98c2 Author: djm@openbsd.org -Date: Mon Sep 26 21:16:11 2016 +0000 +Date: Fri Dec 30 22:08:02 2016 +0000 upstream commit - Avoid a theoretical signed integer overflow should - BN_num_bytes() ever violate its manpage and return a negative value. Improve - order of tests to avoid confusing increasingly pedantic compilers. - - Reported by Guido Vranken from stack (css.csail.mit.edu/stack) - unstable optimisation analyser output. ok deraadt@ + fix deadlock when keys/principals command produces a lot of + output and a key is matched early; bz#2655, patch from jboning AT gmail.com - Upstream-ID: f8508c830c86d8f36c113985e52bf8eedae23505 + Upstream-ID: e19456429bf99087ea994432c16d00a642060afe -commit 8663e51c80c6aa3d750c6d3bcff6ee05091922be -Author: Damien Miller -Date: Wed Sep 28 07:40:33 2016 +1000 +commit 30eee7d1b2fec33c14870cc11910610be5d2aa6f +Author: Darren Tucker +Date: Tue Dec 20 12:16:11 2016 +1100 - fix mdoc2man.awk formatting for top-level lists + Re-add missing "Prerequisites" header and fix typo - Reported by Glenn Golden - Diagnosis and fix from Ingo Schwarze + Patch from HARUYAMA Seigo . -commit b97739dc21570209ed9d4e7beee0c669ed23b097 +commit c8c60f3663165edd6a52632c6ddbfabfce1ca865 Author: djm@openbsd.org -Date: Thu Sep 22 21:15:41 2016 +0000 +Date: Mon Dec 19 22:35:23 2016 +0000 upstream commit - missing bit from previous commit + use standard /bin/sh equality test; from Mike Frysinger - Upstream-ID: 438d5ed6338b28b46e822eb13eee448aca31df37 + Upstream-Regress-ID: 7b6f0b63525f399844c8ac211003acb8e4b0bec2 -commit de6a175a99d22444e10d19ad3fffef39bc3ee3bb -Author: jmc@openbsd.org -Date: Thu Sep 22 19:19:01 2016 +0000 +commit 4a354fc231174901f2629437c2a6e924a2dd6772 +Author: Damien Miller +Date: Mon Dec 19 15:59:26 2016 +1100 - upstream commit - - organise the token stuff into a separate section; ok - markus for an earlier version of the diff ok/tweaks djm - - Upstream-ID: 81a6daa506a4a5af985fce7cf9e59699156527c8 + crank version numbers for release -commit 16277fc45ffc95e4ffc3d45971ff8320b974de2b +commit 5f8d0bb8413d4d909cc7aa3c616fb0538224c3c9 Author: djm@openbsd.org -Date: Thu Sep 22 17:55:13 2016 +0000 +Date: Mon Dec 19 04:55:51 2016 +0000 upstream commit - mention curve25519-sha256 KEX + openssh-7.4 - Upstream-ID: 33ae1f433ce4795ffa6203761fbdf86e0d7ffbaf + Upstream-ID: 1ee404adba6bbe10ae9277cbae3a94abe2867b79 -commit 0493766d5676c7ca358824ea8d3c90f6047953df +commit 3a8213ea0ed843523e34e55ab9c852332bab4c7b Author: djm@openbsd.org -Date: Thu Sep 22 17:52:53 2016 +0000 +Date: Mon Dec 19 04:55:18 2016 +0000 upstream commit - support plain curve25519-sha256 KEX algorithm now that it - is approaching standardisation (same algorithm is currently supported as - curve25519-sha256@libssh.org) + remove testcase that depends on exact output and + behaviour of snprintf(..., "%s", NULL) - Upstream-ID: 5e2b6db2e72667048cf426da43c0ee3fc777baa2 + Upstream-Regress-ID: cab4288531766bd9593cb556613b91a2eeefb56f -commit f31c654b30a6f02ce0b8ea8ab81791b675489628 +commit eae735a82d759054f6ec7b4e887fb7a5692c66d7 Author: dtucker@openbsd.org -Date: Thu Sep 22 02:29:57 2016 +0000 +Date: Mon Dec 19 03:32:57 2016 +0000 upstream commit - If ssh receives a PACKET_DISCONNECT during userauth it - will cause ssh_dispatch_run(DISPATCH_BLOCK, ...) to return without the - session being authenticated. Check for this and exit if necessary. ok djm@ + Use LOGNAME to get current user and fall back to whoami if + not set. Mainly to benefit -portable since some platforms don't have whoami. - Upstream-ID: b3afe126c0839d2eae6cddd41ff2ba317eda0903 + Upstream-Regress-ID: e3a16b7836a3ae24dc8f8a4e43fdf8127a60bdfa -commit 1622649b7a829fc8dc313042a43a974f0f3e8a99 -Author: djm@openbsd.org -Date: Wed Sep 21 19:53:12 2016 +0000 +commit 0d2f88428487518eea60602bd593989013831dcf +Author: dtucker@openbsd.org +Date: Fri Dec 16 03:51:19 2016 +0000 upstream commit - correctly return errors from kex_send_ext_info(). Fix from - Sami Farin via https://github.com/openssh/openssh-portable/pull/50 + Add regression test for AllowUsers and DenyUsers. Patch from + Zev Weiss - Upstream-ID: c85999af28aaecbf92cfa2283381df81e839b42c + Upstream-Regress-ID: 8f1aac24d52728398871dac14ad26ea38b533fb9 -commit f83a0cfe16c7a73627b46a9a94e40087d60f32fb -Author: djm@openbsd.org -Date: Wed Sep 21 17:44:20 2016 +0000 +commit 3bc8180a008929f6fe98af4a56fb37d04444b417 +Author: Darren Tucker +Date: Fri Dec 16 15:02:24 2016 +1100 - upstream commit - - cast uint64_t for printf + Add missing monitor.h include. - Upstream-ID: 76d23e89419ccbd2320f92792a6d878211666ac1 + Fixes warning pointed out by Zev Weiss -commit 5f63ab474f58834feca4f35c498be03b7dd38a16 +commit 410681f9015d76cc7b137dd90dac897f673244a0 Author: djm@openbsd.org -Date: Wed Sep 21 17:03:54 2016 +0000 +Date: Fri Dec 16 02:48:55 2016 +0000 upstream commit - disable tests for affirmative negated match after backout of - match change + revert to rev1.2; the new bits in this test depend on changes + to ssh that aren't yet committed - Upstream-Regress-ID: acebb8e5042f03d66d86a50405c46c4de0badcfd + Upstream-Regress-ID: 828ffc2c7afcf65d50ff2cf3dfc47a073ad39123 -commit a5ad3a9db5a48f350f257a67b62fafd719ecb7e0 -Author: djm@openbsd.org -Date: Wed Sep 21 16:55:42 2016 +0000 +commit 2f2ffa4fbe4b671bbffa0611f15ba44cff64d58e +Author: dtucker@openbsd.org +Date: Fri Dec 16 01:06:27 2016 +0000 upstream commit - Revert two recent changes to negated address matching. The - new behaviour offers unintuitive surprises. We'll find a better way to deal - with single negated matches. - - match.c 1.31: - > fix matching for pattern lists that contain a single negated match, - > e.g. "Host !example" - > - > report and patch from Robin Becker. bz#1918 ok dtucker@ - - addrmatch.c 1.11: - > fix negated address matching where the address list consists of a - > single negated match, e.g. "Match addr !192.20.0.1" - > - > Report and patch from Jakub Jelen. bz#2397 ok dtucker@ + Move the "stop sshd" code into its own helper function. + Patch from Zev Weiss , ok djm@ - Upstream-ID: ec96c770f0f5b9a54e5e72fda25387545e9c80c6 + Upstream-Regress-ID: a113dea77df5bd97fb4633ea31f3d72dbe356329 -commit 119b7a2ca0ef2bf3f81897ae10301b8ca8cba844 +commit e15e7152331e3976b35475fd4e9c72897ad0f074 Author: djm@openbsd.org -Date: Wed Sep 21 01:35:12 2016 +0000 +Date: Fri Dec 16 01:01:07 2016 +0000 upstream commit - test all the AuthorizedPrincipalsCommand % expansions + regression test for certificates along with private key + with no public half. bz#2617, mostly from Adam Eijdenberg - Upstream-Regress-ID: 0a79a84dfaa59f958e46b474c3db780b454d30e3 + Upstream-Regress-ID: 2e74dc2c726f4dc839609b3ce045466b69f01115 -commit bfa9d969ab6235d4938ce069d4db7e5825c56a19 -Author: djm@openbsd.org -Date: Wed Sep 21 01:34:45 2016 +0000 +commit 9a70ec085faf6e55db311cd1a329f1a35ad2a500 +Author: dtucker@openbsd.org +Date: Thu Dec 15 23:50:37 2016 +0000 upstream commit - add a way for principals command to get see key ID and serial - too + Use $SUDO to read pidfile in case root's umask is + restricted. From portable. - Upstream-ID: 0d30978bdcf7e8eaeee4eea1b030eb2eb1823fcb + Upstream-Regress-ID: f6b1c7ffbc5a0dfb7d430adb2883344899174a98 -commit 920585b826af1c639e4ed78b2eba01fd2337b127 -Author: djm@openbsd.org -Date: Fri Sep 16 06:09:31 2016 +0000 +commit fe06b68f824f8f55670442fb31f2c03526dd326c +Author: dtucker@openbsd.org +Date: Thu Dec 15 21:29:05 2016 +0000 upstream commit - add a note on kexfuzz' limitations + Add missing braces in DenyUsers code. Patch from zev at + bewilderbeest.net, ok deraadt@ - Upstream-Regress-ID: 03804d4a0dbc5163e1a285a4c8cc0a76a4e864ec + Upstream-ID: d747ace338dcf943b077925f90f85f789714b54e -commit 0445ff184080b196e12321998b4ce80b0f33f8d1 -Author: djm@openbsd.org -Date: Fri Sep 16 01:01:41 2016 +0000 +commit dcc7d74242a574fd5c4afbb4224795b1644321e7 +Author: dtucker@openbsd.org +Date: Thu Dec 15 21:20:41 2016 +0000 upstream commit - fix for newer modp DH groups - (diffie-hellman-group14-sha256 etc) + Fix text in error message. Patch from zev at + bewilderbeest.net. - Upstream-Regress-ID: fe942c669959462b507516ae1634fde0725f1c68 + Upstream-ID: deb0486e175e7282f98f9a15035d76c55c84f7f6 -commit 28652bca29046f62c7045e933e6b931de1d16737 -Author: markus@openbsd.org -Date: Mon Sep 19 19:02:19 2016 +0000 +commit b737e4d7433577403a31cff6614f6a1b0b5e22f4 +Author: djm@openbsd.org +Date: Wed Dec 14 00:36:34 2016 +0000 upstream commit - move inbound NEWKEYS handling to kex layer; otherwise - early NEWKEYS causes NULL deref; found by Robert Swiecki/honggfuzz; fixed - with & ok djm@ + disable Unix-domain socket forwarding when privsep is + disabled - Upstream-ID: 9a68b882892e9f51dc7bfa9f5a423858af358b2f + Upstream-ID: ab61516ae0faadad407857808517efa900a0d6d0 -commit 492710894acfcc2f173d14d1d45bd2e688df605d -Author: natano@openbsd.org -Date: Mon Sep 19 07:52:42 2016 +0000 +commit 08a1e7014d65c5b59416a0e138c1f73f417496eb +Author: djm@openbsd.org +Date: Fri Dec 9 03:04:29 2016 +0000 upstream commit - Replace two more arc4random() loops with - arc4random_buf(). - - tweaks and ok dtucker - ok deraadt + log connections dropped in excess of MaxStartups at + verbose LogLevel; bz#2613 based on diff from Tomas Kuthan; ok dtucker@ - Upstream-ID: 738d3229130ccc7eac975c190276ca6fcf0208e4 + Upstream-ID: 703ae690dbf9b56620a6018f8a3b2389ce76d92b -commit 1036356324fecc13099ac6e986b549f6219327d7 -Author: tedu@openbsd.org -Date: Sat Sep 17 18:00:27 2016 +0000 +commit 10e290ec00964b2bf70faab15a10a5574bb80527 +Author: Darren Tucker +Date: Tue Dec 13 13:51:32 2016 +1100 - upstream commit - - replace two arc4random loops with arc4random_buf ok - deraadt natano - - Upstream-ID: e18ede972d1737df54b49f011fa4f3917a403f48 + Get default of TEST_SSH_UTF8 from environment. -commit 00df97ff68a49a756d4b977cd02283690f5dfa34 -Author: djm@openbsd.org -Date: Wed Sep 14 20:11:26 2016 +0000 +commit b9b8ba3f9ed92c6220b58d70d1e6d8aa3eea1104 +Author: Darren Tucker +Date: Tue Dec 13 12:56:40 2016 +1100 - upstream commit - - take fingerprint of correct key for - AuthorizedPrincipalsCommand + Remove commented-out includes. - Upstream-ID: 553581a549cd6a3e73ce9f57559a325cc2cb1f38 + These commented-out includes have "Still needed?" comments. Since + they've been commented out for ~13 years I assert that they're not. -commit e7907c1cb938b96dd33d27c2fea72c4e08c6b2f6 -Author: djm@openbsd.org -Date: Wed Sep 14 05:42:25 2016 +0000 +commit 25275f1c9d5f01a0877d39444e8f90521a598ea0 +Author: Darren Tucker +Date: Tue Dec 13 12:54:23 2016 +1100 - upstream commit - - add %-escapes to AuthorizedPrincipalsCommand to match those - supported for AuthorizedKeysCommand (key, key type, fingerprint, etc) and a - few more to provide access to the certificate's CA key; 'looks ok' dtucker@ - - Upstream-ID: 6b00fd446dbebe67f4e4e146d2e492d650ae04eb + Add prototype for strcasestr in compat library. -commit 2b939c272a81c4d0c47badeedbcb2ba7c128ccda -Author: dtucker@openbsd.org -Date: Wed Sep 14 00:45:31 2016 +0000 +commit afec07732aa2985142f3e0b9a01eb6391f523dec +Author: Darren Tucker +Date: Tue Dec 13 10:23:03 2016 +1100 - upstream commit - - Improve test coverage of ssh-keygen -T a bit. + Add strcasestr to compat library. - Upstream-Regress-ID: 8851668c721bcc2b400600cfc5a87644cc024e72 + Fixes build on (at least) Solaris 10. -commit 44d82fc83be6c5ccd70881c2dac1a73e5050398b -Author: dtucker@openbsd.org -Date: Mon Sep 12 02:25:46 2016 +0000 +commit dda78a03af32e7994f132d923c2046e98b7c56c8 +Author: Damien Miller +Date: Mon Dec 12 13:57:10 2016 +1100 - upstream commit - - Add testcase for ssh-keygen -j, -J and -K options for - moduli screening. Does not currently test generation as that is extremely - slow. + Force Turkish locales back to C/POSIX; bz#2643 - Upstream-Regress-ID: 9de6ce801377ed3ce0a63a1413f1cd5fd3c2d062 - -commit 44e5f756d286bc3a1a5272ea484ee276ba3ac5c2 -Author: djm@openbsd.org -Date: Tue Aug 23 08:17:04 2016 +0000 - - upstream commit - - add tests for addr_match_list() + Turkish locales are unique in their handling of the letters 'i' and + 'I' (yes, they are different letters) and OpenSSH isn't remotely + prepared to deal with that. For now, the best we can do is to force + OpenSSH to use the C/POSIX locale and try to preserve the UTF-8 + encoding if possible. - Upstream-Regress-ID: fae2d1fef84687ece584738a924c7bf969616c8e + ok dtucker@ -commit 445e218878035b59c704c18406e8aeaff4c8aa25 -Author: djm@openbsd.org -Date: Mon Sep 12 23:39:34 2016 +0000 +commit c35995048f41239fc8895aadc3374c5f75180554 +Author: Darren Tucker +Date: Fri Dec 9 12:52:02 2016 +1100 - upstream commit - - handle certs in rsa_hash_alg_from_ident(), saving an - unnecessary special case elsewhere. - - Upstream-ID: 901cb081c59d6d2698b57901c427f3f6dc7397d4 + exit is in stdlib.h not unistd.h (that's _exit). -commit 130f5df4fa37cace8c079dccb690e5cafbf00751 -Author: djm@openbsd.org -Date: Mon Sep 12 23:31:27 2016 +0000 +commit d399a8b914aace62418c0cfa20341aa37a192f98 +Author: Darren Tucker +Date: Fri Dec 9 12:33:25 2016 +1100 - upstream commit - - list all supported signature algorithms in the - server-sig-algs Reported by mb AT smartftp.com in bz#2547 and (independantly) - Ron Frederick; ok markus@ - - Upstream-ID: ddf702d721f54646b11ef2cee6d916666cb685cd + Include for exit in utf8 locale test. -commit 8f750ccfc07acb8aa98be5a5dd935033a6468cfd +commit 47b8c99ab3221188ad3926108dd9d36da3b528ec Author: Darren Tucker -Date: Mon Sep 12 14:43:58 2016 +1000 +Date: Thu Dec 8 15:48:34 2016 +1100 - Remove no-op brackets to resync with upstream. + Check for utf8 local support before testing it. + + Check for utf8 local support and if not found, do not attempt to run the + utf8 tests. Suggested by djm@ -commit 7050896e7395866278c19c2ff080c26152619d1d +commit 4089fc1885b3a2822204effbb02b74e3da58240d Author: Darren Tucker -Date: Mon Sep 12 13:57:28 2016 +1000 +Date: Thu Dec 8 12:57:24 2016 +1100 - Resync ssh-keygen -W error message with upstream. + Use AC_PATH_TOOL for krb5-config. + + This will use the host-prefixed version when cross compiling; patch from + david.michael at coreos.com. -commit 43cceff82cc20413cce58ba3375e19684e62cec4 -Author: Darren Tucker -Date: Mon Sep 12 13:55:37 2016 +1000 +commit b4867e0712c89b93be905220c82f0a15e6865d1e +Author: djm@openbsd.org +Date: Tue Dec 6 07:48:01 2016 +0000 - Move ssh-keygen -W handling code to match upstream + upstream commit + + make IdentityFile successfully load and use certificates that + have no corresponding bare public key. E.g. just a private id_rsa and + certificate id_rsa-cert.pub (and no id_rsa.pub). + + bz#2617 ok dtucker@ + + Upstream-ID: c1e9699b8c0e3b63cc4189e6972e3522b6292604 -commit af48d541360b1d7737b35740a4b1ca34e1652cd9 -Author: Darren Tucker -Date: Mon Sep 12 13:52:17 2016 +1000 +commit c9792783a98881eb7ed295680013ca97a958f8ac +Author: Damien Miller +Date: Fri Nov 25 14:04:21 2016 +1100 - Move ssh-keygen -T handling code to match upstream. + Add a gnome-ssh-askpass3 target for GTK+3 version + + Based on patch from Colin Watson via bz#2640 -commit d8c3cfbb018825c6c86547165ddaf11924901c49 -Author: Darren Tucker -Date: Mon Sep 12 13:30:50 2016 +1000 +commit 7be85ae02b9de0993ce0a1d1e978e11329f6e763 +Author: Damien Miller +Date: Fri Nov 25 14:03:53 2016 +1100 - Move -M handling code to match upstream. + Make gnome-ssh-askpass2.c GTK+3-friendly + + Patch from Colin Watson via bz#2640 -commit 7b63cf6dbbfa841c003de57d1061acbf2ff22364 -Author: dtucker@openbsd.org -Date: Mon Sep 12 03:29:16 2016 +0000 +commit b9844a45c7f0162fd1b5465683879793d4cc4aaa +Author: djm@openbsd.org +Date: Sun Dec 4 23:54:02 2016 +0000 upstream commit - Spaces->tabs. + Fix public key authentication when multiple + authentication is in use. Instead of deleting and re-preparing the entire + keys list, just reset the 'used' flags; the keys list is already in a good + order (with already- tried keys at the back) - Upstream-ID: f4829dfc3f36318273f6082b379ac562eead70b7 + Analysis and patch from Vincent Brillault on bz#2642; ok dtucker@ + + Upstream-ID: 7123f12dc2f3bcaae715853035a97923d7300176 -commit 11e5e644536821ceb3bb4dd8487fbf0588522887 +commit f2398eb774075c687b13af5bc22009eb08889abe Author: dtucker@openbsd.org -Date: Mon Sep 12 03:25:20 2016 +0000 +Date: Sun Dec 4 22:27:25 2016 +0000 upstream commit - Style whitespace fix. Also happens to remove a no-op - diff with portable. + Unlink PidFile on SIGHUP and always recreate it when the + new sshd starts. Regression tests (and possibly other things) depend on the + pidfile being recreated after SIGHUP, and unlinking it means it won't contain + a stale pid if sshd fails to restart. ok djm@ markus@ - Upstream-ID: 45d90f9a62ad56340913a433a9453eb30ceb8bf3 + Upstream-ID: 132dd6dda0c77dd49d2f15b2573b5794f6160870 -commit 9136ec134c97a8aff2917760c03134f52945ff3c -Author: deraadt@openbsd.org -Date: Mon Sep 12 01:22:38 2016 +0000 +commit 85aa2efeba51a96bf6834f9accf2935d96150296 +Author: djm@openbsd.org +Date: Wed Nov 30 03:01:33 2016 +0000 upstream commit - Add MAXIMUM(), MINIMUM(), and ROUNDUP() to misc.h, then - use those definitions rather than pulling and unknown namespace - pollution. ok djm markus dtucker + test new behaviour of cert force-command restriction vs. + authorized_key/ principals - Upstream-ID: 712cafa816c9f012a61628b66b9fbd5687223fb8 + Upstream-Regress-ID: 399efa7469d40c404c0b0a295064ce75d495387c -commit f219fc8f03caca7ac82a38ed74bbd6432a1195e7 +commit 5d333131cd8519d022389cfd3236280818dae1bc Author: jmc@openbsd.org -Date: Wed Sep 7 18:39:24 2016 +0000 +Date: Wed Nov 30 06:54:26 2016 +0000 upstream commit - sort; from matthew martin + tweak previous; while here fix up FILES and AUTHORS; - Upstream-ID: 73cec7f7ecc82d37a4adffad7745e4684de67ce7 + Upstream-ID: 93f6e54086145a75df8d8ec7d8689bdadbbac8fa -commit 06ce56b05def9460aecc7cdb40e861a346214793 -Author: markus@openbsd.org -Date: Tue Sep 6 09:22:56 2016 +0000 +commit 786d5994da79151180cb14a6cf157ebbba61c0cc +Author: djm@openbsd.org +Date: Wed Nov 30 03:07:37 2016 +0000 upstream commit - ssh_set_newkeys: print correct block counters on - rekeying; ok djm@ + add a whitelist of paths from which ssh-agent will load + (via ssh-pkcs11-helper) a PKCS#11 module; ok markus@ - Upstream-ID: 32bb7a9cb9919ff5bab28d50ecef3a2b2045dd1e + Upstream-ID: fe79769469d9cd6d26fe0dc15751b83ef2a06e8f -commit e5e8d9114ac6837a038f4952994ca95a97fafe8d -Author: markus@openbsd.org -Date: Tue Sep 6 09:14:05 2016 +0000 +commit 7844f357cdd90530eec81340847783f1f1da010b +Author: djm@openbsd.org +Date: Wed Nov 30 03:00:05 2016 +0000 upstream commit - update ext_info_c every time we receive a kexinit msg; - fixes sending of ext_info if privsep is disabled; report Aris Adamantiadis & - Mancha; ok djm@ + Add a sshd_config DisableForwaring option that disables + X11, agent, TCP, tunnel and Unix domain socket forwarding, as well as + anything else we might implement in the future. - Upstream-ID: 2ceaa1076e19dbd3542254b4fb8e42d608f28856 + This, like the 'restrict' authorized_keys flag, is intended to be a + simple and future-proof way of restricting an account. Suggested as + a complement to 'restrict' by Jann Horn; ok markus@ + + Upstream-ID: 203803f66e533a474086b38a59ceb4cf2410fcf7 -commit da95318dbedbaa1335323dba370975c2f251afd8 +commit fd6dcef2030d23c43f986d26979f84619c10589d Author: djm@openbsd.org -Date: Mon Sep 5 14:02:42 2016 +0000 +Date: Wed Nov 30 02:57:40 2016 +0000 upstream commit - remove 3des-cbc from the client's default proposal; - 64-bit block ciphers are not safe in 2016 and we don't want to wait until - attacks like sweet32 are extended to SSH. + When a forced-command appears in both a certificate and + an authorized keys/principals command= restriction, refuse to accept the + certificate unless they are identical. - As 3des-cbc was the only mandatory cipher in the SSH RFCs, this may - cause problems connecting to older devices using the defaults, but - it's highly likely that such devices already need explicit - configuration for KEX and hostkeys anyway. + The previous (documented) behaviour of having the certificate forced- + command override the other could be a bit confused and more error-prone. - ok deraadt, markus, dtucker + Pointed out by Jann Horn of Project Zero; ok dtucker@ - Upstream-ID: a505dfe65c6733af0f751b64cbc4bb7e0761bc2f + Upstream-ID: 79d811b6eb6bbe1221bf146dde6928f92d2cd05f -commit b33ad6d997d36edfea65e243cd12ccd01f413549 -Author: djm@openbsd.org -Date: Mon Sep 5 13:57:31 2016 +0000 +commit 7fc4766ac78abae81ee75b22b7550720bfa28a33 +Author: dtucker@openbsd.org +Date: Wed Nov 30 00:28:31 2016 +0000 upstream commit - enforce expected request flow for GSSAPI calls; thanks to - Jakub Jelen for testing; ok markus@ + On startup, check to see if sshd is already daemonized + and if so, skip the call to daemon() and do not rewrite the PidFile. This + means that when sshd re-execs itself on SIGHUP the process ID will no longer + change. Should address bz#2641. ok djm@ markus@. - Upstream-ID: d4bc0e70e1be403735d3d9d7e176309b1fd626b9 + Upstream-ID: 5ea0355580056fb3b25c1fd6364307d9638a37b9 -commit 0bb2980260fb24e5e0b51adac471395781b66261 -Author: Darren Tucker -Date: Mon Sep 12 11:07:00 2016 +1000 +commit c9f880c195c65f1dddcbc4ce9d6bfea7747debcc +Author: Damien Miller +Date: Wed Nov 30 13:51:49 2016 +1100 - Restore ssh-keygen's -J and -j option handling. + factor out common PRNG reseed before privdrop - These were incorrectly removed in the 1d9a2e28 sync commit. + Add a call to RAND_poll() to ensure than more than pid+time gets + stirred into child processes states. Prompted by analysis from Jann + Horn at Project Zero. ok dtucker@ -commit 775f8a23f2353f5869003c57a213d14b28e0736e -Author: Damien Miller -Date: Wed Aug 31 10:48:07 2016 +1000 +commit 79e4829ec81dead1b30999e1626eca589319a47f +Author: dtucker@openbsd.org +Date: Fri Nov 25 03:02:01 2016 +0000 - tighten PAM monitor calls + upstream commit - only allow kbd-interactive ones when that authentication method is - enabled. Prompted by Solar Designer + Allow PuTTY interop tests to run unattended. bz#2639, + patch from cjwatson at debian.org. + + Upstream-Regress-ID: 4345253558ac23b2082aebabccd48377433b6fe0 -commit 7fd0ea8a1db4bcfb3d8cd9df149e5d571ebea1f4 -Author: djm@openbsd.org -Date: Tue Aug 30 07:50:21 2016 +0000 +commit 504c3a9a1bf090f6b27260fc3e8ea7d984d163dc +Author: dtucker@openbsd.org +Date: Fri Nov 25 02:56:49 2016 +0000 upstream commit - restrict monitor auth calls to be allowed only when their - respective authentication methods are enabled in the configuration. - - prompted by Solar Designer; ok markus dtucker + Reverse args to sshd-log-wrapper. Matches change in + portable, where it allows sshd do be optionally run under Valgrind. - Upstream-ID: 6eb3f89332b3546d41d6dbf5a8e6ff920142b553 + Upstream-Regress-ID: b438d1c6726dc5caa2a45153e6103a0393faa906 -commit b38b95f5bcc52278feb839afda2987933f68ff96 -Author: Damien Miller -Date: Mon Aug 29 11:47:07 2016 +1000 +commit bd13017736ec2f8f9ca498fe109fb0035f322733 +Author: dtucker@openbsd.org +Date: Fri Nov 25 02:49:18 2016 +0000 - Tighten monitor state-machine flow for PAM calls + upstream commit - (attack surface reduction) + Fix typo in trace message; from portable. + + Upstream-Regress-ID: 4c4a2ba0d37faf5fd230a91b4c7edb5699fbd73a -commit dc664d1bd0fc91b24406a3e9575b81c285b8342b -Author: djm@openbsd.org -Date: Sun Aug 28 22:28:12 2016 +0000 +commit 7da751d8b007c7f3e814fd5737c2351440d78b4c +Author: tb@openbsd.org +Date: Tue Nov 1 13:43:27 2016 +0000 upstream commit - fix uninitialised optlen in getsockopt() call; harmless - on Unix/BSD but potentially crashy on Cygwin. Reported by James Slepicka ok - deraadt@ + Clean up MALLOC_OPTIONS. For the unittests, move + MALLOC_OPTIONS and TEST_ENV to unittets/Makefile.inc. - Upstream-ID: 1987ccee508ba5b18f016c85100d7ac3f70ff965 + ok otto + + Upstream-Regress-ID: 890d497e0a38eeddfebb11cc429098d76cf29f12 -commit 5bcc1e2769f7d6927d41daf0719a9446ceab8dd7 -Author: guenther@openbsd.org -Date: Sat Aug 27 04:05:12 2016 +0000 +commit 36f58e68221bced35e06d1cca8d97c48807a8b71 +Author: tb@openbsd.org +Date: Mon Oct 31 23:45:08 2016 +0000 upstream commit - Pull in for struct timeval + Remove the obsolete A and P flags from MALLOC_OPTIONS. - ok deraadt@ + ok dtucker - Upstream-ID: ae34525485a173bccd61ac8eefeb91c57e3b7df6 + Upstream-Regress-ID: 6cc25024c8174a87e5734a0dc830194be216dd59 -commit fa4a4c96b19127dc2fd4e92f20d99c0c7f34b538 -Author: guenther@openbsd.org -Date: Sat Aug 27 04:04:56 2016 +0000 +commit b0899ee26a6630883c0f2350098b6a35e647f512 +Author: dtucker@openbsd.org +Date: Tue Nov 29 03:54:50 2016 +0000 upstream commit - Pull in for NULL - - ok deraadt@ + Factor out code to disconnect from controlling terminal + into its own function. ok djm@ - Upstream-ID: 7baa6a0f1e049bb3682522b4b95a26c866bfc043 + Upstream-ID: 39fd9e8ebd7222615a837312face5cc7ae962885 -commit ae363d74ccc1451185c0c8bd4631e28c67c7fd36 +commit 54d022026aae4f53fa74cc636e4a032d9689b64d Author: djm@openbsd.org -Date: Thu Aug 25 23:57:54 2016 +0000 +Date: Fri Nov 25 23:24:45 2016 +0000 upstream commit - add a sIgnore opcode that silently ignores options and - use it to suppress noisy deprecation warnings for the Protocol directive. + use sshbuf_allocate() to pre-allocate the buffer used for + loading keys. This avoids implicit realloc inside the buffer code, which + might theoretically leave fragments of the key on the heap. This doesn't + appear to happen in practice for normal sized keys, but was observed for + novelty oversize ones. - req henning, ok markus + Pointed out by Jann Horn of Project Zero; ok markus@ - Upstream-ID: 9fe040aca3d6ff393f6f7e60045cdd821dc4cbe0 + Upstream-ID: d620e1d46a29fdea56aeadeda120879eddc60ab1 -commit a94c60306643ae904add6e8ed219e4be3494255c +commit a9c746088787549bb5b1ae3add7d06a1b6d93d5e Author: djm@openbsd.org -Date: Thu Aug 25 23:56:51 2016 +0000 +Date: Fri Nov 25 23:22:04 2016 +0000 upstream commit - remove superfluous NOTREACHED comment + split allocation out of sshbuf_reserve() into a separate + sshbuf_allocate() function; ok markus@ - Upstream-ID: a7485c1f1be618e8c9e38fd9be46c13b2d03b90c + Upstream-ID: 11b8a2795afeeb1418d508a2c8095b3355577ec2 -commit fc041c47144ce28cf71353124a8a5d183cd6a251 -Author: otto@openbsd.org -Date: Tue Aug 23 16:21:45 2016 +0000 +commit f0ddedee460486fa0e32fefb2950548009e5026e +Author: markus@openbsd.org +Date: Wed Nov 23 23:14:15 2016 +0000 upstream commit - fix previous, a condition was modified incorrectly; ok - markus@ deraadt@ + allow ClientAlive{Interval,CountMax} in Match; ok dtucker, + djm - Upstream-ID: c443e339768e7ed396dff3bb55f693e7d3641453 + Upstream-ID: 8beb4c1eadd588f1080b58932281983864979f55 -commit 23555eb13a9b0550371a16dcf8beaab7a5806a64 +commit 1a6f9d2e2493d445cd9ee496e6e3c2a2f283f66a Author: djm@openbsd.org -Date: Tue Aug 23 08:17:42 2016 +0000 +Date: Tue Nov 8 22:04:34 2016 +0000 upstream commit - downgrade an error() to a debug2() to match similar cases - in addr_match_list() + unbreak DenyUsers; reported by henning@ - Upstream-ID: 07c3d53e357214153d9d08f234411e0d1a3d6f5c + Upstream-ID: 1c67d4148f5e953c35acdb62e7c08ae8e33f7cb2 -commit a39627134f6d90e7009eeb14e9582ecbc7a99192 +commit 010359b32659f455fddd2bd85fd7cc4d7a3b994a Author: djm@openbsd.org -Date: Tue Aug 23 06:36:23 2016 +0000 +Date: Sun Nov 6 05:46:37 2016 +0000 upstream commit - remove Protocol directive from client/server configs that - causes spammy deprecation warnings + Validate address ranges for AllowUser/DenyUsers at + configuration load time and refuse to accept bad ones. It was previously + possible to specify invalid CIDR address ranges (e.g. djm@127.1.2.3/55) and + these would always match. - hardcode SSH_PROTOCOLS=2, since that's all we support on the server - now (the client still may support both, so it could get confused) + Thanks to Laurence Parry for a detailed bug report. ok markus (for + a previous diff version) - Upstream-Regress-ID: c16662c631af51633f9fd06aca552a70535de181 - -commit 6ee4f1c01ee31e65245881d49d4bccf014956066 -Author: Damien Miller -Date: Tue Aug 23 16:33:48 2016 +1000 - - hook match and utf8 unittests up to Makefile + Upstream-ID: 9dfcdd9672b06e65233ea4434c38226680d40bfb -commit 114efe2bc0dd2842d997940a833f115e6fc04854 +commit efb494e81d1317209256b38b49f4280897c61e69 Author: djm@openbsd.org -Date: Fri Aug 19 06:44:13 2016 +0000 +Date: Fri Oct 28 03:33:52 2016 +0000 upstream commit - add tests for matching functions + Improve pkcs11_add_provider() logging: demote some + excessively verbose error()s to debug()s, include PKCS#11 provider name and + slot in log messages where possible. bz#2610, based on patch from Jakub Jelen - Upstream-Regress-ID: 0869d4f5c5d627c583c6a929d69c17d5dd65882c + Upstream-ID: 3223ef693cfcbff9079edfc7e89f55bf63e1973d -commit 857568d2ac81c14bcfd625b27536c1e28c992b3c -Author: Damien Miller -Date: Tue Aug 23 14:32:37 2016 +1000 +commit 5ee3fb5affd7646f141749483205ade5fc54adaf +Author: Darren Tucker +Date: Tue Nov 1 08:12:33 2016 +1100 - removing UseLogin bits from configure.ac + Use ptrace(PT_DENY_ATTACH, ..) on OS X. -commit cc182d01cef8ca35a1d25ea9bf4e2ff72e588208 -Author: djm@openbsd.org -Date: Tue Aug 23 03:24:10 2016 +0000 +commit 315d2a4e674d0b7115574645cb51f968420ebb34 +Author: Damien Miller +Date: Fri Oct 28 14:34:07 2016 +1100 - upstream commit + Unbreak AES-CTR ciphers on old (~0.9.8) OpenSSL - fix negated address matching where the address list - consists of a single negated match, e.g. "Match addr !192.20.0.1" + ok dtucker@ + +commit a9ff3950b8e80ff971b4d44bbce96df27aed28af +Author: Darren Tucker +Date: Fri Oct 28 14:26:58 2016 +1100 + + Move OPENSSL_NO_RIPEMD160 to compat. - Report and patch from Jakub Jelen. bz#2397 ok dtucker@ + Move OPENSSL_NO_RIPEMD160 to compat and add ifdefs to mac.c around the + ripemd160 MACs. + +commit bce58885160e5db2adda3054c3b81fe770f7285a +Author: Darren Tucker +Date: Fri Oct 28 13:52:31 2016 +1100 + + Check if RIPEMD160 is disabled in OpenSSL. + +commit d924640d4c355d1b5eca1f4cc60146a9975dbbff +Author: Darren Tucker +Date: Fri Oct 28 13:38:19 2016 +1100 + + Skip ssh1 specfic ciphers. - Upstream-ID: 01dcac3f3e6ca47518cf293e31c73597a4bb40d8 + cipher-3des1.c and cipher-bf1.c are specific to sshv1 so don't even try + to compile them when Protocol 1 is not enabled. -commit 4067ec8a4c64ccf16250c35ff577b4422767da64 -Author: djm@openbsd.org -Date: Tue Aug 23 03:22:49 2016 +0000 +commit 79d078e7a49caef746516d9710ec369ba45feab6 +Author: jsg@openbsd.org +Date: Tue Oct 25 04:08:13 2016 +0000 upstream commit - fix matching for pattern lists that contain a single - negated match, e.g. "Host !example" + Fix logic in add_local_forward() that inverted a test + when code was refactored out into bind_permitted(). This broke ssh port + forwarding for non-priv ports as a non root user. - report and patch from Robin Becker. bz#1918 ok dtucker@ + ok dtucker@ 'looks good' deraadt@ - Upstream-ID: 05a0cb323ea4bc20e98db099b42c067bfb9ea1ea + Upstream-ID: ddb8156ca03cc99997de284ce7777536ff9570c9 -commit 83b581862a1dbb06fc859959f829dde2654aef3c -Author: djm@openbsd.org -Date: Fri Aug 19 03:18:06 2016 +0000 +commit a903e315dee483e555c8a3a02c2946937f9b4e5d +Author: dtucker@openbsd.org +Date: Mon Oct 24 01:09:17 2016 +0000 upstream commit - remove UseLogin option and support for having /bin/login - manage login sessions; ok deraadt markus dtucker + Remove dead breaks, found via opencoverage.net. ok + deraadt@ - Upstream-ID: bea7213fbf158efab7e602d9d844fba4837d2712 + Upstream-ID: ad9cc655829d67fad219762810770787ba913069 -commit ffe6549c2f7a999cc5264b873a60322e91862581 -Author: naddy@openbsd.org -Date: Mon Aug 15 12:32:04 2016 +0000 +commit b4e96b4c9bea4182846e4942ba2048e6d708ee54 +Author: Darren Tucker +Date: Wed Oct 26 08:43:25 2016 +1100 + + Use !=NULL instead of >0 for getdefaultproj. + + getdefaultproj() returns a pointer so test it for NULL inequality + instead of >0. Fixes compiler warning and is more correct. Patch from + David Binderman. + +commit 1c4ef0b808d3d38232aeeb1cebb7e9a43def42c5 +Author: dtucker@openbsd.org +Date: Sun Oct 23 22:04:05 2016 +0000 upstream commit - Catch up with the SSH1 code removal and delete all - mention of protocol 1 particularities, key files and formats, command line - options, and configuration keywords from the server documentation and - examples. ok jmc@ + Factor out "can bind to low ports" check into its own function. This will + make it easier for Portable to support platforms with permissions models + other than uid==0 (eg bz#2625). ok djm@, "doesn't offend me too much" + deraadt@. - Upstream-ID: 850328854675b4b6a0d4a90f0b4a9dd9ca4e905f + Upstream-ID: 86213df4183e92b8f189a6d2dac858c994bfface -commit c38ea634893a1975dbbec798fb968c9488013f4a -Author: naddy@openbsd.org -Date: Mon Aug 15 12:27:56 2016 +0000 +commit 0b9ee623d57e5de7e83e66fd61a7ba9a5be98894 +Author: dtucker@openbsd.org +Date: Wed Oct 19 23:21:56 2016 +0000 upstream commit - Remove more SSH1 server code: * Drop sshd's -k option. * - Retire configuration keywords that only apply to protocol 1, as well as the - "protocol" keyword. * Remove some related vestiges of protocol 1 support. - - ok markus@ + When tearing down ControlMaster connecctions, don't + pollute stderr when LogLevel=quiet. Patch from Tim Kuijsten via tech@. - Upstream-ID: 9402f82886de917779db12f8ee3f03d4decc244d + Upstream-ID: d9b3a68b2a7c2f2fc7f74678e29a4618d55ceced -commit 33ba55d9e358c07f069e579bfab80eccaaad52cb +commit 09e6a7d8354224933febc08ddcbc2010f542284e Author: Darren Tucker -Date: Wed Aug 17 16:26:04 2016 +1000 +Date: Mon Oct 24 09:06:18 2016 +1100 - Only check for prctl once. + Wrap stdint.h include in ifdef. -commit 976ba8a8fd66a969bf658280c1e5adf694cc2fc6 +commit 08d9e9516e587b25127545c029e5464b2e7f2919 Author: Darren Tucker -Date: Wed Aug 17 15:33:10 2016 +1000 +Date: Fri Oct 21 09:46:46 2016 +1100 - Fix typo. + Fix formatting. -commit 9abf84c25ff4448891edcde60533a6e7b2870de1 +commit 461f50e7ab8751d3a55e9158c44c13031db7ba1d Author: Darren Tucker -Date: Wed Aug 17 14:25:43 2016 +1000 +Date: Fri Oct 21 06:55:58 2016 +1100 - Correct LDFLAGS for clang example. + Update links to https. - --with-ldflags isn't used until after the -ftrapv test, so mention - LDFLAGS instead for now. + www.openssh.com now supports https and ftp.openbsd.org no longer + supports ftp. Make all links to these https. -commit 1e8013a17ff11e3c6bd0012fb1fc8d5f1330eb21 +commit dd4e7212a6141f37742de97795e79db51e4427ad Author: Darren Tucker -Date: Wed Aug 17 14:08:42 2016 +1000 +Date: Fri Oct 21 06:48:46 2016 +1100 - Remove obsolete CVS $Id from source files. + Update host key generation examples. - Since -portable switched to git the CVS $Id tags are no longer being - updated and are becoming increasingly misleading. Remove them. + Remove ssh1 host key generation, add ssh-keygen -A -commit adab758242121181700e48b4f6c60d6b660411fe +commit 6d49ae82634c67e9a4d4af882bee20b40bb8c639 Author: Darren Tucker -Date: Wed Aug 17 13:40:58 2016 +1000 +Date: Fri Oct 21 05:22:55 2016 +1100 - Remove now-obsolete CVS $Id tags from text files. + Update links. - Since -portable switched to git, the CVS $Id tags are no longer being - updated and are becoming increasingly misleading. Remove them. + Make links to openssh.com HTTPS now that it's supported, point release + notes link to the HTML release notes page, and update a couple of other + links and bits of text. -commit 560c0068541315002ec4c1c00a560bbd30f2d671 +commit fe0d1ca6ace06376625084b004ee533f2c2ea9d6 Author: Darren Tucker -Date: Wed Aug 17 13:38:30 2016 +1000 +Date: Thu Oct 20 03:42:09 2016 +1100 - Add a section for compiler specifics. + Remote channels .orig and .rej files. - Add a section for compiler specifics and document the runtime requirements - for clang's integer sanitization. + These files were incorrectly added during an OpenBSD sync. -commit a8fc0f42e1eda2fa3393d1ea5e61322d5e07a9cd -Author: Darren Tucker -Date: Wed Aug 17 13:35:43 2016 +1000 +commit 246aa842a4ad368d8ce030495e657ef3a0e1f95c +Author: dtucker@openbsd.org +Date: Tue Oct 18 17:32:54 2016 +0000 - Test multiplying two long long ints. + upstream commit - When using clang with -ftrapv or -sanitize=integer the tests would pass - but linking would fail with "undefined reference to __mulodi4". - Explicitly test for this before enabling -trapv. - -commit a1cc637e7e11778eb727559634a6ef1c19c619f6 -Author: Damien Miller -Date: Tue Aug 16 14:47:34 2016 +1000 - - add a --with-login-program configure argument + Remove channel_input_port_forward_request(); the only caller + was the recently-removed SSH1 server code so it's now dead code. ok markus@ - Saves messing around with LOGIN_PROGRAM env var, which come - packaging environments make hard to do during configure phase. + Upstream-ID: 05453983230a1f439562535fec2818f63f297af9 -commit 8bd81e1596ab1bab355146cb65e82fb96ade3b23 -Author: Damien Miller -Date: Tue Aug 16 13:30:56 2016 +1000 +commit 2c6697c443d2c9c908260eed73eb9143223e3ec9 +Author: millert@openbsd.org +Date: Tue Oct 18 12:41:22 2016 +0000 - add --with-pam-service to specify PAM service name + upstream commit - Saves messing around with CFLAGS to do it. - -commit 74433a19bb6f4cef607680fa4d1d7d81ca3826aa -Author: Damien Miller -Date: Tue Aug 16 13:28:23 2016 +1000 - - fix false positives when compiled with msan + Install a signal handler for tty-generated signals and + wait for the ssh child to suspend before suspending sftp. This lets ssh + restore the terminal mode as needed when it is suspended at the password + prompt. OK dtucker@ - Our explicit_bzero successfully confused clang -fsanitize-memory - in to thinking that memset is never called to initialise memory. - Ensure that it is called in a way that the compiler recognises. + Upstream-ID: a31c1f42aa3e2985dcc91e46e6a17bd22e372d69 -commit 6cb6dcffe1a2204ba9006de20f73255c268fcb6b -Author: markus@openbsd.org -Date: Sat Aug 13 17:47:40 2016 +0000 +commit fd2a8f1033fa2316fff719fd5176968277560158 +Author: jmc@openbsd.org +Date: Sat Oct 15 19:56:25 2016 +0000 upstream commit - remove ssh1 server code; ok djm@ + various formatting fixes, specifically removing Dq; - Upstream-ID: c24c0c32c49b91740d5a94ae914fb1898ea5f534 + Upstream-ID: 81e85df2b8e474f5f93d66e61d9a4419ce87347c -commit 42d47adc5ad1187f22c726cbc52e71d6b1767ca2 -Author: jca@openbsd.org -Date: Fri Aug 12 19:19:04 2016 +0000 +commit 8f866d8a57b9a2dc5dd04504e27f593b551618e3 +Author: Darren Tucker +Date: Wed Oct 19 03:26:09 2016 +1100 - upstream commit - - Use 2001:db8::/32, the official IPv6 subnet for - configuration examples. - - This makes the IPv6 example consistent with IPv4, and removes a dubious - mention of a 6bone subnet. - - ok sthen@ millert@ + Import readpassphrase.c rev 1.26. - Upstream-ID: b027f3d0e0073419a132fd1bf002e8089b233634 + Author: miller@openbsd.org: + Avoid generate SIGTTOU when restoring the terminal mode. If we get + SIGTTOU it means the process is not in the foreground process group + which, in most cases, means that the shell has taken control of the tty. + Requiring the user the fg the process in this case doesn't make sense + and can result in both SIGTSTP and SIGTTOU being sent which can lead to + the process being suspended again immediately after being brought into + the foreground. -commit b61f53c0c3b43c28e013d3b3696d64d1c0204821 -Author: dtucker@openbsd.org -Date: Thu Aug 11 01:42:11 2016 +0000 +commit f901440cc844062c9bab0183d133f7ccc58ac3a5 +Author: Darren Tucker +Date: Wed Oct 19 03:23:16 2016 +1100 - upstream commit + Import readpassphrase.c rev 1.25. - Update moduli file. + Wrap so internal calls go direct and + readpassphrase is weak. - Upstream-ID: 6da9a37f74aef9f9cc639004345ad893cad582d8 + (DEF_WEAK is a no-op in portable.) -commit f217d9bd42d306f69f56335231036b44502d8191 +commit 032147b69527e5448a511049b2d43dbcae582624 Author: Darren Tucker -Date: Thu Aug 11 11:42:48 2016 +1000 +Date: Sat Oct 15 05:51:12 2016 +1100 - Import updated moduli. + Move DEF_WEAK into defines.h. + + As well pull in more recent changes from OpenBSD these will start to + arrive so put it where the definition is shared. -commit 67dca60fbb4923b7a11c1645b90a5ca57c03d8be -Author: dtucker@openbsd.org -Date: Mon Aug 8 22:40:57 2016 +0000 +commit e0259a82ddd950cfb109ddee86fcebbc09c6bd04 +Author: Darren Tucker +Date: Sat Oct 15 04:34:46 2016 +1100 - upstream commit + Remove do_pam_set_tty which is dead code. - Improve error message for overlong ControlPath. ok markus@ - djm@ + The callers of do_pam_set_tty were removed in 2008, so this is now dead + code. bz#2604, pointed out by jjelen at redhat.com. + +commit ca04de83f210959ad2ed870a30ba1732c3ae00e3 +Author: Damien Miller +Date: Thu Oct 13 18:53:43 2016 +1100 + + unbreak principals-command test - Upstream-ID: aed374e2e88dd3eb41390003e5303d0089861eb5 + Undo inconsistetly updated variable name. -commit 4706c1d8c15cd5565b59512853c2da9bd4ca26c9 +commit 1723ec92eb485ce06b4cbf49712d21975d873909 Author: djm@openbsd.org -Date: Wed Aug 3 05:41:57 2016 +0000 +Date: Tue Oct 11 21:49:54 2016 +0000 upstream commit - small refactor of cipher.c: make ciphercontext opaque to - callers feedback and ok markus@ + fix the KEX fuzzer - the previous method of obtaining the + packet contents was broken. This now uses the new per-packet input hook, so + it sees exact post-decrypt packets and doesn't have to pass packet integrity + checks. ok markus@ - Upstream-ID: 094849f8be68c3bdad2c0f3dee551ecf7be87f6f + Upstream-Regress-ID: 402fb6ffabd97de590e8e57b25788949dce8d2fd -commit e600348a7afd6325cc5cd783cb424065cbc20434 -Author: dtucker@openbsd.org -Date: Wed Aug 3 04:23:55 2016 +0000 +commit 09f997893f109799cddbfce6d7e67f787045cbb2 +Author: natano@openbsd.org +Date: Thu Oct 6 09:31:38 2016 +0000 upstream commit - Fix bug introduced in rev 1.467 which causes - "buffer_get_bignum_ret: incomplete message" errors when built with WITH_SSH1 - and run such that no Protocol 1 ephemeral host key is generated (eg "Protocol - 2", no SSH1 host key supplied). Reported by rainer.laatsch at t-online.de, - ok deraadt@ + Move USER out of the way to unbreak the BUILDUSER + mechanism. ok tb - Upstream-ID: aa6b132da5c325523aed7989cc5a320497c919dc + Upstream-Regress-ID: 74ab9687417dd071d62316eaadd20ddad1d5af3c -commit d7e7348e72f9b203189e3fffb75605afecba4fda -Author: djm@openbsd.org -Date: Wed Jul 27 23:18:12 2016 +0000 +commit 3049a012c482a7016f674db168f23fd524edce27 +Author: bluhm@openbsd.org +Date: Fri Sep 30 11:55:20 2016 +0000 upstream commit - better bounds check on iovcnt (we only ever use fixed, - positive values) + In ssh tests set REGRESS_FAIL_EARLY with ?= so that the + environment can change it. OK djm@ - Upstream-ID: 9baa6eb5cd6e30c9dc7398e5fe853721a3a5bdee - -commit 5faa52d295f764562ed6dd75c4a4ce9134ae71e3 -Author: Darren Tucker -Date: Tue Aug 2 15:22:40 2016 +1000 - - Use tabs consistently inside "case $host". + Upstream-Regress-ID: 77bcb50e47b68c7209c7f0a5a020d73761e5143b -commit 20e5e8ba9c5d868d897896190542213a60fffbd2 -Author: Darren Tucker -Date: Tue Aug 2 12:16:34 2016 +1000 +commit 39af7b444db28c1cb01b7ea468a4f574a44f375b +Author: djm@openbsd.org +Date: Tue Oct 11 21:47:45 2016 +0000 - Explicitly test for broken strnvis. - - NetBSD added an strnvis and unfortunately made it incompatible with the - existing one in OpenBSD and Linux's libbsd (the former having existed - for over ten years). Despite this incompatibility being reported during - development (see http://gnats.netbsd.org/44977) they still shipped it. - Even more unfortunately FreeBSD and later MacOS picked up this incompatible - implementation. Try to detect this mess, and assume the only safe option - if we're cross compiling. + upstream commit - OpenBSD 2.9 (2001): strnvis(char *dst, const char *src, size_t dlen, int flag); - NetBSD 6.0 (2012): strnvis(char *dst, size_t dlen, const char *src, int flag); + Add a per-packet input hook that is called with the + decrypted packet contents. This will be used for fuzzing; ok markus@ - ok djm@ - -commit b0b48beab1b74100b61ecbadb9140c9ab4c2ea8c -Author: Damien Miller -Date: Tue Aug 2 11:06:23 2016 +1000 - - update recommended autoconf version + Upstream-ID: a3221cee6b1725dd4ae1dd2c13841b4784cb75dc -commit 23902e31dfd18c6d7bb41ccd73de3b5358a377da -Author: Damien Miller -Date: Tue Aug 2 10:48:04 2016 +1000 +commit ec165c392ca54317dbe3064a8c200de6531e89ad +Author: markus@openbsd.org +Date: Mon Oct 10 19:28:48 2016 +0000 - update config.guess and config.sub to current + upstream commit - upstream commit 562f3512b3911ba0c77a7f68214881d1f241f46e + Unregister the KEXINIT handler after message has been + received. Otherwise an unauthenticated peer can repeat the KEXINIT and cause + allocation of up to 128MB -- until the connection is closed. Reported by + shilei-c at 360.cn + + Upstream-ID: 43649ae12a27ef94290db16d1a98294588b75c05 -commit dd1031b78b83083615b68d7163c44f4408635be2 +commit 29d40319392e6e19deeca9d45468aa1119846e50 Author: Darren Tucker -Date: Tue Aug 2 10:01:52 2016 +1000 +Date: Thu Oct 13 04:07:20 2016 +1100 - Replace spaces with tabs. + Import rev 1.24 from OpenBSD. - Mechanically replace spaces with tabs in compat files not synced with - OpenBSD. + revision 1.24 + date: 2013/11/24 23:51:29; author: deraadt; state: Exp; lines: +4 -4; + most obvious unsigned char casts for ctype + ok jca krw ingo -commit c20dccb5614c5714f4155dda01bcdebf97cfae7e +commit 12069e56221de207ed666c2449dedb431a2a7ca2 Author: Darren Tucker -Date: Tue Aug 2 09:44:25 2016 +1000 +Date: Thu Oct 13 04:04:44 2016 +1100 - Strip trailing whitespace. + Import rev 1.23 from OpenBSD. Fixes bz#2619. - Mechanically strip trailing whitespace on files not synced with OpenBSD - (or in the case of bsd-snprint.c, rsync). - -commit 30f9bd1c0963c23bfba8468dfd26aa17609ba42f -Author: Darren Tucker -Date: Tue Aug 2 09:06:27 2016 +1000 - - Repair $OpenBSD markers. + revision 1.23 + date: 2010/05/14 13:30:34; author: millert; state: Exp; lines: +41 -39; + Defer installing signal handlers until echo is disabled so that we + get suspended normally when not the foreground process. Fix potential + infinite loop when restoring terminal settings if process is in the + background when restore occurs. OK miod@ -commit 9715d4ad4b53877ec23dc8681dd7a405de9419a6 +commit 7508d83eff89af069760b4cc587305588a64e415 Author: Darren Tucker -Date: Tue Aug 2 09:02:42 2016 +1000 - - Repair $OpenBSD marker. - -commit cf3e0be7f5828a5e5f6c296a607d20be2f07d60c -Author: Tim Rice -Date: Mon Aug 1 14:31:52 2016 -0700 - - modified: configure.ac opensshd.init.in - Skip generating missing RSA1 key on startup unless ssh1 support is enabled. - Spotted by Jean-Pierre Radley - -commit 99522ba7ec6963a05c04a156bf20e3ba3605987c -Author: Damien Miller -Date: Thu Jul 28 08:54:27 2016 +1000 +Date: Thu Oct 13 03:53:51 2016 +1100 - define _OPENBSD_SOURCE for reallocarray on NetBSD + If we don't have TCSASOFT, define it to zero. - Report by and debugged with Hisashi T Fujinaka, dtucker nailed - the problem (lack of prototype causing return type confusion). + This makes it a no-op when we use it below, which allows us to re-sync + those lines with the upstream and make future updates easier. -commit 3e1e076550c27c6bbdddf36d8f42bd79fbaaa187 -Author: Damien Miller -Date: Wed Jul 27 08:25:42 2016 +1000 +commit aae4dbd4c058d3b1fe1eb5c4e6ddf35827271377 +Author: jmc@openbsd.org +Date: Fri Oct 7 14:41:52 2016 +0000 - KNF + upstream commit + + tidy up the formatting in this file. more specifically, + replace .Dq, which looks appalling, with .Cm, where appropriate; + + Upstream-ID: ff8e90aa0343d9bb56f40a535e148607973cc738 -commit d99ee9c4e5e217e7d05eeec84e9ce641f4675331 -Author: Damien Miller -Date: Wed Jul 27 08:25:23 2016 +1000 +commit a571dbcc7b7b25371174569b13df5159bc4c6c7a +Author: djm@openbsd.org +Date: Tue Oct 4 21:34:40 2016 +0000 - Linux auditing also needs packet.h + upstream commit + + add a comment about implicitly-expected checks to + sshkey_ec_validate_public() + + Upstream-ID: 74a7f71c28f7c13a50f89fc78e7863b9cd61713f -commit 393bd381a45884b589baa9aed4394f1d250255ca -Author: Damien Miller -Date: Wed Jul 27 08:18:05 2016 +1000 +commit 2f78a2a698f4222f8e05cad57ac6e0c3d1faff00 +Author: djm@openbsd.org +Date: Fri Sep 30 20:24:46 2016 +0000 - fix auditing on Linux + upstream commit - get_remote_ipaddr() was replaced with ssh_remote_ipaddr() + fix some -Wpointer-sign warnings in the new mux proxy; ok + markus@ + + Upstream-ID: b1ba7b3769fbc6b7f526792a215b0197f5e55dfd -commit 80e766fb089de4f3c92b1600eb99e9495e37c992 -Author: Damien Miller -Date: Sun Jul 24 21:50:13 2016 +1000 +commit ca71c36645fc26fcd739a8cfdc702cec85607761 +Author: bluhm@openbsd.org +Date: Wed Sep 28 20:09:52 2016 +0000 - crank version numbers + upstream commit + + Add a makefile rule to create the ssh library when + regress needs it. This allows to run the ssh regression tests without doing + a "make build" before. Discussed with dtucker@ and djm@; OK djm@ + + Upstream-Regress-ID: ce489bd53afcd471225a125b4b94565d4717c025 -commit b1a478792d458f2e938a302e64bab2b520edc1b3 -Author: djm@openbsd.org -Date: Sun Jul 24 11:45:36 2016 +0000 +commit ce44c970f913d2a047903dba8670554ac42fc479 +Author: bluhm@openbsd.org +Date: Mon Sep 26 21:34:38 2016 +0000 upstream commit - openssh-7.3 + Allow to run ssh regression tests as root. If the user + is already root, the test should not expect that SUDO is set. If ssh needs + another user, use sudo or doas to switch from root if necessary. OK dtucker@ - Upstream-ID: af106a7eb665f642648cf1993e162c899f358718 + Upstream-Regress-ID: b464e55185ac4303529e3e6927db41683aaeace2 -commit 353766e0881f069aeca30275ab706cd60a1a8fdd -Author: Darren Tucker -Date: Sat Jul 23 16:14:42 2016 +1000 +commit 8d0578478586e283e751ca51e7b0690631da139a +Author: markus@openbsd.org +Date: Fri Sep 30 09:19:13 2016 +0000 - Move Cygwin IPPORT_RESERVED overrride to defines.h + upstream commit - Patch from vinschen at redhat.com. + ssh proxy mux mode (-O proxy; idea from Simon Tatham): - mux + client speaks the ssh-packet protocol directly over unix-domain socket. - mux + server acts as a proxy, translates channel IDs and relays to the server. - no + filedescriptor passing necessary. - combined with unix-domain forwarding it's + even possible to run mux client and server on different machines. feedback + & ok djm@ + + Upstream-ID: 666a2fb79f58e5c50e246265fb2b9251e505c25b -commit 368dd977ae07afb93f4ecea23615128c95ab2b32 +commit b7689155f3f5c4999846c07a852b1c7a43b09cec Author: djm@openbsd.org -Date: Sat Jul 23 02:54:08 2016 +0000 +Date: Wed Sep 28 21:44:52 2016 +0000 upstream commit - fix pledge violation with ssh -f; reported by Valentin - Kozamernik ok dtucker@ + put back some pre-auth zlib bits that I shouldn't have + removed - they are still used by the client. Spotted by naddy@ - Upstream-ID: a61db7988db88d9dac3c4dd70e18876a8edf84aa + Upstream-ID: 80919468056031037d56a1f5b261c164a6f90dc2 -commit f00211e3c6d24d6ea2b64b4b1209f671f6c1d42e +commit 4577adead6a7d600c8e764619d99477a08192c8f Author: djm@openbsd.org -Date: Fri Jul 22 07:00:46 2016 +0000 +Date: Wed Sep 28 20:32:42 2016 +0000 upstream commit - improve wording; suggested by jmc@ + restore pre-auth compression support in the client -- the + previous commit was intended to remove it from the server only. - Upstream-ID: 55cb0a24c8e0618b3ceec80998dc82c85db2d2f8 + remove a few server-side pre-auth compression bits that escaped + + adjust wording of Compression directive in sshd_config(5) + + pointed out by naddy@ ok markus@ + + Upstream-ID: d23696ed72a228dacd4839dd9f2dec424ba2016b -commit 83cbca693c3b0719270e6a0f2efe3f9ee93a65b8 -Author: dtucker@openbsd.org -Date: Fri Jul 22 05:46:11 2016 +0000 +commit 80d1c963b4dc84ffd11d09617b39c4bffda08956 +Author: jmc@openbsd.org +Date: Wed Sep 28 17:59:22 2016 +0000 upstream commit - Lower loglevel for "Authenticated with partial success" - message similar to other similar level. bz#2599, patch from cgallek at - gmail.com, ok markus@ + use a separate TOKENS section, as we've done for + sshd_config(5); help/ok djm - Upstream-ID: 3faab814e947dc7b2e292edede23e94c608cb4dd + Upstream-ID: 640e32b5e4838e4363738cdec955084b3579481d -commit 10358abd087ab228b7ce2048efc4f3854a9ab9a6 +commit 1cfd5c06efb121e58e8b6671548fda77ef4b4455 Author: Damien Miller -Date: Fri Jul 22 14:06:36 2016 +1000 +Date: Thu Sep 29 03:19:23 2016 +1000 - retry waitpid on EINTR failure + Remove portability support for mmap - patch from Jakub Jelen on bz#2581; ok dtucker@ + We no longer need to wrap/replace mmap for portability now that + pre-auth compression has been removed from OpenSSH. -commit da88a70a89c800e74ea8e5661ffa127a3cc79a92 +commit 0082fba4efdd492f765ed4c53f0d0fbd3bdbdf7f Author: djm@openbsd.org -Date: Fri Jul 22 03:47:36 2016 +0000 +Date: Wed Sep 28 16:33:06 2016 +0000 upstream commit - constify a few functions' arguments; patch from Jakub - Jelen bz#2581 + Remove support for pre-authentication compression. Doing + compression early in the protocol probably seemed reasonable in the 1990s, + but today it's clearly a bad idea in terms of both cryptography (cf. multiple + compression oracle attacks in TLS) and attack surface. - Upstream-ID: f2043f51454ea37830ff6ad60c8b32b4220f448d - -commit c36d91bd4ebf767f310f7cea88d61d1c15f53ddf -Author: djm@openbsd.org -Date: Fri Jul 22 03:39:13 2016 +0000 - - upstream commit + Moreover, to support it across privilege-separation zlib needed + the assistance of a complex shared-memory manager that made the + required attack surface considerably larger. - move debug("%p", key) to before key is free'd; probable - undefined behaviour on strict compilers; reported by Jakub Jelen bz#2581 + Prompted by Guido Vranken pointing out a compiler-elided security + check in the shared memory manager found by Stack + (http://css.csail.mit.edu/stack/); ok deraadt@ markus@ - Upstream-ID: 767f323e1f5819508a0e35e388ec241bac2f953a + NB. pre-auth authentication has been disabled by default in sshd + for >10 years. + + Upstream-ID: 32af9771788d45a0779693b41d06ec199d849caf -commit 286f5a77c3bfec1e8892ca268087ac885ac871bf +commit 27c3a9c2aede2184856b5de1e6eca414bb751c38 Author: djm@openbsd.org -Date: Fri Jul 22 03:35:11 2016 +0000 +Date: Mon Sep 26 21:16:11 2016 +0000 upstream commit - reverse the order in which -J/JumpHost proxies are visited to - be more intuitive and document + Avoid a theoretical signed integer overflow should + BN_num_bytes() ever violate its manpage and return a negative value. Improve + order of tests to avoid confusing increasingly pedantic compilers. - reported by and manpage bits naddy@ + Reported by Guido Vranken from stack (css.csail.mit.edu/stack) + unstable optimisation analyser output. ok deraadt@ - Upstream-ID: 3a68fd6a841fd6cf8cedf6552a9607ba99df179a + Upstream-ID: f8508c830c86d8f36c113985e52bf8eedae23505 -commit fcd135c9df440bcd2d5870405ad3311743d78d97 -Author: dtucker@openbsd.org -Date: Thu Jul 21 01:39:35 2016 +0000 +commit 8663e51c80c6aa3d750c6d3bcff6ee05091922be +Author: Damien Miller +Date: Wed Sep 28 07:40:33 2016 +1000 - upstream commit - - Skip passwords longer than 1k in length so clients can't - easily DoS sshd by sending very long passwords, causing it to spend CPU - hashing them. feedback djm@, ok markus@. - - Brought to our attention by tomas.kuthan at oracle.com, shilei-c at - 360.cn and coredump at autistici.org + fix mdoc2man.awk formatting for top-level lists - Upstream-ID: d0af7d4a2190b63ba1d38eec502bc4be0be9e333 + Reported by Glenn Golden + Diagnosis and fix from Ingo Schwarze -commit 324583e8fb3935690be58790425793df619c6d4d -Author: naddy@openbsd.org -Date: Wed Jul 20 10:45:27 2016 +0000 +commit b97739dc21570209ed9d4e7beee0c669ed23b097 +Author: djm@openbsd.org +Date: Thu Sep 22 21:15:41 2016 +0000 upstream commit - Do not clobber the global jump_host variables when - parsing an inactive configuration. ok djm@ + missing bit from previous commit - Upstream-ID: 5362210944d91417d5976346d41ac0b244350d31 + Upstream-ID: 438d5ed6338b28b46e822eb13eee448aca31df37 -commit 32d921c323b989d28405e78d0a8923d12913d737 +commit de6a175a99d22444e10d19ad3fffef39bc3ee3bb Author: jmc@openbsd.org -Date: Tue Jul 19 12:59:16 2016 +0000 +Date: Thu Sep 22 19:19:01 2016 +0000 upstream commit - tweak previous; + organise the token stuff into a separate section; ok + markus for an earlier version of the diff ok/tweaks djm - Upstream-ID: f3c1a5b3f05dff366f60c028728a2b43f15ff534 + Upstream-ID: 81a6daa506a4a5af985fce7cf9e59699156527c8 -commit d7eabc86fa049a12ba2c3fb198bd1d51b37f7025 -Author: dtucker@openbsd.org -Date: Tue Jul 19 11:38:53 2016 +0000 +commit 16277fc45ffc95e4ffc3d45971ff8320b974de2b +Author: djm@openbsd.org +Date: Thu Sep 22 17:55:13 2016 +0000 upstream commit - Allow wildcard for PermitOpen hosts as well as ports. - bz#2582, patch from openssh at mzpqnxow.com and jjelen at redhat.com. ok - markus@ + mention curve25519-sha256 KEX - Upstream-ID: af0294e9b9394c4e16e991424ca0a47a7cc605f2 + Upstream-ID: 33ae1f433ce4795ffa6203761fbdf86e0d7ffbaf -commit b98a2a8348e907b3d71caafd80f0be8fdd075943 -Author: markus@openbsd.org -Date: Mon Jul 18 11:35:33 2016 +0000 +commit 0493766d5676c7ca358824ea8d3c90f6047953df +Author: djm@openbsd.org +Date: Thu Sep 22 17:52:53 2016 +0000 upstream commit - Reduce timing attack against obsolete CBC modes by always - computing the MAC over a fixed size of data. Reported by Jean Paul - Degabriele, Kenny Paterson, Torben Hansen and Martin Albrecht. ok djm@ + support plain curve25519-sha256 KEX algorithm now that it + is approaching standardisation (same algorithm is currently supported as + curve25519-sha256@libssh.org) - Upstream-ID: f20a13279b00ba0afbacbcc1f04e62e9d41c2912 + Upstream-ID: 5e2b6db2e72667048cf426da43c0ee3fc777baa2 -commit dbf788b4d9d9490a5fff08a7b09888272bb10fcc -Author: Darren Tucker -Date: Thu Jul 21 14:17:31 2016 +1000 +commit f31c654b30a6f02ce0b8ea8ab81791b675489628 +Author: dtucker@openbsd.org +Date: Thu Sep 22 02:29:57 2016 +0000 - Search users for one with a valid salt. + upstream commit - If the root account is locked (eg password "!!" or "*LK*") keep looking - until we find a user with a valid salt to use for crypting passwords of - invalid users. ok djm@ + If ssh receives a PACKET_DISCONNECT during userauth it + will cause ssh_dispatch_run(DISPATCH_BLOCK, ...) to return without the + session being authenticated. Check for this and exit if necessary. ok djm@ + + Upstream-ID: b3afe126c0839d2eae6cddd41ff2ba317eda0903 -commit e8b58f48fbb1b524fb4f0d4865fa0005d6a4b782 -Author: Darren Tucker -Date: Mon Jul 18 17:22:49 2016 +1000 +commit 1622649b7a829fc8dc313042a43a974f0f3e8a99 +Author: djm@openbsd.org +Date: Wed Sep 21 19:53:12 2016 +0000 - Explicitly specify source files for regress tools. + upstream commit - Since adding $(REGRESSLIBS), $? is wrong because it includes only the - changed source files. $< seems like it'd be right however it doesn't - seem to work on some non-GNU makes, so do what works everywhere. + correctly return errors from kex_send_ext_info(). Fix from + Sami Farin via https://github.com/openssh/openssh-portable/pull/50 + + Upstream-ID: c85999af28aaecbf92cfa2283381df81e839b42c -commit eac1bbd06872c273f16ac0f9976b0aef026b701b -Author: Darren Tucker -Date: Mon Jul 18 17:12:22 2016 +1000 +commit f83a0cfe16c7a73627b46a9a94e40087d60f32fb +Author: djm@openbsd.org +Date: Wed Sep 21 17:44:20 2016 +0000 - Conditionally include err.h. + upstream commit + + cast uint64_t for printf + + Upstream-ID: 76d23e89419ccbd2320f92792a6d878211666ac1 -commit 0a454147568746c503f669e1ba861f76a2e7a585 -Author: Darren Tucker -Date: Mon Jul 18 16:26:26 2016 +1000 +commit 5f63ab474f58834feca4f35c498be03b7dd38a16 +Author: djm@openbsd.org +Date: Wed Sep 21 17:03:54 2016 +0000 - Remove local implementation of err, errx. + upstream commit - We now have a shared implementation in libopenbsd-compat. + disable tests for affirmative negated match after backout of + match change + + Upstream-Regress-ID: acebb8e5042f03d66d86a50405c46c4de0badcfd -commit eb999a4590846ba4d56ddc90bd07c23abfbab7b1 +commit a5ad3a9db5a48f350f257a67b62fafd719ecb7e0 Author: djm@openbsd.org -Date: Mon Jul 18 06:08:01 2016 +0000 +Date: Wed Sep 21 16:55:42 2016 +0000 upstream commit - Add some unsigned overflow checks for extra_pad. None of - these are reachable with the amount of padding that we use internally. - bz#2566, pointed out by Torben Hansen. ok markus@ + Revert two recent changes to negated address matching. The + new behaviour offers unintuitive surprises. We'll find a better way to deal + with single negated matches. - Upstream-ID: 4d4be8450ab2fc1b852d5884339f8e8c31c3fd76 + match.c 1.31: + > fix matching for pattern lists that contain a single negated match, + > e.g. "Host !example" + > + > report and patch from Robin Becker. bz#1918 ok dtucker@ + + addrmatch.c 1.11: + > fix negated address matching where the address list consists of a + > single negated match, e.g. "Match addr !192.20.0.1" + > + > Report and patch from Jakub Jelen. bz#2397 ok dtucker@ + + Upstream-ID: ec96c770f0f5b9a54e5e72fda25387545e9c80c6 -commit c71ba790c304545464bb494de974cdf0f4b5cf1e -Author: Darren Tucker -Date: Mon Jul 18 15:43:25 2016 +1000 +commit 119b7a2ca0ef2bf3f81897ae10301b8ca8cba844 +Author: djm@openbsd.org +Date: Wed Sep 21 01:35:12 2016 +0000 - Add dependency on libs for unit tests. + upstream commit - Makes "./configure && make tests" work again. ok djm@ + test all the AuthorizedPrincipalsCommand % expansions + + Upstream-Regress-ID: 0a79a84dfaa59f958e46b474c3db780b454d30e3 -commit 8199d0311aea3e6fd0284c9025e7a83f4ece79e8 -Author: Darren Tucker -Date: Mon Jul 18 13:47:39 2016 +1000 +commit bfa9d969ab6235d4938ce069d4db7e5825c56a19 +Author: djm@openbsd.org +Date: Wed Sep 21 01:34:45 2016 +0000 - Correct location for kexfuzz in clean target. + upstream commit + + add a way for principals command to get see key ID and serial + too + + Upstream-ID: 0d30978bdcf7e8eaeee4eea1b030eb2eb1823fcb -commit 01558b7b07af43da774d3a11a5c51fa9c310849d -Author: Darren Tucker -Date: Mon Jul 18 09:33:25 2016 +1000 +commit 920585b826af1c639e4ed78b2eba01fd2337b127 +Author: djm@openbsd.org +Date: Fri Sep 16 06:09:31 2016 +0000 - Handle PAM_MAXTRIES from modules. + upstream commit - bz#2249: handle the case where PAM returns PAM_MAXTRIES by ceasing to offer - password and keyboard-interative authentication methods. Should prevent - "sshd ignoring max retries" warnings in the log. ok djm@ + add a note on kexfuzz' limitations - It probably won't trigger with keyboard-interactive in the default - configuration because the retry counter is stored in module-private - storage which goes away with the sshd PAM process (see bz#688). On the - other hand, those cases probably won't log a warning either. + Upstream-Regress-ID: 03804d4a0dbc5163e1a285a4c8cc0a76a4e864ec -commit 65c6c6b567ab5ab12945a5ad8e0ab3a8c26119cc +commit 0445ff184080b196e12321998b4ce80b0f33f8d1 Author: djm@openbsd.org -Date: Sun Jul 17 04:20:16 2016 +0000 +Date: Fri Sep 16 01:01:41 2016 +0000 upstream commit - support UTF-8 characters in ssh(1) banners using - schwarze@'s safe fmprintf printer; bz#2058 + fix for newer modp DH groups + (diffie-hellman-group14-sha256 etc) - feedback schwarze@ ok dtucker@ + Upstream-Regress-ID: fe942c669959462b507516ae1634fde0725f1c68 + +commit 28652bca29046f62c7045e933e6b931de1d16737 +Author: markus@openbsd.org +Date: Mon Sep 19 19:02:19 2016 +0000 + + upstream commit - Upstream-ID: a72ce4e3644c957643c9524eea2959e41b91eea7 + move inbound NEWKEYS handling to kex layer; otherwise + early NEWKEYS causes NULL deref; found by Robert Swiecki/honggfuzz; fixed + with & ok djm@ + + Upstream-ID: 9a68b882892e9f51dc7bfa9f5a423858af358b2f -commit e4eb7d910976fbfc7ce3e90c95c11b07b483d0d7 -Author: jmc@openbsd.org -Date: Sat Jul 16 06:57:55 2016 +0000 +commit 492710894acfcc2f173d14d1d45bd2e688df605d +Author: natano@openbsd.org +Date: Mon Sep 19 07:52:42 2016 +0000 upstream commit - - add proxyjump to the options list - formatting fixes - - update usage() + Replace two more arc4random() loops with + arc4random_buf(). - ok djm + tweaks and ok dtucker + ok deraadt - Upstream-ID: 43d318e14ce677a2eec8f21ef5ba2f9f68a59457 + Upstream-ID: 738d3229130ccc7eac975c190276ca6fcf0208e4 -commit af1f084857621f14bd9391aba8033d35886c2455 -Author: dtucker@openbsd.org -Date: Fri Jul 15 05:01:58 2016 +0000 +commit 1036356324fecc13099ac6e986b549f6219327d7 +Author: tedu@openbsd.org +Date: Sat Sep 17 18:00:27 2016 +0000 upstream commit - Reduce the syslog level of some relatively common protocol - events from LOG_CRIT by replacing fatal() calls with logdie(). Part of - bz#2585, ok djm@ + replace two arc4random loops with arc4random_buf ok + deraadt natano - Upstream-ID: 9005805227c94edf6ac02a160f0e199638d288e5 + Upstream-ID: e18ede972d1737df54b49f011fa4f3917a403f48 -commit bd5f2b78b69cf38d6049a0de445a79c8595e4a1f -Author: Damien Miller -Date: Fri Jul 15 19:14:48 2016 +1000 +commit 00df97ff68a49a756d4b977cd02283690f5dfa34 +Author: djm@openbsd.org +Date: Wed Sep 14 20:11:26 2016 +0000 - missing openssl/dh.h + upstream commit + + take fingerprint of correct key for + AuthorizedPrincipalsCommand + + Upstream-ID: 553581a549cd6a3e73ce9f57559a325cc2cb1f38 -commit 4a984fd342effe5f0aad874a0d538c4322d973c0 -Author: Damien Miller -Date: Fri Jul 15 18:47:07 2016 +1000 +commit e7907c1cb938b96dd33d27c2fea72c4e08c6b2f6 +Author: djm@openbsd.org +Date: Wed Sep 14 05:42:25 2016 +0000 - cast to avoid type warning in error message + upstream commit + + add %-escapes to AuthorizedPrincipalsCommand to match those + supported for AuthorizedKeysCommand (key, key type, fingerprint, etc) and a + few more to provide access to the certificate's CA key; 'looks ok' dtucker@ + + Upstream-ID: 6b00fd446dbebe67f4e4e146d2e492d650ae04eb -commit 5abfb15ced985c340359ae7fb65a625ed3692b3e -Author: Darren Tucker -Date: Fri Jul 15 14:48:30 2016 +1000 +commit 2b939c272a81c4d0c47badeedbcb2ba7c128ccda +Author: dtucker@openbsd.org +Date: Wed Sep 14 00:45:31 2016 +0000 - Move VA_COPY macro into compat header. + upstream commit - Some AIX compilers unconditionally undefine va_copy but don't set it back - to an internal function, causing link errors. In some compat code we - already use VA_COPY instead so move the two existing instances into the - shared header and use for sshbuf-getput-basic.c too. Should fix building - with at lease some versions of AIX's compiler. bz#2589, ok djm@ + Improve test coverage of ssh-keygen -T a bit. + + Upstream-Regress-ID: 8851668c721bcc2b400600cfc5a87644cc024e72 -commit 832b7443b7a8e181c95898bc5d73497b7190decd -Author: Damien Miller -Date: Fri Jul 15 14:45:34 2016 +1000 +commit 44d82fc83be6c5ccd70881c2dac1a73e5050398b +Author: dtucker@openbsd.org +Date: Mon Sep 12 02:25:46 2016 +0000 - disable ciphers not supported by OpenSSL + upstream commit - bz#2466 ok dtucker@ - -commit 5fbe93fc6fbb2fe211e035703dec759d095e3dd8 -Author: Damien Miller -Date: Fri Jul 15 13:54:31 2016 +1000 + Add testcase for ssh-keygen -j, -J and -K options for + moduli screening. Does not currently test generation as that is extremely + slow. + + Upstream-Regress-ID: 9de6ce801377ed3ce0a63a1413f1cd5fd3c2d062 - add a --disable-pkcs11 knob +commit 44e5f756d286bc3a1a5272ea484ee276ba3ac5c2 +Author: djm@openbsd.org +Date: Tue Aug 23 08:17:04 2016 +0000 -commit 679ce88ec2a8e2fe6515261c489e8c1449bb9da9 -Author: Damien Miller -Date: Fri Jul 15 13:44:38 2016 +1000 - - fix newline escaping for unsupported_algorithms + upstream commit - The hmac-ripemd160 was incorrect and could lead to broken - Makefiles on systems that lacked support for it, but I made - all the others consistent too. + add tests for addr_match_list() + + Upstream-Regress-ID: fae2d1fef84687ece584738a924c7bf969616c8e -commit ed877ef653847d056bb433975d731b7a1132a979 +commit 445e218878035b59c704c18406e8aeaff4c8aa25 Author: djm@openbsd.org -Date: Fri Jul 15 00:24:30 2016 +0000 +Date: Mon Sep 12 23:39:34 2016 +0000 upstream commit - Add a ProxyJump ssh_config(5) option and corresponding -J - ssh(1) command-line flag to allow simplified indirection through a SSH - bastion or "jump host". - - These options construct a proxy command that connects to the - specified jump host(s) (more than one may be specified) and uses - port-forwarding to establish a connection to the next destination. + handle certs in rsa_hash_alg_from_ident(), saving an + unnecessary special case elsewhere. - This codifies the safest way of indirecting connections through SSH - servers and makes it easy to use. + Upstream-ID: 901cb081c59d6d2698b57901c427f3f6dc7397d4 + +commit 130f5df4fa37cace8c079dccb690e5cafbf00751 +Author: djm@openbsd.org +Date: Mon Sep 12 23:31:27 2016 +0000 + + upstream commit - ok markus@ + list all supported signature algorithms in the + server-sig-algs Reported by mb AT smartftp.com in bz#2547 and (independantly) + Ron Frederick; ok markus@ - Upstream-ID: fa899cb8b26d889da8f142eb9774c1ea36b04397 + Upstream-ID: ddf702d721f54646b11ef2cee6d916666cb685cd -commit 5c02dd126206a26785379e80f2d3848e4470b711 +commit 8f750ccfc07acb8aa98be5a5dd935033a6468cfd Author: Darren Tucker -Date: Fri Jul 15 12:56:39 2016 +1000 +Date: Mon Sep 12 14:43:58 2016 +1000 - Map umac_ctx struct name too. - - Prevents size mismatch linker warnings on Solaris 11. + Remove no-op brackets to resync with upstream. -commit 283b97ff33ea2c641161950849931bd578de6946 +commit 7050896e7395866278c19c2ff080c26152619d1d Author: Darren Tucker -Date: Fri Jul 15 13:49:44 2016 +1000 +Date: Mon Sep 12 13:57:28 2016 +1000 - Mitigate timing of disallowed users PAM logins. - - When sshd decides to not allow a login (eg PermitRootLogin=no) and - it's using PAM, it sends a fake password to PAM so that the timing for - the failure is not noticeably different whether or not the password - is correct. This behaviour can be detected by sending a very long - password string which is slower to hash than the fake password. - - Mitigate by constructing an invalid password that is the same length - as the one from the client and thus takes the same time to hash. - Diff from djm@ + Resync ssh-keygen -W error message with upstream. -commit 9286875a73b2de7736b5e50692739d314cd8d9dc +commit 43cceff82cc20413cce58ba3375e19684e62cec4 Author: Darren Tucker -Date: Fri Jul 15 13:32:45 2016 +1000 +Date: Mon Sep 12 13:55:37 2016 +1000 - Determine appropriate salt for invalid users. - - When sshd is processing a non-PAM login for a non-existent user it uses - the string from the fakepw structure as the salt for crypt(3)ing the - password supplied by the client. That string has a Blowfish prefix, so on - systems that don't understand that crypt will fail fast due to an invalid - salt, and even on those that do it may have significantly different timing - from the hash methods used for real accounts (eg sha512). This allows - user enumeration by, eg, sending large password strings. This was noted - by EddieEzra.Harari at verint.com (CVE-2016-6210). - - To mitigate, use the same hash algorithm that root uses for hashing - passwords for users that do not exist on the system. ok djm@ + Move ssh-keygen -W handling code to match upstream -commit a162dd5e58ca5b224d7500abe35e1ef32b5de071 +commit af48d541360b1d7737b35740a4b1ca34e1652cd9 Author: Darren Tucker -Date: Thu Jul 14 21:19:59 2016 +1000 +Date: Mon Sep 12 13:52:17 2016 +1000 - OpenSSL 1.1.x not currently supported. + Move ssh-keygen -T handling code to match upstream. -commit 7df91b01fc558a33941c5c5f31abbcdc53a729fb +commit d8c3cfbb018825c6c86547165ddaf11924901c49 Author: Darren Tucker -Date: Thu Jul 14 12:25:24 2016 +1000 +Date: Mon Sep 12 13:30:50 2016 +1000 - Check for VIS_ALL. - - If we don't have it, set BROKEN_STRNVIS to activate the compat replacement. + Move -M handling code to match upstream. -commit ee67716f61f1042d5e67f91c23707cca5dcdd7d0 +commit 7b63cf6dbbfa841c003de57d1061acbf2ff22364 Author: dtucker@openbsd.org -Date: Thu Jul 14 01:24:21 2016 +0000 +Date: Mon Sep 12 03:29:16 2016 +0000 upstream commit - Correct equal in test. + Spaces->tabs. - Upstream-Regress-ID: 4e32f7a5c57a619c4e8766cb193be2a1327ec37a + Upstream-ID: f4829dfc3f36318273f6082b379ac562eead70b7 -commit 372807c2065c8572fdc6478b25cc5ac363743073 -Author: tb@openbsd.org -Date: Mon Jul 11 21:38:13 2016 +0000 +commit 11e5e644536821ceb3bb4dd8487fbf0588522887 +Author: dtucker@openbsd.org +Date: Mon Sep 12 03:25:20 2016 +0000 upstream commit - Add missing "recvfd" pledge promise: Raf Czlonka reported - ssh coredumps when Control* keywords were set in ssh_config. This patch also - fixes similar problems with scp and sftp. - - ok deraadt, looks good to millert + Style whitespace fix. Also happens to remove a no-op + diff with portable. - Upstream-ID: ca2099eade1ef3e87a79614fefa26a0297ad8a3b + Upstream-ID: 45d90f9a62ad56340913a433a9453eb30ceb8bf3 -commit e0453f3df64bf485c61c7eb6bd12893eee9fe2cd -Author: tedu@openbsd.org -Date: Mon Jul 11 03:19:44 2016 +0000 +commit 9136ec134c97a8aff2917760c03134f52945ff3c +Author: deraadt@openbsd.org +Date: Mon Sep 12 01:22:38 2016 +0000 upstream commit - obsolete note about fascistloggin is obsolete. ok djm - dtucker + Add MAXIMUM(), MINIMUM(), and ROUNDUP() to misc.h, then + use those definitions rather than pulling and unknown namespace + pollution. ok djm markus dtucker - Upstream-ID: dae60df23b2bb0e89f42661ddd96a7b0d1b7215a + Upstream-ID: 712cafa816c9f012a61628b66b9fbd5687223fb8 -commit a2333584170a565adf4f209586772ef8053b10b8 -Author: Darren Tucker -Date: Thu Jul 14 10:59:09 2016 +1000 +commit f219fc8f03caca7ac82a38ed74bbd6432a1195e7 +Author: jmc@openbsd.org +Date: Wed Sep 7 18:39:24 2016 +0000 - Add compat code for missing wcwidth. + upstream commit - If we don't have wcwidth force fallback implementations of nl_langinfo - and mbtowc. Based on advice from Ingo Schwarze. - -commit 8aaec7050614494014c47510b7e94daf6e644c62 -Author: Damien Miller -Date: Thu Jul 14 09:48:48 2016 +1000 - - fix missing include for systems with err.h - -commit 6310ef27a2567cda66d6cf0c1ad290ee1167f243 -Author: Darren Tucker -Date: Wed Jul 13 14:42:35 2016 +1000 - - Move err.h replacements into compat lib. + sort; from matthew martin - Move implementations of err.h replacement functions into their own file - in the libopenbsd-compat so we can use them in kexfuzz.c too. ok djm@ + Upstream-ID: 73cec7f7ecc82d37a4adffad7745e4684de67ce7 -commit f3f2cc8386868f51440c45210098f65f9787449a -Author: Darren Tucker -Date: Mon Jul 11 17:23:38 2016 +1000 +commit 06ce56b05def9460aecc7cdb40e861a346214793 +Author: markus@openbsd.org +Date: Tue Sep 6 09:22:56 2016 +0000 - Check for wchar.h and langinfo.h + upstream commit - Wrap includes in the appropriate #ifdefs. - -commit b9c50614eba9d90939b2b119b6e1b7e03b462278 -Author: Damien Miller -Date: Fri Jul 8 13:59:13 2016 +1000 - - whitelist more architectures for seccomp-bpf + ssh_set_newkeys: print correct block counters on + rekeying; ok djm@ - bz#2590 - testing and patch from Jakub Jelen + Upstream-ID: 32bb7a9cb9919ff5bab28d50ecef3a2b2045dd1e -commit 18813a32b6fd964037e0f5e1893cb4468ac6a758 -Author: guenther@openbsd.org -Date: Mon Jul 4 18:01:44 2016 +0000 +commit e5e8d9114ac6837a038f4952994ca95a97fafe8d +Author: markus@openbsd.org +Date: Tue Sep 6 09:14:05 2016 +0000 upstream commit - DEBUGLIBS has been broken since the gcc4 switch, so delete - it. CFLAGS contains -g by default anyway - - problem noted by Edgar Pettijohn (edgar (at) pettijohn-web.com) - ok millert@ kettenis@ deraadt@ + update ext_info_c every time we receive a kexinit msg; + fixes sending of ext_info if privsep is disabled; report Aris Adamantiadis & + Mancha; ok djm@ - Upstream-Regress-ID: 4a0bb72f95c63f2ae9daa8a040ac23914bddb542 + Upstream-ID: 2ceaa1076e19dbd3542254b4fb8e42d608f28856 -commit 6d31193d0baa3da339c196ac49625b7ba1c2ecc7 +commit da95318dbedbaa1335323dba370975c2f251afd8 Author: djm@openbsd.org -Date: Fri Jul 8 03:44:42 2016 +0000 +Date: Mon Sep 5 14:02:42 2016 +0000 upstream commit - Improve crypto ordering for Encrypt-then-MAC (EtM) mode - MAC algorithms. - - Previously we were computing the MAC, decrypting the packet and then - checking the MAC. This gave rise to the possibility of creating a - side-channel oracle in the decryption step, though no such oracle has - been identified. + remove 3des-cbc from the client's default proposal; + 64-bit block ciphers are not safe in 2016 and we don't want to wait until + attacks like sweet32 are extended to SSH. - This adds a mac_check() function that computes and checks the MAC in - one pass, and uses it to advance MAC checking for EtM algorithms to - before payload decryption. + As 3des-cbc was the only mandatory cipher in the SSH RFCs, this may + cause problems connecting to older devices using the defaults, but + it's highly likely that such devices already need explicit + configuration for KEX and hostkeys anyway. - Reported by Jean Paul Degabriele, Kenny Paterson, Torben Hansen and - Martin Albrecht. feedback and ok markus@ + ok deraadt, markus, dtucker - Upstream-ID: 1999bb67cab47dda5b10b80d8155fe83d4a1867b + Upstream-ID: a505dfe65c6733af0f751b64cbc4bb7e0761bc2f -commit 71f5598f06941f645a451948c4a5125c83828e1c -Author: guenther@openbsd.org -Date: Mon Jul 4 18:01:44 2016 +0000 +commit b33ad6d997d36edfea65e243cd12ccd01f413549 +Author: djm@openbsd.org +Date: Mon Sep 5 13:57:31 2016 +0000 upstream commit - DEBUGLIBS has been broken since the gcc4 switch, so - delete it. CFLAGS contains -g by default anyway - - problem noted by Edgar Pettijohn (edgar (at) pettijohn-web.com) - ok millert@ kettenis@ deraadt@ + enforce expected request flow for GSSAPI calls; thanks to + Jakub Jelen for testing; ok markus@ - Upstream-ID: 96c5054e3e1f170c6276902d5bc65bb3b87a2603 + Upstream-ID: d4bc0e70e1be403735d3d9d7e176309b1fd626b9 -commit e683fc6f1c8c7295648dbda679df8307786ec1ce -Author: dtucker@openbsd.org -Date: Thu Jun 30 05:17:05 2016 +0000 +commit 0bb2980260fb24e5e0b51adac471395781b66261 +Author: Darren Tucker +Date: Mon Sep 12 11:07:00 2016 +1000 - upstream commit - - Explicitly check for 100% completion to avoid potential - floating point rounding error, which could cause progressmeter to report 99% - on completion. While there invert the test so the 100% case is clearer. with - & ok djm@ + Restore ssh-keygen's -J and -j option handling. - Upstream-ID: a166870c5878e422f3c71ff802e2ccd7032f715d + These were incorrectly removed in the 1d9a2e28 sync commit. -commit 772e6cec0ed740fc7db618dc30b4134f5a358b43 -Author: jmc@openbsd.org -Date: Wed Jun 29 17:14:28 2016 +0000 +commit 775f8a23f2353f5869003c57a213d14b28e0736e +Author: Damien Miller +Date: Wed Aug 31 10:48:07 2016 +1000 - upstream commit + tighten PAM monitor calls - sort the -o list; - - Upstream-ID: 1a97465ede8790b4d47cb618269978e07f41f8ac - -commit 46ecd19e554ccca15a7309cd1b6b44bc8e6b84af -Author: djm@openbsd.org -Date: Thu Jun 23 05:17:51 2016 +0000 - - upstream commit - - fix AuthenticationMethods during configuration re-parse; - reported by Juan Francisco Cantero Hurtado - - Upstream-ID: 8ffa1dac25c7577eca8238e825317ab20848f9b4 + only allow kbd-interactive ones when that authentication method is + enabled. Prompted by Solar Designer -commit 3147e7595d0f2f842a666c844ac53e6c7a253d7e +commit 7fd0ea8a1db4bcfb3d8cd9df149e5d571ebea1f4 Author: djm@openbsd.org -Date: Sun Jun 19 07:48:02 2016 +0000 +Date: Tue Aug 30 07:50:21 2016 +0000 upstream commit - revert 1.34; causes problems loading public keys + restrict monitor auth calls to be allowed only when their + respective authentication methods are enabled in the configuration. - reported by semarie@ + prompted by Solar Designer; ok markus dtucker - Upstream-ID: b393794f8935c8b15d98a407fe7721c62d2ed179 + Upstream-ID: 6eb3f89332b3546d41d6dbf5a8e6ff920142b553 -commit ad23a75509f4320d43f628c50f0817e3ad12bfa7 -Author: jmc@openbsd.org -Date: Fri Jun 17 06:33:30 2016 +0000 +commit b38b95f5bcc52278feb839afda2987933f68ff96 +Author: Damien Miller +Date: Mon Aug 29 11:47:07 2016 +1000 - upstream commit - - grammar fix; + Tighten monitor state-machine flow for PAM calls - Upstream-ID: 5d5b21c80f1e81db367333ce0bb3e5874fb3e463 + (attack surface reduction) -commit 5e28b1a2a3757548b40018cc2493540a17c82e27 +commit dc664d1bd0fc91b24406a3e9575b81c285b8342b Author: djm@openbsd.org -Date: Fri Jun 17 05:06:23 2016 +0000 +Date: Sun Aug 28 22:28:12 2016 +0000 upstream commit - translate OpenSSL error codes to something more - meaninful; bz#2522 reported by Jakub Jelen, ok dtucker@ + fix uninitialised optlen in getsockopt() call; harmless + on Unix/BSD but potentially crashy on Cygwin. Reported by James Slepicka ok + deraadt@ - Upstream-ID: 4cb0795a366381724314e6515d57790c5930ffe5 + Upstream-ID: 1987ccee508ba5b18f016c85100d7ac3f70ff965 -commit b64faeb5eda7eff8210c754d00464f9fe9d23de5 -Author: djm@openbsd.org -Date: Fri Jun 17 05:03:40 2016 +0000 +commit 5bcc1e2769f7d6927d41daf0719a9446ceab8dd7 +Author: guenther@openbsd.org +Date: Sat Aug 27 04:05:12 2016 +0000 upstream commit - ban AuthenticationMethods="" and accept - AuthenticationMethods=any for the default behaviour of not requiring multiple - authentication + Pull in for struct timeval - bz#2398 from Jakub Jelen; ok dtucker@ + ok deraadt@ - Upstream-ID: fabd7f44d59e4518d241d0d01e226435cc23cf27 + Upstream-ID: ae34525485a173bccd61ac8eefeb91c57e3b7df6 -commit 9816fc5daee5ca924dd5c4781825afbaab728877 -Author: dtucker@openbsd.org -Date: Thu Jun 16 11:00:17 2016 +0000 +commit fa4a4c96b19127dc2fd4e92f20d99c0c7f34b538 +Author: guenther@openbsd.org +Date: Sat Aug 27 04:04:56 2016 +0000 upstream commit - Include stdarg.h for va_copy as per man page. + Pull in for NULL - Upstream-ID: 105d6b2f1af2fbd9d91c893c436ab121434470bd + ok deraadt@ + + Upstream-ID: 7baa6a0f1e049bb3682522b4b95a26c866bfc043 -commit b6cf84b51bc0f5889db48bf29a0c771954ade283 -Author: jmc@openbsd.org -Date: Thu Jun 16 06:10:45 2016 +0000 +commit ae363d74ccc1451185c0c8bd4631e28c67c7fd36 +Author: djm@openbsd.org +Date: Thu Aug 25 23:57:54 2016 +0000 upstream commit - keys stored in openssh format can have comments too; diff - from yonas yanfa, tweaked a bit; + add a sIgnore opcode that silently ignores options and + use it to suppress noisy deprecation warnings for the Protocol directive. - ok djm + req henning, ok markus - Upstream-ID: 03d48536da6e51510d73ade6fcd44ace731ceb27 + Upstream-ID: 9fe040aca3d6ff393f6f7e60045cdd821dc4cbe0 -commit aa37768f17d01974b6bfa481e5e83841b6c76f86 -Author: Darren Tucker -Date: Mon Jun 20 15:55:34 2016 +1000 +commit a94c60306643ae904add6e8ed219e4be3494255c +Author: djm@openbsd.org +Date: Thu Aug 25 23:56:51 2016 +0000 - get_remote_name_or_ip inside LOGIN_NEEDS_UTMPX + upstream commit - Apply the same get_remote_name_or_ip -> session_get_remote_name_or_ip - change as commit 95767262 to the code inside #ifdef LOGIN_NEEDS_UTMPX. - Fixes build on AIX. - -commit 009891afc8df37bc2101e15d1e0b6433cfb90549 -Author: Darren Tucker -Date: Fri Jun 17 14:34:09 2016 +1000 - - Remove duplicate code from PAM. ok djm@ + remove superfluous NOTREACHED comment + + Upstream-ID: a7485c1f1be618e8c9e38fd9be46c13b2d03b90c -commit e690fe85750e93fca1fb7c7c8587d4130a4f7aba -Author: dtucker@openbsd.org -Date: Wed Jun 15 00:40:40 2016 +0000 +commit fc041c47144ce28cf71353124a8a5d183cd6a251 +Author: otto@openbsd.org +Date: Tue Aug 23 16:21:45 2016 +0000 upstream commit - Remove "POSSIBLE BREAK-IN ATTEMPT!" from log message - about forward and reverse DNS not matching. We haven't supported IP-based - auth methods for a very long time so it's now misleading. part of bz#2585, - ok markus@ + fix previous, a condition was modified incorrectly; ok + markus@ deraadt@ - Upstream-ID: 5565ef0ee0599b27f0bd1d3bb1f8a323d8274e29 + Upstream-ID: c443e339768e7ed396dff3bb55f693e7d3641453 -commit 57b4ee04cad0d3e0fec1194753b0c4d31e39a1cd -Author: Darren Tucker -Date: Wed Jun 15 11:22:38 2016 +1000 +commit 23555eb13a9b0550371a16dcf8beaab7a5806a64 +Author: djm@openbsd.org +Date: Tue Aug 23 08:17:42 2016 +0000 - Move platform_disable_tracing into its own file. + upstream commit - Prevents link errors resolving the extern "options" when platform.o - gets linked into ssh-agent when building --with-pam. + downgrade an error() to a debug2() to match similar cases + in addr_match_list() + + Upstream-ID: 07c3d53e357214153d9d08f234411e0d1a3d6f5c -commit 78dc8e3724e30ee3e1983ce013e80277dc6ca070 -Author: Darren Tucker -Date: Tue Jun 14 13:55:12 2016 +1000 +commit a39627134f6d90e7009eeb14e9582ecbc7a99192 +Author: djm@openbsd.org +Date: Tue Aug 23 06:36:23 2016 +0000 - Track skipped upstream commit IDs. + upstream commit - There are a small number of "upstream" commits that do not correspond to - a file in -portable. This file tracks those so that we can reconcile - OpenBSD and Portable to ensure that no commits are accidentally missed. + remove Protocol directive from client/server configs that + causes spammy deprecation warnings - If you add something to .skipped-commit-ids please also add an upstream - ID line in the following format when you commit it. + hardcode SSH_PROTOCOLS=2, since that's all we support on the server + now (the client still may support both, so it could get confused) - Upstream-ID: 321065a95a7ccebdd5fd08482a1e19afbf524e35 - Upstream-ID: d4f699a421504df35254cf1c6f1a7c304fb907ca - Upstream-ID: aafe246655b53b52bc32c8a24002bc262f4230f7 - Upstream-ID: 8fa9cd1dee3c3339ae329cf20fb591db6d605120 - Upstream-ID: f31327a48dd4103333cc53315ec53fe65ed8a17a - Upstream-ID: edbfde98c40007b7752a4ac106095e060c25c1ef - Upstream-ID: 052fd565e3ff2d8cec3bc957d1788f50c827f8e2 - Upstream-ID: 7cf73737f357492776223da1c09179fa6ba74660 - Upstream-ID: 180d84674be1344e45a63990d60349988187c1ae - Upstream-ID: f6ae971186ba68d066cd102e57d5b0b2c211a5ee + Upstream-Regress-ID: c16662c631af51633f9fd06aca552a70535de181 -commit 9f919d1a3219d476d6a662d18df058e1c4f36a6f -Author: Darren Tucker -Date: Tue Jun 14 13:51:01 2016 +1000 +commit 6ee4f1c01ee31e65245881d49d4bccf014956066 +Author: Damien Miller +Date: Tue Aug 23 16:33:48 2016 +1000 - Remove now-defunct .cvsignore files. ok djm + hook match and utf8 unittests up to Makefile -commit 68777faf271efb2713960605c748f6c8a4b26d55 -Author: dtucker@openbsd.org -Date: Wed Jun 8 02:13:01 2016 +0000 +commit 114efe2bc0dd2842d997940a833f115e6fc04854 +Author: djm@openbsd.org +Date: Fri Aug 19 06:44:13 2016 +0000 upstream commit - Back out rev 1.28 "Check min and max sizes sent by the - client" change. It caused "key_verify failed for server_host_key" in clients - that send a DH-GEX min value less that DH_GRP_MIN, eg old OpenSSH and PuTTY. - ok djm@ - - Upstream-ID: 452979d3ca5c1e9dff063287ea0a5314dd091f65 - -commit a86ec4d0737ac5879223e7cd9d68c448df46e169 -Author: Darren Tucker -Date: Tue Jun 14 10:48:27 2016 +1000 - - Use Solaris setpflags(__PROC_PROTECT, ...). + add tests for matching functions - Where possible, use Solaris setpflags to disable process tracing on - ssh-agent and sftp-server. bz#2584, based on a patch from huieying.lee - at oracle.com, ok djm. + Upstream-Regress-ID: 0869d4f5c5d627c583c6a929d69c17d5dd65882c -commit 0f916d39b039fdc0b5baf9b5ab0754c0f11ec573 -Author: Darren Tucker -Date: Tue Jun 14 10:43:53 2016 +1000 +commit 857568d2ac81c14bcfd625b27536c1e28c992b3c +Author: Damien Miller +Date: Tue Aug 23 14:32:37 2016 +1000 - Shorten prctl code a tiny bit. + removing UseLogin bits from configure.ac -commit 0fb7f5985351fbbcd2613d8485482c538e5123be -Author: Darren Tucker -Date: Thu Jun 9 16:23:07 2016 +1000 +commit cc182d01cef8ca35a1d25ea9bf4e2ff72e588208 +Author: djm@openbsd.org +Date: Tue Aug 23 03:24:10 2016 +0000 - Move prctl PR_SET_DUMPABLE into platform.c. + upstream commit - This should make it easier to add additional platform support such as - Solaris (bz#2584). + fix negated address matching where the address list + consists of a single negated match, e.g. "Match addr !192.20.0.1" + + Report and patch from Jakub Jelen. bz#2397 ok dtucker@ + + Upstream-ID: 01dcac3f3e6ca47518cf293e31c73597a4bb40d8 -commit e6508898c3cd838324ecfe1abd0eb8cf802e7106 -Author: dtucker@openbsd.org -Date: Fri Jun 3 04:10:41 2016 +0000 +commit 4067ec8a4c64ccf16250c35ff577b4422767da64 +Author: djm@openbsd.org +Date: Tue Aug 23 03:22:49 2016 +0000 upstream commit - Add a test for ssh(1)'s config file parsing. + fix matching for pattern lists that contain a single + negated match, e.g. "Host !example" - Upstream-Regress-ID: 558b7f4dc45cc3761cc3d3e889b9f3c5bc91e601 + report and patch from Robin Becker. bz#1918 ok dtucker@ + + Upstream-ID: 05a0cb323ea4bc20e98db099b42c067bfb9ea1ea -commit ab0a536066dfa32def0bd7272c096ebb5eb25b11 -Author: dtucker@openbsd.org -Date: Fri Jun 3 03:47:59 2016 +0000 +commit 83b581862a1dbb06fc859959f829dde2654aef3c +Author: djm@openbsd.org +Date: Fri Aug 19 03:18:06 2016 +0000 upstream commit - Add 'sshd' to the test ID as I'm about to add a similar - set for ssh. + remove UseLogin option and support for having /bin/login + manage login sessions; ok deraadt markus dtucker - Upstream-Regress-ID: aea7a9c3bac638530165c801ce836875b228ae7a + Upstream-ID: bea7213fbf158efab7e602d9d844fba4837d2712 -commit a5577c1ed3ecdfe4b7b1107c526cae886fc91afb -Author: schwarze@openbsd.org -Date: Mon May 30 12:14:08 2016 +0000 +commit ffe6549c2f7a999cc5264b873a60322e91862581 +Author: naddy@openbsd.org +Date: Mon Aug 15 12:32:04 2016 +0000 upstream commit - stricter malloc.conf(5) options for utf8 tests - - Upstream-Regress-ID: 111efe20a0fb692fa1a987f6e823310f9b25abf6 - -commit 75f0844b4f29d62ec3a5e166d2ee94b02df819fc -Author: schwarze@openbsd.org -Date: Mon May 30 12:05:56 2016 +0000 + Catch up with the SSH1 code removal and delete all + mention of protocol 1 particularities, key files and formats, command line + options, and configuration keywords from the server documentation and + examples. ok jmc@ + + Upstream-ID: 850328854675b4b6a0d4a90f0b4a9dd9ca4e905f + +commit c38ea634893a1975dbbec798fb968c9488013f4a +Author: naddy@openbsd.org +Date: Mon Aug 15 12:27:56 2016 +0000 upstream commit - Fix two rare edge cases: 1. If vasprintf() returns < 0, - do not access a NULL pointer in snmprintf(), and do not free() the pointer - returned from vasprintf() because on some systems other than OpenBSD, it - might be a bogus pointer. 2. If vasprintf() returns == 0, return 0 and "" - rather than -1 and NULL. + Remove more SSH1 server code: * Drop sshd's -k option. * + Retire configuration keywords that only apply to protocol 1, as well as the + "protocol" keyword. * Remove some related vestiges of protocol 1 support. - Besides, free(dst) is pointless after failure (not a bug). + ok markus@ - One half OK martijn@, the other half OK deraadt@; - committing quickly before people get hurt. + Upstream-ID: 9402f82886de917779db12f8ee3f03d4decc244d + +commit 33ba55d9e358c07f069e579bfab80eccaaad52cb +Author: Darren Tucker +Date: Wed Aug 17 16:26:04 2016 +1000 + + Only check for prctl once. + +commit 976ba8a8fd66a969bf658280c1e5adf694cc2fc6 +Author: Darren Tucker +Date: Wed Aug 17 15:33:10 2016 +1000 + + Fix typo. + +commit 9abf84c25ff4448891edcde60533a6e7b2870de1 +Author: Darren Tucker +Date: Wed Aug 17 14:25:43 2016 +1000 + + Correct LDFLAGS for clang example. - Upstream-Regress-ID: b164f20923812c9bac69856dbc1385eb1522cba4 + --with-ldflags isn't used until after the -ftrapv test, so mention + LDFLAGS instead for now. -commit 016881eb33a7948028848c90f4c7ac42e3af0e87 -Author: schwarze@openbsd.org -Date: Thu May 26 19:14:25 2016 +0000 +commit 1e8013a17ff11e3c6bd0012fb1fc8d5f1330eb21 +Author: Darren Tucker +Date: Wed Aug 17 14:08:42 2016 +1000 - upstream commit + Remove obsolete CVS $Id from source files. - test the new utf8 module + Since -portable switched to git the CVS $Id tags are no longer being + updated and are becoming increasingly misleading. Remove them. + +commit adab758242121181700e48b4f6c60d6b660411fe +Author: Darren Tucker +Date: Wed Aug 17 13:40:58 2016 +1000 + + Remove now-obsolete CVS $Id tags from text files. - Upstream-Regress-ID: c923d05a20e84e4ef152cbec947fdc4ce6eabbe3 + Since -portable switched to git, the CVS $Id tags are no longer being + updated and are becoming increasingly misleading. Remove them. -commit d4219028bdef448e089376f3afe81ef6079da264 -Author: dtucker@openbsd.org -Date: Tue May 3 15:30:46 2016 +0000 +commit 560c0068541315002ec4c1c00a560bbd30f2d671 +Author: Darren Tucker +Date: Wed Aug 17 13:38:30 2016 +1000 - upstream commit + Add a section for compiler specifics. - Set umask to prevent "Bad owner or permissions" errors. + Add a section for compiler specifics and document the runtime requirements + for clang's integer sanitization. + +commit a8fc0f42e1eda2fa3393d1ea5e61322d5e07a9cd +Author: Darren Tucker +Date: Wed Aug 17 13:35:43 2016 +1000 + + Test multiplying two long long ints. - Upstream-Regress-ID: 8fdf2fc4eb595ccd80c443f474d639f851145417 + When using clang with -ftrapv or -sanitize=integer the tests would pass + but linking would fail with "undefined reference to __mulodi4". + Explicitly test for this before enabling -trapv. -commit 07d5608bb237e9b3fe86a2aeaa429392230faebf -Author: djm@openbsd.org -Date: Tue May 3 14:41:04 2016 +0000 +commit a1cc637e7e11778eb727559634a6ef1c19c619f6 +Author: Damien Miller +Date: Tue Aug 16 14:47:34 2016 +1000 - upstream commit + add a --with-login-program configure argument - support doas + Saves messing around with LOGIN_PROGRAM env var, which come + packaging environments make hard to do during configure phase. + +commit 8bd81e1596ab1bab355146cb65e82fb96ade3b23 +Author: Damien Miller +Date: Tue Aug 16 13:30:56 2016 +1000 + + add --with-pam-service to specify PAM service name - Upstream-Regress-ID: 8d5572b27ea810394eeda432d8b4e9e1064a7c38 + Saves messing around with CFLAGS to do it. -commit 01cabf10adc7676cba5f40536a34d3b246edb73f -Author: djm@openbsd.org -Date: Tue May 3 13:48:33 2016 +0000 +commit 74433a19bb6f4cef607680fa4d1d7d81ca3826aa +Author: Damien Miller +Date: Tue Aug 16 13:28:23 2016 +1000 + + fix false positives when compiled with msan + + Our explicit_bzero successfully confused clang -fsanitize-memory + in to thinking that memset is never called to initialise memory. + Ensure that it is called in a way that the compiler recognises. + +commit 6cb6dcffe1a2204ba9006de20f73255c268fcb6b +Author: markus@openbsd.org +Date: Sat Aug 13 17:47:40 2016 +0000 upstream commit - unit tests for sshbuf_dup_string() + remove ssh1 server code; ok djm@ - Upstream-Regress-ID: 7521ff150dc7f20511d1c2c48fd3318e5850a96d + Upstream-ID: c24c0c32c49b91740d5a94ae914fb1898ea5f534 -commit 6915f1698e3d1dd4e22eac20f435e1dfc1d46372 -Author: jmc@openbsd.org -Date: Fri Jun 3 06:44:12 2016 +0000 +commit 42d47adc5ad1187f22c726cbc52e71d6b1767ca2 +Author: jca@openbsd.org +Date: Fri Aug 12 19:19:04 2016 +0000 upstream commit - tweak previous; + Use 2001:db8::/32, the official IPv6 subnet for + configuration examples. - Upstream-ID: 92979f1a0b63e041a0e5b08c9ed0ba9b683a3698 + This makes the IPv6 example consistent with IPv4, and removes a dubious + mention of a 6bone subnet. + + ok sthen@ millert@ + + Upstream-ID: b027f3d0e0073419a132fd1bf002e8089b233634 -commit 0cb2f4c2494b115d0f346ed2d8b603ab3ba643f4 +commit b61f53c0c3b43c28e013d3b3696d64d1c0204821 Author: dtucker@openbsd.org -Date: Fri Jun 3 04:09:38 2016 +0000 +Date: Thu Aug 11 01:42:11 2016 +0000 upstream commit - Allow ExitOnForwardFailure and ClearAllForwardings to be - overridden when using ssh -W (but still default to yes in that case). - bz#2577, ok djm@. + Update moduli file. - Upstream-ID: 4b20c419e93ca11a861c81c284090cfabc8c54d4 + Upstream-ID: 6da9a37f74aef9f9cc639004345ad893cad582d8 -commit 8543ff3f5020fe659839b15f05b8c522bde6cee5 +commit f217d9bd42d306f69f56335231036b44502d8191 +Author: Darren Tucker +Date: Thu Aug 11 11:42:48 2016 +1000 + + Import updated moduli. + +commit 67dca60fbb4923b7a11c1645b90a5ca57c03d8be Author: dtucker@openbsd.org -Date: Fri Jun 3 03:14:41 2016 +0000 +Date: Mon Aug 8 22:40:57 2016 +0000 upstream commit - Move the host and port used by ssh -W into the Options - struct. This will make future changes a bit easier. ok djm@ + Improve error message for overlong ControlPath. ok markus@ + djm@ - Upstream-ID: 151bce5ecab2fbedf0d836250a27968d30389382 + Upstream-ID: aed374e2e88dd3eb41390003e5303d0089861eb5 -commit 6b87311d3acdc460f926b2c40f4c4f3fd345f368 -Author: dtucker@openbsd.org -Date: Wed Jun 1 04:19:49 2016 +0000 +commit 4706c1d8c15cd5565b59512853c2da9bd4ca26c9 +Author: djm@openbsd.org +Date: Wed Aug 3 05:41:57 2016 +0000 upstream commit - Check min and max sizes sent by the client against what - we support before passing them to the monitor. ok djm@ + small refactor of cipher.c: make ciphercontext opaque to + callers feedback and ok markus@ - Upstream-ID: 750627e8117084215412bff00a25b1586ab17ece + Upstream-ID: 094849f8be68c3bdad2c0f3dee551ecf7be87f6f -commit 564cd2a8926ccb1dca43a535073540935b5e0373 +commit e600348a7afd6325cc5cd783cb424065cbc20434 Author: dtucker@openbsd.org -Date: Tue May 31 23:46:14 2016 +0000 +Date: Wed Aug 3 04:23:55 2016 +0000 upstream commit - Ensure that the client's proposed DH-GEX max value is at - least as big as the minimum the server will accept. ok djm@ + Fix bug introduced in rev 1.467 which causes + "buffer_get_bignum_ret: incomplete message" errors when built with WITH_SSH1 + and run such that no Protocol 1 ephemeral host key is generated (eg "Protocol + 2", no SSH1 host key supplied). Reported by rainer.laatsch at t-online.de, + ok deraadt@ - Upstream-ID: b4b84fa04aab2de7e79a6fee4a6e1c189c0fe775 + Upstream-ID: aa6b132da5c325523aed7989cc5a320497c919dc -commit df820722e40309c9b3f360ea4ed47a584ed74333 -Author: Darren Tucker -Date: Mon Jun 6 11:36:13 2016 +1000 +commit d7e7348e72f9b203189e3fffb75605afecba4fda +Author: djm@openbsd.org +Date: Wed Jul 27 23:18:12 2016 +0000 - Add compat bits to utf8.c. + upstream commit + + better bounds check on iovcnt (we only ever use fixed, + positive values) + + Upstream-ID: 9baa6eb5cd6e30c9dc7398e5fe853721a3a5bdee -commit 05c6574652571becfe9d924226c967a3f4b3f879 +commit 5faa52d295f764562ed6dd75c4a4ce9134ae71e3 Author: Darren Tucker -Date: Mon Jun 6 11:33:43 2016 +1000 +Date: Tue Aug 2 15:22:40 2016 +1000 - Fix utf->utf8 typo. + Use tabs consistently inside "case $host". -commit 6c1717190b4d5ddd729cd9e24e8ed71ed4f087ce -Author: schwarze@openbsd.org -Date: Mon May 30 18:34:41 2016 +0000 +commit 20e5e8ba9c5d868d897896190542213a60fffbd2 +Author: Darren Tucker +Date: Tue Aug 2 12:16:34 2016 +1000 - upstream commit - - Backout rev. 1.43 for now. + Explicitly test for broken strnvis. - The function update_progress_meter() calls refresh_progress_meter() - which calls snmprintf() which calls malloc(); but update_progress_meter() - acts as the SIGALRM signal handler. + NetBSD added an strnvis and unfortunately made it incompatible with the + existing one in OpenBSD and Linux's libbsd (the former having existed + for over ten years). Despite this incompatibility being reported during + development (see http://gnats.netbsd.org/44977) they still shipped it. + Even more unfortunately FreeBSD and later MacOS picked up this incompatible + implementation. Try to detect this mess, and assume the only safe option + if we're cross compiling. - "malloc(): error: recursive call" reported by sobrado@. + OpenBSD 2.9 (2001): strnvis(char *dst, const char *src, size_t dlen, int flag); + NetBSD 6.0 (2012): strnvis(char *dst, size_t dlen, const char *src, int flag); - Upstream-ID: aaae57989431e5239c101f8310f74ccc83aeb93e + ok djm@ -commit cd9e1eabeb4137182200035ab6fa4522f8d24044 -Author: schwarze@openbsd.org -Date: Mon May 30 12:57:21 2016 +0000 +commit b0b48beab1b74100b61ecbadb9140c9ab4c2ea8c +Author: Damien Miller +Date: Tue Aug 2 11:06:23 2016 +1000 - upstream commit - - Even when only writing an unescaped character, the dst - buffer may need to grow, or it would be overrun; issue found by tb@ with - malloc.conf(5) 'C'. + update recommended autoconf version + +commit 23902e31dfd18c6d7bb41ccd73de3b5358a377da +Author: Damien Miller +Date: Tue Aug 2 10:48:04 2016 +1000 + + update config.guess and config.sub to current - While here, reserve an additional byte for the terminating NUL - up front such that we don't have to realloc() later just for that. - - OK tb@ - - Upstream-ID: 30ebcc0c097c4571b16f0a78b44969f170db0cff - -commit ac284a355f8065eaef2a16f446f3c44cdd17371d -Author: schwarze@openbsd.org -Date: Mon May 30 12:05:56 2016 +0000 - - upstream commit - - Fix two rare edge cases: 1. If vasprintf() returns < 0, - do not access a NULL pointer in snmprintf(), and do not free() the pointer - returned from vasprintf() because on some systems other than OpenBSD, it - might be a bogus pointer. 2. If vasprintf() returns == 0, return 0 and "" - rather than -1 and NULL. - - Besides, free(dst) is pointless after failure (not a bug). - - One half OK martijn@, the other half OK deraadt@; - committing quickly before people get hurt. - - Upstream-ID: b7bcd2e82fc168a8eff94e41f5db336ed986fed0 - -commit 0e059cdf5fd86297546c63fa8607c24059118832 -Author: schwarze@openbsd.org -Date: Wed May 25 23:48:45 2016 +0000 - - upstream commit - - To prevent screwing up terminal settings when printing to - the terminal, for ASCII and UTF-8, escape bytes not forming characters and - bytes forming non-printable characters with vis(3) VIS_OCTAL. For other - character sets, abort printing of the current string in these cases. In - particular, * let scp(1) respect the local user's LC_CTYPE locale(1); * - sanitize data received from the remote host; * sanitize filenames, usernames, - and similar data even locally; * take character display widths into account - for the progressmeter. - - This is believed to be sufficient to keep the local terminal safe - on OpenBSD, but bad things can still happen on other systems with - state-dependent locales because many places in the code print - unencoded ASCII characters into the output stream. - - Using feedback from djm@ and martijn@, - various aspects discussed with many others. - - deraadt@ says it should go in now, i probably already hesitated too long - - Upstream-ID: e66afbc94ee396ddcaffd433b9a3b80f387647e0 - -commit 8c02e3639acefe1e447e293dbe23a0917abd3734 -Author: dtucker@openbsd.org -Date: Tue May 24 04:43:45 2016 +0000 - - upstream commit - - KNF compression proposal and simplify the client side a - little. ok djm@ - - Upstream-ID: aa814b694efe9e5af8a26e4c80a05526ae6d6605 - -commit 7ec4946fb686813eb5f8c57397e465f5485159f4 -Author: dtucker@openbsd.org -Date: Tue May 24 02:31:57 2016 +0000 - - upstream commit - - Back out 'plug memleak'. - - Upstream-ID: 4faacdde136c24a961e24538de373660f869dbc0 - -commit 82f24c3ddc52053aeb7beb3332fa94c92014b0c5 -Author: djm@openbsd.org -Date: Mon May 23 23:30:50 2016 +0000 - - upstream commit - - prefer agent-hosted keys to keys from PKCS#11; ok markus - - Upstream-ID: 7417f7653d58d6306d9f8c08d0263d050e2fd8f4 - -commit a0cb7778fbc9b43458f7072eb68dd858766384d1 -Author: dtucker@openbsd.org -Date: Mon May 23 00:17:27 2016 +0000 - - upstream commit - - Plug mem leak in filter_proposal. ok djm@ - - Upstream-ID: bf968da7cfcea2a41902832e7d548356a4e2af34 - -commit ae9c0d4d5c581b3040d1f16b5c5f4b1cd1616743 -Author: Darren Tucker -Date: Fri Jun 3 16:03:44 2016 +1000 - - Update vis.h and vis.c from OpenBSD. - - This will be needed for the upcoming utf8 changes. - -commit e1d93705f8f48f519433d6ca9fc3d0abe92a1b77 -Author: Tim Rice -Date: Tue May 31 11:13:22 2016 -0700 - - modified: configure.ac - whitspace clean up. No code changes. - -commit 604a037d84e41e31f0aec9075df0b8740c130200 -Author: Damien Miller -Date: Tue May 31 16:45:28 2016 +1000 - - whitespace at EOL - -commit 18424200160ff5c923113e0a37ebe21ab7bcd17c -Author: Darren Tucker -Date: Mon May 30 19:35:28 2016 +1000 - - Add missing ssh-host-config --name option - - Patch from vinschen@redhat.com. - -commit 39c0cecaa188a37a2e134795caa68e03f3ced592 -Author: Darren Tucker -Date: Fri May 20 10:01:58 2016 +1000 - - Fix comment about sshpam_const and AIX. - - From mschwager via github. - -commit f64062b1f74ad5ee20a8a49aab2732efd0f7ce30 -Author: Damien Miller -Date: Fri May 20 09:56:53 2016 +1000 - - Deny lstat syscalls in seccomp sandbox - - Avoids sandbox violations for some krb/gssapi libraries. - -commit 531c135409b8d8810795b1f3692a4ebfd5c9cae0 -Author: djm@openbsd.org -Date: Thu May 19 07:45:32 2016 +0000 - - upstream commit - - fix type of ed25519 values - - Upstream-ID: b32d0cb372bbe918ca2de56906901eae225a59b0 - -commit 75e21688f523799c9e0cc6601d76a9c5ca79f787 -Author: markus@openbsd.org -Date: Wed May 4 14:32:26 2016 +0000 - - upstream commit - - add IdentityAgent; noticed & ok jmc@ - - Upstream-ID: 4ba9034b00a4cf1beae627f0728da897802df88a - -commit 1a75d14daf4b60db903e6103cf50e74e0cd0a76b -Author: markus@openbsd.org -Date: Wed May 4 14:29:58 2016 +0000 - - upstream commit - - allow setting IdentityAgent to SSH_AUTH_SOCK; ok djm@ - - Upstream-ID: 20c508480d8db3eef18942c0fc39b1fcf25652ac - -commit 0516454151ae722fc8256c3c56115c6baf24c5b0 -Author: markus@openbsd.org -Date: Wed May 4 14:22:33 2016 +0000 - - upstream commit - - move SSH_MSG_NONE, so we don't have to include ssh1.h; - ok deraadt@ - - Upstream-ID: c2f97502efc761a41b18c17ddf460e138ca7994e - -commit 332ff3d770631e7513fea38cf0d3689f673f0e3f -Author: Damien Miller -Date: Tue May 10 09:51:06 2016 +1000 - - initialise salen in binresvport_sa - - avoids failures with UsePrivilegedPort=yes - - patch from Juan Gallego - -commit c5c1d5d2f04ce00d2ddd6647e61b32f28be39804 -Author: markus@openbsd.org -Date: Wed May 4 14:04:40 2016 +0000 - - upstream commit - - missing const in prototypes (ssh1) - - Upstream-ID: 789c6ad4928b5fa557369b88c3a6a34926082c05 - -commit 9faae50e2e82ba42eb0cb2726bf6830fe7948f28 -Author: dtucker@openbsd.org -Date: Wed May 4 14:00:09 2016 +0000 - - upstream commit - - Fix inverted logic for updating StreamLocalBindMask which - would cause the server to set an invalid mask. ok djm@ - - Upstream-ID: 8a4404c8307a5ef9e07ee2169fc6d8106b527587 - -commit b02ad1ce9105bfa7394ac7590c0729dd52e26a81 -Author: markus@openbsd.org -Date: Wed May 4 12:21:53 2016 +0000 - - upstream commit - - IdentityAgent for specifying specific agent sockets; ok - djm@ - - Upstream-ID: 3e6a15eb89ea0fd406f108826b7dc7dec4fbfac1 - -commit 910e59bba09ac309d78ce61e356da35292212935 -Author: djm@openbsd.org -Date: Wed May 4 12:16:39 2016 +0000 - - upstream commit - - fix junk characters after quotes - - Upstream-ID: cc4d0cd32cb6b55a2ef98975d2f7ae857d0dc578 - -commit 9283884e647b8be50ccd2997537af0065672107d -Author: jmc@openbsd.org -Date: Tue May 3 18:38:12 2016 +0000 - - upstream commit - - correct article; - - Upstream-ID: 1fbd5b7ab16d2d9834ec79c3cedd4738fa42a168 - -commit cfefbcea1057c2623e76c579174a4107a0b6e6cd -Author: djm@openbsd.org -Date: Tue May 3 15:57:39 2016 +0000 - - upstream commit - - fix overriding of StreamLocalBindMask and - StreamLocalBindUnlink in Match blocks; found the hard way Rogan Dawes - - Upstream-ID: 940bc69ec0249ab428d24ccd0722ce35cb932ee2 - -commit 771c2f51ffc0c9a2877b7892fada0c77bd1f6549 -Author: djm@openbsd.org -Date: Tue May 3 15:25:06 2016 +0000 - - upstream commit - - don't forget to include StreamLocalBindUnlink in the - config dump output - - Upstream-ID: 14a6d970b3b45c8e94272e3c661e9a0b2a0ee7cb - -commit cdcd941994dc430f50d0a4e6a712d32b66e6199e -Author: djm@openbsd.org -Date: Tue May 3 14:54:08 2016 +0000 - - upstream commit - - make nethack^wrandomart fingerprint flag more readily - searchable pointed out by Matt Johnston - - Upstream-ID: cb40d0235dc153c478c1aad3bc60b195422a54fb - -commit 05855bf2ce7d5cd0a6db18bc0b4214ed5ef7516d -Author: djm@openbsd.org -Date: Tue May 3 13:10:24 2016 +0000 - - upstream commit - - clarify ordering of subkeys; pointed out by ietf-ssh AT - stbuehler.de - - Upstream-ID: 05ebe9f949449a555ebce8e0aad7c8c9acaf8463 - -commit cca3b4395807bfb7aaeb83d2838f5c062ce30566 -Author: dtucker@openbsd.org -Date: Tue May 3 12:15:49 2016 +0000 - - upstream commit - - Use a subshell for constructing key types to work around - different sed behaviours for -portable. - - Upstream-Regress-ID: 0f6eb673162df229eda9a134a0f10da16151552d - -commit fa58208c6502dcce3e0daac0ca991ee657daf1f5 -Author: djm@openbsd.org -Date: Tue May 3 10:27:59 2016 +0000 - - upstream commit - - correct some typos and remove a long-stale XXX note. - - add specification for ed25519 certificates - - mention no host certificate options/extensions are currently defined - - pointed out by Simon Tatham - - Upstream-ID: 7b535ab7dba3340b7d8210ede6791fdaefdf839a - -commit b466f956c32cbaff4200bfcd5db6739fe4bc7d04 -Author: djm@openbsd.org -Date: Tue May 3 10:24:27 2016 +0000 - - upstream commit - - add ed25519 keys that are supported but missing from this - documents; from Peter Moody - - Upstream-ID: 8caac2d8e8cfd2fca6dc304877346e0a064b014b - -commit 7f3d76319a69dab2efe3a520a8fef5b97e923636 -Author: dtucker@openbsd.org -Date: Tue May 3 09:03:49 2016 +0000 - - upstream commit - - Implement IUTF8 as per draft-sgtatham-secsh-iutf8-00. Patch - from Simon Tatham, ok markus@ - - Upstream-ID: 58268ebdf37d9d467f78216c681705a5e10c58e8 - -commit 31bc01c05d9f51bee3ebe33dc57c4fafb059fb62 -Author: djm@openbsd.org -Date: Mon May 2 14:10:58 2016 +0000 - - upstream commit - - unbreak config parsing on reexec from previous commit - - Upstream-ID: bc69932638a291770955bd05ca55a32660a613ab - -commit 67f1459efd2e85bf03d032539283fa8107218936 -Author: djm@openbsd.org -Date: Mon May 2 09:52:00 2016 +0000 - - upstream commit - - unit and regress tests for SHA256/512; ok markus - - Upstream-Regress-ID: a0cd1a92dc824067076a5fcef83c18df9b0bf2c6 - -commit 0e8eeec8e75f6d0eaf33317376f773160018a9c7 -Author: djm@openbsd.org -Date: Mon May 2 10:26:04 2016 +0000 - - upstream commit - - add support for additional fixed DH groups from - draft-ietf-curdle-ssh-kex-sha2-03 - - diffie-hellman-group14-sha256 (2K group) - diffie-hellman-group16-sha512 (4K group) - diffie-hellman-group18-sha512 (8K group) - - based on patch from Mark D. Baushke and Darren Tucker - ok markus@ - - Upstream-ID: ac00406ada4f0dfec41585ca0839f039545bc46f - -commit 57464e3934ba53ad8590ee3ccd840f693407fc1e -Author: djm@openbsd.org -Date: Mon May 2 09:36:42 2016 +0000 - - upstream commit - - support SHA256 and SHA512 RSA signatures in certificates; - ok markus@ - - Upstream-ID: b45be2f2ce8cacd794dc5730edaabc90e5eb434a - -commit 1a31d02b2411c4718de58ce796dbb7b5e14db93e -Author: djm@openbsd.org -Date: Mon May 2 08:49:03 2016 +0000 - - upstream commit - - fix signed/unsigned errors reported by clang-3.7; add - sshbuf_dup_string() to replace a common idiom of strdup(sshbuf_ptr()) with - better safety checking; feedback and ok markus@ - - Upstream-ID: 71f926d9bb3f1efed51319a6daf37e93d57c8820 - -commit d2d6bf864e52af8491a60dd507f85b74361f5da3 -Author: djm@openbsd.org -Date: Fri Apr 29 08:07:53 2016 +0000 - - upstream commit - - close ControlPersist background process stderr when not - in debug mode or when logging to a file or syslog. bz#1988 ok dtucker - - Upstream-ID: 4fb726f0fdcb155ad419913cea10dc4afd409d24 - -commit 9ee692fa1146e887e008a2b9a3d3ea81770c9fc8 -Author: djm@openbsd.org -Date: Thu Apr 28 14:30:21 2016 +0000 - - upstream commit - - fix comment - - Upstream-ID: 313a385bd7b69a82f8e28ecbaf5789c774457b15 - -commit ee1e0a16ff2ba41a4d203c7670b54644b6c57fa6 -Author: jmc@openbsd.org -Date: Wed Apr 27 13:53:48 2016 +0000 - - upstream commit - - cidr permitted for {allow,deny}users; from lars nooden ok djm - - Upstream-ID: 13e7327fe85f6c63f3f7f069e0fdc8c351515d11 - -commit b6e0140a5aa883c27b98415bd8aa9f65fc04ee22 -Author: djm@openbsd.org -Date: Thu Apr 21 06:08:02 2016 +0000 - - upstream commit - - make argument == NULL tests more consistent - - Upstream-ID: dc4816678704aa5cbda3a702e0fa2033ff04581d - -commit 6aaabc2b610e44bae473457ad9556ffb43d90ee3 -Author: jmc@openbsd.org -Date: Sun Apr 17 14:34:46 2016 +0000 - - upstream commit - - tweak previous; - - Upstream-ID: 46c1bab91c164078edbccd5f7d06b9058edd814f - -commit 0f839e5969efa3bda615991be8a9d9311554c573 -Author: djm@openbsd.org -Date: Fri Apr 15 02:57:10 2016 +0000 - - upstream commit - - missing bit of Include regress - - Upstream-Regress-ID: 1063595f7f40f8489a1b7a27230b9e8acccea34f - -commit 12e4ac46aed681da55c2bba3cd11dfcab23591be -Author: djm@openbsd.org -Date: Fri Apr 15 02:55:53 2016 +0000 - - upstream commit - - remove redundant CLEANFILES section - - Upstream-Regress-ID: 29ef1b267fa56daa60a1463396635e7d53afb587 - -commit b1d05aa653ae560c44baf8e8a9756e33f98ea75c -Author: djm@openbsd.org -Date: Fri Apr 15 00:48:01 2016 +0000 - - upstream commit - - sync CLEANFILES with portable, sort - - Upstream-Regress-ID: cb782f4f1ab3e079efbc335c6b64942f790766ed - -commit 35f22dad263cce5c61d933ae439998cb965b8748 -Author: djm@openbsd.org -Date: Fri Apr 15 00:31:10 2016 +0000 - - upstream commit - - regression test for ssh_config Include directive - - Upstream-Regress-ID: 46a38c8101f635461c506d1aac2d96af80f97f1e - -commit 6b8a1a87005818d4700ce8b42faef746e82c1f51 -Author: djm@openbsd.org -Date: Thu Apr 14 23:57:17 2016 +0000 - - upstream commit - - unbreak test for recent ssh de-duplicated forwarding - change - - Upstream-Regress-ID: 6b2b115d99acd7cff13986e6739ea214cf2a3da3 - -commit 076787702418985a2cc6808212dc28ce7afc01f0 -Author: djm@openbsd.org -Date: Thu Apr 14 23:21:42 2016 +0000 - - upstream commit - - add test knob and warning for StrictModes - - Upstream-Regress-ID: 8cd10952ce7898655ee58945904f2a0a3bdf7682 - -commit dc7990be865450574c7940c9880567f5d2555b37 -Author: djm@openbsd.org -Date: Fri Apr 15 00:30:19 2016 +0000 - - upstream commit - - Include directive for ssh_config(5); feedback & ok markus@ - - Upstream-ID: ae3b76e2e343322b9f74acde6f1e1c5f027d5fff - -commit 85bdcd7c92fe7ff133bbc4e10a65c91810f88755 -Author: Damien Miller -Date: Wed Apr 13 10:39:57 2016 +1000 - - ignore PAM environment vars when UseLogin=yes - - If PAM is configured to read user-specified environment variables - and UseLogin=yes in sshd_config, then a hostile local user may - attack /bin/login via LD_PRELOAD or similar environment variables - set via PAM. - - CVE-2015-8325, found by Shayan Sadigh, via Colin Watson - -commit dce19bf6e4a2a3d0b13a81224de63fc316461ab9 -Author: djm@openbsd.org -Date: Sat Apr 9 12:39:30 2016 +0000 - - upstream commit - - make private key loading functions consistently handle NULL - key pointer arguments; ok markus@ - - Upstream-ID: 92038726ef4a338169c35dacc9c5a07fcc7fa761 - -commit 5f41f030e2feb5295657285aa8c6602c7810bc4b -Author: Darren Tucker -Date: Fri Apr 8 21:14:13 2016 +1000 - - Remove NO_IPPORT_RESERVED_CONCEPT - - Replace by defining IPPORT_RESERVED to zero on Cygwin, which should have - the same effect without causing problems syncing patches with OpenBSD. - Resync the two affected functions with OpenBSD. ok djm, sanity checked - by Corinna. - -commit 34a01b2cf737d946ddb140618e28c3048ab7a229 -Author: djm@openbsd.org -Date: Fri Apr 8 08:19:17 2016 +0000 - - upstream commit - - whitespace at EOL - - Upstream-ID: 5beffd4e001515da12851b974e2323ae4aa313b6 - -commit 90ee563fa6b54c59896c6c332c5188f866c5e75f -Author: djm@openbsd.org -Date: Fri Apr 8 06:35:54 2016 +0000 - - upstream commit - - We accidentally send an empty string and a zero uint32 with - every direct-streamlocal@openssh.com channel open, in contravention of our - own spec. - - Fixing this is too hard wrt existing versions that expect these - fields to be present and fatal() if they aren't, so document them - as "reserved" fields in the PROTOCOL spec as though we always - intended this and let us never speak of it again. - - bz#2529, reported by Ron Frederick - - Upstream-ID: 34cd326a4d236ca6e39084c4ff796bd97ab833e7 - -commit 0ccbd5eca0f0dd78e71a4b69c66f03a66908d558 -Author: djm@openbsd.org -Date: Wed Apr 6 06:42:17 2016 +0000 - - upstream commit - - don't record duplicate LocalForward and RemoteForward - entries; fixes failure with ExitOnForwardFailure+hostname canonicalisation - where the same forwards are added on the second pass through the - configuration file. bz#2562; ok dtucker@ - - Upstream-ID: 40a51d68b6300f1cc61deecdb7d4847b8b7b0de1 - -commit 574def0eb493cd6efeffd4ff2e9257abcffee0c8 -Author: krw@openbsd.org -Date: Sat Apr 2 14:37:42 2016 +0000 - - upstream commit - - Another use for fcntl() and thus of the superfluous 3rd - parameter is when sanitising standard fd's before calling daemon(). - - Use a tweaked version of the ssh(1) function in all three places - found using fcntl() this way. - - ok jca@ beck@ - - Upstream-ID: f16811ffa19a1c5f4ef383c5f0fecb843c84e218 - -commit b3413534aa9d71a941005df2760d1eec2c2b0854 -Author: Darren Tucker -Date: Mon Apr 4 11:09:21 2016 +1000 - - Tidy up openssl header test. - -commit 815bcac0b94bb448de5acdd6ba925b8725240b4f -Author: Darren Tucker -Date: Mon Apr 4 11:07:59 2016 +1000 - - Fix configure-time warnings for openssl test. - -commit 95687f5831ae680f7959446d8ae4b52452ee05dd -Author: djm@openbsd.org -Date: Fri Apr 1 02:34:10 2016 +0000 - - upstream commit - - whitespace at EOL - - Upstream-ID: 40ae2203d07cb14e0a89e1a0d4c6120ee8fd8c3a - -commit fdfbf4580de09d84a974211715e14f88a5704b8e -Author: dtucker@openbsd.org -Date: Thu Mar 31 05:24:06 2016 +0000 - - upstream commit - - Remove fallback from moduli to "primes" file that was - deprecated in 2001 and fix log messages referring to primes file. Based on - patch from xnox at ubuntu.com via bz#2559. "kill it" deraadt@ - - Upstream-ID: 0d4f8c70e2fa7431a83b95f8ca81033147ba8713 - -commit 0235a5fa67fcac51adb564cba69011a535f86f6b -Author: djm@openbsd.org -Date: Thu Mar 17 17:19:43 2016 +0000 - - upstream commit - - UseDNS affects ssh hostname processing in authorized_keys, - not known_hosts; bz#2554 reported by jjelen AT redhat.com - - Upstream-ID: c1c1bb895dde46095fc6d81d8653703928437591 - -commit 8c4739338f5e379d05b19d6e544540114965f07e -Author: Darren Tucker -Date: Tue Mar 15 09:24:43 2016 +1100 - - Don't call Solaris setproject() with UsePAM=yes. - - When Solaris Projects are enabled along with PAM setting the project - is PAM's responsiblity. bz#2425, based on patch from - brent.paulson at gmail.com. - -commit cff26f373c58457a32cb263e212cfff53fca987b -Author: Damien Miller -Date: Tue Mar 15 04:30:21 2016 +1100 - - remove slogin from *.spec - -commit c38905ba391434834da86abfc988a2b8b9b62477 -Author: djm@openbsd.org -Date: Mon Mar 14 16:20:54 2016 +0000 - - upstream commit - - unbreak authentication using lone certificate keys in - ssh-agent: when attempting pubkey auth with a certificate, if no separate - private key is found among the keys then try with the certificate key itself. - - bz#2550 reported by Peter Moody - - Upstream-ID: f939cd76d68e6a9a3d1711b5a943d6ed1e623966 - -commit 4b4bfb01cd40b9ddb948e6026ddd287cc303d871 -Author: djm@openbsd.org -Date: Thu Mar 10 11:47:57 2016 +0000 - - upstream commit - - sanitise characters destined for xauth reported by - github.com/tintinweb feedback and ok deraadt and markus - - Upstream-ID: 18ad8d0d74cbd2ea3306a16595a306ee356aa261 - -commit 732b463d37221722b1206f43aa59563766a6a968 -Author: Darren Tucker -Date: Mon Mar 14 16:04:23 2016 +1100 - - Pass supported malloc options to connect-privsep. - - This allows us to activate only the supported options during the malloc - option portion of the connect-privsep test. - -commit d29c5b9b3e9f27394ca97a364ed4bb4a55a59744 -Author: Darren Tucker -Date: Mon Mar 14 09:30:58 2016 +1100 - - Remove leftover roaming.h file. - - Pointed out by des at des.no. - -commit 8ff20ec95f4377021ed5e9b2331320f5c5a34cea -Author: Darren Tucker -Date: Mon Mar 14 09:24:03 2016 +1100 - - Quote variables that may contain whitespace. - - The variable $L_TMP_ID_FILE needs to be surrounded by quotes in order to - survive paths containing whitespace. bz#2551, from Corinna Vinschen via - Philip Hands. - -commit 627824480c01f0b24541842c7206ab9009644d02 -Author: Darren Tucker -Date: Fri Mar 11 14:47:41 2016 +1100 - - Include priv.h for priv_set_t. - - From alex at cooperi.net. - -commit e960051f9a264f682c4d2fefbeecffcfc66b0ddf -Author: Darren Tucker -Date: Wed Mar 9 13:14:18 2016 +1100 - - Wrap stdint.h inside #ifdef HAVE_STDINT_H. - -commit 2c48bd344d2c4b5e08dae9aea5ff44fc19a5e363 -Author: Darren Tucker -Date: Wed Mar 9 12:46:50 2016 +1100 - - Add compat to monotime_double(). - - Apply all of the portability changes in monotime() to monotime() double. - Fixes build on at least older FreeBSD systems. - -commit 7b40ef6c2eef40c339f6ea8920cb8a44838e10c9 -Author: Damien Miller -Date: Tue Mar 8 14:12:58 2016 -0800 - - make a regress-binaries target - - Easier to build all the regression/unit test binaries in one pass - than going through all of ${REGRESS_BINARIES} - -commit c425494d6b6181beb54a1b3763ef9e944fd3c214 -Author: Damien Miller -Date: Tue Mar 8 14:03:54 2016 -0800 - - unbreak kexfuzz for -Werror without __bounded__ - -commit 3ed9218c336607846563daea5d5ab4f701f4e042 -Author: Damien Miller -Date: Tue Mar 8 14:01:29 2016 -0800 - - unbreak PAM after canohost refactor - -commit 885fb2a44ff694f01e4f6470f803629e11f62961 -Author: Darren Tucker -Date: Tue Mar 8 11:58:43 2016 +1100 - - auth_get_canonical_hostname in portable code. - - "refactor canohost.c" replaced get_canonical_hostname, this makes the - same change to some portable-specific code. - -commit 95767262caa6692eff1e1565be1f5cb297949a89 -Author: djm@openbsd.org -Date: Mon Mar 7 19:02:43 2016 +0000 - - upstream commit - - refactor canohost.c: move functions that cache results closer - to the places that use them (authn and session code). After this, no state is - cached in canohost.c - - feedback and ok markus@ - - Upstream-ID: 5f2e4df88d4803fc8ec59ec53629105e23ce625e - -commit af0bb38ffd1f2c4f9f43b0029be2efe922815255 -Author: Damien Miller -Date: Fri Mar 4 15:11:55 2016 +1100 - - hook unittests/misc/kexfuzz into build - -commit 331b8e07ee5bcbdca12c11cc8f51a7e8de09b248 -Author: dtucker@openbsd.org -Date: Fri Mar 4 02:48:06 2016 +0000 - - upstream commit - - Filter debug messages out of log before picking the last - two lines. Should prevent problems if any more debug output is added late in - the connection. - - Upstream-Regress-ID: 345d0a9589c381e7d640a4ead06cfaadf4db1363 - -commit 0892edaa3ce623381d3a7635544cbc69b31cf9cb -Author: djm@openbsd.org -Date: Fri Mar 4 02:30:36 2016 +0000 - - upstream commit - - add KEX fuzzer harness; ok deraadt@ - - Upstream-Regress-ID: 3df5242d30551b12b828aa9ba4a4cec0846be8d1 - -commit ae2562c47d41b68dbb00240fd6dd60bed205367a -Author: dtucker@openbsd.org -Date: Thu Mar 3 00:46:53 2016 +0000 - - upstream commit - - Look back 3 lines for possible error messages. Changes - to the code mean that "Bad packet length" errors are 3 lines back instead of - the previous two, which meant we didn't skip some offsets that we intended - to. - - Upstream-Regress-ID: 24f36912740a634d509a3144ebc8eb7c09b9c684 - -commit 988e429d903acfb298bfddfd75e7994327adfed0 -Author: djm@openbsd.org -Date: Fri Mar 4 03:35:44 2016 +0000 - - upstream commit - - fix ClientAliveInterval when a time-based RekeyLimit is - set; previously keepalive packets were not being sent. bz#2252 report and - analysis by Christian Wittenhorst and Garrett Lee feedback and ok dtucker@ - - Upstream-ID: d48f9deadd35fdacdd5106b41bb07630ddd4aa81 - -commit 8ef04d7a94bcdb8b0085fdd2a79a844b7d40792d -Author: dtucker@openbsd.org -Date: Wed Mar 2 22:43:52 2016 +0000 - - upstream commit - - Improve accuracy of reported transfer speeds by waiting - for the ack from the other end. Pointed out by mmcc@, ok deraadt@ markus@ - - Upstream-ID: 99f1cf15c9a8f161086b814d414d862795ae153d - -commit b8d4eafe29684fe4f5bb587f7eab948e6ed62723 -Author: dtucker@openbsd.org -Date: Wed Mar 2 22:42:40 2016 +0000 - - upstream commit - - Improve precision of progressmeter for sftp and scp by - storing sub-second timestamps. Pointed out by mmcc@, ok deraadt@ markus@ - - Upstream-ID: 38fd83a3d83dbf81c8ff7b5d1302382fe54970ab - -commit 18f64b969c70ed00e74b9d8e50359dbe698ce4c0 -Author: jca@openbsd.org -Date: Mon Feb 29 20:22:36 2016 +0000 - - upstream commit - - Print ssize_t with %zd; ok deraadt@ mmcc@ - - Upstream-ID: 0590313bbb013ff6692298c98f7e0be349d124bd + upstream commit 562f3512b3911ba0c77a7f68214881d1f241f46e -commit 6e7f68ce38130c794ec1fb8d2a6091fbe982628d -Author: djm@openbsd.org -Date: Sun Feb 28 22:27:00 2016 +0000 +commit dd1031b78b83083615b68d7163c44f4408635be2 +Author: Darren Tucker +Date: Tue Aug 2 10:01:52 2016 +1000 - upstream commit - - rearrange DH public value tests to be a little more clear - - rearrange DH private value generation to explain rationale more - clearly and include an extra sanity check. - - ok deraadt + Replace spaces with tabs. - Upstream-ID: 9ad8a07e1a12684e1b329f9bd88941b249d4b2ad + Mechanically replace spaces with tabs in compat files not synced with + OpenBSD. -commit 2ed17aa34008bdfc8db674315adc425a0712be11 +commit c20dccb5614c5714f4155dda01bcdebf97cfae7e Author: Darren Tucker -Date: Tue Mar 1 15:24:20 2016 +1100 +Date: Tue Aug 2 09:44:25 2016 +1000 - Import updated moduli file from OpenBSD. + Strip trailing whitespace. - Note that 1.5k bit groups have been removed. + Mechanically strip trailing whitespace on files not synced with OpenBSD + (or in the case of bsd-snprint.c, rsync). -commit 72b061d4ba0f909501c595d709ea76e06b01e5c9 +commit 30f9bd1c0963c23bfba8468dfd26aa17609ba42f Author: Darren Tucker -Date: Fri Feb 26 14:40:04 2016 +1100 +Date: Tue Aug 2 09:06:27 2016 +1000 - Add a note about using xlc on AIX. + Repair $OpenBSD markers. -commit fd4e4f2416baa2e6565ea49d52aade296bad3e28 +commit 9715d4ad4b53877ec23dc8681dd7a405de9419a6 Author: Darren Tucker -Date: Wed Feb 24 10:44:25 2016 +1100 +Date: Tue Aug 2 09:02:42 2016 +1000 - Skip PrintLastLog in config dump mode. - - When DISABLE_LASTLOG is set, do not try to include PrintLastLog in the - config dump since it'll be reported as UNKNOWN. + Repair $OpenBSD marker. -commit 99135c764fa250801da5ec3b8d06cbd0111caae8 -Author: Damien Miller -Date: Tue Feb 23 20:17:23 2016 +1100 +commit cf3e0be7f5828a5e5f6c296a607d20be2f07d60c +Author: Tim Rice +Date: Mon Aug 1 14:31:52 2016 -0700 - update spec/README versions ahead of release + modified: configure.ac opensshd.init.in + Skip generating missing RSA1 key on startup unless ssh1 support is enabled. + Spotted by Jean-Pierre Radley -commit b86a334aaaa4d1e643eb1fd71f718573d6d948b5 +commit 99522ba7ec6963a05c04a156bf20e3ba3605987c Author: Damien Miller -Date: Tue Feb 23 20:16:53 2016 +1100 - - put back portable patchlevel to p1 - -commit 555dd35ff176847e3c6bd068ba2e8db4022eb24f -Author: djm@openbsd.org -Date: Tue Feb 23 09:14:34 2016 +0000 +Date: Thu Jul 28 08:54:27 2016 +1000 - upstream commit - - openssh-7.2 + define _OPENBSD_SOURCE for reallocarray on NetBSD - Upstream-ID: 9db776b26014147fc907ece8460ef2bcb0f11e78 + Report by and debugged with Hisashi T Fujinaka, dtucker nailed + the problem (lack of prototype causing return type confusion). -commit 1acc058d0a7913838c830ed998a1a1fb5b7864bf +commit 3e1e076550c27c6bbdddf36d8f42bd79fbaaa187 Author: Damien Miller -Date: Tue Feb 23 16:12:13 2016 +1100 +Date: Wed Jul 27 08:25:42 2016 +1000 - Disable tests where fs perms are incorrect - - Some tests have strict requirements on the filesystem permissions - for certain files and directories. This adds a regress/check-perm - tool that copies the relevant logic from sshd to exactly test - the paths in question. This lets us skip tests when the local - filesystem doesn't conform to our expectations rather than - continuing and failing the test run. - - ok dtucker@ + KNF -commit 39f303b1f36d934d8410b05625f25c7bcb75db4d +commit d99ee9c4e5e217e7d05eeec84e9ce641f4675331 Author: Damien Miller -Date: Tue Feb 23 12:56:59 2016 +1100 - - fix sandbox on OSX Lion - - sshd was failing with: - - ssh_sandbox_child: sandbox_init: dlopen(/usr/lib/libsandbox.1.dylib, 261):cw - image not found [preauth] - - caused by chroot before sandboxing. Avoid by explicitly linking libsandbox - to sshd. Spotted by Darren. - -commit 0d1451a32c7436e6d3d482351e776bc5e7824ce4 -Author: djm@openbsd.org -Date: Tue Feb 23 01:34:14 2016 +0000 - - upstream commit - - fix spurious error message when incorrect passphrase - entered for keys; reported by espie@ ok deraadt@ - - Upstream-ID: 58b2e46e63ed6912ed1ee780bd3bd8560f9a5899 - -commit 09d87d79741beb85768b5e788d7dfdf4bc3543dc -Author: sobrado@openbsd.org -Date: Sat Feb 20 23:06:23 2016 +0000 - - upstream commit - - set ssh(1) protocol version to 2 only. - - ok djm@ - - Upstream-ID: e168daf9d27d7e392e3c9923826bd8e87b2b3a10 - -commit 9262e07826ba5eebf8423f7ac9e47ec488c47869 -Author: sobrado@openbsd.org -Date: Sat Feb 20 23:02:39 2016 +0000 - - upstream commit - - add missing ~/.ssh/id_ecdsa and ~/.ssh/id_ed25519 to - IdentityFile. - - ok djm@ - - Upstream-ID: 6ce99466312e4ae7708017c3665e3edb976f70cf - -commit c12f0fdce8f985fca8d71829fd64c5b89dc777f5 -Author: sobrado@openbsd.org -Date: Sat Feb 20 23:01:46 2016 +0000 - - upstream commit - - AddressFamily defaults to any. - - ok djm@ - - Upstream-ID: 0d94aa06a4b889bf57a7f631c45ba36d24c13e0c - -commit 907091acb188b1057d50c2158f74c3ecf1c2302b -Author: Darren Tucker -Date: Fri Feb 19 09:05:39 2016 +1100 - - Make Solaris privs code build on older systems. - - Not all systems with Solaris privs have priv_basicset so factor that - out and provide backward compatibility code. Similarly, not all have - PRIV_NET_ACCESS so wrap that in #ifdef. Based on code from - alex at cooperi.net and djm@ with help from carson at taltos.org and - wieland at purdue.edu. - -commit 292a8dee14e5e67dcd1b49ba5c7b9023e8420d59 -Author: djm@openbsd.org -Date: Wed Feb 17 22:20:14 2016 +0000 +Date: Wed Jul 27 08:25:23 2016 +1000 - upstream commit - - rekey refactor broke SSH1; spotted by Tom G. Christensen - - Upstream-ID: 43f0d57928cc077c949af0bfa71ef574dcb58243 + Linux auditing also needs packet.h -commit 3a13cb543df9919aec2fc6b75f3dd3802facaeca -Author: djm@openbsd.org -Date: Wed Feb 17 08:57:34 2016 +0000 +commit 393bd381a45884b589baa9aed4394f1d250255ca +Author: Damien Miller +Date: Wed Jul 27 08:18:05 2016 +1000 - upstream commit - - rsa-sha2-512,rsa-sha2-256 cannot be selected explicitly - in *KeyTypes options yet. Remove them from the lists of algorithms for now. - committing on behalf of markus@ ok djm@ + fix auditing on Linux - Upstream-ID: c6e8820eb8e610ac21551832c0c89684a9a51bb7 + get_remote_ipaddr() was replaced with ssh_remote_ipaddr() -commit a685ae8d1c24fb7c712c55a4f3280ee76f5f1e4b -Author: jmc@openbsd.org -Date: Wed Feb 17 07:38:19 2016 +0000 +commit 80e766fb089de4f3c92b1600eb99e9495e37c992 +Author: Damien Miller +Date: Sun Jul 24 21:50:13 2016 +1000 - upstream commit - - since these pages now clearly tell folks to avoid v1, - normalise the docs from a v2 perspective (i.e. stop pointing out which bits - are v2 only); - - ok/tweaks djm ok markus - - Upstream-ID: eb474f8c36fb6a532dc05c282f7965e38dcfa129 + crank version numbers -commit c5c3f3279a0e4044b8de71b70d3570d692d0f29d +commit b1a478792d458f2e938a302e64bab2b520edc1b3 Author: djm@openbsd.org -Date: Wed Feb 17 05:29:04 2016 +0000 +Date: Sun Jul 24 11:45:36 2016 +0000 upstream commit - make sandboxed privilege separation the default, not just - for new installs; "absolutely" deraadt@ + openssh-7.3 - Upstream-ID: 5221ef3b927d2df044e9aa3f5db74ae91743f69b + Upstream-ID: af106a7eb665f642648cf1993e162c899f358718 -commit eb3f7337a651aa01d5dec019025e6cdc124ed081 -Author: jmc@openbsd.org -Date: Tue Feb 16 07:47:54 2016 +0000 +commit 353766e0881f069aeca30275ab706cd60a1a8fdd +Author: Darren Tucker +Date: Sat Jul 23 16:14:42 2016 +1000 - upstream commit - - no need to state that protocol 2 is the default twice; + Move Cygwin IPPORT_RESERVED overrride to defines.h - Upstream-ID: b1e4c36b0c2e12e338e5b66e2978f2ac953b95eb + Patch from vinschen at redhat.com. -commit e7901efa9b24e5b0c7e74f2c5520d47eead4d005 +commit 368dd977ae07afb93f4ecea23615128c95ab2b32 Author: djm@openbsd.org -Date: Tue Feb 16 05:11:04 2016 +0000 +Date: Sat Jul 23 02:54:08 2016 +0000 upstream commit - Replace list of ciphers and MACs adjacent to -1/-2 flag - descriptions in ssh(1) with a strong recommendation not to use protocol 1. - Add a similar warning to the Protocol option descriptions in ssh_config(5) - and sshd_config(5); - - prompted by and ok mmcc@ + fix pledge violation with ssh -f; reported by Valentin + Kozamernik ok dtucker@ - Upstream-ID: 961f99e5437d50e636feca023978950a232ead5e + Upstream-ID: a61db7988db88d9dac3c4dd70e18876a8edf84aa -commit 5a0fcb77287342e2fc2ba1cee79b6af108973dc2 +commit f00211e3c6d24d6ea2b64b4b1209f671f6c1d42e Author: djm@openbsd.org -Date: Tue Feb 16 03:37:48 2016 +0000 +Date: Fri Jul 22 07:00:46 2016 +0000 upstream commit - add a "Close session" log entry (at loglevel=verbose) to - correspond to the existing "Starting session" one. Also include the session - id number to make multiplexed sessions more apparent. - - feedback and ok dtucker@ + improve wording; suggested by jmc@ - Upstream-ID: e72d2ac080e02774376325136e532cb24c2e617c + Upstream-ID: 55cb0a24c8e0618b3ceec80998dc82c85db2d2f8 -commit 624fd395b559820705171f460dd33d67743d13d6 -Author: djm@openbsd.org -Date: Wed Feb 17 02:24:17 2016 +0000 +commit 83cbca693c3b0719270e6a0f2efe3f9ee93a65b8 +Author: dtucker@openbsd.org +Date: Fri Jul 22 05:46:11 2016 +0000 upstream commit - include bad $SSH_CONNECTION in failure output + Lower loglevel for "Authenticated with partial success" + message similar to other similar level. bz#2599, patch from cgallek at + gmail.com, ok markus@ - Upstream-Regress-ID: b22d72edfde78c403aaec2b9c9753ef633cc0529 + Upstream-ID: 3faab814e947dc7b2e292edede23e94c608cb4dd -commit 60d860e54b4f199e5e89963b1c086981309753cb -Author: Darren Tucker -Date: Wed Feb 17 13:37:09 2016 +1100 +commit 10358abd087ab228b7ce2048efc4f3854a9ab9a6 +Author: Damien Miller +Date: Fri Jul 22 14:06:36 2016 +1000 - Rollback addition of va_start. + retry waitpid on EINTR failure - va_start was added in 0f754e29dd3760fc0b172c1220f18b753fb0957e, however - it has the wrong number of args and it's not usable in non-variadic - functions anyway so it breaks things (for example Solaris 2.6 as - reported by Tom G. Christensen).i ok djm@ + patch from Jakub Jelen on bz#2581; ok dtucker@ -commit 2fee909c3cee2472a98b26eb82696297b81e0d38 -Author: Darren Tucker -Date: Wed Feb 17 09:48:15 2016 +1100 +commit da88a70a89c800e74ea8e5661ffa127a3cc79a92 +Author: djm@openbsd.org +Date: Fri Jul 22 03:47:36 2016 +0000 - Look for gethostbyname in libresolv and libnsl. + upstream commit - Should fix build problem on Solaris 2.6 reported by Tom G. Christensen. + constify a few functions' arguments; patch from Jakub + Jelen bz#2581 + + Upstream-ID: f2043f51454ea37830ff6ad60c8b32b4220f448d -commit 5ac712d81a84396aab441a272ec429af5b738302 -Author: Damien Miller -Date: Tue Feb 16 10:45:02 2016 +1100 +commit c36d91bd4ebf767f310f7cea88d61d1c15f53ddf +Author: djm@openbsd.org +Date: Fri Jul 22 03:39:13 2016 +0000 - make existing ssh_malloc_init only for __OpenBSD__ + upstream commit + + move debug("%p", key) to before key is free'd; probable + undefined behaviour on strict compilers; reported by Jakub Jelen bz#2581 + + Upstream-ID: 767f323e1f5819508a0e35e388ec241bac2f953a -commit 24c9bded569d9f2449ded73f92fb6d12db7a9eec +commit 286f5a77c3bfec1e8892ca268087ac885ac871bf Author: djm@openbsd.org -Date: Mon Feb 15 23:32:37 2016 +0000 +Date: Fri Jul 22 03:35:11 2016 +0000 upstream commit - memleak of algorithm name in mm_answer_sign; reported by - Jakub Jelen + reverse the order in which -J/JumpHost proxies are visited to + be more intuitive and document - Upstream-ID: ccd742cd25952240ebd23d7d4d6b605862584d08 + reported by and manpage bits naddy@ + + Upstream-ID: 3a68fd6a841fd6cf8cedf6552a9607ba99df179a -commit ffb1e7e896139a42ceb78676f637658f44612411 +commit fcd135c9df440bcd2d5870405ad3311743d78d97 Author: dtucker@openbsd.org -Date: Mon Feb 15 09:47:49 2016 +0000 +Date: Thu Jul 21 01:39:35 2016 +0000 upstream commit - Add a function to enable security-related malloc_options. - With and ok deraadt@, something similar has been in the snaps for a while. + Skip passwords longer than 1k in length so clients can't + easily DoS sshd by sending very long passwords, causing it to spend CPU + hashing them. feedback djm@, ok markus@. - Upstream-ID: 43a95523b832b7f3b943d2908662191110c380ed - -commit ef39e8c0497ff0564990a4f9e8b7338b3ba3507c -Author: Damien Miller -Date: Tue Feb 16 10:34:39 2016 +1100 - - sync ssh-copy-id with upstream 783ef08b0a75 + Brought to our attention by tomas.kuthan at oracle.com, shilei-c at + 360.cn and coredump at autistici.org + + Upstream-ID: d0af7d4a2190b63ba1d38eec502bc4be0be9e333 -commit d2d772f55b19bb0e8d03c2fe1b9bb176d9779efd -Author: djm@openbsd.org -Date: Fri Feb 12 00:20:30 2016 +0000 +commit 324583e8fb3935690be58790425793df619c6d4d +Author: naddy@openbsd.org +Date: Wed Jul 20 10:45:27 2016 +0000 upstream commit - avoid fatal() for PKCS11 tokens that present empty key IDs - bz#1773, ok markus@ + Do not clobber the global jump_host variables when + parsing an inactive configuration. ok djm@ - Upstream-ID: 044a764fee526f2c4a9d530bd10695422d01fc54 + Upstream-ID: 5362210944d91417d5976346d41ac0b244350d31 -commit e4c918a6c721410792b287c9fd21356a1bed5805 -Author: djm@openbsd.org -Date: Thu Feb 11 02:56:32 2016 +0000 +commit 32d921c323b989d28405e78d0a8923d12913d737 +Author: jmc@openbsd.org +Date: Tue Jul 19 12:59:16 2016 +0000 upstream commit - sync crypto algorithm lists in ssh_config(5) and - sshd_config(5) with current reality. bz#2527 + tweak previous; - Upstream-ID: d7fd1b6c1ed848d866236bcb1d7049d2bb9b2ff6 + Upstream-ID: f3c1a5b3f05dff366f60c028728a2b43f15ff534 -commit e30cabfa4ab456a30b3224f7f545f1bdfc4a2517 -Author: djm@openbsd.org -Date: Thu Feb 11 02:21:34 2016 +0000 +commit d7eabc86fa049a12ba2c3fb198bd1d51b37f7025 +Author: dtucker@openbsd.org +Date: Tue Jul 19 11:38:53 2016 +0000 upstream commit - fix regression in openssh-6.8 sftp client: existing - destination directories would incorrectly terminate recursive uploads; - bz#2528 + Allow wildcard for PermitOpen hosts as well as ports. + bz#2582, patch from openssh at mzpqnxow.com and jjelen at redhat.com. ok + markus@ - Upstream-ID: 3306be469f41f26758e3d447987ac6d662623e18 + Upstream-ID: af0294e9b9394c4e16e991424ca0a47a7cc605f2 -commit 714e367226ded4dc3897078be48b961637350b05 -Author: djm@openbsd.org -Date: Tue Feb 9 05:30:04 2016 +0000 +commit b98a2a8348e907b3d71caafd80f0be8fdd075943 +Author: markus@openbsd.org +Date: Mon Jul 18 11:35:33 2016 +0000 upstream commit - turn off more old crypto in the client: hmac-md5, ripemd, - truncated HMACs, RC4, blowfish. ok markus@ dtucker@ + Reduce timing attack against obsolete CBC modes by always + computing the MAC over a fixed size of data. Reported by Jean Paul + Degabriele, Kenny Paterson, Torben Hansen and Martin Albrecht. ok djm@ - Upstream-ID: 96aa11c2c082be45267a690c12f1d2aae6acd46e + Upstream-ID: f20a13279b00ba0afbacbcc1f04e62e9d41c2912 -commit 5a622844ff7f78dcb75e223399f9ef0977e8d0a3 -Author: djm@openbsd.org -Date: Mon Feb 8 23:40:12 2016 +0000 +commit dbf788b4d9d9490a5fff08a7b09888272bb10fcc +Author: Darren Tucker +Date: Thu Jul 21 14:17:31 2016 +1000 - upstream commit + Search users for one with a valid salt. - don't attempt to percent_expand() already-canonicalised - addresses, avoiding unnecessary failures when attempting to connect to scoped - IPv6 addresses (that naturally contain '%' characters) + If the root account is locked (eg password "!!" or "*LK*") keep looking + until we find a user with a valid salt to use for crypting passwords of + invalid users. ok djm@ + +commit e8b58f48fbb1b524fb4f0d4865fa0005d6a4b782 +Author: Darren Tucker +Date: Mon Jul 18 17:22:49 2016 +1000 + + Explicitly specify source files for regress tools. - Upstream-ID: f24569cffa1a7cbde5f08dc739a72f4d78aa5c6a + Since adding $(REGRESSLIBS), $? is wrong because it includes only the + changed source files. $< seems like it'd be right however it doesn't + seem to work on some non-GNU makes, so do what works everywhere. -commit 19bcf2ea2d17413f2d9730dd2a19575ff86b9b6a +commit eac1bbd06872c273f16ac0f9976b0aef026b701b +Author: Darren Tucker +Date: Mon Jul 18 17:12:22 2016 +1000 + + Conditionally include err.h. + +commit 0a454147568746c503f669e1ba861f76a2e7a585 +Author: Darren Tucker +Date: Mon Jul 18 16:26:26 2016 +1000 + + Remove local implementation of err, errx. + + We now have a shared implementation in libopenbsd-compat. + +commit eb999a4590846ba4d56ddc90bd07c23abfbab7b1 Author: djm@openbsd.org -Date: Mon Feb 8 10:57:07 2016 +0000 +Date: Mon Jul 18 06:08:01 2016 +0000 upstream commit - refactor activation of rekeying - - This makes automatic rekeying internal to the packet code (previously - the server and client loops needed to assist). In doing to it makes - application of rekey limits more accurate by accounting for packets - about to be sent as well as packets queued during rekeying events - themselves. + Add some unsigned overflow checks for extra_pad. None of + these are reachable with the amount of padding that we use internally. + bz#2566, pointed out by Torben Hansen. ok markus@ - Based on a patch from dtucker@ which was in turn based on a patch - Aleksander Adamowski in bz#2521; ok markus@ + Upstream-ID: 4d4be8450ab2fc1b852d5884339f8e8c31c3fd76 + +commit c71ba790c304545464bb494de974cdf0f4b5cf1e +Author: Darren Tucker +Date: Mon Jul 18 15:43:25 2016 +1000 + + Add dependency on libs for unit tests. - Upstream-ID: a441227fd64f9739850ca97b4cf794202860fcd8 + Makes "./configure && make tests" work again. ok djm@ -commit 603ba41179e4b53951c7b90ee95b6ef3faa3f15d -Author: naddy@openbsd.org -Date: Fri Feb 5 13:28:19 2016 +0000 +commit 8199d0311aea3e6fd0284c9025e7a83f4ece79e8 +Author: Darren Tucker +Date: Mon Jul 18 13:47:39 2016 +1000 - upstream commit + Correct location for kexfuzz in clean target. + +commit 01558b7b07af43da774d3a11a5c51fa9c310849d +Author: Darren Tucker +Date: Mon Jul 18 09:33:25 2016 +1000 + + Handle PAM_MAXTRIES from modules. - Only check errno if read() has returned an error. EOF is - not an error. This fixes a problem where the mux master would sporadically - fail to notice that the client had exited. ok mikeb@ djm@ + bz#2249: handle the case where PAM returns PAM_MAXTRIES by ceasing to offer + password and keyboard-interative authentication methods. Should prevent + "sshd ignoring max retries" warnings in the log. ok djm@ - Upstream-ID: 3c2dadc21fac6ef64665688aac8a75fffd57ae53 + It probably won't trigger with keyboard-interactive in the default + configuration because the retry counter is stored in module-private + storage which goes away with the sshd PAM process (see bz#688). On the + other hand, those cases probably won't log a warning either. -commit 56d7dac790693ce420d225119283bc355cff9185 -Author: jsg@openbsd.org -Date: Fri Feb 5 04:31:21 2016 +0000 +commit 65c6c6b567ab5ab12945a5ad8e0ab3a8c26119cc +Author: djm@openbsd.org +Date: Sun Jul 17 04:20:16 2016 +0000 upstream commit - avoid an uninitialised value when NumberOfPasswordPrompts - is 0 ok markus@ djm@ + support UTF-8 characters in ssh(1) banners using + schwarze@'s safe fmprintf printer; bz#2058 - Upstream-ID: 11b068d83c2865343aeb46acf1e9eec00f829b6b + feedback schwarze@ ok dtucker@ + + Upstream-ID: a72ce4e3644c957643c9524eea2959e41b91eea7 -commit deae7d52d59c5019c528f977360d87fdda15d20b -Author: djm@openbsd.org -Date: Fri Feb 5 03:07:06 2016 +0000 +commit e4eb7d910976fbfc7ce3e90c95c11b07b483d0d7 +Author: jmc@openbsd.org +Date: Sat Jul 16 06:57:55 2016 +0000 upstream commit - mention internal DH-GEX fallback groups; bz#2302 + - add proxyjump to the options list - formatting fixes - + update usage() - Upstream-ID: e7b395fcca3122cd825515f45a2e41c9a157e09e + ok djm + + Upstream-ID: 43d318e14ce677a2eec8f21ef5ba2f9f68a59457 -commit cac3b6665f884d46192c0dc98a64112e8b11a766 -Author: djm@openbsd.org -Date: Fri Feb 5 02:37:56 2016 +0000 +commit af1f084857621f14bd9391aba8033d35886c2455 +Author: dtucker@openbsd.org +Date: Fri Jul 15 05:01:58 2016 +0000 upstream commit - better description for MaxSessions; bz#2531 + Reduce the syslog level of some relatively common protocol + events from LOG_CRIT by replacing fatal() calls with logdie(). Part of + bz#2585, ok djm@ - Upstream-ID: e2c0d74ee185cd1a3e9d4ca1f1b939b745b354da + Upstream-ID: 9005805227c94edf6ac02a160f0e199638d288e5 -commit 5ef4b0fdcc7a239577a754829b50022b91ab4712 +commit bd5f2b78b69cf38d6049a0de445a79c8595e4a1f Author: Damien Miller -Date: Wed Jan 27 17:45:56 2016 +1100 +Date: Fri Jul 15 19:14:48 2016 +1000 - avoid FreeBSD RCS Id in comment - - Change old $FreeBSD version string in comment so it doesn't - become an RCS ident downstream; requested by des AT des.no + missing openssl/dh.h -commit 696d12683c90d20a0a9c5f4275fc916b7011fb04 -Author: djm@openbsd.org -Date: Thu Feb 4 23:43:48 2016 +0000 +commit 4a984fd342effe5f0aad874a0d538c4322d973c0 +Author: Damien Miller +Date: Fri Jul 15 18:47:07 2016 +1000 - upstream commit - - printf argument casts to avoid warnings on strict - compilers + cast to avoid type warning in error message + +commit 5abfb15ced985c340359ae7fb65a625ed3692b3e +Author: Darren Tucker +Date: Fri Jul 15 14:48:30 2016 +1000 + + Move VA_COPY macro into compat header. - Upstream-ID: 7b9f6712cef01865ad29070262d366cf13587c9c + Some AIX compilers unconditionally undefine va_copy but don't set it back + to an internal function, causing link errors. In some compat code we + already use VA_COPY instead so move the two existing instances into the + shared header and use for sshbuf-getput-basic.c too. Should fix building + with at lease some versions of AIX's compiler. bz#2589, ok djm@ -commit 5658ef2501e785fbbdf5de2dc33b1ff7a4dca73a -Author: millert@openbsd.org -Date: Mon Feb 1 21:18:17 2016 +0000 +commit 832b7443b7a8e181c95898bc5d73497b7190decd +Author: Damien Miller +Date: Fri Jul 15 14:45:34 2016 +1000 - upstream commit + disable ciphers not supported by OpenSSL - Avoid ugly "DISPLAY "(null)" invalid; disabling X11 - forwarding" message when DISPLAY is not set. This could also result in a - crash on systems with a printf that doesn't handle NULL. OK djm@ + bz#2466 ok dtucker@ + +commit 5fbe93fc6fbb2fe211e035703dec759d095e3dd8 +Author: Damien Miller +Date: Fri Jul 15 13:54:31 2016 +1000 + + add a --disable-pkcs11 knob + +commit 679ce88ec2a8e2fe6515261c489e8c1449bb9da9 +Author: Damien Miller +Date: Fri Jul 15 13:44:38 2016 +1000 + + fix newline escaping for unsupported_algorithms - Upstream-ID: 20ee0cfbda678a247264c20ed75362042b90b412 + The hmac-ripemd160 was incorrect and could lead to broken + Makefiles on systems that lacked support for it, but I made + all the others consistent too. -commit 537f88ec7bcf40bd444ac5584c707c5588c55c43 -Author: dtucker@openbsd.org -Date: Fri Jan 29 05:18:15 2016 +0000 +commit ed877ef653847d056bb433975d731b7a1132a979 +Author: djm@openbsd.org +Date: Fri Jul 15 00:24:30 2016 +0000 upstream commit - Add regression test for RekeyLimit parsing of >32bit values - (4G and 8G). + Add a ProxyJump ssh_config(5) option and corresponding -J + ssh(1) command-line flag to allow simplified indirection through a SSH + bastion or "jump host". - Upstream-Regress-ID: 548390350c62747b6234f522a99c319eee401328 + These options construct a proxy command that connects to the + specified jump host(s) (more than one may be specified) and uses + port-forwarding to establish a connection to the next destination. + + This codifies the safest way of indirecting connections through SSH + servers and makes it easy to use. + + ok markus@ + + Upstream-ID: fa899cb8b26d889da8f142eb9774c1ea36b04397 -commit 4c6cb8330460f94e6c7ae28a364236d4188156a3 -Author: dtucker@openbsd.org -Date: Fri Jan 29 23:04:46 2016 +0000 +commit 5c02dd126206a26785379e80f2d3848e4470b711 +Author: Darren Tucker +Date: Fri Jul 15 12:56:39 2016 +1000 - upstream commit - - Remove leftover roaming dead code. ok djm markus. + Map umac_ctx struct name too. - Upstream-ID: 13d1f9c8b65a5109756bcfd3b74df949d53615be + Prevents size mismatch linker warnings on Solaris 11. -commit 28136471809806d6246ef41e4341467a39fe2f91 -Author: djm@openbsd.org -Date: Fri Jan 29 05:46:01 2016 +0000 +commit 283b97ff33ea2c641161950849931bd578de6946 +Author: Darren Tucker +Date: Fri Jul 15 13:49:44 2016 +1000 - upstream commit + Mitigate timing of disallowed users PAM logins. - include packet type of non-data packets in debug3 output; - ok markus dtucker + When sshd decides to not allow a login (eg PermitRootLogin=no) and + it's using PAM, it sends a fake password to PAM so that the timing for + the failure is not noticeably different whether or not the password + is correct. This behaviour can be detected by sending a very long + password string which is slower to hash than the fake password. - Upstream-ID: 034eaf639acc96459b9c5ce782db9fcd8bd02d41 + Mitigate by constructing an invalid password that is the same length + as the one from the client and thus takes the same time to hash. + Diff from djm@ -commit 6fd6e28daccafaa35f02741036abe64534c361a1 -Author: dtucker@openbsd.org -Date: Fri Jan 29 03:31:03 2016 +0000 +commit 9286875a73b2de7736b5e50692739d314cd8d9dc +Author: Darren Tucker +Date: Fri Jul 15 13:32:45 2016 +1000 - upstream commit + Determine appropriate salt for invalid users. - Revert "account for packets buffered but not yet - processed" change as it breaks for very small RekeyLimit values due to - continuous rekeying. ok djm@ + When sshd is processing a non-PAM login for a non-existent user it uses + the string from the fakepw structure as the salt for crypt(3)ing the + password supplied by the client. That string has a Blowfish prefix, so on + systems that don't understand that crypt will fail fast due to an invalid + salt, and even on those that do it may have significantly different timing + from the hash methods used for real accounts (eg sha512). This allows + user enumeration by, eg, sending large password strings. This was noted + by EddieEzra.Harari at verint.com (CVE-2016-6210). - Upstream-ID: 7e03f636cb45ab60db18850236ccf19079182a19 + To mitigate, use the same hash algorithm that root uses for hashing + passwords for users that do not exist on the system. ok djm@ -commit 921ff00b0ac429666fb361d2d6cb1c8fff0006cb -Author: dtucker@openbsd.org -Date: Fri Jan 29 02:54:45 2016 +0000 +commit a162dd5e58ca5b224d7500abe35e1ef32b5de071 +Author: Darren Tucker +Date: Thu Jul 14 21:19:59 2016 +1000 - upstream commit - - Allow RekeyLimits in excess of 4G up to 2**63 bits - (limited by the return type of scan_scaled). Part of bz#2521, ok djm. + OpenSSL 1.1.x not currently supported. + +commit 7df91b01fc558a33941c5c5f31abbcdc53a729fb +Author: Darren Tucker +Date: Thu Jul 14 12:25:24 2016 +1000 + + Check for VIS_ALL. - Upstream-ID: 13bea82be566b9704821b1ea05bf7804335c7979 + If we don't have it, set BROKEN_STRNVIS to activate the compat replacement. -commit c0060a65296f01d4634f274eee184c0e93ba0f23 +commit ee67716f61f1042d5e67f91c23707cca5dcdd7d0 Author: dtucker@openbsd.org -Date: Fri Jan 29 02:42:46 2016 +0000 +Date: Thu Jul 14 01:24:21 2016 +0000 upstream commit - Account for packets buffered but not yet processed when - computing whether or not it is time to perform rekeying. bz#2521, based - loosely on a patch from olo at fb.com, ok djm@ + Correct equal in test. - Upstream-ID: 67e268b547f990ed220f3cb70a5624d9bda12b8c + Upstream-Regress-ID: 4e32f7a5c57a619c4e8766cb193be2a1327ec37a -commit 44cf930e670488c85c9efeb373fa5f4b455692ac -Author: djm@openbsd.org -Date: Wed Jan 27 06:44:58 2016 +0000 +commit 372807c2065c8572fdc6478b25cc5ac363743073 +Author: tb@openbsd.org +Date: Mon Jul 11 21:38:13 2016 +0000 upstream commit - change old $FreeBSD version string in comment so it doesn't - become an RCS ident downstream; requested by des AT des.no + Add missing "recvfd" pledge promise: Raf Czlonka reported + ssh coredumps when Control* keywords were set in ssh_config. This patch also + fixes similar problems with scp and sftp. - Upstream-ID: 8ca558c01f184e596b45e4fc8885534b2c864722 + ok deraadt, looks good to millert + + Upstream-ID: ca2099eade1ef3e87a79614fefa26a0297ad8a3b -commit ebacd377769ac07d1bf3c75169644336056b7060 -Author: djm@openbsd.org -Date: Wed Jan 27 00:53:12 2016 +0000 +commit e0453f3df64bf485c61c7eb6bd12893eee9fe2cd +Author: tedu@openbsd.org +Date: Mon Jul 11 03:19:44 2016 +0000 upstream commit - make the debug messages a bit more useful here + obsolete note about fascistloggin is obsolete. ok djm + dtucker - Upstream-ID: 478ccd4e897e0af8486b294aa63aa3f90ab78d64 + Upstream-ID: dae60df23b2bb0e89f42661ddd96a7b0d1b7215a -commit 458abc2934e82034c5c281336d8dc0f910aecad3 -Author: jsg@openbsd.org -Date: Sat Jan 23 05:31:35 2016 +0000 +commit a2333584170a565adf4f209586772ef8053b10b8 +Author: Darren Tucker +Date: Thu Jul 14 10:59:09 2016 +1000 - upstream commit + Add compat code for missing wcwidth. - Zero a stack buffer with explicit_bzero() instead of - memset() when returning from client_loop() for consistency with - buffer_free()/sshbuf_free(). + If we don't have wcwidth force fallback implementations of nl_langinfo + and mbtowc. Based on advice from Ingo Schwarze. + +commit 8aaec7050614494014c47510b7e94daf6e644c62 +Author: Damien Miller +Date: Thu Jul 14 09:48:48 2016 +1000 + + fix missing include for systems with err.h + +commit 6310ef27a2567cda66d6cf0c1ad290ee1167f243 +Author: Darren Tucker +Date: Wed Jul 13 14:42:35 2016 +1000 + + Move err.h replacements into compat lib. - ok dtucker@ deraadt@ djm@ + Move implementations of err.h replacement functions into their own file + in the libopenbsd-compat so we can use them in kexfuzz.c too. ok djm@ + +commit f3f2cc8386868f51440c45210098f65f9787449a +Author: Darren Tucker +Date: Mon Jul 11 17:23:38 2016 +1000 + + Check for wchar.h and langinfo.h - Upstream-ID: bc9975b2095339811c3b954694d7d15ea5c58f66 + Wrap includes in the appropriate #ifdefs. -commit 65a3c0dacbc7dbb75ddb6a70ebe22d8de084d0b0 -Author: dtucker@openbsd.org -Date: Wed Jan 20 09:22:39 2016 +0000 +commit b9c50614eba9d90939b2b119b6e1b7e03b462278 +Author: Damien Miller +Date: Fri Jul 8 13:59:13 2016 +1000 - upstream commit - - Include sys/time.h for gettimeofday. From sortie at - maxsi.org. + whitelist more architectures for seccomp-bpf - Upstream-ID: 6ed0c33b836d9de0a664cd091e86523ecaa2fb3b + bz#2590 - testing and patch from Jakub Jelen -commit fc77ccdc2ce6d5d06628b8da5048a6a5f6ffca5a -Author: markus@openbsd.org -Date: Thu Jan 14 22:56:56 2016 +0000 +commit 18813a32b6fd964037e0f5e1893cb4468ac6a758 +Author: guenther@openbsd.org +Date: Mon Jul 4 18:01:44 2016 +0000 upstream commit - fd leaks; report Qualys Security Advisory team; ok - deraadt@ + DEBUGLIBS has been broken since the gcc4 switch, so delete + it. CFLAGS contains -g by default anyway - Upstream-ID: 4ec0f12b9d8fa202293c9effa115464185aa071d + problem noted by Edgar Pettijohn (edgar (at) pettijohn-web.com) + ok millert@ kettenis@ deraadt@ + + Upstream-Regress-ID: 4a0bb72f95c63f2ae9daa8a040ac23914bddb542 -commit a306863831c57ec5fad918687cc5d289ee8e2635 -Author: markus@openbsd.org -Date: Thu Jan 14 16:17:39 2016 +0000 +commit 6d31193d0baa3da339c196ac49625b7ba1c2ecc7 +Author: djm@openbsd.org +Date: Fri Jul 8 03:44:42 2016 +0000 upstream commit - remove roaming support; ok djm@ + Improve crypto ordering for Encrypt-then-MAC (EtM) mode + MAC algorithms. - Upstream-ID: 2cab8f4b197bc95776fb1c8dc2859dad0c64dc56 - -commit 6ef49e83e30688504552ac10875feabd5521565f -Author: deraadt@openbsd.org -Date: Thu Jan 14 14:34:34 2016 +0000 - - upstream commit + Previously we were computing the MAC, decrypting the packet and then + checking the MAC. This gave rise to the possibility of creating a + side-channel oracle in the decryption step, though no such oracle has + been identified. - Disable experimental client-side roaming support. Server - side was disabled/gutted for years already, but this aspect was surprisingly - forgotten. Thanks for report from Qualys + This adds a mac_check() function that computes and checks the MAC in + one pass, and uses it to advance MAC checking for EtM algorithms to + before payload decryption. - Upstream-ID: 2328004b58f431a554d4c1bf67f5407eae3389df - -commit 8d7b523b96d3be180572d9d338cedaafc0570f60 -Author: Damien Miller -Date: Thu Jan 14 11:08:19 2016 +1100 - - bump version numbers - -commit 8c3d512a1fac8b9c83b4d0c9c3f2376290bd84ca -Author: Damien Miller -Date: Thu Jan 14 11:04:04 2016 +1100 - - openssh-7.1p2 - -commit e6c85f8889c5c9eb04796fdb76d2807636b9eef5 -Author: Damien Miller -Date: Fri Jan 15 01:30:36 2016 +1100 - - forcibly disable roaming support in the client + Reported by Jean Paul Degabriele, Kenny Paterson, Torben Hansen and + Martin Albrecht. feedback and ok markus@ + + Upstream-ID: 1999bb67cab47dda5b10b80d8155fe83d4a1867b -commit ed4ce82dbfa8a3a3c8ea6fa0db113c71e234416c -Author: djm@openbsd.org -Date: Wed Jan 13 23:04:47 2016 +0000 +commit 71f5598f06941f645a451948c4a5125c83828e1c +Author: guenther@openbsd.org +Date: Mon Jul 4 18:01:44 2016 +0000 upstream commit - eliminate fallback from untrusted X11 forwarding to trusted - forwarding when the X server disables the SECURITY extension; Reported by - Thomas Hoger; ok deraadt@ + DEBUGLIBS has been broken since the gcc4 switch, so + delete it. CFLAGS contains -g by default anyway - Upstream-ID: f76195bd2064615a63ef9674a0e4096b0713f938 + problem noted by Edgar Pettijohn (edgar (at) pettijohn-web.com) + ok millert@ kettenis@ deraadt@ + + Upstream-ID: 96c5054e3e1f170c6276902d5bc65bb3b87a2603 -commit 9a728cc918fad67c8a9a71201088b1e150340ba4 -Author: djm@openbsd.org -Date: Tue Jan 12 23:42:54 2016 +0000 +commit e683fc6f1c8c7295648dbda679df8307786ec1ce +Author: dtucker@openbsd.org +Date: Thu Jun 30 05:17:05 2016 +0000 upstream commit - use explicit_bzero() more liberally in the buffer code; ok - deraadt + Explicitly check for 100% completion to avoid potential + floating point rounding error, which could cause progressmeter to report 99% + on completion. While there invert the test so the 100% case is clearer. with + & ok djm@ - Upstream-ID: 0ece37069fd66bc6e4f55eb1321f93df372b65bf + Upstream-ID: a166870c5878e422f3c71ff802e2ccd7032f715d -commit 4626cbaf78767fc8e9c86dd04785386c59ae0839 -Author: Damien Miller -Date: Fri Jan 8 14:24:56 2016 +1100 +commit 772e6cec0ed740fc7db618dc30b4134f5a358b43 +Author: jmc@openbsd.org +Date: Wed Jun 29 17:14:28 2016 +0000 - Support Illumos/Solaris fine-grained privileges + upstream commit - Includes a pre-auth privsep sandbox and several pledge() - emulations. bz#2511, patch by Alex Wilson. + sort the -o list; - ok dtucker@ + Upstream-ID: 1a97465ede8790b4d47cb618269978e07f41f8ac -commit 422d1b3ee977ff4c724b597fb2e437d38fc8de9d +commit 46ecd19e554ccca15a7309cd1b6b44bc8e6b84af Author: djm@openbsd.org -Date: Thu Dec 31 00:33:52 2015 +0000 +Date: Thu Jun 23 05:17:51 2016 +0000 upstream commit - fix three bugs in KRL code related to (unused) signature - support: verification length was being incorrectly calculated, multiple - signatures were being incorrectly processed and a NULL dereference that - occurred when signatures were verified. Reported by Carl Jackson + fix AuthenticationMethods during configuration re-parse; + reported by Juan Francisco Cantero Hurtado - Upstream-ID: e705e97ad3ccce84291eaa651708dd1b9692576b + Upstream-ID: 8ffa1dac25c7577eca8238e825317ab20848f9b4 -commit 6074c84bf95d00f29cc7d5d3cd3798737851aa1a +commit 3147e7595d0f2f842a666c844ac53e6c7a253d7e Author: djm@openbsd.org -Date: Wed Dec 30 23:46:14 2015 +0000 +Date: Sun Jun 19 07:48:02 2016 +0000 upstream commit - unused prototype + revert 1.34; causes problems loading public keys - Upstream-ID: f3eef4389d53ed6c0d5c77dcdcca3060c745da97 + reported by semarie@ + + Upstream-ID: b393794f8935c8b15d98a407fe7721c62d2ed179 -commit 6213f0e180e54122bb1ba928e11c784e2b4e5380 -Author: guenther@openbsd.org -Date: Sat Dec 26 20:51:35 2015 +0000 +commit ad23a75509f4320d43f628c50f0817e3ad12bfa7 +Author: jmc@openbsd.org +Date: Fri Jun 17 06:33:30 2016 +0000 upstream commit - Use pread/pwrite instead separate lseek+read/write for - lastlog. Cast to off_t before multiplication to avoid truncation on ILP32 - - ok kettenis@ mmcc@ + grammar fix; - Upstream-ID: fc40092568cd195719ddf1a00aa0742340d616cf + Upstream-ID: 5d5b21c80f1e81db367333ce0bb3e5874fb3e463 -commit d7d2bc95045a43dd56ea696cc1d030ac9d77e81f -Author: semarie@openbsd.org -Date: Sat Dec 26 07:46:03 2015 +0000 +commit 5e28b1a2a3757548b40018cc2493540a17c82e27 +Author: djm@openbsd.org +Date: Fri Jun 17 05:06:23 2016 +0000 upstream commit - adjust pledge promises for ControlMaster: when using - "ask" or "autoask", the process will use ssh-askpass for asking confirmation. - - problem found by halex@ - - ok halex@ + translate OpenSSL error codes to something more + meaninful; bz#2522 reported by Jakub Jelen, ok dtucker@ - Upstream-ID: 38a58b30ae3eef85051c74d3c247216ec0735f80 + Upstream-ID: 4cb0795a366381724314e6515d57790c5930ffe5 -commit 271df8185d9689b3fb0523f58514481b858f6843 +commit b64faeb5eda7eff8210c754d00464f9fe9d23de5 Author: djm@openbsd.org -Date: Sun Dec 13 22:42:23 2015 +0000 +Date: Fri Jun 17 05:03:40 2016 +0000 upstream commit - unbreak connections with peers that set - first_kex_follows; fix from Matt Johnston va bz#2515 + ban AuthenticationMethods="" and accept + AuthenticationMethods=any for the default behaviour of not requiring multiple + authentication - Upstream-ID: decc88ec4fc7515594fdb42b04aa03189a44184b + bz#2398 from Jakub Jelen; ok dtucker@ + + Upstream-ID: fabd7f44d59e4518d241d0d01e226435cc23cf27 -commit 43849a47c5f8687699eafbcb5604f6b9c395179f -Author: doug@openbsd.org -Date: Fri Dec 11 17:41:37 2015 +0000 +commit 9816fc5daee5ca924dd5c4781825afbaab728877 +Author: dtucker@openbsd.org +Date: Thu Jun 16 11:00:17 2016 +0000 upstream commit - Add "id" to ssh-agent pledge for subprocess support. - - Found the hard way by Jan Johansson when using ssh-agent with X. Also, - rearranged proc/exec and retval to match other pledge calls in the tree. - - ok djm@ + Include stdarg.h for va_copy as per man page. - Upstream-ID: 914255f6850e5e7fa830a2de6c38605333b584db + Upstream-ID: 105d6b2f1af2fbd9d91c893c436ab121434470bd -commit 52d7078421844b2f88329f5be3de370b0a938636 -Author: mmcc@openbsd.org -Date: Fri Dec 11 04:21:11 2015 +0000 +commit b6cf84b51bc0f5889db48bf29a0c771954ade283 +Author: jmc@openbsd.org +Date: Thu Jun 16 06:10:45 2016 +0000 upstream commit - Remove NULL-checks before sshbuf_free(). + keys stored in openssh format can have comments too; diff + from yonas yanfa, tweaked a bit; - ok djm@ + ok djm - Upstream-ID: 5ebed00ed5f9f03b119a345085e8774565466917 + Upstream-ID: 03d48536da6e51510d73ade6fcd44ace731ceb27 -commit a4b9e0f4e4a6980a0eb8072f76ea611cab5b77e7 -Author: djm@openbsd.org -Date: Fri Dec 11 03:24:25 2015 +0000 +commit aa37768f17d01974b6bfa481e5e83841b6c76f86 +Author: Darren Tucker +Date: Mon Jun 20 15:55:34 2016 +1000 - upstream commit - - include remote port number in a few more messages; makes - tying log messages together into a session a bit easier; bz#2503 ok dtucker@ + get_remote_name_or_ip inside LOGIN_NEEDS_UTMPX - Upstream-ID: 9300dc354015f7a7368d94a8ff4a4266a69d237e + Apply the same get_remote_name_or_ip -> session_get_remote_name_or_ip + change as commit 95767262 to the code inside #ifdef LOGIN_NEEDS_UTMPX. + Fixes build on AIX. -commit 6091c362e89079397e68744ae30df121b0a72c07 -Author: djm@openbsd.org -Date: Fri Dec 11 03:20:09 2015 +0000 +commit 009891afc8df37bc2101e15d1e0b6433cfb90549 +Author: Darren Tucker +Date: Fri Jun 17 14:34:09 2016 +1000 + + Remove duplicate code from PAM. ok djm@ + +commit e690fe85750e93fca1fb7c7c8587d4130a4f7aba +Author: dtucker@openbsd.org +Date: Wed Jun 15 00:40:40 2016 +0000 upstream commit - don't try to load SSHv1 private key when compiled without - SSHv1 support. From Iain Morgan bz#2505 + Remove "POSSIBLE BREAK-IN ATTEMPT!" from log message + about forward and reverse DNS not matching. We haven't supported IP-based + auth methods for a very long time so it's now misleading. part of bz#2585, + ok markus@ - Upstream-ID: 8b8e7b02a448cf5e5635979df2d83028f58868a7 + Upstream-ID: 5565ef0ee0599b27f0bd1d3bb1f8a323d8274e29 -commit cce6a36bb95e81fa8bfb46daf22eabcf13afc352 -Author: djm@openbsd.org -Date: Fri Dec 11 03:19:09 2015 +0000 +commit 57b4ee04cad0d3e0fec1194753b0c4d31e39a1cd +Author: Darren Tucker +Date: Wed Jun 15 11:22:38 2016 +1000 - upstream commit - - use SSH_MAX_PUBKEY_BYTES consistently as buffer size when - reading key files. Increase it to match the size of the buffers already being - used. + Move platform_disable_tracing into its own file. - Upstream-ID: 1b60586b484b55a947d99a0b32bd25e0ced56fae + Prevents link errors resolving the extern "options" when platform.o + gets linked into ssh-agent when building --with-pam. -commit 89540b6de025b80404a0cb8418c06377f3f98848 -Author: mmcc@openbsd.org -Date: Fri Dec 11 02:31:47 2015 +0000 +commit 78dc8e3724e30ee3e1983ce013e80277dc6ca070 +Author: Darren Tucker +Date: Tue Jun 14 13:55:12 2016 +1000 - upstream commit + Track skipped upstream commit IDs. - Remove NULL-checks before sshkey_free(). + There are a small number of "upstream" commits that do not correspond to + a file in -portable. This file tracks those so that we can reconcile + OpenBSD and Portable to ensure that no commits are accidentally missed. - ok djm@ + If you add something to .skipped-commit-ids please also add an upstream + ID line in the following format when you commit it. - Upstream-ID: 3e35afe8a25e021216696b5d6cde7f5d2e5e3f52 + Upstream-ID: 321065a95a7ccebdd5fd08482a1e19afbf524e35 + Upstream-ID: d4f699a421504df35254cf1c6f1a7c304fb907ca + Upstream-ID: aafe246655b53b52bc32c8a24002bc262f4230f7 + Upstream-ID: 8fa9cd1dee3c3339ae329cf20fb591db6d605120 + Upstream-ID: f31327a48dd4103333cc53315ec53fe65ed8a17a + Upstream-ID: edbfde98c40007b7752a4ac106095e060c25c1ef + Upstream-ID: 052fd565e3ff2d8cec3bc957d1788f50c827f8e2 + Upstream-ID: 7cf73737f357492776223da1c09179fa6ba74660 + Upstream-ID: 180d84674be1344e45a63990d60349988187c1ae + Upstream-ID: f6ae971186ba68d066cd102e57d5b0b2c211a5ee -commit 79394ed6d74572c2d2643d73937dad33727fc240 +commit 9f919d1a3219d476d6a662d18df058e1c4f36a6f +Author: Darren Tucker +Date: Tue Jun 14 13:51:01 2016 +1000 + + Remove now-defunct .cvsignore files. ok djm + +commit 68777faf271efb2713960605c748f6c8a4b26d55 Author: dtucker@openbsd.org -Date: Fri Dec 11 02:29:03 2015 +0000 +Date: Wed Jun 8 02:13:01 2016 +0000 upstream commit - fflush stdout so that output is seen even when running in - debug mode when output may otherwise not be flushed. Patch from dustin at - null-ptr.net. + Back out rev 1.28 "Check min and max sizes sent by the + client" change. It caused "key_verify failed for server_host_key" in clients + that send a DH-GEX min value less that DH_GRP_MIN, eg old OpenSSH and PuTTY. + ok djm@ - Upstream-ID: b0c6b4cd2cdb01d7e9eefbffdc522e35b5bc4acc + Upstream-ID: 452979d3ca5c1e9dff063287ea0a5314dd091f65 -commit ee607cccb6636eb543282ba90e0677b0604d8b7a +commit a86ec4d0737ac5879223e7cd9d68c448df46e169 Author: Darren Tucker -Date: Tue Dec 15 15:23:49 2015 +1100 +Date: Tue Jun 14 10:48:27 2016 +1000 - Increase robustness of redhat/openssh.spec - - - remove configure --with-rsh, because this option isn't supported anymore - - replace last occurrence of BuildPreReq by BuildRequires - - update grep statement to query the krb5 include directory + Use Solaris setpflags(__PROC_PROTECT, ...). - Patch from CarstenGrohmann via github, ok djm. + Where possible, use Solaris setpflags to disable process tracing on + ssh-agent and sftp-server. bz#2584, based on a patch from huieying.lee + at oracle.com, ok djm. -commit b5fa0cd73555b991a543145603658d7088ec6b60 +commit 0f916d39b039fdc0b5baf9b5ab0754c0f11ec573 Author: Darren Tucker -Date: Tue Dec 15 15:10:32 2015 +1100 +Date: Tue Jun 14 10:43:53 2016 +1000 - Allow --without-ssl-engine with --without-openssl - - Patch from Mike Frysinger via github. + Shorten prctl code a tiny bit. -commit c1d7e546f6029024f3257cc25c92f2bddf163125 +commit 0fb7f5985351fbbcd2613d8485482c538e5123be Author: Darren Tucker -Date: Tue Dec 15 14:27:09 2015 +1100 +Date: Thu Jun 9 16:23:07 2016 +1000 - Include openssl crypto.h for SSLeay. + Move prctl PR_SET_DUMPABLE into platform.c. - Patch from doughdemon via github. + This should make it easier to add additional platform support such as + Solaris (bz#2584). -commit c6f5f01651526e88c00d988ce59d71f481ebac62 -Author: Darren Tucker -Date: Tue Dec 15 13:59:12 2015 +1100 +commit e6508898c3cd838324ecfe1abd0eb8cf802e7106 +Author: dtucker@openbsd.org +Date: Fri Jun 3 04:10:41 2016 +0000 - Add sys/time.h for gettimeofday. + upstream commit - Should allow it it compile with MUSL libc. Based on patch from - doughdemon via github. + Add a test for ssh(1)'s config file parsing. + + Upstream-Regress-ID: 558b7f4dc45cc3761cc3d3e889b9f3c5bc91e601 -commit 39736be06c7498ef57d6970f2d85cf066ae57c82 -Author: djm@openbsd.org -Date: Fri Dec 11 02:20:28 2015 +0000 +commit ab0a536066dfa32def0bd7272c096ebb5eb25b11 +Author: dtucker@openbsd.org +Date: Fri Jun 3 03:47:59 2016 +0000 upstream commit - correct error messages; from Tomas Kuthan bz#2507 + Add 'sshd' to the test ID as I'm about to add a similar + set for ssh. - Upstream-ID: 7454a0affeab772398052954c79300aa82077093 + Upstream-Regress-ID: aea7a9c3bac638530165c801ce836875b228ae7a -commit 94141b7ade24afceeb6762a3f99e09e47a6c42b6 -Author: mmcc@openbsd.org -Date: Fri Dec 11 00:20:04 2015 +0000 +commit a5577c1ed3ecdfe4b7b1107c526cae886fc91afb +Author: schwarze@openbsd.org +Date: Mon May 30 12:14:08 2016 +0000 upstream commit - Pass (char *)NULL rather than (char *)0 to execl and - execlp. - - ok dtucker@ + stricter malloc.conf(5) options for utf8 tests - Upstream-ID: 56c955106cbddba86c3dd9bbf786ac0d1b361492 + Upstream-Regress-ID: 111efe20a0fb692fa1a987f6e823310f9b25abf6 -commit d59ce08811bf94111c2f442184cf7d1257ffae24 -Author: mmcc@openbsd.org -Date: Thu Dec 10 17:08:40 2015 +0000 +commit 75f0844b4f29d62ec3a5e166d2ee94b02df819fc +Author: schwarze@openbsd.org +Date: Mon May 30 12:05:56 2016 +0000 upstream commit - Remove NULL-checks before free(). + Fix two rare edge cases: 1. If vasprintf() returns < 0, + do not access a NULL pointer in snmprintf(), and do not free() the pointer + returned from vasprintf() because on some systems other than OpenBSD, it + might be a bogus pointer. 2. If vasprintf() returns == 0, return 0 and "" + rather than -1 and NULL. - ok dtucker@ + Besides, free(dst) is pointless after failure (not a bug). - Upstream-ID: e3d3cb1ce900179906af36517b5eea0fb15e6ef8 + One half OK martijn@, the other half OK deraadt@; + committing quickly before people get hurt. + + Upstream-Regress-ID: b164f20923812c9bac69856dbc1385eb1522cba4 -commit 8e56dd46cb37879c73bce2d6032cf5e7f82d5a71 -Author: mmcc@openbsd.org -Date: Thu Dec 10 07:01:35 2015 +0000 +commit 016881eb33a7948028848c90f4c7ac42e3af0e87 +Author: schwarze@openbsd.org +Date: Thu May 26 19:14:25 2016 +0000 upstream commit - Fix a couple "the the" typos. ok dtucker@ + test the new utf8 module - Upstream-ID: ec364c5af32031f013001fd28d1bd3dfacfe9a72 + Upstream-Regress-ID: c923d05a20e84e4ef152cbec947fdc4ce6eabbe3 -commit 6262a0522ddc2c0f2e9358dcb68d59b46e9c533e -Author: markus@openbsd.org -Date: Mon Dec 7 20:04:09 2015 +0000 +commit d4219028bdef448e089376f3afe81ef6079da264 +Author: dtucker@openbsd.org +Date: Tue May 3 15:30:46 2016 +0000 upstream commit - stricter encoding type checks for ssh-rsa; ok djm@ + Set umask to prevent "Bad owner or permissions" errors. - Upstream-ID: 8cca7c787599a5e8391e184d0b4f36fdc3665650 + Upstream-Regress-ID: 8fdf2fc4eb595ccd80c443f474d639f851145417 -commit d86a3ba7af160c13496102aed861ae48a4297072 -Author: Damien Miller -Date: Wed Dec 9 09:18:45 2015 +1100 +commit 07d5608bb237e9b3fe86a2aeaa429392230faebf +Author: djm@openbsd.org +Date: Tue May 3 14:41:04 2016 +0000 - Don't set IPV6_V6ONLY on OpenBSD + upstream commit - It isn't necessary and runs afoul of pledge(2) restrictions. + support doas + + Upstream-Regress-ID: 8d5572b27ea810394eeda432d8b4e9e1064a7c38 -commit da98c11d03d819a15429d8fff9688acd7505439f +commit 01cabf10adc7676cba5f40536a34d3b246edb73f Author: djm@openbsd.org -Date: Mon Dec 7 02:20:46 2015 +0000 +Date: Tue May 3 13:48:33 2016 +0000 upstream commit - basic unit tests for rsa-sha2-* signature types + unit tests for sshbuf_dup_string() - Upstream-Regress-ID: 7dc4b9db809d578ff104d591b4d86560c3598d3c + Upstream-Regress-ID: 7521ff150dc7f20511d1c2c48fd3318e5850a96d -commit 3da893fdec9936dd2c23739cdb3c0c9d4c59fca0 -Author: markus@openbsd.org -Date: Sat Dec 5 20:53:21 2015 +0000 +commit 6915f1698e3d1dd4e22eac20f435e1dfc1d46372 +Author: jmc@openbsd.org +Date: Fri Jun 3 06:44:12 2016 +0000 upstream commit - prefer rsa-sha2-512 over -256 for hostkeys, too; noticed - by naddy@ + tweak previous; - Upstream-ID: 685f55f7ec566a8caca587750672723a0faf3ffe + Upstream-ID: 92979f1a0b63e041a0e5b08c9ed0ba9b683a3698 -commit 8b56e59714d87181505e4678f0d6d39955caf10e -Author: tobias@openbsd.org -Date: Fri Dec 4 21:51:06 2015 +0000 +commit 0cb2f4c2494b115d0f346ed2d8b603ab3ba643f4 +Author: dtucker@openbsd.org +Date: Fri Jun 3 04:09:38 2016 +0000 upstream commit - Properly handle invalid %-format by calling fatal. + Allow ExitOnForwardFailure and ClearAllForwardings to be + overridden when using ssh -W (but still default to yes in that case). + bz#2577, ok djm@. - ok deraadt, djm + Upstream-ID: 4b20c419e93ca11a861c81c284090cfabc8c54d4 + +commit 8543ff3f5020fe659839b15f05b8c522bde6cee5 +Author: dtucker@openbsd.org +Date: Fri Jun 3 03:14:41 2016 +0000 + + upstream commit - Upstream-ID: 5692bce7d9f6eaa9c488cb93d3b55e758bef1eac + Move the host and port used by ssh -W into the Options + struct. This will make future changes a bit easier. ok djm@ + + Upstream-ID: 151bce5ecab2fbedf0d836250a27968d30389382 -commit 76c9fbbe35aabc1db977fb78e827644345e9442e -Author: markus@openbsd.org -Date: Fri Dec 4 16:41:28 2015 +0000 +commit 6b87311d3acdc460f926b2c40f4c4f3fd345f368 +Author: dtucker@openbsd.org +Date: Wed Jun 1 04:19:49 2016 +0000 upstream commit - implement SHA2-{256,512} for RSASSA-PKCS1-v1_5 signatures - (user and host auth) based on draft-rsa-dsa-sha2-256-03.txt and - draft-ssh-ext-info-04.txt; with & ok djm@ + Check min and max sizes sent by the client against what + we support before passing them to the monitor. ok djm@ - Upstream-ID: cf82ce532b2733e5c4b34bb7b7c94835632db309 + Upstream-ID: 750627e8117084215412bff00a25b1586ab17ece -commit 6064a8b8295cb5a17b5ebcfade53053377714f40 -Author: djm@openbsd.org -Date: Fri Dec 4 00:24:55 2015 +0000 +commit 564cd2a8926ccb1dca43a535073540935b5e0373 +Author: dtucker@openbsd.org +Date: Tue May 31 23:46:14 2016 +0000 + + upstream commit + + Ensure that the client's proposed DH-GEX max value is at + least as big as the minimum the server will accept. ok djm@ + + Upstream-ID: b4b84fa04aab2de7e79a6fee4a6e1c189c0fe775 + +commit df820722e40309c9b3f360ea4ed47a584ed74333 +Author: Darren Tucker +Date: Mon Jun 6 11:36:13 2016 +1000 + + Add compat bits to utf8.c. + +commit 05c6574652571becfe9d924226c967a3f4b3f879 +Author: Darren Tucker +Date: Mon Jun 6 11:33:43 2016 +1000 + + Fix utf->utf8 typo. + +commit 6c1717190b4d5ddd729cd9e24e8ed71ed4f087ce +Author: schwarze@openbsd.org +Date: Mon May 30 18:34:41 2016 +0000 upstream commit - clean up agent_fd handling; properly initialise it to -1 - and make tests consistent + Backout rev. 1.43 for now. - ok markus@ + The function update_progress_meter() calls refresh_progress_meter() + which calls snmprintf() which calls malloc(); but update_progress_meter() + acts as the SIGALRM signal handler. - Upstream-ID: ac9554323d5065745caf17b5e37cb0f0d4825707 + "malloc(): error: recursive call" reported by sobrado@. + + Upstream-ID: aaae57989431e5239c101f8310f74ccc83aeb93e -commit b91926a97620f3e51761c271ba57aa5db790f48d -Author: semarie@openbsd.org -Date: Thu Dec 3 17:00:18 2015 +0000 +commit cd9e1eabeb4137182200035ab6fa4522f8d24044 +Author: schwarze@openbsd.org +Date: Mon May 30 12:57:21 2016 +0000 upstream commit - pledges ssh client: - mux client: which is used when - ControlMaster is in use. will end with "stdio proc tty" (proc is to - permit sending SIGWINCH to mux master on window resize) + Even when only writing an unescaped character, the dst + buffer may need to grow, or it would be overrun; issue found by tb@ with + malloc.conf(5) 'C'. - - client loop: several levels of pledging depending of your used options + While here, reserve an additional byte for the terminating NUL + up front such that we don't have to realloc() later just for that. - ok deraadt@ + OK tb@ - Upstream-ID: 21676155a700e51f2ce911e33538e92a2cd1d94b + Upstream-ID: 30ebcc0c097c4571b16f0a78b44969f170db0cff -commit bcce47466bbc974636f588b5e4a9a18ae386f64a -Author: doug@openbsd.org -Date: Wed Dec 2 08:30:50 2015 +0000 +commit ac284a355f8065eaef2a16f446f3c44cdd17371d +Author: schwarze@openbsd.org +Date: Mon May 30 12:05:56 2016 +0000 upstream commit - Add "cpath" to the ssh-agent pledge so the cleanup - handler can unlink(). + Fix two rare edge cases: 1. If vasprintf() returns < 0, + do not access a NULL pointer in snmprintf(), and do not free() the pointer + returned from vasprintf() because on some systems other than OpenBSD, it + might be a bogus pointer. 2. If vasprintf() returns == 0, return 0 and "" + rather than -1 and NULL. - ok djm@ + Besides, free(dst) is pointless after failure (not a bug). - Upstream-ID: 9e632991d48241d56db645602d381253a3d8c29d + One half OK martijn@, the other half OK deraadt@; + committing quickly before people get hurt. + + Upstream-ID: b7bcd2e82fc168a8eff94e41f5db336ed986fed0 -commit a90d001543f46716b6590c6dcc681d5f5322f8cf -Author: djm@openbsd.org -Date: Wed Dec 2 08:00:58 2015 +0000 +commit 0e059cdf5fd86297546c63fa8607c24059118832 +Author: schwarze@openbsd.org +Date: Wed May 25 23:48:45 2016 +0000 upstream commit - ssh-agent pledge needs proc for askpass; spotted by todd@ + To prevent screwing up terminal settings when printing to + the terminal, for ASCII and UTF-8, escape bytes not forming characters and + bytes forming non-printable characters with vis(3) VIS_OCTAL. For other + character sets, abort printing of the current string in these cases. In + particular, * let scp(1) respect the local user's LC_CTYPE locale(1); * + sanitize data received from the remote host; * sanitize filenames, usernames, + and similar data even locally; * take character display widths into account + for the progressmeter. - Upstream-ID: 349aa261b29cc0e7de47ef56167769c432630b2a - -commit d952162b3c158a8f23220587bb6c8fcda75da551 -Author: djm@openbsd.org -Date: Tue Dec 1 23:29:24 2015 +0000 - - upstream commit + This is believed to be sufficient to keep the local terminal safe + on OpenBSD, but bad things can still happen on other systems with + state-dependent locales because many places in the code print + unencoded ASCII characters into the output stream. - basic pledge() for ssh-agent, more refinement needed + Using feedback from djm@ and martijn@, + various aspects discussed with many others. - Upstream-ID: 5b5b03c88162fce549e45e1b6dd833f20bbb5e13 + deraadt@ says it should go in now, i probably already hesitated too long + + Upstream-ID: e66afbc94ee396ddcaffd433b9a3b80f387647e0 -commit f0191d7c8e76e30551084b79341886d9bb38e453 -Author: Damien Miller -Date: Mon Nov 30 10:53:25 2015 +1100 +commit 8c02e3639acefe1e447e293dbe23a0917abd3734 +Author: dtucker@openbsd.org +Date: Tue May 24 04:43:45 2016 +0000 - Revert "stub for pledge(2) for systems that lack it" + upstream commit - This reverts commit 14c887c8393adde2d9fd437d498be30f8c98535c. + KNF compression proposal and simplify the client side a + little. ok djm@ - dtucker beat me to it :/ + Upstream-ID: aa814b694efe9e5af8a26e4c80a05526ae6d6605 -commit 6283cc72eb0e49a3470d30e07ca99a1ba9e89676 -Author: Damien Miller -Date: Mon Nov 30 10:37:03 2015 +1100 +commit 7ec4946fb686813eb5f8c57397e465f5485159f4 +Author: dtucker@openbsd.org +Date: Tue May 24 02:31:57 2016 +0000 - revert 7d4c7513: bring back S/Key prototypes + upstream commit - (but leave RCSID changes) - -commit 14c887c8393adde2d9fd437d498be30f8c98535c -Author: Damien Miller -Date: Mon Nov 30 09:45:29 2015 +1100 - - stub for pledge(2) for systems that lack it + Back out 'plug memleak'. + + Upstream-ID: 4faacdde136c24a961e24538de373660f869dbc0 -commit 452c0b6af5d14c37553e30059bf74456012493f3 +commit 82f24c3ddc52053aeb7beb3332fa94c92014b0c5 Author: djm@openbsd.org -Date: Sun Nov 29 22:18:37 2015 +0000 +Date: Mon May 23 23:30:50 2016 +0000 upstream commit - pledge, better fatal() messages; feedback deraadt@ + prefer agent-hosted keys to keys from PKCS#11; ok markus - Upstream-ID: 3e00f6ccfe2b9a7a2d1dbba5409586180801488f + Upstream-ID: 7417f7653d58d6306d9f8c08d0263d050e2fd8f4 -commit 6da413c085dba37127687b2617a415602505729b -Author: deraadt@openbsd.org -Date: Sat Nov 28 06:50:52 2015 +0000 +commit a0cb7778fbc9b43458f7072eb68dd858766384d1 +Author: dtucker@openbsd.org +Date: Mon May 23 00:17:27 2016 +0000 upstream commit - do not leak temp file if there is no known_hosts file - from craig leres, ok djm + Plug mem leak in filter_proposal. ok djm@ - Upstream-ID: c820497fd5574844c782e79405c55860f170e426 + Upstream-ID: bf968da7cfcea2a41902832e7d548356a4e2af34 -commit 3ddd15e1b63a4d4f06c8ab16fbdd8a5a61764f16 +commit ae9c0d4d5c581b3040d1f16b5c5f4b1cd1616743 Author: Darren Tucker -Date: Mon Nov 30 07:23:53 2015 +1100 +Date: Fri Jun 3 16:03:44 2016 +1000 - Add a null implementation of pledge. + Update vis.h and vis.c from OpenBSD. - Fixes builds on almost everything. + This will be needed for the upcoming utf8 changes. -commit b1d6b3971ef256a08692efc409fc9ada719111cc -Author: djm@openbsd.org -Date: Sat Nov 28 06:41:03 2015 +0000 +commit e1d93705f8f48f519433d6ca9fc3d0abe92a1b77 +Author: Tim Rice +Date: Tue May 31 11:13:22 2016 -0700 - upstream commit - - don't include port number in tcpip-forward replies for - requests that don't allocate a port; bz#2509 diagnosed by Ron Frederick ok - markus - - Upstream-ID: 77efad818addb61ec638b5a2362f1554e21a970a + modified: configure.ac + whitspace clean up. No code changes. -commit 9080bd0b9cf10d0f13b1f642f20cb84285cb8d65 -Author: deraadt@openbsd.org -Date: Fri Nov 27 00:49:31 2015 +0000 +commit 604a037d84e41e31f0aec9075df0b8740c130200 +Author: Damien Miller +Date: Tue May 31 16:45:28 2016 +1000 - upstream commit - - pledge "stdio rpath wpath cpath fattr tty proc exec" - except for the -p option (which sadly has insane semantics...) ok semarie - dtucker - - Upstream-ID: 8854bbd58279abe00f6c33f8094bdc02c8c65059 + whitespace at EOL -commit 4d90625b229cf6b3551d81550a9861897509a65f -Author: halex@openbsd.org -Date: Fri Nov 20 23:04:01 2015 +0000 +commit 18424200160ff5c923113e0a37ebe21ab7bcd17c +Author: Darren Tucker +Date: Mon May 30 19:35:28 2016 +1000 - upstream commit + Add missing ssh-host-config --name option - allow comment change for all supported formats + Patch from vinschen@redhat.com. + +commit 39c0cecaa188a37a2e134795caa68e03f3ced592 +Author: Darren Tucker +Date: Fri May 20 10:01:58 2016 +1000 + + Fix comment about sshpam_const and AIX. - ok djm@ + From mschwager via github. + +commit f64062b1f74ad5ee20a8a49aab2732efd0f7ce30 +Author: Damien Miller +Date: Fri May 20 09:56:53 2016 +1000 + + Deny lstat syscalls in seccomp sandbox - Upstream-ID: 5fc477cf2f119b2d44aa9c683af16cb00bb3744b + Avoids sandbox violations for some krb/gssapi libraries. -commit 8ca915fc761519dd1f7766a550ec597a81db5646 +commit 531c135409b8d8810795b1f3692a4ebfd5c9cae0 Author: djm@openbsd.org -Date: Fri Nov 20 01:45:29 2015 +0000 +Date: Thu May 19 07:45:32 2016 +0000 upstream commit - add cast to make -Werror clean + fix type of ed25519 values - Upstream-ID: 288db4f8f810bd475be01320c198250a04ff064d + Upstream-ID: b32d0cb372bbe918ca2de56906901eae225a59b0 -commit ac9473580dcd401f8281305af98635cdaae9bf96 -Author: Damien Miller -Date: Fri Nov 20 12:35:41 2015 +1100 +commit 75e21688f523799c9e0cc6601d76a9c5ca79f787 +Author: markus@openbsd.org +Date: Wed May 4 14:32:26 2016 +0000 - fix multiple authentication using S/Key w/ privsep + upstream commit - bz#2502, patch from Kevin Korb and feandil_ + add IdentityAgent; noticed & ok jmc@ + + Upstream-ID: 4ba9034b00a4cf1beae627f0728da897802df88a -commit 88b6fcdeb87a2fb76767854d9eb15006662dca57 -Author: djm@openbsd.org -Date: Thu Nov 19 08:23:27 2015 +0000 +commit 1a75d14daf4b60db903e6103cf50e74e0cd0a76b +Author: markus@openbsd.org +Date: Wed May 4 14:29:58 2016 +0000 upstream commit - ban ConnectionAttempts=0, it makes no sense and would cause - ssh_connect_direct() to print an uninitialised stack variable; bz#2500 - reported by dvw AT phas.ubc.ca + allow setting IdentityAgent to SSH_AUTH_SOCK; ok djm@ - Upstream-ID: 32b5134c608270583a90b93a07b3feb3cbd5f7d5 + Upstream-ID: 20c508480d8db3eef18942c0fc39b1fcf25652ac -commit 964ab3ee7a8f96bdbc963d5b5a91933d6045ebe7 -Author: djm@openbsd.org -Date: Thu Nov 19 01:12:32 2015 +0000 +commit 0516454151ae722fc8256c3c56115c6baf24c5b0 +Author: markus@openbsd.org +Date: Wed May 4 14:22:33 2016 +0000 upstream commit - trailing whitespace + move SSH_MSG_NONE, so we don't have to include ssh1.h; + ok deraadt@ - Upstream-ID: 31fe0ad7c4d08e87f1d69c79372f5e3c5cd79051 + Upstream-ID: c2f97502efc761a41b18c17ddf460e138ca7994e -commit f96516d052dbe38561f6b92b0e4365d8e24bb686 -Author: djm@openbsd.org -Date: Thu Nov 19 01:09:38 2015 +0000 +commit 332ff3d770631e7513fea38cf0d3689f673f0e3f +Author: Damien Miller +Date: Tue May 10 09:51:06 2016 +1000 - upstream commit + initialise salen in binresvport_sa - print host certificate contents at debug level + avoids failures with UsePrivilegedPort=yes - Upstream-ID: 39354cdd8a2b32b308fd03f98645f877f540f00d + patch from Juan Gallego -commit 499cf36fecd6040e30e2912dd25655bc574739a7 -Author: djm@openbsd.org -Date: Thu Nov 19 01:08:55 2015 +0000 +commit c5c1d5d2f04ce00d2ddd6647e61b32f28be39804 +Author: markus@openbsd.org +Date: Wed May 4 14:04:40 2016 +0000 upstream commit - move the certificate validity formatting code to - sshkey.[ch] + missing const in prototypes (ssh1) - Upstream-ID: f05f7c78fab20d02ff1d5ceeda533ef52e8fe523 + Upstream-ID: 789c6ad4928b5fa557369b88c3a6a34926082c05 -commit bcb7bc77bbb1535d1008c7714085556f3065d99d -Author: djm@openbsd.org -Date: Wed Nov 18 08:37:28 2015 +0000 +commit 9faae50e2e82ba42eb0cb2726bf6830fe7948f28 +Author: dtucker@openbsd.org +Date: Wed May 4 14:00:09 2016 +0000 upstream commit - fix "ssh-keygen -l" of private key, broken in support for - multiple plain keys on stdin + Fix inverted logic for updating StreamLocalBindMask which + would cause the server to set an invalid mask. ok djm@ - Upstream-ID: 6b3132d2c62d03d0bad6f2bcd7e2d8b7dab5cd9d + Upstream-ID: 8a4404c8307a5ef9e07ee2169fc6d8106b527587 -commit 259adb6179e23195c8f6913635ea71040d1ccd63 -Author: millert@openbsd.org -Date: Mon Nov 16 23:47:52 2015 +0000 +commit b02ad1ce9105bfa7394ac7590c0729dd52e26a81 +Author: markus@openbsd.org +Date: Wed May 4 12:21:53 2016 +0000 upstream commit - Replace remaining calls to index(3) with strchr(3). OK - jca@ krw@ + IdentityAgent for specifying specific agent sockets; ok + djm@ - Upstream-ID: 33837d767a0cf1db1489b96055f9e330bc0bab6d + Upstream-ID: 3e6a15eb89ea0fd406f108826b7dc7dec4fbfac1 -commit c56a255162c2166884539c0a1f7511575325b477 +commit 910e59bba09ac309d78ce61e356da35292212935 Author: djm@openbsd.org -Date: Mon Nov 16 22:53:07 2015 +0000 +Date: Wed May 4 12:16:39 2016 +0000 upstream commit - Allow fingerprinting from standard input "ssh-keygen -lf - -" - - Support fingerprinting multiple plain keys in a file and authorized_keys - files too (bz#1319) - - ok markus@ + fix junk characters after quotes - Upstream-ID: 903f8b4502929d6ccf53509e4e07eae084574b77 + Upstream-ID: cc4d0cd32cb6b55a2ef98975d2f7ae857d0dc578 -commit 5b4010d9b923cf1b46c9c7b1887c013c2967e204 -Author: djm@openbsd.org -Date: Mon Nov 16 22:51:05 2015 +0000 +commit 9283884e647b8be50ccd2997537af0065672107d +Author: jmc@openbsd.org +Date: Tue May 3 18:38:12 2016 +0000 upstream commit - always call privsep_preauth_child() regardless of whether - sshd was started by root; it does important priming before sandboxing and - failing to call it could result in sandbox violations later; ok markus@ + correct article; - Upstream-ID: c8a6d0d56c42f3faab38460dc917ca0d1705d383 + Upstream-ID: 1fbd5b7ab16d2d9834ec79c3cedd4738fa42a168 -commit 3a9f84b58b0534bbb485f1eeab75665e2d03371f +commit cfefbcea1057c2623e76c579174a4107a0b6e6cd Author: djm@openbsd.org -Date: Mon Nov 16 22:50:01 2015 +0000 +Date: Tue May 3 15:57:39 2016 +0000 upstream commit - improve sshkey_read() semantics; only update *cpp when a - key is successfully read; ok markus@ + fix overriding of StreamLocalBindMask and + StreamLocalBindUnlink in Match blocks; found the hard way Rogan Dawes - Upstream-ID: f371e78e8f4fab366cf69a42bdecedaed5d1b089 + Upstream-ID: 940bc69ec0249ab428d24ccd0722ce35cb932ee2 -commit db6f8dc5dd5655b59368efd074994d4568bc3556 -Author: logan@openbsd.org -Date: Mon Nov 16 06:13:04 2015 +0000 +commit 771c2f51ffc0c9a2877b7892fada0c77bd1f6549 +Author: djm@openbsd.org +Date: Tue May 3 15:25:06 2016 +0000 upstream commit - 1) Use xcalloc() instead of xmalloc() to check for - potential overflow. (Feedback from both mmcc@ and djm@) 2) move set_size - just before the for loop. (suggested by djm@) - - OK djm@ + don't forget to include StreamLocalBindUnlink in the + config dump output - Upstream-ID: 013534c308187284756c3141f11d2c0f33c47213 + Upstream-ID: 14a6d970b3b45c8e94272e3c661e9a0b2a0ee7cb -commit 383f10fb84a0fee3c01f9d97594f3e22aa3cd5e0 +commit cdcd941994dc430f50d0a4e6a712d32b66e6199e Author: djm@openbsd.org -Date: Mon Nov 16 00:30:02 2015 +0000 +Date: Tue May 3 14:54:08 2016 +0000 upstream commit - Add a new authorized_keys option "restrict" that - includes all current and future key restrictions (no-*-forwarding, etc). Also - add permissive versions of the existing restrictions, e.g. "no-pty" -> "pty". - This simplifies the task of setting up restricted keys and ensures they are - maximally-restricted, regardless of any permissions we might implement in the - future. - - Example: - - restrict,pty,command="nethack" ssh-ed25519 AAAAC3NzaC1lZDI1... - - Idea from Jann Horn; ok markus@ + make nethack^wrandomart fingerprint flag more readily + searchable pointed out by Matt Johnston - Upstream-ID: 04ceb9d448e46e67e13887a7ae5ea45b4f1719d0 + Upstream-ID: cb40d0235dc153c478c1aad3bc60b195422a54fb -commit e41a071f7bda6af1fb3f081bed0151235fa61f15 -Author: jmc@openbsd.org -Date: Sun Nov 15 23:58:04 2015 +0000 +commit 05855bf2ce7d5cd0a6db18bc0b4214ed5ef7516d +Author: djm@openbsd.org +Date: Tue May 3 13:10:24 2016 +0000 upstream commit - correct section number for ssh-agent; + clarify ordering of subkeys; pointed out by ietf-ssh AT + stbuehler.de - Upstream-ID: 44be72fd8bcc167635c49b357b1beea8d5674bd6 + Upstream-ID: 05ebe9f949449a555ebce8e0aad7c8c9acaf8463 -commit 1a11670286acddcc19f5eff0966c380831fc4638 -Author: jmc@openbsd.org -Date: Sun Nov 15 23:54:15 2015 +0000 +commit cca3b4395807bfb7aaeb83d2838f5c062ce30566 +Author: dtucker@openbsd.org +Date: Tue May 3 12:15:49 2016 +0000 upstream commit - do not confuse mandoc by presenting "Dd"; + Use a subshell for constructing key types to work around + different sed behaviours for -portable. - Upstream-ID: 1470fce171c47b60bbc7ecd0fc717a442c2cfe65 + Upstream-Regress-ID: 0f6eb673162df229eda9a134a0f10da16151552d -commit f361df474c49a097bfcf16d1b7b5c36fcd844b4b -Author: jcs@openbsd.org -Date: Sun Nov 15 22:26:49 2015 +0000 +commit fa58208c6502dcce3e0daac0ca991ee657daf1f5 +Author: djm@openbsd.org +Date: Tue May 3 10:27:59 2016 +0000 upstream commit - Add an AddKeysToAgent client option which can be set to - 'yes', 'no', 'ask', or 'confirm', and defaults to 'no'. When enabled, a - private key that is used during authentication will be added to ssh-agent if - it is running (with confirmation enabled if set to 'confirm'). + correct some typos and remove a long-stale XXX note. - Initial version from Joachim Schipper many years ago. + add specification for ed25519 certificates - ok markus@ + mention no host certificate options/extensions are currently defined - Upstream-ID: a680db2248e8064ec55f8be72d539458c987d5f4 + pointed out by Simon Tatham + + Upstream-ID: 7b535ab7dba3340b7d8210ede6791fdaefdf839a -commit d87063d9baf5479b6e813d47dfb694a97df6f6f5 +commit b466f956c32cbaff4200bfcd5db6739fe4bc7d04 Author: djm@openbsd.org -Date: Fri Nov 13 04:39:35 2015 +0000 +Date: Tue May 3 10:24:27 2016 +0000 upstream commit - send SSH2_MSG_UNIMPLEMENTED replies to unexpected - messages during KEX; bz#2949, ok dtucker@ + add ed25519 keys that are supported but missing from this + documents; from Peter Moody - Upstream-ID: 2b3abdff344d53c8d505f45c83a7b12e84935786 + Upstream-ID: 8caac2d8e8cfd2fca6dc304877346e0a064b014b -commit 9fd04681a1e9b0af21e08ff82eb674cf0a499bfc -Author: djm@openbsd.org -Date: Fri Nov 13 04:38:06 2015 +0000 +commit 7f3d76319a69dab2efe3a520a8fef5b97e923636 +Author: dtucker@openbsd.org +Date: Tue May 3 09:03:49 2016 +0000 upstream commit - Support "none" as an argument for sshd_config - ForceCommand and ChrootDirectory. Useful inside Match blocks to override a - global default. bz#2486 ok dtucker@ + Implement IUTF8 as per draft-sgtatham-secsh-iutf8-00. Patch + from Simon Tatham, ok markus@ - Upstream-ID: 7ef478d6592bc7db5c7376fc33b4443e63dccfa5 + Upstream-ID: 58268ebdf37d9d467f78216c681705a5e10c58e8 -commit 94bc0b72c29e511cbbc5772190d43282e5acfdfe +commit 31bc01c05d9f51bee3ebe33dc57c4fafb059fb62 Author: djm@openbsd.org -Date: Fri Nov 13 04:34:15 2015 +0000 +Date: Mon May 2 14:10:58 2016 +0000 upstream commit - support multiple certificates (one per line) and - reading from standard input (using "-f -") for "ssh-keygen -L"; ok dtucker@ + unbreak config parsing on reexec from previous commit - Upstream-ID: ecbadeeef3926e5be6281689b7250a32a80e88db + Upstream-ID: bc69932638a291770955bd05ca55a32660a613ab -commit b6b9108f5b561c83612cb97ece4134eb59fde071 +commit 67f1459efd2e85bf03d032539283fa8107218936 Author: djm@openbsd.org -Date: Fri Nov 13 02:57:46 2015 +0000 +Date: Mon May 2 09:52:00 2016 +0000 upstream commit - list a couple more options usable in Match blocks; - bz#2489 + unit and regress tests for SHA256/512; ok markus - Upstream-ID: e4d03f39d254db4c0cc54101921bb89fbda19879 + Upstream-Regress-ID: a0cd1a92dc824067076a5fcef83c18df9b0bf2c6 -commit a7994b3f5a5a5a33b52b0a6065d08e888f0a99fb +commit 0e8eeec8e75f6d0eaf33317376f773160018a9c7 Author: djm@openbsd.org -Date: Wed Nov 11 04:56:39 2015 +0000 +Date: Mon May 2 10:26:04 2016 +0000 upstream commit - improve PEEK/POKE macros: better casts, don't multiply - evaluate arguments; ok deraadt@ + add support for additional fixed DH groups from + draft-ietf-curdle-ssh-kex-sha2-03 - Upstream-ID: 9a1889e19647615ededbbabab89064843ba92d3e - -commit 7d4c7513a7f209cb303a608ac6e46b3f1dfc11ec -Author: djm@openbsd.org -Date: Wed Nov 11 01:48:01 2015 +0000 - - upstream commit + diffie-hellman-group14-sha256 (2K group) + diffie-hellman-group16-sha512 (4K group) + diffie-hellman-group18-sha512 (8K group) - remove prototypes for long-gone s/key support; ok - dtucker@ + based on patch from Mark D. Baushke and Darren Tucker + ok markus@ - Upstream-ID: db5bed3c57118af986490ab23d399df807359a79 + Upstream-ID: ac00406ada4f0dfec41585ca0839f039545bc46f -commit 07889c75926c040b8e095949c724e66af26441cb -Author: Damien Miller -Date: Sat Nov 14 18:44:49 2015 +1100 +commit 57464e3934ba53ad8590ee3ccd840f693407fc1e +Author: djm@openbsd.org +Date: Mon May 2 09:36:42 2016 +0000 - read back from libcrypto RAND when privdropping + upstream commit - makes certain libcrypto implementations cache a /dev/urandom fd - in preparation of sandboxing. Based on patch by Greg Hartman. - -commit 1560596f44c01bb0cef977816410950ed17b8ecd -Author: Darren Tucker -Date: Tue Nov 10 11:14:47 2015 +1100 - - Fix compiler warnings in the openssl header check. + support SHA256 and SHA512 RSA signatures in certificates; + ok markus@ - Noted by Austin English. + Upstream-ID: b45be2f2ce8cacd794dc5730edaabc90e5eb434a -commit e72a8575ffe1d8adff42c9abe9ca36938acc036b -Author: jmc@openbsd.org -Date: Sun Nov 8 23:24:03 2015 +0000 +commit 1a31d02b2411c4718de58ce796dbb7b5e14db93e +Author: djm@openbsd.org +Date: Mon May 2 08:49:03 2016 +0000 upstream commit - -c before -H, in SYNOPSIS and usage(); + fix signed/unsigned errors reported by clang-3.7; add + sshbuf_dup_string() to replace a common idiom of strdup(sshbuf_ptr()) with + better safety checking; feedback and ok markus@ - Upstream-ID: 25e8c58a69e1f37fcd54ac2cd1699370acb5e404 + Upstream-ID: 71f926d9bb3f1efed51319a6daf37e93d57c8820 -commit 3a424cdd21db08c7b0ded902f97b8f02af5aa485 +commit d2d6bf864e52af8491a60dd507f85b74361f5da3 Author: djm@openbsd.org -Date: Sun Nov 8 22:30:20 2015 +0000 +Date: Fri Apr 29 08:07:53 2016 +0000 upstream commit - Add "ssh-keyscan -c ..." flag to allow fetching - certificates instead of plain keys; ok markus@ + close ControlPersist background process stderr when not + in debug mode or when logging to a file or syslog. bz#1988 ok dtucker - Upstream-ID: 0947e2177dba92339eced9e49d3c5bf7dda69f82 + Upstream-ID: 4fb726f0fdcb155ad419913cea10dc4afd409d24 -commit 69fead5d7cdaa73bdece9fcba80f8e8e70b90346 -Author: jmc@openbsd.org -Date: Sun Nov 8 22:08:38 2015 +0000 +commit 9ee692fa1146e887e008a2b9a3d3ea81770c9fc8 +Author: djm@openbsd.org +Date: Thu Apr 28 14:30:21 2016 +0000 upstream commit - remove slogin links; ok deraadt markus djm + fix comment - Upstream-ID: 39ba08548acde4c54f2d4520c202c2a863a3c730 + Upstream-ID: 313a385bd7b69a82f8e28ecbaf5789c774457b15 -commit 2fecfd486bdba9f51b3a789277bb0733ca36e1c0 -Author: djm@openbsd.org -Date: Sun Nov 8 21:59:11 2015 +0000 +commit ee1e0a16ff2ba41a4d203c7670b54644b6c57fa6 +Author: jmc@openbsd.org +Date: Wed Apr 27 13:53:48 2016 +0000 upstream commit - fix OOB read in packet code caused by missing return - statement found by Ben Hawkes; ok markus@ deraadt@ + cidr permitted for {allow,deny}users; from lars nooden ok djm - Upstream-ID: a3e3a85434ebfa0690d4879091959591f30efc62 + Upstream-ID: 13e7327fe85f6c63f3f7f069e0fdc8c351515d11 -commit 5e288923a303ca672b686908320bc5368ebec6e6 -Author: mmcc@openbsd.org -Date: Fri Nov 6 00:31:41 2015 +0000 +commit b6e0140a5aa883c27b98415bd8aa9f65fc04ee22 +Author: djm@openbsd.org +Date: Thu Apr 21 06:08:02 2016 +0000 upstream commit - 1. rlogin and rsh are long gone 2. protocol version isn't - of core relevance here, and v1 is going away - - ok markus@, deraadt@ + make argument == NULL tests more consistent - Upstream-ID: 8b46bc94cf1ca7c8c1a75b1c958b2bb38d7579c8 + Upstream-ID: dc4816678704aa5cbda3a702e0fa2033ff04581d -commit 8b29008bbe97f33381d9b4b93fcfa304168d0286 +commit 6aaabc2b610e44bae473457ad9556ffb43d90ee3 Author: jmc@openbsd.org -Date: Thu Nov 5 09:48:05 2015 +0000 +Date: Sun Apr 17 14:34:46 2016 +0000 upstream commit - "commandline" -> "command line", since there are so few - examples of the former in the pages, so many of the latter, and in some of - these pages we had multiple spellings; - - prompted by tj + tweak previous; - Upstream-ID: 78459d59bff74223f8139d9001ccd56fc4310659 + Upstream-ID: 46c1bab91c164078edbccd5f7d06b9058edd814f -commit 996b24cebf20077fbe5db07b3a2c20c2d9db736e -Author: Darren Tucker -Date: Thu Oct 29 20:57:34 2015 +1100 +commit 0f839e5969efa3bda615991be8a9d9311554c573 +Author: djm@openbsd.org +Date: Fri Apr 15 02:57:10 2016 +0000 - (re)wrap SYS_sendsyslog in ifdef. + upstream commit - Replace ifdef that went missing in commit - c61b42f2678f21f05653ac2d3d241b48ab5d59ac. Fixes build on older - OpenBSDs. + missing bit of Include regress + + Upstream-Regress-ID: 1063595f7f40f8489a1b7a27230b9e8acccea34f -commit b67e2e76fcf1ae7c802eb27ca927e16c91a513ff +commit 12e4ac46aed681da55c2bba3cd11dfcab23591be Author: djm@openbsd.org -Date: Thu Oct 29 08:05:17 2015 +0000 +Date: Fri Apr 15 02:55:53 2016 +0000 upstream commit - regress test for "PubkeyAcceptedKeyTypes +..." inside a - Match block + remove redundant CLEANFILES section - Upstream-Regress-ID: 246c37ed64a2e5704d4c158ccdca1ff700e10647 + Upstream-Regress-ID: 29ef1b267fa56daa60a1463396635e7d53afb587 -commit abd9dbc3c0d8c8c7561347cfa22166156e78c077 -Author: dtucker@openbsd.org -Date: Mon Oct 26 02:50:58 2015 +0000 +commit b1d05aa653ae560c44baf8e8a9756e33f98ea75c +Author: djm@openbsd.org +Date: Fri Apr 15 00:48:01 2016 +0000 upstream commit - Fix typo certopt->certopts in shell variable. This would - cause the test to hang at a host key prompt if you have an A or CNAME for - "proxy" in your local domain. + sync CLEANFILES with portable, sort - Upstream-Regress-ID: 6ea03bcd39443a83c89e2c5606392ceb9585836a + Upstream-Regress-ID: cb782f4f1ab3e079efbc335c6b64942f790766ed -commit ed08510d38aef930a061ae30d10f2a9cf233bafa +commit 35f22dad263cce5c61d933ae439998cb965b8748 Author: djm@openbsd.org -Date: Thu Oct 29 08:05:01 2015 +0000 +Date: Fri Apr 15 00:31:10 2016 +0000 upstream commit - Fix "PubkeyAcceptedKeyTypes +..." inside a Match block; - ok dtucker@ + regression test for ssh_config Include directive - Upstream-ID: 853662c4036730b966aab77684390c47b9738c69 + Upstream-Regress-ID: 46a38c8101f635461c506d1aac2d96af80f97f1e -commit a4aef3ed29071719b2af82fdf1ac3c2514f82bc5 +commit 6b8a1a87005818d4700ce8b42faef746e82c1f51 Author: djm@openbsd.org -Date: Tue Oct 27 08:54:52 2015 +0000 +Date: Thu Apr 14 23:57:17 2016 +0000 upstream commit - fix execv arguments in a way less likely to cause grief - for -portable; ok dtucker@ + unbreak test for recent ssh de-duplicated forwarding + change - Upstream-ID: 5902bf0ea0371f39f1300698dc3b8e4105fc0fc5 + Upstream-Regress-ID: 6b2b115d99acd7cff13986e6739ea214cf2a3da3 -commit 63d188175accea83305e89fafa011136ff3d96ad +commit 076787702418985a2cc6808212dc28ce7afc01f0 Author: djm@openbsd.org -Date: Tue Oct 27 01:44:45 2015 +0000 +Date: Thu Apr 14 23:21:42 2016 +0000 upstream commit - log certificate serial in verbose() messages to match the - main auth success/fail message; ok dtucker@ + add test knob and warning for StrictModes - Upstream-ID: dfc48b417c320b97c36ff351d303c142f2186288 + Upstream-Regress-ID: 8cd10952ce7898655ee58945904f2a0a3bdf7682 -commit 2aaba0cfd560ecfe92aa50c00750e6143842cf1f +commit dc7990be865450574c7940c9880567f5d2555b37 Author: djm@openbsd.org -Date: Tue Oct 27 00:49:53 2015 +0000 +Date: Fri Apr 15 00:30:19 2016 +0000 upstream commit - avoid de-const warning & shrink; ok dtucker@ + Include directive for ssh_config(5); feedback & ok markus@ - Upstream-ID: 69a85ef94832378952a22c172009cbf52aaa11db + Upstream-ID: ae3b76e2e343322b9f74acde6f1e1c5f027d5fff -commit 03239c18312b9bab7d1c3b03062c61e8bbc1ca6e -Author: dtucker@openbsd.org -Date: Sun Oct 25 23:42:00 2015 +0000 +commit 85bdcd7c92fe7ff133bbc4e10a65c91810f88755 +Author: Damien Miller +Date: Wed Apr 13 10:39:57 2016 +1000 - upstream commit + ignore PAM environment vars when UseLogin=yes - Expand tildes in filenames passed to -i before checking - whether or not the identity file exists. This means that if the shell - doesn't do the expansion (eg because the option and filename were given as a - single argument) then we'll still add the key. bz#2481, ok markus@ + If PAM is configured to read user-specified environment variables + and UseLogin=yes in sshd_config, then a hostile local user may + attack /bin/login via LD_PRELOAD or similar environment variables + set via PAM. - Upstream-ID: db1757178a14ac519e9a3e1a2dbd21113cb3bfc6 + CVE-2015-8325, found by Shayan Sadigh, via Colin Watson -commit 97e184e508dd33c37860c732c0eca3fc57698b40 -Author: dtucker@openbsd.org -Date: Sun Oct 25 23:14:03 2015 +0000 +commit dce19bf6e4a2a3d0b13a81224de63fc316461ab9 +Author: djm@openbsd.org +Date: Sat Apr 9 12:39:30 2016 +0000 upstream commit - Do not prepend "exec" to the shell command run by "Match - exec" in a config file. It's an unnecessary optimization from repurposed - ProxyCommand code and prevents some things working with some shells. - bz#2471, pointed out by res at qoxp.net. ok markus@ + make private key loading functions consistently handle NULL + key pointer arguments; ok markus@ - Upstream-ID: a1ead25ae336bfa15fb58d8c6b5589f85b4c33a3 + Upstream-ID: 92038726ef4a338169c35dacc9c5a07fcc7fa761 -commit 8db134e7f457bcb069ec72bc4ee722e2af557c69 +commit 5f41f030e2feb5295657285aa8c6602c7810bc4b Author: Darren Tucker -Date: Thu Oct 29 10:48:23 2015 +1100 - - Prevent name collisions with system glob (bz#2463) - - Move glob.h from includes.h to the only caller (sftp) and override the - names for the symbols. This prevents name collisions with the system glob - in the case where something other than ssh uses it (eg kerberos). With - jjelen at redhat.com, ok djm@ - -commit 86c10dbbef6a5800d2431a66cf7f41a954bb62b5 -Author: dtucker@openbsd.org -Date: Fri Oct 23 02:22:01 2015 +0000 +Date: Fri Apr 8 21:14:13 2016 +1000 - upstream commit - - Update expected group sizes to match recent code changes. + Remove NO_IPPORT_RESERVED_CONCEPT - Upstream-Regress-ID: 0004f0ea93428969fe75bcfff0d521c553977794 + Replace by defining IPPORT_RESERVED to zero on Cygwin, which should have + the same effect without causing problems syncing patches with OpenBSD. + Resync the two affected functions with OpenBSD. ok djm, sanity checked + by Corinna. -commit 9ada37d36003a77902e90a3214981e417457cf13 +commit 34a01b2cf737d946ddb140618e28c3048ab7a229 Author: djm@openbsd.org -Date: Sat Oct 24 22:56:19 2015 +0000 +Date: Fri Apr 8 08:19:17 2016 +0000 upstream commit - fix keyscan output for multiple hosts/addrs on one line - when host hashing or a non standard port is in use; bz#2479 ok dtucker@ + whitespace at EOL - Upstream-ID: 5321dabfaeceba343da3c8a8b5754c6f4a0a307b + Upstream-ID: 5beffd4e001515da12851b974e2323ae4aa313b6 -commit 44fc7cd7dcef6c52c6b7e9ff830dfa32879bd319 +commit 90ee563fa6b54c59896c6c332c5188f866c5e75f Author: djm@openbsd.org -Date: Sat Oct 24 22:52:22 2015 +0000 +Date: Fri Apr 8 06:35:54 2016 +0000 upstream commit - skip "Could not chdir to home directory" message when - chrooted - - patch from Christian Hesse in bz#2485 ok dtucker@ + We accidentally send an empty string and a zero uint32 with + every direct-streamlocal@openssh.com channel open, in contravention of our + own spec. - Upstream-ID: 86783c1953da426dff5b03b03ce46e699d9e5431 - -commit a820a8618ec44735dabc688fab96fba38ad66bb2 -Author: sthen@openbsd.org -Date: Sat Oct 24 08:34:09 2015 +0000 - - upstream commit + Fixing this is too hard wrt existing versions that expect these + fields to be present and fatal() if they aren't, so document them + as "reserved" fields in the PROTOCOL spec as though we always + intended this and let us never speak of it again. - Handle the split of tun(4) "link0" into tap(4) in ssh - tun-forwarding. Adapted from portable (using separate devices for this is the - normal case in most OS). ok djm@ + bz#2529, reported by Ron Frederick - Upstream-ID: 90facf4c59ce73d6741db1bc926e578ef465cd39 + Upstream-ID: 34cd326a4d236ca6e39084c4ff796bd97ab833e7 -commit 66d2e229baa9fe57b868c373b05f7ff3bb20055b -Author: gsoares@openbsd.org -Date: Wed Oct 21 11:33:03 2015 +0000 +commit 0ccbd5eca0f0dd78e71a4b69c66f03a66908d558 +Author: djm@openbsd.org +Date: Wed Apr 6 06:42:17 2016 +0000 upstream commit - fix memory leak in error path ok djm@ + don't record duplicate LocalForward and RemoteForward + entries; fixes failure with ExitOnForwardFailure+hostname canonicalisation + where the same forwards are added on the second pass through the + configuration file. bz#2562; ok dtucker@ - Upstream-ID: dd2f402b0a0029b755df029fc7f0679e1365ce35 + Upstream-ID: 40a51d68b6300f1cc61deecdb7d4847b8b7b0de1 -commit 7d6c0362039ceacdc1366b5df29ad5d2693c13e5 -Author: mmcc@openbsd.org -Date: Tue Oct 20 23:24:25 2015 +0000 +commit 574def0eb493cd6efeffd4ff2e9257abcffee0c8 +Author: krw@openbsd.org +Date: Sat Apr 2 14:37:42 2016 +0000 upstream commit - Compare pointers to NULL rather than 0. + Another use for fcntl() and thus of the superfluous 3rd + parameter is when sanitising standard fd's before calling daemon(). - ok djm@ + Use a tweaked version of the ssh(1) function in all three places + found using fcntl() this way. - Upstream-ID: 21616cfea27eda65a06e772cc887530b9a1a27f8 + ok jca@ beck@ + + Upstream-ID: f16811ffa19a1c5f4ef383c5f0fecb843c84e218 -commit f98a09cacff7baad8748c9aa217afd155a4d493f -Author: mmcc@openbsd.org -Date: Tue Oct 20 03:36:35 2015 +0000 +commit b3413534aa9d71a941005df2760d1eec2c2b0854 +Author: Darren Tucker +Date: Mon Apr 4 11:09:21 2016 +1000 - upstream commit - - Replace a function-local allocation with stack memory. - - ok djm@ - - Upstream-ID: c09fbbab637053a2ab9f33ca142b4e20a4c5a17e + Tidy up openssl header test. -commit ac908c1eeacccfa85659594d92428659320fd57e -Author: Damien Miller -Date: Thu Oct 22 09:35:24 2015 +1100 +commit 815bcac0b94bb448de5acdd6ba925b8725240b4f +Author: Darren Tucker +Date: Mon Apr 4 11:07:59 2016 +1000 - turn off PrintLastLog when --disable-lastlog - - bz#2278 from Brent Paulson + Fix configure-time warnings for openssl test. -commit b56deb847f4a0115a8bf488bf6ee8524658162fd +commit 95687f5831ae680f7959446d8ae4b52452ee05dd Author: djm@openbsd.org -Date: Fri Oct 16 22:32:22 2015 +0000 +Date: Fri Apr 1 02:34:10 2016 +0000 upstream commit - increase the minimum modulus that we will send or accept in - diffie-hellman-group-exchange to 2048 bits; ok markus@ + whitespace at EOL - Upstream-ID: 06dce7a24c17b999a0f5fadfe95de1ed6a1a9b6a + Upstream-ID: 40ae2203d07cb14e0a89e1a0d4c6120ee8fd8c3a -commit 5ee0063f024bf5b3f3ffb275b8cd20055d62b4b9 -Author: djm@openbsd.org -Date: Fri Oct 16 18:40:49 2015 +0000 +commit fdfbf4580de09d84a974211715e14f88a5704b8e +Author: dtucker@openbsd.org +Date: Thu Mar 31 05:24:06 2016 +0000 upstream commit - better handle anchored FQDNs (e.g. 'cvs.openbsd.org.') in - hostname canonicalisation - treat them as already canonical and remove the - trailing '.' before matching ssh_config; ok markus@ + Remove fallback from moduli to "primes" file that was + deprecated in 2001 and fix log messages referring to primes file. Based on + patch from xnox at ubuntu.com via bz#2559. "kill it" deraadt@ - Upstream-ID: f7619652e074ac3febe8363f19622aa4853b679a + Upstream-ID: 0d4f8c70e2fa7431a83b95f8ca81033147ba8713 -commit e92c499a75477ecfe94dd7b4aed89f20b1fac5a7 -Author: mmcc@openbsd.org -Date: Fri Oct 16 17:07:24 2015 +0000 +commit 0235a5fa67fcac51adb564cba69011a535f86f6b +Author: djm@openbsd.org +Date: Thu Mar 17 17:19:43 2016 +0000 upstream commit - 0 -> NULL when comparing with a char*. + UseDNS affects ssh hostname processing in authorized_keys, + not known_hosts; bz#2554 reported by jjelen AT redhat.com - ok dtucker@, djm@. + Upstream-ID: c1c1bb895dde46095fc6d81d8653703928437591 + +commit 8c4739338f5e379d05b19d6e544540114965f07e +Author: Darren Tucker +Date: Tue Mar 15 09:24:43 2016 +1100 + + Don't call Solaris setproject() with UsePAM=yes. - Upstream-ID: a928e9c21c0a9020727d99738ff64027c1272300 + When Solaris Projects are enabled along with PAM setting the project + is PAM's responsiblity. bz#2425, based on patch from + brent.paulson at gmail.com. -commit b1d38a3cc6fe349feb8d16a5f520ef12d1de7cb2 +commit cff26f373c58457a32cb263e212cfff53fca987b +Author: Damien Miller +Date: Tue Mar 15 04:30:21 2016 +1100 + + remove slogin from *.spec + +commit c38905ba391434834da86abfc988a2b8b9b62477 Author: djm@openbsd.org -Date: Thu Oct 15 23:51:40 2015 +0000 +Date: Mon Mar 14 16:20:54 2016 +0000 upstream commit - fix some signed/unsigned integer type mismatches in - format strings; reported by Nicholas Lemonias + unbreak authentication using lone certificate keys in + ssh-agent: when attempting pubkey auth with a certificate, if no separate + private key is found among the keys then try with the certificate key itself. - Upstream-ID: 78cd55420a0eef68c4095bdfddd1af84afe5f95c + bz#2550 reported by Peter Moody + + Upstream-ID: f939cd76d68e6a9a3d1711b5a943d6ed1e623966 -commit 1a2663a15d356bb188196b6414b4c50dc12fd42b +commit 4b4bfb01cd40b9ddb948e6026ddd287cc303d871 Author: djm@openbsd.org -Date: Thu Oct 15 23:08:23 2015 +0000 +Date: Thu Mar 10 11:47:57 2016 +0000 upstream commit - argument to sshkey_from_private() and sshkey_demote() - can't be NULL + sanitise characters destined for xauth reported by + github.com/tintinweb feedback and ok deraadt and markus - Upstream-ID: 0111245b1641d387977a9b38da15916820a5fd1f + Upstream-ID: 18ad8d0d74cbd2ea3306a16595a306ee356aa261 -commit 0f754e29dd3760fc0b172c1220f18b753fb0957e -Author: Damien Miller -Date: Fri Oct 16 10:53:14 2015 +1100 +commit 732b463d37221722b1206f43aa59563766a6a968 +Author: Darren Tucker +Date: Mon Mar 14 16:04:23 2016 +1100 - need va_copy before va_start + Pass supported malloc options to connect-privsep. - reported by Nicholas Lemonias + This allows us to activate only the supported options during the malloc + option portion of the connect-privsep test. -commit eb6c50d82aa1f0d3fc95f5630ea69761e918bfcd -Author: Damien Miller -Date: Thu Oct 15 15:48:28 2015 -0700 +commit d29c5b9b3e9f27394ca97a364ed4bb4a55a59744 +Author: Darren Tucker +Date: Mon Mar 14 09:30:58 2016 +1100 - fix compilation on systems without SYMLOOP_MAX + Remove leftover roaming.h file. + + Pointed out by des at des.no. -commit fafe1d84a210fb3dae7744f268059cc583db8c12 -Author: Damien Miller -Date: Wed Oct 14 09:22:15 2015 -0700 +commit 8ff20ec95f4377021ed5e9b2331320f5c5a34cea +Author: Darren Tucker +Date: Mon Mar 14 09:24:03 2016 +1100 - s/SANDBOX_TAME/SANDBOX_PLEDGE/g + Quote variables that may contain whitespace. + + The variable $L_TMP_ID_FILE needs to be surrounded by quotes in order to + survive paths containing whitespace. bz#2551, from Corinna Vinschen via + Philip Hands. -commit 8f22911027ff6c17d7226d232ccd20727f389310 -Author: Damien Miller -Date: Wed Oct 14 08:28:19 2015 +1100 +commit 627824480c01f0b24541842c7206ab9009644d02 +Author: Darren Tucker +Date: Fri Mar 11 14:47:41 2016 +1100 - upstream commit + Include priv.h for priv_set_t. - revision 1.20 - date: 2015/10/13 20:55:37; author: millert; state: Exp; lines: +2 -2; commitid: X39sl5ay1czgFIgp; - In rev 1.15 the sizeof argument was fixed in a strlcat() call but - the truncation check immediately following it was not updated to - match. Not an issue in practice since the buffers are the same - size. OK deraadt@ + From alex at cooperi.net. -commit 23fa695bb735f54f04d46123662609edb6c76767 -Author: Damien Miller -Date: Wed Oct 14 08:27:51 2015 +1100 +commit e960051f9a264f682c4d2fefbeecffcfc66b0ddf +Author: Darren Tucker +Date: Wed Mar 9 13:14:18 2016 +1100 - upstream commit - - revision 1.19 - date: 2015/01/16 16:48:51; author: deraadt; state: Exp; lines: +3 -3; commitid: 0DYulI8hhujBHMcR; - Move to the universe. - review by millert, binary checking process with doug, concept with guenther + Wrap stdint.h inside #ifdef HAVE_STDINT_H. -commit c71be375a69af00c2d0a0c24d8752bec12d8fd1b -Author: Damien Miller -Date: Wed Oct 14 08:27:08 2015 +1100 +commit 2c48bd344d2c4b5e08dae9aea5ff44fc19a5e363 +Author: Darren Tucker +Date: Wed Mar 9 12:46:50 2016 +1100 - upstream commit + Add compat to monotime_double(). - revision 1.18 - date: 2014/10/19 03:56:28; author: doug; state: Exp; lines: +9 -9; commitid: U6QxmtbXrGoc02S5; - Revert last commit due to changed semantics found by make release. + Apply all of the portability changes in monotime() to monotime() double. + Fixes build on at least older FreeBSD systems. -commit c39ad23b06e9aecc3ff788e92f787a08472905b1 +commit 7b40ef6c2eef40c339f6ea8920cb8a44838e10c9 Author: Damien Miller -Date: Wed Oct 14 08:26:24 2015 +1100 +Date: Tue Mar 8 14:12:58 2016 -0800 - upstream commit - - revision 1.17 - date: 2014/10/18 20:43:52; author: doug; state: Exp; lines: +10 -10; commitid: I74hI1tVZtsspKEt; - Better POSIX compliance in realpath(3). - - millert@ made changes to realpath.c based on FreeBSD's version. I merged - Todd's changes into dl_realpath.c. + make a regress-binaries target - ok millert@, guenther@ + Easier to build all the regression/unit test binaries in one pass + than going through all of ${REGRESS_BINARIES} -commit e929a43f957dbd1254aca2aaf85c8c00cbfc25f4 +commit c425494d6b6181beb54a1b3763ef9e944fd3c214 Author: Damien Miller -Date: Wed Oct 14 08:25:55 2015 +1100 +Date: Tue Mar 8 14:03:54 2016 -0800 - upstream commit - - revision 1.16 - date: 2013/04/05 12:59:54; author: kurt; state: Exp; lines: +3 -1; - - Add comments regarding copies of these files also in libexec/ld.so - okay guenther@ + unbreak kexfuzz for -Werror without __bounded__ -commit 5225db68e58a1048cb17f0e36e0d33bc4a8fc410 +commit 3ed9218c336607846563daea5d5ab4f701f4e042 Author: Damien Miller -Date: Wed Oct 14 08:25:32 2015 +1100 +Date: Tue Mar 8 14:01:29 2016 -0800 - upstream commit - - revision 1.15 - date: 2012/09/13 15:39:05; author: deraadt; state: Exp; lines: +2 -2; - specify the bounds of the dst to strlcat (both values were static and - equal, but it is more correct) - from Michal Mazurek + unbreak PAM after canohost refactor -commit 7365fe5b4859de2305e40ea132da3823830fa710 -Author: Damien Miller -Date: Wed Oct 14 08:25:09 2015 +1100 +commit 885fb2a44ff694f01e4f6470f803629e11f62961 +Author: Darren Tucker +Date: Tue Mar 8 11:58:43 2016 +1100 - upstream commit + auth_get_canonical_hostname in portable code. - revision 1.14 - date: 2011/07/24 21:03:00; author: miod; state: Exp; lines: +35 -13; - Recent Single Unix will malloc memory if the second argument of realpath() - is NULL, and third-party software is starting to rely upon this. - Adapted from FreeBSD via Jona Joachim (jaj ; hcl-club , .lu), with minor - tweaks from nicm@ and yours truly. + "refactor canohost.c" replaced get_canonical_hostname, this makes the + same change to some portable-specific code. -commit e679c09cd1951f963793aa3d9748d1c3fdcf808f +commit 95767262caa6692eff1e1565be1f5cb297949a89 Author: djm@openbsd.org -Date: Tue Oct 13 16:15:21 2015 +0000 +Date: Mon Mar 7 19:02:43 2016 +0000 upstream commit - apply PubkeyAcceptedKeyTypes filtering earlier, so all - skipped keys are noted before pubkey authentication starts. ok dtucker@ + refactor canohost.c: move functions that cache results closer + to the places that use them (authn and session code). After this, no state is + cached in canohost.c - Upstream-ID: ba4f52f54268a421a2a5f98bb375403f4cb044b8 + feedback and ok markus@ + + Upstream-ID: 5f2e4df88d4803fc8ec59ec53629105e23ce625e -commit 179c353f564ec7ada64b87730b25fb41107babd7 -Author: djm@openbsd.org -Date: Tue Oct 13 00:21:27 2015 +0000 +commit af0bb38ffd1f2c4f9f43b0029be2efe922815255 +Author: Damien Miller +Date: Fri Mar 4 15:11:55 2016 +1100 - upstream commit - - free the correct IV length, don't assume it's always the - cipher blocksize; ok dtucker@ - - Upstream-ID: c260d9e5ec73628d9ff4b067fbb060eff5a7d298 + hook unittests/misc/kexfuzz into build -commit 2539dce2a049a8f6bb0d44cac51f07ad48e691d3 -Author: deraadt@openbsd.org -Date: Fri Oct 9 01:37:08 2015 +0000 +commit 331b8e07ee5bcbdca12c11cc8f51a7e8de09b248 +Author: dtucker@openbsd.org +Date: Fri Mar 4 02:48:06 2016 +0000 upstream commit - Change all tame callers to namechange to pledge(2). - - Upstream-ID: 17e654fc27ceaf523c60f4ffd9ec7ae4e7efc7f2 - -commit 9846a2f4067383bb76b4e31a9d2303e0a9c13a73 -Author: Damien Miller -Date: Thu Oct 8 04:30:48 2015 +1100 - - hook tame(2) sandbox up to build + Filter debug messages out of log before picking the last + two lines. Should prevent problems if any more debug output is added late in + the connection. - OpenBSD only for now + Upstream-Regress-ID: 345d0a9589c381e7d640a4ead06cfaadf4db1363 -commit 0c46bbe68b70bdf0d6d20588e5847e71f3739fe6 +commit 0892edaa3ce623381d3a7635544cbc69b31cf9cb Author: djm@openbsd.org -Date: Wed Oct 7 15:59:12 2015 +0000 +Date: Fri Mar 4 02:30:36 2016 +0000 upstream commit - include PubkeyAcceptedKeyTypes in ssh -G config dump + add KEX fuzzer harness; ok deraadt@ - Upstream-ID: 6c097ce6ffebf6fe393fb7988b5d152a5d6b36bb + Upstream-Regress-ID: 3df5242d30551b12b828aa9ba4a4cec0846be8d1 -commit bdcb73fb7641b1cf73c0065d1a0dd57b1e8b778e -Author: sobrado@openbsd.org -Date: Wed Oct 7 14:45:30 2015 +0000 +commit ae2562c47d41b68dbb00240fd6dd60bed205367a +Author: dtucker@openbsd.org +Date: Thu Mar 3 00:46:53 2016 +0000 upstream commit - UsePrivilegeSeparation defaults to sandbox now. - - ok djm@ + Look back 3 lines for possible error messages. Changes + to the code mean that "Bad packet length" errors are 3 lines back instead of + the previous two, which meant we didn't skip some offsets that we intended + to. - Upstream-ID: bff136c38bcae89df82e044d2f42de21e1ad914f + Upstream-Regress-ID: 24f36912740a634d509a3144ebc8eb7c09b9c684 -commit 2905d6f99c837bb699b6ebc61711b19acd030709 +commit 988e429d903acfb298bfddfd75e7994327adfed0 Author: djm@openbsd.org -Date: Wed Oct 7 00:54:06 2015 +0000 +Date: Fri Mar 4 03:35:44 2016 +0000 upstream commit - don't try to change tun device flags if they are already - what we need; makes it possible to use tun/tap networking as non- root user - if device permissions and interface flags are pre-established; based on patch - by Ossi Herrala + fix ClientAliveInterval when a time-based RekeyLimit is + set; previously keepalive packets were not being sent. bz#2252 report and + analysis by Christian Wittenhorst and Garrett Lee feedback and ok dtucker@ - Upstream-ID: 89099ac4634cd477b066865acf54cb230780fd21 - -commit 0dc74512bdb105b048883f07de538b37e5e024d4 -Author: Damien Miller -Date: Mon Oct 5 18:33:05 2015 -0700 - - unbreak merge botch + Upstream-ID: d48f9deadd35fdacdd5106b41bb07630ddd4aa81 -commit fdd020e86439afa7f537e2429d29d4b744c94331 -Author: djm@openbsd.org -Date: Tue Oct 6 01:20:59 2015 +0000 +commit 8ef04d7a94bcdb8b0085fdd2a79a844b7d40792d +Author: dtucker@openbsd.org +Date: Wed Mar 2 22:43:52 2016 +0000 upstream commit - adapt to recent sshkey_parse_private_fileblob() API - change + Improve accuracy of reported transfer speeds by waiting + for the ack from the other end. Pointed out by mmcc@, ok deraadt@ markus@ - Upstream-Regress-ID: 5c0d818da511e33e0abf6a92a31bd7163b7ad988 + Upstream-ID: 99f1cf15c9a8f161086b814d414d862795ae153d -commit 21ae8ee3b630b0925f973db647a1b9aa5fcdd4c5 -Author: djm@openbsd.org -Date: Thu Sep 24 07:15:39 2015 +0000 +commit b8d4eafe29684fe4f5bb587f7eab948e6ed62723 +Author: dtucker@openbsd.org +Date: Wed Mar 2 22:42:40 2016 +0000 upstream commit - fix command-line option to match what was actually - committed + Improve precision of progressmeter for sftp and scp by + storing sub-second timestamps. Pointed out by mmcc@, ok deraadt@ markus@ - Upstream-Regress-ID: 3e8c24a2044e8afd37e7ce17b69002ca817ac699 + Upstream-ID: 38fd83a3d83dbf81c8ff7b5d1302382fe54970ab -commit e14ac43b75e68f1ffbd3e1a5e44143c8ae578dcd -Author: djm@openbsd.org -Date: Thu Sep 24 06:16:53 2015 +0000 +commit 18f64b969c70ed00e74b9d8e50359dbe698ce4c0 +Author: jca@openbsd.org +Date: Mon Feb 29 20:22:36 2016 +0000 upstream commit - regress test for CertificateFile; patch from Meghana Bhat - via bz#2436 + Print ssize_t with %zd; ok deraadt@ mmcc@ - Upstream-Regress-ID: e7a6e980cbe0f8081ba2e83de40d06c17be8bd25 + Upstream-ID: 0590313bbb013ff6692298c98f7e0be349d124bd -commit 905b054ed24e0d5b4ef226ebf2c8bfc02ae6d4ad +commit 6e7f68ce38130c794ec1fb8d2a6091fbe982628d Author: djm@openbsd.org -Date: Mon Oct 5 17:11:21 2015 +0000 +Date: Sun Feb 28 22:27:00 2016 +0000 upstream commit - some more bzero->explicit_bzero, from Michael McConville + rearrange DH public value tests to be a little more clear - Upstream-ID: 17f19545685c33327db2efdc357c1c9225ff00d0 + rearrange DH private value generation to explain rationale more + clearly and include an extra sanity check. + + ok deraadt + + Upstream-ID: 9ad8a07e1a12684e1b329f9bd88941b249d4b2ad -commit b007159a0acdbcf65814b3ee05dbe2cf4ea46011 -Author: deraadt@openbsd.org -Date: Fri Oct 2 15:52:55 2015 +0000 +commit 2ed17aa34008bdfc8db674315adc425a0712be11 +Author: Darren Tucker +Date: Tue Mar 1 15:24:20 2016 +1100 - upstream commit + Import updated moduli file from OpenBSD. - fix email + Note that 1.5k bit groups have been removed. + +commit 72b061d4ba0f909501c595d709ea76e06b01e5c9 +Author: Darren Tucker +Date: Fri Feb 26 14:40:04 2016 +1100 + + Add a note about using xlc on AIX. + +commit fd4e4f2416baa2e6565ea49d52aade296bad3e28 +Author: Darren Tucker +Date: Wed Feb 24 10:44:25 2016 +1100 + + Skip PrintLastLog in config dump mode. - Upstream-ID: 72150f2d54b94de14ebef1ea054ef974281bf834 + When DISABLE_LASTLOG is set, do not try to include PrintLastLog in the + config dump since it'll be reported as UNKNOWN. -commit b19e1b4ab11884c4f62aee9f8ab53127a4732658 -Author: deraadt@openbsd.org -Date: Fri Oct 2 01:39:52 2015 +0000 +commit 99135c764fa250801da5ec3b8d06cbd0111caae8 +Author: Damien Miller +Date: Tue Feb 23 20:17:23 2016 +1100 + + update spec/README versions ahead of release + +commit b86a334aaaa4d1e643eb1fd71f718573d6d948b5 +Author: Damien Miller +Date: Tue Feb 23 20:16:53 2016 +1100 + + put back portable patchlevel to p1 + +commit 555dd35ff176847e3c6bd068ba2e8db4022eb24f +Author: djm@openbsd.org +Date: Tue Feb 23 09:14:34 2016 +0000 upstream commit - a sandbox using tame ok djm + openssh-7.2 - Upstream-ID: 4ca24e47895e72f5daaa02f3e3d3e5ca2d820fa3 + Upstream-ID: 9db776b26014147fc907ece8460ef2bcb0f11e78 -commit c61b42f2678f21f05653ac2d3d241b48ab5d59ac -Author: deraadt@openbsd.org -Date: Fri Oct 2 01:39:26 2015 +0000 +commit 1acc058d0a7913838c830ed998a1a1fb5b7864bf +Author: Damien Miller +Date: Tue Feb 23 16:12:13 2016 +1100 - upstream commit + Disable tests where fs perms are incorrect - re-order system calls in order of risk, ok i'll be - honest, ordered this way they look like tame... ok djm + Some tests have strict requirements on the filesystem permissions + for certain files and directories. This adds a regress/check-perm + tool that copies the relevant logic from sshd to exactly test + the paths in question. This lets us skip tests when the local + filesystem doesn't conform to our expectations rather than + continuing and failing the test run. - Upstream-ID: 42a1e6d251fd8be13c8262bee026059ae6328813 + ok dtucker@ -commit c5f7c0843cb6e6074a93c8ac34e49ce33a6f5546 -Author: jmc@openbsd.org -Date: Fri Sep 25 18:19:54 2015 +0000 +commit 39f303b1f36d934d8410b05625f25c7bcb75db4d +Author: Damien Miller +Date: Tue Feb 23 12:56:59 2016 +1100 - upstream commit + fix sandbox on OSX Lion - some certificatefile tweaks; ok djm + sshd was failing with: - Upstream-ID: 0e5a7852c28c05fc193419cc7e50e64c1c535af0 + ssh_sandbox_child: sandbox_init: dlopen(/usr/lib/libsandbox.1.dylib, 261):cw + image not found [preauth] + + caused by chroot before sandboxing. Avoid by explicitly linking libsandbox + to sshd. Spotted by Darren. -commit 4e44a79a07d4b88b6a4e5e8c1bed5f58c841b1b8 +commit 0d1451a32c7436e6d3d482351e776bc5e7824ce4 Author: djm@openbsd.org -Date: Thu Sep 24 06:15:11 2015 +0000 +Date: Tue Feb 23 01:34:14 2016 +0000 upstream commit - add ssh_config CertificateFile option to explicitly list - a certificate; patch from Meghana Bhat on bz#2436; ok markus@ + fix spurious error message when incorrect passphrase + entered for keys; reported by espie@ ok deraadt@ - Upstream-ID: 58648ec53c510b41c1f46d8fe293aadc87229ab8 + Upstream-ID: 58b2e46e63ed6912ed1ee780bd3bd8560f9a5899 -commit e3cbb06ade83c72b640a53728d362bbefa0008e2 +commit 09d87d79741beb85768b5e788d7dfdf4bc3543dc Author: sobrado@openbsd.org -Date: Tue Sep 22 08:33:23 2015 +0000 +Date: Sat Feb 20 23:06:23 2016 +0000 upstream commit - fix two typos. + set ssh(1) protocol version to 2 only. - Upstream-ID: 424402c0d8863a11b51749bacd7f8d932083b709 + ok djm@ + + Upstream-ID: e168daf9d27d7e392e3c9923826bd8e87b2b3a10 -commit 8408218c1ca88cb17d15278174a24a94a6f65fe1 -Author: djm@openbsd.org -Date: Mon Sep 21 04:31:00 2015 +0000 +commit 9262e07826ba5eebf8423f7ac9e47ec488c47869 +Author: sobrado@openbsd.org +Date: Sat Feb 20 23:02:39 2016 +0000 upstream commit - fix possible hang on closed output; bz#2469 reported by Tomas - Kuthan ok markus@ + add missing ~/.ssh/id_ecdsa and ~/.ssh/id_ed25519 to + IdentityFile. - Upstream-ID: f7afd41810f8540f524284f1be6b970859f94fe3 + ok djm@ + + Upstream-ID: 6ce99466312e4ae7708017c3665e3edb976f70cf -commit 0097248f90a00865082e8c146b905a6555cc146f -Author: djm@openbsd.org -Date: Fri Sep 11 04:55:01 2015 +0000 +commit c12f0fdce8f985fca8d71829fd64c5b89dc777f5 +Author: sobrado@openbsd.org +Date: Sat Feb 20 23:01:46 2016 +0000 upstream commit - skip if running as root; many systems (inc OpenBSD) allow - root to ptrace arbitrary processes + AddressFamily defaults to any. - Upstream-Regress-ID: be2b925df89360dff36f972951fa0fa793769038 + ok djm@ + + Upstream-ID: 0d94aa06a4b889bf57a7f631c45ba36d24c13e0c -commit 9c06c814aff925e11a5cc592c06929c258a014f6 -Author: djm@openbsd.org -Date: Fri Sep 11 03:44:21 2015 +0000 +commit 907091acb188b1057d50c2158f74c3ecf1c2302b +Author: Darren Tucker +Date: Fri Feb 19 09:05:39 2016 +1100 - upstream commit - - try all supported key types here; bz#2455 reported by - Jakub Jelen + Make Solaris privs code build on older systems. - Upstream-Regress-ID: 188cb7d9031cdbac3a0fa58b428b8fa2b2482bba + Not all systems with Solaris privs have priv_basicset so factor that + out and provide backward compatibility code. Similarly, not all have + PRIV_NET_ACCESS so wrap that in #ifdef. Based on code from + alex at cooperi.net and djm@ with help from carson at taltos.org and + wieland at purdue.edu. -commit 3c019a936b43f3e2773f3edbde7c114d73caaa4c -Author: tim@openbsd.org -Date: Sun Sep 13 14:39:16 2015 +0000 +commit 292a8dee14e5e67dcd1b49ba5c7b9023e8420d59 +Author: djm@openbsd.org +Date: Wed Feb 17 22:20:14 2016 +0000 upstream commit - - Fix error message: passphrase needs to be at least 5 - characters, not 4. - Remove unused function argument. - Remove two - unnecessary variables. - - OK djm@ + rekey refactor broke SSH1; spotted by Tom G. Christensen - Upstream-ID: 13010c05bfa8b523da1c0dc19e81dd180662bc30 + Upstream-ID: 43f0d57928cc077c949af0bfa71ef574dcb58243 -commit 2681cdb6e0de7c1af549dac37a9531af202b4434 -Author: tim@openbsd.org -Date: Sun Sep 13 13:48:19 2015 +0000 +commit 3a13cb543df9919aec2fc6b75f3dd3802facaeca +Author: djm@openbsd.org +Date: Wed Feb 17 08:57:34 2016 +0000 upstream commit - When adding keys to the agent, don't ignore the comment - of keys for which the user is prompted for a passphrase. - - Tweak and OK djm@ + rsa-sha2-512,rsa-sha2-256 cannot be selected explicitly + in *KeyTypes options yet. Remove them from the lists of algorithms for now. + committing on behalf of markus@ ok djm@ - Upstream-ID: dc737c620a5a8d282cc4f66e3b9b624e9abefbec + Upstream-ID: c6e8820eb8e610ac21551832c0c89684a9a51bb7 -commit 14692f7b8251cdda847e648a82735eef8a4d2a33 -Author: guenther@openbsd.org -Date: Fri Sep 11 08:50:04 2015 +0000 +commit a685ae8d1c24fb7c712c55a4f3280ee76f5f1e4b +Author: jmc@openbsd.org +Date: Wed Feb 17 07:38:19 2016 +0000 upstream commit - Use explicit_bzero() when zeroing before free() + since these pages now clearly tell folks to avoid v1, + normalise the docs from a v2 perspective (i.e. stop pointing out which bits + are v2 only); - from Michael McConville (mmcconv1 (at) sccs.swarthmore.edu) - ok millert@ djm@ + ok/tweaks djm ok markus - Upstream-ID: 2e3337db046c3fe70c7369ee31515ac73ec00f50 + Upstream-ID: eb474f8c36fb6a532dc05c282f7965e38dcfa129 -commit 846f6fa4cfa8483a9195971dbdd162220f199d85 -Author: jmc@openbsd.org -Date: Fri Sep 11 06:55:46 2015 +0000 +commit c5c3f3279a0e4044b8de71b70d3570d692d0f29d +Author: djm@openbsd.org +Date: Wed Feb 17 05:29:04 2016 +0000 upstream commit - sync -Q in usage() to SYNOPSIS; since it's drastically - shorter, i've reformatted the block to sync with the man (80 cols) and saved - a line; + make sandboxed privilege separation the default, not just + for new installs; "absolutely" deraadt@ - Upstream-ID: 86e2c65c3989a0777a6258a77e589b9f6f354abd + Upstream-ID: 5221ef3b927d2df044e9aa3f5db74ae91743f69b -commit 95923e0520a8647417ee6dcdff44694703dfeef0 +commit eb3f7337a651aa01d5dec019025e6cdc124ed081 Author: jmc@openbsd.org -Date: Fri Sep 11 06:51:39 2015 +0000 +Date: Tue Feb 16 07:47:54 2016 +0000 upstream commit - tweak previous; + no need to state that protocol 2 is the default twice; - Upstream-ID: f29b3cfcfd9aa31fa140c393e7bd48c1c74139d6 + Upstream-ID: b1e4c36b0c2e12e338e5b66e2978f2ac953b95eb -commit 86ac462f833b05d8ed9de9c50ccb295d7faa79ff -Author: dtucker@openbsd.org -Date: Fri Sep 11 05:27:02 2015 +0000 +commit e7901efa9b24e5b0c7e74f2c5520d47eead4d005 +Author: djm@openbsd.org +Date: Tue Feb 16 05:11:04 2016 +0000 upstream commit - Update usage to match man page. + Replace list of ciphers and MACs adjacent to -1/-2 flag + descriptions in ssh(1) with a strong recommendation not to use protocol 1. + Add a similar warning to the Protocol option descriptions in ssh_config(5) + and sshd_config(5); - Upstream-ID: 9e85aefaecfb6aaf34c7cfd0700cd21783a35675 + prompted by and ok mmcc@ + + Upstream-ID: 961f99e5437d50e636feca023978950a232ead5e -commit 674b3b68c1d36b2562324927cd03857b565e05e8 +commit 5a0fcb77287342e2fc2ba1cee79b6af108973dc2 Author: djm@openbsd.org -Date: Fri Sep 11 03:47:28 2015 +0000 +Date: Tue Feb 16 03:37:48 2016 +0000 upstream commit - expand %i in ControlPath to UID; bz#2449 + add a "Close session" log entry (at loglevel=verbose) to + correspond to the existing "Starting session" one. Also include the session + id number to make multiplexed sessions more apparent. - patch from Christian Hesse w/ feedback from dtucker@ + feedback and ok dtucker@ - Upstream-ID: 2ba8d303e555a84e2f2165ab4b324b41e80ab925 + Upstream-ID: e72d2ac080e02774376325136e532cb24c2e617c -commit c0f55db7ee00c8202b05cb4b9ad4ce72cc45df41 +commit 624fd395b559820705171f460dd33d67743d13d6 Author: djm@openbsd.org -Date: Fri Sep 11 03:42:32 2015 +0000 +Date: Wed Feb 17 02:24:17 2016 +0000 upstream commit - mention -Q key-plain and -Q key-cert; bz#2455 pointed out - by Jakub Jelen + include bad $SSH_CONNECTION in failure output - Upstream-ID: c8f1f8169332e4fa73ac96b0043e3b84e01d4896 + Upstream-Regress-ID: b22d72edfde78c403aaec2b9c9753ef633cc0529 -commit cfffbdb10fdf0f02d3f4232232eef7ec3876c383 +commit 60d860e54b4f199e5e89963b1c086981309753cb Author: Darren Tucker -Date: Mon Sep 14 16:24:21 2015 +1000 +Date: Wed Feb 17 13:37:09 2016 +1100 - Use ssh-keygen -A when generating host keys. + Rollback addition of va_start. - Use ssh-keygen -A instead of per-keytype invocations when generating host - keys. Add tests when doing host-key-force since we can't use ssh-keygen -A - since it can't specify alternate locations. bz#2459, ok djm@ + va_start was added in 0f754e29dd3760fc0b172c1220f18b753fb0957e, however + it has the wrong number of args and it's not usable in non-variadic + functions anyway so it breaks things (for example Solaris 2.6 as + reported by Tom G. Christensen).i ok djm@ -commit 366bada1e9e124654aac55b72b6ccf878755b0dc +commit 2fee909c3cee2472a98b26eb82696297b81e0d38 Author: Darren Tucker -Date: Fri Sep 11 13:29:22 2015 +1000 +Date: Wed Feb 17 09:48:15 2016 +1100 - Correct default value for --with-ssh1. + Look for gethostbyname in libresolv and libnsl. - bz#2457, from konto-mindrot.org at walimnieto.com. + Should fix build problem on Solaris 2.6 reported by Tom G. Christensen. -commit 2bca8a43e7dd9b04d7070824ffebb823c72587b2 -Author: djm@openbsd.org -Date: Fri Sep 11 03:13:36 2015 +0000 +commit 5ac712d81a84396aab441a272ec429af5b738302 +Author: Damien Miller +Date: Tue Feb 16 10:45:02 2016 +1100 - upstream commit - - more clarity on what AuthorizedKeysFile=none does; based - on diff by Thiebaud Weksteen - - Upstream-ID: 78ab87f069080f0cc3bc353bb04eddd9e8ad3704 + make existing ssh_malloc_init only for __OpenBSD__ -commit 61942ea4a01e6db4fdf37ad61de81312ffe310e9 +commit 24c9bded569d9f2449ded73f92fb6d12db7a9eec Author: djm@openbsd.org -Date: Wed Sep 9 00:52:44 2015 +0000 +Date: Mon Feb 15 23:32:37 2016 +0000 upstream commit - openssh_RSA_verify return type is int, so don't make it - size_t within the function itself with only negative numbers or zero assigned - to it. bz#2460 + memleak of algorithm name in mm_answer_sign; reported by + Jakub Jelen - Upstream-ID: b6e794b0c7fc4f9f329509263c8668d35f83ea55 + Upstream-ID: ccd742cd25952240ebd23d7d4d6b605862584d08 -commit 4f7cc2f8cc861a21e6dbd7f6c25652afb38b9b96 +commit ffb1e7e896139a42ceb78676f637658f44612411 Author: dtucker@openbsd.org -Date: Fri Sep 4 08:21:47 2015 +0000 +Date: Mon Feb 15 09:47:49 2016 +0000 upstream commit - Plug minor memory leaks when options are used more than - once. bz#2182, patch from Tiago Cunha, ok deraadt djm - - Upstream-ID: 5b84d0401e27fe1614c10997010cc55933adb48e - -commit 7ad8b287c8453a3e61dbc0d34d467632b8b06fc8 -Author: Darren Tucker -Date: Fri Sep 11 13:11:02 2015 +1000 - - Force resolution of _res for correct detection. + Add a function to enable security-related malloc_options. + With and ok deraadt@, something similar has been in the snaps for a while. - bz#2259, from sconeu at yahoo.com. + Upstream-ID: 43a95523b832b7f3b943d2908662191110c380ed -commit 26ad18247213ff72b4438abe7fc660c958810fa2 +commit ef39e8c0497ff0564990a4f9e8b7338b3ba3507c Author: Damien Miller -Date: Thu Sep 10 10:57:41 2015 +1000 - - allow getrandom syscall; from Felix von Leitner - -commit 5245bc1e6b129a10a928f73f11c3aa32656c44b4 -Author: jmc@openbsd.org -Date: Fri Sep 4 06:40:45 2015 +0000 +Date: Tue Feb 16 10:34:39 2016 +1100 - upstream commit - - full stop belongs outside the brackets, not inside; - - Upstream-ID: 99d098287767799ac33d2442a05b5053fa5a551a + sync ssh-copy-id with upstream 783ef08b0a75 -commit a85768a9321d74b41219eeb3c9be9f1702cbf6a5 +commit d2d772f55b19bb0e8d03c2fe1b9bb176d9779efd Author: djm@openbsd.org -Date: Fri Sep 4 04:56:09 2015 +0000 +Date: Fri Feb 12 00:20:30 2016 +0000 upstream commit - add a debug2() right before DNS resolution; it's a place - where ssh could previously silently hang for a while. bz#2433 + avoid fatal() for PKCS11 tokens that present empty key IDs + bz#1773, ok markus@ - Upstream-ID: 52a1a3e0748db66518e7598352c427145692a6a0 + Upstream-ID: 044a764fee526f2c4a9d530bd10695422d01fc54 -commit 46152af8d27aa34d5d26ed1c371dc8aa142d4730 +commit e4c918a6c721410792b287c9fd21356a1bed5805 Author: djm@openbsd.org -Date: Fri Sep 4 04:55:24 2015 +0000 +Date: Thu Feb 11 02:56:32 2016 +0000 upstream commit - correct function name in error messages + sync crypto algorithm lists in ssh_config(5) and + sshd_config(5) with current reality. bz#2527 - Upstream-ID: 92fb2798617ad9561370897f4ab60adef2ff4c0e + Upstream-ID: d7fd1b6c1ed848d866236bcb1d7049d2bb9b2ff6 -commit a954cdb799a4d83c2d40fbf3e7b9f187fbfd72fc +commit e30cabfa4ab456a30b3224f7f545f1bdfc4a2517 Author: djm@openbsd.org -Date: Fri Sep 4 04:47:50 2015 +0000 +Date: Thu Feb 11 02:21:34 2016 +0000 upstream commit - better document ExitOnForwardFailure; bz#2444, ok - dtucker@ + fix regression in openssh-6.8 sftp client: existing + destination directories would incorrectly terminate recursive uploads; + bz#2528 - Upstream-ID: a126209b5a6d9cb3117ac7ab5bc63d284538bfc2 + Upstream-ID: 3306be469f41f26758e3d447987ac6d662623e18 -commit f54d8ac2474b6fc3afa081cf759b48a6c89d3319 +commit 714e367226ded4dc3897078be48b961637350b05 Author: djm@openbsd.org -Date: Fri Sep 4 04:44:08 2015 +0000 +Date: Tue Feb 9 05:30:04 2016 +0000 upstream commit - don't record hostbased authentication hostkeys as user - keys in test for multiple authentication with the same key + turn off more old crypto in the client: hmac-md5, ripemd, + truncated HMACs, RC4, blowfish. ok markus@ dtucker@ - Upstream-ID: 26b368fa2cff481f47f37e01b8da1ae5b57b1adc + Upstream-ID: 96aa11c2c082be45267a690c12f1d2aae6acd46e -commit ac3451dd65f27ecf85dc045c46d49e2bbcb8dddd +commit 5a622844ff7f78dcb75e223399f9ef0977e8d0a3 Author: djm@openbsd.org -Date: Fri Sep 4 03:57:38 2015 +0000 +Date: Mon Feb 8 23:40:12 2016 +0000 upstream commit - remove extra newline in nethack-mode hostkey; from - Christian Hesse bz#2686 + don't attempt to percent_expand() already-canonicalised + addresses, avoiding unnecessary failures when attempting to connect to scoped + IPv6 addresses (that naturally contain '%' characters) - Upstream-ID: 4f56368b1cc47baeea0531912186f66007fd5b92 + Upstream-ID: f24569cffa1a7cbde5f08dc739a72f4d78aa5c6a -commit 9e3ed9ebb1a7e47c155c28399ddf09b306ea05df +commit 19bcf2ea2d17413f2d9730dd2a19575ff86b9b6a Author: djm@openbsd.org -Date: Fri Sep 4 04:23:10 2015 +0000 +Date: Mon Feb 8 10:57:07 2016 +0000 upstream commit - trim junk from end of file; bz#2455 from Jakub Jelen - - Upstream-Regress-ID: a4e64e8931e40d23874b047074444eff919cdfe6 - -commit f3a3ea180afff080bab82087ee0b60db9fd84f6c -Author: jsg@openbsd.org -Date: Wed Sep 2 07:51:12 2015 +0000 - - upstream commit + refactor activation of rekeying - Fix occurrences of "r = func() != 0" which result in the - wrong error codes being returned due to != having higher precedence than =. + This makes automatic rekeying internal to the packet code (previously + the server and client loops needed to assist). In doing to it makes + application of rekey limits more accurate by accounting for packets + about to be sent as well as packets queued during rekeying events + themselves. - ok deraadt@ markus@ + Based on a patch from dtucker@ which was in turn based on a patch + Aleksander Adamowski in bz#2521; ok markus@ - Upstream-ID: 5fc35c9fc0319cc6fca243632662d2f06b5fd840 - -commit f498a98cf83feeb7ea01c15cd1c98b3111361f3a -Author: Damien Miller -Date: Thu Sep 3 09:11:22 2015 +1000 - - don't check for yp_match; ok tim@ + Upstream-ID: a441227fd64f9739850ca97b4cf794202860fcd8 -commit 9690b78b7848b0b376980a61d51b1613e187ddb5 -Author: djm@openbsd.org -Date: Fri Aug 21 23:57:48 2015 +0000 +commit 603ba41179e4b53951c7b90ee95b6ef3faa3f15d +Author: naddy@openbsd.org +Date: Fri Feb 5 13:28:19 2016 +0000 upstream commit - Improve printing of KEX offers and decisions - - The debug output now labels the client and server offers and the - negotiated options. ok markus@ + Only check errno if read() has returned an error. EOF is + not an error. This fixes a problem where the mux master would sporadically + fail to notice that the client had exited. ok mikeb@ djm@ - Upstream-ID: 8db921b3f92a4565271b1c1fbce6e7f508e1a2cb + Upstream-ID: 3c2dadc21fac6ef64665688aac8a75fffd57ae53 -commit 60a92470e21340e1a3fc10f9c7140d8e1519dc55 -Author: djm@openbsd.org -Date: Fri Aug 21 23:53:08 2015 +0000 +commit 56d7dac790693ce420d225119283bc355cff9185 +Author: jsg@openbsd.org +Date: Fri Feb 5 04:31:21 2016 +0000 upstream commit - Fix printing (ssh -G ...) of HostKeyAlgorithms=+... - Reported by Bryan Drewery + avoid an uninitialised value when NumberOfPasswordPrompts + is 0 ok markus@ djm@ - Upstream-ID: 19ad20c41bd5971e006289b6f9af829dd46c1293 + Upstream-ID: 11b068d83c2865343aeb46acf1e9eec00f829b6b -commit 6310f60fffca2d1e464168e7d1f7e3b6b0268897 +commit deae7d52d59c5019c528f977360d87fdda15d20b Author: djm@openbsd.org -Date: Fri Aug 21 23:52:30 2015 +0000 +Date: Fri Feb 5 03:07:06 2016 +0000 upstream commit - Fix expansion of HostkeyAlgorithms=+... - - Reported by Bryan Drewery + mention internal DH-GEX fallback groups; bz#2302 - Upstream-ID: 70ca1deea39d758ba36d36428ae832e28566f78d + Upstream-ID: e7b395fcca3122cd825515f45a2e41c9a157e09e -commit e774e5ea56237fd626a8161f9005023dff3e76c9 -Author: deraadt@openbsd.org -Date: Fri Aug 21 23:29:31 2015 +0000 +commit cac3b6665f884d46192c0dc98a64112e8b11a766 +Author: djm@openbsd.org +Date: Fri Feb 5 02:37:56 2016 +0000 upstream commit - Improve size == 0, count == 0 checking in mm_zalloc, - which is "array" like. Discussed with tedu, millert, otto.... and ok djm + better description for MaxSessions; bz#2531 - Upstream-ID: 899b021be43b913fad3eca1aef44efe710c53e29 - -commit 189de02d9ad6f3645417c0ddf359b923aae5f926 -Author: Damien Miller -Date: Fri Aug 21 15:45:02 2015 +1000 - - expose POLLHUP and POLLNVAL for netcat.c + Upstream-ID: e2c0d74ee185cd1a3e9d4ca1f1b939b745b354da -commit e91346dc2bbf460246df2ab591b7613908c1b0ad +commit 5ef4b0fdcc7a239577a754829b50022b91ab4712 Author: Damien Miller -Date: Fri Aug 21 14:49:03 2015 +1000 +Date: Wed Jan 27 17:45:56 2016 +1100 - we don't use Github for issues/pull-requests + avoid FreeBSD RCS Id in comment + + Change old $FreeBSD version string in comment so it doesn't + become an RCS ident downstream; requested by des AT des.no -commit a4f5b507c708cc3dc2c8dd2d02e4416d7514dc23 -Author: Damien Miller -Date: Fri Aug 21 14:43:55 2015 +1000 +commit 696d12683c90d20a0a9c5f4275fc916b7011fb04 +Author: djm@openbsd.org +Date: Thu Feb 4 23:43:48 2016 +0000 - fix URL for connect.c + upstream commit + + printf argument casts to avoid warnings on strict + compilers + + Upstream-ID: 7b9f6712cef01865ad29070262d366cf13587c9c -commit d026a8d3da0f8186598442997c7d0a28e7275414 -Author: Damien Miller -Date: Fri Aug 21 13:47:10 2015 +1000 +commit 5658ef2501e785fbbdf5de2dc33b1ff7a4dca73a +Author: millert@openbsd.org +Date: Mon Feb 1 21:18:17 2016 +0000 - update version numbers for 7.1 + upstream commit + + Avoid ugly "DISPLAY "(null)" invalid; disabling X11 + forwarding" message when DISPLAY is not set. This could also result in a + crash on systems with a printf that doesn't handle NULL. OK djm@ + + Upstream-ID: 20ee0cfbda678a247264c20ed75362042b90b412 -commit 78f8f589f0ca1c9f41e5a9bae3cda5ce8a6b42ed -Author: djm@openbsd.org -Date: Fri Aug 21 03:45:26 2015 +0000 +commit 537f88ec7bcf40bd444ac5584c707c5588c55c43 +Author: dtucker@openbsd.org +Date: Fri Jan 29 05:18:15 2016 +0000 upstream commit - openssh-7.1 + Add regression test for RekeyLimit parsing of >32bit values + (4G and 8G). - Upstream-ID: ff7b1ef4b06caddfb45e08ba998128c88be3d73f + Upstream-Regress-ID: 548390350c62747b6234f522a99c319eee401328 -commit 32a181980c62fce94f7f9ffaf6a79d90f0c309cf -Author: djm@openbsd.org -Date: Fri Aug 21 03:42:19 2015 +0000 +commit 4c6cb8330460f94e6c7ae28a364236d4188156a3 +Author: dtucker@openbsd.org +Date: Fri Jan 29 23:04:46 2016 +0000 upstream commit - fix inverted logic that broke PermitRootLogin; reported - by Mantas Mikulenas; ok markus@ + Remove leftover roaming dead code. ok djm markus. - Upstream-ID: 260dd6a904c1bb7e43267e394b1c9cf70bdd5ea5 + Upstream-ID: 13d1f9c8b65a5109756bcfd3b74df949d53615be -commit ce445b0ed927e45bd5bdce8f836eb353998dd65c -Author: deraadt@openbsd.org -Date: Thu Aug 20 22:32:42 2015 +0000 +commit 28136471809806d6246ef41e4341467a39fe2f91 +Author: djm@openbsd.org +Date: Fri Jan 29 05:46:01 2016 +0000 upstream commit - Do not cast result of malloc/calloc/realloc* if stdlib.h - is in scope ok krw millert + include packet type of non-data packets in debug3 output; + ok markus dtucker - Upstream-ID: 5e50ded78cadf3841556649a16cc4b1cb6c58667 + Upstream-ID: 034eaf639acc96459b9c5ce782db9fcd8bd02d41 -commit 05291e5288704d1a98bacda269eb5a0153599146 -Author: naddy@openbsd.org -Date: Thu Aug 20 19:20:06 2015 +0000 +commit 6fd6e28daccafaa35f02741036abe64534c361a1 +Author: dtucker@openbsd.org +Date: Fri Jan 29 03:31:03 2016 +0000 upstream commit - In the certificates section, be consistent about using - "host_key" and "user_key" for the respective key types. ok sthen@ deraadt@ + Revert "account for packets buffered but not yet + processed" change as it breaks for very small RekeyLimit values due to + continuous rekeying. ok djm@ - Upstream-ID: 9e037ea3b15577b238604c5533e082a3947f13cb + Upstream-ID: 7e03f636cb45ab60db18850236ccf19079182a19 -commit 8543d4ef6f2e9f98c3e6b77c894ceec30c5e4ae4 -Author: djm@openbsd.org -Date: Wed Aug 19 23:21:42 2015 +0000 +commit 921ff00b0ac429666fb361d2d6cb1c8fff0006cb +Author: dtucker@openbsd.org +Date: Fri Jan 29 02:54:45 2016 +0000 upstream commit - Better compat matching for WinSCP, add compat matching - for FuTTY (fork of PuTTY); ok markus@ deraadt@ + Allow RekeyLimits in excess of 4G up to 2**63 bits + (limited by the return type of scan_scaled). Part of bz#2521, ok djm. - Upstream-ID: 24001d1ac115fa3260fbdc329a4b9aeb283c5389 + Upstream-ID: 13bea82be566b9704821b1ea05bf7804335c7979 -commit ec6eda16ebab771aa3dfc90629b41953b999cb1e -Author: djm@openbsd.org -Date: Wed Aug 19 23:19:01 2015 +0000 +commit c0060a65296f01d4634f274eee184c0e93ba0f23 +Author: dtucker@openbsd.org +Date: Fri Jan 29 02:42:46 2016 +0000 upstream commit - fix double-free() in error path of DSA key generation - reported by Mateusz Kocielski; ok markus@ + Account for packets buffered but not yet processed when + computing whether or not it is time to perform rekeying. bz#2521, based + loosely on a patch from olo at fb.com, ok djm@ - Upstream-ID: 4735d8f888b10599a935fa1b374787089116713c + Upstream-ID: 67e268b547f990ed220f3cb70a5624d9bda12b8c -commit 45b0eb752c94954a6de046bfaaf129e518ad4b5b +commit 44cf930e670488c85c9efeb373fa5f4b455692ac Author: djm@openbsd.org -Date: Wed Aug 19 23:18:26 2015 +0000 +Date: Wed Jan 27 06:44:58 2016 +0000 upstream commit - fix free() of uninitialised pointer reported by Mateusz - Kocielski; ok markus@ + change old $FreeBSD version string in comment so it doesn't + become an RCS ident downstream; requested by des AT des.no - Upstream-ID: 519552b050618501a06b7b023de5cb104e2c5663 + Upstream-ID: 8ca558c01f184e596b45e4fc8885534b2c864722 -commit c837643b93509a3ef538cb6624b678c5fe32ff79 +commit ebacd377769ac07d1bf3c75169644336056b7060 Author: djm@openbsd.org -Date: Wed Aug 19 23:17:51 2015 +0000 +Date: Wed Jan 27 00:53:12 2016 +0000 upstream commit - fixed unlink([uninitialised memory]) reported by Mateusz - Kocielski; ok markus@ + make the debug messages a bit more useful here - Upstream-ID: 14a0c4e7d891f5a8dabc4b89d4f6b7c0d5a20109 + Upstream-ID: 478ccd4e897e0af8486b294aa63aa3f90ab78d64 -commit 1f8d3d629cd553031021068eb9c646a5f1e50994 -Author: jmc@openbsd.org -Date: Fri Aug 14 15:32:41 2015 +0000 +commit 458abc2934e82034c5c281336d8dc0f910aecad3 +Author: jsg@openbsd.org +Date: Sat Jan 23 05:31:35 2016 +0000 upstream commit - match myproposal.h order; from brian conway (i snuck in a - tweak while here) + Zero a stack buffer with explicit_bzero() instead of + memset() when returning from client_loop() for consistency with + buffer_free()/sshbuf_free(). - ok dtucker + ok dtucker@ deraadt@ djm@ - Upstream-ID: 35174a19b5237ea36aa3798f042bf5933b772c67 + Upstream-ID: bc9975b2095339811c3b954694d7d15ea5c58f66 -commit 1dc8d93ce69d6565747eb44446ed117187621b26 -Author: deraadt@openbsd.org -Date: Thu Aug 6 14:53:21 2015 +0000 +commit 65a3c0dacbc7dbb75ddb6a70ebe22d8de084d0b0 +Author: dtucker@openbsd.org +Date: Wed Jan 20 09:22:39 2016 +0000 upstream commit - add prohibit-password as a synonymn for without-password, - since the without-password is causing too many questions. Harden it to ban - all but pubkey, hostbased, and GSSAPI auth (when the latter is enabled) from - djm, ok markus + Include sys/time.h for gettimeofday. From sortie at + maxsi.org. - Upstream-ID: d53317d7b28942153e6236d3fd6e12ceb482db7a - -commit 90a95a4745a531b62b81ce3b025e892bdc434de5 -Author: Damien Miller -Date: Tue Aug 11 13:53:41 2015 +1000 - - update version in README + Upstream-ID: 6ed0c33b836d9de0a664cd091e86523ecaa2fb3b -commit 318c37743534b58124f1bab37a8a0087a3a9bd2f -Author: Damien Miller -Date: Tue Aug 11 13:53:09 2015 +1000 +commit fc77ccdc2ce6d5d06628b8da5048a6a5f6ffca5a +Author: markus@openbsd.org +Date: Thu Jan 14 22:56:56 2016 +0000 - update versions in *.spec + upstream commit + + fd leaks; report Qualys Security Advisory team; ok + deraadt@ + + Upstream-ID: 4ec0f12b9d8fa202293c9effa115464185aa071d -commit 5e75f5198769056089fb06c4d738ab0e5abc66f7 -Author: Damien Miller -Date: Tue Aug 11 13:34:12 2015 +1000 +commit a306863831c57ec5fad918687cc5d289ee8e2635 +Author: markus@openbsd.org +Date: Thu Jan 14 16:17:39 2016 +0000 - set sshpam_ctxt to NULL after free + upstream commit - Avoids use-after-free in monitor when privsep child is compromised. - Reported by Moritz Jodeit; ok dtucker@ + remove roaming support; ok djm@ + + Upstream-ID: 2cab8f4b197bc95776fb1c8dc2859dad0c64dc56 -commit d4697fe9a28dab7255c60433e4dd23cf7fce8a8b -Author: Damien Miller -Date: Tue Aug 11 13:33:24 2015 +1000 +commit 6ef49e83e30688504552ac10875feabd5521565f +Author: deraadt@openbsd.org +Date: Thu Jan 14 14:34:34 2016 +0000 - Don't resend username to PAM; it already has it. + upstream commit - Pointed out by Moritz Jodeit; ok dtucker@ + Disable experimental client-side roaming support. Server + side was disabled/gutted for years already, but this aspect was surprisingly + forgotten. Thanks for report from Qualys + + Upstream-ID: 2328004b58f431a554d4c1bf67f5407eae3389df -commit 88763a6c893bf3dfe951ba9271bf09715e8d91ca -Author: Darren Tucker -Date: Mon Jul 27 12:14:25 2015 +1000 +commit 8d7b523b96d3be180572d9d338cedaafc0570f60 +Author: Damien Miller +Date: Thu Jan 14 11:08:19 2016 +1100 - Import updated moduli file from OpenBSD. + bump version numbers -commit 55b263fb7cfeacb81aaf1c2036e0394c881637da +commit 8c3d512a1fac8b9c83b4d0c9c3f2376290bd84ca Author: Damien Miller -Date: Mon Aug 10 11:13:44 2015 +1000 +Date: Thu Jan 14 11:04:04 2016 +1100 - let principals-command.sh work for noexec /var/run + openssh-7.1p2 -commit 2651e34cd11b1aac3a0fe23b86d8c2ff35c07897 +commit e6c85f8889c5c9eb04796fdb76d2807636b9eef5 Author: Damien Miller -Date: Thu Aug 6 11:43:42 2015 +1000 +Date: Fri Jan 15 01:30:36 2016 +1100 - work around echo -n / sed behaviour in tests + forcibly disable roaming support in the client -commit d85dad81778c1aa8106acd46930b25fdf0d15b2a +commit ed4ce82dbfa8a3a3c8ea6fa0db113c71e234416c Author: djm@openbsd.org -Date: Wed Aug 5 05:27:33 2015 +0000 +Date: Wed Jan 13 23:04:47 2016 +0000 upstream commit - adjust for RSA minimum modulus switch; ok deraadt@ + eliminate fallback from untrusted X11 forwarding to trusted + forwarding when the X server disables the SECURITY extension; Reported by + Thomas Hoger; ok deraadt@ - Upstream-Regress-ID: 5a72c83431b96224d583c573ca281cd3a3ebfdae + Upstream-ID: f76195bd2064615a63ef9674a0e4096b0713f938 -commit 57e8e229bad5fe6056b5f1199665f5f7008192c6 +commit 9a728cc918fad67c8a9a71201088b1e150340ba4 Author: djm@openbsd.org -Date: Tue Aug 4 05:23:06 2015 +0000 +Date: Tue Jan 12 23:42:54 2016 +0000 upstream commit - backout SSH_RSA_MINIMUM_MODULUS_SIZE increase for this - release; problems spotted by sthen@ ok deraadt@ markus@ + use explicit_bzero() more liberally in the buffer code; ok + deraadt - Upstream-ID: d0bd60dde9e8c3cd7030007680371894c1499822 + Upstream-ID: 0ece37069fd66bc6e4f55eb1321f93df372b65bf -commit f097d0ea1e0889ca0fa2e53a00214e43ab7fa22a -Author: djm@openbsd.org -Date: Sun Aug 2 09:56:42 2015 +0000 +commit 4626cbaf78767fc8e9c86dd04785386c59ae0839 +Author: Damien Miller +Date: Fri Jan 8 14:24:56 2016 +1100 - upstream commit + Support Illumos/Solaris fine-grained privileges - openssh 7.0; ok deraadt@ + Includes a pre-auth privsep sandbox and several pledge() + emulations. bz#2511, patch by Alex Wilson. - Upstream-ID: c63afdef537f57f28ae84145c5a8e29e9250221f + ok dtucker@ -commit 3d5728a0f6874ce4efb16913a12963595070f3a9 -Author: chris@openbsd.org -Date: Fri Jul 31 15:38:09 2015 +0000 +commit 422d1b3ee977ff4c724b597fb2e437d38fc8de9d +Author: djm@openbsd.org +Date: Thu Dec 31 00:33:52 2015 +0000 upstream commit - Allow PermitRootLogin to be overridden by config - - ok markus@ deeradt@ + fix three bugs in KRL code related to (unused) signature + support: verification length was being incorrectly calculated, multiple + signatures were being incorrectly processed and a NULL dereference that + occurred when signatures were verified. Reported by Carl Jackson - Upstream-ID: 5cf3e26ed702888de84e2dc9d0054ccf4d9125b4 + Upstream-ID: e705e97ad3ccce84291eaa651708dd1b9692576b -commit 6f941396b6835ad18018845f515b0c4fe20be21a +commit 6074c84bf95d00f29cc7d5d3cd3798737851aa1a Author: djm@openbsd.org -Date: Thu Jul 30 23:09:15 2015 +0000 +Date: Wed Dec 30 23:46:14 2015 +0000 upstream commit - fix pty permissions; patch from Nikolay Edigaryev; ok - deraadt + unused prototype - Upstream-ID: 40ff076d2878b916fbfd8e4f45dbe5bec019e550 + Upstream-ID: f3eef4389d53ed6c0d5c77dcdcca3060c745da97 -commit f4373ed1e8fbc7c8ce3fc4ea97d0ba2e0c1d7ef0 -Author: deraadt@openbsd.org -Date: Thu Jul 30 19:23:02 2015 +0000 +commit 6213f0e180e54122bb1ba928e11c784e2b4e5380 +Author: guenther@openbsd.org +Date: Sat Dec 26 20:51:35 2015 +0000 upstream commit - change default: PermitRootLogin without-password matching - install script changes coming as well ok djm markus + Use pread/pwrite instead separate lseek+read/write for + lastlog. Cast to off_t before multiplication to avoid truncation on ILP32 - Upstream-ID: 0e2a6c4441daf5498b47a61767382bead5eb8ea6 - -commit 0c30ba91f87fcda7e975e6ff8a057f624e87ea1c -Author: Damien Miller -Date: Thu Jul 30 12:31:39 2015 +1000 - - downgrade OOM adjustment logging: verbose -> debug + ok kettenis@ mmcc@ + + Upstream-ID: fc40092568cd195719ddf1a00aa0742340d616cf -commit f9eca249d4961f28ae4b09186d7dc91de74b5895 -Author: djm@openbsd.org -Date: Thu Jul 30 00:01:34 2015 +0000 +commit d7d2bc95045a43dd56ea696cc1d030ac9d77e81f +Author: semarie@openbsd.org +Date: Sat Dec 26 07:46:03 2015 +0000 upstream commit - Allow ssh_config and sshd_config kex parameters options be - prefixed by a '+' to indicate that the specified items be appended to the - default rather than replacing it. + adjust pledge promises for ControlMaster: when using + "ask" or "autoask", the process will use ssh-askpass for asking confirmation. - approach suggested by dtucker@, feedback dlg@, ok markus@ + problem found by halex@ - Upstream-ID: 0f901137298fc17095d5756ff1561a7028e8882a + ok halex@ + + Upstream-ID: 38a58b30ae3eef85051c74d3c247216ec0735f80 -commit 5cefe769105a2a2e3ca7479d28d9a325d5ef0163 +commit 271df8185d9689b3fb0523f58514481b858f6843 Author: djm@openbsd.org -Date: Wed Jul 29 08:34:54 2015 +0000 +Date: Sun Dec 13 22:42:23 2015 +0000 upstream commit - fix bug in previous; was printing incorrect string for - failed host key algorithms negotiation + unbreak connections with peers that set + first_kex_follows; fix from Matt Johnston va bz#2515 - Upstream-ID: 22c0dc6bc61930513065d92e11f0753adc4c6e6e + Upstream-ID: decc88ec4fc7515594fdb42b04aa03189a44184b -commit f319912b0d0e1675b8bb051ed8213792c788bcb2 -Author: djm@openbsd.org -Date: Wed Jul 29 04:43:06 2015 +0000 +commit 43849a47c5f8687699eafbcb5604f6b9c395179f +Author: doug@openbsd.org +Date: Fri Dec 11 17:41:37 2015 +0000 upstream commit - include the peer's offer when logging a failure to - negotiate a mutual set of algorithms (kex, pubkey, ciphers, etc.) ok markus@ + Add "id" to ssh-agent pledge for subprocess support. - Upstream-ID: bbb8caabf5c01790bb845f5ce135565248d7c796 - -commit b6ea0e573042eb85d84defb19227c89eb74cf05a -Author: djm@openbsd.org -Date: Tue Jul 28 23:20:42 2015 +0000 - - upstream commit + Found the hard way by Jan Johansson when using ssh-agent with X. Also, + rearranged proc/exec and retval to match other pledge calls in the tree. - add Cisco to the list of clients that choke on the - hostkeys update extension. Pointed out by Howard Kash + ok djm@ - Upstream-ID: c9eadde28ecec056c73d09ee10ba4570dfba7e84 + Upstream-ID: 914255f6850e5e7fa830a2de6c38605333b584db -commit 3f628c7b537291c1019ce86af90756fb4e66d0fd -Author: guenther@openbsd.org -Date: Mon Jul 27 16:29:23 2015 +0000 +commit 52d7078421844b2f88329f5be3de370b0a938636 +Author: mmcc@openbsd.org +Date: Fri Dec 11 04:21:11 2015 +0000 upstream commit - Permit kbind(2) use in the sandbox now, to ease testing - of ld.so work using it + Remove NULL-checks before sshbuf_free(). - reminded by miod@, ok deraadt@ + ok djm@ - Upstream-ID: 523922e4d1ba7a091e3824e77a8a3c818ee97413 + Upstream-ID: 5ebed00ed5f9f03b119a345085e8774565466917 -commit ebe27ebe520098bbc0fe58945a87ce8490121edb -Author: millert@openbsd.org -Date: Mon Jul 20 18:44:12 2015 +0000 +commit a4b9e0f4e4a6980a0eb8072f76ea611cab5b77e7 +Author: djm@openbsd.org +Date: Fri Dec 11 03:24:25 2015 +0000 upstream commit - Move .Pp before .Bl, not after to quiet mandoc -Tlint. - Noticed by jmc@ + include remote port number in a few more messages; makes + tying log messages together into a session a bit easier; bz#2503 ok dtucker@ - Upstream-ID: 59fadbf8407cec4e6931e50c53cfa0214a848e23 + Upstream-ID: 9300dc354015f7a7368d94a8ff4a4266a69d237e -commit d5d91d0da819611167782c66ab629159169d94d4 -Author: millert@openbsd.org -Date: Mon Jul 20 18:42:35 2015 +0000 +commit 6091c362e89079397e68744ae30df121b0a72c07 +Author: djm@openbsd.org +Date: Fri Dec 11 03:20:09 2015 +0000 upstream commit - Sync usage with SYNOPSIS + don't try to load SSHv1 private key when compiled without + SSHv1 support. From Iain Morgan bz#2505 - Upstream-ID: 7a321a170181a54f6450deabaccb6ef60cf3f0b7 + Upstream-ID: 8b8e7b02a448cf5e5635979df2d83028f58868a7 -commit 79ec2142fbc68dd2ed9688608da355fc0b1ed743 -Author: millert@openbsd.org -Date: Mon Jul 20 15:39:52 2015 +0000 +commit cce6a36bb95e81fa8bfb46daf22eabcf13afc352 +Author: djm@openbsd.org +Date: Fri Dec 11 03:19:09 2015 +0000 upstream commit - Better desciption of Unix domain socket forwarding. - bz#2423; ok jmc@ + use SSH_MAX_PUBKEY_BYTES consistently as buffer size when + reading key files. Increase it to match the size of the buffers already being + used. - Upstream-ID: 85e28874726897e3f26ae50dfa2e8d2de683805d - -commit d56fd1828074a4031b18b8faa0bf949669eb18a0 -Author: Damien Miller -Date: Mon Jul 20 11:19:51 2015 +1000 - - make realpath.c compile -Wsign-compare clean + Upstream-ID: 1b60586b484b55a947d99a0b32bd25e0ced56fae -commit c63c9a691dca26bb7648827f5a13668832948929 -Author: djm@openbsd.org -Date: Mon Jul 20 00:30:01 2015 +0000 +commit 89540b6de025b80404a0cb8418c06377f3f98848 +Author: mmcc@openbsd.org +Date: Fri Dec 11 02:31:47 2015 +0000 upstream commit - mention that the default of UseDNS=no implies that - hostnames cannot be used for host matching in sshd_config and - authorized_keys; bz#2045, ok dtucker@ + Remove NULL-checks before sshkey_free(). - Upstream-ID: 0812705d5f2dfa59aab01f2764ee800b1741c4e1 + ok djm@ + + Upstream-ID: 3e35afe8a25e021216696b5d6cde7f5d2e5e3f52 -commit 63ebcd0005e9894fcd6871b7b80aeea1fec0ff76 -Author: djm@openbsd.org -Date: Sat Jul 18 08:02:17 2015 +0000 +commit 79394ed6d74572c2d2643d73937dad33727fc240 +Author: dtucker@openbsd.org +Date: Fri Dec 11 02:29:03 2015 +0000 upstream commit - don't ignore PKCS#11 hosted keys that return empty - CKA_ID; patch by Jakub Jelen via bz#2429; ok markus + fflush stdout so that output is seen even when running in + debug mode when output may otherwise not be flushed. Patch from dustin at + null-ptr.net. - Upstream-ID: 2f7c94744eb0342f8ee8bf97b2351d4e00116485 + Upstream-ID: b0c6b4cd2cdb01d7e9eefbffdc522e35b5bc4acc -commit b15fd989c8c62074397160147a8d5bc34b3f3c63 -Author: djm@openbsd.org -Date: Sat Jul 18 08:00:21 2015 +0000 +commit ee607cccb6636eb543282ba90e0677b0604d8b7a +Author: Darren Tucker +Date: Tue Dec 15 15:23:49 2015 +1100 - upstream commit + Increase robustness of redhat/openssh.spec - skip uninitialised PKCS#11 slots; patch from Jakub Jelen - in bz#2427 ok markus@ + - remove configure --with-rsh, because this option isn't supported anymore + - replace last occurrence of BuildPreReq by BuildRequires + - update grep statement to query the krb5 include directory - Upstream-ID: 744c1e7796e237ad32992d0d02148e8a18f27d29 + Patch from CarstenGrohmann via github, ok djm. -commit 5b64f85bb811246c59ebab70aed331f26ba37b18 -Author: djm@openbsd.org -Date: Sat Jul 18 07:57:14 2015 +0000 +commit b5fa0cd73555b991a543145603658d7088ec6b60 +Author: Darren Tucker +Date: Tue Dec 15 15:10:32 2015 +1100 - upstream commit - - only query each keyboard-interactive device once per - authentication request regardless of how many times it is listed; ok markus@ + Allow --without-ssl-engine with --without-openssl - Upstream-ID: d73fafba6e86030436ff673656ec1f33d9ffeda1 + Patch from Mike Frysinger via github. -commit cd7324d0667794eb5c236d8a4e0f236251babc2d -Author: djm@openbsd.org -Date: Fri Jul 17 03:34:27 2015 +0000 +commit c1d7e546f6029024f3257cc25c92f2bddf163125 +Author: Darren Tucker +Date: Tue Dec 15 14:27:09 2015 +1100 - upstream commit + Include openssl crypto.h for SSLeay. - remove -u flag to diff (only used for error output) to make - things easier for -portable + Patch from doughdemon via github. + +commit c6f5f01651526e88c00d988ce59d71f481ebac62 +Author: Darren Tucker +Date: Tue Dec 15 13:59:12 2015 +1100 + + Add sys/time.h for gettimeofday. - Upstream-Regress-ID: a5d6777d2909540d87afec3039d9bb2414ade548 + Should allow it it compile with MUSL libc. Based on patch from + doughdemon via github. -commit deb8d99ecba70b67f4af7880b11ca8768df9ec3a +commit 39736be06c7498ef57d6970f2d85cf066ae57c82 Author: djm@openbsd.org -Date: Fri Jul 17 03:09:19 2015 +0000 +Date: Fri Dec 11 02:20:28 2015 +0000 upstream commit - direct-streamlocal@openssh.com Unix domain foward - messages do not contain a "reserved for future use" field and in fact, - serverloop.c checks that there isn't one. Remove erroneous mention from - PROTOCOL description. bz#2421 from Daniel Black + correct error messages; from Tomas Kuthan bz#2507 - Upstream-ID: 3d51a19e64f72f764682f1b08f35a8aa810a43ac + Upstream-ID: 7454a0affeab772398052954c79300aa82077093 -commit 356b61f365405b5257f5b2ab446e5d7bd33a7b52 -Author: djm@openbsd.org -Date: Fri Jul 17 03:04:27 2015 +0000 +commit 94141b7ade24afceeb6762a3f99e09e47a6c42b6 +Author: mmcc@openbsd.org +Date: Fri Dec 11 00:20:04 2015 +0000 upstream commit - describe magic for setting up Unix domain socket fowards - via the mux channel; bz#2422 patch from Daniel Black + Pass (char *)NULL rather than (char *)0 to execl and + execlp. - Upstream-ID: 943080fe3864715c423bdeb7c920bb30c4eee861 + ok dtucker@ + + Upstream-ID: 56c955106cbddba86c3dd9bbf786ac0d1b361492 -commit d3e2aee41487d55b8d7d40f538b84ff1db7989bc -Author: Darren Tucker -Date: Fri Jul 17 12:52:34 2015 +1000 +commit d59ce08811bf94111c2f442184cf7d1257ffae24 +Author: mmcc@openbsd.org +Date: Thu Dec 10 17:08:40 2015 +0000 - Check if realpath works on nonexistent files. + upstream commit - On some platforms the native realpath doesn't work with non-existent - files (this is actually specified in some versions of POSIX), however - the sftp spec says its realpath with "canonicalize any given path name". - On those platforms, use realpath from the compat library. + Remove NULL-checks before free(). - In addition, when compiling with -DFORTIFY_SOURCE, glibc redefines - the realpath symbol to the checked version, so redefine ours to - something else so we pick up the compat version we want. + ok dtucker@ - bz#2428, ok djm@ + Upstream-ID: e3d3cb1ce900179906af36517b5eea0fb15e6ef8 -commit 25b14610dab655646a109db5ef8cb4c4bf2a48a0 -Author: djm@openbsd.org -Date: Fri Jul 17 02:47:45 2015 +0000 +commit 8e56dd46cb37879c73bce2d6032cf5e7f82d5a71 +Author: mmcc@openbsd.org +Date: Thu Dec 10 07:01:35 2015 +0000 upstream commit - fix incorrect test for SSH1 keys when compiled without SSH1 - support + Fix a couple "the the" typos. ok dtucker@ - Upstream-ID: 6004d720345b8e481c405e8ad05ce2271726e451 + Upstream-ID: ec364c5af32031f013001fd28d1bd3dfacfe9a72 -commit df56a8035d429b2184ee94aaa7e580c1ff67f73a -Author: djm@openbsd.org -Date: Wed Jul 15 08:00:11 2015 +0000 +commit 6262a0522ddc2c0f2e9358dcb68d59b46e9c533e +Author: markus@openbsd.org +Date: Mon Dec 7 20:04:09 2015 +0000 upstream commit - fix NULL-deref when SSH1 reenabled + stricter encoding type checks for ssh-rsa; ok djm@ - Upstream-ID: f22fd805288c92b3e9646782d15b48894b2d5295 + Upstream-ID: 8cca7c787599a5e8391e184d0b4f36fdc3665650 -commit 41e38c4d49dd60908484e6703316651333f16b93 +commit d86a3ba7af160c13496102aed861ae48a4297072 +Author: Damien Miller +Date: Wed Dec 9 09:18:45 2015 +1100 + + Don't set IPV6_V6ONLY on OpenBSD + + It isn't necessary and runs afoul of pledge(2) restrictions. + +commit da98c11d03d819a15429d8fff9688acd7505439f Author: djm@openbsd.org -Date: Wed Jul 15 07:19:50 2015 +0000 +Date: Mon Dec 7 02:20:46 2015 +0000 upstream commit - regen RSA1 test keys; the last batch was missing their - private parts + basic unit tests for rsa-sha2-* signature types - Upstream-Regress-ID: 7ccf437305dd63ff0b48dd50c5fd0f4d4230c10a + Upstream-Regress-ID: 7dc4b9db809d578ff104d591b4d86560c3598d3c -commit 5bf0933184cb622ca3f96d224bf3299fd2285acc +commit 3da893fdec9936dd2c23739cdb3c0c9d4c59fca0 Author: markus@openbsd.org -Date: Fri Jul 10 06:23:25 2015 +0000 +Date: Sat Dec 5 20:53:21 2015 +0000 upstream commit - Adapt tests, now that DSA if off by default; use - PubkeyAcceptedKeyTypes and PubkeyAcceptedKeyTypes to test DSA. + prefer rsa-sha2-512 over -256 for hostkeys, too; noticed + by naddy@ - Upstream-Regress-ID: 0ff2a3ff5ac1ce5f92321d27aa07b98656efcc5c + Upstream-ID: 685f55f7ec566a8caca587750672723a0faf3ffe -commit 7a6e3fd7b41dbd3756b6bf9acd67954c0b1564cc -Author: markus@openbsd.org -Date: Tue Jul 7 14:54:16 2015 +0000 +commit 8b56e59714d87181505e4678f0d6d39955caf10e +Author: tobias@openbsd.org +Date: Fri Dec 4 21:51:06 2015 +0000 upstream commit - regen test data after mktestdata.sh changes + Properly handle invalid %-format by calling fatal. - Upstream-Regress-ID: 3495ecb082b9a7c048a2d7c5c845d3bf181d25a4 + ok deraadt, djm + + Upstream-ID: 5692bce7d9f6eaa9c488cb93d3b55e758bef1eac -commit 7c8c174c69f681d4910fa41c37646763692b28e2 +commit 76c9fbbe35aabc1db977fb78e827644345e9442e Author: markus@openbsd.org -Date: Tue Jul 7 14:53:30 2015 +0000 +Date: Fri Dec 4 16:41:28 2015 +0000 upstream commit - adapt tests to new minimum RSA size and default FP format + implement SHA2-{256,512} for RSASSA-PKCS1-v1_5 signatures + (user and host auth) based on draft-rsa-dsa-sha2-256-03.txt and + draft-ssh-ext-info-04.txt; with & ok djm@ - Upstream-Regress-ID: a4b30afd174ce82b96df14eb49fb0b81398ffd0e + Upstream-ID: cf82ce532b2733e5c4b34bb7b7c94835632db309 -commit 6a977a4b68747ade189e43d302f33403fd4a47ac +commit 6064a8b8295cb5a17b5ebcfade53053377714f40 Author: djm@openbsd.org -Date: Fri Jul 3 04:39:23 2015 +0000 +Date: Fri Dec 4 00:24:55 2015 +0000 upstream commit - legacy v00 certificates are gone; adapt and don't try to - test them; "sure" markus@ dtucker@ + clean up agent_fd handling; properly initialise it to -1 + and make tests consistent - Upstream-Regress-ID: c57321e69b3cd4a3b3396dfcc43f0803d047da12 + ok markus@ + + Upstream-ID: ac9554323d5065745caf17b5e37cb0f0d4825707 -commit 0c4123ad5e93fb90fee9c6635b13a6cdabaac385 -Author: djm@openbsd.org -Date: Wed Jul 1 23:11:18 2015 +0000 +commit b91926a97620f3e51761c271ba57aa5db790f48d +Author: semarie@openbsd.org +Date: Thu Dec 3 17:00:18 2015 +0000 upstream commit - don't expect SSH v.1 in unittests + pledges ssh client: - mux client: which is used when + ControlMaster is in use. will end with "stdio proc tty" (proc is to + permit sending SIGWINCH to mux master on window resize) - Upstream-Regress-ID: f8812b16668ba78e6a698646b2a652b90b653397 - -commit 3c099845798a817cdde513c39074ec2063781f18 -Author: djm@openbsd.org -Date: Mon Jun 15 06:38:50 2015 +0000 - - upstream commit + - client loop: several levels of pledging depending of your used options - turn SSH1 back on to match src/usr.bin/ssh being tested + ok deraadt@ - Upstream-Regress-ID: 6c4f763a2f0cc6893bf33983919e9030ae638333 + Upstream-ID: 21676155a700e51f2ce911e33538e92a2cd1d94b -commit b1dc2b33689668c75e95f873a42d5aea1f4af1db -Author: dtucker@openbsd.org -Date: Mon Jul 13 04:57:14 2015 +0000 +commit bcce47466bbc974636f588b5e4a9a18ae386f64a +Author: doug@openbsd.org +Date: Wed Dec 2 08:30:50 2015 +0000 upstream commit - Add "PuTTY_Local:" to the clients to which we do not - offer DH-GEX. This was the string that was used for development versions - prior to September 2014 and they don't do RFC4419 DH-GEX, but unfortunately - there are some extant products based on those versions. bx2424 from Jay - Rouman, ok markus@ djm@ + Add "cpath" to the ssh-agent pledge so the cleanup + handler can unlink(). - Upstream-ID: be34d41e18b966832fe09ca243d275b81882e1d5 + ok djm@ + + Upstream-ID: 9e632991d48241d56db645602d381253a3d8c29d -commit 3a1638dda19bbc73d0ae02b4c251ce08e564b4b9 -Author: markus@openbsd.org -Date: Fri Jul 10 06:21:53 2015 +0000 +commit a90d001543f46716b6590c6dcc681d5f5322f8cf +Author: djm@openbsd.org +Date: Wed Dec 2 08:00:58 2015 +0000 upstream commit - Turn off DSA by default; add HostKeyAlgorithms to the - server and PubkeyAcceptedKeyTypes to the client side, so it still can be - tested or turned back on; feedback and ok djm@ + ssh-agent pledge needs proc for askpass; spotted by todd@ - Upstream-ID: 8450a9e6d83f80c9bfed864ff061dfc9323cec21 + Upstream-ID: 349aa261b29cc0e7de47ef56167769c432630b2a -commit 16db0a7ee9a87945cc594d13863cfcb86038db59 -Author: markus@openbsd.org -Date: Thu Jul 9 09:49:46 2015 +0000 +commit d952162b3c158a8f23220587bb6c8fcda75da551 +Author: djm@openbsd.org +Date: Tue Dec 1 23:29:24 2015 +0000 upstream commit - re-enable ed25519-certs if compiled w/o openssl; ok djm + basic pledge() for ssh-agent, more refinement needed - Upstream-ID: e10c90808b001fd2c7a93778418e9b318f5c4c49 + Upstream-ID: 5b5b03c88162fce549e45e1b6dd833f20bbb5e13 -commit c355bf306ac33de6545ce9dac22b84a194601e2f -Author: markus@openbsd.org -Date: Wed Jul 8 20:24:02 2015 +0000 +commit f0191d7c8e76e30551084b79341886d9bb38e453 +Author: Damien Miller +Date: Mon Nov 30 10:53:25 2015 +1100 - upstream commit + Revert "stub for pledge(2) for systems that lack it" - no need to include the old buffer/key API + This reverts commit 14c887c8393adde2d9fd437d498be30f8c98535c. - Upstream-ID: fb13c9f7c0bba2545f3eb0a0e69cb0030819f52b + dtucker beat me to it :/ -commit a3cc48cdf9853f1e832d78cb29bedfab7adce1ee -Author: markus@openbsd.org -Date: Wed Jul 8 19:09:25 2015 +0000 +commit 6283cc72eb0e49a3470d30e07ca99a1ba9e89676 +Author: Damien Miller +Date: Mon Nov 30 10:37:03 2015 +1100 - upstream commit - - typedefs for Cipher&CipherContext are unused + revert 7d4c7513: bring back S/Key prototypes - Upstream-ID: 50e6a18ee92221d23ad173a96d5b6c42207cf9a7 + (but leave RCSID changes) -commit a635bd06b5c427a57c3ae760d3a2730bb2c863c0 -Author: markus@openbsd.org -Date: Wed Jul 8 19:04:21 2015 +0000 +commit 14c887c8393adde2d9fd437d498be30f8c98535c +Author: Damien Miller +Date: Mon Nov 30 09:45:29 2015 +1100 - upstream commit - - xmalloc.h is unused - - Upstream-ID: afb532355b7fa7135a60d944ca1e644d1d63cb58 + stub for pledge(2) for systems that lack it -commit 2521cf0e36c7f3f6b19f206da0af134f535e4a31 -Author: markus@openbsd.org -Date: Wed Jul 8 19:01:15 2015 +0000 +commit 452c0b6af5d14c37553e30059bf74456012493f3 +Author: djm@openbsd.org +Date: Sun Nov 29 22:18:37 2015 +0000 upstream commit - compress.c is gone + pledge, better fatal() messages; feedback deraadt@ - Upstream-ID: 174fa7faa9b9643cba06164b5e498591356fbced + Upstream-ID: 3e00f6ccfe2b9a7a2d1dbba5409586180801488f -commit c65a7aa6c43aa7a308ee1ab8a96f216169ae9615 -Author: djm@openbsd.org -Date: Fri Jul 3 04:05:54 2015 +0000 +commit 6da413c085dba37127687b2617a415602505729b +Author: deraadt@openbsd.org +Date: Sat Nov 28 06:50:52 2015 +0000 upstream commit - another SSH_RSA_MINIMUM_MODULUS_SIZE that needed - cranking + do not leak temp file if there is no known_hosts file + from craig leres, ok djm - Upstream-ID: 9d8826cafe96aab4ae8e2f6fd22800874b7ffef1 + Upstream-ID: c820497fd5574844c782e79405c55860f170e426 -commit b1f383da5cd3cb921fc7776f17a14f44b8a31757 -Author: djm@openbsd.org -Date: Fri Jul 3 03:56:25 2015 +0000 +commit 3ddd15e1b63a4d4f06c8ab16fbdd8a5a61764f16 +Author: Darren Tucker +Date: Mon Nov 30 07:23:53 2015 +1100 - upstream commit - - add an XXX reminder for getting correct key paths from - sshd_config + Add a null implementation of pledge. - Upstream-ID: feae52b209d7782ad742df04a4260e9fe41741db + Fixes builds on almost everything. -commit 933935ce8d093996c34d7efa4d59113163080680 +commit b1d6b3971ef256a08692efc409fc9ada719111cc Author: djm@openbsd.org -Date: Fri Jul 3 03:49:45 2015 +0000 +Date: Sat Nov 28 06:41:03 2015 +0000 upstream commit - refuse to generate or accept RSA keys smaller than 1024 - bits; feedback and ok dtucker@ + don't include port number in tcpip-forward replies for + requests that don't allocate a port; bz#2509 diagnosed by Ron Frederick ok + markus - Upstream-ID: 7ea3d31271366ba264f06e34a3539bf1ac30f0ba + Upstream-ID: 77efad818addb61ec638b5a2362f1554e21a970a -commit bdfd29f60b74f3e678297269dc6247a5699583c1 -Author: djm@openbsd.org -Date: Fri Jul 3 03:47:00 2015 +0000 +commit 9080bd0b9cf10d0f13b1f642f20cb84285cb8d65 +Author: deraadt@openbsd.org +Date: Fri Nov 27 00:49:31 2015 +0000 upstream commit - turn off 1024 bit diffie-hellman-group1-sha1 key - exchange method (already off in server, this turns it off in the client by - default too) ok dtucker@ + pledge "stdio rpath wpath cpath fattr tty proc exec" + except for the -p option (which sadly has insane semantics...) ok semarie + dtucker - Upstream-ID: f59b88f449210ab7acf7d9d88f20f1daee97a4fa + Upstream-ID: 8854bbd58279abe00f6c33f8094bdc02c8c65059 -commit c28fc62d789d860c75e23a9fa9fb250eb2beca57 -Author: djm@openbsd.org -Date: Fri Jul 3 03:43:18 2015 +0000 +commit 4d90625b229cf6b3551d81550a9861897509a65f +Author: halex@openbsd.org +Date: Fri Nov 20 23:04:01 2015 +0000 upstream commit - delete support for legacy v00 certificates; "sure" - markus@ dtucker@ + allow comment change for all supported formats - Upstream-ID: b5b9bb5f9202d09e88f912989d74928601b6636f + ok djm@ + + Upstream-ID: 5fc477cf2f119b2d44aa9c683af16cb00bb3744b -commit 564d63e1b4a9637a209d42a9d49646781fc9caef +commit 8ca915fc761519dd1f7766a550ec597a81db5646 Author: djm@openbsd.org -Date: Wed Jul 1 23:10:47 2015 +0000 +Date: Fri Nov 20 01:45:29 2015 +0000 upstream commit - Compile-time disable SSH v.1 again + add cast to make -Werror clean - Upstream-ID: 1d4b513a3a06232f02650b73bad25100d1b800af + Upstream-ID: 288db4f8f810bd475be01320c198250a04ff064d -commit 868109b650504dd9bcccdb1f51d0906f967c20ff -Author: djm@openbsd.org -Date: Wed Jul 1 02:39:06 2015 +0000 +commit ac9473580dcd401f8281305af98635cdaae9bf96 +Author: Damien Miller +Date: Fri Nov 20 12:35:41 2015 +1100 - upstream commit - - twiddle PermitRootLogin back + fix multiple authentication using S/Key w/ privsep - Upstream-ID: 2bd23976305d0512e9f84d054e1fc23cd70b89f2 + bz#2502, patch from Kevin Korb and feandil_ -commit 7de4b03a6e4071d454b72927ffaf52949fa34545 +commit 88b6fcdeb87a2fb76767854d9eb15006662dca57 Author: djm@openbsd.org -Date: Wed Jul 1 02:32:17 2015 +0000 +Date: Thu Nov 19 08:23:27 2015 +0000 upstream commit - twiddle; (this commit marks the openssh-6.9 release) + ban ConnectionAttempts=0, it makes no sense and would cause + ssh_connect_direct() to print an uninitialised stack variable; bz#2500 + reported by dvw AT phas.ubc.ca - Upstream-ID: 78500582819f61dd8adee36ec5cc9b9ac9351234 + Upstream-ID: 32b5134c608270583a90b93a07b3feb3cbd5f7d5 -commit 1bf477d3cdf1a864646d59820878783d42357a1d +commit 964ab3ee7a8f96bdbc963d5b5a91933d6045ebe7 Author: djm@openbsd.org -Date: Wed Jul 1 02:26:31 2015 +0000 +Date: Thu Nov 19 01:12:32 2015 +0000 upstream commit - better refuse ForwardX11Trusted=no connections attempted - after ForwardX11Timeout expires; reported by Jann Horn + trailing whitespace - Upstream-ID: bf0fddadc1b46a0334e26c080038313b4b6dea21 + Upstream-ID: 31fe0ad7c4d08e87f1d69c79372f5e3c5cd79051 -commit 47aa7a0f8551b471fcae0447c1d78464f6dba869 +commit f96516d052dbe38561f6b92b0e4365d8e24bb686 Author: djm@openbsd.org -Date: Wed Jul 1 01:56:13 2015 +0000 +Date: Thu Nov 19 01:09:38 2015 +0000 upstream commit - put back default PermitRootLogin=no + print host certificate contents at debug level - Upstream-ID: 7bdedd5cead99c57ed5571f3b6b7840922d5f728 + Upstream-ID: 39354cdd8a2b32b308fd03f98645f877f540f00d -commit 984b064fe2a23733733262f88d2e1b2a1a501662 +commit 499cf36fecd6040e30e2912dd25655bc574739a7 Author: djm@openbsd.org -Date: Wed Jul 1 01:55:13 2015 +0000 +Date: Thu Nov 19 01:08:55 2015 +0000 upstream commit - openssh-6.9 + move the certificate validity formatting code to + sshkey.[ch] - Upstream-ID: 6cfe8e1904812531080e6ab6e752d7001b5b2d45 + Upstream-ID: f05f7c78fab20d02ff1d5ceeda533ef52e8fe523 -commit d921082ed670f516652eeba50705e1e9f6325346 +commit bcb7bc77bbb1535d1008c7714085556f3065d99d Author: djm@openbsd.org -Date: Wed Jul 1 01:55:00 2015 +0000 +Date: Wed Nov 18 08:37:28 2015 +0000 upstream commit - reset default PermitRootLogin to 'yes' (momentarily, for - release) + fix "ssh-keygen -l" of private key, broken in support for + multiple plain keys on stdin - Upstream-ID: cad8513527066e65dd7a1c16363d6903e8cefa24 - -commit 66295e0e1ba860e527f191b6325d2d77dec4dbce -Author: Damien Miller -Date: Wed Jul 1 11:49:12 2015 +1000 - - crank version numbers for release + Upstream-ID: 6b3132d2c62d03d0bad6f2bcd7e2d8b7dab5cd9d -commit 37035c07d4f26bb1fbe000d2acf78efdb008681d -Author: Damien Miller -Date: Wed Jul 1 10:49:37 2015 +1000 +commit 259adb6179e23195c8f6913635ea71040d1ccd63 +Author: millert@openbsd.org +Date: Mon Nov 16 23:47:52 2015 +0000 - s/--with-ssh1/--without-ssh1/ + upstream commit + + Replace remaining calls to index(3) with strchr(3). OK + jca@ krw@ + + Upstream-ID: 33837d767a0cf1db1489b96055f9e330bc0bab6d -commit 629df770dbadc2accfbe1c81b3f31f876d0acd84 +commit c56a255162c2166884539c0a1f7511575325b477 Author: djm@openbsd.org -Date: Tue Jun 30 05:25:07 2015 +0000 +Date: Mon Nov 16 22:53:07 2015 +0000 upstream commit - fatal() when a remote window update causes the window - value to overflow. Reported by Georg Wicherski, ok markus@ + Allow fingerprinting from standard input "ssh-keygen -lf + -" - Upstream-ID: ead397a9aceb3bf74ebfa5fcaf259d72e569f351 + Support fingerprinting multiple plain keys in a file and authorized_keys + files too (bz#1319) + + ok markus@ + + Upstream-ID: 903f8b4502929d6ccf53509e4e07eae084574b77 -commit f715afebe735d61df3fd30ad72d9ac1c8bd3b5f2 +commit 5b4010d9b923cf1b46c9c7b1887c013c2967e204 Author: djm@openbsd.org -Date: Tue Jun 30 05:23:25 2015 +0000 +Date: Mon Nov 16 22:51:05 2015 +0000 upstream commit - Fix math error in remote window calculations that causes - eventual stalls for datagram channels. Reported by Georg Wicherski, ok - markus@ + always call privsep_preauth_child() regardless of whether + sshd was started by root; it does important priming before sandboxing and + failing to call it could result in sandbox violations later; ok markus@ - Upstream-ID: be54059d11bf64e0d85061f7257f53067842e2ab + Upstream-ID: c8a6d0d56c42f3faab38460dc917ca0d1705d383 -commit 52fb6b9b034fcfd24bf88cc7be313e9c31de9889 -Author: Damien Miller -Date: Tue Jun 30 16:05:40 2015 +1000 +commit 3a9f84b58b0534bbb485f1eeab75665e2d03371f +Author: djm@openbsd.org +Date: Mon Nov 16 22:50:01 2015 +0000 - skip IPv6-related portions on hosts without IPv6 + upstream commit - with Tim Rice + improve sshkey_read() semantics; only update *cpp when a + key is successfully read; ok markus@ + + Upstream-ID: f371e78e8f4fab366cf69a42bdecedaed5d1b089 -commit 512caddf590857af6aa12218461b5c0441028cf5 -Author: djm@openbsd.org -Date: Mon Jun 29 22:35:12 2015 +0000 +commit db6f8dc5dd5655b59368efd074994d4568bc3556 +Author: logan@openbsd.org +Date: Mon Nov 16 06:13:04 2015 +0000 upstream commit - add getpid to sandbox, reachable by grace_alarm_handler + 1) Use xcalloc() instead of xmalloc() to check for + potential overflow. (Feedback from both mmcc@ and djm@) 2) move set_size + just before the for loop. (suggested by djm@) - reported by Jakub Jelen; bz#2419 + OK djm@ - Upstream-ID: d0da1117c16d4c223954995d35b0f47c8f684cd8 + Upstream-ID: 013534c308187284756c3141f11d2c0f33c47213 -commit 78c2a4f883ea9aba866358e2acd9793a7f42ca93 +commit 383f10fb84a0fee3c01f9d97594f3e22aa3cd5e0 Author: djm@openbsd.org -Date: Fri Jun 26 05:13:20 2015 +0000 +Date: Mon Nov 16 00:30:02 2015 +0000 upstream commit - Fix \-escaping bug that caused forward path parsing to skip - two characters and skip past the end of the string. + Add a new authorized_keys option "restrict" that + includes all current and future key restrictions (no-*-forwarding, etc). Also + add permissive versions of the existing restrictions, e.g. "no-pty" -> "pty". + This simplifies the task of setting up restricted keys and ensures they are + maximally-restricted, regardless of any permissions we might implement in the + future. - Based on patch by Salvador Fandino; ok dtucker@ + Example: - Upstream-ID: 7b879dc446335677cbe4cb549495636a0535f3bd + restrict,pty,command="nethack" ssh-ed25519 AAAAC3NzaC1lZDI1... + + Idea from Jann Horn; ok markus@ + + Upstream-ID: 04ceb9d448e46e67e13887a7ae5ea45b4f1719d0 -commit bc20205c91c9920361d12b15d253d4997dba494a -Author: Damien Miller -Date: Thu Jun 25 09:51:39 2015 +1000 +commit e41a071f7bda6af1fb3f081bed0151235fa61f15 +Author: jmc@openbsd.org +Date: Sun Nov 15 23:58:04 2015 +0000 - add missing pselect6 + upstream commit - patch from Jakub Jelen + correct section number for ssh-agent; + + Upstream-ID: 44be72fd8bcc167635c49b357b1beea8d5674bd6 -commit 9d27fb73b4a4e5e99cb880af790d5b1ce44f720a -Author: djm@openbsd.org -Date: Wed Jun 24 23:47:23 2015 +0000 +commit 1a11670286acddcc19f5eff0966c380831fc4638 +Author: jmc@openbsd.org +Date: Sun Nov 15 23:54:15 2015 +0000 upstream commit - correct test to sshkey_sign(); spotted by Albert S. + do not confuse mandoc by presenting "Dd"; - Upstream-ID: 5f7347f40f0ca6abdaca2edb3bd62f4776518933 + Upstream-ID: 1470fce171c47b60bbc7ecd0fc717a442c2cfe65 -commit 7ed01a96a1911d8b4a9ef4f3d064e1923bfad7e3 -Author: dtucker@openbsd.org -Date: Wed Jun 24 01:49:19 2015 +0000 +commit f361df474c49a097bfcf16d1b7b5c36fcd844b4b +Author: jcs@openbsd.org +Date: Sun Nov 15 22:26:49 2015 +0000 upstream commit - Revert previous commit. We still want to call setgroups - in the case where there are zero groups to remove any that we might otherwise - inherit (as pointed out by grawity at gmail.com) and since the 2nd argument - to setgroups is always a static global it's always valid to dereference in - this case. ok deraadt@ djm@ + Add an AddKeysToAgent client option which can be set to + 'yes', 'no', 'ask', or 'confirm', and defaults to 'no'. When enabled, a + private key that is used during authentication will be added to ssh-agent if + it is running (with confirmation enabled if set to 'confirm'). - Upstream-ID: 895b5ac560a10befc6b82afa778641315725fd01 + Initial version from Joachim Schipper many years ago. + + ok markus@ + + Upstream-ID: a680db2248e8064ec55f8be72d539458c987d5f4 -commit 882f8bf94f79528caa65b0ba71c185d705bb7195 -Author: dtucker@openbsd.org -Date: Wed Jun 24 01:49:19 2015 +0000 +commit d87063d9baf5479b6e813d47dfb694a97df6f6f5 +Author: djm@openbsd.org +Date: Fri Nov 13 04:39:35 2015 +0000 upstream commit - Revert previous commit. We still want to call setgroups in - the case where there are zero groups to remove any that we might otherwise - inherit (as pointed out by grawity at gmail.com) and since the 2nd argument - to setgroups is always a static global it's always valid to dereference in - this case. ok deraadt@ djm@ + send SSH2_MSG_UNIMPLEMENTED replies to unexpected + messages during KEX; bz#2949, ok dtucker@ - Upstream-ID: 895b5ac560a10befc6b82afa778641315725fd01 + Upstream-ID: 2b3abdff344d53c8d505f45c83a7b12e84935786 -commit 9488538a726951e82b3a4374f3c558d72c80a89b +commit 9fd04681a1e9b0af21e08ff82eb674cf0a499bfc Author: djm@openbsd.org -Date: Mon Jun 22 23:42:16 2015 +0000 +Date: Fri Nov 13 04:38:06 2015 +0000 upstream commit - Don't count successful partial authentication as failures - in monitor; this may have caused the monitor to refuse multiple - authentications that would otherwise have successfully completed; ok markus@ + Support "none" as an argument for sshd_config + ForceCommand and ChrootDirectory. Useful inside Match blocks to override a + global default. bz#2486 ok dtucker@ - Upstream-ID: eb74b8e506714d0f649bd5c300f762a527af04a3 + Upstream-ID: 7ef478d6592bc7db5c7376fc33b4443e63dccfa5 -commit 63b78d003bd8ca111a736e6cea6333da50f5f09b -Author: dtucker@openbsd.org -Date: Mon Jun 22 12:29:57 2015 +0000 +commit 94bc0b72c29e511cbbc5772190d43282e5acfdfe +Author: djm@openbsd.org +Date: Fri Nov 13 04:34:15 2015 +0000 upstream commit - Don't call setgroups if we have zero groups; there's no - guarantee that it won't try to deref the pointer. Based on a patch from mail - at quitesimple.org, ok djm deraadt + support multiple certificates (one per line) and + reading from standard input (using "-f -") for "ssh-keygen -L"; ok dtucker@ - Upstream-ID: 2fff85e11d7a9a387ef7fddf41fbfaf566708ab1 - -commit 5c15e22c691c79a47747bcf5490126656f97cecd -Author: Damien Miller -Date: Thu Jun 18 15:07:56 2015 +1000 - - fix syntax error + Upstream-ID: ecbadeeef3926e5be6281689b7250a32a80e88db -commit 596dbca82f3f567fb3d2d69af4b4e1d3ba1e6403 -Author: jsing@openbsd.org -Date: Mon Jun 15 18:44:22 2015 +0000 +commit b6b9108f5b561c83612cb97ece4134eb59fde071 +Author: djm@openbsd.org +Date: Fri Nov 13 02:57:46 2015 +0000 upstream commit - If AuthorizedPrincipalsCommand is specified, however - AuthorizedPrincipalsFile is not (or is set to "none"), authentication will - potentially fail due to key_cert_check_authority() failing to locate a - principal that matches the username, even though an authorized principal has - already been matched in the output of the subprocess. Fix this by using the - same logic to determine if pw->pw_name should be passed, as is used to - determine if a authorized principal must be matched earlier on. - - ok djm@ + list a couple more options usable in Match blocks; + bz#2489 - Upstream-ID: 43b42302ec846b0ea68aceb40677245391b9409d + Upstream-ID: e4d03f39d254db4c0cc54101921bb89fbda19879 -commit aff3e94c0d75d0d0fa84ea392b50ab04f8c57905 -Author: jsing@openbsd.org -Date: Mon Jun 15 18:42:19 2015 +0000 +commit a7994b3f5a5a5a33b52b0a6065d08e888f0a99fb +Author: djm@openbsd.org +Date: Wed Nov 11 04:56:39 2015 +0000 upstream commit - Make the arguments to match_principals_command() similar - to match_principals_file(), by changing the last argument a struct - sshkey_cert * and dereferencing key->cert in the caller. + improve PEEK/POKE macros: better casts, don't multiply + evaluate arguments; ok deraadt@ - No functional change. + Upstream-ID: 9a1889e19647615ededbbabab89064843ba92d3e + +commit 7d4c7513a7f209cb303a608ac6e46b3f1dfc11ec +Author: djm@openbsd.org +Date: Wed Nov 11 01:48:01 2015 +0000 + + upstream commit - ok djm@ + remove prototypes for long-gone s/key support; ok + dtucker@ - Upstream-ID: 533f99b844b21b47342b32b62e198dfffcf8651c + Upstream-ID: db5bed3c57118af986490ab23d399df807359a79 -commit 97e2e1596c202a4693468378b16b2353fd2d6c5e +commit 07889c75926c040b8e095949c724e66af26441cb Author: Damien Miller -Date: Wed Jun 17 14:36:54 2015 +1000 +Date: Sat Nov 14 18:44:49 2015 +1100 - trivial optimisation for seccomp-bpf + read back from libcrypto RAND when privdropping - When doing arg inspection and the syscall doesn't match, skip - past the instruction that reloads the syscall into the accumulator, - since the accumulator hasn't been modified at this point. + makes certain libcrypto implementations cache a /dev/urandom fd + in preparation of sandboxing. Based on patch by Greg Hartman. -commit 99f33d7304893bd9fa04d227cb6e870171cded19 -Author: Damien Miller -Date: Wed Jun 17 10:50:51 2015 +1000 +commit 1560596f44c01bb0cef977816410950ed17b8ecd +Author: Darren Tucker +Date: Tue Nov 10 11:14:47 2015 +1100 - aarch64 support for seccomp-bpf sandbox + Fix compiler warnings in the openssl header check. - Also resort and tidy syscall list. Based on patches by Jakub Jelen - bz#2361; ok dtucker@ + Noted by Austin English. -commit 4ef702e1244633c1025ec7cfe044b9ab267097bf -Author: djm@openbsd.org -Date: Mon Jun 15 01:32:50 2015 +0000 +commit e72a8575ffe1d8adff42c9abe9ca36938acc036b +Author: jmc@openbsd.org +Date: Sun Nov 8 23:24:03 2015 +0000 upstream commit - return failure on RSA signature error; reported by Albert S + -c before -H, in SYNOPSIS and usage(); - Upstream-ID: e61bb93dbe0349625807b0810bc213a6822121fa + Upstream-ID: 25e8c58a69e1f37fcd54ac2cd1699370acb5e404 -commit a170f22baf18af0b1acf2788b8b715605f41a1f9 -Author: Tim Rice -Date: Tue Jun 9 22:41:13 2015 -0700 +commit 3a424cdd21db08c7b0ded902f97b8f02af5aa485 +Author: djm@openbsd.org +Date: Sun Nov 8 22:30:20 2015 +0000 - Fix t12 rules for out of tree builds. + upstream commit + + Add "ssh-keyscan -c ..." flag to allow fetching + certificates instead of plain keys; ok markus@ + + Upstream-ID: 0947e2177dba92339eced9e49d3c5bf7dda69f82 -commit ec04dc4a5515c913121bc04ed261857e68fa5c18 -Author: millert@openbsd.org -Date: Fri Jun 5 15:13:13 2015 +0000 +commit 69fead5d7cdaa73bdece9fcba80f8e8e70b90346 +Author: jmc@openbsd.org +Date: Sun Nov 8 22:08:38 2015 +0000 upstream commit - For "ssh -L 12345:/tmp/sock" don't fail with "No forward host - name." (we have a path, not a host name). Based on a diff from Jared - Yanovich. OK djm@ + remove slogin links; ok deraadt markus djm - Upstream-ID: 2846b0a8c7de037e33657f95afbd282837fc213f + Upstream-ID: 39ba08548acde4c54f2d4520c202c2a863a3c730 -commit 732d61f417a6aea0aa5308b59cb0f563bcd6edd6 +commit 2fecfd486bdba9f51b3a789277bb0733ca36e1c0 Author: djm@openbsd.org -Date: Fri Jun 5 03:44:14 2015 +0000 +Date: Sun Nov 8 21:59:11 2015 +0000 upstream commit - typo: accidental repetition; bz#2386 + fix OOB read in packet code caused by missing return + statement found by Ben Hawkes; ok markus@ deraadt@ - Upstream-ID: 45e620d99f6bc301e5949d34a54027374991c88b + Upstream-ID: a3e3a85434ebfa0690d4879091959591f30efc62 -commit adfb24c69d1b6f5e758db200866c711e25a2ba73 -Author: Darren Tucker -Date: Fri Jun 5 14:51:40 2015 +1000 +commit 5e288923a303ca672b686908320bc5368ebec6e6 +Author: mmcc@openbsd.org +Date: Fri Nov 6 00:31:41 2015 +0000 - Add Linux powerpc64le and powerpcle entries. + upstream commit - Stopgap to resolve bz#2409 because we are so close to release and will - update config.guess and friends shortly after the release. ok djm@ - -commit a1195a0fdc9eddddb04d3e9e44c4775431cb77da -Merge: 6397eed d2480bc -Author: Tim Rice -Date: Wed Jun 3 21:43:13 2015 -0700 - - Merge branch 'master' of git.mindrot.org:/var/git/openssh + 1. rlogin and rsh are long gone 2. protocol version isn't + of core relevance here, and v1 is going away + + ok markus@, deraadt@ + + Upstream-ID: 8b46bc94cf1ca7c8c1a75b1c958b2bb38d7579c8 -commit 6397eedf953b2b973d2d7cbb504ab501a07f8ddc -Author: Tim Rice -Date: Wed Jun 3 21:41:11 2015 -0700 +commit 8b29008bbe97f33381d9b4b93fcfa304168d0286 +Author: jmc@openbsd.org +Date: Thu Nov 5 09:48:05 2015 +0000 - Remove unneeded backslashes. Patch from Ángel González + upstream commit + + "commandline" -> "command line", since there are so few + examples of the former in the pages, so many of the latter, and in some of + these pages we had multiple spellings; + + prompted by tj + + Upstream-ID: 78459d59bff74223f8139d9001ccd56fc4310659 -commit d2480bcac1caf31b03068de877a47d6e1027bf6d +commit 996b24cebf20077fbe5db07b3a2c20c2d9db736e Author: Darren Tucker -Date: Thu Jun 4 14:10:55 2015 +1000 +Date: Thu Oct 29 20:57:34 2015 +1100 - Remove redundant include of stdarg.h. bz#2410 + (re)wrap SYS_sendsyslog in ifdef. + + Replace ifdef that went missing in commit + c61b42f2678f21f05653ac2d3d241b48ab5d59ac. Fixes build on older + OpenBSDs. -commit 5e67859a623826ccdf2df284cbb37e2d8e2787eb +commit b67e2e76fcf1ae7c802eb27ca927e16c91a513ff Author: djm@openbsd.org -Date: Tue Jun 2 09:10:40 2015 +0000 +Date: Thu Oct 29 08:05:17 2015 +0000 upstream commit - mention CheckHostIP adding addresses to known_hosts; - bz#1993; ok dtucker@ + regress test for "PubkeyAcceptedKeyTypes +..." inside a + Match block - Upstream-ID: fd44b68440fd0dc29abf9f2d3f703d74a2396cb7 + Upstream-Regress-ID: 246c37ed64a2e5704d4c158ccdca1ff700e10647 -commit d7a58bbac6583e33fd5eca8e2c2cc70c57617818 -Author: Darren Tucker -Date: Tue Jun 2 20:15:26 2015 +1000 +commit abd9dbc3c0d8c8c7561347cfa22166156e78c077 +Author: dtucker@openbsd.org +Date: Mon Oct 26 02:50:58 2015 +0000 - Replace strcpy with strlcpy. + upstream commit - ok djm, sanity check by Corinna Vinschen. + Fix typo certopt->certopts in shell variable. This would + cause the test to hang at a host key prompt if you have an A or CNAME for + "proxy" in your local domain. + + Upstream-Regress-ID: 6ea03bcd39443a83c89e2c5606392ceb9585836a -commit 51a1c2115265c6e80ede8a5c9dccada9aeed7143 -Author: Damien Miller -Date: Fri May 29 18:27:21 2015 +1000 +commit ed08510d38aef930a061ae30d10f2a9cf233bafa +Author: djm@openbsd.org +Date: Thu Oct 29 08:05:01 2015 +0000 - skip, rather than fatal when run without SUDO set + upstream commit + + Fix "PubkeyAcceptedKeyTypes +..." inside a Match block; + ok dtucker@ + + Upstream-ID: 853662c4036730b966aab77684390c47b9738c69 -commit 599f01142a376645b15cbc9349d7e8975e1cf245 -Author: Damien Miller -Date: Fri May 29 18:03:15 2015 +1000 +commit a4aef3ed29071719b2af82fdf1ac3c2514f82bc5 +Author: djm@openbsd.org +Date: Tue Oct 27 08:54:52 2015 +0000 - fix merge botch that left ",," in KEX algs + upstream commit + + fix execv arguments in a way less likely to cause grief + for -portable; ok dtucker@ + + Upstream-ID: 5902bf0ea0371f39f1300698dc3b8e4105fc0fc5 -commit 0c2a81dfc21822f2423edd30751e5ec53467b347 -Author: Damien Miller -Date: Fri May 29 17:08:28 2015 +1000 +commit 63d188175accea83305e89fafa011136ff3d96ad +Author: djm@openbsd.org +Date: Tue Oct 27 01:44:45 2015 +0000 - re-enable SSH protocol 1 at compile time + upstream commit + + log certificate serial in verbose() messages to match the + main auth success/fail message; ok dtucker@ + + Upstream-ID: dfc48b417c320b97c36ff351d303c142f2186288 -commit db438f9285d64282d3ac9e8c0944f59f037c0151 +commit 2aaba0cfd560ecfe92aa50c00750e6143842cf1f Author: djm@openbsd.org -Date: Fri May 29 03:05:13 2015 +0000 +Date: Tue Oct 27 00:49:53 2015 +0000 upstream commit - make this work without SUDO set; ok dtucker@ + avoid de-const warning & shrink; ok dtucker@ - Upstream-Regress-ID: bca88217b70bce2fe52b23b8e06bdeb82d98c715 + Upstream-ID: 69a85ef94832378952a22c172009cbf52aaa11db -commit 1d9a2e2849c9864fe75daabf433436341c968e14 -Author: djm@openbsd.org -Date: Thu May 28 07:37:31 2015 +0000 +commit 03239c18312b9bab7d1c3b03062c61e8bbc1ca6e +Author: dtucker@openbsd.org +Date: Sun Oct 25 23:42:00 2015 +0000 upstream commit - wrap all moduli-related code in #ifdef WITH_OPENSSL. - based on patch from Reuben Hawkins; bz#2388 feedback and ok dtucker@ + Expand tildes in filenames passed to -i before checking + whether or not the identity file exists. This means that if the shell + doesn't do the expansion (eg because the option and filename were given as a + single argument) then we'll still add the key. bz#2481, ok markus@ - Upstream-ID: d80cfc8be3e6ec65b3fac9e87c4466533b31b7cf + Upstream-ID: db1757178a14ac519e9a3e1a2dbd21113cb3bfc6 -commit 496aeb25bc2d6c434171292e4714771b594bd00e +commit 97e184e508dd33c37860c732c0eca3fc57698b40 Author: dtucker@openbsd.org -Date: Thu May 28 05:41:29 2015 +0000 +Date: Sun Oct 25 23:14:03 2015 +0000 upstream commit - Increase the allowed length of the known host file name - in the log message to be consistent with other cases. Part of bz#1993, ok - deraadt. + Do not prepend "exec" to the shell command run by "Match + exec" in a config file. It's an unnecessary optimization from repurposed + ProxyCommand code and prevents some things working with some shells. + bz#2471, pointed out by res at qoxp.net. ok markus@ - Upstream-ID: a9e97567be49f25daf286721450968251ff78397 + Upstream-ID: a1ead25ae336bfa15fb58d8c6b5589f85b4c33a3 -commit dd2cfeb586c646ff8d70eb93567b2e559ace5b14 +commit 8db134e7f457bcb069ec72bc4ee722e2af557c69 +Author: Darren Tucker +Date: Thu Oct 29 10:48:23 2015 +1100 + + Prevent name collisions with system glob (bz#2463) + + Move glob.h from includes.h to the only caller (sftp) and override the + names for the symbols. This prevents name collisions with the system glob + in the case where something other than ssh uses it (eg kerberos). With + jjelen at redhat.com, ok djm@ + +commit 86c10dbbef6a5800d2431a66cf7f41a954bb62b5 Author: dtucker@openbsd.org -Date: Thu May 28 05:09:45 2015 +0000 +Date: Fri Oct 23 02:22:01 2015 +0000 upstream commit - Fix typo (keywork->keyword) + Update expected group sizes to match recent code changes. - Upstream-ID: 8aacd0f4089c0a244cf43417f4f9045dfaeab534 + Upstream-Regress-ID: 0004f0ea93428969fe75bcfff0d521c553977794 -commit 9cc6842493fbf23025ccc1edab064869640d3bec +commit 9ada37d36003a77902e90a3214981e417457cf13 Author: djm@openbsd.org -Date: Thu May 28 04:50:53 2015 +0000 +Date: Sat Oct 24 22:56:19 2015 +0000 upstream commit - add error message on ftruncate failure; bz#2176 + fix keyscan output for multiple hosts/addrs on one line + when host hashing or a non standard port is in use; bz#2479 ok dtucker@ - Upstream-ID: cbcc606e0b748520c74a210d8f3cc9718d3148cf + Upstream-ID: 5321dabfaeceba343da3c8a8b5754c6f4a0a307b -commit d1958793a0072c22be26d136dbda5ae263e717a0 +commit 44fc7cd7dcef6c52c6b7e9ff830dfa32879bd319 Author: djm@openbsd.org -Date: Thu May 28 04:40:13 2015 +0000 +Date: Sat Oct 24 22:52:22 2015 +0000 upstream commit - make ssh-keygen default to ed25519 keys when compiled - without OpenSSL; bz#2388, ok dtucker@ + skip "Could not chdir to home directory" message when + chrooted - Upstream-ID: 85a471fa6d3fa57a7b8e882d22cfbfc1d84cdc71 + patch from Christian Hesse in bz#2485 ok dtucker@ + + Upstream-ID: 86783c1953da426dff5b03b03ce46e699d9e5431 -commit 3ecde664c9fc5fb3667aedf9e6671462600f6496 -Author: dtucker@openbsd.org -Date: Wed May 27 23:51:10 2015 +0000 +commit a820a8618ec44735dabc688fab96fba38ad66bb2 +Author: sthen@openbsd.org +Date: Sat Oct 24 08:34:09 2015 +0000 upstream commit - Reorder client proposal to prefer - diffie-hellman-group-exchange-sha1 over diffie-hellman-group14-sha1. ok djm@ + Handle the split of tun(4) "link0" into tap(4) in ssh + tun-forwarding. Adapted from portable (using separate devices for this is the + normal case in most OS). ok djm@ - Upstream-ID: 552c08d47347c3ee1a9a57d88441ab50abe17058 + Upstream-ID: 90facf4c59ce73d6741db1bc926e578ef465cd39 -commit 40f64292b907afd0a674fdbf3e4c2356d17a7d68 -Author: dtucker@openbsd.org -Date: Wed May 27 23:39:18 2015 +0000 +commit 66d2e229baa9fe57b868c373b05f7ff3bb20055b +Author: gsoares@openbsd.org +Date: Wed Oct 21 11:33:03 2015 +0000 upstream commit - Add a stronger (4k bit) fallback group that sshd can use - when the moduli file is missing or broken, sourced from RFC3526. bz#2302, ok - markus@ (earlier version), djm@ + fix memory leak in error path ok djm@ - Upstream-ID: b635215746a25a829d117673d5e5a76d4baee7f4 + Upstream-ID: dd2f402b0a0029b755df029fc7f0679e1365ce35 -commit 5ab7d5fa03ad55bc438fab45dfb3aeb30a3c237a -Author: Darren Tucker -Date: Thu May 28 10:03:40 2015 +1000 +commit 7d6c0362039ceacdc1366b5df29ad5d2693c13e5 +Author: mmcc@openbsd.org +Date: Tue Oct 20 23:24:25 2015 +0000 - New moduli file from OpenBSD, removing 1k groups. + upstream commit - Remove 1k bit groups. ok deraadt@, markus@ + Compare pointers to NULL rather than 0. + + ok djm@ + + Upstream-ID: 21616cfea27eda65a06e772cc887530b9a1a27f8 -commit a71ba58adf34e599f30cdda6e9b93ae6e3937eea -Author: djm@openbsd.org -Date: Wed May 27 05:15:02 2015 +0000 +commit f98a09cacff7baad8748c9aa217afd155a4d493f +Author: mmcc@openbsd.org +Date: Tue Oct 20 03:36:35 2015 +0000 upstream commit - support PKCS#11 devices with external PIN entry devices - bz#2240, based on patch from Dirk-Willem van Gulik; feedback and ok dtucker@ + Replace a function-local allocation with stack memory. - Upstream-ID: 504568992b55a8fc984375242b1bd505ced61b0d + ok djm@ + + Upstream-ID: c09fbbab637053a2ab9f33ca142b4e20a4c5a17e -commit b282fec1aa05246ed3482270eb70fc3ec5f39a00 -Author: dtucker@openbsd.org -Date: Tue May 26 23:23:40 2015 +0000 +commit ac908c1eeacccfa85659594d92428659320fd57e +Author: Damien Miller +Date: Thu Oct 22 09:35:24 2015 +1100 - upstream commit - - Cap DH-GEX group size at 4kbits for Cisco implementations. - Some of them will choke when asked for preferred sizes >4k instead of - returning the 4k group that they do have. bz#2209, ok djm@ + turn off PrintLastLog when --disable-lastlog - Upstream-ID: 54b863a19713446b7431f9d06ad0532b4fcfef8d + bz#2278 from Brent Paulson -commit 3e91b4e8b0dc2b4b7e7d42cf6e8994a32e4cb55e +commit b56deb847f4a0115a8bf488bf6ee8524658162fd Author: djm@openbsd.org -Date: Sun May 24 23:39:16 2015 +0000 +Date: Fri Oct 16 22:32:22 2015 +0000 upstream commit - add missing 'c' option to getopt(), case statement was - already there; from Felix Bolte + increase the minimum modulus that we will send or accept in + diffie-hellman-group-exchange to 2048 bits; ok markus@ - Upstream-ID: 9b19b4e2e0b54d6fefa0dfac707c51cf4bae3081 + Upstream-ID: 06dce7a24c17b999a0f5fadfe95de1ed6a1a9b6a -commit 64a89ec07660abba4d0da7c0095b7371c98bab62 -Author: jsg@openbsd.org -Date: Sat May 23 14:28:37 2015 +0000 +commit 5ee0063f024bf5b3f3ffb275b8cd20055d62b4b9 +Author: djm@openbsd.org +Date: Fri Oct 16 18:40:49 2015 +0000 upstream commit - fix a memory leak in an error path ok markus@ dtucker@ + better handle anchored FQDNs (e.g. 'cvs.openbsd.org.') in + hostname canonicalisation - treat them as already canonical and remove the + trailing '.' before matching ssh_config; ok markus@ - Upstream-ID: bc1da0f205494944918533d8780fde65dff6c598 + Upstream-ID: f7619652e074ac3febe8363f19622aa4853b679a -commit f948737449257d2cb83ffcfe7275eb79b677fd4a -Author: djm@openbsd.org -Date: Fri May 22 05:28:45 2015 +0000 +commit e92c499a75477ecfe94dd7b4aed89f20b1fac5a7 +Author: mmcc@openbsd.org +Date: Fri Oct 16 17:07:24 2015 +0000 upstream commit - mention ssh-keygen -E for comparing legacy MD5 - fingerprints; bz#2332 + 0 -> NULL when comparing with a char*. - Upstream-ID: 079a3669549041dbf10dbc072d9563f0dc3b2859 + ok dtucker@, djm@. + + Upstream-ID: a928e9c21c0a9020727d99738ff64027c1272300 -commit 0882332616e4f0272c31cc47bf2018f9cb258a4e +commit b1d38a3cc6fe349feb8d16a5f520ef12d1de7cb2 Author: djm@openbsd.org -Date: Fri May 22 04:45:52 2015 +0000 +Date: Thu Oct 15 23:51:40 2015 +0000 upstream commit - Reorder EscapeChar option parsing to avoid a single-byte - out- of-bounds read. bz#2396 from Jaak Ristioja; ok dtucker@ + fix some signed/unsigned integer type mismatches in + format strings; reported by Nicholas Lemonias - Upstream-ID: 1dc6b5b63d1c8d9a88619da0b27ade461d79b060 + Upstream-ID: 78cd55420a0eef68c4095bdfddd1af84afe5f95c -commit d7c31da4d42c115843edee2074d7d501f8804420 +commit 1a2663a15d356bb188196b6414b4c50dc12fd42b Author: djm@openbsd.org -Date: Fri May 22 03:50:02 2015 +0000 +Date: Thu Oct 15 23:08:23 2015 +0000 upstream commit - add knob to relax GSSAPI host credential check for - multihomed hosts bz#928, patch by Simon Wilkinson; ok dtucker - (kerberos/GSSAPI is not compiled by default on OpenBSD) + argument to sshkey_from_private() and sshkey_demote() + can't be NULL - Upstream-ID: 15ddf1c6f7fd9d98eea9962f480079ae3637285d + Upstream-ID: 0111245b1641d387977a9b38da15916820a5fd1f -commit aa72196a00be6e0b666215edcffbc10af234cb0e -Author: Darren Tucker -Date: Fri May 22 17:49:46 2015 +1000 +commit 0f754e29dd3760fc0b172c1220f18b753fb0957e +Author: Damien Miller +Date: Fri Oct 16 10:53:14 2015 +1100 - Include signal.h for sig_atomic_t, used by kex.h. + need va_copy before va_start - bz#2402, from tomas.kuthan at oracle com. + reported by Nicholas Lemonias -commit 8b02481143d75e91c49d1bfae0876ac1fbf9511a -Author: Darren Tucker -Date: Fri May 22 12:47:24 2015 +1000 +commit eb6c50d82aa1f0d3fc95f5630ea69761e918bfcd +Author: Damien Miller +Date: Thu Oct 15 15:48:28 2015 -0700 - Import updated moduli file from OpenBSD. + fix compilation on systems without SYMLOOP_MAX -commit 4739e8d5e1c0be49624082bd9f6b077e9e758db9 -Author: djm@openbsd.org -Date: Thu May 21 12:01:19 2015 +0000 +commit fafe1d84a210fb3dae7744f268059cc583db8c12 +Author: Damien Miller +Date: Wed Oct 14 09:22:15 2015 -0700 - upstream commit - - Support "ssh-keygen -lF hostname" to find search known_hosts - and print key hashes. Already advertised by ssh-keygen(1), but not delivered - by code; ok dtucker@ - - Upstream-ID: 459e0e2bf39825e41b0811c336db2d56a1c23387 + s/SANDBOX_TAME/SANDBOX_PLEDGE/g -commit e97201feca10b5196da35819ae516d0b87cf3a50 +commit 8f22911027ff6c17d7226d232ccd20727f389310 Author: Damien Miller -Date: Thu May 21 17:55:15 2015 +1000 +Date: Wed Oct 14 08:28:19 2015 +1100 - conditionalise util.h inclusion + upstream commit + + revision 1.20 + date: 2015/10/13 20:55:37; author: millert; state: Exp; lines: +2 -2; commitid: X39sl5ay1czgFIgp; + In rev 1.15 the sizeof argument was fixed in a strlcat() call but + the truncation check immediately following it was not updated to + match. Not an issue in practice since the buffers are the same + size. OK deraadt@ -commit 13640798c7dd011ece0a7d02841fe48e94cfa0e0 -Author: djm@openbsd.org -Date: Thu May 21 06:44:25 2015 +0000 +commit 23fa695bb735f54f04d46123662609edb6c76767 +Author: Damien Miller +Date: Wed Oct 14 08:27:51 2015 +1100 upstream commit - regress test for AuthorizedPrincipalsCommand - - Upstream-Regress-ID: c658fbf1ab6b6011dc83b73402322e396f1e1219 - -commit 84452c5d03c21f9bfb28c234e0dc1dc67dd817b1 -Author: djm@openbsd.org -Date: Thu May 21 06:40:02 2015 +0000 + revision 1.19 + date: 2015/01/16 16:48:51; author: deraadt; state: Exp; lines: +3 -3; commitid: 0DYulI8hhujBHMcR; + Move to the universe. + review by millert, binary checking process with doug, concept with guenther + +commit c71be375a69af00c2d0a0c24d8752bec12d8fd1b +Author: Damien Miller +Date: Wed Oct 14 08:27:08 2015 +1100 upstream commit - regress test for AuthorizedKeysCommand arguments - - Upstream-Regress-ID: bbd65c13c6b3be9a442ec115800bff9625898f12 + revision 1.18 + date: 2014/10/19 03:56:28; author: doug; state: Exp; lines: +9 -9; commitid: U6QxmtbXrGoc02S5; + Revert last commit due to changed semantics found by make release. -commit bcc50d816187fa9a03907ac1f3a52f04a52e10d1 -Author: djm@openbsd.org -Date: Thu May 21 06:43:30 2015 +0000 +commit c39ad23b06e9aecc3ff788e92f787a08472905b1 +Author: Damien Miller +Date: Wed Oct 14 08:26:24 2015 +1100 upstream commit - add AuthorizedPrincipalsCommand that allows getting - authorized_principals from a subprocess rather than a file, which is quite - useful in deployments with large userbases + revision 1.17 + date: 2014/10/18 20:43:52; author: doug; state: Exp; lines: +10 -10; commitid: I74hI1tVZtsspKEt; + Better POSIX compliance in realpath(3). - feedback and ok markus@ + millert@ made changes to realpath.c based on FreeBSD's version. I merged + Todd's changes into dl_realpath.c. - Upstream-ID: aa1bdac7b16fc6d2fa3524ef08f04c7258d247f6 + ok millert@, guenther@ -commit 24232a3e5ab467678a86aa67968bbb915caffed4 -Author: djm@openbsd.org -Date: Thu May 21 06:38:35 2015 +0000 +commit e929a43f957dbd1254aca2aaf85c8c00cbfc25f4 +Author: Damien Miller +Date: Wed Oct 14 08:25:55 2015 +1100 upstream commit - support arguments to AuthorizedKeysCommand - - bz#2081 loosely based on patch by Sami Hartikainen - feedback and ok markus@ - - Upstream-ID: b080387a14aa67dddd8ece67c00f268d626541f7 + revision 1.16 + date: 2013/04/05 12:59:54; author: kurt; state: Exp; lines: +3 -1; + - Add comments regarding copies of these files also in libexec/ld.so + okay guenther@ -commit d80fbe41a57c72420c87a628444da16d09d66ca7 -Author: djm@openbsd.org -Date: Thu May 21 04:55:51 2015 +0000 +commit 5225db68e58a1048cb17f0e36e0d33bc4a8fc410 +Author: Damien Miller +Date: Wed Oct 14 08:25:32 2015 +1100 upstream commit - refactor: split base64 encoding of pubkey into its own - sshkey_to_base64() function and out of sshkey_write(); ok markus@ - - Upstream-ID: 54fc38f5832e9b91028900819bda46c3959a0c1a + revision 1.15 + date: 2012/09/13 15:39:05; author: deraadt; state: Exp; lines: +2 -2; + specify the bounds of the dst to strlcat (both values were static and + equal, but it is more correct) + from Michal Mazurek -commit 7cc44ef74133a473734bbcbd3484f24d6a7328c5 -Author: deraadt@openbsd.org -Date: Mon May 18 15:06:05 2015 +0000 +commit 7365fe5b4859de2305e40ea132da3823830fa710 +Author: Damien Miller +Date: Wed Oct 14 08:25:09 2015 +1100 upstream commit - getentropy() and sendsyslog() have been around long - enough. openssh-portable may want the #ifdef's but not base. discussed with - djm few weeks back - - Upstream-ID: 0506a4334de108e3fb6c66f8d6e0f9c112866926 + revision 1.14 + date: 2011/07/24 21:03:00; author: miod; state: Exp; lines: +35 -13; + Recent Single Unix will malloc memory if the second argument of realpath() + is NULL, and third-party software is starting to rely upon this. + Adapted from FreeBSD via Jona Joachim (jaj ; hcl-club , .lu), with minor + tweaks from nicm@ and yours truly. -commit 9173d0fbe44de7ebcad8a15618e13a8b8d78902e -Author: dtucker@openbsd.org -Date: Fri May 15 05:44:21 2015 +0000 +commit e679c09cd1951f963793aa3d9748d1c3fdcf808f +Author: djm@openbsd.org +Date: Tue Oct 13 16:15:21 2015 +0000 upstream commit - Use a salted hash of the lock passphrase instead of plain - text and do constant-time comparisons of it. Should prevent leaking any - information about it via timing, pointed out by Ryan Castellucci. Add a 0.1s - incrementing delay for each failed unlock attempt up to 10s. ok markus@ - (earlier version), djm@ + apply PubkeyAcceptedKeyTypes filtering earlier, so all + skipped keys are noted before pubkey authentication starts. ok dtucker@ - Upstream-ID: c599fcc325aa1cc65496b25220b622d22208c85f + Upstream-ID: ba4f52f54268a421a2a5f98bb375403f4cb044b8 -commit d028d5d3a697c71b21e4066d8672cacab3caa0a8 -Author: Damien Miller -Date: Tue May 5 19:10:58 2015 +1000 +commit 179c353f564ec7ada64b87730b25fb41107babd7 +Author: djm@openbsd.org +Date: Tue Oct 13 00:21:27 2015 +0000 upstream commit - - tedu@cvs.openbsd.org 2015/01/12 03:20:04 - [bcrypt_pbkdf.c] - rename blocks to words. bcrypt "blocks" are unrelated to blowfish blocks, - nor are they the same size. + free the correct IV length, don't assume it's always the + cipher blocksize; ok dtucker@ + + Upstream-ID: c260d9e5ec73628d9ff4b067fbb060eff5a7d298 -commit f6391d4e59b058984163ab28f4e317e7a72478f1 -Author: Damien Miller -Date: Tue May 5 19:10:23 2015 +1000 +commit 2539dce2a049a8f6bb0d44cac51f07ad48e691d3 +Author: deraadt@openbsd.org +Date: Fri Oct 9 01:37:08 2015 +0000 upstream commit - - deraadt@cvs.openbsd.org 2015/01/08 00:30:07 - [bcrypt_pbkdf.c] - declare a local version of MIN(), call it MINIMUM() + Change all tame callers to namechange to pledge(2). + + Upstream-ID: 17e654fc27ceaf523c60f4ffd9ec7ae4e7efc7f2 -commit 8ac6b13cc9113eb47cd9e86c97d7b26b4b71b77f +commit 9846a2f4067383bb76b4e31a9d2303e0a9c13a73 Author: Damien Miller -Date: Tue May 5 19:09:46 2015 +1000 +Date: Thu Oct 8 04:30:48 2015 +1100 - upstream commit + hook tame(2) sandbox up to build - - djm@cvs.openbsd.org 2014/12/30 01:41:43 - [bcrypt_pbkdf.c] - typo in comment: ouput => output + OpenBSD only for now -commit 1f792489d5cf86a4f4e3003e6e9177654033f0f2 +commit 0c46bbe68b70bdf0d6d20588e5847e71f3739fe6 Author: djm@openbsd.org -Date: Mon May 4 06:10:48 2015 +0000 +Date: Wed Oct 7 15:59:12 2015 +0000 upstream commit - Remove pattern length argument from match_pattern_list(), we - only ever use it for strlen(pattern). - - Prompted by hanno AT hboeck.de pointing an out-of-bound read - error caused by an incorrect pattern length found using AFL - and his own tools. + include PubkeyAcceptedKeyTypes in ssh -G config dump - ok markus@ + Upstream-ID: 6c097ce6ffebf6fe393fb7988b5d152a5d6b36bb -commit 639d6bc57b1942393ed12fb48f00bc05d4e093e4 -Author: djm@openbsd.org -Date: Fri May 1 07:10:01 2015 +0000 +commit bdcb73fb7641b1cf73c0065d1a0dd57b1e8b778e +Author: sobrado@openbsd.org +Date: Wed Oct 7 14:45:30 2015 +0000 upstream commit - refactor ssh_dispatch_run_fatal() to use sshpkt_fatal() - to better report error conditions. Teach sshpkt_fatal() about ECONNRESET. + UsePrivilegeSeparation defaults to sandbox now. - Improves error messages on TCP connection resets. bz#2257 + ok djm@ - ok dtucker@ + Upstream-ID: bff136c38bcae89df82e044d2f42de21e1ad914f -commit 9559d7de34c572d4d3fd990ca211f8ec99f62c4d +commit 2905d6f99c837bb699b6ebc61711b19acd030709 Author: djm@openbsd.org -Date: Fri May 1 07:08:08 2015 +0000 +Date: Wed Oct 7 00:54:06 2015 +0000 upstream commit - a couple of parse targets were missing activep checks, - causing them to be misapplied in match context; bz#2272 diagnosis and - original patch from Sami Hartikainen ok dtucker@ + don't try to change tun device flags if they are already + what we need; makes it possible to use tun/tap networking as non- root user + if device permissions and interface flags are pre-established; based on patch + by Ossi Herrala + + Upstream-ID: 89099ac4634cd477b066865acf54cb230780fd21 -commit 7e8528cad04b2775c3b7db08abf8fb42e47e6b2a -Author: djm@openbsd.org -Date: Fri May 1 04:17:51 2015 +0000 +commit 0dc74512bdb105b048883f07de538b37e5e024d4 +Author: Damien Miller +Date: Mon Oct 5 18:33:05 2015 -0700 - upstream commit - - make handling of AuthorizedPrincipalsFile=none more - consistent with other =none options; bz#2288 from Jakub Jelen; ok dtucker@ + unbreak merge botch -commit ca430d4d9cc0f62eca3b1fb1e2928395b7ce80f7 +commit fdd020e86439afa7f537e2429d29d4b744c94331 Author: djm@openbsd.org -Date: Fri May 1 04:03:20 2015 +0000 +Date: Tue Oct 6 01:20:59 2015 +0000 upstream commit - remove failed remote forwards established by muliplexing - from the list of active forwards; bz#2363, patch mostly by Yoann Ricordel; ok - dtucker@ + adapt to recent sshkey_parse_private_fileblob() API + change + + Upstream-Regress-ID: 5c0d818da511e33e0abf6a92a31bd7163b7ad988 -commit 8312cfb8ad88657517b3e23ac8c56c8e38eb9792 +commit 21ae8ee3b630b0925f973db647a1b9aa5fcdd4c5 Author: djm@openbsd.org -Date: Fri May 1 04:01:58 2015 +0000 +Date: Thu Sep 24 07:15:39 2015 +0000 upstream commit - reduce stderr spam when using ssh -S /path/mux -O forward - -R 0:... ok dtucker@ + fix command-line option to match what was actually + committed + + Upstream-Regress-ID: 3e8c24a2044e8afd37e7ce17b69002ca817ac699 -commit 179be0f5e62f1f492462571944e45a3da660d82b +commit e14ac43b75e68f1ffbd3e1a5e44143c8ae578dcd Author: djm@openbsd.org -Date: Fri May 1 03:23:51 2015 +0000 +Date: Thu Sep 24 06:16:53 2015 +0000 upstream commit - prevent authorized_keys options picked up on public key - tests without a corresponding private key authentication being applied to - other authentication methods. Reported by halex@, ok markus@ + regress test for CertificateFile; patch from Meghana Bhat + via bz#2436 + + Upstream-Regress-ID: e7a6e980cbe0f8081ba2e83de40d06c17be8bd25 -commit a42d67be65b719a430b7fcaba2a4e4118382723a +commit 905b054ed24e0d5b4ef226ebf2c8bfc02ae6d4ad Author: djm@openbsd.org -Date: Fri May 1 03:20:54 2015 +0000 +Date: Mon Oct 5 17:11:21 2015 +0000 upstream commit - Don't make parsing of authorized_keys' environment= - option conditional on PermitUserEnv - always parse it, but only use the - result if the option is enabled. This prevents the syntax of authorized_keys - changing depending on which sshd_config options were enabled. + some more bzero->explicit_bzero, from Michael McConville - bz#2329; based on patch from coladict AT gmail.com, ok dtucker@ + Upstream-ID: 17f19545685c33327db2efdc357c1c9225ff00d0 -commit e661a86353e11592c7ed6a847e19a83609f49e77 -Author: djm@openbsd.org -Date: Mon May 4 06:10:48 2015 +0000 +commit b007159a0acdbcf65814b3ee05dbe2cf4ea46011 +Author: deraadt@openbsd.org +Date: Fri Oct 2 15:52:55 2015 +0000 upstream commit - Remove pattern length argument from match_pattern_list(), we - only ever use it for strlen(pattern). - - Prompted by hanno AT hboeck.de pointing an out-of-bound read - error caused by an incorrect pattern length found using AFL - and his own tools. + fix email - ok markus@ + Upstream-ID: 72150f2d54b94de14ebef1ea054ef974281bf834 -commit 0ef1de742be2ee4b10381193fe90730925b7f027 -Author: dtucker@openbsd.org -Date: Thu Apr 23 05:01:19 2015 +0000 +commit b19e1b4ab11884c4f62aee9f8ab53127a4732658 +Author: deraadt@openbsd.org +Date: Fri Oct 2 01:39:52 2015 +0000 upstream commit - Add a simple regression test for sshd's configuration - parser. Right now, all it does is run the output of sshd -T back through - itself and ensure the output is valid and invariant. + a sandbox using tame ok djm + + Upstream-ID: 4ca24e47895e72f5daaa02f3e3d3e5ca2d820fa3 -commit 368f83c793275faa2c52f60eaa9bdac155c4254b -Author: djm@openbsd.org -Date: Wed Apr 22 01:38:36 2015 +0000 +commit c61b42f2678f21f05653ac2d3d241b48ab5d59ac +Author: deraadt@openbsd.org +Date: Fri Oct 2 01:39:26 2015 +0000 upstream commit - use correct key for nested certificate test + re-order system calls in order of risk, ok i'll be + honest, ordered this way they look like tame... ok djm + + Upstream-ID: 42a1e6d251fd8be13c8262bee026059ae6328813 -commit 8d4d1bfddbbd7d21f545dc6997081d1ea1fbc99a -Author: djm@openbsd.org -Date: Fri May 1 07:11:47 2015 +0000 +commit c5f7c0843cb6e6074a93c8ac34e49ce33a6f5546 +Author: jmc@openbsd.org +Date: Fri Sep 25 18:19:54 2015 +0000 upstream commit - mention that the user's shell from /etc/passwd is used - for commands too; bz#1459 ok dtucker@ + some certificatefile tweaks; ok djm + + Upstream-ID: 0e5a7852c28c05fc193419cc7e50e64c1c535af0 -commit 5ab283d0016bbc9d4d71e8e5284d011bc5a930cf +commit 4e44a79a07d4b88b6a4e5e8c1bed5f58c841b1b8 Author: djm@openbsd.org -Date: Fri May 8 07:29:00 2015 +0000 +Date: Thu Sep 24 06:15:11 2015 +0000 upstream commit - whitespace + add ssh_config CertificateFile option to explicitly list + a certificate; patch from Meghana Bhat on bz#2436; ok markus@ - Upstream-Regress-ID: 6b708a3e709d5b7fd37890f874bafdff1f597519 + Upstream-ID: 58648ec53c510b41c1f46d8fe293aadc87229ab8 -commit 8377d5008ad260048192e1e56ad7d15a56d103dd -Author: djm@openbsd.org -Date: Fri May 8 07:26:13 2015 +0000 +commit e3cbb06ade83c72b640a53728d362bbefa0008e2 +Author: sobrado@openbsd.org +Date: Tue Sep 22 08:33:23 2015 +0000 upstream commit - whitespace at EOL + fix two typos. - Upstream-Regress-ID: 9c48911643d5b05173b36a012041bed4080b8554 + Upstream-ID: 424402c0d8863a11b51749bacd7f8d932083b709 -commit c28a3436fa8737709ea88e4437f8f23a6ab50359 +commit 8408218c1ca88cb17d15278174a24a94a6f65fe1 Author: djm@openbsd.org -Date: Fri May 8 06:45:13 2015 +0000 +Date: Mon Sep 21 04:31:00 2015 +0000 upstream commit - moar whitespace at eol + fix possible hang on closed output; bz#2469 reported by Tomas + Kuthan ok markus@ - Upstream-ID: 64eaf872a3ba52ed41e494287e80d40aaba4b515 + Upstream-ID: f7afd41810f8540f524284f1be6b970859f94fe3 -commit 2b64c490468fd4ca35ac8d5cc31c0520dc1508bb +commit 0097248f90a00865082e8c146b905a6555cc146f Author: djm@openbsd.org -Date: Fri May 8 06:41:56 2015 +0000 +Date: Fri Sep 11 04:55:01 2015 +0000 upstream commit - whitespace at EOL + skip if running as root; many systems (inc OpenBSD) allow + root to ptrace arbitrary processes - Upstream-ID: 57bcf67d666c6fc1ad798aee448fdc3f70f7ec2c + Upstream-Regress-ID: be2b925df89360dff36f972951fa0fa793769038 -commit 4e636cf201ce6e7e3b9088568218f9d4e2c51712 +commit 9c06c814aff925e11a5cc592c06929c258a014f6 Author: djm@openbsd.org -Date: Fri May 8 03:56:51 2015 +0000 +Date: Fri Sep 11 03:44:21 2015 +0000 upstream commit - whitespace at EOL + try all supported key types here; bz#2455 reported by + Jakub Jelen + + Upstream-Regress-ID: 188cb7d9031cdbac3a0fa58b428b8fa2b2482bba -commit 38b8272f823dc1dd4e29dbcee83943ed48bb12fa -Author: dtucker@openbsd.org -Date: Mon May 4 01:47:53 2015 +0000 +commit 3c019a936b43f3e2773f3edbde7c114d73caaa4c +Author: tim@openbsd.org +Date: Sun Sep 13 14:39:16 2015 +0000 upstream commit - Use diff w/out -u for better portability + - Fix error message: passphrase needs to be at least 5 + characters, not 4. - Remove unused function argument. - Remove two + unnecessary variables. + + OK djm@ + + Upstream-ID: 13010c05bfa8b523da1c0dc19e81dd180662bc30 -commit 297060f42d5189a4065ea1b6f0afdf6371fb0507 -Author: dtucker@openbsd.org -Date: Fri May 8 03:25:07 2015 +0000 +commit 2681cdb6e0de7c1af549dac37a9531af202b4434 +Author: tim@openbsd.org +Date: Sun Sep 13 13:48:19 2015 +0000 upstream commit - Use xcalloc for permitted_adm_opens instead of xmalloc to - ensure it's zeroed. Fixes post-auth crash with permitopen=none. bz#2355, ok - djm@ + When adding keys to the agent, don't ignore the comment + of keys for which the user is prompted for a passphrase. + + Tweak and OK djm@ + + Upstream-ID: dc737c620a5a8d282cc4f66e3b9b624e9abefbec -commit 63ebf019be863b2d90492a85e248cf55a6e87403 -Author: djm@openbsd.org -Date: Fri May 8 03:17:49 2015 +0000 +commit 14692f7b8251cdda847e648a82735eef8a4d2a33 +Author: guenther@openbsd.org +Date: Fri Sep 11 08:50:04 2015 +0000 upstream commit - don't choke on new-format private keys encrypted with an - AEAD cipher; bz#2366, patch from Ron Frederick; ok markus@ + Use explicit_bzero() when zeroing before free() + + from Michael McConville (mmcconv1 (at) sccs.swarthmore.edu) + ok millert@ djm@ + + Upstream-ID: 2e3337db046c3fe70c7369ee31515ac73ec00f50 -commit f8484dac678ab3098ae522a5f03bb2530f822987 -Author: dtucker@openbsd.org -Date: Wed May 6 05:45:17 2015 +0000 +commit 846f6fa4cfa8483a9195971dbdd162220f199d85 +Author: jmc@openbsd.org +Date: Fri Sep 11 06:55:46 2015 +0000 upstream commit - Clarify pseudo-terminal request behaviour and use - "pseudo-terminal" consistently. bz#1716, ok jmc@ "I like it" deraadt@. + sync -Q in usage() to SYNOPSIS; since it's drastically + shorter, i've reformatted the block to sync with the man (80 cols) and saved + a line; + + Upstream-ID: 86e2c65c3989a0777a6258a77e589b9f6f354abd -commit ea139507bef8bad26e86ed99a42c7233ad115c38 -Author: dtucker@openbsd.org -Date: Wed May 6 04:07:18 2015 +0000 +commit 95923e0520a8647417ee6dcdff44694703dfeef0 +Author: jmc@openbsd.org +Date: Fri Sep 11 06:51:39 2015 +0000 upstream commit - Blacklist DH-GEX for specific PuTTY versions known to - send non-RFC4419 DH-GEX messages rather than all versions of PuTTY. - According to Simon Tatham, 0.65 and newer versions will send RFC4419 DH-GEX - messages. ok djm@ + tweak previous; + + Upstream-ID: f29b3cfcfd9aa31fa140c393e7bd48c1c74139d6 -commit b58234f00ee3872eb84f6e9e572a9a34e902e36e +commit 86ac462f833b05d8ed9de9c50ccb295d7faa79ff Author: dtucker@openbsd.org -Date: Tue May 5 10:17:49 2015 +0000 +Date: Fri Sep 11 05:27:02 2015 +0000 upstream commit - WinSCP doesn't implement RFC4419 DH-GEX so flag it so we - don't offer that KEX method. ok markus@ + Update usage to match man page. + + Upstream-ID: 9e85aefaecfb6aaf34c7cfd0700cd21783a35675 -commit d5b1507a207253b39e810e91e68f9598691b7a29 -Author: jsg@openbsd.org -Date: Tue May 5 02:48:17 2015 +0000 +commit 674b3b68c1d36b2562324927cd03857b565e05e8 +Author: djm@openbsd.org +Date: Fri Sep 11 03:47:28 2015 +0000 upstream commit - use the sizeof the struct not the sizeof a pointer to the - struct in ssh_digest_start() - - This file is only used if ssh is built with OPENSSL=no + expand %i in ControlPath to UID; bz#2449 - ok markus@ - -commit a647b9b8e616c231594b2710c925d31b1b8afea3 -Author: Darren Tucker -Date: Fri May 8 11:07:27 2015 +1000 - - Put brackets around mblen() compat constant. + patch from Christian Hesse w/ feedback from dtucker@ - This might help with the reported problem cross compiling for Android - ("error: expected identifier or '(' before numeric constant") but - shouldn't hurt in any case. - -commit d1680d36e17244d9af3843aeb5025cb8e40d6c07 -Author: Darren Tucker -Date: Thu Apr 30 09:18:11 2015 +1000 - - xrealloc -> xreallocarray in portable code too. + Upstream-ID: 2ba8d303e555a84e2f2165ab4b324b41e80ab925 -commit 531a57a3893f9fcd4aaaba8c312b612bbbcc021e -Author: dtucker@openbsd.org -Date: Wed Apr 29 03:48:56 2015 +0000 +commit c0f55db7ee00c8202b05cb4b9ad4ce72cc45df41 +Author: djm@openbsd.org +Date: Fri Sep 11 03:42:32 2015 +0000 upstream commit - Allow ListenAddress, Port and AddressFamily in any - order. bz#68, ok djm@, jmc@ (for the man page bit). - -commit c1d5bcf1aaf1209af02f79e48ba1cbc76a87b56f -Author: jmc@openbsd.org -Date: Tue Apr 28 13:47:38 2015 +0000 - - upstream commit + mention -Q key-plain and -Q key-cert; bz#2455 pointed out + by Jakub Jelen - enviroment -> environment: apologies to darren for not - spotting that first time round... + Upstream-ID: c8f1f8169332e4fa73ac96b0043e3b84e01d4896 -commit 43beea053db191cac47c2cd8d3dc1930158aff1a -Author: dtucker@openbsd.org -Date: Tue Apr 28 10:25:15 2015 +0000 +commit cfffbdb10fdf0f02d3f4232232eef7ec3876c383 +Author: Darren Tucker +Date: Mon Sep 14 16:24:21 2015 +1000 - upstream commit + Use ssh-keygen -A when generating host keys. - Fix typo in previous + Use ssh-keygen -A instead of per-keytype invocations when generating host + keys. Add tests when doing host-key-force since we can't use ssh-keygen -A + since it can't specify alternate locations. bz#2459, ok djm@ -commit 85b96ef41374f3ddc9139581f87da09b2cd9199e -Author: dtucker@openbsd.org -Date: Tue Apr 28 10:17:58 2015 +0000 +commit 366bada1e9e124654aac55b72b6ccf878755b0dc +Author: Darren Tucker +Date: Fri Sep 11 13:29:22 2015 +1000 - upstream commit + Correct default value for --with-ssh1. - Document that the TERM environment variable is not - subject to SendEnv and AcceptEnv. bz#2386, based loosely on a patch from - jjelen at redhat, help and ok jmc@ + bz#2457, from konto-mindrot.org at walimnieto.com. -commit 88a7c598a94ff53f76df228eeaae238d2d467565 +commit 2bca8a43e7dd9b04d7070824ffebb823c72587b2 Author: djm@openbsd.org -Date: Mon Apr 27 21:42:48 2015 +0000 +Date: Fri Sep 11 03:13:36 2015 +0000 upstream commit - Make sshd default to PermitRootLogin=no; ok deraadt@ - rpe@ + more clarity on what AuthorizedKeysFile=none does; based + on diff by Thiebaud Weksteen + + Upstream-ID: 78ab87f069080f0cc3bc353bb04eddd9e8ad3704 -commit 734226b4480a6c736096c729fcf6f391400599c7 +commit 61942ea4a01e6db4fdf37ad61de81312ffe310e9 Author: djm@openbsd.org -Date: Mon Apr 27 01:52:30 2015 +0000 +Date: Wed Sep 9 00:52:44 2015 +0000 upstream commit - fix compilation with OPENSSL=no; ok dtucker@ + openssh_RSA_verify return type is int, so don't make it + size_t within the function itself with only negative numbers or zero assigned + to it. bz#2460 + + Upstream-ID: b6e794b0c7fc4f9f329509263c8668d35f83ea55 -commit a4b9d2ce1eb7703eaf0809b0c8a82ded8aa4f1c6 +commit 4f7cc2f8cc861a21e6dbd7f6c25652afb38b9b96 Author: dtucker@openbsd.org -Date: Mon Apr 27 00:37:53 2015 +0000 +Date: Fri Sep 4 08:21:47 2015 +0000 upstream commit - Include stdio.h for FILE (used in sshkey.h) so it - compiles with OPENSSL=no. + Plug minor memory leaks when options are used more than + once. bz#2182, patch from Tiago Cunha, ok deraadt djm + + Upstream-ID: 5b84d0401e27fe1614c10997010cc55933adb48e -commit dbcc652f4ca11fe04e5930c7ef18a219318c6cda -Author: djm@openbsd.org -Date: Mon Apr 27 00:21:21 2015 +0000 +commit 7ad8b287c8453a3e61dbc0d34d467632b8b06fc8 +Author: Darren Tucker +Date: Fri Sep 11 13:11:02 2015 +1000 - upstream commit + Force resolution of _res for correct detection. - allow "sshd -f none" to skip reading the config file, - much like "ssh -F none" does. ok dtucker + bz#2259, from sconeu at yahoo.com. -commit b7ca276fca316c952f0b90f5adb1448c8481eedc +commit 26ad18247213ff72b4438abe7fc660c958810fa2 +Author: Damien Miller +Date: Thu Sep 10 10:57:41 2015 +1000 + + allow getrandom syscall; from Felix von Leitner + +commit 5245bc1e6b129a10a928f73f11c3aa32656c44b4 Author: jmc@openbsd.org -Date: Fri Apr 24 06:26:49 2015 +0000 +Date: Fri Sep 4 06:40:45 2015 +0000 upstream commit - combine -Dd onto one line and update usage(); + full stop belongs outside the brackets, not inside; + + Upstream-ID: 99d098287767799ac33d2442a05b5053fa5a551a -commit 2ea974630d7017e4c7666d14d9dc939707613e96 +commit a85768a9321d74b41219eeb3c9be9f1702cbf6a5 Author: djm@openbsd.org -Date: Fri Apr 24 05:26:44 2015 +0000 +Date: Fri Sep 4 04:56:09 2015 +0000 upstream commit - add ssh-agent -D to leave ssh-agent in foreground - without enabling debug mode; bz#2381 ok dtucker@ + add a debug2() right before DNS resolution; it's a place + where ssh could previously silently hang for a while. bz#2433 + + Upstream-ID: 52a1a3e0748db66518e7598352c427145692a6a0 -commit 8ac2ffd7aa06042f6b924c87139f2fea5c5682f7 -Author: deraadt@openbsd.org -Date: Fri Apr 24 01:36:24 2015 +0000 +commit 46152af8d27aa34d5d26ed1c371dc8aa142d4730 +Author: djm@openbsd.org +Date: Fri Sep 4 04:55:24 2015 +0000 upstream commit - 2*len -> use xreallocarray() ok djm + correct function name in error messages + + Upstream-ID: 92fb2798617ad9561370897f4ab60adef2ff4c0e -commit 657a5fbc0d0aff309079ff8fb386f17e964963c2 -Author: deraadt@openbsd.org -Date: Fri Apr 24 01:36:00 2015 +0000 +commit a954cdb799a4d83c2d40fbf3e7b9f187fbfd72fc +Author: djm@openbsd.org +Date: Fri Sep 4 04:47:50 2015 +0000 upstream commit - rename xrealloc() to xreallocarray() since it follows - that form. ok djm + better document ExitOnForwardFailure; bz#2444, ok + dtucker@ + + Upstream-ID: a126209b5a6d9cb3117ac7ab5bc63d284538bfc2 -commit 1108ae242fdd2c304307b68ddf46aebe43ebffaa -Author: dtucker@openbsd.org -Date: Thu Apr 23 04:59:10 2015 +0000 +commit f54d8ac2474b6fc3afa081cf759b48a6c89d3319 +Author: djm@openbsd.org +Date: Fri Sep 4 04:44:08 2015 +0000 upstream commit - Two small fixes for sshd -T: ListenAddress'es are added - to a list head so reverse the order when printing them to ensure the - behaviour remains the same, and print StreamLocalBindMask as octal with - leading zero. ok deraadt@ + don't record hostbased authentication hostkeys as user + keys in test for multiple authentication with the same key + + Upstream-ID: 26b368fa2cff481f47f37e01b8da1ae5b57b1adc -commit bd902b8473e1168f19378d5d0ae68d0c203525df -Author: dtucker@openbsd.org -Date: Thu Apr 23 04:53:53 2015 +0000 +commit ac3451dd65f27ecf85dc045c46d49e2bbcb8dddd +Author: djm@openbsd.org +Date: Fri Sep 4 03:57:38 2015 +0000 upstream commit - Check for and reject missing arguments for - VersionAddendum and ForceCommand. bz#2281, patch from plautrba at redhat com, - ok djm@ + remove extra newline in nethack-mode hostkey; from + Christian Hesse bz#2686 + + Upstream-ID: 4f56368b1cc47baeea0531912186f66007fd5b92 -commit ca42c1758575e592239de1d5755140e054b91a0d +commit 9e3ed9ebb1a7e47c155c28399ddf09b306ea05df Author: djm@openbsd.org -Date: Wed Apr 22 01:24:01 2015 +0000 +Date: Fri Sep 4 04:23:10 2015 +0000 upstream commit - unknown certificate extensions are non-fatal, so don't - fatal when they are encountered; bz#2387 reported by Bob Van Zant; ok - dtucker@ + trim junk from end of file; bz#2455 from Jakub Jelen + + Upstream-Regress-ID: a4e64e8931e40d23874b047074444eff919cdfe6 -commit 39bfbf7caad231cc4bda6909fb1af0705bca04d8 +commit f3a3ea180afff080bab82087ee0b60db9fd84f6c Author: jsg@openbsd.org -Date: Tue Apr 21 07:01:00 2015 +0000 +Date: Wed Sep 2 07:51:12 2015 +0000 upstream commit - Add back a backslash removed in rev 1.42 so - KEX_SERVER_ENCRYPT will include aes again. + Fix occurrences of "r = func() != 0" which result in the + wrong error codes being returned due to != having higher precedence than =. - ok deraadt@ - -commit 6b0d576bb87eca3efd2b309fcfe4edfefc289f9c -Author: djm@openbsd.org -Date: Fri Apr 17 13:32:09 2015 +0000 - - upstream commit + ok deraadt@ markus@ - s/recommended/required/ that private keys be og-r this - wording change was made a while ago but got accidentally reverted + Upstream-ID: 5fc35c9fc0319cc6fca243632662d2f06b5fd840 -commit 44a8e7ce6f3ab4c2eb1ae49115c210b98e53c4df -Author: djm@openbsd.org -Date: Fri Apr 17 13:25:52 2015 +0000 +commit f498a98cf83feeb7ea01c15cd1c98b3111361f3a +Author: Damien Miller +Date: Thu Sep 3 09:11:22 2015 +1000 - upstream commit - - don't try to cleanup NULL KEX proposals in - kex_prop_free(); found by Jukka Taimisto and Markus Hietava + don't check for yp_match; ok tim@ -commit 3038a191872d2882052306098c1810d14835e704 +commit 9690b78b7848b0b376980a61d51b1613e187ddb5 Author: djm@openbsd.org -Date: Fri Apr 17 13:19:22 2015 +0000 +Date: Fri Aug 21 23:57:48 2015 +0000 upstream commit - use error/logit/fatal instead of fprintf(stderr, ...) - and exit(0), fix a few errors that were being printed to stdout instead of - stderr and a few non-errors that were going to stderr instead of stdout - bz#2325; ok dtucker + Improve printing of KEX offers and decisions + + The debug output now labels the client and server offers and the + negotiated options. ok markus@ + + Upstream-ID: 8db921b3f92a4565271b1c1fbce6e7f508e1a2cb -commit a58be33cb6cd24441fa7e634db0e5babdd56f07f +commit 60a92470e21340e1a3fc10f9c7140d8e1519dc55 Author: djm@openbsd.org -Date: Fri Apr 17 13:16:48 2015 +0000 +Date: Fri Aug 21 23:53:08 2015 +0000 upstream commit - debug log missing DISPLAY environment when X11 - forwarding requested; bz#1682 ok dtucker@ + Fix printing (ssh -G ...) of HostKeyAlgorithms=+... + Reported by Bryan Drewery + + Upstream-ID: 19ad20c41bd5971e006289b6f9af829dd46c1293 -commit 17d4d9d9fbc8fb80e322f94d95eecc604588a474 +commit 6310f60fffca2d1e464168e7d1f7e3b6b0268897 Author: djm@openbsd.org -Date: Fri Apr 17 04:32:31 2015 +0000 +Date: Fri Aug 21 23:52:30 2015 +0000 upstream commit - don't call record_login() in monitor when UseLogin is - enabled; bz#278 reported by drk AT sgi.com; ok dtucker - -commit 40132ff87b6cbc3dc05fb5df2e9d8e3afa06aafd -Author: dtucker@openbsd.org -Date: Fri Apr 17 04:12:35 2015 +0000 - - upstream commit + Fix expansion of HostkeyAlgorithms=+... - Add some missing options to sshd -T and fix the output - of VersionAddendum HostCertificate. bz#2346, patch from jjelen at redhat - com, ok djm. + Reported by Bryan Drewery + + Upstream-ID: 70ca1deea39d758ba36d36428ae832e28566f78d -commit 6cc7cfa936afde2d829e56ee6528c7ea47a42441 -Author: dtucker@openbsd.org -Date: Thu Apr 16 23:25:50 2015 +0000 +commit e774e5ea56237fd626a8161f9005023dff3e76c9 +Author: deraadt@openbsd.org +Date: Fri Aug 21 23:29:31 2015 +0000 upstream commit - Document "none" for PidFile XAuthLocation - TrustedUserCAKeys and RevokedKeys. bz#2382, feedback from jmc@, ok djm@ + Improve size == 0, count == 0 checking in mm_zalloc, + which is "array" like. Discussed with tedu, millert, otto.... and ok djm + + Upstream-ID: 899b021be43b913fad3eca1aef44efe710c53e29 -commit 15fdfc9b1c6808b26bc54d4d61a38b54541763ed -Author: dtucker@openbsd.org -Date: Wed Apr 15 23:23:25 2015 +0000 +commit 189de02d9ad6f3645417c0ddf359b923aae5f926 +Author: Damien Miller +Date: Fri Aug 21 15:45:02 2015 +1000 - upstream commit - - Plug leak of address passed to logging. bz#2373, patch - from jjelen at redhat, ok markus@ + expose POLLHUP and POLLNVAL for netcat.c -commit bb2289e2a47d465eaaaeff3dee2a6b7777b4c291 -Author: dtucker@openbsd.org -Date: Tue Apr 14 04:17:03 2015 +0000 +commit e91346dc2bbf460246df2ab591b7613908c1b0ad +Author: Damien Miller +Date: Fri Aug 21 14:49:03 2015 +1000 - upstream commit - - Output remote username in debug output since with Host - and Match it's not always obvious what it will be. bz#2368, ok djm@ + we don't use Github for issues/pull-requests -commit 70860b6d07461906730632f9758ff1b7c98c695a -Author: Darren Tucker -Date: Fri Apr 17 10:56:13 2015 +1000 +commit a4f5b507c708cc3dc2c8dd2d02e4416d7514dc23 +Author: Damien Miller +Date: Fri Aug 21 14:43:55 2015 +1000 - Format UsePAM setting when using sshd -T. - - Part of bz#2346, patch from jjelen at redhat com. + fix URL for connect.c -commit ee15d9c9f0720f5a8b0b34e4b10ecf21f9824814 -Author: Darren Tucker -Date: Fri Apr 17 10:40:23 2015 +1000 +commit d026a8d3da0f8186598442997c7d0a28e7275414 +Author: Damien Miller +Date: Fri Aug 21 13:47:10 2015 +1000 - Wrap endian.h include inside ifdef (bz#2370). + update version numbers for 7.1 -commit 408f4c2ad4a4c41baa7b9b2b7423d875abbfa70b -Author: Darren Tucker -Date: Fri Apr 17 09:39:58 2015 +1000 +commit 78f8f589f0ca1c9f41e5a9bae3cda5ce8a6b42ed +Author: djm@openbsd.org +Date: Fri Aug 21 03:45:26 2015 +0000 - Look for '${host}-ar' before 'ar'. + upstream commit - This changes configure.ac to look for '${host}-ar' as set by - AC_CANONICAL_HOST before looking for the unprefixed 'ar'. - Useful when cross-compiling when all your binutils are prefixed. + openssh-7.1 - Patch from moben at exherbo org via astrand at lysator liu se and - bz#2352. + Upstream-ID: ff7b1ef4b06caddfb45e08ba998128c88be3d73f -commit 673a1c16ad078d41558247ce739fe812c960acc8 -Author: Damien Miller -Date: Thu Apr 16 11:40:20 2015 +1000 +commit 32a181980c62fce94f7f9ffaf6a79d90f0c309cf +Author: djm@openbsd.org +Date: Fri Aug 21 03:42:19 2015 +0000 - remove dependency on arpa/telnet.h + upstream commit + + fix inverted logic that broke PermitRootLogin; reported + by Mantas Mikulenas; ok markus@ + + Upstream-ID: 260dd6a904c1bb7e43267e394b1c9cf70bdd5ea5 -commit 202d443eeda1829d336595a3cfc07827e49f45ed -Author: Darren Tucker -Date: Wed Apr 15 15:59:49 2015 +1000 +commit ce445b0ed927e45bd5bdce8f836eb353998dd65c +Author: deraadt@openbsd.org +Date: Thu Aug 20 22:32:42 2015 +0000 - Remove duplicate include of pwd.h. bz#2337, patch from Mordy Ovits. + upstream commit + + Do not cast result of malloc/calloc/realloc* if stdlib.h + is in scope ok krw millert + + Upstream-ID: 5e50ded78cadf3841556649a16cc4b1cb6c58667 -commit 597986493412c499f2bc2209420cb195f97b3668 -Author: Damien Miller -Date: Thu Apr 9 10:14:48 2015 +1000 +commit 05291e5288704d1a98bacda269eb5a0153599146 +Author: naddy@openbsd.org +Date: Thu Aug 20 19:20:06 2015 +0000 - platform's with openpty don't need pty_release + upstream commit + + In the certificates section, be consistent about using + "host_key" and "user_key" for the respective key types. ok sthen@ deraadt@ + + Upstream-ID: 9e037ea3b15577b238604c5533e082a3947f13cb -commit 318be28cda1fd9108f2e6f2f86b0b7589ba2aed0 +commit 8543d4ef6f2e9f98c3e6b77c894ceec30c5e4ae4 Author: djm@openbsd.org -Date: Mon Apr 13 02:04:08 2015 +0000 +Date: Wed Aug 19 23:21:42 2015 +0000 upstream commit - deprecate ancient, pre-RFC4419 and undocumented - SSH2_MSG_KEX_DH_GEX_REQUEST_OLD message; ok markus@ deraadt@ "seems - reasonable" dtucker@ + Better compat matching for WinSCP, add compat matching + for FuTTY (fork of PuTTY); ok markus@ deraadt@ + + Upstream-ID: 24001d1ac115fa3260fbdc329a4b9aeb283c5389 -commit d8f391caef62378463a0e6b36f940170dadfe605 -Author: dtucker@openbsd.org -Date: Fri Apr 10 05:16:50 2015 +0000 +commit ec6eda16ebab771aa3dfc90629b41953b999cb1e +Author: djm@openbsd.org +Date: Wed Aug 19 23:19:01 2015 +0000 upstream commit - Don't send hostkey advertisments - (hostkeys-00@openssh.com) to current versions of Tera Term as they can't - handle them. Newer versions should be OK. Patch from Bryan Drewery and - IWAMOTO Kouichi, ok djm@ + fix double-free() in error path of DSA key generation + reported by Mateusz Kocielski; ok markus@ + + Upstream-ID: 4735d8f888b10599a935fa1b374787089116713c -commit 2c2cfe1a1c97eb9a08cc9817fd0678209680c636 +commit 45b0eb752c94954a6de046bfaaf129e518ad4b5b Author: djm@openbsd.org -Date: Fri Apr 10 00:08:55 2015 +0000 +Date: Wed Aug 19 23:18:26 2015 +0000 upstream commit - include port number if a non-default one has been - specified; based on patch from Michael Handler + fix free() of uninitialised pointer reported by Mateusz + Kocielski; ok markus@ + + Upstream-ID: 519552b050618501a06b7b023de5cb104e2c5663 -commit 4492a4f222da4cf1e8eab12689196322e27b08c4 +commit c837643b93509a3ef538cb6624b678c5fe32ff79 Author: djm@openbsd.org -Date: Tue Apr 7 23:00:42 2015 +0000 +Date: Wed Aug 19 23:17:51 2015 +0000 upstream commit - treat Protocol=1,2|2,1 as Protocol=2 when compiled - without SSH1 support; ok dtucker@ millert@ + fixed unlink([uninitialised memory]) reported by Mateusz + Kocielski; ok markus@ + + Upstream-ID: 14a0c4e7d891f5a8dabc4b89d4f6b7c0d5a20109 -commit c265e2e6e932efc6d86f6cc885dea33637a67564 -Author: miod@openbsd.org -Date: Sun Apr 5 15:43:43 2015 +0000 +commit 1f8d3d629cd553031021068eb9c646a5f1e50994 +Author: jmc@openbsd.org +Date: Fri Aug 14 15:32:41 2015 +0000 upstream commit - Do not use int for sig_atomic_t; spotted by - christos@netbsd; ok markus@ + match myproposal.h order; from brian conway (i snuck in a + tweak while here) + + ok dtucker + + Upstream-ID: 35174a19b5237ea36aa3798f042bf5933b772c67 -commit e7bf3a5eda6a1b02bef6096fed78527ee11e54cc -Author: Darren Tucker -Date: Tue Apr 7 10:48:04 2015 +1000 +commit 1dc8d93ce69d6565747eb44446ed117187621b26 +Author: deraadt@openbsd.org +Date: Thu Aug 6 14:53:21 2015 +0000 - Use do{}while(0) for no-op functions. + upstream commit - From FreeBSD. + add prohibit-password as a synonymn for without-password, + since the without-password is causing too many questions. Harden it to ban + all but pubkey, hostbased, and GSSAPI auth (when the latter is enabled) from + djm, ok markus + + Upstream-ID: d53317d7b28942153e6236d3fd6e12ceb482db7a -commit bb99844abae2b6447272f79e7fa84134802eb4df -Author: Darren Tucker -Date: Tue Apr 7 10:47:15 2015 +1000 +commit 90a95a4745a531b62b81ce3b025e892bdc434de5 +Author: Damien Miller +Date: Tue Aug 11 13:53:41 2015 +1000 - Wrap blf.h include in ifdef. From FreeBSD. + update version in README -commit d9b9b43656091cf0ad55c122f08fadb07dad0abd -Author: Darren Tucker -Date: Tue Apr 7 09:10:00 2015 +1000 +commit 318c37743534b58124f1bab37a8a0087a3a9bd2f +Author: Damien Miller +Date: Tue Aug 11 13:53:09 2015 +1000 - Fix misspellings of regress CONFOPTS env variables. - - Patch from Bryan Drewery. + update versions in *.spec -commit 3f4ea3c9ab1d32d43c9222c4351f58ca11144156 -Author: djm@openbsd.org -Date: Fri Apr 3 22:17:27 2015 +0000 +commit 5e75f5198769056089fb06c4d738ab0e5abc66f7 +Author: Damien Miller +Date: Tue Aug 11 13:34:12 2015 +1000 - upstream commit + set sshpam_ctxt to NULL after free - correct return value in pubkey parsing, spotted by Ben Hawkes - ok markus@ + Avoids use-after-free in monitor when privsep child is compromised. + Reported by Moritz Jodeit; ok dtucker@ -commit 7da2be0cb9601ed25460c83aa4d44052b967ba0f -Author: djm@openbsd.org -Date: Tue Mar 31 22:59:01 2015 +0000 +commit d4697fe9a28dab7255c60433e4dd23cf7fce8a8b +Author: Damien Miller +Date: Tue Aug 11 13:33:24 2015 +1000 - upstream commit + Don't resend username to PAM; it already has it. - adapt to recent hostfile.c change: when parsing - known_hosts without fully parsing the keys therein, hostkeys_foreach() will - now correctly identify KEY_RSA1 keys; ok markus@ miod@ + Pointed out by Moritz Jodeit; ok dtucker@ -commit 9e1777a0d1c706714b055811c12ab8cc21033e4a -Author: markus@openbsd.org -Date: Tue Mar 24 20:19:15 2015 +0000 +commit 88763a6c893bf3dfe951ba9271bf09715e8d91ca +Author: Darren Tucker +Date: Mon Jul 27 12:14:25 2015 +1000 - upstream commit - - use ${SSH} for -Q instead of installed ssh + Import updated moduli file from OpenBSD. -commit ce1b358ea414a2cc88e4430cd5a2ea7fecd9de57 -Author: djm@openbsd.org -Date: Mon Mar 16 22:46:14 2015 +0000 +commit 55b263fb7cfeacb81aaf1c2036e0394c881637da +Author: Damien Miller +Date: Mon Aug 10 11:13:44 2015 +1000 - upstream commit - - make CLEANFILES clean up more of the tests' droppings + let principals-command.sh work for noexec /var/run -commit 398f9ef192d820b67beba01ec234d66faca65775 +commit 2651e34cd11b1aac3a0fe23b86d8c2ff35c07897 +Author: Damien Miller +Date: Thu Aug 6 11:43:42 2015 +1000 + + work around echo -n / sed behaviour in tests + +commit d85dad81778c1aa8106acd46930b25fdf0d15b2a Author: djm@openbsd.org -Date: Tue Mar 31 22:57:06 2015 +0000 +Date: Wed Aug 5 05:27:33 2015 +0000 upstream commit - downgrade error() for known_hosts parse errors to debug() - to quiet warnings from ssh1 keys present when compiled !ssh1. - - also identify ssh1 keys when scanning, even when compiled !ssh1 + adjust for RSA minimum modulus switch; ok deraadt@ - ok markus@ miod@ + Upstream-Regress-ID: 5a72c83431b96224d583c573ca281cd3a3ebfdae -commit 9a47ab80030a31f2d122b8fd95bd48c408b9fcd9 +commit 57e8e229bad5fe6056b5f1199665f5f7008192c6 Author: djm@openbsd.org -Date: Tue Mar 31 22:55:50 2015 +0000 +Date: Tue Aug 4 05:23:06 2015 +0000 upstream commit - fd leak for !ssh1 case; found by unittests; ok markus@ + backout SSH_RSA_MINIMUM_MODULUS_SIZE increase for this + release; problems spotted by sthen@ ok deraadt@ markus@ + + Upstream-ID: d0bd60dde9e8c3cd7030007680371894c1499822 -commit c9a0805a6280681901c270755a7cd630d7c5280e +commit f097d0ea1e0889ca0fa2e53a00214e43ab7fa22a Author: djm@openbsd.org -Date: Tue Mar 31 22:55:24 2015 +0000 +Date: Sun Aug 2 09:56:42 2015 +0000 upstream commit - don't fatal when a !ssh1 sshd is reexeced from a w/ssh1 - listener; reported by miod@; ok miod@ markus@ + openssh 7.0; ok deraadt@ + + Upstream-ID: c63afdef537f57f28ae84145c5a8e29e9250221f -commit 704d8c88988cae38fb755a6243b119731d223222 -Author: tobias@openbsd.org -Date: Tue Mar 31 11:06:49 2015 +0000 +commit 3d5728a0f6874ce4efb16913a12963595070f3a9 +Author: chris@openbsd.org +Date: Fri Jul 31 15:38:09 2015 +0000 upstream commit - Comments are only supported for RSA1 keys. If a user - tried to add one and entered his passphrase, explicitly clear it before exit. - This is done in all other error paths, too. + Allow PermitRootLogin to be overridden by config - ok djm + ok markus@ deeradt@ + + Upstream-ID: 5cf3e26ed702888de84e2dc9d0054ccf4d9125b4 -commit 78de1673c05ea2c33e0d4a4b64ecb5186b6ea2e9 -Author: jmc@openbsd.org -Date: Mon Mar 30 18:28:37 2015 +0000 +commit 6f941396b6835ad18018845f515b0c4fe20be21a +Author: djm@openbsd.org +Date: Thu Jul 30 23:09:15 2015 +0000 upstream commit - ssh-askpass(1) is the default, overridden by SSH_ASKPASS; - diff originally from jiri b; + fix pty permissions; patch from Nikolay Edigaryev; ok + deraadt + + Upstream-ID: 40ff076d2878b916fbfd8e4f45dbe5bec019e550 -commit 26e0bcf766fadb4a44fb6199386fb1dcab65ad00 -Author: djm@openbsd.org -Date: Mon Mar 30 00:00:29 2015 +0000 +commit f4373ed1e8fbc7c8ce3fc4ea97d0ba2e0c1d7ef0 +Author: deraadt@openbsd.org +Date: Thu Jul 30 19:23:02 2015 +0000 upstream commit - fix uninitialised memory read when parsing a config file - consisting of a single nul byte. Found by hanno AT hboeck.de using AFL; ok - dtucker + change default: PermitRootLogin without-password matching + install script changes coming as well ok djm markus + + Upstream-ID: 0e2a6c4441daf5498b47a61767382bead5eb8ea6 -commit fecede00a76fbb33a349f5121c0b2f9fbc04a777 -Author: markus@openbsd.org -Date: Thu Mar 26 19:32:19 2015 +0000 +commit 0c30ba91f87fcda7e975e6ff8a057f624e87ea1c +Author: Damien Miller +Date: Thu Jul 30 12:31:39 2015 +1000 + + downgrade OOM adjustment logging: verbose -> debug + +commit f9eca249d4961f28ae4b09186d7dc91de74b5895 +Author: djm@openbsd.org +Date: Thu Jul 30 00:01:34 2015 +0000 upstream commit - sigp and lenp are not optional in ssh_agent_sign(); ok - djm@ + Allow ssh_config and sshd_config kex parameters options be + prefixed by a '+' to indicate that the specified items be appended to the + default rather than replacing it. + + approach suggested by dtucker@, feedback dlg@, ok markus@ + + Upstream-ID: 0f901137298fc17095d5756ff1561a7028e8882a -commit 1b0ef3813244c78669e6d4d54c624f600945327d -Author: naddy@openbsd.org -Date: Thu Mar 26 12:32:38 2015 +0000 +commit 5cefe769105a2a2e3ca7479d28d9a325d5ef0163 +Author: djm@openbsd.org +Date: Wed Jul 29 08:34:54 2015 +0000 upstream commit - don't try to load .ssh/identity by default if SSH1 is - disabled; ok markus@ + fix bug in previous; was printing incorrect string for + failed host key algorithms negotiation + + Upstream-ID: 22c0dc6bc61930513065d92e11f0753adc4c6e6e -commit f9b78852379b74a2d14e6fc94fe52af30b7e9c31 +commit f319912b0d0e1675b8bb051ed8213792c788bcb2 Author: djm@openbsd.org -Date: Thu Mar 26 07:00:04 2015 +0000 +Date: Wed Jul 29 04:43:06 2015 +0000 upstream commit - ban all-zero curve25519 keys as recommended by latest - CFRG curves draft; ok markus + include the peer's offer when logging a failure to + negotiate a mutual set of algorithms (kex, pubkey, ciphers, etc.) ok markus@ + + Upstream-ID: bbb8caabf5c01790bb845f5ce135565248d7c796 -commit b8afbe2c1aaf573565e4da775261dfafc8b1ba9c +commit b6ea0e573042eb85d84defb19227c89eb74cf05a Author: djm@openbsd.org -Date: Thu Mar 26 06:59:28 2015 +0000 +Date: Tue Jul 28 23:20:42 2015 +0000 upstream commit - relax bits needed check to allow - diffie-hellman-group1-sha1 key exchange to complete for chacha20-poly1305 was - selected as symmetric cipher; ok markus + add Cisco to the list of clients that choke on the + hostkeys update extension. Pointed out by Howard Kash + + Upstream-ID: c9eadde28ecec056c73d09ee10ba4570dfba7e84 -commit 47842f71e31da130555353c1d57a1e5a8937f1c0 -Author: markus@openbsd.org -Date: Wed Mar 25 19:29:58 2015 +0000 +commit 3f628c7b537291c1019ce86af90756fb4e66d0fd +Author: guenther@openbsd.org +Date: Mon Jul 27 16:29:23 2015 +0000 upstream commit - ignore v1 errors on ssh-add -D; only try v2 keys on - -l/-L (unless WITH_SSH1) ok djm@ + Permit kbind(2) use in the sandbox now, to ease testing + of ld.so work using it + + reminded by miod@, ok deraadt@ + + Upstream-ID: 523922e4d1ba7a091e3824e77a8a3c818ee97413 -commit 5f57e77f91bf2230c09eca96eb5ecec39e5f2da6 -Author: markus@openbsd.org -Date: Wed Mar 25 19:21:48 2015 +0000 +commit ebe27ebe520098bbc0fe58945a87ce8490121edb +Author: millert@openbsd.org +Date: Mon Jul 20 18:44:12 2015 +0000 upstream commit - unbreak ssh_agent_sign (lenp vs *lenp) + Move .Pp before .Bl, not after to quiet mandoc -Tlint. + Noticed by jmc@ + + Upstream-ID: 59fadbf8407cec4e6931e50c53cfa0214a848e23 -commit 4daeb67181054f2a377677fac919ee8f9ed3490e -Author: markus@openbsd.org -Date: Tue Mar 24 20:10:08 2015 +0000 +commit d5d91d0da819611167782c66ab629159169d94d4 +Author: millert@openbsd.org +Date: Mon Jul 20 18:42:35 2015 +0000 upstream commit - don't leak 'setp' on error; noted by Nicholas Lemonias; - ok djm@ + Sync usage with SYNOPSIS + + Upstream-ID: 7a321a170181a54f6450deabaccb6ef60cf3f0b7 -commit 7d4f96f9de2a18af0d9fa75ea89a4990de0344f5 -Author: markus@openbsd.org -Date: Tue Mar 24 20:09:11 2015 +0000 +commit 79ec2142fbc68dd2ed9688608da355fc0b1ed743 +Author: millert@openbsd.org +Date: Mon Jul 20 15:39:52 2015 +0000 upstream commit - consistent check for NULL as noted by Nicholas - Lemonias; ok djm@ + Better desciption of Unix domain socket forwarding. + bz#2423; ok jmc@ + + Upstream-ID: 85e28874726897e3f26ae50dfa2e8d2de683805d -commit df100be51354e447d9345cf1ec22e6013c0eed50 -Author: markus@openbsd.org -Date: Tue Mar 24 20:03:44 2015 +0000 +commit d56fd1828074a4031b18b8faa0bf949669eb18a0 +Author: Damien Miller +Date: Mon Jul 20 11:19:51 2015 +1000 - upstream commit - - correct fmt-string for size_t as noted by Nicholas - Lemonias; ok djm@ + make realpath.c compile -Wsign-compare clean -commit a22b9ef21285e81775732436f7c84a27bd3f71e0 +commit c63c9a691dca26bb7648827f5a13668832948929 Author: djm@openbsd.org -Date: Tue Mar 24 09:17:21 2015 +0000 +Date: Mon Jul 20 00:30:01 2015 +0000 upstream commit - promote chacha20-poly1305@openssh.com to be the default - cipher; ok markus + mention that the default of UseDNS=no implies that + hostnames cannot be used for host matching in sshd_config and + authorized_keys; bz#2045, ok dtucker@ + + Upstream-ID: 0812705d5f2dfa59aab01f2764ee800b1741c4e1 -commit 2aa9da1a3b360cf7b13e96fe1521534b91501fb5 +commit 63ebcd0005e9894fcd6871b7b80aeea1fec0ff76 Author: djm@openbsd.org -Date: Tue Mar 24 01:29:19 2015 +0000 +Date: Sat Jul 18 08:02:17 2015 +0000 upstream commit - Compile-time disable SSH protocol 1. You can turn it - back on using the Makefile.inc knob if you need it to talk to ancient - devices. + don't ignore PKCS#11 hosted keys that return empty + CKA_ID; patch by Jakub Jelen via bz#2429; ok markus + + Upstream-ID: 2f7c94744eb0342f8ee8bf97b2351d4e00116485 -commit 53097b2022154edf96b4e8526af5666f979503f7 +commit b15fd989c8c62074397160147a8d5bc34b3f3c63 Author: djm@openbsd.org -Date: Tue Mar 24 01:11:12 2015 +0000 +Date: Sat Jul 18 08:00:21 2015 +0000 upstream commit - fix double-negative error message "ssh1 is not - unsupported" + skip uninitialised PKCS#11 slots; patch from Jakub Jelen + in bz#2427 ok markus@ + + Upstream-ID: 744c1e7796e237ad32992d0d02148e8a18f27d29 -commit 5c27e3b6ec2db711dfcd40e6359c0bcdd0b62ea9 +commit 5b64f85bb811246c59ebab70aed331f26ba37b18 Author: djm@openbsd.org -Date: Mon Mar 23 06:06:38 2015 +0000 +Date: Sat Jul 18 07:57:14 2015 +0000 upstream commit - for ssh-keygen -A, don't try (and fail) to generate ssh - v.1 keys when compiled without SSH1 support RSA/DSA/ECDSA keys when compiled - without OpenSSL based on patch by Mike Frysinger; bz#2369 + only query each keyboard-interactive device once per + authentication request regardless of how many times it is listed; ok markus@ + + Upstream-ID: d73fafba6e86030436ff673656ec1f33d9ffeda1 -commit 725fd22a8c41db7de73a638539a5157b7e4424ae +commit cd7324d0667794eb5c236d8a4e0f236251babc2d Author: djm@openbsd.org -Date: Wed Mar 18 01:44:21 2015 +0000 +Date: Fri Jul 17 03:34:27 2015 +0000 upstream commit - KRL support doesn't need OpenSSL anymore, remove #ifdefs - from around call + remove -u flag to diff (only used for error output) to make + things easier for -portable + + Upstream-Regress-ID: a5d6777d2909540d87afec3039d9bb2414ade548 -commit b07011c18e0b2e172c5fd09d21fb159a0bf5fcc7 +commit deb8d99ecba70b67f4af7880b11ca8768df9ec3a Author: djm@openbsd.org -Date: Mon Mar 16 11:09:52 2015 +0000 +Date: Fri Jul 17 03:09:19 2015 +0000 upstream commit - #if 0 some more arrays used only for decrypting (we don't - use since we only need encrypt for AES-CTR) + direct-streamlocal@openssh.com Unix domain foward + messages do not contain a "reserved for future use" field and in fact, + serverloop.c checks that there isn't one. Remove erroneous mention from + PROTOCOL description. bz#2421 from Daniel Black + + Upstream-ID: 3d51a19e64f72f764682f1b08f35a8aa810a43ac -commit 1cb3016635898d287e9d58b50c430995652d5358 -Author: jsg@openbsd.org -Date: Wed Mar 11 00:48:39 2015 +0000 +commit 356b61f365405b5257f5b2ab446e5d7bd33a7b52 +Author: djm@openbsd.org +Date: Fri Jul 17 03:04:27 2015 +0000 upstream commit - add back the changes from rev 1.206, djm reverted this by - mistake in rev 1.207 - -commit 4d24b3b6a4a6383e05e7da26d183b79fa8663697 -Author: Damien Miller -Date: Fri Mar 20 09:11:59 2015 +1100 - - remove error() accidentally inserted for debugging + describe magic for setting up Unix domain socket fowards + via the mux channel; bz#2422 patch from Daniel Black - pointed out by Christian Hesse - -commit 9f82e5a9042f2d872e98f48a876fcab3e25dd9bb -Author: Tim Rice -Date: Mon Mar 16 22:49:20 2015 -0700 - - portability fix: Solaris systems may not have a grep that understands -q - -commit 8ef691f7d9ef500257a549d0906d78187490668f -Author: Damien Miller -Date: Wed Mar 11 10:35:26 2015 +1100 - - fix compile with clang + Upstream-ID: 943080fe3864715c423bdeb7c920bb30c4eee861 -commit 4df590cf8dc799e8986268d62019b487a8ed63ad -Author: Damien Miller -Date: Wed Mar 11 10:02:39 2015 +1100 +commit d3e2aee41487d55b8d7d40f538b84ff1db7989bc +Author: Darren Tucker +Date: Fri Jul 17 12:52:34 2015 +1000 - make unit tests work for !OPENSSH_HAS_ECC + Check if realpath works on nonexistent files. + + On some platforms the native realpath doesn't work with non-existent + files (this is actually specified in some versions of POSIX), however + the sftp spec says its realpath with "canonicalize any given path name". + On those platforms, use realpath from the compat library. + + In addition, when compiling with -DFORTIFY_SOURCE, glibc redefines + the realpath symbol to the checked version, so redefine ours to + something else so we pick up the compat version we want. + + bz#2428, ok djm@ -commit 307bb40277ca2c32e97e61d70d1ed74b571fd6ba +commit 25b14610dab655646a109db5ef8cb4c4bf2a48a0 Author: djm@openbsd.org -Date: Sat Mar 7 04:41:48 2015 +0000 +Date: Fri Jul 17 02:47:45 2015 +0000 upstream commit - unbreak for w/SSH1 (default) case; ok markus@ deraadt@ - -commit b44ee0c998fb4c5f3c3281f2398af5ce42840b6f -Author: Damien Miller -Date: Thu Mar 5 18:39:20 2015 -0800 - - unbreak hostkeys test for w/ SSH1 case + fix incorrect test for SSH1 keys when compiled without SSH1 + support + + Upstream-ID: 6004d720345b8e481c405e8ad05ce2271726e451 -commit 55e5bdeb519cb60cc18b7ba0545be581fb8598b4 +commit df56a8035d429b2184ee94aaa7e580c1ff67f73a Author: djm@openbsd.org -Date: Fri Mar 6 01:40:56 2015 +0000 +Date: Wed Jul 15 08:00:11 2015 +0000 upstream commit - fix sshkey_certify() return value for unsupported key types; - ok markus@ deraadt@ - -commit be8f658e550a434eac04256bfbc4289457a24e99 -Author: Damien Miller -Date: Wed Mar 4 15:38:03 2015 -0800 - - update version numbers to match version.h + fix NULL-deref when SSH1 reenabled + + Upstream-ID: f22fd805288c92b3e9646782d15b48894b2d5295 -commit ac5e8acefa253eb5e5ba186e34236c0e8007afdc +commit 41e38c4d49dd60908484e6703316651333f16b93 Author: djm@openbsd.org -Date: Wed Mar 4 23:22:35 2015 +0000 +Date: Wed Jul 15 07:19:50 2015 +0000 upstream commit - make these work with !SSH1; ok markus@ deraadt@ + regen RSA1 test keys; the last batch was missing their + private parts + + Upstream-Regress-ID: 7ccf437305dd63ff0b48dd50c5fd0f4d4230c10a -commit 2f04af92f036b0c87a23efb259c37da98cd81fe6 -Author: djm@openbsd.org -Date: Wed Mar 4 21:12:59 2015 +0000 +commit 5bf0933184cb622ca3f96d224bf3299fd2285acc +Author: markus@openbsd.org +Date: Fri Jul 10 06:23:25 2015 +0000 upstream commit - make ssh-add -D work with !SSH1 agent + Adapt tests, now that DSA if off by default; use + PubkeyAcceptedKeyTypes and PubkeyAcceptedKeyTypes to test DSA. + + Upstream-Regress-ID: 0ff2a3ff5ac1ce5f92321d27aa07b98656efcc5c -commit a05adf95d2af6abb2b7826ddaa7a0ec0cdc1726b -Author: Damien Miller -Date: Wed Mar 4 00:55:48 2015 -0800 +commit 7a6e3fd7b41dbd3756b6bf9acd67954c0b1564cc +Author: markus@openbsd.org +Date: Tue Jul 7 14:54:16 2015 +0000 - netcat needs poll.h portability goop + upstream commit + + regen test data after mktestdata.sh changes + + Upstream-Regress-ID: 3495ecb082b9a7c048a2d7c5c845d3bf181d25a4 -commit dad2b1892b4c1b7e58df483a8c5b983c4454e099 +commit 7c8c174c69f681d4910fa41c37646763692b28e2 Author: markus@openbsd.org -Date: Tue Mar 3 22:35:19 2015 +0000 +Date: Tue Jul 7 14:53:30 2015 +0000 upstream commit - make it possible to run tests w/o ssh1 support; ok djm@ + adapt tests to new minimum RSA size and default FP format + + Upstream-Regress-ID: a4b30afd174ce82b96df14eb49fb0b81398ffd0e -commit d48a22601bdd3eec054794c535f4ae8d8ae4c6e2 +commit 6a977a4b68747ade189e43d302f33403fd4a47ac Author: djm@openbsd.org -Date: Wed Mar 4 18:53:53 2015 +0000 +Date: Fri Jul 3 04:39:23 2015 +0000 upstream commit - crank; ok markus, deraadt - -commit bbffb23daa0b002dd9f296e396a9ab8a5866b339 -Author: Damien Miller -Date: Tue Mar 3 13:50:27 2015 -0800 - - more --without-ssh1 fixes - -commit 6c2039286f503e2012a58a1d109e389016e7a99b -Author: Damien Miller -Date: Tue Mar 3 13:48:48 2015 -0800 - - fix merge both that broke --without-ssh1 compile + legacy v00 certificates are gone; adapt and don't try to + test them; "sure" markus@ dtucker@ + + Upstream-Regress-ID: c57321e69b3cd4a3b3396dfcc43f0803d047da12 -commit 111dfb225478a76f89ecbcd31e96eaf1311b59d3 +commit 0c4123ad5e93fb90fee9c6635b13a6cdabaac385 Author: djm@openbsd.org -Date: Tue Mar 3 21:21:13 2015 +0000 +Date: Wed Jul 1 23:11:18 2015 +0000 upstream commit - add SSH1 Makefile knob to make it easier to build without - SSH1 support; ok markus@ + don't expect SSH v.1 in unittests + + Upstream-Regress-ID: f8812b16668ba78e6a698646b2a652b90b653397 -commit 3f7f5e6c5d2aa3f6710289c1a30119e534e56c5c +commit 3c099845798a817cdde513c39074ec2063781f18 Author: djm@openbsd.org -Date: Tue Mar 3 20:42:49 2015 +0000 +Date: Mon Jun 15 06:38:50 2015 +0000 upstream commit - expand __unused to full __attribute__ for better portability - -commit 2fab9b0f8720baf990c931e3f68babb0bf9949c6 -Author: Damien Miller -Date: Wed Mar 4 07:41:27 2015 +1100 - - avoid warning + turn SSH1 back on to match src/usr.bin/ssh being tested + + Upstream-Regress-ID: 6c4f763a2f0cc6893bf33983919e9030ae638333 -commit d1bc844322461f882b4fd2277ba9a8d4966573d2 -Author: Damien Miller -Date: Wed Mar 4 06:31:45 2015 +1100 +commit b1dc2b33689668c75e95f873a42d5aea1f4af1db +Author: dtucker@openbsd.org +Date: Mon Jul 13 04:57:14 2015 +0000 - Revert "define __unused to nothing if not already defined" + upstream commit - This reverts commit 1598419e38afbaa8aa5df8dd6b0af98301e2c908. + Add "PuTTY_Local:" to the clients to which we do not + offer DH-GEX. This was the string that was used for development versions + prior to September 2014 and they don't do RFC4419 DH-GEX, but unfortunately + there are some extant products based on those versions. bx2424 from Jay + Rouman, ok markus@ djm@ - Some system headers have objects named __unused + Upstream-ID: be34d41e18b966832fe09ca243d275b81882e1d5 -commit 00797e86b2d98334d1bb808f65fa1fd47f328ff1 -Author: Damien Miller -Date: Wed Mar 4 05:02:45 2015 +1100 +commit 3a1638dda19bbc73d0ae02b4c251ce08e564b4b9 +Author: markus@openbsd.org +Date: Fri Jul 10 06:21:53 2015 +0000 - check for crypt and DES_crypt in openssl block + upstream commit + + Turn off DSA by default; add HostKeyAlgorithms to the + server and PubkeyAcceptedKeyTypes to the client side, so it still can be + tested or turned back on; feedback and ok djm@ - fixes builds on systems that use DES_crypt; based on patch - from Roumen Petrov + Upstream-ID: 8450a9e6d83f80c9bfed864ff061dfc9323cec21 -commit 1598419e38afbaa8aa5df8dd6b0af98301e2c908 -Author: Damien Miller -Date: Wed Mar 4 04:59:13 2015 +1100 +commit 16db0a7ee9a87945cc594d13863cfcb86038db59 +Author: markus@openbsd.org +Date: Thu Jul 9 09:49:46 2015 +0000 - define __unused to nothing if not already defined + upstream commit + + re-enable ed25519-certs if compiled w/o openssl; ok djm - fixes builds on BSD/OS + Upstream-ID: e10c90808b001fd2c7a93778418e9b318f5c4c49 -commit d608a51daad4f14ad6ab43d7cf74ef4801cc3fe9 -Author: djm@openbsd.org -Date: Tue Mar 3 17:53:40 2015 +0000 +commit c355bf306ac33de6545ce9dac22b84a194601e2f +Author: markus@openbsd.org +Date: Wed Jul 8 20:24:02 2015 +0000 upstream commit - reorder logic for better portability; patch from Roumen - Petrov + no need to include the old buffer/key API + + Upstream-ID: fb13c9f7c0bba2545f3eb0a0e69cb0030819f52b -commit 68d2dfc464fbcdf8d6387884260f9801f4352393 -Author: djm@openbsd.org -Date: Tue Mar 3 06:48:58 2015 +0000 +commit a3cc48cdf9853f1e832d78cb29bedfab7adce1ee +Author: markus@openbsd.org +Date: Wed Jul 8 19:09:25 2015 +0000 upstream commit - Allow "ssh -Q protocol-version" to list supported SSH - protocol versions. Useful for detecting builds without SSH v.1 support; idea - and ok markus@ + typedefs for Cipher&CipherContext are unused + + Upstream-ID: 50e6a18ee92221d23ad173a96d5b6c42207cf9a7 -commit 39e2f1229562e1195169905607bc12290d21f021 -Author: millert@openbsd.org -Date: Sun Mar 1 15:44:40 2015 +0000 +commit a635bd06b5c427a57c3ae760d3a2730bb2c863c0 +Author: markus@openbsd.org +Date: Wed Jul 8 19:04:21 2015 +0000 upstream commit - Make sure we only call getnameinfo() for AF_INET or AF_INET6 - sockets. getpeername() of a Unix domain socket may return without error on - some systems without actually setting ss_family so getnameinfo() was getting - called with ss_family set to AF_UNSPEC. OK djm@ - -commit e47536ba9692d271b8ad89078abdecf0a1c11707 -Author: Damien Miller -Date: Sat Feb 28 08:20:11 2015 -0800 - - portability fixes for regress/netcat.c + xmalloc.h is unused - Mostly avoiding "err(1, NULL)" + Upstream-ID: afb532355b7fa7135a60d944ca1e644d1d63cb58 -commit 02973ad5f6f49d8420e50a392331432b0396c100 -Author: Damien Miller -Date: Sat Feb 28 08:05:27 2015 -0800 +commit 2521cf0e36c7f3f6b19f206da0af134f535e4a31 +Author: markus@openbsd.org +Date: Wed Jul 8 19:01:15 2015 +0000 - twiddle another test for portability + upstream commit - from Tom G. Christensen - -commit f7f3116abf2a6e2f309ab096b08c58d19613e5d0 -Author: Damien Miller -Date: Fri Feb 27 15:52:49 2015 -0800 - - twiddle test for portability - -commit 1ad3a77cc9d5568f5437ff99d377aa7a41859b83 -Author: Damien Miller -Date: Thu Feb 26 20:33:22 2015 -0800 - - make regress/netcat.c fd passing (more) portable - -commit 9e1cfca7e1fe9cf8edb634fc894e43993e4da1ea -Author: Damien Miller -Date: Thu Feb 26 20:32:58 2015 -0800 - - create OBJ/valgrind-out before running unittests - -commit bd58853102cee739f0e115e6d4b5334332ab1442 -Author: Damien Miller -Date: Wed Feb 25 16:58:22 2015 -0800 - - valgrind support + compress.c is gone + + Upstream-ID: 174fa7faa9b9643cba06164b5e498591356fbced -commit f43d17269194761eded9e89f17456332f4c83824 +commit c65a7aa6c43aa7a308ee1ab8a96f216169ae9615 Author: djm@openbsd.org -Date: Thu Feb 26 20:45:47 2015 +0000 +Date: Fri Jul 3 04:05:54 2015 +0000 upstream commit - don't printf NULL key comments; reported by Tom Christensen + another SSH_RSA_MINIMUM_MODULUS_SIZE that needed + cranking + + Upstream-ID: 9d8826cafe96aab4ae8e2f6fd22800874b7ffef1 -commit 6e6458b476ec854db33e3e68ebf4f489d0ab3df8 +commit b1f383da5cd3cb921fc7776f17a14f44b8a31757 Author: djm@openbsd.org -Date: Wed Feb 25 23:05:47 2015 +0000 +Date: Fri Jul 3 03:56:25 2015 +0000 upstream commit - zero cmsgbuf before use; we initialise the bits we use - but valgrind still spams warning on it + add an XXX reminder for getting correct key paths from + sshd_config + + Upstream-ID: feae52b209d7782ad742df04a4260e9fe41741db -commit a63cfa26864b93ab6afefad0b630e5358ed8edfa +commit 933935ce8d093996c34d7efa4d59113163080680 Author: djm@openbsd.org -Date: Wed Feb 25 19:54:02 2015 +0000 +Date: Fri Jul 3 03:49:45 2015 +0000 upstream commit - fix small memory leak when UpdateHostkeys=no - -commit e6b950341dd75baa8526f1862bca39e52f5b879b -Author: Tim Rice -Date: Wed Feb 25 09:56:48 2015 -0800 - - Revert "Work around finicky USL linker so netcat will build." - - This reverts commit d1db656021d0cd8c001a6692f772f1de29b67c8b. + refuse to generate or accept RSA keys smaller than 1024 + bits; feedback and ok dtucker@ - No longer needed with commit 678e473e2af2e4802f24dd913985864d9ead7fb3 + Upstream-ID: 7ea3d31271366ba264f06e34a3539bf1ac30f0ba -commit 6f621603f9cff2a5d6016a404c96cb2f8ac2dec0 +commit bdfd29f60b74f3e678297269dc6247a5699583c1 Author: djm@openbsd.org -Date: Wed Feb 25 17:29:38 2015 +0000 +Date: Fri Jul 3 03:47:00 2015 +0000 upstream commit - don't leak validity of user in "too many authentication - failures" disconnect message; reported by Sebastian Reitenbach - -commit 6288e3a935494df12519164f52ca5c8c65fc3ca5 -Author: naddy@openbsd.org -Date: Tue Feb 24 15:24:05 2015 +0000 - - upstream commit + turn off 1024 bit diffie-hellman-group1-sha1 key + exchange method (already off in server, this turns it off in the client by + default too) ok dtucker@ - add -v (show ASCII art) to -l's synopsis; ok djm@ + Upstream-ID: f59b88f449210ab7acf7d9d88f20f1daee97a4fa -commit 678e473e2af2e4802f24dd913985864d9ead7fb3 -Author: Darren Tucker -Date: Thu Feb 26 04:12:58 2015 +1100 +commit c28fc62d789d860c75e23a9fa9fb250eb2beca57 +Author: djm@openbsd.org +Date: Fri Jul 3 03:43:18 2015 +0000 - Remove dependency on xmalloc. + upstream commit - Remove ssh_get_progname's dependency on xmalloc, which should reduce - link order problems. ok djm@ - -commit 5d5ec165c5b614b03678afdad881f10e25832e46 -Author: Darren Tucker -Date: Wed Feb 25 15:32:49 2015 +1100 - - Restrict ECDSA and ECDH tests. + delete support for legacy v00 certificates; "sure" + markus@ dtucker@ - ifdef out some more ECDSA and ECDH tests when built against an OpenSSL - that does not have eliptic curve functionality. + Upstream-ID: b5b9bb5f9202d09e88f912989d74928601b6636f -commit 1734e276d99b17e92d4233fac7aef3a3180aaca7 -Author: Darren Tucker -Date: Wed Feb 25 13:40:45 2015 +1100 +commit 564d63e1b4a9637a209d42a9d49646781fc9caef +Author: djm@openbsd.org +Date: Wed Jul 1 23:10:47 2015 +0000 - Move definition of _NSIG. + upstream commit - _NSIG is only unsed in one file, so move it there prevent redefinition - warnings reported by Kevin Brott. - -commit a47ead7c95cfbeb72721066c4da2312e5b1b9f3d -Author: Darren Tucker -Date: Wed Feb 25 13:17:40 2015 +1100 - - Add includes.h for compatibility stuff. - -commit 38806bda6d2e48ad32812b461eebe17672ada771 -Author: Damien Miller -Date: Tue Feb 24 16:50:06 2015 -0800 - - include netdb.h to look for MAXHOSTNAMELEN; ok tim - -commit d1db656021d0cd8c001a6692f772f1de29b67c8b -Author: Tim Rice -Date: Tue Feb 24 10:42:08 2015 -0800 - - Work around finicky USL linker so netcat will build. - -commit cb030ce25f555737e8ba97bdd7883ac43f3ff2a3 -Author: Damien Miller -Date: Tue Feb 24 09:23:04 2015 -0800 - - include includes.h to avoid build failure on AIX - -commit 13af342458f5064144abbb07e5ac9bbd4eb42567 -Author: Tim Rice -Date: Tue Feb 24 07:56:47 2015 -0800 - - Original portability patch from djm@ for platforms missing err.h. - Fix name space clash on Solaris 10. Still more to do for Solaris 10 - to deal with msghdr structure differences. ok djm@ - -commit 910209203d0cd60c5083901cbcc0b7b44d9f48d2 -Author: Tim Rice -Date: Mon Feb 23 22:06:56 2015 -0800 - - cleaner way fix dispatch.h portion of commit - a88dd1da119052870bb2654c1a32c51971eade16 - (some systems have sig_atomic_t in signal.h, some in sys/signal.h) - Sounds good to me djm@ - -commit 676c38d7cbe65b76bbfff796861bb6615cc6a596 -Author: Tim Rice -Date: Mon Feb 23 21:51:33 2015 -0800 - - portability fix: if we can't dind a better define for HOST_NAME_MAX, use 255 - -commit 1221b22023dce38cbc90ba77eae4c5d78c77a5e6 -Author: Tim Rice -Date: Mon Feb 23 21:50:34 2015 -0800 - - portablity fix: s/__inline__/inline/ - -commit 4c356308a88d309c796325bb75dce90ca16591d5 -Author: Darren Tucker -Date: Tue Feb 24 13:49:31 2015 +1100 - - Wrap stdint.h includes in HAVE_STDINT_H. - -commit c9c88355c6a27a908e7d1e5003a2b35ea99c1614 -Author: Darren Tucker -Date: Tue Feb 24 13:43:57 2015 +1100 - - Add AI_NUMERICSERV to fake-rfc2553. + Compile-time disable SSH v.1 again - Our getaddrinfo implementation always returns numeric values already. + Upstream-ID: 1d4b513a3a06232f02650b73bad25100d1b800af -commit ef342ab1ce6fb9a4b30186c89c309d0ae9d0eeb4 -Author: Darren Tucker -Date: Tue Feb 24 13:39:57 2015 +1100 +commit 868109b650504dd9bcccdb1f51d0906f967c20ff +Author: djm@openbsd.org +Date: Wed Jul 1 02:39:06 2015 +0000 - Include OpenSSL's objects.h before bn.h. + upstream commit - Prevents compile errors on some platforms (at least old GCCs and AIX's - XLC compilers). - -commit dcc8997d116f615195aa7c9ec019fb36c28c6228 -Author: Darren Tucker -Date: Tue Feb 24 12:30:59 2015 +1100 - - Convert two macros into functions. + twiddle PermitRootLogin back - Convert packet_send_debug and packet_disconnect from macros to - functions. Some older GCCs (2.7.x, 2.95.x) see to have problems with - variadic macros with only one argument so we convert these two into - functions. ok djm@ + Upstream-ID: 2bd23976305d0512e9f84d054e1fc23cd70b89f2 -commit 2285c30d51b7e2052c6526445abe7e7cc7e170a1 +commit 7de4b03a6e4071d454b72927ffaf52949fa34545 Author: djm@openbsd.org -Date: Mon Feb 23 22:21:21 2015 +0000 +Date: Wed Jul 1 02:32:17 2015 +0000 upstream commit - further silence spurious error message even when -v is - specified (e.g. to get visual host keys); reported by naddy@ - -commit 9af21979c00652029e160295e988dea40758ece2 -Author: Damien Miller -Date: Tue Feb 24 09:04:32 2015 +1100 - - don't include stdint.h unless HAVE_STDINT_H set - -commit 62f678dd51660d6f8aee1da33d3222c5de10a89e -Author: Damien Miller -Date: Tue Feb 24 09:02:54 2015 +1100 - - nother sys/queue.h -> sys-queue.h fix + twiddle; (this commit marks the openssh-6.9 release) - spotted by Tom Christensen + Upstream-ID: 78500582819f61dd8adee36ec5cc9b9ac9351234 -commit b3c19151cba2c0ed01b27f55de0d723ad07ca98f +commit 1bf477d3cdf1a864646d59820878783d42357a1d Author: djm@openbsd.org -Date: Mon Feb 23 20:32:15 2015 +0000 +Date: Wed Jul 1 02:26:31 2015 +0000 upstream commit - fix a race condition by using a mux socket rather than an - ineffectual wait statement - -commit a88dd1da119052870bb2654c1a32c51971eade16 -Author: Damien Miller -Date: Tue Feb 24 06:30:29 2015 +1100 - - various include fixes for portable + better refuse ForwardX11Trusted=no connections attempted + after ForwardX11Timeout expires; reported by Jann Horn + + Upstream-ID: bf0fddadc1b46a0334e26c080038313b4b6dea21 -commit 5248429b5ec524d0a65507cff0cdd6e0cb99effd +commit 47aa7a0f8551b471fcae0447c1d78464f6dba869 Author: djm@openbsd.org -Date: Mon Feb 23 16:55:51 2015 +0000 +Date: Wed Jul 1 01:56:13 2015 +0000 upstream commit - add an XXX to remind me to improve sshkey_load_public + put back default PermitRootLogin=no + + Upstream-ID: 7bdedd5cead99c57ed5571f3b6b7840922d5f728 -commit e94e4b07ef2eaead38b085a60535df9981cdbcdb +commit 984b064fe2a23733733262f88d2e1b2a1a501662 Author: djm@openbsd.org -Date: Mon Feb 23 16:55:31 2015 +0000 +Date: Wed Jul 1 01:55:13 2015 +0000 upstream commit - silence a spurious error message when listing - fingerprints for known_hosts; bz#2342 + openssh-6.9 + + Upstream-ID: 6cfe8e1904812531080e6ab6e752d7001b5b2d45 -commit f2293a65392b54ac721f66bc0b44462e8d1d81f8 +commit d921082ed670f516652eeba50705e1e9f6325346 Author: djm@openbsd.org -Date: Mon Feb 23 16:33:25 2015 +0000 +Date: Wed Jul 1 01:55:00 2015 +0000 upstream commit - fix setting/clearing of TTY raw mode around - UpdateHostKeys=ask confirmation question; reported by Herb Goldman - -commit f2004cd1adf34492eae0a44b1ef84e0e31b06088 -Author: Darren Tucker -Date: Mon Feb 23 05:04:21 2015 +1100 - - Repair for non-ECC OpenSSL. + reset default PermitRootLogin to 'yes' (momentarily, for + release) - Ifdef out the ECC parts when building with an OpenSSL that doesn't have - it. - -commit 37f9220db8d1a52c75894c3de1e5f2ae5bd71b6f -Author: Darren Tucker -Date: Mon Feb 23 03:07:24 2015 +1100 - - Wrap stdint.h includes in ifdefs. - -commit f81f1bbc5b892c8614ea740b1f92735652eb43f0 -Author: Tim Rice -Date: Sat Feb 21 18:12:10 2015 -0800 - - out of tree build fix - -commit 2e13a1e4d22f3b503c3bfc878562cc7386a1d1ae -Author: Tim Rice -Date: Sat Feb 21 18:08:51 2015 -0800 - - mkdir kex unit test directory so testing out of tree builds works + Upstream-ID: cad8513527066e65dd7a1c16363d6903e8cefa24 -commit 1797f49b1ba31e8700231cd6b1d512d80bb50d2c -Author: halex@openbsd.org -Date: Sat Feb 21 21:46:57 2015 +0000 +commit 66295e0e1ba860e527f191b6325d2d77dec4dbce +Author: Damien Miller +Date: Wed Jul 1 11:49:12 2015 +1000 - upstream commit - - make "ssh-add -d" properly remove a corresponding - certificate, and also not whine and fail if there is none - - ok djm@ + crank version numbers for release -commit 7faaa32da83a609059d95dbfcb0649fdb04caaf6 +commit 37035c07d4f26bb1fbe000d2acf78efdb008681d Author: Damien Miller -Date: Sun Feb 22 07:57:27 2015 +1100 +Date: Wed Jul 1 10:49:37 2015 +1000 - mkdir hostkey and bitmap unit test directories + s/--with-ssh1/--without-ssh1/ -commit bd49da2ef197efac5e38f5399263a8b47990c538 +commit 629df770dbadc2accfbe1c81b3f31f876d0acd84 Author: djm@openbsd.org -Date: Fri Feb 20 23:46:01 2015 +0000 +Date: Tue Jun 30 05:25:07 2015 +0000 upstream commit - sort options useable under Match case-insensitively; prodded - jmc@ + fatal() when a remote window update causes the window + value to overflow. Reported by Georg Wicherski, ok markus@ + + Upstream-ID: ead397a9aceb3bf74ebfa5fcaf259d72e569f351 -commit 1a779a0dd6cd8b4a1a40ea33b5415ab8408128ac +commit f715afebe735d61df3fd30ad72d9ac1c8bd3b5f2 Author: djm@openbsd.org -Date: Sat Feb 21 20:51:02 2015 +0000 +Date: Tue Jun 30 05:23:25 2015 +0000 upstream commit - correct paths to configuration files being written/updated; - they live in $OBJ not cwd; some by Roumen Petrov - -commit 28ba006c1acddff992ae946d0bc0b500b531ba6b -Author: Darren Tucker -Date: Sat Feb 21 15:41:07 2015 +1100 - - More correct checking of HAVE_DECL_AI_NUMERICSERV. - -commit e50e8c97a9cecae1f28febccaa6ca5ab3bc10f54 -Author: Darren Tucker -Date: Sat Feb 21 15:10:33 2015 +1100 - - Add null declaration of AI_NUMERICINFO. + Fix math error in remote window calculations that causes + eventual stalls for datagram channels. Reported by Georg Wicherski, ok + markus@ - Some platforms (older FreeBSD and DragonFly versions) do have - getaddrinfo() but do not have AI_NUMERICINFO. so define it to zero - in those cases. + Upstream-ID: be54059d11bf64e0d85061f7257f53067842e2ab -commit 18a208d6a460d707a45916db63a571e805f5db46 -Author: djm@openbsd.org -Date: Fri Feb 20 22:40:32 2015 +0000 +commit 52fb6b9b034fcfd24bf88cc7be313e9c31de9889 +Author: Damien Miller +Date: Tue Jun 30 16:05:40 2015 +1000 - upstream commit + skip IPv6-related portions on hosts without IPv6 - more options that are available under Match; bz#2353 reported - by calestyo AT scientia.net + with Tim Rice -commit 44732de06884238049f285f1455b2181baa7dc82 +commit 512caddf590857af6aa12218461b5c0441028cf5 Author: djm@openbsd.org -Date: Fri Feb 20 22:17:21 2015 +0000 +Date: Mon Jun 29 22:35:12 2015 +0000 upstream commit - UpdateHostKeys fixes: - - I accidentally changed the format of the hostkeys@openssh.com messages - last week without changing the extension name, and this has been causing - connection failures for people who are running -current. First reported - by sthen@ + add getpid to sandbox, reachable by grace_alarm_handler - s/hostkeys@openssh.com/hostkeys-00@openssh.com/ - Change the name of the proof message too, and reorder it a little. + reported by Jakub Jelen; bz#2419 - Also, UpdateHostKeys=ask is incompatible with ControlPersist (no TTY - available to read the response) so disable UpdateHostKeys if it is in - ask mode and ControlPersist is active (and document this) + Upstream-ID: d0da1117c16d4c223954995d35b0f47c8f684cd8 -commit 13a39414d25646f93e6d355521d832a03aaaffe2 +commit 78c2a4f883ea9aba866358e2acd9793a7f42ca93 Author: djm@openbsd.org -Date: Tue Feb 17 00:14:05 2015 +0000 +Date: Fri Jun 26 05:13:20 2015 +0000 upstream commit - Regression: I broke logging of public key fingerprints in - 1.46. Pointed out by Pontus Lundkvist + Fix \-escaping bug that caused forward path parsing to skip + two characters and skip past the end of the string. + + Based on patch by Salvador Fandino; ok dtucker@ + + Upstream-ID: 7b879dc446335677cbe4cb549495636a0535f3bd -commit 773dda25e828c4c9a52f7bdce6e1e5924157beab +commit bc20205c91c9920361d12b15d253d4997dba494a Author: Damien Miller -Date: Fri Jan 30 23:10:17 2015 +1100 +Date: Thu Jun 25 09:51:39 2015 +1000 - repair --without-openssl; broken in refactor + add missing pselect6 + + patch from Jakub Jelen -commit e89c780886b23600de1e1c8d74aabd1ff61f43f0 -Author: Damien Miller -Date: Tue Feb 17 10:04:55 2015 +1100 +commit 9d27fb73b4a4e5e99cb880af790d5b1ce44f720a +Author: djm@openbsd.org +Date: Wed Jun 24 23:47:23 2015 +0000 - hook up hostkeys unittest to portable Makefiles + upstream commit + + correct test to sshkey_sign(); spotted by Albert S. + + Upstream-ID: 5f7347f40f0ca6abdaca2edb3bd62f4776518933 -commit 0abf41f99aa16ff09b263bead242d6cb2dbbcf99 -Author: djm@openbsd.org -Date: Mon Feb 16 22:21:03 2015 +0000 +commit 7ed01a96a1911d8b4a9ef4f3d064e1923bfad7e3 +Author: dtucker@openbsd.org +Date: Wed Jun 24 01:49:19 2015 +0000 upstream commit - enable hostkeys unit tests + Revert previous commit. We still want to call setgroups + in the case where there are zero groups to remove any that we might otherwise + inherit (as pointed out by grawity at gmail.com) and since the 2nd argument + to setgroups is always a static global it's always valid to dereference in + this case. ok deraadt@ djm@ + + Upstream-ID: 895b5ac560a10befc6b82afa778641315725fd01 -commit 68a5d647ccf0fb6782b2f749433a1eee5bc9044b -Author: djm@openbsd.org -Date: Mon Feb 16 22:20:50 2015 +0000 +commit 882f8bf94f79528caa65b0ba71c185d705bb7195 +Author: dtucker@openbsd.org +Date: Wed Jun 24 01:49:19 2015 +0000 upstream commit - check string/memory compare arguments aren't NULL + Revert previous commit. We still want to call setgroups in + the case where there are zero groups to remove any that we might otherwise + inherit (as pointed out by grawity at gmail.com) and since the 2nd argument + to setgroups is always a static global it's always valid to dereference in + this case. ok deraadt@ djm@ + + Upstream-ID: 895b5ac560a10befc6b82afa778641315725fd01 -commit ef575ef20d09f20722e26b45dab80b3620469687 +commit 9488538a726951e82b3a4374f3c558d72c80a89b Author: djm@openbsd.org -Date: Mon Feb 16 22:18:34 2015 +0000 +Date: Mon Jun 22 23:42:16 2015 +0000 upstream commit - unit tests for hostfile.c code, just hostkeys_foreach so - far + Don't count successful partial authentication as failures + in monitor; this may have caused the monitor to refuse multiple + authentications that would otherwise have successfully completed; ok markus@ + + Upstream-ID: eb74b8e506714d0f649bd5c300f762a527af04a3 -commit 8ea3365e6aa2759ccf5c76eaea62cbc8a280b0e7 -Author: markus@openbsd.org -Date: Sat Feb 14 12:43:16 2015 +0000 +commit 63b78d003bd8ca111a736e6cea6333da50f5f09b +Author: dtucker@openbsd.org +Date: Mon Jun 22 12:29:57 2015 +0000 upstream commit - test server rekey limit + Don't call setgroups if we have zero groups; there's no + guarantee that it won't try to deref the pointer. Based on a patch from mail + at quitesimple.org, ok djm deraadt + + Upstream-ID: 2fff85e11d7a9a387ef7fddf41fbfaf566708ab1 -commit ce63c4b063c39b2b22d4ada449c9e3fbde788cb3 -Author: djm@openbsd.org -Date: Mon Feb 16 22:30:03 2015 +0000 +commit 5c15e22c691c79a47747bcf5490126656f97cecd +Author: Damien Miller +Date: Thu Jun 18 15:07:56 2015 +1000 + + fix syntax error + +commit 596dbca82f3f567fb3d2d69af4b4e1d3ba1e6403 +Author: jsing@openbsd.org +Date: Mon Jun 15 18:44:22 2015 +0000 upstream commit - partial backout of: + If AuthorizedPrincipalsCommand is specified, however + AuthorizedPrincipalsFile is not (or is set to "none"), authentication will + potentially fail due to key_cert_check_authority() failing to locate a + principal that matches the username, even though an authorized principal has + already been matched in the output of the subprocess. Fix this by using the + same logic to determine if pw->pw_name should be passed, as is used to + determine if a authorized principal must be matched earlier on. - revision 1.441 - date: 2015/01/31 20:30:05; author: djm; state: Exp; lines: +17 -10; commitid - : x8klYPZMJSrVlt3O; - Let sshd load public host keys even when private keys are missing. - Allows sshd to advertise additional keys for future key rotation. - Also log fingerprint of hostkeys loaded; ok markus@ + ok djm@ - hostkey updates now require access to the private key, so we can't - load public keys only. The improved log messages (fingerprints of keys - loaded) are kept. + Upstream-ID: 43b42302ec846b0ea68aceb40677245391b9409d -commit 523463a3a2a9bfc6cfc5afa01bae9147f76a37cc -Author: djm@openbsd.org -Date: Mon Feb 16 22:13:32 2015 +0000 +commit aff3e94c0d75d0d0fa84ea392b50ab04f8c57905 +Author: jsing@openbsd.org +Date: Mon Jun 15 18:42:19 2015 +0000 upstream commit - Revise hostkeys@openssh.com hostkey learning extension. + Make the arguments to match_principals_command() similar + to match_principals_file(), by changing the last argument a struct + sshkey_cert * and dereferencing key->cert in the caller. - The client will not ask the server to prove ownership of the private - halves of any hitherto-unseen hostkeys it offers to the client. + No functional change. - Allow UpdateHostKeys option to take an 'ask' argument to let the - user manually review keys offered. + ok djm@ - ok markus@ + Upstream-ID: 533f99b844b21b47342b32b62e198dfffcf8651c -commit 6c5c949782d86a6e7d58006599c7685bfcd01685 -Author: djm@openbsd.org -Date: Mon Feb 16 22:08:57 2015 +0000 +commit 97e2e1596c202a4693468378b16b2353fd2d6c5e +Author: Damien Miller +Date: Wed Jun 17 14:36:54 2015 +1000 - upstream commit + trivial optimisation for seccomp-bpf - Refactor hostkeys_foreach() and dependent code Deal with - IP addresses (i.e. CheckHostIP) Don't clobber known_hosts when nothing - changed ok markus@ as part of larger commit + When doing arg inspection and the syscall doesn't match, skip + past the instruction that reloads the syscall into the accumulator, + since the accumulator hasn't been modified at this point. -commit 51b082ccbe633dc970df1d1f4c9c0497115fe721 -Author: miod@openbsd.org -Date: Mon Feb 16 18:26:26 2015 +0000 +commit 99f33d7304893bd9fa04d227cb6e870171cded19 +Author: Damien Miller +Date: Wed Jun 17 10:50:51 2015 +1000 - upstream commit + aarch64 support for seccomp-bpf sandbox - Declare ge25519_base as extern, to prevent it from - becoming a common. Gets us rid of ``lignment 4 of symbol - `crypto_sign_ed25519_ref_ge25519_base' in mod_ge25519.o is smaller than 16 in - mod_ed25519.o'' warnings at link time. + Also resort and tidy syscall list. Based on patches by Jakub Jelen + bz#2361; ok dtucker@ -commit 02db468bf7e3281a8e3c058ced571b38b6407c34 -Author: markus@openbsd.org -Date: Fri Feb 13 18:57:00 2015 +0000 +commit 4ef702e1244633c1025ec7cfe044b9ab267097bf +Author: djm@openbsd.org +Date: Mon Jun 15 01:32:50 2015 +0000 upstream commit - make rekey_limit for sshd w/privsep work; ok djm@ - dtucker@ + return failure on RSA signature error; reported by Albert S + + Upstream-ID: e61bb93dbe0349625807b0810bc213a6822121fa -commit 8ec67d505bd23c8bf9e17b7a364b563a07a58ec8 -Author: dtucker@openbsd.org -Date: Thu Feb 12 20:34:19 2015 +0000 +commit a170f22baf18af0b1acf2788b8b715605f41a1f9 +Author: Tim Rice +Date: Tue Jun 9 22:41:13 2015 -0700 + + Fix t12 rules for out of tree builds. + +commit ec04dc4a5515c913121bc04ed261857e68fa5c18 +Author: millert@openbsd.org +Date: Fri Jun 5 15:13:13 2015 +0000 upstream commit - Prevent sshd spamming syslog with - "ssh_dispatch_run_fatal: disconnected". ok markus@ + For "ssh -L 12345:/tmp/sock" don't fail with "No forward host + name." (we have a path, not a host name). Based on a diff from Jared + Yanovich. OK djm@ + + Upstream-ID: 2846b0a8c7de037e33657f95afbd282837fc213f -commit d4c0295d1afc342057ba358237acad6be8af480b +commit 732d61f417a6aea0aa5308b59cb0f563bcd6edd6 Author: djm@openbsd.org -Date: Wed Feb 11 01:20:38 2015 +0000 +Date: Fri Jun 5 03:44:14 2015 +0000 upstream commit - Some packet error messages show the address of the peer, - but might be generated after the socket to the peer has suffered a TCP reset. - In these cases, getpeername() won't work so cache the address earlier. + typo: accidental repetition; bz#2386 - spotted in the wild via deraadt@ and tedu@ + Upstream-ID: 45e620d99f6bc301e5949d34a54027374991c88b -commit 4af1709cf774475ce5d1bc3ddcc165f6c222897d -Author: jsg@openbsd.org -Date: Mon Feb 9 23:22:37 2015 +0000 +commit adfb24c69d1b6f5e758db200866c711e25a2ba73 +Author: Darren Tucker +Date: Fri Jun 5 14:51:40 2015 +1000 - upstream commit + Add Linux powerpc64le and powerpcle entries. - fix some leaks in error paths ok markus@ + Stopgap to resolve bz#2409 because we are so close to release and will + update config.guess and friends shortly after the release. ok djm@ -commit fd36834871d06a03e1ff8d69e41992efa1bbf85f -Author: millert@openbsd.org -Date: Fri Feb 6 23:21:59 2015 +0000 +commit a1195a0fdc9eddddb04d3e9e44c4775431cb77da +Merge: 6397eed d2480bc +Author: Tim Rice +Date: Wed Jun 3 21:43:13 2015 -0700 - upstream commit - - SIZE_MAX is standard, we should be using it in preference to - the obsolete SIZE_T_MAX. OK miod@ beck@ + Merge branch 'master' of git.mindrot.org:/var/git/openssh -commit 1910a286d7771eab84c0b047f31c0a17505236fa -Author: millert@openbsd.org -Date: Thu Feb 5 12:59:57 2015 +0000 +commit 6397eedf953b2b973d2d7cbb504ab501a07f8ddc +Author: Tim Rice +Date: Wed Jun 3 21:41:11 2015 -0700 - upstream commit - - Include stdint.h, not limits.h to get SIZE_MAX. OK guenther@ + Remove unneeded backslashes. Patch from Ángel González -commit ce4f59b2405845584f45e0b3214760eb0008c06c -Author: deraadt@openbsd.org -Date: Tue Feb 3 08:07:20 2015 +0000 +commit d2480bcac1caf31b03068de877a47d6e1027bf6d +Author: Darren Tucker +Date: Thu Jun 4 14:10:55 2015 +1000 - upstream commit - - missing ; djm and mlarkin really having great - interactions recently + Remove redundant include of stdarg.h. bz#2410 -commit 5d34aa94938abb12b877a25be51862757f25d54b -Author: halex@openbsd.org -Date: Tue Feb 3 00:34:14 2015 +0000 +commit 5e67859a623826ccdf2df284cbb37e2d8e2787eb +Author: djm@openbsd.org +Date: Tue Jun 2 09:10:40 2015 +0000 upstream commit - slightly extend the passphrase prompt if running with -c - in order to give the user a chance to notice if unintentionally running - without it + mention CheckHostIP adding addresses to known_hosts; + bz#1993; ok dtucker@ - wording tweak and ok djm@ + Upstream-ID: fd44b68440fd0dc29abf9f2d3f703d74a2396cb7 -commit cb3bde373e80902c7d5d0db429f85068d19b2918 -Author: djm@openbsd.org -Date: Mon Feb 2 22:48:53 2015 +0000 +commit d7a58bbac6583e33fd5eca8e2c2cc70c57617818 +Author: Darren Tucker +Date: Tue Jun 2 20:15:26 2015 +1000 - upstream commit + Replace strcpy with strlcpy. - handle PKCS#11 C_Login returning - CKR_USER_ALREADY_LOGGED_IN; based on patch from Yuri Samoilenko; ok markus@ + ok djm, sanity check by Corinna Vinschen. -commit 15ad750e5ec3cc69765b7eba1ce90060e7083399 -Author: djm@openbsd.org -Date: Mon Feb 2 07:41:40 2015 +0000 +commit 51a1c2115265c6e80ede8a5c9dccada9aeed7143 +Author: Damien Miller +Date: Fri May 29 18:27:21 2015 +1000 - upstream commit - - turn UpdateHostkeys off by default until I figure out - mlarkin@'s warning message; requested by deraadt@ + skip, rather than fatal when run without SUDO set -commit 3cd5103c1e1aaa59bd66f7f52f6ebbcd5deb12f9 -Author: deraadt@openbsd.org -Date: Mon Feb 2 01:57:44 2015 +0000 +commit 599f01142a376645b15cbc9349d7e8975e1cf245 +Author: Damien Miller +Date: Fri May 29 18:03:15 2015 +1000 - upstream commit - - increasing encounters with difficult DNS setups in - darknets has convinced me UseDNS off by default is better ok djm + fix merge botch that left ",," in KEX algs -commit 6049a548a8a68ff0bbe581ab1748ea6a59ecdc38 -Author: djm@openbsd.org -Date: Sat Jan 31 20:30:05 2015 +0000 +commit 0c2a81dfc21822f2423edd30751e5ec53467b347 +Author: Damien Miller +Date: Fri May 29 17:08:28 2015 +1000 - upstream commit - - Let sshd load public host keys even when private keys are - missing. Allows sshd to advertise additional keys for future key rotation. - Also log fingerprint of hostkeys loaded; ok markus@ + re-enable SSH protocol 1 at compile time -commit 46347ed5968f582661e8a70a45f448e0179ca0ab +commit db438f9285d64282d3ac9e8c0944f59f037c0151 Author: djm@openbsd.org -Date: Fri Jan 30 11:43:14 2015 +0000 +Date: Fri May 29 03:05:13 2015 +0000 upstream commit - Add a ssh_config HostbasedKeyType option to control which - host public key types are tried during hostbased authentication. - - This may be used to prevent too many keys being sent to the server, - and blowing past its MaxAuthTries limit. + make this work without SUDO set; ok dtucker@ - bz#2211 based on patch by Iain Morgan; ok markus@ + Upstream-Regress-ID: bca88217b70bce2fe52b23b8e06bdeb82d98c715 -commit 802660cb70453fa4d230cb0233bc1bbdf8328de1 +commit 1d9a2e2849c9864fe75daabf433436341c968e14 Author: djm@openbsd.org -Date: Fri Jan 30 10:44:49 2015 +0000 +Date: Thu May 28 07:37:31 2015 +0000 upstream commit - set a timeout to prevent hangs when talking to busted - servers; ok markus@ - -commit 86936ec245a15c7abe71a0722610998b0a28b194 -Author: djm@openbsd.org -Date: Fri Jan 30 01:11:39 2015 +0000 - - upstream commit + wrap all moduli-related code in #ifdef WITH_OPENSSL. + based on patch from Reuben Hawkins; bz#2388 feedback and ok dtucker@ - regression test for 'wildcard CA' serial/key ID revocations + Upstream-ID: d80cfc8be3e6ec65b3fac9e87c4466533b31b7cf -commit 4509b5d4a4fa645a022635bfa7e86d09b285001f -Author: djm@openbsd.org -Date: Fri Jan 30 01:13:33 2015 +0000 +commit 496aeb25bc2d6c434171292e4714771b594bd00e +Author: dtucker@openbsd.org +Date: Thu May 28 05:41:29 2015 +0000 upstream commit - avoid more fatal/exit in the packet.c paths that - ssh-keyscan uses; feedback and "looks good" markus@ - -commit 669aee994348468af8b4b2ebd29b602cf2860b22 -Author: djm@openbsd.org -Date: Fri Jan 30 01:10:33 2015 +0000 - - upstream commit + Increase the allowed length of the known host file name + in the log message to be consistent with other cases. Part of bz#1993, ok + deraadt. - permit KRLs that revoke certificates by serial number or - key ID without scoping to a particular CA; ok markus@ + Upstream-ID: a9e97567be49f25daf286721450968251ff78397 -commit 7a2c368477e26575d0866247d3313da4256cb2b5 -Author: djm@openbsd.org -Date: Fri Jan 30 00:59:19 2015 +0000 +commit dd2cfeb586c646ff8d70eb93567b2e559ace5b14 +Author: dtucker@openbsd.org +Date: Thu May 28 05:09:45 2015 +0000 upstream commit - missing parentheses after if in do_convert_from() broke - private key conversion from other formats some time in 2010; bz#2345 reported - by jjelen AT redhat.com - -commit 25f5f78d8bf5c22d9cea8b49de24ebeee648a355 -Author: djm@openbsd.org -Date: Fri Jan 30 00:22:25 2015 +0000 - - upstream commit + Fix typo (keywork->keyword) - fix ssh protocol 1, spotted by miod@ + Upstream-ID: 8aacd0f4089c0a244cf43417f4f9045dfaeab534 -commit 9ce86c926dfa6e0635161b035e3944e611cbccf0 +commit 9cc6842493fbf23025ccc1edab064869640d3bec Author: djm@openbsd.org -Date: Wed Jan 28 22:36:00 2015 +0000 +Date: Thu May 28 04:50:53 2015 +0000 upstream commit - update to new API (key_fingerprint => sshkey_fingerprint) - check sshkey_fingerprint return values; ok markus - -commit 9125525c37bf73ad3ee4025520889d2ce9d10f29 -Author: djm@openbsd.org -Date: Wed Jan 28 22:05:31 2015 +0000 - - upstream commit + add error message on ftruncate failure; bz#2176 - avoid fatal() calls in packet code makes ssh-keyscan more - reliable against server failures ok dtucker@ markus@ + Upstream-ID: cbcc606e0b748520c74a210d8f3cc9718d3148cf -commit fae7bbe544cba7a9e5e4ab47ff6faa3d978646eb +commit d1958793a0072c22be26d136dbda5ae263e717a0 Author: djm@openbsd.org -Date: Wed Jan 28 21:15:47 2015 +0000 +Date: Thu May 28 04:40:13 2015 +0000 upstream commit - avoid fatal() calls in packet code makes ssh-keyscan more - reliable against server failures ok dtucker@ markus@ + make ssh-keygen default to ed25519 keys when compiled + without OpenSSL; bz#2388, ok dtucker@ + + Upstream-ID: 85a471fa6d3fa57a7b8e882d22cfbfc1d84cdc71 -commit 1a3d14f6b44a494037c7deab485abe6496bf2c60 -Author: djm@openbsd.org -Date: Wed Jan 28 11:07:25 2015 +0000 +commit 3ecde664c9fc5fb3667aedf9e6671462600f6496 +Author: dtucker@openbsd.org +Date: Wed May 27 23:51:10 2015 +0000 upstream commit - remove obsolete comment + Reorder client proposal to prefer + diffie-hellman-group-exchange-sha1 over diffie-hellman-group14-sha1. ok djm@ + + Upstream-ID: 552c08d47347c3ee1a9a57d88441ab50abe17058 -commit 80c25b7bc0a71d75c43a4575d9a1336f589eb639 -Author: okan@openbsd.org -Date: Tue Jan 27 12:54:06 2015 +0000 +commit 40f64292b907afd0a674fdbf3e4c2356d17a7d68 +Author: dtucker@openbsd.org +Date: Wed May 27 23:39:18 2015 +0000 upstream commit - Since r1.2 removed the use of PRI* macros, inttypes.h is - no longer required. + Add a stronger (4k bit) fallback group that sshd can use + when the moduli file is missing or broken, sourced from RFC3526. bz#2302, ok + markus@ (earlier version), djm@ - ok djm@ - -commit 69ff64f69615c2a21c97cb5878a0996c21423257 -Author: Damien Miller -Date: Tue Jan 27 23:07:43 2015 +1100 - - compile on systems without TCP_MD5SIG (e.g. OSX) + Upstream-ID: b635215746a25a829d117673d5e5a76d4baee7f4 -commit 358964f3082fb90b2ae15bcab07b6105cfad5a43 -Author: Damien Miller -Date: Tue Jan 27 23:07:25 2015 +1100 +commit 5ab7d5fa03ad55bc438fab45dfb3aeb30a3c237a +Author: Darren Tucker +Date: Thu May 28 10:03:40 2015 +1000 - use ssh-keygen under test rather than system's + New moduli file from OpenBSD, removing 1k groups. + + Remove 1k bit groups. ok deraadt@, markus@ -commit a2c95c1bf33ea53038324d1fdd774bc953f98236 -Author: Damien Miller -Date: Tue Jan 27 23:06:59 2015 +1100 +commit a71ba58adf34e599f30cdda6e9b93ae6e3937eea +Author: djm@openbsd.org +Date: Wed May 27 05:15:02 2015 +0000 - OSX lacks HOST_NAME_MAX, has _POSIX_HOST_NAME_MAX + upstream commit + + support PKCS#11 devices with external PIN entry devices + bz#2240, based on patch from Dirk-Willem van Gulik; feedback and ok dtucker@ + + Upstream-ID: 504568992b55a8fc984375242b1bd505ced61b0d -commit ade31d7b6f608a19b85bee29a7a00b1e636a2919 -Author: Damien Miller -Date: Tue Jan 27 23:06:23 2015 +1100 +commit b282fec1aa05246ed3482270eb70fc3ec5f39a00 +Author: dtucker@openbsd.org +Date: Tue May 26 23:23:40 2015 +0000 - these need active_state defined to link on OSX + upstream commit + + Cap DH-GEX group size at 4kbits for Cisco implementations. + Some of them will choke when asked for preferred sizes >4k instead of + returning the 4k group that they do have. bz#2209, ok djm@ - temporary measure until active_state goes away entirely + Upstream-ID: 54b863a19713446b7431f9d06ad0532b4fcfef8d -commit e56aa87502f22c5844918c10190e8b4f785f067b +commit 3e91b4e8b0dc2b4b7e7d42cf6e8994a32e4cb55e Author: djm@openbsd.org -Date: Tue Jan 27 12:01:36 2015 +0000 +Date: Sun May 24 23:39:16 2015 +0000 upstream commit - use printf instead of echo -n to reduce diff against - -portable + add missing 'c' option to getopt(), case statement was + already there; from Felix Bolte + + Upstream-ID: 9b19b4e2e0b54d6fefa0dfac707c51cf4bae3081 -commit 9f7637f56eddfaf62ce3c0af89c25480f2cf1068 -Author: jmc@openbsd.org -Date: Mon Jan 26 13:55:29 2015 +0000 +commit 64a89ec07660abba4d0da7c0095b7371c98bab62 +Author: jsg@openbsd.org +Date: Sat May 23 14:28:37 2015 +0000 upstream commit - sort previous; + fix a memory leak in an error path ok markus@ dtucker@ + + Upstream-ID: bc1da0f205494944918533d8780fde65dff6c598 -commit 3076ee7d530d5b16842fac7a6229706c7e5acd26 +commit f948737449257d2cb83ffcfe7275eb79b677fd4a Author: djm@openbsd.org -Date: Mon Jan 26 13:36:53 2015 +0000 +Date: Fri May 22 05:28:45 2015 +0000 upstream commit - properly restore umask + mention ssh-keygen -E for comparing legacy MD5 + fingerprints; bz#2332 + + Upstream-ID: 079a3669549041dbf10dbc072d9563f0dc3b2859 -commit d411d395556b73ba1b9e451516a0bd6697c4b03d +commit 0882332616e4f0272c31cc47bf2018f9cb258a4e Author: djm@openbsd.org -Date: Mon Jan 26 06:12:18 2015 +0000 +Date: Fri May 22 04:45:52 2015 +0000 upstream commit - regression test for host key rotation + Reorder EscapeChar option parsing to avoid a single-byte + out- of-bounds read. bz#2396 from Jaak Ristioja; ok dtucker@ + + Upstream-ID: 1dc6b5b63d1c8d9a88619da0b27ade461d79b060 -commit fe8a3a51699afbc6407a8fae59b73349d01e49f8 +commit d7c31da4d42c115843edee2074d7d501f8804420 Author: djm@openbsd.org -Date: Mon Jan 26 06:11:28 2015 +0000 +Date: Fri May 22 03:50:02 2015 +0000 upstream commit - adapt to sshkey API tweaks + add knob to relax GSSAPI host credential check for + multihomed hosts bz#928, patch by Simon Wilkinson; ok dtucker + (kerberos/GSSAPI is not compiled by default on OpenBSD) + + Upstream-ID: 15ddf1c6f7fd9d98eea9962f480079ae3637285d -commit 7dd355fb1f0038a3d5cdca57ebab4356c7a5b434 -Author: miod@openbsd.org -Date: Sat Jan 24 10:39:21 2015 +0000 +commit aa72196a00be6e0b666215edcffbc10af234cb0e +Author: Darren Tucker +Date: Fri May 22 17:49:46 2015 +1000 - upstream commit + Include signal.h for sig_atomic_t, used by kex.h. - Move -lz late in the linker commandline for things to - build on static arches. + bz#2402, from tomas.kuthan at oracle com. -commit 0dad3b806fddb93c475b30853b9be1a25d673a33 -Author: miod@openbsd.org -Date: Fri Jan 23 21:21:23 2015 +0000 +commit 8b02481143d75e91c49d1bfae0876ac1fbf9511a +Author: Darren Tucker +Date: Fri May 22 12:47:24 2015 +1000 - upstream commit - - -Wpointer-sign is supported by gcc 4 only. + Import updated moduli file from OpenBSD. -commit 2b3b1c1e4bd9577b6e780c255c278542ea66c098 +commit 4739e8d5e1c0be49624082bd9f6b077e9e758db9 Author: djm@openbsd.org -Date: Tue Jan 20 22:58:57 2015 +0000 +Date: Thu May 21 12:01:19 2015 +0000 upstream commit - use SUBDIR to recuse into unit tests; makes "make obj" - actually work + Support "ssh-keygen -lF hostname" to find search known_hosts + and print key hashes. Already advertised by ssh-keygen(1), but not delivered + by code; ok dtucker@ + + Upstream-ID: 459e0e2bf39825e41b0811c336db2d56a1c23387 + +commit e97201feca10b5196da35819ae516d0b87cf3a50 +Author: Damien Miller +Date: Thu May 21 17:55:15 2015 +1000 + + conditionalise util.h inclusion -commit 1d1092bff8db27080155541212b420703f8b9c92 +commit 13640798c7dd011ece0a7d02841fe48e94cfa0e0 Author: djm@openbsd.org -Date: Mon Jan 26 12:16:36 2015 +0000 +Date: Thu May 21 06:44:25 2015 +0000 upstream commit - correct description of UpdateHostKeys in ssh_config.5 and - add it to -o lists for ssh, scp and sftp; pointed out by jmc@ + regress test for AuthorizedPrincipalsCommand + + Upstream-Regress-ID: c658fbf1ab6b6011dc83b73402322e396f1e1219 -commit 5104db7cbd6cdd9c5971f4358e74414862fc1022 +commit 84452c5d03c21f9bfb28c234e0dc1dc67dd817b1 Author: djm@openbsd.org -Date: Mon Jan 26 06:10:03 2015 +0000 +Date: Thu May 21 06:40:02 2015 +0000 upstream commit - correctly match ECDSA subtype (== curve) for - offered/recevied host keys. Fixes connection-killing host key mismatches when - a server offers multiple ECDSA keys with different curve type (an extremely - unlikely configuration). + regress test for AuthorizedKeysCommand arguments - ok markus, "looks mechanical" deraadt@ + Upstream-Regress-ID: bbd65c13c6b3be9a442ec115800bff9625898f12 -commit 8d4f87258f31cb6def9b3b55b6a7321d84728ff2 +commit bcc50d816187fa9a03907ac1f3a52f04a52e10d1 Author: djm@openbsd.org -Date: Mon Jan 26 03:04:45 2015 +0000 +Date: Thu May 21 06:43:30 2015 +0000 upstream commit - Host key rotation support. - - Add a hostkeys@openssh.com protocol extension (global request) for - a server to inform a client of all its available host key after - authentication has completed. The client may record the keys in - known_hosts, allowing it to upgrade to better host key algorithms - and a server to gracefully rotate its keys. + add AuthorizedPrincipalsCommand that allows getting + authorized_principals from a subprocess rather than a file, which is quite + useful in deployments with large userbases - The client side of this is controlled by a UpdateHostkeys config - option (default on). + feedback and ok markus@ - ok markus@ + Upstream-ID: aa1bdac7b16fc6d2fa3524ef08f04c7258d247f6 -commit 60b1825262b1f1e24fc72050b907189c92daf18e +commit 24232a3e5ab467678a86aa67968bbb915caffed4 Author: djm@openbsd.org -Date: Mon Jan 26 02:59:11 2015 +0000 +Date: Thu May 21 06:38:35 2015 +0000 upstream commit - small refactor and add some convenience functions; ok - markus + support arguments to AuthorizedKeysCommand + + bz#2081 loosely based on patch by Sami Hartikainen + feedback and ok markus@ + + Upstream-ID: b080387a14aa67dddd8ece67c00f268d626541f7 -commit a5a3e3328ddce91e76f71ff479022d53e35c60c9 -Author: jmc@openbsd.org -Date: Thu Jan 22 21:00:42 2015 +0000 +commit d80fbe41a57c72420c87a628444da16d09d66ca7 +Author: djm@openbsd.org +Date: Thu May 21 04:55:51 2015 +0000 upstream commit - heirarchy -> hierarchy; + refactor: split base64 encoding of pubkey into its own + sshkey_to_base64() function and out of sshkey_write(); ok markus@ + + Upstream-ID: 54fc38f5832e9b91028900819bda46c3959a0c1a -commit dcff5810a11195c57e1b3343c0d6b6f2b9974c11 +commit 7cc44ef74133a473734bbcbd3484f24d6a7328c5 Author: deraadt@openbsd.org -Date: Thu Jan 22 20:24:41 2015 +0000 +Date: Mon May 18 15:06:05 2015 +0000 upstream commit - Provide a warning about chroot misuses (which sadly, seem - to have become quite popular because shiny). sshd cannot detect/manage/do - anything about these cases, best we can do is warn in the right spot in the - man page. ok markus + getentropy() and sendsyslog() have been around long + enough. openssh-portable may want the #ifdef's but not base. discussed with + djm few weeks back + + Upstream-ID: 0506a4334de108e3fb6c66f8d6e0f9c112866926 -commit 087266ec33c76fc8d54ac5a19efacf2f4a4ca076 -Author: deraadt@openbsd.org -Date: Tue Jan 20 23:14:00 2015 +0000 +commit 9173d0fbe44de7ebcad8a15618e13a8b8d78902e +Author: dtucker@openbsd.org +Date: Fri May 15 05:44:21 2015 +0000 upstream commit - Reduce use of and transition to - throughout. ok djm markus + Use a salted hash of the lock passphrase instead of plain + text and do constant-time comparisons of it. Should prevent leaking any + information about it via timing, pointed out by Ryan Castellucci. Add a 0.1s + incrementing delay for each failed unlock attempt up to 10s. ok markus@ + (earlier version), djm@ + + Upstream-ID: c599fcc325aa1cc65496b25220b622d22208c85f -commit 57e783c8ba2c0797f93977e83b2a8644a03065d8 -Author: markus@openbsd.org -Date: Tue Jan 20 20:16:21 2015 +0000 +commit d028d5d3a697c71b21e4066d8672cacab3caa0a8 +Author: Damien Miller +Date: Tue May 5 19:10:58 2015 +1000 upstream commit - kex_setup errors are fatal() + - tedu@cvs.openbsd.org 2015/01/12 03:20:04 + [bcrypt_pbkdf.c] + rename blocks to words. bcrypt "blocks" are unrelated to blowfish blocks, + nor are they the same size. -commit 1d6424a6ff94633c221297ae8f42d54e12a20912 -Author: djm@openbsd.org -Date: Tue Jan 20 08:02:33 2015 +0000 +commit f6391d4e59b058984163ab28f4e317e7a72478f1 +Author: Damien Miller +Date: Tue May 5 19:10:23 2015 +1000 upstream commit - this test would accidentally delete agent.sh if run without - obj/ + - deraadt@cvs.openbsd.org 2015/01/08 00:30:07 + [bcrypt_pbkdf.c] + declare a local version of MIN(), call it MINIMUM() -commit 12b5f50777203e12575f1b08568281e447249ed3 -Author: djm@openbsd.org -Date: Tue Jan 20 07:56:44 2015 +0000 +commit 8ac6b13cc9113eb47cd9e86c97d7b26b4b71b77f +Author: Damien Miller +Date: Tue May 5 19:09:46 2015 +1000 upstream commit - make this compile with KERBEROS5 enabled + - djm@cvs.openbsd.org 2014/12/30 01:41:43 + [bcrypt_pbkdf.c] + typo in comment: ouput => output -commit e2cc6bef08941256817d44d146115b3478586ad4 +commit 1f792489d5cf86a4f4e3003e6e9177654033f0f2 Author: djm@openbsd.org -Date: Tue Jan 20 07:55:33 2015 +0000 +Date: Mon May 4 06:10:48 2015 +0000 upstream commit - fix hostkeys in agent; ok markus@ - -commit 1ca3e2155aa5d3801a7ae050f85c71f41fcb95b1 -Author: Damien Miller -Date: Tue Jan 20 10:11:31 2015 +1100 - - fix kex test - -commit c78a578107c7e6dcf5d30a2f34cb6581bef14029 -Author: markus@openbsd.org -Date: Mon Jan 19 20:45:25 2015 +0000 - - upstream commit + Remove pattern length argument from match_pattern_list(), we + only ever use it for strlen(pattern). + + Prompted by hanno AT hboeck.de pointing an out-of-bound read + error caused by an incorrect pattern length found using AFL + and his own tools. - finally enable the KEX tests I wrote some years ago... + ok markus@ -commit 31821d7217e686667d04935aeec99e1fc4a46e7e -Author: markus@openbsd.org -Date: Mon Jan 19 20:42:31 2015 +0000 +commit 639d6bc57b1942393ed12fb48f00bc05d4e093e4 +Author: djm@openbsd.org +Date: Fri May 1 07:10:01 2015 +0000 upstream commit - adapt to new error message (SSH_ERR_MAC_INVALID) + refactor ssh_dispatch_run_fatal() to use sshpkt_fatal() + to better report error conditions. Teach sshpkt_fatal() about ECONNRESET. + + Improves error messages on TCP connection resets. bz#2257 + + ok dtucker@ -commit d3716ca19e510e95d956ae14d5b367e364bff7f1 +commit 9559d7de34c572d4d3fd990ca211f8ec99f62c4d Author: djm@openbsd.org -Date: Mon Jan 19 17:31:13 2015 +0000 +Date: Fri May 1 07:08:08 2015 +0000 upstream commit - this test was broken in at least two ways, such that it - wasn't checking that a KRL was not excluding valid keys + a couple of parse targets were missing activep checks, + causing them to be misapplied in match context; bz#2272 diagnosis and + original patch from Sami Hartikainen ok dtucker@ -commit 3f797653748e7c2b037dacb57574c01d9ef3b4d3 -Author: markus@openbsd.org -Date: Mon Jan 19 20:32:39 2015 +0000 +commit 7e8528cad04b2775c3b7db08abf8fb42e47e6b2a +Author: djm@openbsd.org +Date: Fri May 1 04:17:51 2015 +0000 upstream commit - switch ssh-keyscan from setjmp to multiple ssh transport - layer instances ok djm@ + make handling of AuthorizedPrincipalsFile=none more + consistent with other =none options; bz#2288 from Jakub Jelen; ok dtucker@ -commit f582f0e917bb0017b00944783cd5f408bf4b0b5e -Author: markus@openbsd.org -Date: Mon Jan 19 20:30:23 2015 +0000 +commit ca430d4d9cc0f62eca3b1fb1e2928395b7ce80f7 +Author: djm@openbsd.org +Date: Fri May 1 04:03:20 2015 +0000 upstream commit - add experimental api for packet layer; ok djm@ + remove failed remote forwards established by muliplexing + from the list of active forwards; bz#2363, patch mostly by Yoann Ricordel; ok + dtucker@ -commit 48b3b2ba75181f11fca7f327058a591f4426cade -Author: markus@openbsd.org -Date: Mon Jan 19 20:20:20 2015 +0000 +commit 8312cfb8ad88657517b3e23ac8c56c8e38eb9792 +Author: djm@openbsd.org +Date: Fri May 1 04:01:58 2015 +0000 upstream commit - store compat flags in struct ssh; ok djm@ + reduce stderr spam when using ssh -S /path/mux -O forward + -R 0:... ok dtucker@ -commit 57d10cbe861a235dd269c74fb2fe248469ecee9d -Author: markus@openbsd.org -Date: Mon Jan 19 20:16:15 2015 +0000 +commit 179be0f5e62f1f492462571944e45a3da660d82b +Author: djm@openbsd.org +Date: Fri May 1 03:23:51 2015 +0000 upstream commit - adapt kex to sshbuf and struct ssh; ok djm@ + prevent authorized_keys options picked up on public key + tests without a corresponding private key authentication being applied to + other authentication methods. Reported by halex@, ok markus@ -commit 3fdc88a0def4f86aa88a5846ac079dc964c0546a -Author: markus@openbsd.org -Date: Mon Jan 19 20:07:45 2015 +0000 +commit a42d67be65b719a430b7fcaba2a4e4118382723a +Author: djm@openbsd.org +Date: Fri May 1 03:20:54 2015 +0000 upstream commit - move dispatch to struct ssh; ok djm@ + Don't make parsing of authorized_keys' environment= + option conditional on PermitUserEnv - always parse it, but only use the + result if the option is enabled. This prevents the syntax of authorized_keys + changing depending on which sshd_config options were enabled. + + bz#2329; based on patch from coladict AT gmail.com, ok dtucker@ -commit 091c302829210c41e7f57c3f094c7b9c054306f0 -Author: markus@openbsd.org -Date: Mon Jan 19 19:52:16 2015 +0000 +commit e661a86353e11592c7ed6a847e19a83609f49e77 +Author: djm@openbsd.org +Date: Mon May 4 06:10:48 2015 +0000 upstream commit - update packet.c & isolate, introduce struct ssh a) switch - packet.c to buffer api and isolate per-connection info into struct ssh b) - (de)serialization of the state is moved from monitor to packet.c c) the old - packet.c API is implemented in opacket.[ch] d) compress.c/h is removed and - integrated into packet.c with and ok djm@ + Remove pattern length argument from match_pattern_list(), we + only ever use it for strlen(pattern). + + Prompted by hanno AT hboeck.de pointing an out-of-bound read + error caused by an incorrect pattern length found using AFL + and his own tools. + + ok markus@ -commit 4e62cc68ce4ba20245d208b252e74e91d3785b74 -Author: djm@openbsd.org -Date: Mon Jan 19 17:35:48 2015 +0000 +commit 0ef1de742be2ee4b10381193fe90730925b7f027 +Author: dtucker@openbsd.org +Date: Thu Apr 23 05:01:19 2015 +0000 upstream commit - fix format strings in (disabled) debugging + Add a simple regression test for sshd's configuration + parser. Right now, all it does is run the output of sshd -T back through + itself and ensure the output is valid and invariant. -commit d85e06245907d49a2cd0cfa0abf59150ad616f42 +commit 368f83c793275faa2c52f60eaa9bdac155c4254b Author: djm@openbsd.org -Date: Mon Jan 19 06:01:32 2015 +0000 +Date: Wed Apr 22 01:38:36 2015 +0000 upstream commit - be a bit more careful in these tests to ensure that - known_hosts is clean + use correct key for nested certificate test -commit 7947810eab5fe0ad311f32a48f4d4eb1f71be6cf +commit 8d4d1bfddbbd7d21f545dc6997081d1ea1fbc99a Author: djm@openbsd.org -Date: Sun Jan 18 22:00:18 2015 +0000 +Date: Fri May 1 07:11:47 2015 +0000 upstream commit - regression test for known_host file editing using - ssh-keygen (-H / -R / -F) after hostkeys_foreach() change; feedback and ok - markus@ + mention that the user's shell from /etc/passwd is used + for commands too; bz#1459 ok dtucker@ -commit 3a2b09d147a565d8a47edf37491e149a02c0d3a3 +commit 5ab283d0016bbc9d4d71e8e5284d011bc5a930cf Author: djm@openbsd.org -Date: Sun Jan 18 19:54:46 2015 +0000 +Date: Fri May 8 07:29:00 2015 +0000 upstream commit - more and better key tests - - test signatures and verification - test certificate generation - flesh out nested cert test + whitespace - removes most of the XXX todo markers + Upstream-Regress-ID: 6b708a3e709d5b7fd37890f874bafdff1f597519 -commit 589e69fd82724cfc9738f128e4771da2e6405d0d +commit 8377d5008ad260048192e1e56ad7d15a56d103dd Author: djm@openbsd.org -Date: Sun Jan 18 19:53:58 2015 +0000 +Date: Fri May 8 07:26:13 2015 +0000 upstream commit - make the signature fuzzing test much more rigorous: - ensure that the fuzzed input cases do not match the original (using new - fuzz_matches_original() function) and check that the verification fails in - each case + whitespace at EOL + + Upstream-Regress-ID: 9c48911643d5b05173b36a012041bed4080b8554 -commit 80603c0daa2538c349c1c152405580b164d5475f +commit c28a3436fa8737709ea88e4437f8f23a6ab50359 Author: djm@openbsd.org -Date: Sun Jan 18 19:52:44 2015 +0000 +Date: Fri May 8 06:45:13 2015 +0000 upstream commit - add a fuzz_matches_original() function to the fuzzer to - detect fuzz cases that are identical to the original data. Hacky - implementation, but very useful when you need the fuzz to be different, e.g. - when verifying signature + moar whitespace at eol + + Upstream-ID: 64eaf872a3ba52ed41e494287e80d40aaba4b515 -commit 87d5495bd337e358ad69c524fcb9495208c0750b +commit 2b64c490468fd4ca35ac8d5cc31c0520dc1508bb Author: djm@openbsd.org -Date: Sun Jan 18 19:50:55 2015 +0000 +Date: Fri May 8 06:41:56 2015 +0000 upstream commit - better dumps from the fuzzer (shown on errors) - - include the original data as well as the fuzzed copy. + whitespace at EOL + + Upstream-ID: 57bcf67d666c6fc1ad798aee448fdc3f70f7ec2c -commit d59ec478c453a3fff05badbbfd96aa856364f2c2 +commit 4e636cf201ce6e7e3b9088568218f9d4e2c51712 Author: djm@openbsd.org -Date: Sun Jan 18 19:47:55 2015 +0000 +Date: Fri May 8 03:56:51 2015 +0000 upstream commit - enable hostkey-agent.sh test + whitespace at EOL -commit 26b3425170bf840e4b095e1c10bf25a0a3e3a105 -Author: djm@openbsd.org -Date: Sat Jan 17 18:54:30 2015 +0000 +commit 38b8272f823dc1dd4e29dbcee83943ed48bb12fa +Author: dtucker@openbsd.org +Date: Mon May 4 01:47:53 2015 +0000 upstream commit - unit test for hostkeys in ssh-agent + Use diff w/out -u for better portability -commit 9e06a0fb23ec55d9223b26a45bb63c7649e2f2f2 -Author: markus@openbsd.org -Date: Thu Jan 15 23:41:29 2015 +0000 +commit 297060f42d5189a4065ea1b6f0afdf6371fb0507 +Author: dtucker@openbsd.org +Date: Fri May 8 03:25:07 2015 +0000 upstream commit - add kex unit tests + Use xcalloc for permitted_adm_opens instead of xmalloc to + ensure it's zeroed. Fixes post-auth crash with permitopen=none. bz#2355, ok + djm@ -commit d2099dec6da21ae627f6289aedae6bc1d41a22ce -Author: deraadt@openbsd.org -Date: Mon Jan 19 00:32:54 2015 +0000 +commit 63ebf019be863b2d90492a85e248cf55a6e87403 +Author: djm@openbsd.org +Date: Fri May 8 03:17:49 2015 +0000 upstream commit - djm, your /usr/include tree is old + don't choke on new-format private keys encrypted with an + AEAD cipher; bz#2366, patch from Ron Frederick; ok markus@ -commit 2b3c3c76c30dc5076fe09d590f5b26880f148a54 -Author: djm@openbsd.org -Date: Sun Jan 18 21:51:19 2015 +0000 +commit f8484dac678ab3098ae522a5f03bb2530f822987 +Author: dtucker@openbsd.org +Date: Wed May 6 05:45:17 2015 +0000 upstream commit - some feedback from markus@: comment hostkeys_foreach() - context and avoid a member in it. + Clarify pseudo-terminal request behaviour and use + "pseudo-terminal" consistently. bz#1716, ok jmc@ "I like it" deraadt@. -commit cecb30bc2ba6d594366e657d664d5c494b6c8a7f -Author: djm@openbsd.org -Date: Sun Jan 18 21:49:42 2015 +0000 +commit ea139507bef8bad26e86ed99a42c7233ad115c38 +Author: dtucker@openbsd.org +Date: Wed May 6 04:07:18 2015 +0000 upstream commit - make ssh-keygen use hostkeys_foreach(). Removes some - horrendous code; ok markus@ + Blacklist DH-GEX for specific PuTTY versions known to + send non-RFC4419 DH-GEX messages rather than all versions of PuTTY. + According to Simon Tatham, 0.65 and newer versions will send RFC4419 DH-GEX + messages. ok djm@ -commit ec3d065df3a9557ea96b02d061fd821a18c1a0b9 -Author: djm@openbsd.org -Date: Sun Jan 18 21:48:09 2015 +0000 +commit b58234f00ee3872eb84f6e9e572a9a34e902e36e +Author: dtucker@openbsd.org +Date: Tue May 5 10:17:49 2015 +0000 upstream commit - convert load_hostkeys() (hostkey ordering and - known_host matching) to use the new hostkey_foreach() iterator; ok markus + WinSCP doesn't implement RFC4419 DH-GEX so flag it so we + don't offer that KEX method. ok markus@ -commit c29811cc480a260e42fd88849fc86a80c1e91038 -Author: djm@openbsd.org -Date: Sun Jan 18 21:40:23 2015 +0000 +commit d5b1507a207253b39e810e91e68f9598691b7a29 +Author: jsg@openbsd.org +Date: Tue May 5 02:48:17 2015 +0000 upstream commit - introduce hostkeys_foreach() to allow iteration over a - known_hosts file or controlled subset thereof. This will allow us to pull out - some ugly and duplicated code, and will be used to implement hostkey rotation - later. + use the sizeof the struct not the sizeof a pointer to the + struct in ssh_digest_start() + + This file is only used if ssh is built with OPENSSL=no - feedback and ok markus + ok markus@ -commit f101d8291da01bbbfd6fb8c569cfd0cc61c0d346 -Author: deraadt@openbsd.org -Date: Sun Jan 18 14:01:00 2015 +0000 +commit a647b9b8e616c231594b2710c925d31b1b8afea3 +Author: Darren Tucker +Date: Fri May 8 11:07:27 2015 +1000 - upstream commit + Put brackets around mblen() compat constant. - string truncation due to sizeof(size) ok djm markus + This might help with the reported problem cross compiling for Android + ("error: expected identifier or '(' before numeric constant") but + shouldn't hurt in any case. -commit 35d6022b55b7969fc10c261cb6aa78cc4a5fcc41 -Author: djm@openbsd.org -Date: Sun Jan 18 13:33:34 2015 +0000 +commit d1680d36e17244d9af3843aeb5025cb8e40d6c07 +Author: Darren Tucker +Date: Thu Apr 30 09:18:11 2015 +1000 - upstream commit - - avoid trailing ',' in host key algorithms + xrealloc -> xreallocarray in portable code too. -commit 7efb455789a0cb76bdcdee91c6060a3dc8f5c007 -Author: djm@openbsd.org -Date: Sun Jan 18 13:22:28 2015 +0000 +commit 531a57a3893f9fcd4aaaba8c312b612bbbcc021e +Author: dtucker@openbsd.org +Date: Wed Apr 29 03:48:56 2015 +0000 upstream commit - infer key length correctly when user specified a fully- - qualified key name instead of using the -b bits option; ok markus@ + Allow ListenAddress, Port and AddressFamily in any + order. bz#68, ok djm@, jmc@ (for the man page bit). -commit 83f8ffa6a55ccd0ce9d8a205e3e7439ec18fedf5 -Author: djm@openbsd.org -Date: Sat Jan 17 18:53:34 2015 +0000 +commit c1d5bcf1aaf1209af02f79e48ba1cbc76a87b56f +Author: jmc@openbsd.org +Date: Tue Apr 28 13:47:38 2015 +0000 upstream commit - fix hostkeys on ssh agent; found by unit test I'm about - to commit + enviroment -> environment: apologies to darren for not + spotting that first time round... -commit 369d61f17657b814124268f99c033e4dc6e436c1 -Author: schwarze@openbsd.org -Date: Fri Jan 16 16:20:23 2015 +0000 +commit 43beea053db191cac47c2cd8d3dc1930158aff1a +Author: dtucker@openbsd.org +Date: Tue Apr 28 10:25:15 2015 +0000 upstream commit - garbage collect empty .No macros mandoc warns about + Fix typo in previous -commit bb8b442d32dbdb8521d610e10d8b248d938bd747 -Author: djm@openbsd.org -Date: Fri Jan 16 15:55:07 2015 +0000 +commit 85b96ef41374f3ddc9139581f87da09b2cd9199e +Author: dtucker@openbsd.org +Date: Tue Apr 28 10:17:58 2015 +0000 upstream commit - regression: incorrect error message on - otherwise-successful ssh-keygen -A. Reported by Dmitry Orlov, via deraadt@ + Document that the TERM environment variable is not + subject to SendEnv and AcceptEnv. bz#2386, based loosely on a patch from + jjelen at redhat, help and ok jmc@ -commit 9010902954a40b59d0bf3df3ccbc3140a653e2bc +commit 88a7c598a94ff53f76df228eeaae238d2d467565 Author: djm@openbsd.org -Date: Fri Jan 16 07:19:48 2015 +0000 +Date: Mon Apr 27 21:42:48 2015 +0000 upstream commit - when hostname canonicalisation is enabled, try to parse - hostnames as addresses before looking them up for canonicalisation. fixes - bz#2074 and avoids needless DNS lookups in some cases; ok markus + Make sshd default to PermitRootLogin=no; ok deraadt@ + rpe@ -commit 2ae4f337b2a5fb2841b6b0053b49496fef844d1c -Author: deraadt@openbsd.org -Date: Fri Jan 16 06:40:12 2015 +0000 +commit 734226b4480a6c736096c729fcf6f391400599c7 +Author: djm@openbsd.org +Date: Mon Apr 27 01:52:30 2015 +0000 upstream commit - Replace with and other less - dirty headers where possible. Annotate lines with their - current reasons. Switch to PATH_MAX, NGROUPS_MAX, HOST_NAME_MAX+1, - LOGIN_NAME_MAX, etc. Change MIN() and MAX() to local definitions of - MINIMUM() and MAXIMUM() where sensible to avoid pulling in the pollution. - These are the files confirmed through binary verification. ok guenther, - millert, doug (helped with the verification protocol) + fix compilation with OPENSSL=no; ok dtucker@ -commit 3c4726f4c24118e8f1bb80bf75f1456c76df072c -Author: markus@openbsd.org -Date: Thu Jan 15 21:38:50 2015 +0000 +commit a4b9d2ce1eb7703eaf0809b0c8a82ded8aa4f1c6 +Author: dtucker@openbsd.org +Date: Mon Apr 27 00:37:53 2015 +0000 upstream commit - remove xmalloc, switch to sshbuf + Include stdio.h for FILE (used in sshkey.h) so it + compiles with OPENSSL=no. -commit e17ac01f8b763e4b83976b9e521e90a280acc097 -Author: markus@openbsd.org -Date: Thu Jan 15 21:37:14 2015 +0000 +commit dbcc652f4ca11fe04e5930c7ef18a219318c6cda +Author: djm@openbsd.org +Date: Mon Apr 27 00:21:21 2015 +0000 upstream commit - switch to sshbuf + allow "sshd -f none" to skip reading the config file, + much like "ssh -F none" does. ok dtucker -commit ddef9995a1fa6c7a8ff3b38bfe6cf724bebf13d0 -Author: naddy@openbsd.org -Date: Thu Jan 15 18:32:54 2015 +0000 +commit b7ca276fca316c952f0b90f5adb1448c8481eedc +Author: jmc@openbsd.org +Date: Fri Apr 24 06:26:49 2015 +0000 upstream commit - handle UMAC128 initialization like UMAC; ok djm@ markus@ + combine -Dd onto one line and update usage(); -commit f14564c1f7792446bca143580aef0e7ac25dcdae +commit 2ea974630d7017e4c7666d14d9dc939707613e96 Author: djm@openbsd.org -Date: Thu Jan 15 11:04:36 2015 +0000 +Date: Fri Apr 24 05:26:44 2015 +0000 upstream commit - fix regression reported by brad@ for passworded keys without - agent present - -commit 45c0fd70bb2a88061319dfff20cb12ef7b1bc47e -Author: Damien Miller -Date: Thu Jan 15 22:08:23 2015 +1100 - - make bitmap test compile + add ssh-agent -D to leave ssh-agent in foreground + without enabling debug mode; bz#2381 ok dtucker@ -commit d333f89abf7179021e5c3f28673f469abe032062 -Author: djm@openbsd.org -Date: Thu Jan 15 07:36:28 2015 +0000 +commit 8ac2ffd7aa06042f6b924c87139f2fea5c5682f7 +Author: deraadt@openbsd.org +Date: Fri Apr 24 01:36:24 2015 +0000 upstream commit - unit tests for KRL bitmap + 2*len -> use xreallocarray() ok djm -commit 7613f828f49c55ff356007ae9645038ab6682556 -Author: markus@openbsd.org -Date: Wed Jan 14 09:58:21 2015 +0000 +commit 657a5fbc0d0aff309079ff8fb386f17e964963c2 +Author: deraadt@openbsd.org +Date: Fri Apr 24 01:36:00 2015 +0000 upstream commit - re-add comment about full path + rename xrealloc() to xreallocarray() since it follows + that form. ok djm -commit 6c43b48b307c41cd656b415621a644074579a578 -Author: markus@openbsd.org -Date: Wed Jan 14 09:54:38 2015 +0000 +commit 1108ae242fdd2c304307b68ddf46aebe43ebffaa +Author: dtucker@openbsd.org +Date: Thu Apr 23 04:59:10 2015 +0000 upstream commit - don't reset to the installed sshd; connect before - reconfigure, too + Two small fixes for sshd -T: ListenAddress'es are added + to a list head so reverse the order when printing them to ensure the + behaviour remains the same, and print StreamLocalBindMask as octal with + leading zero. ok deraadt@ -commit 771bb47a1df8b69061f09462e78aa0b66cd594bf -Author: djm@openbsd.org -Date: Tue Jan 13 14:51:51 2015 +0000 +commit bd902b8473e1168f19378d5d0ae68d0c203525df +Author: dtucker@openbsd.org +Date: Thu Apr 23 04:53:53 2015 +0000 upstream commit - implement a SIGINFO handler so we can discern a stuck - fuzz test from a merely glacial one; prompted by and ok markus + Check for and reject missing arguments for + VersionAddendum and ForceCommand. bz#2281, patch from plautrba at redhat com, + ok djm@ -commit cfaa57962f8536f3cf0fd7daf4d6a55d6f6de45f +commit ca42c1758575e592239de1d5755140e054b91a0d Author: djm@openbsd.org -Date: Tue Jan 13 08:23:26 2015 +0000 +Date: Wed Apr 22 01:24:01 2015 +0000 upstream commit - use $SSH instead of installed ssh to allow override; - spotted by markus@ + unknown certificate extensions are non-fatal, so don't + fatal when they are encountered; bz#2387 reported by Bob Van Zant; ok + dtucker@ -commit 0920553d0aee117a596b03ed5b49b280d34a32c5 -Author: djm@openbsd.org -Date: Tue Jan 13 07:49:49 2015 +0000 +commit 39bfbf7caad231cc4bda6909fb1af0705bca04d8 +Author: jsg@openbsd.org +Date: Tue Apr 21 07:01:00 2015 +0000 upstream commit - regress test for PubkeyAcceptedKeyTypes; ok markus@ - -commit 27ca1a5c0095eda151934bca39a77e391f875d17 -Author: markus@openbsd.org -Date: Mon Jan 12 20:13:27 2015 +0000 - - upstream commit + Add back a backslash removed in rev 1.42 so + KEX_SERVER_ENCRYPT will include aes again. - unbreak parsing of pubkey comments; with gerhard; ok - djm/deraadt + ok deraadt@ -commit 55358f0b4e0b83bc0df81c5f854c91b11e0bb4dc +commit 6b0d576bb87eca3efd2b309fcfe4edfefc289f9c Author: djm@openbsd.org -Date: Mon Jan 12 11:46:32 2015 +0000 +Date: Fri Apr 17 13:32:09 2015 +0000 upstream commit - fatal if soft-PKCS11 library is missing rather (rather - than continue and fail with a more cryptic error) + s/recommended/required/ that private keys be og-r this + wording change was made a while ago but got accidentally reverted -commit c3554cdd2a1a62434b8161017aa76fa09718a003 +commit 44a8e7ce6f3ab4c2eb1ae49115c210b98e53c4df Author: djm@openbsd.org -Date: Mon Jan 12 11:12:38 2015 +0000 +Date: Fri Apr 17 13:25:52 2015 +0000 upstream commit - let this test all supporte key types; pointed out/ok - markus@ + don't try to cleanup NULL KEX proposals in + kex_prop_free(); found by Jukka Taimisto and Markus Hietava -commit 1129dcfc5a3e508635004bcc05a3574cb7687167 +commit 3038a191872d2882052306098c1810d14835e704 Author: djm@openbsd.org -Date: Thu Jan 15 09:40:00 2015 +0000 +Date: Fri Apr 17 13:19:22 2015 +0000 upstream commit - sync ssh-keysign, ssh-keygen and some dependencies to the - new buffer/key API; mostly mechanical, ok markus@ + use error/logit/fatal instead of fprintf(stderr, ...) + and exit(0), fix a few errors that were being printed to stdout instead of + stderr and a few non-errors that were going to stderr instead of stdout + bz#2325; ok dtucker -commit e4ebf5586452bf512da662ac277aaf6ecf0efe7c +commit a58be33cb6cd24441fa7e634db0e5babdd56f07f Author: djm@openbsd.org -Date: Thu Jan 15 07:57:08 2015 +0000 +Date: Fri Apr 17 13:16:48 2015 +0000 upstream commit - remove commented-out test code now that it has moved to a - proper unit test + debug log missing DISPLAY environment when X11 + forwarding requested; bz#1682 ok dtucker@ -commit e81cba066c1e9eb70aba0f6e7c0ff220611b370f +commit 17d4d9d9fbc8fb80e322f94d95eecc604588a474 Author: djm@openbsd.org -Date: Wed Jan 14 20:54:29 2015 +0000 +Date: Fri Apr 17 04:32:31 2015 +0000 upstream commit - whitespace + don't call record_login() in monitor when UseLogin is + enabled; bz#278 reported by drk AT sgi.com; ok dtucker -commit 141efe49542f7156cdbc2e4cd0a041d8b1aab622 -Author: djm@openbsd.org -Date: Wed Jan 14 20:05:27 2015 +0000 +commit 40132ff87b6cbc3dc05fb5df2e9d8e3afa06aafd +Author: dtucker@openbsd.org +Date: Fri Apr 17 04:12:35 2015 +0000 upstream commit - move authfd.c and its tentacles to the new buffer/key - API; ok markus@ + Add some missing options to sshd -T and fix the output + of VersionAddendum HostCertificate. bz#2346, patch from jjelen at redhat + com, ok djm. -commit 0088c57af302cda278bd26d8c3ae81d5b6f7c289 -Author: djm@openbsd.org -Date: Wed Jan 14 19:33:41 2015 +0000 +commit 6cc7cfa936afde2d829e56ee6528c7ea47a42441 +Author: dtucker@openbsd.org +Date: Thu Apr 16 23:25:50 2015 +0000 upstream commit - fix small regression: ssh-agent would return a success - message but an empty signature if asked to sign using an unknown key; ok - markus@ + Document "none" for PidFile XAuthLocation + TrustedUserCAKeys and RevokedKeys. bz#2382, feedback from jmc@, ok djm@ -commit b03ebe2c22b8166e4f64c37737f4278676e3488d -Author: Damien Miller -Date: Thu Jan 15 03:08:58 2015 +1100 +commit 15fdfc9b1c6808b26bc54d4d61a38b54541763ed +Author: dtucker@openbsd.org +Date: Wed Apr 15 23:23:25 2015 +0000 - more --without-openssl - - fix some regressions caused by upstream merges + upstream commit - enable KRLs now that they no longer require BIGNUMs - -commit bc42cc6fe784f36df225c44c93b74830027cb5a2 -Author: Damien Miller -Date: Thu Jan 15 03:08:29 2015 +1100 - - kludge around tun API mismatch betterer + Plug leak of address passed to logging. bz#2373, patch + from jjelen at redhat, ok markus@ -commit c332110291089b624fa0951fbf2d1ee6de525b9f -Author: Damien Miller -Date: Thu Jan 15 02:59:51 2015 +1100 +commit bb2289e2a47d465eaaaeff3dee2a6b7777b4c291 +Author: dtucker@openbsd.org +Date: Tue Apr 14 04:17:03 2015 +0000 - some systems lack SO_REUSEPORT + upstream commit + + Output remote username in debug output since with Host + and Match it's not always obvious what it will be. bz#2368, ok djm@ -commit 83b9678a62cbdc74eb2031cf1e1e4ffd58e233ae -Author: Damien Miller -Date: Thu Jan 15 02:35:50 2015 +1100 +commit 70860b6d07461906730632f9758ff1b7c98c695a +Author: Darren Tucker +Date: Fri Apr 17 10:56:13 2015 +1000 - fix merge botch + Format UsePAM setting when using sshd -T. + + Part of bz#2346, patch from jjelen at redhat com. -commit 0cdc5a3eb6fb383569a4da2a30705d9b90428d6b -Author: Damien Miller -Date: Thu Jan 15 02:35:33 2015 +1100 +commit ee15d9c9f0720f5a8b0b34e4b10ecf21f9824814 +Author: Darren Tucker +Date: Fri Apr 17 10:40:23 2015 +1000 - unbreak across API change + Wrap endian.h include inside ifdef (bz#2370). -commit 6e2549ac2b5e7f96cbc2d83a6e0784b120444b47 -Author: Damien Miller -Date: Thu Jan 15 02:30:18 2015 +1100 +commit 408f4c2ad4a4c41baa7b9b2b7423d875abbfa70b +Author: Darren Tucker +Date: Fri Apr 17 09:39:58 2015 +1000 - need includes.h for portable OpenSSH + Look for '${host}-ar' before 'ar'. + + This changes configure.ac to look for '${host}-ar' as set by + AC_CANONICAL_HOST before looking for the unprefixed 'ar'. + Useful when cross-compiling when all your binutils are prefixed. + + Patch from moben at exherbo org via astrand at lysator liu se and + bz#2352. -commit 72ef7c148c42db7d5632a29f137f8b87b579f2d9 -Author: Damien Miller -Date: Thu Jan 15 02:21:31 2015 +1100 +commit 673a1c16ad078d41558247ce739fe812c960acc8 +Author: Damien Miller +Date: Thu Apr 16 11:40:20 2015 +1000 - support --without-openssl at configure time - - Disables and removes dependency on OpenSSL. Many features don't - work and the set of crypto options is greatly restricted. This - will only work on system with native arc4random or /dev/urandom. - - Considered highly experimental for now. + remove dependency on arpa/telnet.h -commit 4f38c61c68ae7e3f9ee4b3c38bc86cd39f65ece9 -Author: Damien Miller -Date: Thu Jan 15 02:28:00 2015 +1100 +commit 202d443eeda1829d336595a3cfc07827e49f45ed +Author: Darren Tucker +Date: Wed Apr 15 15:59:49 2015 +1000 - add files missed in last commit + Remove duplicate include of pwd.h. bz#2337, patch from Mordy Ovits. -commit a165bab605f7be55940bb8fae977398e8c96a46d -Author: djm@openbsd.org -Date: Wed Jan 14 15:02:39 2015 +0000 +commit 597986493412c499f2bc2209420cb195f97b3668 +Author: Damien Miller +Date: Thu Apr 9 10:14:48 2015 +1000 - upstream commit - - avoid BIGNUM in KRL code by using a simple bitmap; - feedback and ok markus + platform's with openpty don't need pty_release -commit 7d845f4a0b7ec97887be204c3760e44de8bf1f32 +commit 318be28cda1fd9108f2e6f2f86b0b7589ba2aed0 Author: djm@openbsd.org -Date: Wed Jan 14 13:54:13 2015 +0000 +Date: Mon Apr 13 02:04:08 2015 +0000 upstream commit - update sftp client and server to new buffer API. pretty - much just mechanical changes; with & ok markus + deprecate ancient, pre-RFC4419 and undocumented + SSH2_MSG_KEX_DH_GEX_REQUEST_OLD message; ok markus@ deraadt@ "seems + reasonable" dtucker@ -commit 139ca81866ec1b219c717d17061e5e7ad1059e2a -Author: markus@openbsd.org -Date: Wed Jan 14 13:09:09 2015 +0000 +commit d8f391caef62378463a0e6b36f940170dadfe605 +Author: dtucker@openbsd.org +Date: Fri Apr 10 05:16:50 2015 +0000 upstream commit - switch to sshbuf/sshkey; with & ok djm@ - -commit 81bfbd0bd35683de5d7f2238b985e5f8150a9180 -Author: Damien Miller -Date: Wed Jan 14 21:48:18 2015 +1100 - - support --without-openssl at configure time - - Disables and removes dependency on OpenSSL. Many features don't - work and the set of crypto options is greatly restricted. This - will only work on system with native arc4random or /dev/urandom. - - Considered highly experimental for now. + Don't send hostkey advertisments + (hostkeys-00@openssh.com) to current versions of Tera Term as they can't + handle them. Newer versions should be OK. Patch from Bryan Drewery and + IWAMOTO Kouichi, ok djm@ -commit 54924b53af15ccdcbb9f89984512b5efef641a31 +commit 2c2cfe1a1c97eb9a08cc9817fd0678209680c636 Author: djm@openbsd.org -Date: Wed Jan 14 10:46:28 2015 +0000 - - upstream commit - - avoid an warning for the !OPENSSL case - -commit ae8b463217f7c9b66655bfc3945c050ffdaeb861 -Author: markus@openbsd.org -Date: Wed Jan 14 10:30:34 2015 +0000 +Date: Fri Apr 10 00:08:55 2015 +0000 upstream commit - swith auth-options to new sshbuf/sshkey; ok djm@ + include port number if a non-default one has been + specified; based on patch from Michael Handler -commit 540e891191b98b89ee90aacf5b14a4a68635e763 +commit 4492a4f222da4cf1e8eab12689196322e27b08c4 Author: djm@openbsd.org -Date: Wed Jan 14 10:29:45 2015 +0000 +Date: Tue Apr 7 23:00:42 2015 +0000 upstream commit - make non-OpenSSL aes-ctr work on sshd w/ privsep; ok - markus@ + treat Protocol=1,2|2,1 as Protocol=2 when compiled + without SSH1 support; ok dtucker@ millert@ -commit 60c2c4ea5e1ad0ddfe8b2877b78ed5143be79c53 -Author: markus@openbsd.org -Date: Wed Jan 14 10:24:42 2015 +0000 +commit c265e2e6e932efc6d86f6cc885dea33637a67564 +Author: miod@openbsd.org +Date: Sun Apr 5 15:43:43 2015 +0000 upstream commit - remove unneeded includes, sync my copyright across files - & whitespace; ok djm@ + Do not use int for sig_atomic_t; spotted by + christos@netbsd; ok markus@ -commit 128343bcdb0b60fc826f2733df8cf979ec1627b4 -Author: markus@openbsd.org -Date: Tue Jan 13 19:31:40 2015 +0000 +commit e7bf3a5eda6a1b02bef6096fed78527ee11e54cc +Author: Darren Tucker +Date: Tue Apr 7 10:48:04 2015 +1000 - upstream commit + Use do{}while(0) for no-op functions. - adapt mac.c to ssherr.h return codes (de-fatal) and - simplify dependencies ok djm@ + From FreeBSD. -commit e7fd952f4ea01f09ceb068721a5431ac2fd416ed -Author: djm@openbsd.org -Date: Tue Jan 13 19:04:35 2015 +0000 +commit bb99844abae2b6447272f79e7fa84134802eb4df +Author: Darren Tucker +Date: Tue Apr 7 10:47:15 2015 +1000 - upstream commit - - sync changes from libopenssh; prepared by markus@ mostly - debug output tweaks, a couple of error return value changes and some other - minor stuff + Wrap blf.h include in ifdef. From FreeBSD. -commit 76c0480a85675f03a1376167cb686abed01a3583 -Author: Damien Miller -Date: Tue Jan 13 19:38:18 2015 +1100 +commit d9b9b43656091cf0ad55c122f08fadb07dad0abd +Author: Darren Tucker +Date: Tue Apr 7 09:10:00 2015 +1000 - add --without-ssh1 option to configure + Fix misspellings of regress CONFOPTS env variables. - Allows disabling support for SSH protocol 1. + Patch from Bryan Drewery. -commit 1f729f0614d1376c3332fa1edb6a5e5cec7e9e03 +commit 3f4ea3c9ab1d32d43c9222c4351f58ca11144156 Author: djm@openbsd.org -Date: Tue Jan 13 07:39:19 2015 +0000 +Date: Fri Apr 3 22:17:27 2015 +0000 upstream commit - add sshd_config HostbasedAcceptedKeyTypes and - PubkeyAcceptedKeyTypes options to allow sshd to control what public key types - will be accepted. Currently defaults to all. Feedback & ok markus@ + correct return value in pubkey parsing, spotted by Ben Hawkes + ok markus@ -commit 816d1538c24209a93ba0560b27c4fda57c3fff65 -Author: markus@openbsd.org -Date: Mon Jan 12 20:13:27 2015 +0000 +commit 7da2be0cb9601ed25460c83aa4d44052b967ba0f +Author: djm@openbsd.org +Date: Tue Mar 31 22:59:01 2015 +0000 upstream commit - unbreak parsing of pubkey comments; with gerhard; ok - djm/deraadt + adapt to recent hostfile.c change: when parsing + known_hosts without fully parsing the keys therein, hostkeys_foreach() will + now correctly identify KEY_RSA1 keys; ok markus@ miod@ -commit 0097565f849851812df610b7b6b3c4bd414f6c62 +commit 9e1777a0d1c706714b055811c12ab8cc21033e4a Author: markus@openbsd.org -Date: Mon Jan 12 19:22:46 2015 +0000 +Date: Tue Mar 24 20:19:15 2015 +0000 upstream commit - missing error assigment on sshbuf_put_string() + use ${SSH} for -Q instead of installed ssh -commit a7f49dcb527dd17877fcb8d5c3a9a6f550e0bba5 +commit ce1b358ea414a2cc88e4430cd5a2ea7fecd9de57 Author: djm@openbsd.org -Date: Mon Jan 12 15:18:07 2015 +0000 +Date: Mon Mar 16 22:46:14 2015 +0000 upstream commit - apparently memcpy(x, NULL, 0) is undefined behaviour - according to C99 (cf. sections 7.21.1 and 7.1.4), so check skip memcpy calls - when length==0; ok markus@ + make CLEANFILES clean up more of the tests' droppings -commit 905fe30fca82f38213763616d0d26eb6790bde33 -Author: markus@openbsd.org -Date: Mon Jan 12 14:05:19 2015 +0000 +commit 398f9ef192d820b67beba01ec234d66faca65775 +Author: djm@openbsd.org +Date: Tue Mar 31 22:57:06 2015 +0000 upstream commit - free->sshkey_free; ok djm@ - -commit f067cca2bc20c86b110174c3fef04086a7f57b13 -Author: markus@openbsd.org -Date: Mon Jan 12 13:29:27 2015 +0000 - - upstream commit + downgrade error() for known_hosts parse errors to debug() + to quiet warnings from ssh1 keys present when compiled !ssh1. - allow WITH_OPENSSL w/o WITH_SSH1; ok djm@ - -commit c4bfafcc2a9300d9cfb3c15e75572d3a7d74670d -Author: djm@openbsd.org -Date: Thu Jan 8 13:10:58 2015 +0000 - - upstream commit + also identify ssh1 keys when scanning, even when compiled !ssh1 - adjust for sshkey_load_file() API change + ok markus@ miod@ -commit e752c6d547036c602b89e9e704851463bd160e32 +commit 9a47ab80030a31f2d122b8fd95bd48c408b9fcd9 Author: djm@openbsd.org -Date: Thu Jan 8 13:44:36 2015 +0000 +Date: Tue Mar 31 22:55:50 2015 +0000 upstream commit - fix ssh_config FingerprintHash evaluation order; from Petr - Lautrbach + fd leak for !ssh1 case; found by unittests; ok markus@ -commit ab24ab847b0fc94c8d5e419feecff0bcb6d6d1bf +commit c9a0805a6280681901c270755a7cd630d7c5280e Author: djm@openbsd.org -Date: Thu Jan 8 10:15:45 2015 +0000 +Date: Tue Mar 31 22:55:24 2015 +0000 upstream commit - reorder hostbased key attempts to better match the - default hostkey algorithms order in myproposal.h; ok markus@ + don't fatal when a !ssh1 sshd is reexeced from a w/ssh1 + listener; reported by miod@; ok miod@ markus@ -commit 1195f4cb07ef4b0405c839293c38600b3e9bdb46 -Author: djm@openbsd.org -Date: Thu Jan 8 10:14:08 2015 +0000 +commit 704d8c88988cae38fb755a6243b119731d223222 +Author: tobias@openbsd.org +Date: Tue Mar 31 11:06:49 2015 +0000 upstream commit - deprecate key_load_private_pem() and - sshkey_load_private_pem() interfaces. Refactor the generic key loading API to - not require pathnames to be specified (they weren't really used). - - Fixes a few other things en passant: - - Makes ed25519 keys work for hostbased authentication (ssh-keysign - previously used the PEM-only routines). - - Fixes key comment regression bz#2306: key pathnames were being lost as - comment fields. + Comments are only supported for RSA1 keys. If a user + tried to add one and entered his passphrase, explicitly clear it before exit. + This is done in all other error paths, too. - ok markus@ + ok djm -commit febbe09e4e9aff579b0c5cc1623f756862e4757d -Author: tedu@openbsd.org -Date: Wed Jan 7 18:15:07 2015 +0000 +commit 78de1673c05ea2c33e0d4a4b64ecb5186b6ea2e9 +Author: jmc@openbsd.org +Date: Mon Mar 30 18:28:37 2015 +0000 upstream commit - workaround for the Meyer, et al, Bleichenbacher Side - Channel Attack. fake up a bignum key before RSA decryption. discussed/ok djm - markus + ssh-askpass(1) is the default, overridden by SSH_ASKPASS; + diff originally from jiri b; -commit 5191df927db282d3123ca2f34a04d8d96153911a +commit 26e0bcf766fadb4a44fb6199386fb1dcab65ad00 Author: djm@openbsd.org -Date: Tue Dec 23 22:42:48 2014 +0000 +Date: Mon Mar 30 00:00:29 2015 +0000 upstream commit - KNF and add a little more debug() + fix uninitialised memory read when parsing a config file + consisting of a single nul byte. Found by hanno AT hboeck.de using AFL; ok + dtucker -commit 8abd80315d3419b20e6938f74d37e2e2b547f0b7 -Author: jmc@openbsd.org -Date: Mon Dec 22 09:26:31 2014 +0000 +commit fecede00a76fbb33a349f5121c0b2f9fbc04a777 +Author: markus@openbsd.org +Date: Thu Mar 26 19:32:19 2015 +0000 upstream commit - add fingerprinthash to the options list; + sigp and lenp are not optional in ssh_agent_sign(); ok + djm@ -commit 296ef0560f60980da01d83b9f0e1a5257826536f -Author: jmc@openbsd.org -Date: Mon Dec 22 09:24:59 2014 +0000 +commit 1b0ef3813244c78669e6d4d54c624f600945327d +Author: naddy@openbsd.org +Date: Thu Mar 26 12:32:38 2015 +0000 upstream commit - tweak previous; - -commit 462082eacbd37778a173afb6b84c6f4d898a18b5 -Author: Damien Miller -Date: Tue Dec 30 08:16:11 2014 +1100 - - avoid uninitialised free of ldns_res - - If an invalid rdclass was passed to getrrsetbyname() then - this would execute a free on an uninitialised pointer. - OpenSSH only ever calls this with a fixed and valid rdclass. - - Reported by Joshua Rogers - -commit 01b63498801053f131a0740eb9d13faf35d636c8 -Author: Damien Miller -Date: Mon Dec 29 18:10:18 2014 +1100 - - pull updated OpenBSD BCrypt PBKDF implementation - - Includes fix for 1 byte output overflow for large key length - requests (not reachable in OpenSSH). - - Pointed out by Joshua Rogers - -commit c528c1b4af2f06712177b3de9b30705752f7cbcb -Author: Damien Miller -Date: Tue Dec 23 15:26:13 2014 +1100 - - fix variable name for IPv6 case in construct_utmpx - - patch from writeonce AT midipix.org via bz#2296 - -commit 293cac52dcda123244b2e594d15592e5e481c55e -Author: Damien Miller -Date: Mon Dec 22 16:30:42 2014 +1100 - - include and use OpenBSD netcat in regress/ + don't try to load .ssh/identity by default if SSH1 is + disabled; ok markus@ -commit 8f6784f0cb56dc4fd00af3e81a10050a5785228d +commit f9b78852379b74a2d14e6fc94fe52af30b7e9c31 Author: djm@openbsd.org -Date: Mon Dec 22 09:05:17 2014 +0000 +Date: Thu Mar 26 07:00:04 2015 +0000 upstream commit - mention ssh -Q feature to list supported { MAC, cipher, - KEX, key } algorithms in more places and include the query string used to - list the relevant information; bz#2288 + ban all-zero curve25519 keys as recommended by latest + CFRG curves draft; ok markus -commit 449e11b4d7847079bd0a2daa6e3e7ea03d8ef700 -Author: jmc@openbsd.org -Date: Mon Dec 22 08:24:17 2014 +0000 +commit b8afbe2c1aaf573565e4da775261dfafc8b1ba9c +Author: djm@openbsd.org +Date: Thu Mar 26 06:59:28 2015 +0000 upstream commit - tweak previous; + relax bits needed check to allow + diffie-hellman-group1-sha1 key exchange to complete for chacha20-poly1305 was + selected as symmetric cipher; ok markus -commit 4bea0ab3290c0b9dd2aa199e932de8e7e18062d6 -Author: djm@openbsd.org -Date: Mon Dec 22 08:06:03 2014 +0000 +commit 47842f71e31da130555353c1d57a1e5a8937f1c0 +Author: markus@openbsd.org +Date: Wed Mar 25 19:29:58 2015 +0000 upstream commit - regression test for multiple required pubkey authentication; - ok markus@ + ignore v1 errors on ssh-add -D; only try v2 keys on + -l/-L (unless WITH_SSH1) ok djm@ -commit f1c4d8ec52158b6f57834b8cd839605b0a33e7f2 -Author: djm@openbsd.org -Date: Mon Dec 22 08:04:23 2014 +0000 +commit 5f57e77f91bf2230c09eca96eb5ecec39e5f2da6 +Author: markus@openbsd.org +Date: Wed Mar 25 19:21:48 2015 +0000 upstream commit - correct description of what will happen when a - AuthorizedKeysCommand is specified but AuthorizedKeysCommandUser is not (sshd - will refuse to start) + unbreak ssh_agent_sign (lenp vs *lenp) -commit 161cf419f412446635013ac49e8c660cadc36080 -Author: djm@openbsd.org -Date: Mon Dec 22 07:55:51 2014 +0000 +commit 4daeb67181054f2a377677fac919ee8f9ed3490e +Author: markus@openbsd.org +Date: Tue Mar 24 20:10:08 2015 +0000 upstream commit - make internal handling of filename arguments of "none" - more consistent with ssh. "none" arguments are now replaced with NULL when - the configuration is finalised. - - Simplifies checking later on (just need to test not-NULL rather than - that + strcmp) and cleans up some inconsistencies. ok markus@ + don't leak 'setp' on error; noted by Nicholas Lemonias; + ok djm@ -commit f69b69b8625be447b8826b21d87713874dac25a6 -Author: djm@openbsd.org -Date: Mon Dec 22 07:51:30 2014 +0000 +commit 7d4f96f9de2a18af0d9fa75ea89a4990de0344f5 +Author: markus@openbsd.org +Date: Tue Mar 24 20:09:11 2015 +0000 upstream commit - remember which public keys have been used for - authentication and refuse to accept previously-used keys. - - This allows AuthenticationMethods=publickey,publickey to require - that users authenticate using two _different_ pubkeys. - - ok markus@ + consistent check for NULL as noted by Nicholas + Lemonias; ok djm@ -commit 46ac2ed4677968224c4ca825bc98fc68dae183f0 -Author: djm@openbsd.org -Date: Mon Dec 22 07:24:11 2014 +0000 +commit df100be51354e447d9345cf1ec22e6013c0eed50 +Author: markus@openbsd.org +Date: Tue Mar 24 20:03:44 2015 +0000 upstream commit - fix passing of wildcard forward bind addresses when - connection multiplexing is in use; patch from Sami Hartikainen via bz#2324; - ok dtucker@ + correct fmt-string for size_t as noted by Nicholas + Lemonias; ok djm@ -commit 0d1b241a262e4d0a6bbfdd595489ab1b853c43a1 +commit a22b9ef21285e81775732436f7c84a27bd3f71e0 Author: djm@openbsd.org -Date: Mon Dec 22 06:14:29 2014 +0000 +Date: Tue Mar 24 09:17:21 2015 +0000 upstream commit - make this slightly easier to diff against portable - -commit 0715bcdddbf68953964058f17255bf54734b8737 -Author: Damien Miller -Date: Mon Dec 22 13:47:07 2014 +1100 - - add missing regress output file + promote chacha20-poly1305@openssh.com to be the default + cipher; ok markus -commit 1e30483c8ad2c2f39445d4a4b6ab20c241e40593 +commit 2aa9da1a3b360cf7b13e96fe1521534b91501fb5 Author: djm@openbsd.org -Date: Mon Dec 22 02:15:52 2014 +0000 +Date: Tue Mar 24 01:29:19 2015 +0000 upstream commit - adjust for new SHA256 key fingerprints and - slightly-different MD5 hex fingerprint format + Compile-time disable SSH protocol 1. You can turn it + back on using the Makefile.inc knob if you need it to talk to ancient + devices. -commit 6b40567ed722df98593ad8e6a2d2448fc2b4b151 +commit 53097b2022154edf96b4e8526af5666f979503f7 Author: djm@openbsd.org -Date: Mon Dec 22 01:14:49 2014 +0000 +Date: Tue Mar 24 01:11:12 2015 +0000 upstream commit - poll changes to netcat (usr.bin/netcat.c r1.125) broke - this test; fix it by ensuring more stdio fds are sent to devnull + fix double-negative error message "ssh1 is not + unsupported" -commit a5375ccb970f49dddf7d0ef63c9b713ede9e7260 -Author: jmc@openbsd.org -Date: Sun Dec 21 23:35:14 2014 +0000 +commit 5c27e3b6ec2db711dfcd40e6359c0bcdd0b62ea9 +Author: djm@openbsd.org +Date: Mon Mar 23 06:06:38 2015 +0000 upstream commit - tweak previous; + for ssh-keygen -A, don't try (and fail) to generate ssh + v.1 keys when compiled without SSH1 support RSA/DSA/ECDSA keys when compiled + without OpenSSL based on patch by Mike Frysinger; bz#2369 -commit b79efde5c3badf5ce4312fe608d8307eade533c5 +commit 725fd22a8c41db7de73a638539a5157b7e4424ae Author: djm@openbsd.org -Date: Sun Dec 21 23:12:42 2014 +0000 +Date: Wed Mar 18 01:44:21 2015 +0000 upstream commit - document FingerprintHash here too - -commit d16bdd8027dd116afa01324bb071a4016cdc1a75 -Author: Damien Miller -Date: Mon Dec 22 10:18:09 2014 +1100 - - missing include for base64 encoding + KRL support doesn't need OpenSSL anymore, remove #ifdefs + from around call -commit 56d1c83cdd1ac76f1c6bd41e01e80dad834f3994 +commit b07011c18e0b2e172c5fd09d21fb159a0bf5fcc7 Author: djm@openbsd.org -Date: Sun Dec 21 22:27:55 2014 +0000 +Date: Mon Mar 16 11:09:52 2015 +0000 upstream commit - Add FingerprintHash option to control algorithm used for - key fingerprints. Default changes from MD5 to SHA256 and format from hex to - base64. - - Feedback and ok naddy@ markus@ + #if 0 some more arrays used only for decrypting (we don't + use since we only need encrypt for AES-CTR) -commit 058f839fe15c51be8b3a844a76ab9a8db550be4f -Author: djm@openbsd.org -Date: Thu Dec 18 23:58:04 2014 +0000 +commit 1cb3016635898d287e9d58b50c430995652d5358 +Author: jsg@openbsd.org +Date: Wed Mar 11 00:48:39 2015 +0000 upstream commit - don't count partial authentication success as a failure - against MaxAuthTries; ok deraadt@ + add back the changes from rev 1.206, djm reverted this by + mistake in rev 1.207 diff --git a/crypto/openssh/INSTALL b/crypto/openssh/INSTALL index 71581d4e0c77..d257e28fc178 100644 --- a/crypto/openssh/INSTALL +++ b/crypto/openssh/INSTALL @@ -1,281 +1,282 @@ +1. Prerequisites ---------------- A C compiler. Any C89 or better compiler should work. Where supported, configure will attempt to enable the compiler's run-time integrity checking options. Some notes about specific compilers: - clang: -ftrapv and -sanitize=integer require the compiler-rt runtime (CC=clang LDFLAGS=--rtlib=compiler-rt ./configure) You will need working installations of Zlib and libcrypto (LibreSSL / OpenSSL) Zlib 1.1.4 or 1.2.1.2 or greater (earlier 1.2.x versions have problems): http://www.gzip.org/zlib/ libcrypto (LibreSSL or OpenSSL >= 0.9.8f < 1.1.0) LibreSSL http://www.libressl.org/ ; or OpenSSL http://www.openssl.org/ LibreSSL/OpenSSL should be compiled as a position-independent library (i.e. with -fPIC) otherwise OpenSSH will not be able to link with it. If you must use a non-position-independent libcrypto, then you may need to configure OpenSSH --without-pie. Note that because of API changes, OpenSSL 1.1.x is not currently supported. The remaining items are optional. NB. If you operating system supports /dev/random, you should configure libcrypto (LibreSSL/OpenSSL) to use it. OpenSSH relies on libcrypto's direct support of /dev/random, or failing that, either prngd or egd PRNGD: If your system lacks kernel-based random collection, the use of Lutz Jaenicke's PRNGd is recommended. http://prngd.sourceforge.net/ EGD: If the kernel lacks /dev/random the Entropy Gathering Daemon (EGD) is supported only if libcrypto supports it. http://egd.sourceforge.net/ PAM: OpenSSH can utilise Pluggable Authentication Modules (PAM) if your system supports it. PAM is standard most Linux distributions, Solaris, HP-UX 11, AIX >= 5.2, FreeBSD and NetBSD. Information about the various PAM implementations are available: Solaris PAM: http://www.sun.com/software/solaris/pam/ Linux PAM: http://www.kernel.org/pub/linux/libs/pam/ OpenPAM: http://www.openpam.org/ If you wish to build the GNOME passphrase requester, you will need the GNOME libraries and headers. GNOME: http://www.gnome.org/ Alternatively, Jim Knoble has written an excellent X11 passphrase requester. This is maintained separately at: http://www.jmknoble.net/software/x11-ssh-askpass/ TCP Wrappers: If you wish to use the TCP wrappers functionality you will need at least tcpd.h and libwrap.a, either in the standard include and library paths, or in the directory specified by --with-tcp-wrappers. Version 7.6 is known to work. http://ftp.porcupine.org/pub/security/index.html S/Key Libraries: If you wish to use --with-skey then you will need the library below installed. No other S/Key library is currently known to be supported. http://www.sparc.spb.su/solaris/skey/ LibEdit: sftp supports command-line editing via NetBSD's libedit. If your platform has it available natively you can use that, alternatively you might try these multi-platform ports: http://www.thrysoee.dk/editline/ http://sourceforge.net/projects/libedit/ LDNS: LDNS is a DNS BSD-licensed resolver library which supports DNSSEC. http://nlnetlabs.nl/projects/ldns/ Autoconf: If you modify configure.ac or configure doesn't exist (eg if you checked the code out of CVS yourself) then you will need autoconf-2.69 to rebuild the automatically generated files by running "autoreconf". Earlier versions may also work but this is not guaranteed. http://www.gnu.org/software/autoconf/ Basic Security Module (BSM): Native BSM support is know to exist in Solaris from at least 2.5.1, FreeBSD 6.1 and OS X. Alternatively, you may use the OpenBSM implementation (http://www.openbsm.org). 2. Building / Installation -------------------------- To install OpenSSH with default options: ./configure make make install This will install the OpenSSH binaries in /usr/local/bin, configuration files in /usr/local/etc, the server in /usr/local/sbin, etc. To specify a different installation prefix, use the --prefix option to configure: ./configure --prefix=/opt make make install Will install OpenSSH in /opt/{bin,etc,lib,sbin}. You can also override specific paths, for example: ./configure --prefix=/opt --sysconfdir=/etc/ssh make make install This will install the binaries in /opt/{bin,lib,sbin}, but will place the configuration files in /etc/ssh. If you are using Privilege Separation (which is enabled by default) then you will also need to create the user, group and directory used by sshd for privilege separation. See README.privsep for details. If you are using PAM, you may need to manually install a PAM control file as "/etc/pam.d/sshd" (or wherever your system prefers to keep them). Note that the service name used to start PAM is __progname, which is the basename of the path of your sshd (e.g., the service name for /usr/sbin/osshd will be osshd). If you have renamed your sshd executable, your PAM configuration may need to be modified. A generic PAM configuration is included as "contrib/sshd.pam.generic", you may need to edit it before using it on your system. If you are using a recent version of Red Hat Linux, the config file in contrib/redhat/sshd.pam should be more useful. Failure to install a valid PAM file may result in an inability to use password authentication. On HP-UX 11 and Solaris, the standard /etc/pam.conf configuration will work with sshd (sshd will match the other service name). There are a few other options to the configure script: --with-audit=[module] enable additional auditing via the specified module. Currently, drivers for "debug" (additional info via syslog) and "bsm" (Sun's Basic Security Module) are supported. --with-pam enables PAM support. If PAM support is compiled in, it must also be enabled in sshd_config (refer to the UsePAM directive). --with-prngd-socket=/some/file allows you to enable EGD or PRNGD support and to specify a PRNGd socket. Use this if your Unix lacks /dev/random and you don't want to use OpenSSH's builtin entropy collection support. --with-prngd-port=portnum allows you to enable EGD or PRNGD support and to specify a EGD localhost TCP port. Use this if your Unix lacks /dev/random and you don't want to use OpenSSH's builtin entropy collection support. --with-lastlog=FILE will specify the location of the lastlog file. ./configure searches a few locations for lastlog, but may not find it if lastlog is installed in a different place. --without-lastlog will disable lastlog support entirely. --with-osfsia, --without-osfsia will enable or disable OSF1's Security Integration Architecture. The default for OSF1 machines is enable. --with-skey=PATH will enable S/Key one time password support. You will need the S/Key libraries and header files installed for this to work. --with-tcp-wrappers will enable TCP Wrappers (/etc/hosts.allow|deny) support. --with-md5-passwords will enable the use of MD5 passwords. Enable this if your operating system uses MD5 passwords and the system crypt() does not support them directly (see the crypt(3/3c) man page). If enabled, the resulting binary will support both MD5 and traditional crypt passwords. --with-utmpx enables utmpx support. utmpx support is automatic for some platforms. --without-shadow disables shadow password support. --with-ipaddr-display forces the use of a numeric IP address in the $DISPLAY environment variable. Some broken systems need this. --with-default-path=PATH allows you to specify a default $PATH for sessions started by sshd. This replaces the standard path entirely. --with-pid-dir=PATH specifies the directory in which the sshd.pid file is created. --with-xauth=PATH specifies the location of the xauth binary --with-ssl-dir=DIR allows you to specify where your Libre/OpenSSL libraries are installed. --with-ssl-engine enables Libre/OpenSSL's (hardware) ENGINE support --with-4in6 Check for IPv4 in IPv6 mapped addresses and convert them to real (AF_INET) IPv4 addresses. Works around some quirks on Linux. If you need to pass special options to the compiler or linker, you can specify these as environment variables before running ./configure. For example: CFLAGS="-O -m486" LDFLAGS="-s" LIBS="-lrubbish" LD="/usr/foo/ld" ./configure 3. Configuration ---------------- The runtime configuration files are installed by in ${prefix}/etc or whatever you specified as your --sysconfdir (/usr/local/etc by default). The default configuration should be instantly usable, though you should review it to ensure that it matches your security requirements. To generate a host key, run "make host-key". Alternately you can do so manually using the following commands: ssh-keygen -t [type] -f /etc/ssh/ssh_host_key -N "" -for each of the types you wish to generate (rsa, dsa or ecdsaa) or +for each of the types you wish to generate (rsa, dsa or ecdsa) or ssh-keygen -A to generate keys for all supported types. Replacing /etc/ssh with the correct path to the configuration directory. (${prefix}/etc or whatever you specified with --sysconfdir during configuration) If you have configured OpenSSH with EGD support, ensure that EGD is running and has collected some Entropy. For more information on configuration, please refer to the manual pages for sshd, ssh and ssh-agent. 4. (Optional) Send survey ------------------------- $ make survey [check the contents of the file "survey" to ensure there's no information that you consider sensitive] $ make send-survey This will send configuration information for the currently configured host to a survey address. This will help determine which configurations are actually in use, and what valid combinations of configure options exist. The raw data is available only to the OpenSSH developers, however summary data may be published. 5. Problems? ------------ If you experience problems compiling, installing or running OpenSSH. Please refer to the "reporting bugs" section of the webpage at https://www.openssh.com/ diff --git a/crypto/openssh/Makefile.in b/crypto/openssh/Makefile.in index e10f3742a855..5870e9e6e6b9 100644 --- a/crypto/openssh/Makefile.in +++ b/crypto/openssh/Makefile.in @@ -1,634 +1,651 @@ # $Id: Makefile.in,v 1.365 2014/08/30 06:23:07 djm Exp $ # uncomment if you run a non bourne compatable shell. Ie. csh #SHELL = @SH@ AUTORECONF=autoreconf prefix=@prefix@ exec_prefix=@exec_prefix@ bindir=@bindir@ sbindir=@sbindir@ libexecdir=@libexecdir@ datadir=@datadir@ datarootdir=@datarootdir@ mandir=@mandir@ mansubdir=@mansubdir@ sysconfdir=@sysconfdir@ piddir=@piddir@ srcdir=@srcdir@ top_srcdir=@top_srcdir@ DESTDIR= VPATH=@srcdir@ SSH_PROGRAM=@bindir@/ssh ASKPASS_PROGRAM=$(libexecdir)/ssh-askpass SFTP_SERVER=$(libexecdir)/sftp-server SSH_KEYSIGN=$(libexecdir)/ssh-keysign SSH_PKCS11_HELPER=$(libexecdir)/ssh-pkcs11-helper PRIVSEP_PATH=@PRIVSEP_PATH@ SSH_PRIVSEP_USER=@SSH_PRIVSEP_USER@ STRIP_OPT=@STRIP_OPT@ TEST_SHELL=@TEST_SHELL@ PATHS= -DSSHDIR=\"$(sysconfdir)\" \ -D_PATH_SSH_PROGRAM=\"$(SSH_PROGRAM)\" \ -D_PATH_SSH_ASKPASS_DEFAULT=\"$(ASKPASS_PROGRAM)\" \ -D_PATH_SFTP_SERVER=\"$(SFTP_SERVER)\" \ -D_PATH_SSH_KEY_SIGN=\"$(SSH_KEYSIGN)\" \ -D_PATH_SSH_PKCS11_HELPER=\"$(SSH_PKCS11_HELPER)\" \ -D_PATH_SSH_PIDDIR=\"$(piddir)\" \ -D_PATH_PRIVSEP_CHROOT_DIR=\"$(PRIVSEP_PATH)\" CC=@CC@ LD=@LD@ CFLAGS=@CFLAGS@ CPPFLAGS=-I. -I$(srcdir) @CPPFLAGS@ $(PATHS) @DEFS@ LIBS=@LIBS@ K5LIBS=@K5LIBS@ GSSLIBS=@GSSLIBS@ SSHLIBS=@SSHLIBS@ SSHDLIBS=@SSHDLIBS@ LIBEDIT=@LIBEDIT@ AR=@AR@ AWK=@AWK@ RANLIB=@RANLIB@ INSTALL=@INSTALL@ PERL=@PERL@ SED=@SED@ ENT=@ENT@ XAUTH_PATH=@XAUTH_PATH@ LDFLAGS=-L. -Lopenbsd-compat/ @LDFLAGS@ EXEEXT=@EXEEXT@ MANFMT=@MANFMT@ TARGETS=ssh$(EXEEXT) sshd$(EXEEXT) ssh-add$(EXEEXT) ssh-keygen$(EXEEXT) ssh-keyscan${EXEEXT} ssh-keysign${EXEEXT} ssh-pkcs11-helper$(EXEEXT) ssh-agent$(EXEEXT) scp$(EXEEXT) sftp-server$(EXEEXT) sftp$(EXEEXT) LIBOPENSSH_OBJS=\ ssh_api.o \ ssherr.o \ sshbuf.o \ sshkey.o \ sshbuf-getput-basic.o \ sshbuf-misc.o \ sshbuf-getput-crypto.o \ krl.o \ bitmap.o LIBSSH_OBJS=${LIBOPENSSH_OBJS} \ authfd.o authfile.o bufaux.o bufbn.o bufec.o buffer.o \ canohost.o channels.o cipher.o cipher-aes.o cipher-aesctr.o \ cipher-bf1.o cipher-ctr.o cipher-3des1.o cleanup.o \ compat.o crc32.o deattack.o fatal.o hostfile.o \ log.o match.o md-sha256.o moduli.o nchan.o packet.o opacket.o \ readpass.o rsa.o ttymodes.o xmalloc.o addrmatch.o \ atomicio.o key.o dispatch.o mac.o uidswap.o uuencode.o misc.o utf8.o \ monitor_fdpass.o rijndael.o ssh-dss.o ssh-ecdsa.o ssh-rsa.o dh.o \ msg.o progressmeter.o dns.o entropy.o gss-genr.o umac.o umac128.o \ ssh-pkcs11.o smult_curve25519_ref.o \ poly1305.o chacha.o cipher-chachapoly.o \ ssh-ed25519.o digest-openssl.o digest-libc.o hmac.o \ sc25519.o ge25519.o fe25519.o ed25519.o verify.o hash.o blocks.o \ kex.o kexdh.o kexgex.o kexecdh.o kexc25519.o \ kexdhc.o kexgexc.o kexecdhc.o kexc25519c.o \ kexdhs.o kexgexs.o kexecdhs.o kexc25519s.o \ platform-pledge.o platform-tracing.o SSHOBJS= ssh.o readconf.o clientloop.o sshtty.o \ sshconnect.o sshconnect1.o sshconnect2.o mux.o SSHDOBJS=sshd.o auth-rhosts.o auth-passwd.o \ audit.o audit-bsm.o audit-linux.o platform.o \ sshpty.o sshlogin.o servconf.o serverloop.o \ auth.o auth2.o auth-options.o session.o \ auth2-chall.o groupaccess.o \ auth-skey.o auth-bsdauth.o auth2-hostbased.o auth2-kbdint.o \ auth2-none.o auth2-passwd.o auth2-pubkey.o \ monitor.o monitor_wrap.o auth-krb5.o \ auth2-gss.o gss-serv.o gss-serv-krb5.o \ loginrec.o auth-pam.o auth-shadow.o auth-sia.o md5crypt.o \ sftp-server.o sftp-common.o \ sandbox-null.o sandbox-rlimit.o sandbox-systrace.o sandbox-darwin.o \ sandbox-seccomp-filter.o sandbox-capsicum.o sandbox-pledge.o \ sandbox-solaris.o MANPAGES = moduli.5.out scp.1.out ssh-add.1.out ssh-agent.1.out ssh-keygen.1.out ssh-keyscan.1.out ssh.1.out sshd.8.out sftp-server.8.out sftp.1.out ssh-keysign.8.out ssh-pkcs11-helper.8.out sshd_config.5.out ssh_config.5.out MANPAGES_IN = moduli.5 scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh-keyscan.1 ssh.1 sshd.8 sftp-server.8 sftp.1 ssh-keysign.8 ssh-pkcs11-helper.8 sshd_config.5 ssh_config.5 MANTYPE = @MANTYPE@ CONFIGFILES=sshd_config.out ssh_config.out moduli.out CONFIGFILES_IN=sshd_config ssh_config moduli PATHSUBS = \ -e 's|/etc/ssh/ssh_config|$(sysconfdir)/ssh_config|g' \ -e 's|/etc/ssh/ssh_known_hosts|$(sysconfdir)/ssh_known_hosts|g' \ -e 's|/etc/ssh/sshd_config|$(sysconfdir)/sshd_config|g' \ -e 's|/usr/libexec|$(libexecdir)|g' \ -e 's|/etc/shosts.equiv|$(sysconfdir)/shosts.equiv|g' \ -e 's|/etc/ssh/ssh_host_key|$(sysconfdir)/ssh_host_key|g' \ -e 's|/etc/ssh/ssh_host_ecdsa_key|$(sysconfdir)/ssh_host_ecdsa_key|g' \ -e 's|/etc/ssh/ssh_host_dsa_key|$(sysconfdir)/ssh_host_dsa_key|g' \ -e 's|/etc/ssh/ssh_host_rsa_key|$(sysconfdir)/ssh_host_rsa_key|g' \ -e 's|/etc/ssh/ssh_host_ed25519_key|$(sysconfdir)/ssh_host_ed25519_key|g' \ -e 's|/var/run/sshd.pid|$(piddir)/sshd.pid|g' \ -e 's|/etc/moduli|$(sysconfdir)/moduli|g' \ -e 's|/etc/ssh/moduli|$(sysconfdir)/moduli|g' \ -e 's|/etc/ssh/sshrc|$(sysconfdir)/sshrc|g' \ -e 's|/usr/X11R6/bin/xauth|$(XAUTH_PATH)|g' \ -e 's|/var/empty|$(PRIVSEP_PATH)|g' \ -e 's|/usr/bin:/bin:/usr/sbin:/sbin|@user_path@|g' FIXPATHSCMD = $(SED) $(PATHSUBS) FIXALGORITHMSCMD= $(SHELL) $(srcdir)/fixalgorithms $(SED) \ @UNSUPPORTED_ALGORITHMS@ all: $(CONFIGFILES) $(MANPAGES) $(TARGETS) $(LIBSSH_OBJS): Makefile.in config.h $(SSHOBJS): Makefile.in config.h $(SSHDOBJS): Makefile.in config.h .c.o: $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ LIBCOMPAT=openbsd-compat/libopenbsd-compat.a $(LIBCOMPAT): always (cd openbsd-compat && $(MAKE)) always: libssh.a: $(LIBSSH_OBJS) $(AR) rv $@ $(LIBSSH_OBJS) $(RANLIB) $@ ssh$(EXEEXT): $(LIBCOMPAT) libssh.a $(SSHOBJS) $(LD) -o $@ $(SSHOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(SSHLIBS) $(LIBS) $(GSSLIBS) sshd$(EXEEXT): libssh.a $(LIBCOMPAT) $(SSHDOBJS) $(LD) -o $@ $(SSHDOBJS) $(LDFLAGS) -lssh -lopenbsd-compat $(SSHDLIBS) $(LIBS) $(GSSLIBS) $(K5LIBS) scp$(EXEEXT): $(LIBCOMPAT) libssh.a scp.o progressmeter.o $(LD) -o $@ scp.o progressmeter.o bufaux.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) ssh-add$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-add.o $(LD) -o $@ ssh-add.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) ssh-agent$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-agent.o ssh-pkcs11-client.o $(LD) -o $@ ssh-agent.o ssh-pkcs11-client.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) ssh-keygen$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keygen.o $(LD) -o $@ ssh-keygen.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) ssh-keysign$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keysign.o readconf.o $(LD) -o $@ ssh-keysign.o readconf.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) ssh-pkcs11-helper$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-pkcs11-helper.o ssh-pkcs11.o $(LD) -o $@ ssh-pkcs11-helper.o ssh-pkcs11.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o $(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) sftp-server$(EXEEXT): $(LIBCOMPAT) libssh.a sftp.o sftp-common.o sftp-server.o sftp-server-main.o $(LD) -o $@ sftp-server.o sftp-common.o sftp-server-main.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) sftp$(EXEEXT): $(LIBCOMPAT) libssh.a sftp.o sftp-client.o sftp-common.o sftp-glob.o progressmeter.o $(LD) -o $@ progressmeter.o sftp.o sftp-client.o sftp-common.o sftp-glob.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) $(LIBEDIT) # test driver for the loginrec code - not built by default logintest: logintest.o $(LIBCOMPAT) libssh.a loginrec.o $(LD) -o $@ logintest.o $(LDFLAGS) loginrec.o -lopenbsd-compat -lssh $(LIBS) $(MANPAGES): $(MANPAGES_IN) if test "$(MANTYPE)" = "cat"; then \ manpage=$(srcdir)/`echo $@ | sed 's/\.[1-9]\.out$$/\.0/'`; \ else \ manpage=$(srcdir)/`echo $@ | sed 's/\.out$$//'`; \ fi; \ if test "$(MANTYPE)" = "man"; then \ $(FIXPATHSCMD) $${manpage} | $(FIXALGORITHMSCMD) | \ $(AWK) -f $(srcdir)/mdoc2man.awk > $@; \ else \ $(FIXPATHSCMD) $${manpage} | $(FIXALGORITHMSCMD) > $@; \ fi $(CONFIGFILES): $(CONFIGFILES_IN) conffile=`echo $@ | sed 's/.out$$//'`; \ $(FIXPATHSCMD) $(srcdir)/$${conffile} > $@ # fake rule to stop make trying to compile moduli.o into a binary "moduli.o" moduli: echo # special case target for umac128 umac128.o: umac.c $(CC) $(CFLAGS) $(CPPFLAGS) -o umac128.o -c $(srcdir)/umac.c \ -DUMAC_OUTPUT_LEN=16 -Dumac_new=umac128_new \ -Dumac_update=umac128_update -Dumac_final=umac128_final \ -Dumac_delete=umac128_delete -Dumac_ctx=umac128_ctx clean: regressclean rm -f *.o *.a $(TARGETS) logintest config.cache config.log rm -f *.out core survey rm -f regress/unittests/test_helper/*.a rm -f regress/unittests/test_helper/*.o rm -f regress/unittests/sshbuf/*.o rm -f regress/unittests/sshbuf/test_sshbuf rm -f regress/unittests/sshkey/*.o rm -f regress/unittests/sshkey/test_sshkey rm -f regress/unittests/bitmap/*.o rm -f regress/unittests/bitmap/test_bitmap + rm -f regress/unittests/conversion/*.o + rm -f regress/unittests/conversion/test_conversion rm -f regress/unittests/hostkeys/*.o rm -f regress/unittests/hostkeys/test_hostkeys rm -f regress/unittests/kex/*.o rm -f regress/unittests/kex/test_kex rm -f regress/unittests/match/*.o rm -f regress/unittests/match/test_match rm -f regress/unittests/utf8/*.o rm -f regress/unittests/utf8/test_utf8 rm -f regress/misc/kexfuzz/*.o rm -f regress/misc/kexfuzz/kexfuzz (cd openbsd-compat && $(MAKE) clean) distclean: regressclean rm -f *.o *.a $(TARGETS) logintest config.cache config.log rm -f *.out core opensshd.init openssh.xml rm -f Makefile buildpkg.sh config.h config.status rm -f survey.sh openbsd-compat/regress/Makefile *~ rm -rf autom4te.cache rm -f regress/unittests/test_helper/*.a rm -f regress/unittests/test_helper/*.o rm -f regress/unittests/sshbuf/*.o rm -f regress/unittests/sshbuf/test_sshbuf rm -f regress/unittests/sshkey/*.o rm -f regress/unittests/sshkey/test_sshkey rm -f regress/unittests/bitmap/*.o rm -f regress/unittests/bitmap/test_bitmap + rm -f regress/unittests/conversion/*.o + rm -f regress/unittests/conversion/test_conversion rm -f regress/unittests/hostkeys/*.o rm -f regress/unittests/hostkeys/test_hostkeys rm -f regress/unittests/kex/*.o rm -f regress/unittests/kex/test_kex rm -f regress/unittests/match/*.o rm -f regress/unittests/match/test_match rm -f regress/unittests/utf8/*.o rm -f regress/unittests/utf8/test_utf8 rm -f regress/unittests/misc/kexfuzz (cd openbsd-compat && $(MAKE) distclean) if test -d pkg ; then \ rm -fr pkg ; \ fi veryclean: distclean rm -f configure config.h.in *.0 cleandir: veryclean mrproper: veryclean realclean: veryclean catman-do: @for f in $(MANPAGES_IN) ; do \ base=`echo $$f | sed 's/\..*$$//'` ; \ echo "$$f -> $$base.0" ; \ $(MANFMT) $$f | cat -v | sed -e 's/.\^H//g' \ >$$base.0 ; \ done distprep: catman-do $(AUTORECONF) -rm -rf autom4te.cache install: $(CONFIGFILES) $(MANPAGES) $(TARGETS) install-files install-sysconf host-key check-config install-nokeys: $(CONFIGFILES) $(MANPAGES) $(TARGETS) install-files install-sysconf install-nosysconf: $(CONFIGFILES) $(MANPAGES) $(TARGETS) install-files check-config: -$(DESTDIR)$(sbindir)/sshd -t -f $(DESTDIR)$(sysconfdir)/sshd_config install-files: $(srcdir)/mkinstalldirs $(DESTDIR)$(bindir) $(srcdir)/mkinstalldirs $(DESTDIR)$(sbindir) $(srcdir)/mkinstalldirs $(DESTDIR)$(mandir) $(srcdir)/mkinstalldirs $(DESTDIR)$(mandir)/$(mansubdir)1 $(srcdir)/mkinstalldirs $(DESTDIR)$(mandir)/$(mansubdir)5 $(srcdir)/mkinstalldirs $(DESTDIR)$(mandir)/$(mansubdir)8 $(srcdir)/mkinstalldirs $(DESTDIR)$(libexecdir) (umask 022 ; $(srcdir)/mkinstalldirs $(DESTDIR)$(PRIVSEP_PATH)) $(INSTALL) -m 0755 $(STRIP_OPT) ssh$(EXEEXT) $(DESTDIR)$(bindir)/ssh$(EXEEXT) $(INSTALL) -m 0755 $(STRIP_OPT) scp$(EXEEXT) $(DESTDIR)$(bindir)/scp$(EXEEXT) $(INSTALL) -m 0755 $(STRIP_OPT) ssh-add$(EXEEXT) $(DESTDIR)$(bindir)/ssh-add$(EXEEXT) $(INSTALL) -m 0755 $(STRIP_OPT) ssh-agent$(EXEEXT) $(DESTDIR)$(bindir)/ssh-agent$(EXEEXT) $(INSTALL) -m 0755 $(STRIP_OPT) ssh-keygen$(EXEEXT) $(DESTDIR)$(bindir)/ssh-keygen$(EXEEXT) $(INSTALL) -m 0755 $(STRIP_OPT) ssh-keyscan$(EXEEXT) $(DESTDIR)$(bindir)/ssh-keyscan$(EXEEXT) $(INSTALL) -m 0755 $(STRIP_OPT) sshd$(EXEEXT) $(DESTDIR)$(sbindir)/sshd$(EXEEXT) $(INSTALL) -m 4711 $(STRIP_OPT) ssh-keysign$(EXEEXT) $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT) $(INSTALL) -m 0755 $(STRIP_OPT) ssh-pkcs11-helper$(EXEEXT) $(DESTDIR)$(SSH_PKCS11_HELPER)$(EXEEXT) $(INSTALL) -m 0755 $(STRIP_OPT) sftp$(EXEEXT) $(DESTDIR)$(bindir)/sftp$(EXEEXT) $(INSTALL) -m 0755 $(STRIP_OPT) sftp-server$(EXEEXT) $(DESTDIR)$(SFTP_SERVER)$(EXEEXT) $(INSTALL) -m 644 ssh.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1 $(INSTALL) -m 644 scp.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/scp.1 $(INSTALL) -m 644 ssh-add.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-add.1 $(INSTALL) -m 644 ssh-agent.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-agent.1 $(INSTALL) -m 644 ssh-keygen.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keygen.1 $(INSTALL) -m 644 ssh-keyscan.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keyscan.1 $(INSTALL) -m 644 moduli.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/moduli.5 $(INSTALL) -m 644 sshd_config.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/sshd_config.5 $(INSTALL) -m 644 ssh_config.5.out $(DESTDIR)$(mandir)/$(mansubdir)5/ssh_config.5 $(INSTALL) -m 644 sshd.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/sshd.8 $(INSTALL) -m 644 sftp.1.out $(DESTDIR)$(mandir)/$(mansubdir)1/sftp.1 $(INSTALL) -m 644 sftp-server.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/sftp-server.8 $(INSTALL) -m 644 ssh-keysign.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-keysign.8 $(INSTALL) -m 644 ssh-pkcs11-helper.8.out $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-pkcs11-helper.8 install-sysconf: if [ ! -d $(DESTDIR)$(sysconfdir) ]; then \ $(srcdir)/mkinstalldirs $(DESTDIR)$(sysconfdir); \ fi @if [ ! -f $(DESTDIR)$(sysconfdir)/ssh_config ]; then \ $(INSTALL) -m 644 ssh_config.out $(DESTDIR)$(sysconfdir)/ssh_config; \ else \ echo "$(DESTDIR)$(sysconfdir)/ssh_config already exists, install will not overwrite"; \ fi @if [ ! -f $(DESTDIR)$(sysconfdir)/sshd_config ]; then \ $(INSTALL) -m 644 sshd_config.out $(DESTDIR)$(sysconfdir)/sshd_config; \ else \ echo "$(DESTDIR)$(sysconfdir)/sshd_config already exists, install will not overwrite"; \ fi @if [ ! -f $(DESTDIR)$(sysconfdir)/moduli ]; then \ if [ -f $(DESTDIR)$(sysconfdir)/primes ]; then \ echo "moving $(DESTDIR)$(sysconfdir)/primes to $(DESTDIR)$(sysconfdir)/moduli"; \ mv "$(DESTDIR)$(sysconfdir)/primes" "$(DESTDIR)$(sysconfdir)/moduli"; \ else \ $(INSTALL) -m 644 moduli.out $(DESTDIR)$(sysconfdir)/moduli; \ fi ; \ else \ echo "$(DESTDIR)$(sysconfdir)/moduli already exists, install will not overwrite"; \ fi host-key: ssh-keygen$(EXEEXT) @if [ -z "$(DESTDIR)" ] ; then \ ./ssh-keygen -A; \ fi host-key-force: ssh-keygen$(EXEEXT) ssh$(EXEEXT) if ./ssh -Q protocol-version | grep '^1$$' >/dev/null; then \ ./ssh-keygen -t rsa1 -f $(DESTDIR)$(sysconfdir)/ssh_host_key -N ""; \ fi ./ssh-keygen -t dsa -f $(DESTDIR)$(sysconfdir)/ssh_host_dsa_key -N "" ./ssh-keygen -t rsa -f $(DESTDIR)$(sysconfdir)/ssh_host_rsa_key -N "" ./ssh-keygen -t ed25519 -f $(DESTDIR)$(sysconfdir)/ssh_host_ed25519_key -N "" if ./ssh -Q key | grep ecdsa >/dev/null ; then \ ./ssh-keygen -t ecdsa -f $(DESTDIR)$(sysconfdir)/ssh_host_ecdsa_key -N ""; \ fi uninstallall: uninstall -rm -f $(DESTDIR)$(sysconfdir)/ssh_config -rm -f $(DESTDIR)$(sysconfdir)/sshd_config -rmdir $(DESTDIR)$(sysconfdir) -rmdir $(DESTDIR)$(bindir) -rmdir $(DESTDIR)$(sbindir) -rmdir $(DESTDIR)$(mandir)/$(mansubdir)1 -rmdir $(DESTDIR)$(mandir)/$(mansubdir)8 -rmdir $(DESTDIR)$(mandir) -rmdir $(DESTDIR)$(libexecdir) uninstall: -rm -f $(DESTDIR)$(bindir)/ssh$(EXEEXT) -rm -f $(DESTDIR)$(bindir)/scp$(EXEEXT) -rm -f $(DESTDIR)$(bindir)/ssh-add$(EXEEXT) -rm -f $(DESTDIR)$(bindir)/ssh-agent$(EXEEXT) -rm -f $(DESTDIR)$(bindir)/ssh-keygen$(EXEEXT) -rm -f $(DESTDIR)$(bindir)/ssh-keyscan$(EXEEXT) -rm -f $(DESTDIR)$(bindir)/sftp$(EXEEXT) -rm -f $(DESTDIR)$(sbindir)/sshd$(EXEEXT) -rm -r $(DESTDIR)$(SFTP_SERVER)$(EXEEXT) -rm -f $(DESTDIR)$(SSH_KEYSIGN)$(EXEEXT) -rm -f $(DESTDIR)$(SSH_PKCS11_HELPER)$(EXEEXT) -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh.1 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/scp.1 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-add.1 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-agent.1 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keygen.1 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/sftp.1 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)1/ssh-keyscan.1 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/sshd.8 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/sftp-server.8 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-keysign.8 -rm -f $(DESTDIR)$(mandir)/$(mansubdir)8/ssh-pkcs11-helper.8 regress-prep: [ -d `pwd`/regress ] || mkdir -p `pwd`/regress [ -d `pwd`/regress/unittests ] || mkdir -p `pwd`/regress/unittests [ -d `pwd`/regress/unittests/test_helper ] || \ mkdir -p `pwd`/regress/unittests/test_helper [ -d `pwd`/regress/unittests/sshbuf ] || \ mkdir -p `pwd`/regress/unittests/sshbuf [ -d `pwd`/regress/unittests/sshkey ] || \ mkdir -p `pwd`/regress/unittests/sshkey [ -d `pwd`/regress/unittests/bitmap ] || \ mkdir -p `pwd`/regress/unittests/bitmap + [ -d `pwd`/regress/unittests/conversion ] || \ + mkdir -p `pwd`/regress/unittests/conversion [ -d `pwd`/regress/unittests/hostkeys ] || \ mkdir -p `pwd`/regress/unittests/hostkeys [ -d `pwd`/regress/unittests/kex ] || \ mkdir -p `pwd`/regress/unittests/kex [ -d `pwd`/regress/unittests/match ] || \ mkdir -p `pwd`/regress/unittests/match [ -d `pwd`/regress/unittests/utf8 ] || \ mkdir -p `pwd`/regress/unittests/utf8 [ -d `pwd`/regress/misc/kexfuzz ] || \ mkdir -p `pwd`/regress/misc/kexfuzz [ -f `pwd`/regress/Makefile ] || \ ln -s `cd $(srcdir) && pwd`/regress/Makefile `pwd`/regress/Makefile REGRESSLIBS=libssh.a $(LIBCOMPAT) regress/modpipe$(EXEEXT): $(srcdir)/regress/modpipe.c $(REGRESSLIBS) $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(srcdir)/regress/modpipe.c \ $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) regress/setuid-allowed$(EXEEXT): $(srcdir)/regress/setuid-allowed.c $(REGRESSLIBS) $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(srcdir)/regress/setuid-allowed.c \ $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) regress/netcat$(EXEEXT): $(srcdir)/regress/netcat.c $(REGRESSLIBS) $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(srcdir)/regress/netcat.c \ $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) regress/check-perm$(EXEEXT): $(srcdir)/regress/check-perm.c $(REGRESSLIBS) $(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $(srcdir)/regress/check-perm.c \ $(LDFLAGS) -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) UNITTESTS_TEST_HELPER_OBJS=\ regress/unittests/test_helper/test_helper.o \ regress/unittests/test_helper/fuzz.o regress/unittests/test_helper/libtest_helper.a: ${UNITTESTS_TEST_HELPER_OBJS} $(AR) rv $@ $(UNITTESTS_TEST_HELPER_OBJS) $(RANLIB) $@ UNITTESTS_TEST_SSHBUF_OBJS=\ regress/unittests/sshbuf/tests.o \ regress/unittests/sshbuf/test_sshbuf.o \ regress/unittests/sshbuf/test_sshbuf_getput_basic.o \ regress/unittests/sshbuf/test_sshbuf_getput_crypto.o \ regress/unittests/sshbuf/test_sshbuf_misc.o \ regress/unittests/sshbuf/test_sshbuf_fuzz.o \ regress/unittests/sshbuf/test_sshbuf_getput_fuzz.o \ regress/unittests/sshbuf/test_sshbuf_fixed.o regress/unittests/sshbuf/test_sshbuf$(EXEEXT): ${UNITTESTS_TEST_SSHBUF_OBJS} \ regress/unittests/test_helper/libtest_helper.a libssh.a $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_SSHBUF_OBJS) \ regress/unittests/test_helper/libtest_helper.a \ -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) UNITTESTS_TEST_SSHKEY_OBJS=\ regress/unittests/sshkey/test_fuzz.o \ regress/unittests/sshkey/tests.o \ regress/unittests/sshkey/common.o \ regress/unittests/sshkey/test_file.o \ regress/unittests/sshkey/test_sshkey.o regress/unittests/sshkey/test_sshkey$(EXEEXT): ${UNITTESTS_TEST_SSHKEY_OBJS} \ regress/unittests/test_helper/libtest_helper.a libssh.a $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_SSHKEY_OBJS) \ regress/unittests/test_helper/libtest_helper.a \ -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) UNITTESTS_TEST_BITMAP_OBJS=\ regress/unittests/bitmap/tests.o regress/unittests/bitmap/test_bitmap$(EXEEXT): ${UNITTESTS_TEST_BITMAP_OBJS} \ regress/unittests/test_helper/libtest_helper.a libssh.a $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_BITMAP_OBJS) \ regress/unittests/test_helper/libtest_helper.a \ -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) +UNITTESTS_TEST_CONVERSION_OBJS=\ + regress/unittests/conversion/tests.o + +regress/unittests/conversion/test_conversion$(EXEEXT): \ + ${UNITTESTS_TEST_CONVERSION_OBJS} \ + regress/unittests/test_helper/libtest_helper.a libssh.a + $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_CONVERSION_OBJS) \ + regress/unittests/test_helper/libtest_helper.a \ + -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) + UNITTESTS_TEST_KEX_OBJS=\ regress/unittests/kex/tests.o \ regress/unittests/kex/test_kex.o regress/unittests/kex/test_kex$(EXEEXT): ${UNITTESTS_TEST_KEX_OBJS} \ regress/unittests/test_helper/libtest_helper.a libssh.a $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_KEX_OBJS) \ regress/unittests/test_helper/libtest_helper.a \ -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) UNITTESTS_TEST_HOSTKEYS_OBJS=\ regress/unittests/hostkeys/tests.o \ regress/unittests/hostkeys/test_iterate.o regress/unittests/hostkeys/test_hostkeys$(EXEEXT): \ ${UNITTESTS_TEST_HOSTKEYS_OBJS} \ regress/unittests/test_helper/libtest_helper.a libssh.a $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_HOSTKEYS_OBJS) \ regress/unittests/test_helper/libtest_helper.a \ -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) UNITTESTS_TEST_MATCH_OBJS=\ regress/unittests/match/tests.o regress/unittests/match/test_match$(EXEEXT): \ ${UNITTESTS_TEST_MATCH_OBJS} \ regress/unittests/test_helper/libtest_helper.a libssh.a $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_MATCH_OBJS) \ regress/unittests/test_helper/libtest_helper.a \ -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) UNITTESTS_TEST_UTF8_OBJS=\ regress/unittests/utf8/tests.o regress/unittests/utf8/test_utf8$(EXEEXT): \ ${UNITTESTS_TEST_UTF8_OBJS} \ regress/unittests/test_helper/libtest_helper.a libssh.a $(LD) -o $@ $(LDFLAGS) $(UNITTESTS_TEST_UTF8_OBJS) \ regress/unittests/test_helper/libtest_helper.a \ -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) MISC_KEX_FUZZ_OBJS=\ regress/misc/kexfuzz/kexfuzz.o regress/misc/kexfuzz/kexfuzz$(EXEEXT): ${MISC_KEX_FUZZ_OBJS} libssh.a $(LD) -o $@ $(LDFLAGS) $(MISC_KEX_FUZZ_OBJS) \ -lssh -lopenbsd-compat -lssh -lopenbsd-compat $(LIBS) regress-binaries: regress/modpipe$(EXEEXT) \ regress/setuid-allowed$(EXEEXT) \ regress/netcat$(EXEEXT) \ regress/check-perm$(EXEEXT) \ regress/unittests/sshbuf/test_sshbuf$(EXEEXT) \ regress/unittests/sshkey/test_sshkey$(EXEEXT) \ regress/unittests/bitmap/test_bitmap$(EXEEXT) \ + regress/unittests/conversion/test_conversion$(EXEEXT) \ regress/unittests/hostkeys/test_hostkeys$(EXEEXT) \ regress/unittests/kex/test_kex$(EXEEXT) \ regress/unittests/match/test_match$(EXEEXT) \ regress/unittests/utf8/test_utf8$(EXEEXT) \ regress/misc/kexfuzz/kexfuzz$(EXEEXT) -tests interop-tests t-exec: regress-prep regress-binaries $(TARGETS) +tests interop-tests t-exec unit: regress-prep regress-binaries $(TARGETS) BUILDDIR=`pwd`; \ TEST_SSH_SCP="$${BUILDDIR}/scp"; \ TEST_SSH_SSH="$${BUILDDIR}/ssh"; \ TEST_SSH_SSHD="$${BUILDDIR}/sshd"; \ TEST_SSH_SSHAGENT="$${BUILDDIR}/ssh-agent"; \ TEST_SSH_SSHADD="$${BUILDDIR}/ssh-add"; \ TEST_SSH_SSHKEYGEN="$${BUILDDIR}/ssh-keygen"; \ TEST_SSH_SSHPKCS11HELPER="$${BUILDDIR}/ssh-pkcs11-helper"; \ TEST_SSH_SSHKEYSCAN="$${BUILDDIR}/ssh-keyscan"; \ TEST_SSH_SFTP="$${BUILDDIR}/sftp"; \ TEST_SSH_SFTPSERVER="$${BUILDDIR}/sftp-server"; \ TEST_SSH_PLINK="plink"; \ TEST_SSH_PUTTYGEN="puttygen"; \ TEST_SSH_CONCH="conch"; \ TEST_SSH_IPV6="@TEST_SSH_IPV6@" ; \ TEST_SSH_UTF8="@TEST_SSH_UTF8@" ; \ TEST_SSH_ECC="@TEST_SSH_ECC@" ; \ cd $(srcdir)/regress || exit $$?; \ $(MAKE) \ .OBJDIR="$${BUILDDIR}/regress" \ .CURDIR="`pwd`" \ BUILDDIR="$${BUILDDIR}" \ OBJ="$${BUILDDIR}/regress/" \ PATH="$${BUILDDIR}:$${PATH}" \ TEST_ENV=MALLOC_OPTIONS="@TEST_MALLOC_OPTIONS@" \ TEST_MALLOC_OPTIONS="@TEST_MALLOC_OPTIONS@" \ TEST_SSH_SCP="$${TEST_SSH_SCP}" \ TEST_SSH_SSH="$${TEST_SSH_SSH}" \ TEST_SSH_SSHD="$${TEST_SSH_SSHD}" \ TEST_SSH_SSHAGENT="$${TEST_SSH_SSHAGENT}" \ TEST_SSH_SSHADD="$${TEST_SSH_SSHADD}" \ TEST_SSH_SSHKEYGEN="$${TEST_SSH_SSHKEYGEN}" \ TEST_SSH_SSHPKCS11HELPER="$${TEST_SSH_SSHPKCS11HELPER}" \ TEST_SSH_SSHKEYSCAN="$${TEST_SSH_SSHKEYSCAN}" \ TEST_SSH_SFTP="$${TEST_SSH_SFTP}" \ TEST_SSH_SFTPSERVER="$${TEST_SSH_SFTPSERVER}" \ TEST_SSH_PLINK="$${TEST_SSH_PLINK}" \ TEST_SSH_PUTTYGEN="$${TEST_SSH_PUTTYGEN}" \ TEST_SSH_CONCH="$${TEST_SSH_CONCH}" \ TEST_SSH_IPV6="$${TEST_SSH_IPV6}" \ TEST_SSH_UTF8="$${TEST_SSH_UTF8}" \ TEST_SSH_ECC="$${TEST_SSH_ECC}" \ TEST_SHELL="${TEST_SHELL}" \ EXEEXT="$(EXEEXT)" \ $@ && echo all tests passed compat-tests: $(LIBCOMPAT) (cd openbsd-compat/regress && $(MAKE)) regressclean: if [ -f regress/Makefile ] && [ -r regress/Makefile ]; then \ (cd regress && $(MAKE) clean) \ fi survey: survey.sh ssh @$(SHELL) ./survey.sh > survey @echo 'The survey results have been placed in the file "survey" in the' @echo 'current directory. Please review the file then send with' @echo '"make send-survey".' send-survey: survey mail portable-survey@mindrot.org This is the port of OpenBSD's excellent OpenSSH[0] to Linux and other Unices. OpenSSH is based on the last free version of Tatu Ylonen's sample implementation with all patent-encumbered algorithms removed (to external libraries), all known security bugs fixed, new features reintroduced and many other clean-ups. OpenSSH has been created by Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, Theo de Raadt, and Dug Song. It has a homepage at https://www.openssh.com/ This port consists of the re-introduction of autoconf support, PAM support, EGD[1]/PRNGD[2] support and replacements for OpenBSD library functions that are (regrettably) absent from other unices. This port has been best tested on AIX, Cygwin, HP-UX, Linux, MacOS/X, NetBSD, OpenBSD, OpenServer, Solaris, Unicos, and UnixWare. This version actively tracks changes in the OpenBSD CVS repository. The PAM support is now more functional than the popular packages of commercial ssh-1.2.x. It checks "account" and "session" modules for all logins, not just when using password authentication. OpenSSH depends on Zlib[3], OpenSSL[4] and optionally PAM[5]. There is now several mailing lists for this port of OpenSSH. Please refer to https://www.openssh.com/list.html for details on how to join. Please send bug reports and patches to the mailing list openssh-unix-dev@mindrot.org. The list is open to posting by unsubscribed users. Code contribution are welcomed, but please follow the OpenBSD style guidelines[6]. Please refer to the INSTALL document for information on how to install OpenSSH on your system. Damien Miller Miscellania - This version of OpenSSH is based upon code retrieved from the OpenBSD CVS repository which in turn was based on the last free sample implementation released by Tatu Ylonen. References - [0] https://www.openssh.com/ [1] http://www.lothar.com/tech/crypto/ [2] http://www.aet.tu-cottbus.de/personen/jaenicke/postfix_tls/prngd.html [3] http://www.gzip.org/zlib/ [4] http://www.openssl.org/ [5] http://www.openpam.org http://www.kernel.org/pub/linux/libs/pam/ (PAM also is standard on Solaris and HP-UX 11) [6] http://man.openbsd.org/style.9 diff --git a/crypto/openssh/auth-pam.c b/crypto/openssh/auth-pam.c index 77e9e2bae7d3..2dfb5094ddc7 100644 --- a/crypto/openssh/auth-pam.c +++ b/crypto/openssh/auth-pam.c @@ -1,1262 +1,1264 @@ /*- * Copyright (c) 2002 Networks Associates Technology, Inc. * All rights reserved. * * This software was developed for the FreeBSD Project by ThinkSec AS and * NAI Labs, the Security Research Division of Network Associates, Inc. * under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the * DARPA CHATS research program. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Copyright (c) 2003,2004 Damien Miller * Copyright (c) 2003,2004 Darren Tucker * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* Based on FreeBSD: src/crypto/openssh/auth2-pam-freebsd.c,v 1.11 2003/03/31 13:48:18 des */ #include "includes.h" #include #include #include #include #include #include #include #include #ifdef USE_PAM #if defined(HAVE_SECURITY_PAM_APPL_H) #include #elif defined (HAVE_PAM_PAM_APPL_H) #include #endif #if !defined(SSHD_PAM_SERVICE) extern char *__progname; # define SSHD_PAM_SERVICE __progname #endif /* OpenGroup RFC86.0 and XSSO specify no "const" on arguments */ #ifdef PAM_SUN_CODEBASE # define sshpam_const /* Solaris, HP-UX, SunOS */ #else # define sshpam_const const /* LinuxPAM, OpenPAM, AIX */ #endif /* Ambiguity in spec: is it an array of pointers or a pointer to an array? */ #ifdef PAM_SUN_CODEBASE # define PAM_MSG_MEMBER(msg, n, member) ((*(msg))[(n)].member) #else # define PAM_MSG_MEMBER(msg, n, member) ((msg)[(n)]->member) #endif #include "xmalloc.h" #include "buffer.h" #include "key.h" #include "hostfile.h" #include "auth.h" #include "auth-pam.h" #include "canohost.h" #include "log.h" #include "msg.h" #include "packet.h" #include "misc.h" #include "servconf.h" #include "ssh2.h" #include "auth-options.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "monitor_wrap.h" #include "blacklist_client.h" extern ServerOptions options; extern Buffer loginmsg; extern int compat20; extern u_int utmp_len; /* so we don't silently change behaviour */ #ifdef USE_POSIX_THREADS # error "USE_POSIX_THREADS replaced by UNSUPPORTED_POSIX_THREADS_HACK" #endif /* * Formerly known as USE_POSIX_THREADS, using this is completely unsupported * and generally a bad idea. Use at own risk and do not expect support if * this breaks. */ #ifdef UNSUPPORTED_POSIX_THREADS_HACK #include /* * Avoid namespace clash when *not* using pthreads for systems *with* * pthreads, which unconditionally define pthread_t via sys/types.h * (e.g. Linux) */ typedef pthread_t sp_pthread_t; #else typedef pid_t sp_pthread_t; #endif struct pam_ctxt { sp_pthread_t pam_thread; int pam_psock; int pam_csock; int pam_done; }; static void sshpam_free_ctx(void *); static struct pam_ctxt *cleanup_ctxt; #ifndef UNSUPPORTED_POSIX_THREADS_HACK /* * Simulate threads with processes. */ static int sshpam_thread_status = -1; static mysig_t sshpam_oldsig; static void sshpam_sigchld_handler(int sig) { signal(SIGCHLD, SIG_DFL); if (cleanup_ctxt == NULL) return; /* handler called after PAM cleanup, shouldn't happen */ if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, WNOHANG) <= 0) { /* PAM thread has not exitted, privsep slave must have */ kill(cleanup_ctxt->pam_thread, SIGTERM); while (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, 0) == -1) { if (errno == EINTR) continue; return; } } if (WIFSIGNALED(sshpam_thread_status) && WTERMSIG(sshpam_thread_status) == SIGTERM) return; /* terminated by pthread_cancel */ if (!WIFEXITED(sshpam_thread_status)) sigdie("PAM: authentication thread exited unexpectedly"); if (WEXITSTATUS(sshpam_thread_status) != 0) sigdie("PAM: authentication thread exited uncleanly"); } /* ARGSUSED */ static void pthread_exit(void *value) { _exit(0); } /* ARGSUSED */ static int pthread_create(sp_pthread_t *thread, const void *attr, void *(*thread_start)(void *), void *arg) { pid_t pid; struct pam_ctxt *ctx = arg; sshpam_thread_status = -1; switch ((pid = fork())) { case -1: error("fork(): %s", strerror(errno)); return (-1); case 0: close(ctx->pam_psock); ctx->pam_psock = -1; thread_start(arg); _exit(1); default: *thread = pid; close(ctx->pam_csock); ctx->pam_csock = -1; sshpam_oldsig = signal(SIGCHLD, sshpam_sigchld_handler); return (0); } } static int pthread_cancel(sp_pthread_t thread) { signal(SIGCHLD, sshpam_oldsig); return (kill(thread, SIGTERM)); } /* ARGSUSED */ static int pthread_join(sp_pthread_t thread, void **value) { int status; if (sshpam_thread_status != -1) return (sshpam_thread_status); signal(SIGCHLD, sshpam_oldsig); while (waitpid(thread, &status, 0) == -1) { if (errno == EINTR) continue; fatal("%s: waitpid: %s", __func__, strerror(errno)); } return (status); } #endif static pam_handle_t *sshpam_handle = NULL; static int sshpam_err = 0; static int sshpam_authenticated = 0; static int sshpam_session_open = 0; static int sshpam_cred_established = 0; static int sshpam_account_status = -1; static int sshpam_maxtries_reached = 0; static char **sshpam_env = NULL; static Authctxt *sshpam_authctxt = NULL; static const char *sshpam_password = NULL; /* Some PAM implementations don't implement this */ #ifndef HAVE_PAM_GETENVLIST static char ** pam_getenvlist(pam_handle_t *pamh) { /* * XXX - If necessary, we can still support envrionment passing * for platforms without pam_getenvlist by searching for known * env vars (e.g. KRB5CCNAME) from the PAM environment. */ return NULL; } #endif /* * Some platforms, notably Solaris, do not enforce password complexity * rules during pam_chauthtok() if the real uid of the calling process * is 0, on the assumption that it's being called by "passwd" run by root. * This wraps pam_chauthtok and sets/restore the real uid so PAM will do * the right thing. */ #ifdef SSHPAM_CHAUTHTOK_NEEDS_RUID static int sshpam_chauthtok_ruid(pam_handle_t *pamh, int flags) { int result; if (sshpam_authctxt == NULL) fatal("PAM: sshpam_authctxt not initialized"); if (setreuid(sshpam_authctxt->pw->pw_uid, -1) == -1) fatal("%s: setreuid failed: %s", __func__, strerror(errno)); result = pam_chauthtok(pamh, flags); if (setreuid(0, -1) == -1) fatal("%s: setreuid failed: %s", __func__, strerror(errno)); return result; } # define pam_chauthtok(a,b) (sshpam_chauthtok_ruid((a), (b))) #endif void sshpam_password_change_required(int reqd) { debug3("%s %d", __func__, reqd); if (sshpam_authctxt == NULL) fatal("%s: PAM authctxt not initialized", __func__); sshpam_authctxt->force_pwchange = reqd; if (reqd) { no_port_forwarding_flag |= 2; no_agent_forwarding_flag |= 2; no_x11_forwarding_flag |= 2; } else { no_port_forwarding_flag &= ~2; no_agent_forwarding_flag &= ~2; no_x11_forwarding_flag &= ~2; } } /* Import regular and PAM environment from subprocess */ static void import_environments(Buffer *b) { char *env; u_int i, num_env; int err; debug3("PAM: %s entering", __func__); #ifndef UNSUPPORTED_POSIX_THREADS_HACK /* Import variables set by do_pam_account */ sshpam_account_status = buffer_get_int(b); sshpam_password_change_required(buffer_get_int(b)); /* Import environment from subprocess */ num_env = buffer_get_int(b); if (num_env > 1024) fatal("%s: received %u environment variables, expected <= 1024", __func__, num_env); sshpam_env = xcalloc(num_env + 1, sizeof(*sshpam_env)); debug3("PAM: num env strings %d", num_env); for(i = 0; i < num_env; i++) sshpam_env[i] = buffer_get_string(b, NULL); sshpam_env[num_env] = NULL; /* Import PAM environment from subprocess */ num_env = buffer_get_int(b); debug("PAM: num PAM env strings %d", num_env); for(i = 0; i < num_env; i++) { env = buffer_get_string(b, NULL); #ifdef HAVE_PAM_PUTENV /* Errors are not fatal here */ if ((err = pam_putenv(sshpam_handle, env)) != PAM_SUCCESS) { error("PAM: pam_putenv: %s", pam_strerror(sshpam_handle, sshpam_err)); } #endif } #endif } /* * Conversation function for authentication thread. */ static int sshpam_thread_conv(int n, sshpam_const struct pam_message **msg, struct pam_response **resp, void *data) { Buffer buffer; struct pam_ctxt *ctxt; struct pam_response *reply; int i; debug3("PAM: %s entering, %d messages", __func__, n); *resp = NULL; if (data == NULL) { error("PAM: conversation function passed a null context"); return (PAM_CONV_ERR); } ctxt = data; if (n <= 0 || n > PAM_MAX_NUM_MSG) return (PAM_CONV_ERR); if ((reply = calloc(n, sizeof(*reply))) == NULL) return (PAM_CONV_ERR); buffer_init(&buffer); for (i = 0; i < n; ++i) { switch (PAM_MSG_MEMBER(msg, i, msg_style)) { case PAM_PROMPT_ECHO_OFF: case PAM_PROMPT_ECHO_ON: buffer_put_cstring(&buffer, PAM_MSG_MEMBER(msg, i, msg)); if (ssh_msg_send(ctxt->pam_csock, PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1) goto fail; if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1) goto fail; if (buffer_get_char(&buffer) != PAM_AUTHTOK) goto fail; reply[i].resp = buffer_get_string(&buffer, NULL); break; case PAM_ERROR_MSG: case PAM_TEXT_INFO: buffer_put_cstring(&buffer, PAM_MSG_MEMBER(msg, i, msg)); if (ssh_msg_send(ctxt->pam_csock, PAM_MSG_MEMBER(msg, i, msg_style), &buffer) == -1) goto fail; break; default: goto fail; } buffer_clear(&buffer); } buffer_free(&buffer); *resp = reply; return (PAM_SUCCESS); fail: for(i = 0; i < n; i++) { free(reply[i].resp); } free(reply); buffer_free(&buffer); return (PAM_CONV_ERR); } /* * Authentication thread. */ static void * sshpam_thread(void *ctxtp) { struct pam_ctxt *ctxt = ctxtp; Buffer buffer; struct pam_conv sshpam_conv; int flags = (options.permit_empty_passwd == 0 ? PAM_DISALLOW_NULL_AUTHTOK : 0); #ifndef UNSUPPORTED_POSIX_THREADS_HACK extern char **environ; char **env_from_pam; u_int i; const char *pam_user; const char **ptr_pam_user = &pam_user; char *tz = getenv("TZ"); sshpam_err = pam_get_item(sshpam_handle, PAM_USER, (sshpam_const void **)ptr_pam_user); if (sshpam_err != PAM_SUCCESS) goto auth_fail; environ[0] = NULL; if (tz != NULL) if (setenv("TZ", tz, 1) == -1) error("PAM: could not set TZ environment: %s", strerror(errno)); if (sshpam_authctxt != NULL) { setproctitle("%s [pam]", sshpam_authctxt->valid ? pam_user : "unknown"); } #endif sshpam_conv.conv = sshpam_thread_conv; sshpam_conv.appdata_ptr = ctxt; if (sshpam_authctxt == NULL) fatal("%s: PAM authctxt not initialized", __func__); buffer_init(&buffer); sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, (const void *)&sshpam_conv); if (sshpam_err != PAM_SUCCESS) goto auth_fail; sshpam_err = pam_authenticate(sshpam_handle, flags); if (sshpam_err == PAM_MAXTRIES) sshpam_set_maxtries_reached(1); if (sshpam_err != PAM_SUCCESS) goto auth_fail; if (compat20) { if (!do_pam_account()) { sshpam_err = PAM_ACCT_EXPIRED; goto auth_fail; } if (sshpam_authctxt->force_pwchange) { sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK); if (sshpam_err != PAM_SUCCESS) goto auth_fail; sshpam_password_change_required(0); } } buffer_put_cstring(&buffer, "OK"); #ifndef UNSUPPORTED_POSIX_THREADS_HACK /* Export variables set by do_pam_account */ buffer_put_int(&buffer, sshpam_account_status); buffer_put_int(&buffer, sshpam_authctxt->force_pwchange); /* Export any environment strings set in child */ for(i = 0; environ[i] != NULL; i++) ; /* Count */ buffer_put_int(&buffer, i); for(i = 0; environ[i] != NULL; i++) buffer_put_cstring(&buffer, environ[i]); /* Export any environment strings set by PAM in child */ env_from_pam = pam_getenvlist(sshpam_handle); for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) ; /* Count */ buffer_put_int(&buffer, i); for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) buffer_put_cstring(&buffer, env_from_pam[i]); #endif /* UNSUPPORTED_POSIX_THREADS_HACK */ /* XXX - can't do much about an error here */ ssh_msg_send(ctxt->pam_csock, sshpam_err, &buffer); buffer_free(&buffer); pthread_exit(NULL); auth_fail: buffer_put_cstring(&buffer, pam_strerror(sshpam_handle, sshpam_err)); /* XXX - can't do much about an error here */ if (sshpam_err == PAM_ACCT_EXPIRED) ssh_msg_send(ctxt->pam_csock, PAM_ACCT_EXPIRED, &buffer); else if (sshpam_maxtries_reached) ssh_msg_send(ctxt->pam_csock, PAM_MAXTRIES, &buffer); else ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer); buffer_free(&buffer); pthread_exit(NULL); return (NULL); /* Avoid warning for non-pthread case */ } void sshpam_thread_cleanup(void) { struct pam_ctxt *ctxt = cleanup_ctxt; debug3("PAM: %s entering", __func__); if (ctxt != NULL && ctxt->pam_thread != 0) { pthread_cancel(ctxt->pam_thread); pthread_join(ctxt->pam_thread, NULL); close(ctxt->pam_psock); close(ctxt->pam_csock); memset(ctxt, 0, sizeof(*ctxt)); cleanup_ctxt = NULL; } } static int sshpam_null_conv(int n, sshpam_const struct pam_message **msg, struct pam_response **resp, void *data) { debug3("PAM: %s entering, %d messages", __func__, n); return (PAM_CONV_ERR); } static struct pam_conv null_conv = { sshpam_null_conv, NULL }; static int sshpam_store_conv(int n, sshpam_const struct pam_message **msg, struct pam_response **resp, void *data) { struct pam_response *reply; int i; size_t len; debug3("PAM: %s called with %d messages", __func__, n); *resp = NULL; if (n <= 0 || n > PAM_MAX_NUM_MSG) return (PAM_CONV_ERR); if ((reply = calloc(n, sizeof(*reply))) == NULL) return (PAM_CONV_ERR); for (i = 0; i < n; ++i) { switch (PAM_MSG_MEMBER(msg, i, msg_style)) { case PAM_ERROR_MSG: case PAM_TEXT_INFO: len = strlen(PAM_MSG_MEMBER(msg, i, msg)); buffer_append(&loginmsg, PAM_MSG_MEMBER(msg, i, msg), len); buffer_append(&loginmsg, "\n", 1 ); reply[i].resp_retcode = PAM_SUCCESS; break; default: goto fail; } } *resp = reply; return (PAM_SUCCESS); fail: for(i = 0; i < n; i++) { free(reply[i].resp); } free(reply); return (PAM_CONV_ERR); } static struct pam_conv store_conv = { sshpam_store_conv, NULL }; void sshpam_cleanup(void) { if (sshpam_handle == NULL || (use_privsep && !mm_is_monitor())) return; debug("PAM: cleanup"); pam_set_item(sshpam_handle, PAM_CONV, (const void *)&null_conv); if (sshpam_session_open) { debug("PAM: closing session"); pam_close_session(sshpam_handle, PAM_SILENT); sshpam_session_open = 0; } if (sshpam_cred_established) { debug("PAM: deleting credentials"); pam_setcred(sshpam_handle, PAM_DELETE_CRED); sshpam_cred_established = 0; } sshpam_authenticated = 0; pam_end(sshpam_handle, sshpam_err); sshpam_handle = NULL; } static int sshpam_init(Authctxt *authctxt) { const char *pam_rhost, *pam_user, *user = authctxt->user; const char **ptr_pam_user = &pam_user; struct ssh *ssh = active_state; /* XXX */ if (sshpam_handle != NULL) { /* We already have a PAM context; check if the user matches */ sshpam_err = pam_get_item(sshpam_handle, PAM_USER, (sshpam_const void **)ptr_pam_user); if (sshpam_err == PAM_SUCCESS && strcmp(user, pam_user) == 0) return (0); pam_end(sshpam_handle, sshpam_err); sshpam_handle = NULL; } debug("PAM: initializing for \"%s\"", user); sshpam_err = pam_start(SSHD_PAM_SERVICE, user, &store_conv, &sshpam_handle); sshpam_authctxt = authctxt; if (sshpam_err != PAM_SUCCESS) { pam_end(sshpam_handle, sshpam_err); sshpam_handle = NULL; return (-1); } pam_rhost = auth_get_canonical_hostname(ssh, options.use_dns); debug("PAM: setting PAM_RHOST to \"%s\"", pam_rhost); sshpam_err = pam_set_item(sshpam_handle, PAM_RHOST, pam_rhost); if (sshpam_err != PAM_SUCCESS) { pam_end(sshpam_handle, sshpam_err); sshpam_handle = NULL; return (-1); } #ifdef PAM_TTY_KLUDGE /* * Some silly PAM modules (e.g. pam_time) require a TTY to operate. * sshd doesn't set the tty until too late in the auth process and * may not even set one (for tty-less connections) */ debug("PAM: setting PAM_TTY to \"ssh\""); sshpam_err = pam_set_item(sshpam_handle, PAM_TTY, "ssh"); if (sshpam_err != PAM_SUCCESS) { pam_end(sshpam_handle, sshpam_err); sshpam_handle = NULL; return (-1); } #endif return (0); } static void * sshpam_init_ctx(Authctxt *authctxt) { struct pam_ctxt *ctxt; int socks[2]; debug3("PAM: %s entering", __func__); /* * Refuse to start if we don't have PAM enabled or do_pam_account * has previously failed. */ if (!options.use_pam || sshpam_account_status == 0) return NULL; /* Initialize PAM */ if (sshpam_init(authctxt) == -1) { error("PAM: initialization failed"); return (NULL); } ctxt = xcalloc(1, sizeof *ctxt); /* Start the authentication thread */ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) { error("PAM: failed create sockets: %s", strerror(errno)); free(ctxt); return (NULL); } ctxt->pam_psock = socks[0]; ctxt->pam_csock = socks[1]; if (pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt) == -1) { error("PAM: failed to start authentication thread: %s", strerror(errno)); close(socks[0]); close(socks[1]); free(ctxt); return (NULL); } cleanup_ctxt = ctxt; return (ctxt); } static int sshpam_query(void *ctx, char **name, char **info, u_int *num, char ***prompts, u_int **echo_on) { struct ssh *ssh = active_state; /* XXX */ Buffer buffer; struct pam_ctxt *ctxt = ctx; size_t plen; u_char type; char *msg; size_t len, mlen; debug3("PAM: %s entering", __func__); buffer_init(&buffer); *name = xstrdup(""); *info = xstrdup(""); *prompts = xmalloc(sizeof(char *)); **prompts = NULL; plen = 0; *echo_on = xmalloc(sizeof(u_int)); while (ssh_msg_recv(ctxt->pam_psock, &buffer) == 0) { type = buffer_get_char(&buffer); msg = buffer_get_string(&buffer, NULL); mlen = strlen(msg); switch (type) { case PAM_PROMPT_ECHO_ON: case PAM_PROMPT_ECHO_OFF: *num = 1; len = plen + mlen + 1; **prompts = xreallocarray(**prompts, 1, len); strlcpy(**prompts + plen, msg, len - plen); plen += mlen; **echo_on = (type == PAM_PROMPT_ECHO_ON); free(msg); return (0); case PAM_ERROR_MSG: case PAM_TEXT_INFO: /* accumulate messages */ len = plen + mlen + 2; **prompts = xreallocarray(**prompts, 1, len); strlcpy(**prompts + plen, msg, len - plen); plen += mlen; strlcat(**prompts + plen, "\n", len - plen); plen++; free(msg); break; case PAM_ACCT_EXPIRED: case PAM_MAXTRIES: if (type == PAM_ACCT_EXPIRED) sshpam_account_status = 0; if (type == PAM_MAXTRIES) sshpam_set_maxtries_reached(1); /* FALLTHROUGH */ case PAM_AUTH_ERR: debug3("PAM: %s", pam_strerror(sshpam_handle, type)); if (**prompts != NULL && strlen(**prompts) != 0) { *info = **prompts; **prompts = NULL; *num = 0; **echo_on = 0; ctxt->pam_done = -1; free(msg); return 0; } /* FALLTHROUGH */ case PAM_SUCCESS: if (**prompts != NULL) { /* drain any accumulated messages */ debug("PAM: %s", **prompts); buffer_append(&loginmsg, **prompts, strlen(**prompts)); free(**prompts); **prompts = NULL; } if (type == PAM_SUCCESS) { if (!sshpam_authctxt->valid || (sshpam_authctxt->pw->pw_uid == 0 && options.permit_root_login != PERMIT_YES)) fatal("Internal error: PAM auth " "succeeded when it should have " "failed"); import_environments(&buffer); *num = 0; **echo_on = 0; ctxt->pam_done = 1; free(msg); return (0); } BLACKLIST_NOTIFY(BLACKLIST_BAD_USER, sshpam_authctxt->user); error("PAM: %s for %s%.100s from %.100s", msg, sshpam_authctxt->valid ? "" : "illegal user ", sshpam_authctxt->user, auth_get_canonical_hostname(ssh, options.use_dns)); /* FALLTHROUGH */ default: *num = 0; **echo_on = 0; free(msg); ctxt->pam_done = -1; return (-1); } } return (-1); } /* * Returns a junk password of identical length to that the user supplied. * Used to mitigate timing attacks against crypt(3)/PAM stacks that * vary processing time in proportion to password length. */ static char * fake_password(const char *wire_password) { const char junk[] = "\b\n\r\177INCORRECT"; char *ret = NULL; size_t i, l = wire_password != NULL ? strlen(wire_password) : 0; if (l >= INT_MAX) fatal("%s: password length too long: %zu", __func__, l); ret = malloc(l + 1); + if (ret == NULL) + return NULL; for (i = 0; i < l; i++) ret[i] = junk[i % (sizeof(junk) - 1)]; ret[i] = '\0'; return ret; } /* XXX - see also comment in auth-chall.c:verify_response */ static int sshpam_respond(void *ctx, u_int num, char **resp) { Buffer buffer; struct pam_ctxt *ctxt = ctx; char *fake; debug2("PAM: %s entering, %u responses", __func__, num); switch (ctxt->pam_done) { case 1: sshpam_authenticated = 1; return (0); case 0: break; default: return (-1); } if (num != 1) { error("PAM: expected one response, got %u", num); return (-1); } buffer_init(&buffer); if (sshpam_authctxt->valid && (sshpam_authctxt->pw->pw_uid != 0 || options.permit_root_login == PERMIT_YES)) buffer_put_cstring(&buffer, *resp); else { fake = fake_password(*resp); buffer_put_cstring(&buffer, fake); free(fake); } if (ssh_msg_send(ctxt->pam_psock, PAM_AUTHTOK, &buffer) == -1) { buffer_free(&buffer); return (-1); } buffer_free(&buffer); return (1); } static void sshpam_free_ctx(void *ctxtp) { struct pam_ctxt *ctxt = ctxtp; debug3("PAM: %s entering", __func__); sshpam_thread_cleanup(); free(ctxt); /* * We don't call sshpam_cleanup() here because we may need the PAM * handle at a later stage, e.g. when setting up a session. It's * still on the cleanup list, so pam_end() *will* be called before * the server process terminates. */ } KbdintDevice sshpam_device = { "pam", sshpam_init_ctx, sshpam_query, sshpam_respond, sshpam_free_ctx }; KbdintDevice mm_sshpam_device = { "pam", mm_sshpam_init_ctx, mm_sshpam_query, mm_sshpam_respond, mm_sshpam_free_ctx }; /* * This replaces auth-pam.c */ void start_pam(Authctxt *authctxt) { if (!options.use_pam) fatal("PAM: initialisation requested when UsePAM=no"); if (sshpam_init(authctxt) == -1) fatal("PAM: initialisation failed"); } void finish_pam(void) { sshpam_cleanup(); } u_int do_pam_account(void) { debug("%s: called", __func__); if (sshpam_account_status != -1) return (sshpam_account_status); sshpam_err = pam_acct_mgmt(sshpam_handle, 0); debug3("PAM: %s pam_acct_mgmt = %d (%s)", __func__, sshpam_err, pam_strerror(sshpam_handle, sshpam_err)); if (sshpam_err != PAM_SUCCESS && sshpam_err != PAM_NEW_AUTHTOK_REQD) { sshpam_account_status = 0; return (sshpam_account_status); } if (sshpam_err == PAM_NEW_AUTHTOK_REQD) sshpam_password_change_required(1); sshpam_account_status = 1; return (sshpam_account_status); } void do_pam_setcred(int init) { sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, (const void *)&store_conv); if (sshpam_err != PAM_SUCCESS) fatal("PAM: failed to set PAM_CONV: %s", pam_strerror(sshpam_handle, sshpam_err)); if (init) { debug("PAM: establishing credentials"); sshpam_err = pam_setcred(sshpam_handle, PAM_ESTABLISH_CRED); } else { debug("PAM: reinitializing credentials"); sshpam_err = pam_setcred(sshpam_handle, PAM_REINITIALIZE_CRED); } if (sshpam_err == PAM_SUCCESS) { sshpam_cred_established = 1; return; } if (sshpam_authenticated) fatal("PAM: pam_setcred(): %s", pam_strerror(sshpam_handle, sshpam_err)); else debug("PAM: pam_setcred(): %s", pam_strerror(sshpam_handle, sshpam_err)); } static int sshpam_tty_conv(int n, sshpam_const struct pam_message **msg, struct pam_response **resp, void *data) { char input[PAM_MAX_MSG_SIZE]; struct pam_response *reply; int i; debug3("PAM: %s called with %d messages", __func__, n); *resp = NULL; if (n <= 0 || n > PAM_MAX_NUM_MSG || !isatty(STDIN_FILENO)) return (PAM_CONV_ERR); if ((reply = calloc(n, sizeof(*reply))) == NULL) return (PAM_CONV_ERR); for (i = 0; i < n; ++i) { switch (PAM_MSG_MEMBER(msg, i, msg_style)) { case PAM_PROMPT_ECHO_OFF: reply[i].resp = read_passphrase(PAM_MSG_MEMBER(msg, i, msg), RP_ALLOW_STDIN); reply[i].resp_retcode = PAM_SUCCESS; break; case PAM_PROMPT_ECHO_ON: fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg)); if (fgets(input, sizeof input, stdin) == NULL) input[0] = '\0'; if ((reply[i].resp = strdup(input)) == NULL) goto fail; reply[i].resp_retcode = PAM_SUCCESS; break; case PAM_ERROR_MSG: case PAM_TEXT_INFO: fprintf(stderr, "%s\n", PAM_MSG_MEMBER(msg, i, msg)); reply[i].resp_retcode = PAM_SUCCESS; break; default: goto fail; } } *resp = reply; return (PAM_SUCCESS); fail: for(i = 0; i < n; i++) { free(reply[i].resp); } free(reply); return (PAM_CONV_ERR); } static struct pam_conv tty_conv = { sshpam_tty_conv, NULL }; /* * XXX this should be done in the authentication phase, but ssh1 doesn't * support that */ void do_pam_chauthtok(void) { if (use_privsep) fatal("Password expired (unable to change with privsep)"); sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, (const void *)&tty_conv); if (sshpam_err != PAM_SUCCESS) fatal("PAM: failed to set PAM_CONV: %s", pam_strerror(sshpam_handle, sshpam_err)); debug("PAM: changing password"); sshpam_err = pam_chauthtok(sshpam_handle, PAM_CHANGE_EXPIRED_AUTHTOK); if (sshpam_err != PAM_SUCCESS) fatal("PAM: pam_chauthtok(): %s", pam_strerror(sshpam_handle, sshpam_err)); } void do_pam_session(void) { debug3("PAM: opening session"); sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, (const void *)&store_conv); if (sshpam_err != PAM_SUCCESS) fatal("PAM: failed to set PAM_CONV: %s", pam_strerror(sshpam_handle, sshpam_err)); sshpam_err = pam_open_session(sshpam_handle, 0); if (sshpam_err == PAM_SUCCESS) sshpam_session_open = 1; else { sshpam_session_open = 0; disable_forwarding(); error("PAM: pam_open_session(): %s", pam_strerror(sshpam_handle, sshpam_err)); } } int is_pam_session_open(void) { return sshpam_session_open; } /* * Set a PAM environment string. We need to do this so that the session * modules can handle things like Kerberos/GSI credentials that appear * during the ssh authentication process. */ int do_pam_putenv(char *name, char *value) { int ret = 1; #ifdef HAVE_PAM_PUTENV char *compound; size_t len; len = strlen(name) + strlen(value) + 2; compound = xmalloc(len); snprintf(compound, len, "%s=%s", name, value); ret = pam_putenv(sshpam_handle, compound); free(compound); #endif return (ret); } char ** fetch_pam_child_environment(void) { return sshpam_env; } char ** fetch_pam_environment(void) { return (pam_getenvlist(sshpam_handle)); } void free_pam_environment(char **env) { char **envp; if (env == NULL) return; for (envp = env; *envp; envp++) free(*envp); free(env); } /* * "Blind" conversation function for password authentication. Assumes that * echo-off prompts are for the password and stores messages for later * display. */ static int sshpam_passwd_conv(int n, sshpam_const struct pam_message **msg, struct pam_response **resp, void *data) { struct pam_response *reply; int i; size_t len; debug3("PAM: %s called with %d messages", __func__, n); *resp = NULL; if (n <= 0 || n > PAM_MAX_NUM_MSG) return (PAM_CONV_ERR); if ((reply = calloc(n, sizeof(*reply))) == NULL) return (PAM_CONV_ERR); for (i = 0; i < n; ++i) { switch (PAM_MSG_MEMBER(msg, i, msg_style)) { case PAM_PROMPT_ECHO_OFF: if (sshpam_password == NULL) goto fail; if ((reply[i].resp = strdup(sshpam_password)) == NULL) goto fail; reply[i].resp_retcode = PAM_SUCCESS; break; case PAM_ERROR_MSG: case PAM_TEXT_INFO: len = strlen(PAM_MSG_MEMBER(msg, i, msg)); if (len > 0) { buffer_append(&loginmsg, PAM_MSG_MEMBER(msg, i, msg), len); buffer_append(&loginmsg, "\n", 1); } if ((reply[i].resp = strdup("")) == NULL) goto fail; reply[i].resp_retcode = PAM_SUCCESS; break; default: goto fail; } } *resp = reply; return (PAM_SUCCESS); fail: for(i = 0; i < n; i++) { free(reply[i].resp); } free(reply); return (PAM_CONV_ERR); } static struct pam_conv passwd_conv = { sshpam_passwd_conv, NULL }; /* * Attempt password authentication via PAM */ int sshpam_auth_passwd(Authctxt *authctxt, const char *password) { int flags = (options.permit_empty_passwd == 0 ? PAM_DISALLOW_NULL_AUTHTOK : 0); char *fake = NULL; if (!options.use_pam || sshpam_handle == NULL) fatal("PAM: %s called when PAM disabled or failed to " "initialise.", __func__); sshpam_password = password; sshpam_authctxt = authctxt; /* * If the user logging in is invalid, or is root but is not permitted * by PermitRootLogin, use an invalid password to prevent leaking * information via timing (eg if the PAM config has a delay on fail). */ if (!authctxt->valid || (authctxt->pw->pw_uid == 0 && options.permit_root_login != PERMIT_YES)) sshpam_password = fake = fake_password(password); sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, (const void *)&passwd_conv); if (sshpam_err != PAM_SUCCESS) fatal("PAM: %s: failed to set PAM_CONV: %s", __func__, pam_strerror(sshpam_handle, sshpam_err)); sshpam_err = pam_authenticate(sshpam_handle, flags); sshpam_password = NULL; free(fake); if (sshpam_err == PAM_MAXTRIES) sshpam_set_maxtries_reached(1); if (sshpam_err == PAM_SUCCESS && authctxt->valid) { debug("PAM: password authentication accepted for %.100s", authctxt->user); return 1; } else { debug("PAM: password authentication failed for %.100s: %s", authctxt->valid ? authctxt->user : "an illegal user", pam_strerror(sshpam_handle, sshpam_err)); return 0; } } int sshpam_get_maxtries_reached(void) { return sshpam_maxtries_reached; } void sshpam_set_maxtries_reached(int reached) { if (reached == 0 || sshpam_maxtries_reached) return; sshpam_maxtries_reached = 1; options.password_authentication = 0; options.kbd_interactive_authentication = 0; options.challenge_response_authentication = 0; } #endif /* USE_PAM */ diff --git a/crypto/openssh/auth1.c b/crypto/openssh/auth1.c deleted file mode 100644 index 189954425ac2..000000000000 --- a/crypto/openssh/auth1.c +++ /dev/null @@ -1,448 +0,0 @@ -/* $OpenBSD: auth1.c,v 1.82 2014/07/15 15:54:14 millert Exp $ */ -/* - * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland - * All rights reserved - * - * As far as I am concerned, the code I have written for this software - * can be used freely for any purpose. Any derived versions of this - * software must be clearly marked as such, and if the derived work is - * incompatible with the protocol description in the RFC file, it must be - * called by a name other than "ssh" or "Secure Shell". - */ - -#include "includes.h" - -#ifdef WITH_SSH1 - -#include - -#include -#include -#include -#include -#include - -#include "openbsd-compat/sys-queue.h" -#include "xmalloc.h" -#include "rsa.h" -#include "ssh1.h" -#include "packet.h" -#include "buffer.h" -#include "log.h" -#include "misc.h" -#include "servconf.h" -#include "compat.h" -#include "key.h" -#include "hostfile.h" -#include "auth.h" -#include "channels.h" -#include "session.h" -#include "uidswap.h" -#ifdef GSSAPI -#include "ssh-gss.h" -#endif -#include "monitor_wrap.h" -#include "buffer.h" -#include "blacklist_client.h" - -/* import */ -extern ServerOptions options; -extern Buffer loginmsg; - -static int auth1_process_password(Authctxt *); -static int auth1_process_rsa(Authctxt *); -static int auth1_process_rhosts_rsa(Authctxt *); -static int auth1_process_tis_challenge(Authctxt *); -static int auth1_process_tis_response(Authctxt *); - -static char *client_user = NULL; /* Used to fill in remote user for PAM */ - -struct AuthMethod1 { - int type; - char *name; - int *enabled; - int (*method)(Authctxt *); -}; - -const struct AuthMethod1 auth1_methods[] = { - { - SSH_CMSG_AUTH_PASSWORD, "password", - &options.password_authentication, auth1_process_password - }, - { - SSH_CMSG_AUTH_RSA, "rsa", - &options.rsa_authentication, auth1_process_rsa - }, - { - SSH_CMSG_AUTH_RHOSTS_RSA, "rhosts-rsa", - &options.rhosts_rsa_authentication, auth1_process_rhosts_rsa - }, - { - SSH_CMSG_AUTH_TIS, "challenge-response", - &options.challenge_response_authentication, - auth1_process_tis_challenge - }, - { - SSH_CMSG_AUTH_TIS_RESPONSE, "challenge-response", - &options.challenge_response_authentication, - auth1_process_tis_response - }, - { -1, NULL, NULL, NULL} -}; - -static const struct AuthMethod1 -*lookup_authmethod1(int type) -{ - int i; - - for (i = 0; auth1_methods[i].name != NULL; i++) - if (auth1_methods[i].type == type) - return (&(auth1_methods[i])); - - return (NULL); -} - -static char * -get_authname(int type) -{ - const struct AuthMethod1 *a; - static char buf[64]; - - if ((a = lookup_authmethod1(type)) != NULL) - return (a->name); - snprintf(buf, sizeof(buf), "bad-auth-msg-%d", type); - return (buf); -} - -/*ARGSUSED*/ -static int -auth1_process_password(Authctxt *authctxt) -{ - int authenticated = 0; - char *password; - u_int dlen; - - /* - * Read user password. It is in plain text, but was - * transmitted over the encrypted channel so it is - * not visible to an outside observer. - */ - password = packet_get_string(&dlen); - packet_check_eom(); - - /* Try authentication with the password. */ - authenticated = PRIVSEP(auth_password(authctxt, password)); - - explicit_bzero(password, dlen); - free(password); - - return (authenticated); -} - -/*ARGSUSED*/ -static int -auth1_process_rsa(Authctxt *authctxt) -{ - int authenticated = 0; - BIGNUM *n; - - /* RSA authentication requested. */ - if ((n = BN_new()) == NULL) - fatal("do_authloop: BN_new failed"); - packet_get_bignum(n); - packet_check_eom(); - authenticated = auth_rsa(authctxt, n); - BN_clear_free(n); - - return (authenticated); -} - -/*ARGSUSED*/ -static int -auth1_process_rhosts_rsa(Authctxt *authctxt) -{ - int keybits, authenticated = 0; - u_int bits; - Key *client_host_key; - u_int ulen; - - /* - * Get client user name. Note that we just have to - * trust the client; root on the client machine can - * claim to be any user. - */ - client_user = packet_get_cstring(&ulen); - - /* Get the client host key. */ - client_host_key = key_new(KEY_RSA1); - bits = packet_get_int(); - packet_get_bignum(client_host_key->rsa->e); - packet_get_bignum(client_host_key->rsa->n); - - keybits = BN_num_bits(client_host_key->rsa->n); - if (keybits < 0 || bits != (u_int)keybits) { - verbose("Warning: keysize mismatch for client_host_key: " - "actual %d, announced %d", - BN_num_bits(client_host_key->rsa->n), bits); - } - packet_check_eom(); - - authenticated = auth_rhosts_rsa(authctxt, client_user, - client_host_key); - key_free(client_host_key); - - auth_info(authctxt, "ruser %.100s", client_user); - - return (authenticated); -} - -/*ARGSUSED*/ -static int -auth1_process_tis_challenge(Authctxt *authctxt) -{ - char *challenge; - - if ((challenge = get_challenge(authctxt)) == NULL) - return (0); - - debug("sending challenge '%s'", challenge); - packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE); - packet_put_cstring(challenge); - free(challenge); - packet_send(); - packet_write_wait(); - - return (-1); -} - -/*ARGSUSED*/ -static int -auth1_process_tis_response(Authctxt *authctxt) -{ - int authenticated = 0; - char *response; - u_int dlen; - - response = packet_get_string(&dlen); - packet_check_eom(); - authenticated = verify_response(authctxt, response); - explicit_bzero(response, dlen); - free(response); - - return (authenticated); -} - -/* - * read packets, try to authenticate the user and - * return only if authentication is successful - */ -static void -do_authloop(Authctxt *authctxt) -{ - int authenticated = 0; - int prev = 0, type = 0; - const struct AuthMethod1 *meth; - - debug("Attempting authentication for %s%.100s.", - authctxt->valid ? "" : "invalid user ", authctxt->user); - - /* If the user has no password, accept authentication immediately. */ - if (options.permit_empty_passwd && options.password_authentication && -#ifdef KRB5 - (!options.kerberos_authentication || options.kerberos_or_local_passwd) && -#endif - PRIVSEP(auth_password(authctxt, ""))) { -#ifdef USE_PAM - if (options.use_pam && (PRIVSEP(do_pam_account()))) -#endif - { - auth_log(authctxt, 1, 0, "without authentication", - NULL); - return; - } - } - - /* Indicate that authentication is needed. */ - packet_start(SSH_SMSG_FAILURE); - packet_send(); - packet_write_wait(); - - for (;;) { - /* default to fail */ - authenticated = 0; - - - /* Get a packet from the client. */ - prev = type; - type = packet_read(); - - /* - * If we started challenge-response authentication but the - * next packet is not a response to our challenge, release - * the resources allocated by get_challenge() (which would - * normally have been released by verify_response() had we - * received such a response) - */ - if (prev == SSH_CMSG_AUTH_TIS && - type != SSH_CMSG_AUTH_TIS_RESPONSE) - abandon_challenge_response(authctxt); - - if (authctxt->failures >= options.max_authtries) - goto skip; - if ((meth = lookup_authmethod1(type)) == NULL) { - logit("Unknown message during authentication: " - "type %d", type); - goto skip; - } - - if (!*(meth->enabled)) { - verbose("%s authentication disabled.", meth->name); - goto skip; - } - - authenticated = meth->method(authctxt); - if (authenticated == -1) - continue; /* "postponed" */ - -#ifdef BSD_AUTH - if (authctxt->as) { - auth_close(authctxt->as); - authctxt->as = NULL; - } -#endif - if (!authctxt->valid && authenticated) - fatal("INTERNAL ERROR: authenticated invalid user %s", - authctxt->user); - -#ifdef _UNICOS - if (authenticated && cray_access_denied(authctxt->user)) { - authenticated = 0; - fatal("Access denied for user %s.",authctxt->user); - } -#endif /* _UNICOS */ - -#ifndef HAVE_CYGWIN - /* Special handling for root */ - if (authenticated && authctxt->pw->pw_uid == 0 && - !auth_root_allowed(meth->name)) { - authenticated = 0; -# ifdef SSH_AUDIT_EVENTS - PRIVSEP(audit_event(SSH_LOGIN_ROOT_DENIED)); -# endif - } -#endif - -#ifdef USE_PAM - if (options.use_pam && authenticated && - !PRIVSEP(do_pam_account())) { - char *msg; - size_t len; - - BLACKLIST_NOTIFY(BLACKLIST_AUTH_FAIL, "ssh"); - error("Access denied for user %s by PAM account " - "configuration", authctxt->user); - len = buffer_len(&loginmsg); - buffer_append(&loginmsg, "\0", 1); - msg = buffer_ptr(&loginmsg); - /* strip trailing newlines */ - if (len > 0) - while (len > 0 && msg[--len] == '\n') - msg[len] = '\0'; - else - msg = "Access denied."; - packet_disconnect("%s", msg); - } -#endif - - skip: - /* Log before sending the reply */ - auth_log(authctxt, authenticated, 0, get_authname(type), NULL); - - free(client_user); - client_user = NULL; - - if (authenticated) - return; - - BLACKLIST_NOTIFY(BLACKLIST_AUTH_FAIL, "ssh"); - if (++authctxt->failures >= options.max_authtries) { -#ifdef SSH_AUDIT_EVENTS - PRIVSEP(audit_event(SSH_LOGIN_EXCEED_MAXTRIES)); -#endif - auth_maxtries_exceeded(authctxt); - } - - packet_start(SSH_SMSG_FAILURE); - packet_send(); - packet_write_wait(); - } -} - -/* - * Performs authentication of an incoming connection. Session key has already - * been exchanged and encryption is enabled. - */ -void -do_authentication(Authctxt *authctxt) -{ - u_int ulen; - char *user, *style = NULL; - - /* Get the name of the user that we wish to log in as. */ - packet_read_expect(SSH_CMSG_USER); - - /* Get the user name. */ - user = packet_get_cstring(&ulen); - packet_check_eom(); - - if ((style = strchr(user, ':')) != NULL) - *style++ = '\0'; - - authctxt->user = user; - authctxt->style = style; - - /* Verify that the user is a valid user. */ - if ((authctxt->pw = PRIVSEP(getpwnamallow(user))) != NULL) - authctxt->valid = 1; - else { - debug("do_authentication: invalid user %s", user); - authctxt->pw = fakepw(); - BLACKLIST_NOTIFY(BLACKLIST_BAD_USER, user); - } - - /* Configuration may have changed as a result of Match */ - if (options.num_auth_methods != 0) - fatal("AuthenticationMethods is not supported with SSH " - "protocol 1"); - - setproctitle("%s%s", authctxt->valid ? user : "unknown", - use_privsep ? " [net]" : ""); - -#ifdef USE_PAM - if (options.use_pam) - PRIVSEP(start_pam(authctxt)); -#endif - - /* - * If we are not running as root, the user must have the same uid as - * the server. - */ -#ifndef HAVE_CYGWIN - if (!use_privsep && getuid() != 0 && authctxt->pw && - authctxt->pw->pw_uid != getuid()) - packet_disconnect("Cannot change user when server not running as root."); -#endif - - /* - * Loop until the user has been authenticated or the connection is - * closed, do_authloop() returns only if authentication is successful - */ - do_authloop(authctxt); - - /* The user has been authenticated and accepted. */ - packet_start(SSH_SMSG_SUCCESS); - packet_send(); - packet_write_wait(); -} - -#endif /* WITH_SSH1 */ diff --git a/crypto/openssh/auth2-pubkey.c b/crypto/openssh/auth2-pubkey.c index 20f3309e1828..3e5706f4dbef 100644 --- a/crypto/openssh/auth2-pubkey.c +++ b/crypto/openssh/auth2-pubkey.c @@ -1,1143 +1,1156 @@ -/* $OpenBSD: auth2-pubkey.c,v 1.60 2016/11/30 02:57:40 djm Exp $ */ +/* $OpenBSD: auth2-pubkey.c,v 1.62 2017/01/30 01:03:00 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #ifdef HAVE_PATHS_H # include #endif #include #include #include #include #include #include #include #include #include "xmalloc.h" #include "ssh.h" #include "ssh2.h" #include "packet.h" #include "buffer.h" #include "log.h" #include "misc.h" #include "servconf.h" #include "compat.h" #include "key.h" #include "hostfile.h" #include "auth.h" #include "pathnames.h" #include "uidswap.h" #include "auth-options.h" #include "canohost.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "monitor_wrap.h" #include "authfile.h" #include "match.h" #include "ssherr.h" #include "channels.h" /* XXX for session.h */ #include "session.h" /* XXX for child_set_env(); refactor? */ /* import */ extern ServerOptions options; extern u_char *session_id2; extern u_int session_id2_len; static int userauth_pubkey(Authctxt *authctxt) { Buffer b; Key *key = NULL; char *pkalg, *userstyle, *fp = NULL; u_char *pkblob, *sig; u_int alen, blen, slen; int have_sig, pktype; int authenticated = 0; if (!authctxt->valid) { debug2("%s: disabled because of invalid user", __func__); return 0; } have_sig = packet_get_char(); if (datafellows & SSH_BUG_PKAUTH) { debug2("%s: SSH_BUG_PKAUTH", __func__); /* no explicit pkalg given */ pkblob = packet_get_string(&blen); buffer_init(&b); buffer_append(&b, pkblob, blen); /* so we have to extract the pkalg from the pkblob */ pkalg = buffer_get_string(&b, &alen); buffer_free(&b); } else { pkalg = packet_get_string(&alen); pkblob = packet_get_string(&blen); } pktype = key_type_from_name(pkalg); if (pktype == KEY_UNSPEC) { /* this is perfectly legal */ logit("%s: unsupported public key algorithm: %s", __func__, pkalg); goto done; } key = key_from_blob(pkblob, blen); if (key == NULL) { error("%s: cannot decode key: %s", __func__, pkalg); goto done; } if (key->type != pktype) { error("%s: type mismatch for decoded key " "(received %d, expected %d)", __func__, key->type, pktype); goto done; } if (key_type_plain(key->type) == KEY_RSA && (datafellows & SSH_BUG_RSASIGMD5) != 0) { logit("Refusing RSA key because client uses unsafe " "signature scheme"); goto done; } fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT); if (auth2_userkey_already_used(authctxt, key)) { logit("refusing previously-used %s key", key_type(key)); goto done; } if (match_pattern_list(sshkey_ssh_name(key), options.pubkey_key_types, 0) != 1) { logit("%s: key type %s not in PubkeyAcceptedKeyTypes", __func__, sshkey_ssh_name(key)); goto done; } if (have_sig) { debug3("%s: have signature for %s %s", __func__, sshkey_type(key), fp); sig = packet_get_string(&slen); packet_check_eom(); buffer_init(&b); if (datafellows & SSH_OLD_SESSIONID) { buffer_append(&b, session_id2, session_id2_len); } else { buffer_put_string(&b, session_id2, session_id2_len); } /* reconstruct packet */ buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); xasprintf(&userstyle, "%s%s%s", authctxt->user, authctxt->style ? ":" : "", authctxt->style ? authctxt->style : ""); buffer_put_cstring(&b, userstyle); free(userstyle); buffer_put_cstring(&b, datafellows & SSH_BUG_PKSERVICE ? "ssh-userauth" : authctxt->service); if (datafellows & SSH_BUG_PKAUTH) { buffer_put_char(&b, have_sig); } else { buffer_put_cstring(&b, "publickey"); buffer_put_char(&b, have_sig); buffer_put_cstring(&b, pkalg); } buffer_put_string(&b, pkblob, blen); #ifdef DEBUG_PK buffer_dump(&b); #endif pubkey_auth_info(authctxt, key, NULL); /* test for correct signature */ authenticated = 0; if (PRIVSEP(user_key_allowed(authctxt->pw, key, 1)) && PRIVSEP(key_verify(key, sig, slen, buffer_ptr(&b), buffer_len(&b))) == 1) { authenticated = 1; /* Record the successful key to prevent reuse */ auth2_record_userkey(authctxt, key); key = NULL; /* Don't free below */ } buffer_free(&b); free(sig); } else { debug("%s: test whether pkalg/pkblob are acceptable for %s %s", __func__, sshkey_type(key), fp); packet_check_eom(); /* XXX fake reply and always send PK_OK ? */ /* * XXX this allows testing whether a user is allowed * to login: if you happen to have a valid pubkey this * message is sent. the message is NEVER sent at all * if a user is not allowed to login. is this an * issue? -markus */ if (PRIVSEP(user_key_allowed(authctxt->pw, key, 0))) { packet_start(SSH2_MSG_USERAUTH_PK_OK); packet_put_string(pkalg, alen); packet_put_string(pkblob, blen); packet_send(); packet_write_wait(); authctxt->postponed = 1; } } if (authenticated != 1) auth_clear_options(); done: debug2("%s: authenticated %d pkalg %s", __func__, authenticated, pkalg); if (key != NULL) key_free(key); free(pkalg); free(pkblob); free(fp); return authenticated; } void pubkey_auth_info(Authctxt *authctxt, const Key *key, const char *fmt, ...) { char *fp, *extra; va_list ap; int i; extra = NULL; if (fmt != NULL) { va_start(ap, fmt); i = vasprintf(&extra, fmt, ap); va_end(ap); if (i < 0 || extra == NULL) fatal("%s: vasprintf failed", __func__); } if (key_is_cert(key)) { fp = sshkey_fingerprint(key->cert->signature_key, options.fingerprint_hash, SSH_FP_DEFAULT); auth_info(authctxt, "%s ID %s (serial %llu) CA %s %s%s%s", key_type(key), key->cert->key_id, (unsigned long long)key->cert->serial, key_type(key->cert->signature_key), fp == NULL ? "(null)" : fp, extra == NULL ? "" : ", ", extra == NULL ? "" : extra); free(fp); } else { fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT); auth_info(authctxt, "%s %s%s%s", key_type(key), fp == NULL ? "(null)" : fp, extra == NULL ? "" : ", ", extra == NULL ? "" : extra); free(fp); } free(extra); } /* * Splits 's' into an argument vector. Handles quoted string and basic * escape characters (\\, \", \'). Caller must free the argument vector * and its members. */ static int split_argv(const char *s, int *argcp, char ***argvp) { int r = SSH_ERR_INTERNAL_ERROR; int argc = 0, quote, i, j; char *arg, **argv = xcalloc(1, sizeof(*argv)); *argvp = NULL; *argcp = 0; for (i = 0; s[i] != '\0'; i++) { /* Skip leading whitespace */ if (s[i] == ' ' || s[i] == '\t') continue; /* Start of a token */ quote = 0; if (s[i] == '\\' && (s[i + 1] == '\'' || s[i + 1] == '\"' || s[i + 1] == '\\')) i++; else if (s[i] == '\'' || s[i] == '"') quote = s[i++]; argv = xreallocarray(argv, (argc + 2), sizeof(*argv)); arg = argv[argc++] = xcalloc(1, strlen(s + i) + 1); argv[argc] = NULL; /* Copy the token in, removing escapes */ for (j = 0; s[i] != '\0'; i++) { if (s[i] == '\\') { if (s[i + 1] == '\'' || s[i + 1] == '\"' || s[i + 1] == '\\') { i++; /* Skip '\' */ arg[j++] = s[i]; } else { /* Unrecognised escape */ arg[j++] = s[i]; } } else if (quote == 0 && (s[i] == ' ' || s[i] == '\t')) break; /* done */ else if (quote != 0 && s[i] == quote) break; /* done */ else arg[j++] = s[i]; } if (s[i] == '\0') { if (quote != 0) { /* Ran out of string looking for close quote */ r = SSH_ERR_INVALID_FORMAT; goto out; } break; } } /* Success */ *argcp = argc; *argvp = argv; argc = 0; argv = NULL; r = 0; out: if (argc != 0 && argv != NULL) { for (i = 0; i < argc; i++) free(argv[i]); free(argv); } return r; } /* * Reassemble an argument vector into a string, quoting and escaping as * necessary. Caller must free returned string. */ static char * assemble_argv(int argc, char **argv) { int i, j, ws, r; char c, *ret; struct sshbuf *buf, *arg; if ((buf = sshbuf_new()) == NULL || (arg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); for (i = 0; i < argc; i++) { ws = 0; sshbuf_reset(arg); for (j = 0; argv[i][j] != '\0'; j++) { r = 0; c = argv[i][j]; switch (c) { case ' ': case '\t': ws = 1; r = sshbuf_put_u8(arg, c); break; case '\\': case '\'': case '"': if ((r = sshbuf_put_u8(arg, '\\')) != 0) break; /* FALLTHROUGH */ default: r = sshbuf_put_u8(arg, c); break; } if (r != 0) fatal("%s: sshbuf_put_u8: %s", __func__, ssh_err(r)); } if ((i != 0 && (r = sshbuf_put_u8(buf, ' ')) != 0) || (ws != 0 && (r = sshbuf_put_u8(buf, '"')) != 0) || (r = sshbuf_putb(buf, arg)) != 0 || (ws != 0 && (r = sshbuf_put_u8(buf, '"')) != 0)) fatal("%s: buffer error: %s", __func__, ssh_err(r)); } if ((ret = malloc(sshbuf_len(buf) + 1)) == NULL) fatal("%s: malloc failed", __func__); memcpy(ret, sshbuf_ptr(buf), sshbuf_len(buf)); ret[sshbuf_len(buf)] = '\0'; sshbuf_free(buf); sshbuf_free(arg); return ret; } /* * Runs command in a subprocess. Returns pid on success and a FILE* to the * subprocess' stdout or 0 on failure. * NB. "command" is only used for logging. */ static pid_t subprocess(const char *tag, struct passwd *pw, const char *command, int ac, char **av, FILE **child) { FILE *f; struct stat st; int devnull, p[2], i; pid_t pid; char *cp, errmsg[512]; u_int envsize; char **child_env; *child = NULL; debug3("%s: %s command \"%s\" running as %s", __func__, tag, command, pw->pw_name); /* Verify the path exists and is safe-ish to execute */ if (*av[0] != '/') { error("%s path is not absolute", tag); return 0; } temporarily_use_uid(pw); if (stat(av[0], &st) < 0) { error("Could not stat %s \"%s\": %s", tag, av[0], strerror(errno)); restore_uid(); return 0; } if (auth_secure_path(av[0], &st, NULL, 0, errmsg, sizeof(errmsg)) != 0) { error("Unsafe %s \"%s\": %s", tag, av[0], errmsg); restore_uid(); return 0; } /* * Run the command; stderr is left in place, stdout is the * authorized_keys output. */ if (pipe(p) != 0) { error("%s: pipe: %s", tag, strerror(errno)); restore_uid(); return 0; } /* * Don't want to call this in the child, where it can fatal() and * run cleanup_exit() code. */ restore_uid(); switch ((pid = fork())) { case -1: /* error */ error("%s: fork: %s", tag, strerror(errno)); close(p[0]); close(p[1]); return 0; case 0: /* child */ /* Prepare a minimal environment for the child. */ envsize = 5; child_env = xcalloc(sizeof(*child_env), envsize); child_set_env(&child_env, &envsize, "PATH", _PATH_STDPATH); child_set_env(&child_env, &envsize, "USER", pw->pw_name); child_set_env(&child_env, &envsize, "LOGNAME", pw->pw_name); child_set_env(&child_env, &envsize, "HOME", pw->pw_dir); if ((cp = getenv("LANG")) != NULL) child_set_env(&child_env, &envsize, "LANG", cp); for (i = 0; i < NSIG; i++) signal(i, SIG_DFL); if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) { error("%s: open %s: %s", tag, _PATH_DEVNULL, strerror(errno)); _exit(1); } /* Keep stderr around a while longer to catch errors */ if (dup2(devnull, STDIN_FILENO) == -1 || dup2(p[1], STDOUT_FILENO) == -1) { error("%s: dup2: %s", tag, strerror(errno)); _exit(1); } closefrom(STDERR_FILENO + 1); /* Don't use permanently_set_uid() here to avoid fatal() */ if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) != 0) { error("%s: setresgid %u: %s", tag, (u_int)pw->pw_gid, strerror(errno)); _exit(1); } if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) != 0) { error("%s: setresuid %u: %s", tag, (u_int)pw->pw_uid, strerror(errno)); _exit(1); } /* stdin is pointed to /dev/null at this point */ if (dup2(STDIN_FILENO, STDERR_FILENO) == -1) { error("%s: dup2: %s", tag, strerror(errno)); _exit(1); } execve(av[0], av, child_env); error("%s exec \"%s\": %s", tag, command, strerror(errno)); _exit(127); default: /* parent */ break; } close(p[1]); if ((f = fdopen(p[0], "r")) == NULL) { error("%s: fdopen: %s", tag, strerror(errno)); close(p[0]); /* Don't leave zombie child */ kill(pid, SIGTERM); while (waitpid(pid, NULL, 0) == -1 && errno == EINTR) ; return 0; } /* Success */ debug3("%s: %s pid %ld", __func__, tag, (long)pid); *child = f; return pid; } /* Returns 0 if pid exited cleanly, non-zero otherwise */ static int exited_cleanly(pid_t pid, const char *tag, const char *cmd) { int status; while (waitpid(pid, &status, 0) == -1) { if (errno != EINTR) { error("%s: waitpid: %s", tag, strerror(errno)); return -1; } } if (WIFSIGNALED(status)) { error("%s %s exited on signal %d", tag, cmd, WTERMSIG(status)); return -1; } else if (WEXITSTATUS(status) != 0) { error("%s %s failed, status %d", tag, cmd, WEXITSTATUS(status)); return -1; } return 0; } static int match_principals_option(const char *principal_list, struct sshkey_cert *cert) { char *result; u_int i; /* XXX percent_expand() sequences for authorized_principals? */ for (i = 0; i < cert->nprincipals; i++) { if ((result = match_list(cert->principals[i], principal_list, NULL)) != NULL) { debug3("matched principal from key options \"%.100s\"", result); free(result); return 1; } } return 0; } static int process_principals(FILE *f, char *file, struct passwd *pw, const struct sshkey_cert *cert) { char line[SSH_MAX_PUBKEY_BYTES], *cp, *ep, *line_opts; u_long linenum = 0; - u_int i; + u_int i, found_principal = 0; while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { + /* Always consume entire input */ + if (found_principal) + continue; /* Skip leading whitespace. */ for (cp = line; *cp == ' ' || *cp == '\t'; cp++) ; /* Skip blank and comment lines. */ if ((ep = strchr(cp, '#')) != NULL) *ep = '\0'; if (!*cp || *cp == '\n') continue; /* Trim trailing whitespace. */ ep = cp + strlen(cp) - 1; while (ep > cp && (*ep == '\n' || *ep == ' ' || *ep == '\t')) *ep-- = '\0'; /* * If the line has internal whitespace then assume it has * key options. */ line_opts = NULL; if ((ep = strrchr(cp, ' ')) != NULL || (ep = strrchr(cp, '\t')) != NULL) { for (; *ep == ' ' || *ep == '\t'; ep++) ; line_opts = cp; cp = ep; } for (i = 0; i < cert->nprincipals; i++) { if (strcmp(cp, cert->principals[i]) == 0) { debug3("%s:%lu: matched principal \"%.100s\"", file == NULL ? "(command)" : file, linenum, cert->principals[i]); if (auth_parse_options(pw, line_opts, file, linenum) != 1) continue; - return 1; + found_principal = 1; + continue; } } } - return 0; + return found_principal; } static int match_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert) { FILE *f; int success; temporarily_use_uid(pw); debug("trying authorized principals file %s", file); if ((f = auth_openprincipals(file, pw, options.strict_modes)) == NULL) { restore_uid(); return 0; } success = process_principals(f, file, pw, cert); fclose(f); restore_uid(); return success; } /* * Checks whether principal is allowed in output of command. * returns 1 if the principal is allowed or 0 otherwise. */ static int match_principals_command(struct passwd *user_pw, const struct sshkey *key) { const struct sshkey_cert *cert = key->cert; FILE *f = NULL; int r, ok, found_principal = 0; struct passwd *pw; int i, ac = 0, uid_swapped = 0; pid_t pid; char *tmp, *username = NULL, *command = NULL, **av = NULL; char *ca_fp = NULL, *key_fp = NULL, *catext = NULL, *keytext = NULL; char serial_s[16]; void (*osigchld)(int); if (options.authorized_principals_command == NULL) return 0; if (options.authorized_principals_command_user == NULL) { error("No user for AuthorizedPrincipalsCommand specified, " "skipping"); return 0; } /* * NB. all returns later this function should go via "out" to * ensure the original SIGCHLD handler is restored properly. */ osigchld = signal(SIGCHLD, SIG_DFL); /* Prepare and verify the user for the command */ username = percent_expand(options.authorized_principals_command_user, "u", user_pw->pw_name, (char *)NULL); pw = getpwnam(username); if (pw == NULL) { error("AuthorizedPrincipalsCommandUser \"%s\" not found: %s", username, strerror(errno)); goto out; } /* Turn the command into an argument vector */ if (split_argv(options.authorized_principals_command, &ac, &av) != 0) { error("AuthorizedPrincipalsCommand \"%s\" contains " "invalid quotes", command); goto out; } if (ac == 0) { error("AuthorizedPrincipalsCommand \"%s\" yielded no arguments", command); goto out; } if ((ca_fp = sshkey_fingerprint(cert->signature_key, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { error("%s: sshkey_fingerprint failed", __func__); goto out; } if ((key_fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { error("%s: sshkey_fingerprint failed", __func__); goto out; } if ((r = sshkey_to_base64(cert->signature_key, &catext)) != 0) { error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r)); goto out; } if ((r = sshkey_to_base64(key, &keytext)) != 0) { error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r)); goto out; } snprintf(serial_s, sizeof(serial_s), "%llu", (unsigned long long)cert->serial); for (i = 1; i < ac; i++) { tmp = percent_expand(av[i], "u", user_pw->pw_name, "h", user_pw->pw_dir, "t", sshkey_ssh_name(key), "T", sshkey_ssh_name(cert->signature_key), "f", key_fp, "F", ca_fp, "k", keytext, "K", catext, "i", cert->key_id, "s", serial_s, (char *)NULL); if (tmp == NULL) fatal("%s: percent_expand failed", __func__); free(av[i]); av[i] = tmp; } /* Prepare a printable command for logs, etc. */ command = assemble_argv(ac, av); if ((pid = subprocess("AuthorizedPrincipalsCommand", pw, command, ac, av, &f)) == 0) goto out; uid_swapped = 1; temporarily_use_uid(pw); ok = process_principals(f, NULL, pw, cert); + fclose(f); + f = NULL; + if (exited_cleanly(pid, "AuthorizedPrincipalsCommand", command) != 0) goto out; /* Read completed successfully */ found_principal = ok; out: if (f != NULL) fclose(f); signal(SIGCHLD, osigchld); for (i = 0; i < ac; i++) free(av[i]); free(av); if (uid_swapped) restore_uid(); free(command); free(username); free(ca_fp); free(key_fp); free(catext); free(keytext); return found_principal; } /* * Checks whether key is allowed in authorized_keys-format file, * returns 1 if the key is allowed or 0 otherwise. */ static int check_authkeys_file(FILE *f, char *file, Key* key, struct passwd *pw) { char line[SSH_MAX_PUBKEY_BYTES]; int found_key = 0; u_long linenum = 0; Key *found; found_key = 0; found = NULL; while (read_keyfile_line(f, file, line, sizeof(line), &linenum) != -1) { char *cp, *key_options = NULL, *fp = NULL; const char *reason = NULL; + /* Always consume entrire file */ + if (found_key) + continue; if (found != NULL) key_free(found); found = key_new(key_is_cert(key) ? KEY_UNSPEC : key->type); auth_clear_options(); /* Skip leading whitespace, empty and comment lines. */ for (cp = line; *cp == ' ' || *cp == '\t'; cp++) ; if (!*cp || *cp == '\n' || *cp == '#') continue; if (key_read(found, &cp) != 1) { /* no key? check if there are options for this key */ int quoted = 0; debug2("user_key_allowed: check options: '%s'", cp); key_options = cp; for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { if (*cp == '\\' && cp[1] == '"') cp++; /* Skip both */ else if (*cp == '"') quoted = !quoted; } /* Skip remaining whitespace. */ for (; *cp == ' ' || *cp == '\t'; cp++) ; if (key_read(found, &cp) != 1) { debug2("user_key_allowed: advance: '%s'", cp); /* still no key? advance to next line*/ continue; } } if (key_is_cert(key)) { if (!key_equal(found, key->cert->signature_key)) continue; if (auth_parse_options(pw, key_options, file, linenum) != 1) continue; if (!key_is_cert_authority) continue; if ((fp = sshkey_fingerprint(found, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) continue; debug("matching CA found: file %s, line %lu, %s %s", file, linenum, key_type(found), fp); /* * If the user has specified a list of principals as * a key option, then prefer that list to matching * their username in the certificate principals list. */ if (authorized_principals != NULL && !match_principals_option(authorized_principals, key->cert)) { reason = "Certificate does not contain an " "authorized principal"; fail_reason: free(fp); error("%s", reason); auth_debug_add("%s", reason); continue; } if (key_cert_check_authority(key, 0, 0, authorized_principals == NULL ? pw->pw_name : NULL, &reason) != 0) goto fail_reason; if (auth_cert_options(key, pw, &reason) != 0) goto fail_reason; verbose("Accepted certificate ID \"%s\" (serial %llu) " "signed by %s CA %s via %s", key->cert->key_id, (unsigned long long)key->cert->serial, key_type(found), fp, file); free(fp); found_key = 1; break; } else if (key_equal(found, key)) { if (auth_parse_options(pw, key_options, file, linenum) != 1) continue; if (key_is_cert_authority) continue; if ((fp = sshkey_fingerprint(found, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) continue; debug("matching key found: file %s, line %lu %s %s", file, linenum, key_type(found), fp); free(fp); found_key = 1; - break; + continue; } } if (found != NULL) key_free(found); if (!found_key) debug2("key not found"); return found_key; } /* Authenticate a certificate key against TrustedUserCAKeys */ static int user_cert_trusted_ca(struct passwd *pw, Key *key) { char *ca_fp, *principals_file = NULL; const char *reason; int ret = 0, found_principal = 0, use_authorized_principals; if (!key_is_cert(key) || options.trusted_user_ca_keys == NULL) return 0; if ((ca_fp = sshkey_fingerprint(key->cert->signature_key, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) return 0; if (sshkey_in_file(key->cert->signature_key, options.trusted_user_ca_keys, 1, 0) != 0) { debug2("%s: CA %s %s is not listed in %s", __func__, key_type(key->cert->signature_key), ca_fp, options.trusted_user_ca_keys); goto out; } /* * If AuthorizedPrincipals is in use, then compare the certificate * principals against the names in that file rather than matching * against the username. */ if ((principals_file = authorized_principals_file(pw)) != NULL) { if (match_principals_file(principals_file, pw, key->cert)) found_principal = 1; } /* Try querying command if specified */ if (!found_principal && match_principals_command(pw, key)) found_principal = 1; /* If principals file or command is specified, then require a match */ use_authorized_principals = principals_file != NULL || options.authorized_principals_command != NULL; if (!found_principal && use_authorized_principals) { reason = "Certificate does not contain an authorized principal"; fail_reason: error("%s", reason); auth_debug_add("%s", reason); goto out; } if (key_cert_check_authority(key, 0, 1, use_authorized_principals ? NULL : pw->pw_name, &reason) != 0) goto fail_reason; if (auth_cert_options(key, pw, &reason) != 0) goto fail_reason; verbose("Accepted certificate ID \"%s\" (serial %llu) signed by " "%s CA %s via %s", key->cert->key_id, (unsigned long long)key->cert->serial, key_type(key->cert->signature_key), ca_fp, options.trusted_user_ca_keys); ret = 1; out: free(principals_file); free(ca_fp); return ret; } /* * Checks whether key is allowed in file. * returns 1 if the key is allowed or 0 otherwise. */ static int user_key_allowed2(struct passwd *pw, Key *key, char *file) { FILE *f; int found_key = 0; /* Temporarily use the user's uid. */ temporarily_use_uid(pw); debug("trying public key file %s", file); if ((f = auth_openkeyfile(file, pw, options.strict_modes)) != NULL) { found_key = check_authkeys_file(f, file, key, pw); fclose(f); } restore_uid(); return found_key; } /* * Checks whether key is allowed in output of command. * returns 1 if the key is allowed or 0 otherwise. */ static int user_key_command_allowed2(struct passwd *user_pw, Key *key) { FILE *f = NULL; int r, ok, found_key = 0; struct passwd *pw; int i, uid_swapped = 0, ac = 0; pid_t pid; char *username = NULL, *key_fp = NULL, *keytext = NULL; char *tmp, *command = NULL, **av = NULL; void (*osigchld)(int); if (options.authorized_keys_command == NULL) return 0; if (options.authorized_keys_command_user == NULL) { error("No user for AuthorizedKeysCommand specified, skipping"); return 0; } /* * NB. all returns later this function should go via "out" to * ensure the original SIGCHLD handler is restored properly. */ osigchld = signal(SIGCHLD, SIG_DFL); /* Prepare and verify the user for the command */ username = percent_expand(options.authorized_keys_command_user, "u", user_pw->pw_name, (char *)NULL); pw = getpwnam(username); if (pw == NULL) { error("AuthorizedKeysCommandUser \"%s\" not found: %s", username, strerror(errno)); goto out; } /* Prepare AuthorizedKeysCommand */ if ((key_fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { error("%s: sshkey_fingerprint failed", __func__); goto out; } if ((r = sshkey_to_base64(key, &keytext)) != 0) { error("%s: sshkey_to_base64 failed: %s", __func__, ssh_err(r)); goto out; } /* Turn the command into an argument vector */ if (split_argv(options.authorized_keys_command, &ac, &av) != 0) { error("AuthorizedKeysCommand \"%s\" contains invalid quotes", command); goto out; } if (ac == 0) { error("AuthorizedKeysCommand \"%s\" yielded no arguments", command); goto out; } for (i = 1; i < ac; i++) { tmp = percent_expand(av[i], "u", user_pw->pw_name, "h", user_pw->pw_dir, "t", sshkey_ssh_name(key), "f", key_fp, "k", keytext, (char *)NULL); if (tmp == NULL) fatal("%s: percent_expand failed", __func__); free(av[i]); av[i] = tmp; } /* Prepare a printable command for logs, etc. */ command = assemble_argv(ac, av); /* * If AuthorizedKeysCommand was run without arguments * then fall back to the old behaviour of passing the * target username as a single argument. */ if (ac == 1) { av = xreallocarray(av, ac + 2, sizeof(*av)); av[1] = xstrdup(user_pw->pw_name); av[2] = NULL; /* Fix up command too, since it is used in log messages */ free(command); xasprintf(&command, "%s %s", av[0], av[1]); } if ((pid = subprocess("AuthorizedKeysCommand", pw, command, ac, av, &f)) == 0) goto out; uid_swapped = 1; temporarily_use_uid(pw); ok = check_authkeys_file(f, options.authorized_keys_command, key, pw); + fclose(f); + f = NULL; + if (exited_cleanly(pid, "AuthorizedKeysCommand", command) != 0) goto out; /* Read completed successfully */ found_key = ok; out: if (f != NULL) fclose(f); signal(SIGCHLD, osigchld); for (i = 0; i < ac; i++) free(av[i]); free(av); if (uid_swapped) restore_uid(); free(command); free(username); free(key_fp); free(keytext); return found_key; } /* * Check whether key authenticates and authorises the user. */ int user_key_allowed(struct passwd *pw, Key *key, int auth_attempt) { u_int success, i; char *file; if (auth_key_is_revoked(key)) return 0; if (key_is_cert(key) && auth_key_is_revoked(key->cert->signature_key)) return 0; success = user_cert_trusted_ca(pw, key); if (success) return success; success = user_key_command_allowed2(pw, key); if (success > 0) return success; for (i = 0; !success && i < options.num_authkeys_files; i++) { if (strcasecmp(options.authorized_keys_files[i], "none") == 0) continue; file = expand_authorized_keys( options.authorized_keys_files[i], pw); success = user_key_allowed2(pw, key, file); free(file); } return success; } /* Records a public key in the list of previously-successful keys */ void auth2_record_userkey(Authctxt *authctxt, struct sshkey *key) { struct sshkey **tmp; if (authctxt->nprev_userkeys >= INT_MAX || (tmp = reallocarray(authctxt->prev_userkeys, authctxt->nprev_userkeys + 1, sizeof(*tmp))) == NULL) fatal("%s: reallocarray failed", __func__); authctxt->prev_userkeys = tmp; authctxt->prev_userkeys[authctxt->nprev_userkeys] = key; authctxt->nprev_userkeys++; } /* Checks whether a key has already been used successfully for authentication */ int auth2_userkey_already_used(Authctxt *authctxt, struct sshkey *key) { u_int i; for (i = 0; i < authctxt->nprev_userkeys; i++) { if (sshkey_equal_public(key, authctxt->prev_userkeys[i])) { return 1; } } return 0; } Authmethod method_pubkey = { "publickey", userauth_pubkey, &options.pubkey_authentication }; diff --git a/crypto/openssh/auth2.c b/crypto/openssh/auth2.c index b6695f79d0aa..30e52d26fb10 100644 --- a/crypto/openssh/auth2.c +++ b/crypto/openssh/auth2.c @@ -1,649 +1,655 @@ -/* $OpenBSD: auth2.c,v 1.136 2016/05/02 08:49:03 djm Exp $ */ +/* $OpenBSD: auth2.c,v 1.137 2017/02/03 23:05:57 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" __RCSID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "atomicio.h" #include "xmalloc.h" #include "ssh2.h" #include "packet.h" #include "log.h" #include "buffer.h" #include "misc.h" #include "servconf.h" #include "compat.h" #include "key.h" #include "hostfile.h" #include "auth.h" #include "dispatch.h" #include "pathnames.h" #include "buffer.h" #include "blacklist_client.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "monitor_wrap.h" /* import */ extern ServerOptions options; extern u_char *session_id2; extern u_int session_id2_len; extern Buffer loginmsg; /* methods */ extern Authmethod method_none; extern Authmethod method_pubkey; extern Authmethod method_passwd; extern Authmethod method_kbdint; extern Authmethod method_hostbased; #ifdef GSSAPI extern Authmethod method_gssapi; #endif Authmethod *authmethods[] = { &method_none, &method_pubkey, #ifdef GSSAPI &method_gssapi, #endif &method_passwd, &method_kbdint, &method_hostbased, NULL }; /* protocol */ static int input_service_request(int, u_int32_t, void *); static int input_userauth_request(int, u_int32_t, void *); /* helper */ static Authmethod *authmethod_lookup(Authctxt *, const char *); static char *authmethods_get(Authctxt *authctxt); #define MATCH_NONE 0 /* method or submethod mismatch */ #define MATCH_METHOD 1 /* method matches (no submethod specified) */ #define MATCH_BOTH 2 /* method and submethod match */ #define MATCH_PARTIAL 3 /* method matches, submethod can't be checked */ static int list_starts_with(const char *, const char *, const char *); char * auth2_read_banner(void) { struct stat st; char *banner = NULL; size_t len, n; int fd; if ((fd = open(options.banner, O_RDONLY)) == -1) return (NULL); if (fstat(fd, &st) == -1) { close(fd); return (NULL); } if (st.st_size <= 0 || st.st_size > 1*1024*1024) { close(fd); return (NULL); } len = (size_t)st.st_size; /* truncate */ banner = xmalloc(len + 1); n = atomicio(read, fd, banner, len); close(fd); if (n != len) { free(banner); return (NULL); } banner[n] = '\0'; return (banner); } void userauth_send_banner(const char *msg) { if (datafellows & SSH_BUG_BANNER) return; packet_start(SSH2_MSG_USERAUTH_BANNER); packet_put_cstring(msg); packet_put_cstring(""); /* language, unused */ packet_send(); debug("%s: sent", __func__); } static void userauth_banner(void) { char *banner = NULL; if (options.banner == NULL || (datafellows & SSH_BUG_BANNER) != 0) return; if ((banner = PRIVSEP(auth2_read_banner())) == NULL) goto done; userauth_send_banner(banner); done: free(banner); } /* * loop until authctxt->success == TRUE */ void do_authentication2(Authctxt *authctxt) { dispatch_init(&dispatch_protocol_error); dispatch_set(SSH2_MSG_SERVICE_REQUEST, &input_service_request); dispatch_run(DISPATCH_BLOCK, &authctxt->success, authctxt); } /*ARGSUSED*/ static int input_service_request(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; u_int len; int acceptit = 0; char *service = packet_get_cstring(&len); packet_check_eom(); if (authctxt == NULL) fatal("input_service_request: no authctxt"); if (strcmp(service, "ssh-userauth") == 0) { if (!authctxt->success) { acceptit = 1; /* now we can handle user-auth requests */ dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &input_userauth_request); } } /* XXX all other service requests are denied */ if (acceptit) { packet_start(SSH2_MSG_SERVICE_ACCEPT); packet_put_cstring(service); packet_send(); packet_write_wait(); } else { debug("bad service request %s", service); packet_disconnect("bad service request %s", service); } free(service); return 0; } /*ARGSUSED*/ static int input_userauth_request(int type, u_int32_t seq, void *ctxt) { + struct ssh *ssh = active_state; /* XXX */ Authctxt *authctxt = ctxt; Authmethod *m = NULL; char *user, *service, *method, *style = NULL; int authenticated = 0; #ifdef HAVE_LOGIN_CAP - struct ssh *ssh = active_state; /* XXX */ login_cap_t *lc; const char *from_host, *from_ip; #endif + if (authctxt == NULL) fatal("input_userauth_request: no authctxt"); user = packet_get_cstring(NULL); service = packet_get_cstring(NULL); method = packet_get_cstring(NULL); debug("userauth-request for user %s service %s method %s", user, service, method); debug("attempt %d failures %d", authctxt->attempt, authctxt->failures); if ((style = strchr(user, ':')) != NULL) *style++ = 0; if (authctxt->attempt++ == 0) { /* setup auth context */ authctxt->pw = PRIVSEP(getpwnamallow(user)); authctxt->user = xstrdup(user); if (authctxt->pw && strcmp(service, "ssh-connection")==0) { authctxt->valid = 1; - debug2("input_userauth_request: setting up authctxt for %s", user); + debug2("%s: setting up authctxt for %s", + __func__, user); } else { - logit("input_userauth_request: invalid user %s", user); + /* Invalid user, fake password information */ authctxt->pw = fakepw(); #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_INVALID_USER)); #endif } #ifdef USE_PAM if (options.use_pam) PRIVSEP(start_pam(authctxt)); #endif + ssh_packet_set_log_preamble(ssh, "%suser %s", + authctxt->valid ? "authenticating " : "invalid ", user); setproctitle("%s%s", authctxt->valid ? user : "unknown", use_privsep ? " [net]" : ""); authctxt->service = xstrdup(service); authctxt->style = style ? xstrdup(style) : NULL; if (use_privsep) mm_inform_authserv(service, style); userauth_banner(); if (auth2_setup_methods_lists(authctxt) != 0) packet_disconnect("no authentication methods enabled"); } else if (strcmp(user, authctxt->user) != 0 || strcmp(service, authctxt->service) != 0) { packet_disconnect("Change of username or service not allowed: " "(%s,%s) -> (%s,%s)", authctxt->user, authctxt->service, user, service); } #ifdef HAVE_LOGIN_CAP if (authctxt->pw != NULL && (lc = login_getpwclass(authctxt->pw)) != NULL) { logit("user %s login class %s", authctxt->pw->pw_name, authctxt->pw->pw_class); from_host = auth_get_canonical_hostname(ssh, options.use_dns); from_ip = ssh_remote_ipaddr(ssh); if (!auth_hostok(lc, from_host, from_ip)) { logit("Denied connection for %.200s from %.200s [%.200s].", authctxt->pw->pw_name, from_host, from_ip); packet_disconnect("Sorry, you are not allowed to connect."); } if (!auth_timeok(lc, time(NULL))) { logit("LOGIN %.200s REFUSED (TIME) FROM %.200s", authctxt->pw->pw_name, from_host); packet_disconnect("Logins not available right now."); } login_close(lc); } #endif /* HAVE_LOGIN_CAP */ /* reset state */ auth2_challenge_stop(authctxt); #ifdef GSSAPI /* XXX move to auth2_gssapi_stop() */ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); #endif authctxt->postponed = 0; authctxt->server_caused_failure = 0; /* try to authenticate user */ m = authmethod_lookup(authctxt, method); if (m != NULL && authctxt->failures < options.max_authtries) { debug2("input_userauth_request: try method %s", method); authenticated = m->userauth(authctxt); } userauth_finish(authctxt, authenticated, method, NULL); free(service); free(user); free(method); return 0; } void userauth_finish(Authctxt *authctxt, int authenticated, const char *method, const char *submethod) { + struct ssh *ssh = active_state; /* XXX */ char *methods; int partial = 0; if (!authctxt->valid && authenticated) fatal("INTERNAL ERROR: authenticated invalid user %s", authctxt->user); if (authenticated && authctxt->postponed) fatal("INTERNAL ERROR: authenticated and postponed"); /* Special handling for root */ if (authenticated && authctxt->pw->pw_uid == 0 && !auth_root_allowed(method)) { authenticated = 0; #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_LOGIN_ROOT_DENIED)); #endif } if (authenticated && options.num_auth_methods != 0) { if (!auth2_update_methods_lists(authctxt, method, submethod)) { authenticated = 0; partial = 1; } } /* Log before sending the reply */ auth_log(authctxt, authenticated, partial, method, submethod); if (authctxt->postponed) return; #ifdef USE_PAM if (options.use_pam && authenticated) { if (!PRIVSEP(do_pam_account())) { /* if PAM returned a message, send it to the user */ if (buffer_len(&loginmsg) > 0) { buffer_append(&loginmsg, "\0", 1); userauth_send_banner(buffer_ptr(&loginmsg)); packet_write_wait(); } fatal("Access denied for user %s by PAM account " "configuration", authctxt->user); } } #endif #ifdef _UNICOS if (authenticated && cray_access_denied(authctxt->user)) { authenticated = 0; fatal("Access denied for user %s.", authctxt->user); } #endif /* _UNICOS */ if (authenticated == 1) { /* turn off userauth */ dispatch_set(SSH2_MSG_USERAUTH_REQUEST, &dispatch_protocol_ignore); packet_start(SSH2_MSG_USERAUTH_SUCCESS); packet_send(); packet_write_wait(); /* now we can break out */ authctxt->success = 1; + ssh_packet_set_log_preamble(ssh, "user %s", authctxt->user); } else { /* Allow initial try of "none" auth without failure penalty */ if (!partial && !authctxt->server_caused_failure && (authctxt->attempt > 1 || strcmp(method, "none") != 0)) { authctxt->failures++; BLACKLIST_NOTIFY(BLACKLIST_AUTH_FAIL, "ssh"); } if (authctxt->failures >= options.max_authtries) { #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_LOGIN_EXCEED_MAXTRIES)); #endif auth_maxtries_exceeded(authctxt); } methods = authmethods_get(authctxt); debug3("%s: failure partial=%d next methods=\"%s\"", __func__, partial, methods); packet_start(SSH2_MSG_USERAUTH_FAILURE); packet_put_cstring(methods); packet_put_char(partial); packet_send(); packet_write_wait(); free(methods); } } /* * Checks whether method is allowed by at least one AuthenticationMethods * methods list. Returns 1 if allowed, or no methods lists configured. * 0 otherwise. */ int auth2_method_allowed(Authctxt *authctxt, const char *method, const char *submethod) { u_int i; /* * NB. authctxt->num_auth_methods might be zero as a result of * auth2_setup_methods_lists(), so check the configuration. */ if (options.num_auth_methods == 0) return 1; for (i = 0; i < authctxt->num_auth_methods; i++) { if (list_starts_with(authctxt->auth_methods[i], method, submethod) != MATCH_NONE) return 1; } return 0; } static char * authmethods_get(Authctxt *authctxt) { Buffer b; char *list; u_int i; buffer_init(&b); for (i = 0; authmethods[i] != NULL; i++) { if (strcmp(authmethods[i]->name, "none") == 0) continue; if (authmethods[i]->enabled == NULL || *(authmethods[i]->enabled) == 0) continue; if (!auth2_method_allowed(authctxt, authmethods[i]->name, NULL)) continue; if (buffer_len(&b) > 0) buffer_append(&b, ",", 1); buffer_append(&b, authmethods[i]->name, strlen(authmethods[i]->name)); } if ((list = sshbuf_dup_string(&b)) == NULL) fatal("%s: sshbuf_dup_string failed", __func__); buffer_free(&b); return list; } static Authmethod * authmethod_lookup(Authctxt *authctxt, const char *name) { int i; if (name != NULL) for (i = 0; authmethods[i] != NULL; i++) if (authmethods[i]->enabled != NULL && *(authmethods[i]->enabled) != 0 && strcmp(name, authmethods[i]->name) == 0 && auth2_method_allowed(authctxt, authmethods[i]->name, NULL)) return authmethods[i]; debug2("Unrecognized authentication method name: %s", name ? name : "NULL"); return NULL; } /* * Check a comma-separated list of methods for validity. Is need_enable is * non-zero, then also require that the methods are enabled. * Returns 0 on success or -1 if the methods list is invalid. */ int auth2_methods_valid(const char *_methods, int need_enable) { char *methods, *omethods, *method, *p; u_int i, found; int ret = -1; if (*_methods == '\0') { error("empty authentication method list"); return -1; } omethods = methods = xstrdup(_methods); while ((method = strsep(&methods, ",")) != NULL) { for (found = i = 0; !found && authmethods[i] != NULL; i++) { if ((p = strchr(method, ':')) != NULL) *p = '\0'; if (strcmp(method, authmethods[i]->name) != 0) continue; if (need_enable) { if (authmethods[i]->enabled == NULL || *(authmethods[i]->enabled) == 0) { error("Disabled method \"%s\" in " "AuthenticationMethods list \"%s\"", method, _methods); goto out; } } found = 1; break; } if (!found) { error("Unknown authentication method \"%s\" in list", method); goto out; } } ret = 0; out: free(omethods); return ret; } /* * Prune the AuthenticationMethods supplied in the configuration, removing * any methods lists that include disabled methods. Note that this might * leave authctxt->num_auth_methods == 0, even when multiple required auth * has been requested. For this reason, all tests for whether multiple is * enabled should consult options.num_auth_methods directly. */ int auth2_setup_methods_lists(Authctxt *authctxt) { u_int i; if (options.num_auth_methods == 0) return 0; debug3("%s: checking methods", __func__); authctxt->auth_methods = xcalloc(options.num_auth_methods, sizeof(*authctxt->auth_methods)); authctxt->num_auth_methods = 0; for (i = 0; i < options.num_auth_methods; i++) { if (auth2_methods_valid(options.auth_methods[i], 1) != 0) { logit("Authentication methods list \"%s\" contains " "disabled method, skipping", options.auth_methods[i]); continue; } debug("authentication methods list %d: %s", authctxt->num_auth_methods, options.auth_methods[i]); authctxt->auth_methods[authctxt->num_auth_methods++] = xstrdup(options.auth_methods[i]); } if (authctxt->num_auth_methods == 0) { error("No AuthenticationMethods left after eliminating " "disabled methods"); return -1; } return 0; } static int list_starts_with(const char *methods, const char *method, const char *submethod) { size_t l = strlen(method); int match; const char *p; if (strncmp(methods, method, l) != 0) return MATCH_NONE; p = methods + l; match = MATCH_METHOD; if (*p == ':') { if (!submethod) return MATCH_PARTIAL; l = strlen(submethod); p += 1; if (strncmp(submethod, p, l)) return MATCH_NONE; p += l; match = MATCH_BOTH; } if (*p != ',' && *p != '\0') return MATCH_NONE; return match; } /* * Remove method from the start of a comma-separated list of methods. * Returns 0 if the list of methods did not start with that method or 1 * if it did. */ static int remove_method(char **methods, const char *method, const char *submethod) { char *omethods = *methods, *p; size_t l = strlen(method); int match; match = list_starts_with(omethods, method, submethod); if (match != MATCH_METHOD && match != MATCH_BOTH) return 0; p = omethods + l; if (submethod && match == MATCH_BOTH) p += 1 + strlen(submethod); /* include colon */ if (*p == ',') p++; *methods = xstrdup(p); free(omethods); return 1; } /* * Called after successful authentication. Will remove the successful method * from the start of each list in which it occurs. If it was the last method * in any list, then authentication is deemed successful. * Returns 1 if the method completed any authentication list or 0 otherwise. */ int auth2_update_methods_lists(Authctxt *authctxt, const char *method, const char *submethod) { u_int i, found = 0; debug3("%s: updating methods list after \"%s\"", __func__, method); for (i = 0; i < authctxt->num_auth_methods; i++) { if (!remove_method(&(authctxt->auth_methods[i]), method, submethod)) continue; found = 1; if (*authctxt->auth_methods[i] == '\0') { debug2("authentication methods list %d complete", i); return 1; } debug3("authentication methods list %d remaining: \"%s\"", i, authctxt->auth_methods[i]); } /* This should not happen, but would be bad if it did */ if (!found) fatal("%s: method not in AuthenticationMethods", __func__); return 0; } diff --git a/crypto/openssh/channels.c b/crypto/openssh/channels.c index bef8ad6aa2b3..d030fcdd9010 100644 --- a/crypto/openssh/channels.c +++ b/crypto/openssh/channels.c @@ -1,4619 +1,4672 @@ -/* $OpenBSD: channels.c,v 1.356 2016/10/18 17:32:54 dtucker Exp $ */ +/* $OpenBSD: channels.c,v 1.357 2017/02/01 02:59:09 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * This file contains functions for generic socket connection forwarding. * There is also code for initiating connection forwarding for X11 connections, * arbitrary tcp/ip connections, and the authentication agent connection. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * SSH2 support added by Markus Friedl. * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved. * Copyright (c) 1999 Dug Song. All rights reserved. * Copyright (c) 1999 Theo de Raadt. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include #include #include #ifdef HAVE_STDINT_H #include #endif #include #include #include #include #include #include #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "ssh.h" #include "ssh1.h" #include "ssh2.h" #include "ssherr.h" #include "packet.h" #include "log.h" #include "misc.h" #include "buffer.h" #include "channels.h" #include "compat.h" #include "canohost.h" #include "key.h" #include "authfd.h" #include "pathnames.h" /* -- channel core */ /* * Pointer to an array containing all allocated channels. The array is * dynamically extended as needed. */ static Channel **channels = NULL; /* * Size of the channel array. All slots of the array must always be * initialized (at least the type field); unused slots set to NULL */ static u_int channels_alloc = 0; /* * Maximum file descriptor value used in any of the channels. This is * updated in channel_new. */ static int channel_max_fd = 0; /* -- tcp forwarding */ /* * Data structure for storing which hosts are permitted for forward requests. * The local sides of any remote forwards are stored in this array to prevent * a corrupt remote server from accessing arbitrary TCP/IP ports on our local * network (which might be behind a firewall). */ /* XXX: streamlocal wants a path instead of host:port */ /* Overload host_to_connect; we could just make this match Forward */ /* XXX - can we use listen_host instead of listen_path? */ typedef struct { char *host_to_connect; /* Connect to 'host'. */ int port_to_connect; /* Connect to 'port'. */ char *listen_host; /* Remote side should listen address. */ char *listen_path; /* Remote side should listen path. */ int listen_port; /* Remote side should listen port. */ Channel *downstream; /* Downstream mux*/ } ForwardPermission; /* List of all permitted host/port pairs to connect by the user. */ static ForwardPermission *permitted_opens = NULL; /* List of all permitted host/port pairs to connect by the admin. */ static ForwardPermission *permitted_adm_opens = NULL; /* Number of permitted host/port pairs in the array permitted by the user. */ static int num_permitted_opens = 0; /* Number of permitted host/port pair in the array permitted by the admin. */ static int num_adm_permitted_opens = 0; /* special-case port number meaning allow any port */ #define FWD_PERMIT_ANY_PORT 0 /* special-case wildcard meaning allow any host */ #define FWD_PERMIT_ANY_HOST "*" /* * If this is true, all opens are permitted. This is the case on the server * on which we have to trust the client anyway, and the user could do * anything after logging in anyway. */ static int all_opens_permitted = 0; /* -- X11 forwarding */ /* Maximum number of fake X11 displays to try. */ #define MAX_DISPLAYS 1000 /* Saved X11 local (client) display. */ static char *x11_saved_display = NULL; /* Saved X11 authentication protocol name. */ static char *x11_saved_proto = NULL; /* Saved X11 authentication data. This is the real data. */ static char *x11_saved_data = NULL; static u_int x11_saved_data_len = 0; /* Deadline after which all X11 connections are refused */ static u_int x11_refuse_time; /* * Fake X11 authentication data. This is what the server will be sending us; * we should replace any occurrences of this by the real data. */ static u_char *x11_fake_data = NULL; static u_int x11_fake_data_len; /* -- agent forwarding */ #define NUM_SOCKS 10 /* AF_UNSPEC or AF_INET or AF_INET6 */ static int IPv4or6 = AF_UNSPEC; /* helper */ static void port_open_helper(Channel *c, char *rtype); static const char *channel_rfwd_bind_host(const char *listen_host); /* non-blocking connect helpers */ static int connect_next(struct channel_connect *); static void channel_connect_ctx_free(struct channel_connect *); /* -- channel core */ Channel * channel_by_id(int id) { Channel *c; if (id < 0 || (u_int)id >= channels_alloc) { logit("channel_by_id: %d: bad id", id); return NULL; } c = channels[id]; if (c == NULL) { logit("channel_by_id: %d: bad id: channel free", id); return NULL; } return c; } Channel * channel_by_remote_id(int remote_id) { Channel *c; u_int i; for (i = 0; i < channels_alloc; i++) { c = channels[i]; if (c != NULL && c->remote_id == remote_id) return c; } return NULL; } /* * Returns the channel if it is allowed to receive protocol messages. * Private channels, like listening sockets, may not receive messages. */ Channel * channel_lookup(int id) { Channel *c; if ((c = channel_by_id(id)) == NULL) return (NULL); switch (c->type) { case SSH_CHANNEL_X11_OPEN: case SSH_CHANNEL_LARVAL: case SSH_CHANNEL_CONNECTING: case SSH_CHANNEL_DYNAMIC: case SSH_CHANNEL_OPENING: case SSH_CHANNEL_OPEN: case SSH_CHANNEL_INPUT_DRAINING: case SSH_CHANNEL_OUTPUT_DRAINING: case SSH_CHANNEL_ABANDONED: case SSH_CHANNEL_MUX_PROXY: return (c); } logit("Non-public channel %d, type %d.", id, c->type); return (NULL); } /* * Register filedescriptors for a channel, used when allocating a channel or * when the channel consumer/producer is ready, e.g. shell exec'd */ static void channel_register_fds(Channel *c, int rfd, int wfd, int efd, int extusage, int nonblock, int is_tty) { /* Update the maximum file descriptor value. */ channel_max_fd = MAXIMUM(channel_max_fd, rfd); channel_max_fd = MAXIMUM(channel_max_fd, wfd); channel_max_fd = MAXIMUM(channel_max_fd, efd); if (rfd != -1) fcntl(rfd, F_SETFD, FD_CLOEXEC); if (wfd != -1 && wfd != rfd) fcntl(wfd, F_SETFD, FD_CLOEXEC); if (efd != -1 && efd != rfd && efd != wfd) fcntl(efd, F_SETFD, FD_CLOEXEC); c->rfd = rfd; c->wfd = wfd; c->sock = (rfd == wfd) ? rfd : -1; c->efd = efd; c->extended_usage = extusage; if ((c->isatty = is_tty) != 0) debug2("channel %d: rfd %d isatty", c->self, c->rfd); #ifdef _AIX /* XXX: Later AIX versions can't push as much data to tty */ c->wfd_isatty = is_tty || isatty(c->wfd); #endif /* enable nonblocking mode */ if (nonblock) { if (rfd != -1) set_nonblock(rfd); if (wfd != -1) set_nonblock(wfd); if (efd != -1) set_nonblock(efd); } } /* * Allocate a new channel object and set its type and socket. This will cause * remote_name to be freed. */ Channel * channel_new(char *ctype, int type, int rfd, int wfd, int efd, u_int window, u_int maxpack, int extusage, char *remote_name, int nonblock) { int found; u_int i; Channel *c; /* Do initial allocation if this is the first call. */ if (channels_alloc == 0) { channels_alloc = 10; channels = xcalloc(channels_alloc, sizeof(Channel *)); for (i = 0; i < channels_alloc; i++) channels[i] = NULL; } /* Try to find a free slot where to put the new channel. */ for (found = -1, i = 0; i < channels_alloc; i++) if (channels[i] == NULL) { /* Found a free slot. */ found = (int)i; break; } if (found < 0) { /* There are no free slots. Take last+1 slot and expand the array. */ found = channels_alloc; if (channels_alloc > 10000) fatal("channel_new: internal error: channels_alloc %d " "too big.", channels_alloc); channels = xreallocarray(channels, channels_alloc + 10, sizeof(Channel *)); channels_alloc += 10; debug2("channel: expanding %d", channels_alloc); for (i = found; i < channels_alloc; i++) channels[i] = NULL; } /* Initialize and return new channel. */ c = channels[found] = xcalloc(1, sizeof(Channel)); buffer_init(&c->input); buffer_init(&c->output); buffer_init(&c->extended); c->path = NULL; c->listening_addr = NULL; c->listening_port = 0; c->ostate = CHAN_OUTPUT_OPEN; c->istate = CHAN_INPUT_OPEN; c->flags = 0; channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, 0); c->notbefore = 0; c->self = found; c->type = type; c->ctype = ctype; c->local_window = window; c->local_window_max = window; c->local_consumed = 0; c->local_maxpacket = maxpack; c->remote_id = -1; c->remote_name = xstrdup(remote_name); c->remote_window = 0; c->remote_maxpacket = 0; c->force_drain = 0; c->single_connection = 0; c->detach_user = NULL; c->detach_close = 0; c->open_confirm = NULL; c->open_confirm_ctx = NULL; c->input_filter = NULL; c->output_filter = NULL; c->filter_ctx = NULL; c->filter_cleanup = NULL; c->ctl_chan = -1; c->mux_rcb = NULL; c->mux_ctx = NULL; c->mux_pause = 0; c->delayed = 1; /* prevent call to channel_post handler */ TAILQ_INIT(&c->status_confirms); debug("channel %d: new [%s]", found, remote_name); return c; } static int channel_find_maxfd(void) { u_int i; int max = 0; Channel *c; for (i = 0; i < channels_alloc; i++) { c = channels[i]; if (c != NULL) { max = MAXIMUM(max, c->rfd); max = MAXIMUM(max, c->wfd); max = MAXIMUM(max, c->efd); } } return max; } int channel_close_fd(int *fdp) { int ret = 0, fd = *fdp; if (fd != -1) { ret = close(fd); *fdp = -1; if (fd == channel_max_fd) channel_max_fd = channel_find_maxfd(); } return ret; } /* Close all channel fd/socket. */ static void channel_close_fds(Channel *c) { channel_close_fd(&c->sock); channel_close_fd(&c->rfd); channel_close_fd(&c->wfd); channel_close_fd(&c->efd); } /* Free the channel and close its fd/socket. */ void channel_free(Channel *c) { char *s; u_int i, n; Channel *other; struct channel_confirm *cc; for (n = 0, i = 0; i < channels_alloc; i++) { if ((other = channels[i]) != NULL) { n++; /* detach from mux client and prepare for closing */ if (c->type == SSH_CHANNEL_MUX_CLIENT && other->type == SSH_CHANNEL_MUX_PROXY && other->mux_ctx == c) { other->mux_ctx = NULL; other->type = SSH_CHANNEL_OPEN; other->istate = CHAN_INPUT_CLOSED; other->ostate = CHAN_OUTPUT_CLOSED; } } } debug("channel %d: free: %s, nchannels %u", c->self, c->remote_name ? c->remote_name : "???", n); /* XXX more MUX cleanup: remove remote forwardings */ if (c->type == SSH_CHANNEL_MUX_CLIENT) { for (i = 0; i < (u_int)num_permitted_opens; i++) { if (permitted_opens[i].downstream != c) continue; /* cancel on the server, since mux client is gone */ debug("channel %d: cleanup remote forward for %s:%u", c->self, permitted_opens[i].listen_host, permitted_opens[i].listen_port); packet_start(SSH2_MSG_GLOBAL_REQUEST); packet_put_cstring("cancel-tcpip-forward"); packet_put_char(0); packet_put_cstring(channel_rfwd_bind_host( permitted_opens[i].listen_host)); packet_put_int(permitted_opens[i].listen_port); packet_send(); /* unregister */ permitted_opens[i].listen_port = 0; permitted_opens[i].port_to_connect = 0; free(permitted_opens[i].host_to_connect); permitted_opens[i].host_to_connect = NULL; free(permitted_opens[i].listen_host); permitted_opens[i].listen_host = NULL; permitted_opens[i].listen_path = NULL; permitted_opens[i].downstream = NULL; } } s = channel_open_message(); debug3("channel %d: status: %s", c->self, s); free(s); if (c->sock != -1) shutdown(c->sock, SHUT_RDWR); channel_close_fds(c); buffer_free(&c->input); buffer_free(&c->output); buffer_free(&c->extended); free(c->remote_name); c->remote_name = NULL; free(c->path); c->path = NULL; free(c->listening_addr); c->listening_addr = NULL; while ((cc = TAILQ_FIRST(&c->status_confirms)) != NULL) { if (cc->abandon_cb != NULL) cc->abandon_cb(c, cc->ctx); TAILQ_REMOVE(&c->status_confirms, cc, entry); explicit_bzero(cc, sizeof(*cc)); free(cc); } if (c->filter_cleanup != NULL && c->filter_ctx != NULL) c->filter_cleanup(c->self, c->filter_ctx); channels[c->self] = NULL; free(c); } void channel_free_all(void) { u_int i; for (i = 0; i < channels_alloc; i++) if (channels[i] != NULL) channel_free(channels[i]); } /* * Closes the sockets/fds of all channels. This is used to close extra file * descriptors after a fork. */ void channel_close_all(void) { u_int i; for (i = 0; i < channels_alloc; i++) if (channels[i] != NULL) channel_close_fds(channels[i]); } /* * Stop listening to channels. */ void channel_stop_listening(void) { u_int i; Channel *c; for (i = 0; i < channels_alloc; i++) { c = channels[i]; if (c != NULL) { switch (c->type) { case SSH_CHANNEL_AUTH_SOCKET: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_RPORT_LISTENER: case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_UNIX_LISTENER: case SSH_CHANNEL_RUNIX_LISTENER: channel_close_fd(&c->sock); channel_free(c); break; } } } } /* * Returns true if no channel has too much buffered data, and false if one or * more channel is overfull. */ int channel_not_very_much_buffered_data(void) { u_int i; Channel *c; for (i = 0; i < channels_alloc; i++) { c = channels[i]; if (c != NULL && c->type == SSH_CHANNEL_OPEN) { #if 0 if (!compat20 && buffer_len(&c->input) > packet_get_maxsize()) { debug2("channel %d: big input buffer %d", c->self, buffer_len(&c->input)); return 0; } #endif if (buffer_len(&c->output) > packet_get_maxsize()) { debug2("channel %d: big output buffer %u > %u", c->self, buffer_len(&c->output), packet_get_maxsize()); return 0; } } } return 1; } /* Returns true if any channel is still open. */ int channel_still_open(void) { u_int i; Channel *c; for (i = 0; i < channels_alloc; i++) { c = channels[i]; if (c == NULL) continue; switch (c->type) { case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_RPORT_LISTENER: case SSH_CHANNEL_MUX_LISTENER: case SSH_CHANNEL_CLOSED: case SSH_CHANNEL_AUTH_SOCKET: case SSH_CHANNEL_DYNAMIC: case SSH_CHANNEL_CONNECTING: case SSH_CHANNEL_ZOMBIE: case SSH_CHANNEL_ABANDONED: case SSH_CHANNEL_UNIX_LISTENER: case SSH_CHANNEL_RUNIX_LISTENER: continue; case SSH_CHANNEL_LARVAL: if (!compat20) fatal("cannot happen: SSH_CHANNEL_LARVAL"); continue; case SSH_CHANNEL_OPENING: case SSH_CHANNEL_OPEN: case SSH_CHANNEL_X11_OPEN: case SSH_CHANNEL_MUX_CLIENT: case SSH_CHANNEL_MUX_PROXY: return 1; case SSH_CHANNEL_INPUT_DRAINING: case SSH_CHANNEL_OUTPUT_DRAINING: if (!compat13) fatal("cannot happen: OUT_DRAIN"); return 1; default: fatal("channel_still_open: bad channel type %d", c->type); /* NOTREACHED */ } } return 0; } /* Returns the id of an open channel suitable for keepaliving */ int channel_find_open(void) { u_int i; Channel *c; for (i = 0; i < channels_alloc; i++) { c = channels[i]; if (c == NULL || c->remote_id < 0) continue; switch (c->type) { case SSH_CHANNEL_CLOSED: case SSH_CHANNEL_DYNAMIC: case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_RPORT_LISTENER: case SSH_CHANNEL_MUX_LISTENER: case SSH_CHANNEL_MUX_CLIENT: case SSH_CHANNEL_MUX_PROXY: case SSH_CHANNEL_OPENING: case SSH_CHANNEL_CONNECTING: case SSH_CHANNEL_ZOMBIE: case SSH_CHANNEL_ABANDONED: case SSH_CHANNEL_UNIX_LISTENER: case SSH_CHANNEL_RUNIX_LISTENER: continue; case SSH_CHANNEL_LARVAL: case SSH_CHANNEL_AUTH_SOCKET: case SSH_CHANNEL_OPEN: case SSH_CHANNEL_X11_OPEN: return i; case SSH_CHANNEL_INPUT_DRAINING: case SSH_CHANNEL_OUTPUT_DRAINING: if (!compat13) fatal("cannot happen: OUT_DRAIN"); return i; default: fatal("channel_find_open: bad channel type %d", c->type); /* NOTREACHED */ } } return -1; } /* * Returns a message describing the currently open forwarded connections, * suitable for sending to the client. The message contains crlf pairs for * newlines. */ char * channel_open_message(void) { Buffer buffer; Channel *c; char buf[1024], *cp; u_int i; buffer_init(&buffer); snprintf(buf, sizeof buf, "The following connections are open:\r\n"); buffer_append(&buffer, buf, strlen(buf)); for (i = 0; i < channels_alloc; i++) { c = channels[i]; if (c == NULL) continue; switch (c->type) { case SSH_CHANNEL_X11_LISTENER: case SSH_CHANNEL_PORT_LISTENER: case SSH_CHANNEL_RPORT_LISTENER: case SSH_CHANNEL_CLOSED: case SSH_CHANNEL_AUTH_SOCKET: case SSH_CHANNEL_ZOMBIE: case SSH_CHANNEL_ABANDONED: case SSH_CHANNEL_MUX_LISTENER: case SSH_CHANNEL_UNIX_LISTENER: case SSH_CHANNEL_RUNIX_LISTENER: continue; case SSH_CHANNEL_LARVAL: case SSH_CHANNEL_OPENING: case SSH_CHANNEL_CONNECTING: case SSH_CHANNEL_DYNAMIC: case SSH_CHANNEL_OPEN: case SSH_CHANNEL_X11_OPEN: case SSH_CHANNEL_INPUT_DRAINING: case SSH_CHANNEL_OUTPUT_DRAINING: case SSH_CHANNEL_MUX_PROXY: case SSH_CHANNEL_MUX_CLIENT: snprintf(buf, sizeof buf, " #%d %.300s (t%d r%d i%u/%d o%u/%d fd %d/%d cc %d)\r\n", c->self, c->remote_name, c->type, c->remote_id, c->istate, buffer_len(&c->input), c->ostate, buffer_len(&c->output), c->rfd, c->wfd, c->ctl_chan); buffer_append(&buffer, buf, strlen(buf)); continue; default: fatal("channel_open_message: bad channel type %d", c->type); /* NOTREACHED */ } } buffer_append(&buffer, "\0", 1); cp = xstrdup((char *)buffer_ptr(&buffer)); buffer_free(&buffer); return cp; } void channel_send_open(int id) { Channel *c = channel_lookup(id); if (c == NULL) { logit("channel_send_open: %d: bad id", id); return; } debug2("channel %d: send open", id); packet_start(SSH2_MSG_CHANNEL_OPEN); packet_put_cstring(c->ctype); packet_put_int(c->self); packet_put_int(c->local_window); packet_put_int(c->local_maxpacket); packet_send(); } void channel_request_start(int id, char *service, int wantconfirm) { Channel *c = channel_lookup(id); if (c == NULL) { logit("channel_request_start: %d: unknown channel id", id); return; } debug2("channel %d: request %s confirm %d", id, service, wantconfirm); packet_start(SSH2_MSG_CHANNEL_REQUEST); packet_put_int(c->remote_id); packet_put_cstring(service); packet_put_char(wantconfirm); } void channel_register_status_confirm(int id, channel_confirm_cb *cb, channel_confirm_abandon_cb *abandon_cb, void *ctx) { struct channel_confirm *cc; Channel *c; if ((c = channel_lookup(id)) == NULL) fatal("channel_register_expect: %d: bad id", id); cc = xcalloc(1, sizeof(*cc)); cc->cb = cb; cc->abandon_cb = abandon_cb; cc->ctx = ctx; TAILQ_INSERT_TAIL(&c->status_confirms, cc, entry); } void channel_register_open_confirm(int id, channel_open_fn *fn, void *ctx) { Channel *c = channel_lookup(id); if (c == NULL) { logit("channel_register_open_confirm: %d: bad id", id); return; } c->open_confirm = fn; c->open_confirm_ctx = ctx; } void channel_register_cleanup(int id, channel_callback_fn *fn, int do_close) { Channel *c = channel_by_id(id); if (c == NULL) { logit("channel_register_cleanup: %d: bad id", id); return; } c->detach_user = fn; c->detach_close = do_close; } void channel_cancel_cleanup(int id) { Channel *c = channel_by_id(id); if (c == NULL) { logit("channel_cancel_cleanup: %d: bad id", id); return; } c->detach_user = NULL; c->detach_close = 0; } void channel_register_filter(int id, channel_infilter_fn *ifn, channel_outfilter_fn *ofn, channel_filter_cleanup_fn *cfn, void *ctx) { Channel *c = channel_lookup(id); if (c == NULL) { logit("channel_register_filter: %d: bad id", id); return; } c->input_filter = ifn; c->output_filter = ofn; c->filter_ctx = ctx; c->filter_cleanup = cfn; } void channel_set_fds(int id, int rfd, int wfd, int efd, int extusage, int nonblock, int is_tty, u_int window_max) { Channel *c = channel_lookup(id); if (c == NULL || c->type != SSH_CHANNEL_LARVAL) fatal("channel_activate for non-larval channel %d.", id); channel_register_fds(c, rfd, wfd, efd, extusage, nonblock, is_tty); c->type = SSH_CHANNEL_OPEN; c->local_window = c->local_window_max = window_max; packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); packet_put_int(c->remote_id); packet_put_int(c->local_window); packet_send(); } /* * 'channel_pre*' are called just before select() to add any bits relevant to * channels in the select bitmasks. */ /* * 'channel_post*': perform any appropriate operations for channels which * have events pending. */ typedef void chan_fn(Channel *c, fd_set *readset, fd_set *writeset); chan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE]; chan_fn *channel_post[SSH_CHANNEL_MAX_TYPE]; /* ARGSUSED */ static void channel_pre_listener(Channel *c, fd_set *readset, fd_set *writeset) { FD_SET(c->sock, readset); } /* ARGSUSED */ static void channel_pre_connecting(Channel *c, fd_set *readset, fd_set *writeset) { debug3("channel %d: waiting for connection", c->self); FD_SET(c->sock, writeset); } static void channel_pre_open_13(Channel *c, fd_set *readset, fd_set *writeset) { if (buffer_len(&c->input) < packet_get_maxsize()) FD_SET(c->sock, readset); if (buffer_len(&c->output) > 0) FD_SET(c->sock, writeset); } static void channel_pre_open(Channel *c, fd_set *readset, fd_set *writeset) { u_int limit = compat20 ? c->remote_window : packet_get_maxsize(); if (c->istate == CHAN_INPUT_OPEN && limit > 0 && buffer_len(&c->input) < limit && buffer_check_alloc(&c->input, CHAN_RBUF)) FD_SET(c->rfd, readset); if (c->ostate == CHAN_OUTPUT_OPEN || c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { if (buffer_len(&c->output) > 0) { FD_SET(c->wfd, writeset); } else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { if (CHANNEL_EFD_OUTPUT_ACTIVE(c)) debug2("channel %d: obuf_empty delayed efd %d/(%d)", c->self, c->efd, buffer_len(&c->extended)); else chan_obuf_empty(c); } } /** XXX check close conditions, too */ if (compat20 && c->efd != -1 && !(c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED)) { if (c->extended_usage == CHAN_EXTENDED_WRITE && buffer_len(&c->extended) > 0) FD_SET(c->efd, writeset); else if (c->efd != -1 && !(c->flags & CHAN_EOF_SENT) && (c->extended_usage == CHAN_EXTENDED_READ || c->extended_usage == CHAN_EXTENDED_IGNORE) && buffer_len(&c->extended) < c->remote_window) FD_SET(c->efd, readset); } /* XXX: What about efd? races? */ } /* ARGSUSED */ static void channel_pre_input_draining(Channel *c, fd_set *readset, fd_set *writeset) { if (buffer_len(&c->input) == 0) { packet_start(SSH_MSG_CHANNEL_CLOSE); packet_put_int(c->remote_id); packet_send(); c->type = SSH_CHANNEL_CLOSED; debug2("channel %d: closing after input drain.", c->self); } } /* ARGSUSED */ static void channel_pre_output_draining(Channel *c, fd_set *readset, fd_set *writeset) { if (buffer_len(&c->output) == 0) chan_mark_dead(c); else FD_SET(c->sock, writeset); } /* * This is a special state for X11 authentication spoofing. An opened X11 * connection (when authentication spoofing is being done) remains in this * state until the first packet has been completely read. The authentication * data in that packet is then substituted by the real data if it matches the * fake data, and the channel is put into normal mode. * XXX All this happens at the client side. * Returns: 0 = need more data, -1 = wrong cookie, 1 = ok */ static int x11_open_helper(Buffer *b) { u_char *ucp; u_int proto_len, data_len; /* Is this being called after the refusal deadline? */ if (x11_refuse_time != 0 && (u_int)monotime() >= x11_refuse_time) { verbose("Rejected X11 connection after ForwardX11Timeout " "expired"); return -1; } /* Check if the fixed size part of the packet is in buffer. */ if (buffer_len(b) < 12) return 0; /* Parse the lengths of variable-length fields. */ ucp = buffer_ptr(b); if (ucp[0] == 0x42) { /* Byte order MSB first. */ proto_len = 256 * ucp[6] + ucp[7]; data_len = 256 * ucp[8] + ucp[9]; } else if (ucp[0] == 0x6c) { /* Byte order LSB first. */ proto_len = ucp[6] + 256 * ucp[7]; data_len = ucp[8] + 256 * ucp[9]; } else { debug2("Initial X11 packet contains bad byte order byte: 0x%x", ucp[0]); return -1; } /* Check if the whole packet is in buffer. */ if (buffer_len(b) < 12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3)) return 0; /* Check if authentication protocol matches. */ if (proto_len != strlen(x11_saved_proto) || memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) { debug2("X11 connection uses different authentication protocol."); return -1; } /* Check if authentication data matches our fake data. */ if (data_len != x11_fake_data_len || timingsafe_bcmp(ucp + 12 + ((proto_len + 3) & ~3), x11_fake_data, x11_fake_data_len) != 0) { debug2("X11 auth data does not match fake data."); return -1; } /* Check fake data length */ if (x11_fake_data_len != x11_saved_data_len) { error("X11 fake_data_len %d != saved_data_len %d", x11_fake_data_len, x11_saved_data_len); return -1; } /* * Received authentication protocol and data match * our fake data. Substitute the fake data with real * data. */ memcpy(ucp + 12 + ((proto_len + 3) & ~3), x11_saved_data, x11_saved_data_len); return 1; } static void channel_pre_x11_open_13(Channel *c, fd_set *readset, fd_set *writeset) { int ret = x11_open_helper(&c->output); if (ret == 1) { /* Start normal processing for the channel. */ c->type = SSH_CHANNEL_OPEN; channel_pre_open_13(c, readset, writeset); } else if (ret == -1) { /* * We have received an X11 connection that has bad * authentication information. */ logit("X11 connection rejected because of wrong authentication."); buffer_clear(&c->input); buffer_clear(&c->output); channel_close_fd(&c->sock); c->sock = -1; c->type = SSH_CHANNEL_CLOSED; packet_start(SSH_MSG_CHANNEL_CLOSE); packet_put_int(c->remote_id); packet_send(); } } static void channel_pre_x11_open(Channel *c, fd_set *readset, fd_set *writeset) { int ret = x11_open_helper(&c->output); /* c->force_drain = 1; */ if (ret == 1) { c->type = SSH_CHANNEL_OPEN; channel_pre_open(c, readset, writeset); } else if (ret == -1) { logit("X11 connection rejected because of wrong authentication."); debug2("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate); chan_read_failed(c); buffer_clear(&c->input); chan_ibuf_empty(c); buffer_clear(&c->output); /* for proto v1, the peer will send an IEOF */ if (compat20) chan_write_failed(c); else c->type = SSH_CHANNEL_OPEN; debug2("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate); } } static void channel_pre_mux_client(Channel *c, fd_set *readset, fd_set *writeset) { if (c->istate == CHAN_INPUT_OPEN && !c->mux_pause && buffer_check_alloc(&c->input, CHAN_RBUF)) FD_SET(c->rfd, readset); if (c->istate == CHAN_INPUT_WAIT_DRAIN) { /* clear buffer immediately (discard any partial packet) */ buffer_clear(&c->input); chan_ibuf_empty(c); /* Start output drain. XXX just kill chan? */ chan_rcvd_oclose(c); } if (c->ostate == CHAN_OUTPUT_OPEN || c->ostate == CHAN_OUTPUT_WAIT_DRAIN) { if (buffer_len(&c->output) > 0) FD_SET(c->wfd, writeset); else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) chan_obuf_empty(c); } } /* try to decode a socks4 header */ /* ARGSUSED */ static int channel_decode_socks4(Channel *c, fd_set *readset, fd_set *writeset) { char *p, *host; u_int len, have, i, found, need; char username[256]; struct { u_int8_t version; u_int8_t command; u_int16_t dest_port; struct in_addr dest_addr; } s4_req, s4_rsp; debug2("channel %d: decode socks4", c->self); have = buffer_len(&c->input); len = sizeof(s4_req); if (have < len) return 0; p = (char *)buffer_ptr(&c->input); need = 1; /* SOCKS4A uses an invalid IP address 0.0.0.x */ if (p[4] == 0 && p[5] == 0 && p[6] == 0 && p[7] != 0) { debug2("channel %d: socks4a request", c->self); /* ... and needs an extra string (the hostname) */ need = 2; } /* Check for terminating NUL on the string(s) */ for (found = 0, i = len; i < have; i++) { if (p[i] == '\0') { found++; if (found == need) break; } if (i > 1024) { /* the peer is probably sending garbage */ debug("channel %d: decode socks4: too long", c->self); return -1; } } if (found < need) return 0; buffer_get(&c->input, (char *)&s4_req.version, 1); buffer_get(&c->input, (char *)&s4_req.command, 1); buffer_get(&c->input, (char *)&s4_req.dest_port, 2); buffer_get(&c->input, (char *)&s4_req.dest_addr, 4); have = buffer_len(&c->input); p = (char *)buffer_ptr(&c->input); if (memchr(p, '\0', have) == NULL) fatal("channel %d: decode socks4: user not nul terminated", c->self); len = strlen(p); debug2("channel %d: decode socks4: user %s/%d", c->self, p, len); len++; /* trailing '\0' */ if (len > have) fatal("channel %d: decode socks4: len %d > have %d", c->self, len, have); strlcpy(username, p, sizeof(username)); buffer_consume(&c->input, len); free(c->path); c->path = NULL; if (need == 1) { /* SOCKS4: one string */ host = inet_ntoa(s4_req.dest_addr); c->path = xstrdup(host); } else { /* SOCKS4A: two strings */ have = buffer_len(&c->input); p = (char *)buffer_ptr(&c->input); len = strlen(p); debug2("channel %d: decode socks4a: host %s/%d", c->self, p, len); len++; /* trailing '\0' */ if (len > have) fatal("channel %d: decode socks4a: len %d > have %d", c->self, len, have); if (len > NI_MAXHOST) { error("channel %d: hostname \"%.100s\" too long", c->self, p); return -1; } c->path = xstrdup(p); buffer_consume(&c->input, len); } c->host_port = ntohs(s4_req.dest_port); debug2("channel %d: dynamic request: socks4 host %s port %u command %u", c->self, c->path, c->host_port, s4_req.command); if (s4_req.command != 1) { debug("channel %d: cannot handle: %s cn %d", c->self, need == 1 ? "SOCKS4" : "SOCKS4A", s4_req.command); return -1; } s4_rsp.version = 0; /* vn: 0 for reply */ s4_rsp.command = 90; /* cd: req granted */ s4_rsp.dest_port = 0; /* ignored */ s4_rsp.dest_addr.s_addr = INADDR_ANY; /* ignored */ buffer_append(&c->output, &s4_rsp, sizeof(s4_rsp)); return 1; } /* try to decode a socks5 header */ #define SSH_SOCKS5_AUTHDONE 0x1000 #define SSH_SOCKS5_NOAUTH 0x00 #define SSH_SOCKS5_IPV4 0x01 #define SSH_SOCKS5_DOMAIN 0x03 #define SSH_SOCKS5_IPV6 0x04 #define SSH_SOCKS5_CONNECT 0x01 #define SSH_SOCKS5_SUCCESS 0x00 /* ARGSUSED */ static int channel_decode_socks5(Channel *c, fd_set *readset, fd_set *writeset) { struct { u_int8_t version; u_int8_t command; u_int8_t reserved; u_int8_t atyp; } s5_req, s5_rsp; u_int16_t dest_port; char dest_addr[255+1], ntop[INET6_ADDRSTRLEN]; u_char *p; u_int have, need, i, found, nmethods, addrlen, af; debug2("channel %d: decode socks5", c->self); p = buffer_ptr(&c->input); if (p[0] != 0x05) return -1; have = buffer_len(&c->input); if (!(c->flags & SSH_SOCKS5_AUTHDONE)) { /* format: ver | nmethods | methods */ if (have < 2) return 0; nmethods = p[1]; if (have < nmethods + 2) return 0; /* look for method: "NO AUTHENTICATION REQUIRED" */ for (found = 0, i = 2; i < nmethods + 2; i++) { if (p[i] == SSH_SOCKS5_NOAUTH) { found = 1; break; } } if (!found) { debug("channel %d: method SSH_SOCKS5_NOAUTH not found", c->self); return -1; } buffer_consume(&c->input, nmethods + 2); buffer_put_char(&c->output, 0x05); /* version */ buffer_put_char(&c->output, SSH_SOCKS5_NOAUTH); /* method */ FD_SET(c->sock, writeset); c->flags |= SSH_SOCKS5_AUTHDONE; debug2("channel %d: socks5 auth done", c->self); return 0; /* need more */ } debug2("channel %d: socks5 post auth", c->self); if (have < sizeof(s5_req)+1) return 0; /* need more */ memcpy(&s5_req, p, sizeof(s5_req)); if (s5_req.version != 0x05 || s5_req.command != SSH_SOCKS5_CONNECT || s5_req.reserved != 0x00) { debug2("channel %d: only socks5 connect supported", c->self); return -1; } switch (s5_req.atyp){ case SSH_SOCKS5_IPV4: addrlen = 4; af = AF_INET; break; case SSH_SOCKS5_DOMAIN: addrlen = p[sizeof(s5_req)]; af = -1; break; case SSH_SOCKS5_IPV6: addrlen = 16; af = AF_INET6; break; default: debug2("channel %d: bad socks5 atyp %d", c->self, s5_req.atyp); return -1; } need = sizeof(s5_req) + addrlen + 2; if (s5_req.atyp == SSH_SOCKS5_DOMAIN) need++; if (have < need) return 0; buffer_consume(&c->input, sizeof(s5_req)); if (s5_req.atyp == SSH_SOCKS5_DOMAIN) buffer_consume(&c->input, 1); /* host string length */ buffer_get(&c->input, &dest_addr, addrlen); buffer_get(&c->input, (char *)&dest_port, 2); dest_addr[addrlen] = '\0'; free(c->path); c->path = NULL; if (s5_req.atyp == SSH_SOCKS5_DOMAIN) { if (addrlen >= NI_MAXHOST) { error("channel %d: dynamic request: socks5 hostname " "\"%.100s\" too long", c->self, dest_addr); return -1; } c->path = xstrdup(dest_addr); } else { if (inet_ntop(af, dest_addr, ntop, sizeof(ntop)) == NULL) return -1; c->path = xstrdup(ntop); } c->host_port = ntohs(dest_port); debug2("channel %d: dynamic request: socks5 host %s port %u command %u", c->self, c->path, c->host_port, s5_req.command); s5_rsp.version = 0x05; s5_rsp.command = SSH_SOCKS5_SUCCESS; s5_rsp.reserved = 0; /* ignored */ s5_rsp.atyp = SSH_SOCKS5_IPV4; dest_port = 0; /* ignored */ buffer_append(&c->output, &s5_rsp, sizeof(s5_rsp)); buffer_put_int(&c->output, ntohl(INADDR_ANY)); /* bind address */ buffer_append(&c->output, &dest_port, sizeof(dest_port)); return 1; } Channel * channel_connect_stdio_fwd(const char *host_to_connect, u_short port_to_connect, int in, int out) { Channel *c; debug("channel_connect_stdio_fwd %s:%d", host_to_connect, port_to_connect); c = channel_new("stdio-forward", SSH_CHANNEL_OPENING, in, out, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "stdio-forward", /*nonblock*/0); c->path = xstrdup(host_to_connect); c->host_port = port_to_connect; c->listening_port = 0; c->force_drain = 1; channel_register_fds(c, in, out, -1, 0, 1, 0); port_open_helper(c, "direct-tcpip"); return c; } /* dynamic port forwarding */ static void channel_pre_dynamic(Channel *c, fd_set *readset, fd_set *writeset) { u_char *p; u_int have; int ret; have = buffer_len(&c->input); debug2("channel %d: pre_dynamic: have %d", c->self, have); /* buffer_dump(&c->input); */ /* check if the fixed size part of the packet is in buffer. */ if (have < 3) { /* need more */ FD_SET(c->sock, readset); return; } /* try to guess the protocol */ p = buffer_ptr(&c->input); switch (p[0]) { case 0x04: ret = channel_decode_socks4(c, readset, writeset); break; case 0x05: ret = channel_decode_socks5(c, readset, writeset); break; default: ret = -1; break; } if (ret < 0) { chan_mark_dead(c); } else if (ret == 0) { debug2("channel %d: pre_dynamic: need more", c->self); /* need more */ FD_SET(c->sock, readset); } else { /* switch to the next state */ c->type = SSH_CHANNEL_OPENING; port_open_helper(c, "direct-tcpip"); } } /* This is our fake X11 server socket. */ /* ARGSUSED */ static void channel_post_x11_listener(Channel *c, fd_set *readset, fd_set *writeset) { Channel *nc; struct sockaddr_storage addr; int newsock, oerrno; socklen_t addrlen; char buf[16384], *remote_ipaddr; int remote_port; if (FD_ISSET(c->sock, readset)) { debug("X11 connection requested."); addrlen = sizeof(addr); newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); if (c->single_connection) { oerrno = errno; debug2("single_connection: closing X11 listener."); channel_close_fd(&c->sock); chan_mark_dead(c); errno = oerrno; } if (newsock < 0) { if (errno != EINTR && errno != EWOULDBLOCK && errno != ECONNABORTED) error("accept: %.100s", strerror(errno)); if (errno == EMFILE || errno == ENFILE) c->notbefore = monotime() + 1; return; } set_nodelay(newsock); remote_ipaddr = get_peer_ipaddr(newsock); remote_port = get_peer_port(newsock); snprintf(buf, sizeof buf, "X11 connection from %.200s port %d", remote_ipaddr, remote_port); nc = channel_new("accepted x11 socket", SSH_CHANNEL_OPENING, newsock, newsock, -1, c->local_window_max, c->local_maxpacket, 0, buf, 1); if (compat20) { packet_start(SSH2_MSG_CHANNEL_OPEN); packet_put_cstring("x11"); packet_put_int(nc->self); packet_put_int(nc->local_window_max); packet_put_int(nc->local_maxpacket); /* originator ipaddr and port */ packet_put_cstring(remote_ipaddr); if (datafellows & SSH_BUG_X11FWD) { debug2("ssh2 x11 bug compat mode"); } else { packet_put_int(remote_port); } packet_send(); } else { packet_start(SSH_SMSG_X11_OPEN); packet_put_int(nc->self); if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) packet_put_cstring(buf); packet_send(); } free(remote_ipaddr); } } static void port_open_helper(Channel *c, char *rtype) { char buf[1024]; char *local_ipaddr = get_local_ipaddr(c->sock); int local_port = c->sock == -1 ? 65536 : get_local_port(c->sock); char *remote_ipaddr = get_peer_ipaddr(c->sock); int remote_port = get_peer_port(c->sock); if (remote_port == -1) { /* Fake addr/port to appease peers that validate it (Tectia) */ free(remote_ipaddr); remote_ipaddr = xstrdup("127.0.0.1"); remote_port = 65535; } snprintf(buf, sizeof buf, "%s: listening port %d for %.100s port %d, " "connect from %.200s port %d to %.100s port %d", rtype, c->listening_port, c->path, c->host_port, remote_ipaddr, remote_port, local_ipaddr, local_port); free(c->remote_name); c->remote_name = xstrdup(buf); if (compat20) { packet_start(SSH2_MSG_CHANNEL_OPEN); packet_put_cstring(rtype); packet_put_int(c->self); packet_put_int(c->local_window_max); packet_put_int(c->local_maxpacket); if (strcmp(rtype, "direct-tcpip") == 0) { /* target host, port */ packet_put_cstring(c->path); packet_put_int(c->host_port); } else if (strcmp(rtype, "direct-streamlocal@openssh.com") == 0) { /* target path */ packet_put_cstring(c->path); } else if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { /* listen path */ packet_put_cstring(c->path); } else { /* listen address, port */ packet_put_cstring(c->path); packet_put_int(local_port); } if (strcmp(rtype, "forwarded-streamlocal@openssh.com") == 0) { /* reserved for future owner/mode info */ packet_put_cstring(""); } else { /* originator host and port */ packet_put_cstring(remote_ipaddr); packet_put_int((u_int)remote_port); } packet_send(); } else { packet_start(SSH_MSG_PORT_OPEN); packet_put_int(c->self); packet_put_cstring(c->path); packet_put_int(c->host_port); if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) packet_put_cstring(c->remote_name); packet_send(); } free(remote_ipaddr); free(local_ipaddr); } static void channel_set_reuseaddr(int fd) { int on = 1; /* * Set socket options. * Allow local port reuse in TIME_WAIT. */ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) error("setsockopt SO_REUSEADDR fd %d: %s", fd, strerror(errno)); } void channel_set_x11_refuse_time(u_int refuse_time) { x11_refuse_time = refuse_time; } /* * This socket is listening for connections to a forwarded TCP/IP port. */ /* ARGSUSED */ static void channel_post_port_listener(Channel *c, fd_set *readset, fd_set *writeset) { Channel *nc; struct sockaddr_storage addr; int newsock, nextstate; socklen_t addrlen; char *rtype; if (FD_ISSET(c->sock, readset)) { debug("Connection to port %d forwarding " "to %.100s port %d requested.", c->listening_port, c->path, c->host_port); if (c->type == SSH_CHANNEL_RPORT_LISTENER) { nextstate = SSH_CHANNEL_OPENING; rtype = "forwarded-tcpip"; } else if (c->type == SSH_CHANNEL_RUNIX_LISTENER) { nextstate = SSH_CHANNEL_OPENING; rtype = "forwarded-streamlocal@openssh.com"; } else if (c->host_port == PORT_STREAMLOCAL) { nextstate = SSH_CHANNEL_OPENING; rtype = "direct-streamlocal@openssh.com"; } else if (c->host_port == 0) { nextstate = SSH_CHANNEL_DYNAMIC; rtype = "dynamic-tcpip"; } else { nextstate = SSH_CHANNEL_OPENING; rtype = "direct-tcpip"; } addrlen = sizeof(addr); newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); if (newsock < 0) { if (errno != EINTR && errno != EWOULDBLOCK && errno != ECONNABORTED) error("accept: %.100s", strerror(errno)); if (errno == EMFILE || errno == ENFILE) c->notbefore = monotime() + 1; return; } if (c->host_port != PORT_STREAMLOCAL) set_nodelay(newsock); nc = channel_new(rtype, nextstate, newsock, newsock, -1, c->local_window_max, c->local_maxpacket, 0, rtype, 1); nc->listening_port = c->listening_port; nc->host_port = c->host_port; if (c->path != NULL) nc->path = xstrdup(c->path); if (nextstate != SSH_CHANNEL_DYNAMIC) port_open_helper(nc, rtype); } } /* * This is the authentication agent socket listening for connections from * clients. */ /* ARGSUSED */ static void channel_post_auth_listener(Channel *c, fd_set *readset, fd_set *writeset) { Channel *nc; int newsock; struct sockaddr_storage addr; socklen_t addrlen; if (FD_ISSET(c->sock, readset)) { addrlen = sizeof(addr); newsock = accept(c->sock, (struct sockaddr *)&addr, &addrlen); if (newsock < 0) { error("accept from auth socket: %.100s", strerror(errno)); if (errno == EMFILE || errno == ENFILE) c->notbefore = monotime() + 1; return; } nc = channel_new("accepted auth socket", SSH_CHANNEL_OPENING, newsock, newsock, -1, c->local_window_max, c->local_maxpacket, 0, "accepted auth socket", 1); if (compat20) { packet_start(SSH2_MSG_CHANNEL_OPEN); packet_put_cstring("auth-agent@openssh.com"); packet_put_int(nc->self); packet_put_int(c->local_window_max); packet_put_int(c->local_maxpacket); } else { packet_start(SSH_SMSG_AGENT_OPEN); packet_put_int(nc->self); } packet_send(); } } /* ARGSUSED */ static void channel_post_connecting(Channel *c, fd_set *readset, fd_set *writeset) { int err = 0, sock; socklen_t sz = sizeof(err); if (FD_ISSET(c->sock, writeset)) { if (getsockopt(c->sock, SOL_SOCKET, SO_ERROR, &err, &sz) < 0) { err = errno; error("getsockopt SO_ERROR failed"); } if (err == 0) { debug("channel %d: connected to %s port %d", c->self, c->connect_ctx.host, c->connect_ctx.port); channel_connect_ctx_free(&c->connect_ctx); c->type = SSH_CHANNEL_OPEN; if (compat20) { packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); packet_put_int(c->remote_id); packet_put_int(c->self); packet_put_int(c->local_window); packet_put_int(c->local_maxpacket); } else { packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); packet_put_int(c->remote_id); packet_put_int(c->self); } } else { debug("channel %d: connection failed: %s", c->self, strerror(err)); /* Try next address, if any */ if ((sock = connect_next(&c->connect_ctx)) > 0) { close(c->sock); c->sock = c->rfd = c->wfd = sock; channel_max_fd = channel_find_maxfd(); return; } /* Exhausted all addresses */ error("connect_to %.100s port %d: failed.", c->connect_ctx.host, c->connect_ctx.port); channel_connect_ctx_free(&c->connect_ctx); if (compat20) { packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(c->remote_id); packet_put_int(SSH2_OPEN_CONNECT_FAILED); if (!(datafellows & SSH_BUG_OPENFAILURE)) { packet_put_cstring(strerror(err)); packet_put_cstring(""); } } else { packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(c->remote_id); } chan_mark_dead(c); } packet_send(); } } /* ARGSUSED */ static int channel_handle_rfd(Channel *c, fd_set *readset, fd_set *writeset) { char buf[CHAN_RBUF]; int len, force; force = c->isatty && c->detach_close && c->istate != CHAN_INPUT_CLOSED; if (c->rfd != -1 && (force || FD_ISSET(c->rfd, readset))) { errno = 0; len = read(c->rfd, buf, sizeof(buf)); if (len < 0 && (errno == EINTR || ((errno == EAGAIN || errno == EWOULDBLOCK) && !force))) return 1; #ifndef PTY_ZEROREAD if (len <= 0) { #else if ((!c->isatty && len <= 0) || (c->isatty && (len < 0 || (len == 0 && errno != 0)))) { #endif debug2("channel %d: read<=0 rfd %d len %d", c->self, c->rfd, len); if (c->type != SSH_CHANNEL_OPEN) { debug2("channel %d: not open", c->self); chan_mark_dead(c); return -1; } else if (compat13) { buffer_clear(&c->output); c->type = SSH_CHANNEL_INPUT_DRAINING; debug2("channel %d: input draining.", c->self); } else { chan_read_failed(c); } return -1; } if (c->input_filter != NULL) { if (c->input_filter(c, buf, len) == -1) { debug2("channel %d: filter stops", c->self); chan_read_failed(c); } } else if (c->datagram) { buffer_put_string(&c->input, buf, len); } else { buffer_append(&c->input, buf, len); } } return 1; } /* ARGSUSED */ static int channel_handle_wfd(Channel *c, fd_set *readset, fd_set *writeset) { struct termios tio; u_char *data = NULL, *buf; u_int dlen, olen = 0; int len; /* Send buffered output data to the socket. */ if (c->wfd != -1 && FD_ISSET(c->wfd, writeset) && buffer_len(&c->output) > 0) { olen = buffer_len(&c->output); if (c->output_filter != NULL) { if ((buf = c->output_filter(c, &data, &dlen)) == NULL) { debug2("channel %d: filter stops", c->self); if (c->type != SSH_CHANNEL_OPEN) chan_mark_dead(c); else chan_write_failed(c); return -1; } } else if (c->datagram) { buf = data = buffer_get_string(&c->output, &dlen); } else { buf = data = buffer_ptr(&c->output); dlen = buffer_len(&c->output); } if (c->datagram) { /* ignore truncated writes, datagrams might get lost */ len = write(c->wfd, buf, dlen); free(data); if (len < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) return 1; if (len <= 0) { if (c->type != SSH_CHANNEL_OPEN) chan_mark_dead(c); else chan_write_failed(c); return -1; } goto out; } #ifdef _AIX /* XXX: Later AIX versions can't push as much data to tty */ if (compat20 && c->wfd_isatty) dlen = MIN(dlen, 8*1024); #endif len = write(c->wfd, buf, dlen); if (len < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) return 1; if (len <= 0) { if (c->type != SSH_CHANNEL_OPEN) { debug2("channel %d: not open", c->self); chan_mark_dead(c); return -1; } else if (compat13) { buffer_clear(&c->output); debug2("channel %d: input draining.", c->self); c->type = SSH_CHANNEL_INPUT_DRAINING; } else { chan_write_failed(c); } return -1; } #ifndef BROKEN_TCGETATTR_ICANON if (compat20 && c->isatty && dlen >= 1 && buf[0] != '\r') { if (tcgetattr(c->wfd, &tio) == 0 && !(tio.c_lflag & ECHO) && (tio.c_lflag & ICANON)) { /* * Simulate echo to reduce the impact of * traffic analysis. We need to match the * size of a SSH2_MSG_CHANNEL_DATA message * (4 byte channel id + buf) */ packet_send_ignore(4 + len); packet_send(); } } #endif buffer_consume(&c->output, len); } out: if (compat20 && olen > 0) c->local_consumed += olen - buffer_len(&c->output); return 1; } static int channel_handle_efd(Channel *c, fd_set *readset, fd_set *writeset) { char buf[CHAN_RBUF]; int len; /** XXX handle drain efd, too */ if (c->efd != -1) { if (c->extended_usage == CHAN_EXTENDED_WRITE && FD_ISSET(c->efd, writeset) && buffer_len(&c->extended) > 0) { len = write(c->efd, buffer_ptr(&c->extended), buffer_len(&c->extended)); debug2("channel %d: written %d to efd %d", c->self, len, c->efd); if (len < 0 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) return 1; if (len <= 0) { debug2("channel %d: closing write-efd %d", c->self, c->efd); channel_close_fd(&c->efd); } else { buffer_consume(&c->extended, len); c->local_consumed += len; } } else if (c->efd != -1 && (c->extended_usage == CHAN_EXTENDED_READ || c->extended_usage == CHAN_EXTENDED_IGNORE) && (c->detach_close || FD_ISSET(c->efd, readset))) { len = read(c->efd, buf, sizeof(buf)); debug2("channel %d: read %d from efd %d", c->self, len, c->efd); if (len < 0 && (errno == EINTR || ((errno == EAGAIN || errno == EWOULDBLOCK) && !c->detach_close))) return 1; if (len <= 0) { debug2("channel %d: closing read-efd %d", c->self, c->efd); channel_close_fd(&c->efd); } else { if (c->extended_usage == CHAN_EXTENDED_IGNORE) { debug3("channel %d: discard efd", c->self); } else buffer_append(&c->extended, buf, len); } } } return 1; } static int channel_check_window(Channel *c) { if (c->type == SSH_CHANNEL_OPEN && !(c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD)) && ((c->local_window_max - c->local_window > c->local_maxpacket*3) || c->local_window < c->local_window_max/2) && c->local_consumed > 0) { packet_start(SSH2_MSG_CHANNEL_WINDOW_ADJUST); packet_put_int(c->remote_id); packet_put_int(c->local_consumed); packet_send(); debug2("channel %d: window %d sent adjust %d", c->self, c->local_window, c->local_consumed); c->local_window += c->local_consumed; c->local_consumed = 0; } return 1; } static void channel_post_open(Channel *c, fd_set *readset, fd_set *writeset) { channel_handle_rfd(c, readset, writeset); channel_handle_wfd(c, readset, writeset); if (!compat20) return; channel_handle_efd(c, readset, writeset); channel_check_window(c); } static u_int read_mux(Channel *c, u_int need) { char buf[CHAN_RBUF]; int len; u_int rlen; if (buffer_len(&c->input) < need) { rlen = need - buffer_len(&c->input); len = read(c->rfd, buf, MINIMUM(rlen, CHAN_RBUF)); if (len < 0 && (errno == EINTR || errno == EAGAIN)) return buffer_len(&c->input); if (len <= 0) { debug2("channel %d: ctl read<=0 rfd %d len %d", c->self, c->rfd, len); chan_read_failed(c); return 0; } else buffer_append(&c->input, buf, len); } return buffer_len(&c->input); } static void channel_post_mux_client(Channel *c, fd_set *readset, fd_set *writeset) { u_int need; ssize_t len; if (!compat20) fatal("%s: entered with !compat20", __func__); if (c->rfd != -1 && !c->mux_pause && FD_ISSET(c->rfd, readset) && (c->istate == CHAN_INPUT_OPEN || c->istate == CHAN_INPUT_WAIT_DRAIN)) { /* * Don't not read past the precise end of packets to * avoid disrupting fd passing. */ if (read_mux(c, 4) < 4) /* read header */ return; need = get_u32(buffer_ptr(&c->input)); #define CHANNEL_MUX_MAX_PACKET (256 * 1024) if (need > CHANNEL_MUX_MAX_PACKET) { debug2("channel %d: packet too big %u > %u", c->self, CHANNEL_MUX_MAX_PACKET, need); chan_rcvd_oclose(c); return; } if (read_mux(c, need + 4) < need + 4) /* read body */ return; if (c->mux_rcb(c) != 0) { debug("channel %d: mux_rcb failed", c->self); chan_mark_dead(c); return; } } if (c->wfd != -1 && FD_ISSET(c->wfd, writeset) && buffer_len(&c->output) > 0) { len = write(c->wfd, buffer_ptr(&c->output), buffer_len(&c->output)); if (len < 0 && (errno == EINTR || errno == EAGAIN)) return; if (len <= 0) { chan_mark_dead(c); return; } buffer_consume(&c->output, len); } } static void channel_post_mux_listener(Channel *c, fd_set *readset, fd_set *writeset) { Channel *nc; struct sockaddr_storage addr; socklen_t addrlen; int newsock; uid_t euid; gid_t egid; if (!FD_ISSET(c->sock, readset)) return; debug("multiplexing control connection"); /* * Accept connection on control socket */ memset(&addr, 0, sizeof(addr)); addrlen = sizeof(addr); if ((newsock = accept(c->sock, (struct sockaddr*)&addr, &addrlen)) == -1) { error("%s accept: %s", __func__, strerror(errno)); if (errno == EMFILE || errno == ENFILE) c->notbefore = monotime() + 1; return; } if (getpeereid(newsock, &euid, &egid) < 0) { error("%s getpeereid failed: %s", __func__, strerror(errno)); close(newsock); return; } if ((euid != 0) && (getuid() != euid)) { error("multiplex uid mismatch: peer euid %u != uid %u", (u_int)euid, (u_int)getuid()); close(newsock); return; } nc = channel_new("multiplex client", SSH_CHANNEL_MUX_CLIENT, newsock, newsock, -1, c->local_window_max, c->local_maxpacket, 0, "mux-control", 1); nc->mux_rcb = c->mux_rcb; debug3("%s: new mux channel %d fd %d", __func__, nc->self, nc->sock); /* establish state */ nc->mux_rcb(nc); /* mux state transitions must not elicit protocol messages */ nc->flags |= CHAN_LOCAL; } /* ARGSUSED */ static void channel_post_output_drain_13(Channel *c, fd_set *readset, fd_set *writeset) { int len; /* Send buffered output data to the socket. */ if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) { len = write(c->sock, buffer_ptr(&c->output), buffer_len(&c->output)); if (len <= 0) buffer_clear(&c->output); else buffer_consume(&c->output, len); } } static void channel_handler_init_20(void) { channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_RPORT_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_UNIX_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_RUNIX_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; channel_pre[SSH_CHANNEL_MUX_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_MUX_CLIENT] = &channel_pre_mux_client; channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; channel_post[SSH_CHANNEL_RPORT_LISTENER] = &channel_post_port_listener; channel_post[SSH_CHANNEL_UNIX_LISTENER] = &channel_post_port_listener; channel_post[SSH_CHANNEL_RUNIX_LISTENER] = &channel_post_port_listener; channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; channel_post[SSH_CHANNEL_MUX_LISTENER] = &channel_post_mux_listener; channel_post[SSH_CHANNEL_MUX_CLIENT] = &channel_post_mux_client; } static void channel_handler_init_13(void) { channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open_13; channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open_13; channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; channel_pre[SSH_CHANNEL_INPUT_DRAINING] = &channel_pre_input_draining; channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_pre_output_draining; channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; channel_post[SSH_CHANNEL_OUTPUT_DRAINING] = &channel_post_output_drain_13; channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; } static void channel_handler_init_15(void) { channel_pre[SSH_CHANNEL_OPEN] = &channel_pre_open; channel_pre[SSH_CHANNEL_X11_OPEN] = &channel_pre_x11_open; channel_pre[SSH_CHANNEL_X11_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_PORT_LISTENER] = &channel_pre_listener; channel_pre[SSH_CHANNEL_AUTH_SOCKET] = &channel_pre_listener; channel_pre[SSH_CHANNEL_CONNECTING] = &channel_pre_connecting; channel_pre[SSH_CHANNEL_DYNAMIC] = &channel_pre_dynamic; channel_post[SSH_CHANNEL_X11_LISTENER] = &channel_post_x11_listener; channel_post[SSH_CHANNEL_PORT_LISTENER] = &channel_post_port_listener; channel_post[SSH_CHANNEL_AUTH_SOCKET] = &channel_post_auth_listener; channel_post[SSH_CHANNEL_OPEN] = &channel_post_open; channel_post[SSH_CHANNEL_CONNECTING] = &channel_post_connecting; channel_post[SSH_CHANNEL_DYNAMIC] = &channel_post_open; } static void channel_handler_init(void) { int i; for (i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) { channel_pre[i] = NULL; channel_post[i] = NULL; } if (compat20) channel_handler_init_20(); else if (compat13) channel_handler_init_13(); else channel_handler_init_15(); } /* gc dead channels */ static void channel_garbage_collect(Channel *c) { if (c == NULL) return; if (c->detach_user != NULL) { if (!chan_is_dead(c, c->detach_close)) return; debug2("channel %d: gc: notify user", c->self); c->detach_user(c->self, NULL); /* if we still have a callback */ if (c->detach_user != NULL) return; debug2("channel %d: gc: user detached", c->self); } if (!chan_is_dead(c, 1)) return; debug2("channel %d: garbage collecting", c->self); channel_free(c); } static void channel_handler(chan_fn *ftab[], fd_set *readset, fd_set *writeset, time_t *unpause_secs) { static int did_init = 0; u_int i, oalloc; Channel *c; time_t now; if (!did_init) { channel_handler_init(); did_init = 1; } now = monotime(); if (unpause_secs != NULL) *unpause_secs = 0; for (i = 0, oalloc = channels_alloc; i < oalloc; i++) { c = channels[i]; if (c == NULL) continue; if (c->delayed) { if (ftab == channel_pre) c->delayed = 0; else continue; } if (ftab[c->type] != NULL) { /* * Run handlers that are not paused. */ if (c->notbefore <= now) (*ftab[c->type])(c, readset, writeset); else if (unpause_secs != NULL) { /* * Collect the time that the earliest * channel comes off pause. */ debug3("%s: chan %d: skip for %d more seconds", __func__, c->self, (int)(c->notbefore - now)); if (*unpause_secs == 0 || (c->notbefore - now) < *unpause_secs) *unpause_secs = c->notbefore - now; } } channel_garbage_collect(c); } if (unpause_secs != NULL && *unpause_secs != 0) debug3("%s: first channel unpauses in %d seconds", __func__, (int)*unpause_secs); } /* * Allocate/update select bitmasks and add any bits relevant to channels in * select bitmasks. */ void channel_prepare_select(fd_set **readsetp, fd_set **writesetp, int *maxfdp, u_int *nallocp, time_t *minwait_secs, int rekeying) { u_int n, sz, nfdset; n = MAXIMUM(*maxfdp, channel_max_fd); nfdset = howmany(n+1, NFDBITS); /* Explicitly test here, because xrealloc isn't always called */ if (nfdset && SIZE_MAX / nfdset < sizeof(fd_mask)) fatal("channel_prepare_select: max_fd (%d) is too large", n); sz = nfdset * sizeof(fd_mask); /* perhaps check sz < nalloc/2 and shrink? */ if (*readsetp == NULL || sz > *nallocp) { *readsetp = xreallocarray(*readsetp, nfdset, sizeof(fd_mask)); *writesetp = xreallocarray(*writesetp, nfdset, sizeof(fd_mask)); *nallocp = sz; } *maxfdp = n; memset(*readsetp, 0, sz); memset(*writesetp, 0, sz); if (!rekeying) channel_handler(channel_pre, *readsetp, *writesetp, minwait_secs); } /* * After select, perform any appropriate operations for channels which have * events pending. */ void channel_after_select(fd_set *readset, fd_set *writeset) { channel_handler(channel_post, readset, writeset, NULL); } /* If there is data to send to the connection, enqueue some of it now. */ void channel_output_poll(void) { Channel *c; u_int i, len; for (i = 0; i < channels_alloc; i++) { c = channels[i]; if (c == NULL) continue; /* * We are only interested in channels that can have buffered * incoming data. */ if (compat13) { if (c->type != SSH_CHANNEL_OPEN && c->type != SSH_CHANNEL_INPUT_DRAINING) continue; } else { if (c->type != SSH_CHANNEL_OPEN) continue; } if (compat20 && (c->flags & (CHAN_CLOSE_SENT|CHAN_CLOSE_RCVD))) { /* XXX is this true? */ debug3("channel %d: will not send data after close", c->self); continue; } /* Get the amount of buffered data for this channel. */ if ((c->istate == CHAN_INPUT_OPEN || c->istate == CHAN_INPUT_WAIT_DRAIN) && (len = buffer_len(&c->input)) > 0) { if (c->datagram) { if (len > 0) { u_char *data; u_int dlen; data = buffer_get_string(&c->input, &dlen); if (dlen > c->remote_window || dlen > c->remote_maxpacket) { debug("channel %d: datagram " "too big for channel", c->self); free(data); continue; } packet_start(SSH2_MSG_CHANNEL_DATA); packet_put_int(c->remote_id); packet_put_string(data, dlen); packet_send(); c->remote_window -= dlen; free(data); } continue; } /* * Send some data for the other side over the secure * connection. */ if (compat20) { if (len > c->remote_window) len = c->remote_window; if (len > c->remote_maxpacket) len = c->remote_maxpacket; } else { if (packet_is_interactive()) { if (len > 1024) len = 512; } else { /* Keep the packets at reasonable size. */ if (len > packet_get_maxsize()/2) len = packet_get_maxsize()/2; } } if (len > 0) { packet_start(compat20 ? SSH2_MSG_CHANNEL_DATA : SSH_MSG_CHANNEL_DATA); packet_put_int(c->remote_id); packet_put_string(buffer_ptr(&c->input), len); packet_send(); buffer_consume(&c->input, len); c->remote_window -= len; } } else if (c->istate == CHAN_INPUT_WAIT_DRAIN) { if (compat13) fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3"); /* * input-buffer is empty and read-socket shutdown: * tell peer, that we will not send more data: send IEOF. * hack for extended data: delay EOF if EFD still in use. */ if (CHANNEL_EFD_INPUT_ACTIVE(c)) debug2("channel %d: ibuf_empty delayed efd %d/(%d)", c->self, c->efd, buffer_len(&c->extended)); else chan_ibuf_empty(c); } /* Send extended data, i.e. stderr */ if (compat20 && !(c->flags & CHAN_EOF_SENT) && c->remote_window > 0 && (len = buffer_len(&c->extended)) > 0 && c->extended_usage == CHAN_EXTENDED_READ) { debug2("channel %d: rwin %u elen %u euse %d", c->self, c->remote_window, buffer_len(&c->extended), c->extended_usage); if (len > c->remote_window) len = c->remote_window; if (len > c->remote_maxpacket) len = c->remote_maxpacket; packet_start(SSH2_MSG_CHANNEL_EXTENDED_DATA); packet_put_int(c->remote_id); packet_put_int(SSH2_EXTENDED_DATA_STDERR); packet_put_string(buffer_ptr(&c->extended), len); packet_send(); buffer_consume(&c->extended, len); c->remote_window -= len; debug2("channel %d: sent ext data %d", c->self, len); } } } /* -- mux proxy support */ /* * When multiplexing channel messages for mux clients we have to deal * with downstream messages from the mux client and upstream messages * from the ssh server: * 1) Handling downstream messages is straightforward and happens * in channel_proxy_downstream(): * - We forward all messages (mostly) unmodified to the server. * - However, in order to route messages from upstream to the correct * downstream client, we have to replace the channel IDs used by the * mux clients with a unique channel ID because the mux clients might * use conflicting channel IDs. * - so we inspect and change both SSH2_MSG_CHANNEL_OPEN and * SSH2_MSG_CHANNEL_OPEN_CONFIRMATION messages, create a local * SSH_CHANNEL_MUX_PROXY channel and replace the mux clients ID * with the newly allocated channel ID. * 2) Upstream messages are received by matching SSH_CHANNEL_MUX_PROXY * channels and procesed by channel_proxy_upstream(). The local channel ID * is then translated back to the original mux client ID. * 3) In both cases we need to keep track of matching SSH2_MSG_CHANNEL_CLOSE * messages so we can clean up SSH_CHANNEL_MUX_PROXY channels. * 4) The SSH_CHANNEL_MUX_PROXY channels also need to closed when the * downstream mux client are removed. * 5) Handling SSH2_MSG_CHANNEL_OPEN messages from the upstream server * requires more work, because they are not addressed to a specific * channel. E.g. client_request_forwarded_tcpip() needs to figure * out whether the request is addressed to the local client or a * specific downstream client based on the listen-address/port. * 6) Agent and X11-Forwarding have a similar problem and are currenly * not supported as the matching session/channel cannot be identified * easily. */ /* * receive packets from downstream mux clients: * channel callback fired on read from mux client, creates * SSH_CHANNEL_MUX_PROXY channels and translates channel IDs * on channel creation. */ int channel_proxy_downstream(Channel *downstream) { Channel *c = NULL; struct ssh *ssh = active_state; struct sshbuf *original = NULL, *modified = NULL; const u_char *cp; char *ctype = NULL, *listen_host = NULL; u_char type; size_t have; int ret = -1, r, idx; u_int id, remote_id, listen_port; /* sshbuf_dump(&downstream->input, stderr); */ if ((r = sshbuf_get_string_direct(&downstream->input, &cp, &have)) != 0) { error("%s: malformed message: %s", __func__, ssh_err(r)); return -1; } if (have < 2) { error("%s: short message", __func__); return -1; } type = cp[1]; /* skip padlen + type */ cp += 2; have -= 2; if (ssh_packet_log_type(type)) debug3("%s: channel %u: down->up: type %u", __func__, downstream->self, type); switch (type) { case SSH2_MSG_CHANNEL_OPEN: if ((original = sshbuf_from(cp, have)) == NULL || (modified = sshbuf_new()) == NULL) { error("%s: alloc", __func__); goto out; } if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0 || (r = sshbuf_get_u32(original, &id)) != 0) { error("%s: parse error %s", __func__, ssh_err(r)); goto out; } c = channel_new("mux proxy", SSH_CHANNEL_MUX_PROXY, -1, -1, -1, 0, 0, 0, ctype, 1); c->mux_ctx = downstream; /* point to mux client */ c->mux_downstream_id = id; /* original downstream id */ if ((r = sshbuf_put_cstring(modified, ctype)) != 0 || (r = sshbuf_put_u32(modified, c->self)) != 0 || (r = sshbuf_putb(modified, original)) != 0) { error("%s: compose error %s", __func__, ssh_err(r)); channel_free(c); goto out; } break; case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: /* * Almost the same as SSH2_MSG_CHANNEL_OPEN, except then we * need to parse 'remote_id' instead of 'ctype'. */ if ((original = sshbuf_from(cp, have)) == NULL || (modified = sshbuf_new()) == NULL) { error("%s: alloc", __func__); goto out; } if ((r = sshbuf_get_u32(original, &remote_id)) != 0 || (r = sshbuf_get_u32(original, &id)) != 0) { error("%s: parse error %s", __func__, ssh_err(r)); goto out; } c = channel_new("mux proxy", SSH_CHANNEL_MUX_PROXY, -1, -1, -1, 0, 0, 0, "mux-down-connect", 1); c->mux_ctx = downstream; /* point to mux client */ c->mux_downstream_id = id; c->remote_id = remote_id; if ((r = sshbuf_put_u32(modified, remote_id)) != 0 || (r = sshbuf_put_u32(modified, c->self)) != 0 || (r = sshbuf_putb(modified, original)) != 0) { error("%s: compose error %s", __func__, ssh_err(r)); channel_free(c); goto out; } break; case SSH2_MSG_GLOBAL_REQUEST: if ((original = sshbuf_from(cp, have)) == NULL) { error("%s: alloc", __func__); goto out; } if ((r = sshbuf_get_cstring(original, &ctype, NULL)) != 0) { error("%s: parse error %s", __func__, ssh_err(r)); goto out; } if (strcmp(ctype, "tcpip-forward") != 0) { error("%s: unsupported request %s", __func__, ctype); goto out; } if ((r = sshbuf_get_u8(original, NULL)) != 0 || (r = sshbuf_get_cstring(original, &listen_host, NULL)) != 0 || (r = sshbuf_get_u32(original, &listen_port)) != 0) { error("%s: parse error %s", __func__, ssh_err(r)); goto out; } if (listen_port > 65535) { error("%s: tcpip-forward for %s: bad port %u", __func__, listen_host, listen_port); goto out; } /* Record that connection to this host/port is permitted. */ permitted_opens = xreallocarray(permitted_opens, num_permitted_opens + 1, sizeof(*permitted_opens)); idx = num_permitted_opens++; permitted_opens[idx].host_to_connect = xstrdup(""); permitted_opens[idx].port_to_connect = -1; permitted_opens[idx].listen_host = listen_host; permitted_opens[idx].listen_port = (int)listen_port; permitted_opens[idx].downstream = downstream; listen_host = NULL; break; case SSH2_MSG_CHANNEL_CLOSE: if (have < 4) break; remote_id = PEEK_U32(cp); if ((c = channel_by_remote_id(remote_id)) != NULL) { if (c->flags & CHAN_CLOSE_RCVD) channel_free(c); else c->flags |= CHAN_CLOSE_SENT; } break; } if (modified) { if ((r = sshpkt_start(ssh, type)) != 0 || (r = sshpkt_putb(ssh, modified)) != 0 || (r = sshpkt_send(ssh)) != 0) { error("%s: send %s", __func__, ssh_err(r)); goto out; } } else { if ((r = sshpkt_start(ssh, type)) != 0 || (r = sshpkt_put(ssh, cp, have)) != 0 || (r = sshpkt_send(ssh)) != 0) { error("%s: send %s", __func__, ssh_err(r)); goto out; } } ret = 0; out: free(ctype); free(listen_host); sshbuf_free(original); sshbuf_free(modified); return ret; } /* * receive packets from upstream server and de-multiplex packets * to correct downstream: * implemented as a helper for channel input handlers, * replaces local (proxy) channel ID with downstream channel ID. */ int channel_proxy_upstream(Channel *c, int type, u_int32_t seq, void *ctxt) { struct ssh *ssh = active_state; struct sshbuf *b = NULL; Channel *downstream; const u_char *cp = NULL; size_t len; int r; /* * When receiving packets from the peer we need to check whether we * need to forward the packets to the mux client. In this case we * restore the orignal channel id and keep track of CLOSE messages, * so we can cleanup the channel. */ if (c == NULL || c->type != SSH_CHANNEL_MUX_PROXY) return 0; if ((downstream = c->mux_ctx) == NULL) return 0; switch (type) { case SSH2_MSG_CHANNEL_CLOSE: case SSH2_MSG_CHANNEL_DATA: case SSH2_MSG_CHANNEL_EOF: case SSH2_MSG_CHANNEL_EXTENDED_DATA: case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: case SSH2_MSG_CHANNEL_OPEN_FAILURE: case SSH2_MSG_CHANNEL_WINDOW_ADJUST: case SSH2_MSG_CHANNEL_SUCCESS: case SSH2_MSG_CHANNEL_FAILURE: case SSH2_MSG_CHANNEL_REQUEST: break; default: debug2("%s: channel %u: unsupported type %u", __func__, c->self, type); return 0; } if ((b = sshbuf_new()) == NULL) { error("%s: alloc reply", __func__); goto out; } /* get remaining payload (after id) */ cp = sshpkt_ptr(ssh, &len); if (cp == NULL) { error("%s: no packet", __func__); goto out; } /* translate id and send to muxclient */ if ((r = sshbuf_put_u8(b, 0)) != 0 || /* padlen */ (r = sshbuf_put_u8(b, type)) != 0 || (r = sshbuf_put_u32(b, c->mux_downstream_id)) != 0 || (r = sshbuf_put(b, cp, len)) != 0 || (r = sshbuf_put_stringb(&downstream->output, b)) != 0) { error("%s: compose for muxclient %s", __func__, ssh_err(r)); goto out; } /* sshbuf_dump(b, stderr); */ if (ssh_packet_log_type(type)) debug3("%s: channel %u: up->down: type %u", __func__, c->self, type); out: /* update state */ switch (type) { case SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: /* record remote_id for SSH2_MSG_CHANNEL_CLOSE */ if (cp && len > 4) c->remote_id = PEEK_U32(cp); break; case SSH2_MSG_CHANNEL_CLOSE: if (c->flags & CHAN_CLOSE_SENT) channel_free(c); else c->flags |= CHAN_CLOSE_RCVD; break; } sshbuf_free(b); return 1; } /* -- protocol input */ /* ARGSUSED */ int channel_input_data(int type, u_int32_t seq, void *ctxt) { int id; const u_char *data; u_int data_len, win_len; Channel *c; /* Get the channel number and verify it. */ id = packet_get_int(); c = channel_lookup(id); if (c == NULL) packet_disconnect("Received data for nonexistent channel %d.", id); if (channel_proxy_upstream(c, type, seq, ctxt)) return 0; /* Ignore any data for non-open channels (might happen on close) */ if (c->type != SSH_CHANNEL_OPEN && c->type != SSH_CHANNEL_X11_OPEN) return 0; /* Get the data. */ data = packet_get_string_ptr(&data_len); win_len = data_len; if (c->datagram) win_len += 4; /* string length header */ /* * Ignore data for protocol > 1.3 if output end is no longer open. * For protocol 2 the sending side is reducing its window as it sends * data, so we must 'fake' consumption of the data in order to ensure * that window updates are sent back. Otherwise the connection might * deadlock. */ if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN) { if (compat20) { c->local_window -= win_len; c->local_consumed += win_len; } return 0; } if (compat20) { if (win_len > c->local_maxpacket) { logit("channel %d: rcvd big packet %d, maxpack %d", c->self, win_len, c->local_maxpacket); } if (win_len > c->local_window) { logit("channel %d: rcvd too much data %d, win %d", c->self, win_len, c->local_window); return 0; } c->local_window -= win_len; } if (c->datagram) buffer_put_string(&c->output, data, data_len); else buffer_append(&c->output, data, data_len); packet_check_eom(); return 0; } /* ARGSUSED */ int channel_input_extended_data(int type, u_int32_t seq, void *ctxt) { int id; char *data; u_int data_len, tcode; Channel *c; /* Get the channel number and verify it. */ id = packet_get_int(); c = channel_lookup(id); if (c == NULL) packet_disconnect("Received extended_data for bad channel %d.", id); if (channel_proxy_upstream(c, type, seq, ctxt)) return 0; if (c->type != SSH_CHANNEL_OPEN) { logit("channel %d: ext data for non open", id); return 0; } if (c->flags & CHAN_EOF_RCVD) { if (datafellows & SSH_BUG_EXTEOF) debug("channel %d: accepting ext data after eof", id); else packet_disconnect("Received extended_data after EOF " "on channel %d.", id); } tcode = packet_get_int(); if (c->efd == -1 || c->extended_usage != CHAN_EXTENDED_WRITE || tcode != SSH2_EXTENDED_DATA_STDERR) { logit("channel %d: bad ext data", c->self); return 0; } data = packet_get_string(&data_len); packet_check_eom(); if (data_len > c->local_window) { logit("channel %d: rcvd too much extended_data %d, win %d", c->self, data_len, c->local_window); free(data); return 0; } debug2("channel %d: rcvd ext data %d", c->self, data_len); c->local_window -= data_len; buffer_append(&c->extended, data, data_len); free(data); return 0; } /* ARGSUSED */ int channel_input_ieof(int type, u_int32_t seq, void *ctxt) { int id; Channel *c; id = packet_get_int(); packet_check_eom(); c = channel_lookup(id); if (c == NULL) packet_disconnect("Received ieof for nonexistent channel %d.", id); if (channel_proxy_upstream(c, type, seq, ctxt)) return 0; chan_rcvd_ieof(c); /* XXX force input close */ if (c->force_drain && c->istate == CHAN_INPUT_OPEN) { debug("channel %d: FORCE input drain", c->self); c->istate = CHAN_INPUT_WAIT_DRAIN; if (buffer_len(&c->input) == 0) chan_ibuf_empty(c); } return 0; } /* ARGSUSED */ int channel_input_close(int type, u_int32_t seq, void *ctxt) { int id; Channel *c; id = packet_get_int(); packet_check_eom(); c = channel_lookup(id); if (c == NULL) packet_disconnect("Received close for nonexistent channel %d.", id); if (channel_proxy_upstream(c, type, seq, ctxt)) return 0; /* * Send a confirmation that we have closed the channel and no more * data is coming for it. */ packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION); packet_put_int(c->remote_id); packet_send(); /* * If the channel is in closed state, we have sent a close request, * and the other side will eventually respond with a confirmation. * Thus, we cannot free the channel here, because then there would be * no-one to receive the confirmation. The channel gets freed when * the confirmation arrives. */ if (c->type != SSH_CHANNEL_CLOSED) { /* * Not a closed channel - mark it as draining, which will * cause it to be freed later. */ buffer_clear(&c->input); c->type = SSH_CHANNEL_OUTPUT_DRAINING; } return 0; } /* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */ /* ARGSUSED */ int channel_input_oclose(int type, u_int32_t seq, void *ctxt) { int id = packet_get_int(); Channel *c = channel_lookup(id); if (c == NULL) packet_disconnect("Received oclose for nonexistent channel %d.", id); if (channel_proxy_upstream(c, type, seq, ctxt)) return 0; packet_check_eom(); chan_rcvd_oclose(c); return 0; } /* ARGSUSED */ int channel_input_close_confirmation(int type, u_int32_t seq, void *ctxt) { int id = packet_get_int(); Channel *c = channel_lookup(id); if (c == NULL) packet_disconnect("Received close confirmation for " "out-of-range channel %d.", id); if (channel_proxy_upstream(c, type, seq, ctxt)) return 0; packet_check_eom(); if (c->type != SSH_CHANNEL_CLOSED && c->type != SSH_CHANNEL_ABANDONED) packet_disconnect("Received close confirmation for " "non-closed channel %d (type %d).", id, c->type); channel_free(c); return 0; } /* ARGSUSED */ int channel_input_open_confirmation(int type, u_int32_t seq, void *ctxt) { int id, remote_id; Channel *c; id = packet_get_int(); c = channel_lookup(id); if (c==NULL) packet_disconnect("Received open confirmation for " "unknown channel %d.", id); if (channel_proxy_upstream(c, type, seq, ctxt)) return 0; if (c->type != SSH_CHANNEL_OPENING) packet_disconnect("Received open confirmation for " "non-opening channel %d.", id); remote_id = packet_get_int(); /* Record the remote channel number and mark that the channel is now open. */ c->remote_id = remote_id; c->type = SSH_CHANNEL_OPEN; if (compat20) { c->remote_window = packet_get_int(); c->remote_maxpacket = packet_get_int(); if (c->open_confirm) { debug2("callback start"); c->open_confirm(c->self, 1, c->open_confirm_ctx); debug2("callback done"); } debug2("channel %d: open confirm rwindow %u rmax %u", c->self, c->remote_window, c->remote_maxpacket); } packet_check_eom(); return 0; } static char * reason2txt(int reason) { switch (reason) { case SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED: return "administratively prohibited"; case SSH2_OPEN_CONNECT_FAILED: return "connect failed"; case SSH2_OPEN_UNKNOWN_CHANNEL_TYPE: return "unknown channel type"; case SSH2_OPEN_RESOURCE_SHORTAGE: return "resource shortage"; } return "unknown reason"; } /* ARGSUSED */ int channel_input_open_failure(int type, u_int32_t seq, void *ctxt) { int id, reason; char *msg = NULL, *lang = NULL; Channel *c; id = packet_get_int(); c = channel_lookup(id); if (c==NULL) packet_disconnect("Received open failure for " "unknown channel %d.", id); if (channel_proxy_upstream(c, type, seq, ctxt)) return 0; if (c->type != SSH_CHANNEL_OPENING) packet_disconnect("Received open failure for " "non-opening channel %d.", id); if (compat20) { reason = packet_get_int(); if (!(datafellows & SSH_BUG_OPENFAILURE)) { msg = packet_get_string(NULL); lang = packet_get_string(NULL); } logit("channel %d: open failed: %s%s%s", id, reason2txt(reason), msg ? ": ": "", msg ? msg : ""); free(msg); free(lang); if (c->open_confirm) { debug2("callback start"); c->open_confirm(c->self, 0, c->open_confirm_ctx); debug2("callback done"); } } packet_check_eom(); /* Schedule the channel for cleanup/deletion. */ chan_mark_dead(c); return 0; } /* ARGSUSED */ int channel_input_window_adjust(int type, u_int32_t seq, void *ctxt) { Channel *c; int id; u_int adjust, tmp; if (!compat20) return 0; /* Get the channel number and verify it. */ id = packet_get_int(); c = channel_lookup(id); if (c == NULL) { logit("Received window adjust for non-open channel %d.", id); return 0; } if (channel_proxy_upstream(c, type, seq, ctxt)) return 0; adjust = packet_get_int(); packet_check_eom(); debug2("channel %d: rcvd adjust %u", id, adjust); if ((tmp = c->remote_window + adjust) < c->remote_window) fatal("channel %d: adjust %u overflows remote window %u", id, adjust, c->remote_window); c->remote_window = tmp; return 0; } /* ARGSUSED */ int channel_input_port_open(int type, u_int32_t seq, void *ctxt) { Channel *c = NULL; u_short host_port; char *host, *originator_string; int remote_id; remote_id = packet_get_int(); host = packet_get_string(NULL); host_port = packet_get_int(); if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { originator_string = packet_get_string(NULL); } else { originator_string = xstrdup("unknown (remote did not supply name)"); } packet_check_eom(); c = channel_connect_to_port(host, host_port, - "connected socket", originator_string); + "connected socket", originator_string, NULL, NULL); free(originator_string); free(host); if (c == NULL) { packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(remote_id); packet_send(); } else c->remote_id = remote_id; return 0; } /* ARGSUSED */ int channel_input_status_confirm(int type, u_int32_t seq, void *ctxt) { Channel *c; struct channel_confirm *cc; int id; /* Reset keepalive timeout */ packet_set_alive_timeouts(0); id = packet_get_int(); debug2("channel_input_status_confirm: type %d id %d", type, id); if ((c = channel_lookup(id)) == NULL) { logit("channel_input_status_confirm: %d: unknown", id); return 0; } if (channel_proxy_upstream(c, type, seq, ctxt)) return 0; packet_check_eom(); if ((cc = TAILQ_FIRST(&c->status_confirms)) == NULL) return 0; cc->cb(type, c, cc->ctx); TAILQ_REMOVE(&c->status_confirms, cc, entry); explicit_bzero(cc, sizeof(*cc)); free(cc); return 0; } /* -- tcp forwarding */ void channel_set_af(int af) { IPv4or6 = af; } /* * Determine whether or not a port forward listens to loopback, the * specified address or wildcard. On the client, a specified bind * address will always override gateway_ports. On the server, a * gateway_ports of 1 (``yes'') will override the client's specification * and force a wildcard bind, whereas a value of 2 (``clientspecified'') * will bind to whatever address the client asked for. * * Special-case listen_addrs are: * * "0.0.0.0" -> wildcard v4/v6 if SSH_OLD_FORWARD_ADDR * "" (empty string), "*" -> wildcard v4/v6 * "localhost" -> loopback v4/v6 * "127.0.0.1" / "::1" -> accepted even if gateway_ports isn't set */ static const char * channel_fwd_bind_addr(const char *listen_addr, int *wildcardp, int is_client, struct ForwardOptions *fwd_opts) { const char *addr = NULL; int wildcard = 0; if (listen_addr == NULL) { /* No address specified: default to gateway_ports setting */ if (fwd_opts->gateway_ports) wildcard = 1; } else if (fwd_opts->gateway_ports || is_client) { if (((datafellows & SSH_OLD_FORWARD_ADDR) && strcmp(listen_addr, "0.0.0.0") == 0 && is_client == 0) || *listen_addr == '\0' || strcmp(listen_addr, "*") == 0 || (!is_client && fwd_opts->gateway_ports == 1)) { wildcard = 1; /* * Notify client if they requested a specific listen * address and it was overridden. */ if (*listen_addr != '\0' && strcmp(listen_addr, "0.0.0.0") != 0 && strcmp(listen_addr, "*") != 0) { packet_send_debug("Forwarding listen address " "\"%s\" overridden by server " "GatewayPorts", listen_addr); } } else if (strcmp(listen_addr, "localhost") != 0 || strcmp(listen_addr, "127.0.0.1") == 0 || strcmp(listen_addr, "::1") == 0) { /* Accept localhost address when GatewayPorts=yes */ addr = listen_addr; } } else if (strcmp(listen_addr, "127.0.0.1") == 0 || strcmp(listen_addr, "::1") == 0) { /* * If a specific IPv4/IPv6 localhost address has been * requested then accept it even if gateway_ports is in * effect. This allows the client to prefer IPv4 or IPv6. */ addr = listen_addr; } if (wildcardp != NULL) *wildcardp = wildcard; return addr; } static int channel_setup_fwd_listener_tcpip(int type, struct Forward *fwd, int *allocated_listen_port, struct ForwardOptions *fwd_opts) { Channel *c; int sock, r, success = 0, wildcard = 0, is_client; struct addrinfo hints, *ai, *aitop; const char *host, *addr; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; in_port_t *lport_p; is_client = (type == SSH_CHANNEL_PORT_LISTENER); if (is_client && fwd->connect_path != NULL) { host = fwd->connect_path; } else { host = (type == SSH_CHANNEL_RPORT_LISTENER) ? fwd->listen_host : fwd->connect_host; if (host == NULL) { error("No forward host name."); return 0; } if (strlen(host) >= NI_MAXHOST) { error("Forward host name too long."); return 0; } } /* Determine the bind address, cf. channel_fwd_bind_addr() comment */ addr = channel_fwd_bind_addr(fwd->listen_host, &wildcard, is_client, fwd_opts); debug3("%s: type %d wildcard %d addr %s", __func__, type, wildcard, (addr == NULL) ? "NULL" : addr); /* * getaddrinfo returns a loopback address if the hostname is * set to NULL and hints.ai_flags is not AI_PASSIVE */ memset(&hints, 0, sizeof(hints)); hints.ai_family = IPv4or6; hints.ai_flags = wildcard ? AI_PASSIVE : 0; hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%d", fwd->listen_port); if ((r = getaddrinfo(addr, strport, &hints, &aitop)) != 0) { if (addr == NULL) { /* This really shouldn't happen */ packet_disconnect("getaddrinfo: fatal error: %s", ssh_gai_strerror(r)); } else { error("%s: getaddrinfo(%.64s): %s", __func__, addr, ssh_gai_strerror(r)); } return 0; } if (allocated_listen_port != NULL) *allocated_listen_port = 0; for (ai = aitop; ai; ai = ai->ai_next) { switch (ai->ai_family) { case AF_INET: lport_p = &((struct sockaddr_in *)ai->ai_addr)-> sin_port; break; case AF_INET6: lport_p = &((struct sockaddr_in6 *)ai->ai_addr)-> sin6_port; break; default: continue; } /* * If allocating a port for -R forwards, then use the * same port for all address families. */ if (type == SSH_CHANNEL_RPORT_LISTENER && fwd->listen_port == 0 && allocated_listen_port != NULL && *allocated_listen_port > 0) *lport_p = htons(*allocated_listen_port); if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { error("%s: getnameinfo failed", __func__); continue; } /* Create a port to listen for the host. */ sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sock < 0) { /* this is no error since kernel may not support ipv6 */ verbose("socket: %.100s", strerror(errno)); continue; } channel_set_reuseaddr(sock); if (ai->ai_family == AF_INET6) sock_set_v6only(sock); debug("Local forwarding listening on %s port %s.", ntop, strport); /* Bind the socket to the address. */ if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { /* address can be in use ipv6 address is already bound */ if (!ai->ai_next) error("bind: %.100s", strerror(errno)); else verbose("bind: %.100s", strerror(errno)); close(sock); continue; } /* Start listening for connections on the socket. */ if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { error("listen: %.100s", strerror(errno)); close(sock); continue; } /* * fwd->listen_port == 0 requests a dynamically allocated port - * record what we got. */ if (type == SSH_CHANNEL_RPORT_LISTENER && fwd->listen_port == 0 && allocated_listen_port != NULL && *allocated_listen_port == 0) { *allocated_listen_port = get_local_port(sock); debug("Allocated listen port %d", *allocated_listen_port); } /* Allocate a channel number for the socket. */ c = channel_new("port listener", type, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "port listener", 1); c->path = xstrdup(host); c->host_port = fwd->connect_port; c->listening_addr = addr == NULL ? NULL : xstrdup(addr); if (fwd->listen_port == 0 && allocated_listen_port != NULL && !(datafellows & SSH_BUG_DYNAMIC_RPORT)) c->listening_port = *allocated_listen_port; else c->listening_port = fwd->listen_port; success = 1; } if (success == 0) error("%s: cannot listen to port: %d", __func__, fwd->listen_port); freeaddrinfo(aitop); return success; } static int channel_setup_fwd_listener_streamlocal(int type, struct Forward *fwd, struct ForwardOptions *fwd_opts) { struct sockaddr_un sunaddr; const char *path; Channel *c; int port, sock; mode_t omask; switch (type) { case SSH_CHANNEL_UNIX_LISTENER: if (fwd->connect_path != NULL) { if (strlen(fwd->connect_path) > sizeof(sunaddr.sun_path)) { error("Local connecting path too long: %s", fwd->connect_path); return 0; } path = fwd->connect_path; port = PORT_STREAMLOCAL; } else { if (fwd->connect_host == NULL) { error("No forward host name."); return 0; } if (strlen(fwd->connect_host) >= NI_MAXHOST) { error("Forward host name too long."); return 0; } path = fwd->connect_host; port = fwd->connect_port; } break; case SSH_CHANNEL_RUNIX_LISTENER: path = fwd->listen_path; port = PORT_STREAMLOCAL; break; default: error("%s: unexpected channel type %d", __func__, type); return 0; } if (fwd->listen_path == NULL) { error("No forward path name."); return 0; } if (strlen(fwd->listen_path) > sizeof(sunaddr.sun_path)) { error("Local listening path too long: %s", fwd->listen_path); return 0; } debug3("%s: type %d path %s", __func__, type, fwd->listen_path); /* Start a Unix domain listener. */ omask = umask(fwd_opts->streamlocal_bind_mask); sock = unix_listener(fwd->listen_path, SSH_LISTEN_BACKLOG, fwd_opts->streamlocal_bind_unlink); umask(omask); if (sock < 0) return 0; debug("Local forwarding listening on path %s.", fwd->listen_path); /* Allocate a channel number for the socket. */ c = channel_new("unix listener", type, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "unix listener", 1); c->path = xstrdup(path); c->host_port = port; c->listening_port = PORT_STREAMLOCAL; c->listening_addr = xstrdup(fwd->listen_path); return 1; } static int channel_cancel_rport_listener_tcpip(const char *host, u_short port) { u_int i; int found = 0; for (i = 0; i < channels_alloc; i++) { Channel *c = channels[i]; if (c == NULL || c->type != SSH_CHANNEL_RPORT_LISTENER) continue; if (strcmp(c->path, host) == 0 && c->listening_port == port) { debug2("%s: close channel %d", __func__, i); channel_free(c); found = 1; } } return (found); } static int channel_cancel_rport_listener_streamlocal(const char *path) { u_int i; int found = 0; for (i = 0; i < channels_alloc; i++) { Channel *c = channels[i]; if (c == NULL || c->type != SSH_CHANNEL_RUNIX_LISTENER) continue; if (c->path == NULL) continue; if (strcmp(c->path, path) == 0) { debug2("%s: close channel %d", __func__, i); channel_free(c); found = 1; } } return (found); } int channel_cancel_rport_listener(struct Forward *fwd) { if (fwd->listen_path != NULL) return channel_cancel_rport_listener_streamlocal(fwd->listen_path); else return channel_cancel_rport_listener_tcpip(fwd->listen_host, fwd->listen_port); } static int channel_cancel_lport_listener_tcpip(const char *lhost, u_short lport, int cport, struct ForwardOptions *fwd_opts) { u_int i; int found = 0; const char *addr = channel_fwd_bind_addr(lhost, NULL, 1, fwd_opts); for (i = 0; i < channels_alloc; i++) { Channel *c = channels[i]; if (c == NULL || c->type != SSH_CHANNEL_PORT_LISTENER) continue; if (c->listening_port != lport) continue; if (cport == CHANNEL_CANCEL_PORT_STATIC) { /* skip dynamic forwardings */ if (c->host_port == 0) continue; } else { if (c->host_port != cport) continue; } if ((c->listening_addr == NULL && addr != NULL) || (c->listening_addr != NULL && addr == NULL)) continue; if (addr == NULL || strcmp(c->listening_addr, addr) == 0) { debug2("%s: close channel %d", __func__, i); channel_free(c); found = 1; } } return (found); } static int channel_cancel_lport_listener_streamlocal(const char *path) { u_int i; int found = 0; if (path == NULL) { error("%s: no path specified.", __func__); return 0; } for (i = 0; i < channels_alloc; i++) { Channel *c = channels[i]; if (c == NULL || c->type != SSH_CHANNEL_UNIX_LISTENER) continue; if (c->listening_addr == NULL) continue; if (strcmp(c->listening_addr, path) == 0) { debug2("%s: close channel %d", __func__, i); channel_free(c); found = 1; } } return (found); } int channel_cancel_lport_listener(struct Forward *fwd, int cport, struct ForwardOptions *fwd_opts) { if (fwd->listen_path != NULL) return channel_cancel_lport_listener_streamlocal(fwd->listen_path); else return channel_cancel_lport_listener_tcpip(fwd->listen_host, fwd->listen_port, cport, fwd_opts); } /* protocol local port fwd, used by ssh (and sshd in v1) */ int channel_setup_local_fwd_listener(struct Forward *fwd, struct ForwardOptions *fwd_opts) { if (fwd->listen_path != NULL) { return channel_setup_fwd_listener_streamlocal( SSH_CHANNEL_UNIX_LISTENER, fwd, fwd_opts); } else { return channel_setup_fwd_listener_tcpip(SSH_CHANNEL_PORT_LISTENER, fwd, NULL, fwd_opts); } } /* protocol v2 remote port fwd, used by sshd */ int channel_setup_remote_fwd_listener(struct Forward *fwd, int *allocated_listen_port, struct ForwardOptions *fwd_opts) { if (fwd->listen_path != NULL) { return channel_setup_fwd_listener_streamlocal( SSH_CHANNEL_RUNIX_LISTENER, fwd, fwd_opts); } else { return channel_setup_fwd_listener_tcpip( SSH_CHANNEL_RPORT_LISTENER, fwd, allocated_listen_port, fwd_opts); } } /* * Translate the requested rfwd listen host to something usable for * this server. */ static const char * channel_rfwd_bind_host(const char *listen_host) { if (listen_host == NULL) { if (datafellows & SSH_BUG_RFWD_ADDR) return "127.0.0.1"; else return "localhost"; } else if (*listen_host == '\0' || strcmp(listen_host, "*") == 0) { if (datafellows & SSH_BUG_RFWD_ADDR) return "0.0.0.0"; else return ""; } else return listen_host; } /* * Initiate forwarding of connections to port "port" on remote host through * the secure channel to host:port from local side. * Returns handle (index) for updating the dynamic listen port with * channel_update_permitted_opens(). */ int channel_request_remote_forwarding(struct Forward *fwd) { int type, success = 0, idx = -1; /* Send the forward request to the remote side. */ if (compat20) { packet_start(SSH2_MSG_GLOBAL_REQUEST); if (fwd->listen_path != NULL) { packet_put_cstring("streamlocal-forward@openssh.com"); packet_put_char(1); /* boolean: want reply */ packet_put_cstring(fwd->listen_path); } else { packet_put_cstring("tcpip-forward"); packet_put_char(1); /* boolean: want reply */ packet_put_cstring(channel_rfwd_bind_host(fwd->listen_host)); packet_put_int(fwd->listen_port); } packet_send(); packet_write_wait(); /* Assume that server accepts the request */ success = 1; } else if (fwd->listen_path == NULL) { packet_start(SSH_CMSG_PORT_FORWARD_REQUEST); packet_put_int(fwd->listen_port); packet_put_cstring(fwd->connect_host); packet_put_int(fwd->connect_port); packet_send(); packet_write_wait(); /* Wait for response from the remote side. */ type = packet_read(); switch (type) { case SSH_SMSG_SUCCESS: success = 1; break; case SSH_SMSG_FAILURE: break; default: /* Unknown packet */ packet_disconnect("Protocol error for port forward request:" "received packet type %d.", type); } } else { logit("Warning: Server does not support remote stream local forwarding."); } if (success) { /* Record that connection to this host/port is permitted. */ permitted_opens = xreallocarray(permitted_opens, num_permitted_opens + 1, sizeof(*permitted_opens)); idx = num_permitted_opens++; if (fwd->connect_path != NULL) { permitted_opens[idx].host_to_connect = xstrdup(fwd->connect_path); permitted_opens[idx].port_to_connect = PORT_STREAMLOCAL; } else { permitted_opens[idx].host_to_connect = xstrdup(fwd->connect_host); permitted_opens[idx].port_to_connect = fwd->connect_port; } if (fwd->listen_path != NULL) { permitted_opens[idx].listen_host = NULL; permitted_opens[idx].listen_path = xstrdup(fwd->listen_path); permitted_opens[idx].listen_port = PORT_STREAMLOCAL; } else { permitted_opens[idx].listen_host = fwd->listen_host ? xstrdup(fwd->listen_host) : NULL; permitted_opens[idx].listen_path = NULL; permitted_opens[idx].listen_port = fwd->listen_port; } permitted_opens[idx].downstream = NULL; } return (idx); } static int open_match(ForwardPermission *allowed_open, const char *requestedhost, int requestedport) { if (allowed_open->host_to_connect == NULL) return 0; if (allowed_open->port_to_connect != FWD_PERMIT_ANY_PORT && allowed_open->port_to_connect != requestedport) return 0; if (strcmp(allowed_open->host_to_connect, FWD_PERMIT_ANY_HOST) != 0 && strcmp(allowed_open->host_to_connect, requestedhost) != 0) return 0; return 1; } /* * Note that in the listen host/port case * we don't support FWD_PERMIT_ANY_PORT and * need to translate between the configured-host (listen_host) * and what we've sent to the remote server (channel_rfwd_bind_host) */ static int open_listen_match_tcpip(ForwardPermission *allowed_open, const char *requestedhost, u_short requestedport, int translate) { const char *allowed_host; if (allowed_open->host_to_connect == NULL) return 0; if (allowed_open->listen_port != requestedport) return 0; if (!translate && allowed_open->listen_host == NULL && requestedhost == NULL) return 1; allowed_host = translate ? channel_rfwd_bind_host(allowed_open->listen_host) : allowed_open->listen_host; if (allowed_host == NULL || strcmp(allowed_host, requestedhost) != 0) return 0; return 1; } static int open_listen_match_streamlocal(ForwardPermission *allowed_open, const char *requestedpath) { if (allowed_open->host_to_connect == NULL) return 0; if (allowed_open->listen_port != PORT_STREAMLOCAL) return 0; if (allowed_open->listen_path == NULL || strcmp(allowed_open->listen_path, requestedpath) != 0) return 0; return 1; } /* * Request cancellation of remote forwarding of connection host:port from * local side. */ static int channel_request_rforward_cancel_tcpip(const char *host, u_short port) { int i; if (!compat20) return -1; for (i = 0; i < num_permitted_opens; i++) { if (open_listen_match_tcpip(&permitted_opens[i], host, port, 0)) break; } if (i >= num_permitted_opens) { debug("%s: requested forward not found", __func__); return -1; } packet_start(SSH2_MSG_GLOBAL_REQUEST); packet_put_cstring("cancel-tcpip-forward"); packet_put_char(0); packet_put_cstring(channel_rfwd_bind_host(host)); packet_put_int(port); packet_send(); permitted_opens[i].listen_port = 0; permitted_opens[i].port_to_connect = 0; free(permitted_opens[i].host_to_connect); permitted_opens[i].host_to_connect = NULL; free(permitted_opens[i].listen_host); permitted_opens[i].listen_host = NULL; permitted_opens[i].listen_path = NULL; permitted_opens[i].downstream = NULL; return 0; } /* * Request cancellation of remote forwarding of Unix domain socket * path from local side. */ static int channel_request_rforward_cancel_streamlocal(const char *path) { int i; if (!compat20) return -1; for (i = 0; i < num_permitted_opens; i++) { if (open_listen_match_streamlocal(&permitted_opens[i], path)) break; } if (i >= num_permitted_opens) { debug("%s: requested forward not found", __func__); return -1; } packet_start(SSH2_MSG_GLOBAL_REQUEST); packet_put_cstring("cancel-streamlocal-forward@openssh.com"); packet_put_char(0); packet_put_cstring(path); packet_send(); permitted_opens[i].listen_port = 0; permitted_opens[i].port_to_connect = 0; free(permitted_opens[i].host_to_connect); permitted_opens[i].host_to_connect = NULL; permitted_opens[i].listen_host = NULL; free(permitted_opens[i].listen_path); permitted_opens[i].listen_path = NULL; permitted_opens[i].downstream = NULL; return 0; } /* * Request cancellation of remote forwarding of a connection from local side. */ int channel_request_rforward_cancel(struct Forward *fwd) { if (fwd->listen_path != NULL) { return (channel_request_rforward_cancel_streamlocal( fwd->listen_path)); } else { return (channel_request_rforward_cancel_tcpip(fwd->listen_host, fwd->listen_port ? fwd->listen_port : fwd->allocated_port)); } } /* * Permits opening to any host/port if permitted_opens[] is empty. This is * usually called by the server, because the user could connect to any port * anyway, and the server has no way to know but to trust the client anyway. */ void channel_permit_all_opens(void) { if (num_permitted_opens == 0) all_opens_permitted = 1; } void channel_add_permitted_opens(char *host, int port) { debug("allow port forwarding to host %s port %d", host, port); permitted_opens = xreallocarray(permitted_opens, num_permitted_opens + 1, sizeof(*permitted_opens)); permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host); permitted_opens[num_permitted_opens].port_to_connect = port; permitted_opens[num_permitted_opens].listen_host = NULL; permitted_opens[num_permitted_opens].listen_path = NULL; permitted_opens[num_permitted_opens].listen_port = 0; permitted_opens[num_permitted_opens].downstream = NULL; num_permitted_opens++; all_opens_permitted = 0; } /* * Update the listen port for a dynamic remote forward, after * the actual 'newport' has been allocated. If 'newport' < 0 is * passed then they entry will be invalidated. */ void channel_update_permitted_opens(int idx, int newport) { if (idx < 0 || idx >= num_permitted_opens) { debug("channel_update_permitted_opens: index out of range:" " %d num_permitted_opens %d", idx, num_permitted_opens); return; } debug("%s allowed port %d for forwarding to host %s port %d", newport > 0 ? "Updating" : "Removing", newport, permitted_opens[idx].host_to_connect, permitted_opens[idx].port_to_connect); if (newport >= 0) { permitted_opens[idx].listen_port = (datafellows & SSH_BUG_DYNAMIC_RPORT) ? 0 : newport; } else { permitted_opens[idx].listen_port = 0; permitted_opens[idx].port_to_connect = 0; free(permitted_opens[idx].host_to_connect); permitted_opens[idx].host_to_connect = NULL; free(permitted_opens[idx].listen_host); permitted_opens[idx].listen_host = NULL; free(permitted_opens[idx].listen_path); permitted_opens[idx].listen_path = NULL; } } int channel_add_adm_permitted_opens(char *host, int port) { debug("config allows port forwarding to host %s port %d", host, port); permitted_adm_opens = xreallocarray(permitted_adm_opens, num_adm_permitted_opens + 1, sizeof(*permitted_adm_opens)); permitted_adm_opens[num_adm_permitted_opens].host_to_connect = xstrdup(host); permitted_adm_opens[num_adm_permitted_opens].port_to_connect = port; permitted_adm_opens[num_adm_permitted_opens].listen_host = NULL; permitted_adm_opens[num_adm_permitted_opens].listen_path = NULL; permitted_adm_opens[num_adm_permitted_opens].listen_port = 0; return ++num_adm_permitted_opens; } void channel_disable_adm_local_opens(void) { channel_clear_adm_permitted_opens(); permitted_adm_opens = xcalloc(sizeof(*permitted_adm_opens), 1); permitted_adm_opens[num_adm_permitted_opens].host_to_connect = NULL; num_adm_permitted_opens = 1; } void channel_clear_permitted_opens(void) { int i; for (i = 0; i < num_permitted_opens; i++) { free(permitted_opens[i].host_to_connect); free(permitted_opens[i].listen_host); free(permitted_opens[i].listen_path); } free(permitted_opens); permitted_opens = NULL; num_permitted_opens = 0; } void channel_clear_adm_permitted_opens(void) { int i; for (i = 0; i < num_adm_permitted_opens; i++) { free(permitted_adm_opens[i].host_to_connect); free(permitted_adm_opens[i].listen_host); free(permitted_adm_opens[i].listen_path); } free(permitted_adm_opens); permitted_adm_opens = NULL; num_adm_permitted_opens = 0; } void channel_print_adm_permitted_opens(void) { int i; printf("permitopen"); if (num_adm_permitted_opens == 0) { printf(" any\n"); return; } for (i = 0; i < num_adm_permitted_opens; i++) if (permitted_adm_opens[i].host_to_connect == NULL) printf(" none"); else printf(" %s:%d", permitted_adm_opens[i].host_to_connect, permitted_adm_opens[i].port_to_connect); printf("\n"); } /* returns port number, FWD_PERMIT_ANY_PORT or -1 on error */ int permitopen_port(const char *p) { int port; if (strcmp(p, "*") == 0) return FWD_PERMIT_ANY_PORT; if ((port = a2port(p)) > 0) return port; return -1; } /* Try to start non-blocking connect to next host in cctx list */ static int connect_next(struct channel_connect *cctx) { int sock, saved_errno; struct sockaddr_un *sunaddr; char ntop[NI_MAXHOST], strport[MAXIMUM(NI_MAXSERV,sizeof(sunaddr->sun_path))]; for (; cctx->ai; cctx->ai = cctx->ai->ai_next) { switch (cctx->ai->ai_family) { case AF_UNIX: /* unix:pathname instead of host:port */ sunaddr = (struct sockaddr_un *)cctx->ai->ai_addr; strlcpy(ntop, "unix", sizeof(ntop)); strlcpy(strport, sunaddr->sun_path, sizeof(strport)); break; case AF_INET: case AF_INET6: if (getnameinfo(cctx->ai->ai_addr, cctx->ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { error("connect_next: getnameinfo failed"); continue; } break; default: continue; } if ((sock = socket(cctx->ai->ai_family, cctx->ai->ai_socktype, cctx->ai->ai_protocol)) == -1) { if (cctx->ai->ai_next == NULL) error("socket: %.100s", strerror(errno)); else verbose("socket: %.100s", strerror(errno)); continue; } if (set_nonblock(sock) == -1) fatal("%s: set_nonblock(%d)", __func__, sock); if (connect(sock, cctx->ai->ai_addr, cctx->ai->ai_addrlen) == -1 && errno != EINPROGRESS) { debug("connect_next: host %.100s ([%.100s]:%s): " "%.100s", cctx->host, ntop, strport, strerror(errno)); saved_errno = errno; close(sock); errno = saved_errno; continue; /* fail -- try next */ } if (cctx->ai->ai_family != AF_UNIX) set_nodelay(sock); debug("connect_next: host %.100s ([%.100s]:%s) " "in progress, fd=%d", cctx->host, ntop, strport, sock); cctx->ai = cctx->ai->ai_next; return sock; } return -1; } static void channel_connect_ctx_free(struct channel_connect *cctx) { free(cctx->host); if (cctx->aitop) { if (cctx->aitop->ai_family == AF_UNIX) free(cctx->aitop); else freeaddrinfo(cctx->aitop); } memset(cctx, 0, sizeof(*cctx)); } -/* Return CONNECTING channel to remote host:port or local socket path */ +/* + * Return CONNECTING channel to remote host:port or local socket path, + * passing back the failure reason if appropriate. + */ static Channel * -connect_to(const char *name, int port, char *ctype, char *rname) +connect_to_reason(const char *name, int port, char *ctype, char *rname, + int *reason, const char **errmsg) { struct addrinfo hints; int gaierr; int sock = -1; char strport[NI_MAXSERV]; struct channel_connect cctx; Channel *c; memset(&cctx, 0, sizeof(cctx)); if (port == PORT_STREAMLOCAL) { struct sockaddr_un *sunaddr; struct addrinfo *ai; if (strlen(name) > sizeof(sunaddr->sun_path)) { error("%.100s: %.100s", name, strerror(ENAMETOOLONG)); return (NULL); } /* * Fake up a struct addrinfo for AF_UNIX connections. * channel_connect_ctx_free() must check ai_family * and use free() not freeaddirinfo() for AF_UNIX. */ ai = xmalloc(sizeof(*ai) + sizeof(*sunaddr)); memset(ai, 0, sizeof(*ai) + sizeof(*sunaddr)); ai->ai_addr = (struct sockaddr *)(ai + 1); ai->ai_addrlen = sizeof(*sunaddr); ai->ai_family = AF_UNIX; ai->ai_socktype = SOCK_STREAM; ai->ai_protocol = PF_UNSPEC; sunaddr = (struct sockaddr_un *)ai->ai_addr; sunaddr->sun_family = AF_UNIX; strlcpy(sunaddr->sun_path, name, sizeof(sunaddr->sun_path)); cctx.aitop = ai; } else { memset(&hints, 0, sizeof(hints)); hints.ai_family = IPv4or6; hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%d", port); - if ((gaierr = getaddrinfo(name, strport, &hints, &cctx.aitop)) != 0) { + if ((gaierr = getaddrinfo(name, strport, &hints, &cctx.aitop)) + != 0) { + if (errmsg != NULL) + *errmsg = ssh_gai_strerror(gaierr); + if (reason != NULL) + *reason = SSH2_OPEN_CONNECT_FAILED; error("connect_to %.100s: unknown host (%s)", name, ssh_gai_strerror(gaierr)); return NULL; } } cctx.host = xstrdup(name); cctx.port = port; cctx.ai = cctx.aitop; if ((sock = connect_next(&cctx)) == -1) { error("connect to %.100s port %d failed: %s", name, port, strerror(errno)); channel_connect_ctx_free(&cctx); return NULL; } c = channel_new(ctype, SSH_CHANNEL_CONNECTING, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, rname, 1); c->connect_ctx = cctx; return c; } +/* Return CONNECTING channel to remote host:port or local socket path */ +static Channel * +connect_to(const char *name, int port, char *ctype, char *rname) +{ + return connect_to_reason(name, port, ctype, rname, NULL, NULL); +} + /* * returns either the newly connected channel or the downstream channel * that needs to deal with this connection. */ Channel * channel_connect_by_listen_address(const char *listen_host, u_short listen_port, char *ctype, char *rname) { int i; for (i = 0; i < num_permitted_opens; i++) { if (open_listen_match_tcpip(&permitted_opens[i], listen_host, listen_port, 1)) { if (permitted_opens[i].downstream) return permitted_opens[i].downstream; return connect_to( permitted_opens[i].host_to_connect, permitted_opens[i].port_to_connect, ctype, rname); } } error("WARNING: Server requests forwarding for unknown listen_port %d", listen_port); return NULL; } Channel * channel_connect_by_listen_path(const char *path, char *ctype, char *rname) { int i; for (i = 0; i < num_permitted_opens; i++) { if (open_listen_match_streamlocal(&permitted_opens[i], path)) { return connect_to( permitted_opens[i].host_to_connect, permitted_opens[i].port_to_connect, ctype, rname); } } error("WARNING: Server requests forwarding for unknown path %.100s", path); return NULL; } /* Check if connecting to that port is permitted and connect. */ Channel * -channel_connect_to_port(const char *host, u_short port, char *ctype, char *rname) +channel_connect_to_port(const char *host, u_short port, char *ctype, + char *rname, int *reason, const char **errmsg) { int i, permit, permit_adm = 1; permit = all_opens_permitted; if (!permit) { for (i = 0; i < num_permitted_opens; i++) if (open_match(&permitted_opens[i], host, port)) { permit = 1; break; } } if (num_adm_permitted_opens > 0) { permit_adm = 0; for (i = 0; i < num_adm_permitted_opens; i++) if (open_match(&permitted_adm_opens[i], host, port)) { permit_adm = 1; break; } } if (!permit || !permit_adm) { logit("Received request to connect to host %.100s port %d, " "but the request was denied.", host, port); + if (reason != NULL) + *reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED; return NULL; } - return connect_to(host, port, ctype, rname); + return connect_to_reason(host, port, ctype, rname, reason, errmsg); } /* Check if connecting to that path is permitted and connect. */ Channel * channel_connect_to_path(const char *path, char *ctype, char *rname) { int i, permit, permit_adm = 1; permit = all_opens_permitted; if (!permit) { for (i = 0; i < num_permitted_opens; i++) if (open_match(&permitted_opens[i], path, PORT_STREAMLOCAL)) { permit = 1; break; } } if (num_adm_permitted_opens > 0) { permit_adm = 0; for (i = 0; i < num_adm_permitted_opens; i++) if (open_match(&permitted_adm_opens[i], path, PORT_STREAMLOCAL)) { permit_adm = 1; break; } } if (!permit || !permit_adm) { logit("Received request to connect to path %.100s, " "but the request was denied.", path); return NULL; } return connect_to(path, PORT_STREAMLOCAL, ctype, rname); } void channel_send_window_changes(void) { u_int i; struct winsize ws; for (i = 0; i < channels_alloc; i++) { if (channels[i] == NULL || !channels[i]->client_tty || channels[i]->type != SSH_CHANNEL_OPEN) continue; if (ioctl(channels[i]->rfd, TIOCGWINSZ, &ws) < 0) continue; channel_request_start(i, "window-change", 0); packet_put_int((u_int)ws.ws_col); packet_put_int((u_int)ws.ws_row); packet_put_int((u_int)ws.ws_xpixel); packet_put_int((u_int)ws.ws_ypixel); packet_send(); } } /* -- X11 forwarding */ /* * Creates an internet domain socket for listening for X11 connections. * Returns 0 and a suitable display number for the DISPLAY variable * stored in display_numberp , or -1 if an error occurs. */ int x11_create_display_inet(int x11_display_offset, int x11_use_localhost, int single_connection, u_int *display_numberp, int **chanids) { Channel *nc = NULL; int display_number, sock; u_short port; struct addrinfo hints, *ai, *aitop; char strport[NI_MAXSERV]; int gaierr, n, num_socks = 0, socks[NUM_SOCKS]; if (chanids == NULL) return -1; for (display_number = x11_display_offset; display_number < MAX_DISPLAYS; display_number++) { port = 6000 + display_number; memset(&hints, 0, sizeof(hints)); hints.ai_family = IPv4or6; hints.ai_flags = x11_use_localhost ? 0: AI_PASSIVE; hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%d", port); if ((gaierr = getaddrinfo(NULL, strport, &hints, &aitop)) != 0) { error("getaddrinfo: %.100s", ssh_gai_strerror(gaierr)); return -1; } for (ai = aitop; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue; sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sock < 0) { if ((errno != EINVAL) && (errno != EAFNOSUPPORT) #ifdef EPFNOSUPPORT && (errno != EPFNOSUPPORT) #endif ) { error("socket: %.100s", strerror(errno)); freeaddrinfo(aitop); return -1; } else { debug("x11_create_display_inet: Socket family %d not supported", ai->ai_family); continue; } } if (ai->ai_family == AF_INET6) sock_set_v6only(sock); if (x11_use_localhost) channel_set_reuseaddr(sock); if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { debug2("bind port %d: %.100s", port, strerror(errno)); close(sock); for (n = 0; n < num_socks; n++) { close(socks[n]); } num_socks = 0; break; } socks[num_socks++] = sock; if (num_socks == NUM_SOCKS) break; } freeaddrinfo(aitop); if (num_socks > 0) break; } if (display_number >= MAX_DISPLAYS) { error("Failed to allocate internet-domain X11 display socket."); return -1; } /* Start listening for connections on the socket. */ for (n = 0; n < num_socks; n++) { sock = socks[n]; if (listen(sock, SSH_LISTEN_BACKLOG) < 0) { error("listen: %.100s", strerror(errno)); close(sock); return -1; } } /* Allocate a channel for each socket. */ *chanids = xcalloc(num_socks + 1, sizeof(**chanids)); for (n = 0; n < num_socks; n++) { sock = socks[n]; nc = channel_new("x11 listener", SSH_CHANNEL_X11_LISTENER, sock, sock, -1, CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "X11 inet listener", 1); nc->single_connection = single_connection; (*chanids)[n] = nc->self; } (*chanids)[n] = -1; /* Return the display number for the DISPLAY environment variable. */ *display_numberp = display_number; return (0); } static int connect_local_xsocket_path(const char *pathname) { int sock; struct sockaddr_un addr; sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) error("socket: %.100s", strerror(errno)); memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; strlcpy(addr.sun_path, pathname, sizeof addr.sun_path); if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == 0) return sock; close(sock); error("connect %.100s: %.100s", addr.sun_path, strerror(errno)); return -1; } static int connect_local_xsocket(u_int dnr) { char buf[1024]; snprintf(buf, sizeof buf, _PATH_UNIX_X, dnr); return connect_local_xsocket_path(buf); } +#ifdef __APPLE__ +static int +is_path_to_xsocket(const char *display, char *path, size_t pathlen) +{ + struct stat sbuf; + + if (strlcpy(path, display, pathlen) >= pathlen) { + error("%s: display path too long", __func__); + return 0; + } + if (display[0] != '/') + return 0; + if (stat(path, &sbuf) == 0) { + return 1; + } else { + char *dot = strrchr(path, '.'); + if (dot != NULL) { + *dot = '\0'; + if (stat(path, &sbuf) == 0) { + return 1; + } + } + } + return 0; +} +#endif + int x11_connect_display(void) { u_int display_number; const char *display; char buf[1024], *cp; struct addrinfo hints, *ai, *aitop; char strport[NI_MAXSERV]; int gaierr, sock = 0; /* Try to open a socket for the local X server. */ display = getenv("DISPLAY"); if (!display) { error("DISPLAY not set."); return -1; } /* * Now we decode the value of the DISPLAY variable and make a * connection to the real X server. */ - /* Check if the display is from launchd. */ #ifdef __APPLE__ - if (strncmp(display, "/tmp/launch", 11) == 0) { - sock = connect_local_xsocket_path(display); - if (sock < 0) - return -1; + /* Check if display is a path to a socket (as set by launchd). */ + { + char path[PATH_MAX]; - /* OK, we now have a connection to the display. */ - return sock; + if (is_path_to_xsocket(display, path, sizeof(path))) { + debug("x11_connect_display: $DISPLAY is launchd"); + + /* Create a socket. */ + sock = connect_local_xsocket_path(path); + if (sock < 0) + return -1; + + /* OK, we now have a connection to the display. */ + return sock; + } } #endif /* * Check if it is a unix domain socket. Unix domain displays are in * one of the following formats: unix:d[.s], :d[.s], ::d[.s] */ if (strncmp(display, "unix:", 5) == 0 || display[0] == ':') { /* Connect to the unix domain socket. */ if (sscanf(strrchr(display, ':') + 1, "%u", &display_number) != 1) { error("Could not parse display number from DISPLAY: %.100s", display); return -1; } /* Create a socket. */ sock = connect_local_xsocket(display_number); if (sock < 0) return -1; /* OK, we now have a connection to the display. */ return sock; } /* * Connect to an inet socket. The DISPLAY value is supposedly * hostname:d[.s], where hostname may also be numeric IP address. */ strlcpy(buf, display, sizeof(buf)); cp = strchr(buf, ':'); if (!cp) { error("Could not find ':' in DISPLAY: %.100s", display); return -1; } *cp = 0; /* buf now contains the host name. But first we parse the display number. */ if (sscanf(cp + 1, "%u", &display_number) != 1) { error("Could not parse display number from DISPLAY: %.100s", display); return -1; } /* Look up the host address */ memset(&hints, 0, sizeof(hints)); hints.ai_family = IPv4or6; hints.ai_socktype = SOCK_STREAM; snprintf(strport, sizeof strport, "%u", 6000 + display_number); if ((gaierr = getaddrinfo(buf, strport, &hints, &aitop)) != 0) { error("%.100s: unknown host. (%s)", buf, ssh_gai_strerror(gaierr)); return -1; } for (ai = aitop; ai; ai = ai->ai_next) { /* Create a socket. */ sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sock < 0) { debug2("socket: %.100s", strerror(errno)); continue; } /* Connect it to the display. */ if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) { debug2("connect %.100s port %u: %.100s", buf, 6000 + display_number, strerror(errno)); close(sock); continue; } /* Success */ break; } freeaddrinfo(aitop); if (!ai) { error("connect %.100s port %u: %.100s", buf, 6000 + display_number, strerror(errno)); return -1; } set_nodelay(sock); return sock; } /* * This is called when SSH_SMSG_X11_OPEN is received. The packet contains * the remote channel number. We should do whatever we want, and respond * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE. */ /* ARGSUSED */ int x11_input_open(int type, u_int32_t seq, void *ctxt) { Channel *c = NULL; int remote_id, sock = 0; char *remote_host; debug("Received X11 open request."); remote_id = packet_get_int(); if (packet_get_protocol_flags() & SSH_PROTOFLAG_HOST_IN_FWD_OPEN) { remote_host = packet_get_string(NULL); } else { remote_host = xstrdup("unknown (remote did not supply name)"); } packet_check_eom(); /* Obtain a connection to the real X display. */ sock = x11_connect_display(); if (sock != -1) { /* Allocate a channel for this connection. */ c = channel_new("connected x11 socket", SSH_CHANNEL_X11_OPEN, sock, sock, -1, 0, 0, 0, remote_host, 1); c->remote_id = remote_id; c->force_drain = 1; } free(remote_host); if (c == NULL) { /* Send refusal to the remote host. */ packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(remote_id); } else { /* Send a confirmation to the remote host. */ packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); packet_put_int(remote_id); packet_put_int(c->self); } packet_send(); return 0; } /* dummy protocol handler that denies SSH-1 requests (agent/x11) */ /* ARGSUSED */ int deny_input_open(int type, u_int32_t seq, void *ctxt) { int rchan = packet_get_int(); switch (type) { case SSH_SMSG_AGENT_OPEN: error("Warning: ssh server tried agent forwarding."); break; case SSH_SMSG_X11_OPEN: error("Warning: ssh server tried X11 forwarding."); break; default: error("deny_input_open: type %d", type); break; } error("Warning: this is probably a break-in attempt by a malicious server."); packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(rchan); packet_send(); return 0; } /* * Requests forwarding of X11 connections, generates fake authentication * data, and enables authentication spoofing. * This should be called in the client only. */ void x11_request_forwarding_with_spoofing(int client_session_id, const char *disp, const char *proto, const char *data, int want_reply) { u_int data_len = (u_int) strlen(data) / 2; u_int i, value; char *new_data; int screen_number; const char *cp; if (x11_saved_display == NULL) x11_saved_display = xstrdup(disp); else if (strcmp(disp, x11_saved_display) != 0) { error("x11_request_forwarding_with_spoofing: different " "$DISPLAY already forwarded"); return; } cp = strchr(disp, ':'); if (cp) cp = strchr(cp, '.'); if (cp) screen_number = (u_int)strtonum(cp + 1, 0, 400, NULL); else screen_number = 0; if (x11_saved_proto == NULL) { /* Save protocol name. */ x11_saved_proto = xstrdup(proto); /* Extract real authentication data. */ x11_saved_data = xmalloc(data_len); for (i = 0; i < data_len; i++) { if (sscanf(data + 2 * i, "%2x", &value) != 1) fatal("x11_request_forwarding: bad " "authentication data: %.100s", data); x11_saved_data[i] = value; } x11_saved_data_len = data_len; /* Generate fake data of the same length. */ x11_fake_data = xmalloc(data_len); arc4random_buf(x11_fake_data, data_len); x11_fake_data_len = data_len; } /* Convert the fake data into hex. */ new_data = tohex(x11_fake_data, data_len); /* Send the request packet. */ if (compat20) { channel_request_start(client_session_id, "x11-req", want_reply); packet_put_char(0); /* XXX bool single connection */ } else { packet_start(SSH_CMSG_X11_REQUEST_FORWARDING); } packet_put_cstring(proto); packet_put_cstring(new_data); packet_put_int(screen_number); packet_send(); packet_write_wait(); free(new_data); } /* -- agent forwarding */ /* Sends a message to the server to request authentication fd forwarding. */ void auth_request_forwarding(void) { packet_start(SSH_CMSG_AGENT_REQUEST_FORWARDING); packet_send(); packet_write_wait(); } diff --git a/crypto/openssh/channels.h b/crypto/openssh/channels.h index 09c3c36557df..ce43236d5459 100644 --- a/crypto/openssh/channels.h +++ b/crypto/openssh/channels.h @@ -1,322 +1,323 @@ -/* $OpenBSD: channels.h,v 1.120 2016/10/18 17:32:54 dtucker Exp $ */ +/* $OpenBSD: channels.h,v 1.121 2017/02/01 02:59:09 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ /* * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CHANNEL_H #define CHANNEL_H /* Definitions for channel types. */ #define SSH_CHANNEL_X11_LISTENER 1 /* Listening for inet X11 conn. */ #define SSH_CHANNEL_PORT_LISTENER 2 /* Listening on a port. */ #define SSH_CHANNEL_OPENING 3 /* waiting for confirmation */ #define SSH_CHANNEL_OPEN 4 /* normal open two-way channel */ #define SSH_CHANNEL_CLOSED 5 /* waiting for close confirmation */ #define SSH_CHANNEL_AUTH_SOCKET 6 /* authentication socket */ #define SSH_CHANNEL_X11_OPEN 7 /* reading first X11 packet */ #define SSH_CHANNEL_INPUT_DRAINING 8 /* sending remaining data to conn */ #define SSH_CHANNEL_OUTPUT_DRAINING 9 /* sending remaining data to app */ #define SSH_CHANNEL_LARVAL 10 /* larval session */ #define SSH_CHANNEL_RPORT_LISTENER 11 /* Listening to a R-style port */ #define SSH_CHANNEL_CONNECTING 12 #define SSH_CHANNEL_DYNAMIC 13 #define SSH_CHANNEL_ZOMBIE 14 /* Almost dead. */ #define SSH_CHANNEL_MUX_LISTENER 15 /* Listener for mux conn. */ #define SSH_CHANNEL_MUX_CLIENT 16 /* Conn. to mux slave */ #define SSH_CHANNEL_ABANDONED 17 /* Abandoned session, eg mux */ #define SSH_CHANNEL_UNIX_LISTENER 18 /* Listening on a domain socket. */ #define SSH_CHANNEL_RUNIX_LISTENER 19 /* Listening to a R-style domain socket. */ #define SSH_CHANNEL_MUX_PROXY 20 /* proxy channel for mux-slave */ #define SSH_CHANNEL_MAX_TYPE 21 #define CHANNEL_CANCEL_PORT_STATIC -1 struct Channel; typedef struct Channel Channel; typedef void channel_open_fn(int, int, void *); typedef void channel_callback_fn(int, void *); typedef int channel_infilter_fn(struct Channel *, char *, int); typedef void channel_filter_cleanup_fn(int, void *); typedef u_char *channel_outfilter_fn(struct Channel *, u_char **, u_int *); /* Channel success/failure callbacks */ typedef void channel_confirm_cb(int, struct Channel *, void *); typedef void channel_confirm_abandon_cb(struct Channel *, void *); struct channel_confirm { TAILQ_ENTRY(channel_confirm) entry; channel_confirm_cb *cb; channel_confirm_abandon_cb *abandon_cb; void *ctx; }; TAILQ_HEAD(channel_confirms, channel_confirm); /* Context for non-blocking connects */ struct channel_connect { char *host; int port; struct addrinfo *ai, *aitop; }; /* Callbacks for mux channels back into client-specific code */ typedef int mux_callback_fn(struct Channel *); struct Channel { int type; /* channel type/state */ int self; /* my own channel identifier */ int remote_id; /* channel identifier for remote peer */ u_int istate; /* input from channel (state of receive half) */ u_int ostate; /* output to channel (state of transmit half) */ int flags; /* close sent/rcvd */ int rfd; /* read fd */ int wfd; /* write fd */ int efd; /* extended fd */ int sock; /* sock fd */ int ctl_chan; /* control channel (multiplexed connections) */ int isatty; /* rfd is a tty */ #ifdef _AIX int wfd_isatty; /* wfd is a tty */ #endif int client_tty; /* (client) TTY has been requested */ int force_drain; /* force close on iEOF */ time_t notbefore; /* Pause IO until deadline (time_t) */ int delayed; /* post-select handlers for newly created * channels are delayed until the first call * to a matching pre-select handler. * this way post-select handlers are not * accidentally called if a FD gets reused */ Buffer input; /* data read from socket, to be sent over * encrypted connection */ Buffer output; /* data received over encrypted connection for * send on socket */ Buffer extended; char *path; /* path for unix domain sockets, or host name for forwards */ int listening_port; /* port being listened for forwards */ char *listening_addr; /* addr being listened for forwards */ int host_port; /* remote port to connect for forwards */ char *remote_name; /* remote hostname */ u_int remote_window; u_int remote_maxpacket; u_int local_window; u_int local_window_max; u_int local_consumed; u_int local_maxpacket; int extended_usage; int single_connection; char *ctype; /* type */ /* callback */ channel_open_fn *open_confirm; void *open_confirm_ctx; channel_callback_fn *detach_user; int detach_close; struct channel_confirms status_confirms; /* filter */ channel_infilter_fn *input_filter; channel_outfilter_fn *output_filter; void *filter_ctx; channel_filter_cleanup_fn *filter_cleanup; /* keep boundaries */ int datagram; /* non-blocking connect */ struct channel_connect connect_ctx; /* multiplexing protocol hook, called for each packet received */ mux_callback_fn *mux_rcb; void *mux_ctx; int mux_pause; int mux_downstream_id; }; #define CHAN_EXTENDED_IGNORE 0 #define CHAN_EXTENDED_READ 1 #define CHAN_EXTENDED_WRITE 2 /* default window/packet sizes for tcp/x11-fwd-channel */ #define CHAN_SES_PACKET_DEFAULT (32*1024) #define CHAN_SES_WINDOW_DEFAULT (64*CHAN_SES_PACKET_DEFAULT) #define CHAN_TCP_PACKET_DEFAULT (32*1024) #define CHAN_TCP_WINDOW_DEFAULT (64*CHAN_TCP_PACKET_DEFAULT) #define CHAN_X11_PACKET_DEFAULT (16*1024) #define CHAN_X11_WINDOW_DEFAULT (4*CHAN_X11_PACKET_DEFAULT) /* possible input states */ #define CHAN_INPUT_OPEN 0 #define CHAN_INPUT_WAIT_DRAIN 1 #define CHAN_INPUT_WAIT_OCLOSE 2 #define CHAN_INPUT_CLOSED 3 /* possible output states */ #define CHAN_OUTPUT_OPEN 0 #define CHAN_OUTPUT_WAIT_DRAIN 1 #define CHAN_OUTPUT_WAIT_IEOF 2 #define CHAN_OUTPUT_CLOSED 3 #define CHAN_CLOSE_SENT 0x01 #define CHAN_CLOSE_RCVD 0x02 #define CHAN_EOF_SENT 0x04 #define CHAN_EOF_RCVD 0x08 #define CHAN_LOCAL 0x10 #define CHAN_RBUF 16*1024 /* check whether 'efd' is still in use */ #define CHANNEL_EFD_INPUT_ACTIVE(c) \ (compat20 && c->extended_usage == CHAN_EXTENDED_READ && \ (c->efd != -1 || \ buffer_len(&c->extended) > 0)) #define CHANNEL_EFD_OUTPUT_ACTIVE(c) \ (compat20 && c->extended_usage == CHAN_EXTENDED_WRITE && \ c->efd != -1 && (!(c->flags & (CHAN_EOF_RCVD|CHAN_CLOSE_RCVD)) || \ buffer_len(&c->extended) > 0)) /* channel management */ Channel *channel_by_id(int); Channel *channel_by_remote_id(int); Channel *channel_lookup(int); Channel *channel_new(char *, int, int, int, int, u_int, u_int, int, char *, int); void channel_set_fds(int, int, int, int, int, int, int, u_int); void channel_free(Channel *); void channel_free_all(void); void channel_stop_listening(void); void channel_send_open(int); void channel_request_start(int, char *, int); void channel_register_cleanup(int, channel_callback_fn *, int); void channel_register_open_confirm(int, channel_open_fn *, void *); void channel_register_filter(int, channel_infilter_fn *, channel_outfilter_fn *, channel_filter_cleanup_fn *, void *); void channel_register_status_confirm(int, channel_confirm_cb *, channel_confirm_abandon_cb *, void *); void channel_cancel_cleanup(int); int channel_close_fd(int *); void channel_send_window_changes(void); /* mux proxy support */ int channel_proxy_downstream(Channel *mc); int channel_proxy_upstream(Channel *, int, u_int32_t, void *); /* protocol handler */ int channel_input_close(int, u_int32_t, void *); int channel_input_close_confirmation(int, u_int32_t, void *); int channel_input_data(int, u_int32_t, void *); int channel_input_extended_data(int, u_int32_t, void *); int channel_input_ieof(int, u_int32_t, void *); int channel_input_oclose(int, u_int32_t, void *); int channel_input_open_confirmation(int, u_int32_t, void *); int channel_input_open_failure(int, u_int32_t, void *); int channel_input_port_open(int, u_int32_t, void *); int channel_input_window_adjust(int, u_int32_t, void *); int channel_input_status_confirm(int, u_int32_t, void *); /* file descriptor handling (read/write) */ void channel_prepare_select(fd_set **, fd_set **, int *, u_int*, time_t*, int); void channel_after_select(fd_set *, fd_set *); void channel_output_poll(void); int channel_not_very_much_buffered_data(void); void channel_close_all(void); int channel_still_open(void); char *channel_open_message(void); int channel_find_open(void); /* tcp forwarding */ struct Forward; struct ForwardOptions; void channel_set_af(int af); void channel_permit_all_opens(void); void channel_add_permitted_opens(char *, int); int channel_add_adm_permitted_opens(char *, int); void channel_disable_adm_local_opens(void); void channel_update_permitted_opens(int, int); void channel_clear_permitted_opens(void); void channel_clear_adm_permitted_opens(void); void channel_print_adm_permitted_opens(void); -Channel *channel_connect_to_port(const char *, u_short, char *, char *); +Channel *channel_connect_to_port(const char *, u_short, char *, char *, int *, + const char **); Channel *channel_connect_to_path(const char *, char *, char *); Channel *channel_connect_stdio_fwd(const char*, u_short, int, int); Channel *channel_connect_by_listen_address(const char *, u_short, char *, char *); Channel *channel_connect_by_listen_path(const char *, char *, char *); int channel_request_remote_forwarding(struct Forward *); int channel_setup_local_fwd_listener(struct Forward *, struct ForwardOptions *); int channel_request_rforward_cancel(struct Forward *); int channel_setup_remote_fwd_listener(struct Forward *, int *, struct ForwardOptions *); int channel_cancel_rport_listener(struct Forward *); int channel_cancel_lport_listener(struct Forward *, int, struct ForwardOptions *); int permitopen_port(const char *); /* x11 forwarding */ void channel_set_x11_refuse_time(u_int); int x11_connect_display(void); int x11_create_display_inet(int, int, int, u_int *, int **); int x11_input_open(int, u_int32_t, void *); void x11_request_forwarding_with_spoofing(int, const char *, const char *, const char *, int); int deny_input_open(int, u_int32_t, void *); /* agent forwarding */ void auth_request_forwarding(void); /* channel close */ int chan_is_dead(Channel *, int); void chan_mark_dead(Channel *); /* channel events */ void chan_rcvd_oclose(Channel *); void chan_rcvd_eow(Channel *); /* SSH2-only */ void chan_read_failed(Channel *); void chan_ibuf_empty(Channel *); void chan_rcvd_ieof(Channel *); void chan_write_failed(Channel *); void chan_obuf_empty(Channel *); #endif diff --git a/crypto/openssh/clientloop.c b/crypto/openssh/clientloop.c index 4289a40812bc..0648162341f7 100644 --- a/crypto/openssh/clientloop.c +++ b/crypto/openssh/clientloop.c @@ -1,2762 +1,2779 @@ -/* $OpenBSD: clientloop.c,v 1.289 2016/09/30 09:19:13 markus Exp $ */ +/* $OpenBSD: clientloop.c,v 1.291 2017/03/10 05:01:13 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * The main loop for the interactive session (client side). * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * * Copyright (c) 1999 Theo de Raadt. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * * SSH2 support added by Markus Friedl. * Copyright (c) 1999, 2000, 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include #ifdef HAVE_PATHS_H #include #endif #include #include #include #include #include #include #include #include #include #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "ssh.h" #include "ssh1.h" #include "ssh2.h" #include "packet.h" #include "buffer.h" #include "compat.h" #include "channels.h" #include "dispatch.h" #include "key.h" #include "cipher.h" #include "kex.h" #include "myproposal.h" #include "log.h" #include "misc.h" #include "readconf.h" #include "clientloop.h" #include "sshconnect.h" #include "authfd.h" #include "atomicio.h" #include "sshpty.h" #include "match.h" #include "msg.h" #include "ssherr.h" #include "hostfile.h" /* import options */ extern Options options; /* Flag indicating that stdin should be redirected from /dev/null. */ extern int stdin_null_flag; /* Flag indicating that no shell has been requested */ extern int no_shell_flag; /* Flag indicating that ssh should daemonise after authentication is complete */ extern int fork_after_authentication_flag; /* Control socket */ extern int muxserver_sock; /* XXX use mux_client_cleanup() instead */ /* * Name of the host we are connecting to. This is the name given on the * command line, or the HostName specified for the user-supplied name in a * configuration file. */ extern char *host; /* * Flag to indicate that we have received a window change signal which has * not yet been processed. This will cause a message indicating the new * window size to be sent to the server a little later. This is volatile * because this is updated in a signal handler. */ static volatile sig_atomic_t received_window_change_signal = 0; static volatile sig_atomic_t received_signal = 0; /* Flag indicating whether the user's terminal is in non-blocking mode. */ static int in_non_blocking_mode = 0; /* Time when backgrounded control master using ControlPersist should exit */ static time_t control_persist_exit_time = 0; /* Common data for the client loop code. */ volatile sig_atomic_t quit_pending; /* Set non-zero to quit the loop. */ static int escape_char1; /* Escape character. (proto1 only) */ static int escape_pending1; /* Last character was an escape (proto1 only) */ static int last_was_cr; /* Last character was a newline. */ static int exit_status; /* Used to store the command exit status. */ static int stdin_eof; /* EOF has been encountered on stderr. */ static Buffer stdin_buffer; /* Buffer for stdin data. */ static Buffer stdout_buffer; /* Buffer for stdout data. */ static Buffer stderr_buffer; /* Buffer for stderr data. */ static u_int buffer_high; /* Soft max buffer size. */ static int connection_in; /* Connection to server (input). */ static int connection_out; /* Connection to server (output). */ static int need_rekeying; /* Set to non-zero if rekeying is requested. */ static int session_closed; /* In SSH2: login session closed. */ static u_int x11_refuse_time; /* If >0, refuse x11 opens after this time. */ static void client_init_dispatch(void); int session_ident = -1; /* Track escape per proto2 channel */ struct escape_filter_ctx { int escape_pending; int escape_char; }; /* Context for channel confirmation replies */ struct channel_reply_ctx { const char *request_type; int id; enum confirm_action action; }; /* Global request success/failure callbacks */ struct global_confirm { TAILQ_ENTRY(global_confirm) entry; global_confirm_cb *cb; void *ctx; int ref_count; }; TAILQ_HEAD(global_confirms, global_confirm); static struct global_confirms global_confirms = TAILQ_HEAD_INITIALIZER(global_confirms); void ssh_process_session2_setup(int, int, int, Buffer *); /* Restores stdin to blocking mode. */ static void leave_non_blocking(void) { if (in_non_blocking_mode) { unset_nonblock(fileno(stdin)); in_non_blocking_mode = 0; } } /* Puts stdin terminal in non-blocking mode. */ static void enter_non_blocking(void) { in_non_blocking_mode = 1; set_nonblock(fileno(stdin)); } /* * Signal handler for the window change signal (SIGWINCH). This just sets a * flag indicating that the window has changed. */ /*ARGSUSED */ static void window_change_handler(int sig) { received_window_change_signal = 1; signal(SIGWINCH, window_change_handler); } /* * Signal handler for signals that cause the program to terminate. These * signals must be trapped to restore terminal modes. */ /*ARGSUSED */ static void signal_handler(int sig) { received_signal = sig; quit_pending = 1; } /* * Returns current time in seconds from Jan 1, 1970 with the maximum * available resolution. */ static double get_current_time(void) { struct timeval tv; gettimeofday(&tv, NULL); return (double) tv.tv_sec + (double) tv.tv_usec / 1000000.0; } /* * Sets control_persist_exit_time to the absolute time when the * backgrounded control master should exit due to expiry of the * ControlPersist timeout. Sets it to 0 if we are not a backgrounded * control master process, or if there is no ControlPersist timeout. */ static void set_control_persist_exit_time(void) { if (muxserver_sock == -1 || !options.control_persist || options.control_persist_timeout == 0) { /* not using a ControlPersist timeout */ control_persist_exit_time = 0; } else if (channel_still_open()) { /* some client connections are still open */ if (control_persist_exit_time > 0) debug2("%s: cancel scheduled exit", __func__); control_persist_exit_time = 0; } else if (control_persist_exit_time <= 0) { /* a client connection has recently closed */ control_persist_exit_time = monotime() + (time_t)options.control_persist_timeout; debug2("%s: schedule exit in %d seconds", __func__, options.control_persist_timeout); } /* else we are already counting down to the timeout */ } #define SSH_X11_VALID_DISPLAY_CHARS ":/.-_" static int client_x11_display_valid(const char *display) { size_t i, dlen; if (display == NULL) return 0; dlen = strlen(display); for (i = 0; i < dlen; i++) { if (!isalnum((u_char)display[i]) && strchr(SSH_X11_VALID_DISPLAY_CHARS, display[i]) == NULL) { debug("Invalid character '%c' in DISPLAY", display[i]); return 0; } } return 1; } #define SSH_X11_PROTO "MIT-MAGIC-COOKIE-1" #define X11_TIMEOUT_SLACK 60 int client_x11_get_proto(const char *display, const char *xauth_path, u_int trusted, u_int timeout, char **_proto, char **_data) { char cmd[1024], line[512], xdisplay[512]; char xauthfile[PATH_MAX], xauthdir[PATH_MAX]; static char proto[512], data[512]; FILE *f; int got_data = 0, generated = 0, do_unlink = 0, r; struct stat st; u_int now, x11_timeout_real; *_proto = proto; *_data = data; proto[0] = data[0] = xauthfile[0] = xauthdir[0] = '\0'; if (!client_x11_display_valid(display)) { if (display != NULL) logit("DISPLAY \"%s\" invalid; disabling X11 forwarding", display); return -1; } if (xauth_path != NULL && stat(xauth_path, &st) == -1) { debug("No xauth program."); xauth_path = NULL; } if (xauth_path != NULL) { /* * Handle FamilyLocal case where $DISPLAY does * not match an authorization entry. For this we * just try "xauth list unix:displaynum.screennum". * XXX: "localhost" match to determine FamilyLocal * is not perfect. */ if (strncmp(display, "localhost:", 10) == 0) { if ((r = snprintf(xdisplay, sizeof(xdisplay), "unix:%s", display + 10)) < 0 || (size_t)r >= sizeof(xdisplay)) { error("%s: display name too long", __func__); return -1; } display = xdisplay; } if (trusted == 0) { /* * Generate an untrusted X11 auth cookie. * * The authentication cookie should briefly outlive * ssh's willingness to forward X11 connections to * avoid nasty fail-open behaviour in the X server. */ mktemp_proto(xauthdir, sizeof(xauthdir)); if (mkdtemp(xauthdir) == NULL) { error("%s: mkdtemp: %s", __func__, strerror(errno)); return -1; } do_unlink = 1; if ((r = snprintf(xauthfile, sizeof(xauthfile), "%s/xauthfile", xauthdir)) < 0 || (size_t)r >= sizeof(xauthfile)) { error("%s: xauthfile path too long", __func__); unlink(xauthfile); rmdir(xauthdir); return -1; } if (timeout >= UINT_MAX - X11_TIMEOUT_SLACK) x11_timeout_real = UINT_MAX; else x11_timeout_real = timeout + X11_TIMEOUT_SLACK; if ((r = snprintf(cmd, sizeof(cmd), "%s -f %s generate %s " SSH_X11_PROTO " untrusted timeout %u 2>" _PATH_DEVNULL, xauth_path, xauthfile, display, x11_timeout_real)) < 0 || (size_t)r >= sizeof(cmd)) fatal("%s: cmd too long", __func__); debug2("%s: %s", __func__, cmd); if (x11_refuse_time == 0) { now = monotime() + 1; if (UINT_MAX - timeout < now) x11_refuse_time = UINT_MAX; else x11_refuse_time = now + timeout; channel_set_x11_refuse_time(x11_refuse_time); } if (system(cmd) == 0) generated = 1; } /* * When in untrusted mode, we read the cookie only if it was * successfully generated as an untrusted one in the step * above. */ if (trusted || generated) { snprintf(cmd, sizeof(cmd), "%s %s%s list %s 2>" _PATH_DEVNULL, xauth_path, generated ? "-f " : "" , generated ? xauthfile : "", display); debug2("x11_get_proto: %s", cmd); f = popen(cmd, "r"); if (f && fgets(line, sizeof(line), f) && sscanf(line, "%*s %511s %511s", proto, data) == 2) got_data = 1; if (f) pclose(f); } } if (do_unlink) { unlink(xauthfile); rmdir(xauthdir); } /* Don't fall back to fake X11 data for untrusted forwarding */ if (!trusted && !got_data) { error("Warning: untrusted X11 forwarding setup failed: " "xauth key data not generated"); return -1; } /* * If we didn't get authentication data, just make up some * data. The forwarding code will check the validity of the * response anyway, and substitute this data. The X11 * server, however, will ignore this fake data and use * whatever authentication mechanisms it was using otherwise * for the local connection. */ if (!got_data) { u_int8_t rnd[16]; u_int i; logit("Warning: No xauth data; " "using fake authentication data for X11 forwarding."); strlcpy(proto, SSH_X11_PROTO, sizeof proto); arc4random_buf(rnd, sizeof(rnd)); for (i = 0; i < sizeof(rnd); i++) { snprintf(data + 2 * i, sizeof data - 2 * i, "%02x", rnd[i]); } } return 0; } /* * This is called when the interactive is entered. This checks if there is * an EOF coming on stdin. We must check this explicitly, as select() does * not appear to wake up when redirecting from /dev/null. */ static void client_check_initial_eof_on_stdin(void) { int len; char buf[1]; /* * If standard input is to be "redirected from /dev/null", we simply * mark that we have seen an EOF and send an EOF message to the * server. Otherwise, we try to read a single character; it appears * that for some files, such /dev/null, select() never wakes up for * read for this descriptor, which means that we never get EOF. This * way we will get the EOF if stdin comes from /dev/null or similar. */ if (stdin_null_flag) { /* Fake EOF on stdin. */ debug("Sending eof."); stdin_eof = 1; packet_start(SSH_CMSG_EOF); packet_send(); } else { enter_non_blocking(); /* Check for immediate EOF on stdin. */ len = read(fileno(stdin), buf, 1); if (len == 0) { /* * EOF. Record that we have seen it and send * EOF to server. */ debug("Sending eof."); stdin_eof = 1; packet_start(SSH_CMSG_EOF); packet_send(); } else if (len > 0) { /* * Got data. We must store the data in the buffer, * and also process it as an escape character if * appropriate. */ if ((u_char) buf[0] == escape_char1) escape_pending1 = 1; else buffer_append(&stdin_buffer, buf, 1); } leave_non_blocking(); } } /* * Make packets from buffered stdin data, and buffer them for sending to the * connection. */ static void client_make_packets_from_stdin_data(void) { u_int len; /* Send buffered stdin data to the server. */ while (buffer_len(&stdin_buffer) > 0 && packet_not_very_much_data_to_write()) { len = buffer_len(&stdin_buffer); /* Keep the packets at reasonable size. */ if (len > packet_get_maxsize()) len = packet_get_maxsize(); packet_start(SSH_CMSG_STDIN_DATA); packet_put_string(buffer_ptr(&stdin_buffer), len); packet_send(); buffer_consume(&stdin_buffer, len); /* If we have a pending EOF, send it now. */ if (stdin_eof && buffer_len(&stdin_buffer) == 0) { packet_start(SSH_CMSG_EOF); packet_send(); } } } /* * Checks if the client window has changed, and sends a packet about it to * the server if so. The actual change is detected elsewhere (by a software * interrupt on Unix); this just checks the flag and sends a message if * appropriate. */ static void client_check_window_change(void) { struct winsize ws; if (! received_window_change_signal) return; /** XXX race */ received_window_change_signal = 0; debug2("client_check_window_change: changed"); if (compat20) { channel_send_window_changes(); } else { if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) return; packet_start(SSH_CMSG_WINDOW_SIZE); packet_put_int((u_int)ws.ws_row); packet_put_int((u_int)ws.ws_col); packet_put_int((u_int)ws.ws_xpixel); packet_put_int((u_int)ws.ws_ypixel); packet_send(); } } static int client_global_request_reply(int type, u_int32_t seq, void *ctxt) { struct global_confirm *gc; if ((gc = TAILQ_FIRST(&global_confirms)) == NULL) return 0; if (gc->cb != NULL) gc->cb(type, seq, gc->ctx); if (--gc->ref_count <= 0) { TAILQ_REMOVE(&global_confirms, gc, entry); explicit_bzero(gc, sizeof(*gc)); free(gc); } packet_set_alive_timeouts(0); return 0; } static void server_alive_check(void) { if (packet_inc_alive_timeouts() > options.server_alive_count_max) { logit("Timeout, server %s not responding.", host); cleanup_exit(255); } packet_start(SSH2_MSG_GLOBAL_REQUEST); packet_put_cstring("keepalive@openssh.com"); packet_put_char(1); /* boolean: want reply */ packet_send(); /* Insert an empty placeholder to maintain ordering */ client_register_global_confirm(NULL, NULL); } /* * Waits until the client can do something (some data becomes available on * one of the file descriptors). */ static void client_wait_until_can_do_something(fd_set **readsetp, fd_set **writesetp, int *maxfdp, u_int *nallocp, int rekeying) { struct timeval tv, *tvp; int timeout_secs; time_t minwait_secs = 0, server_alive_time = 0, now = monotime(); int ret; /* Add any selections by the channel mechanism. */ channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, &minwait_secs, rekeying); if (!compat20) { /* Read from the connection, unless our buffers are full. */ if (buffer_len(&stdout_buffer) < buffer_high && buffer_len(&stderr_buffer) < buffer_high && channel_not_very_much_buffered_data()) FD_SET(connection_in, *readsetp); /* * Read from stdin, unless we have seen EOF or have very much * buffered data to send to the server. */ if (!stdin_eof && packet_not_very_much_data_to_write()) FD_SET(fileno(stdin), *readsetp); /* Select stdout/stderr if have data in buffer. */ if (buffer_len(&stdout_buffer) > 0) FD_SET(fileno(stdout), *writesetp); if (buffer_len(&stderr_buffer) > 0) FD_SET(fileno(stderr), *writesetp); } else { /* channel_prepare_select could have closed the last channel */ if (session_closed && !channel_still_open() && !packet_have_data_to_write()) { /* clear mask since we did not call select() */ memset(*readsetp, 0, *nallocp); memset(*writesetp, 0, *nallocp); return; } else { FD_SET(connection_in, *readsetp); } } /* Select server connection if have data to write to the server. */ if (packet_have_data_to_write()) FD_SET(connection_out, *writesetp); /* * Wait for something to happen. This will suspend the process until * some selected descriptor can be read, written, or has some other * event pending, or a timeout expires. */ timeout_secs = INT_MAX; /* we use INT_MAX to mean no timeout */ if (options.server_alive_interval > 0 && compat20) { timeout_secs = options.server_alive_interval; server_alive_time = now + options.server_alive_interval; } if (options.rekey_interval > 0 && compat20 && !rekeying) timeout_secs = MINIMUM(timeout_secs, packet_get_rekey_timeout()); set_control_persist_exit_time(); if (control_persist_exit_time > 0) { timeout_secs = MINIMUM(timeout_secs, control_persist_exit_time - now); if (timeout_secs < 0) timeout_secs = 0; } if (minwait_secs != 0) timeout_secs = MINIMUM(timeout_secs, (int)minwait_secs); if (timeout_secs == INT_MAX) tvp = NULL; else { tv.tv_sec = timeout_secs; tv.tv_usec = 0; tvp = &tv; } ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); if (ret < 0) { char buf[100]; /* * We have to clear the select masks, because we return. * We have to return, because the mainloop checks for the flags * set by the signal handlers. */ memset(*readsetp, 0, *nallocp); memset(*writesetp, 0, *nallocp); if (errno == EINTR) return; /* Note: we might still have data in the buffers. */ snprintf(buf, sizeof buf, "select: %s\r\n", strerror(errno)); buffer_append(&stderr_buffer, buf, strlen(buf)); quit_pending = 1; } else if (ret == 0) { /* * Timeout. Could have been either keepalive or rekeying. * Keepalive we check here, rekeying is checked in clientloop. */ if (server_alive_time != 0 && server_alive_time <= monotime()) server_alive_check(); } } static void client_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr) { /* Flush stdout and stderr buffers. */ if (buffer_len(bout) > 0) atomicio(vwrite, fileno(stdout), buffer_ptr(bout), buffer_len(bout)); if (buffer_len(berr) > 0) atomicio(vwrite, fileno(stderr), buffer_ptr(berr), buffer_len(berr)); leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); /* * Free (and clear) the buffer to reduce the amount of data that gets * written to swap. */ buffer_free(bin); buffer_free(bout); buffer_free(berr); /* Send the suspend signal to the program itself. */ kill(getpid(), SIGTSTP); /* Reset window sizes in case they have changed */ received_window_change_signal = 1; /* OK, we have been continued by the user. Reinitialize buffers. */ buffer_init(bin); buffer_init(bout); buffer_init(berr); enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); } static void client_process_net_input(fd_set *readset) { int len; char buf[SSH_IOBUFSZ]; /* * Read input from the server, and add any such data to the buffer of * the packet subsystem. */ if (FD_ISSET(connection_in, readset)) { /* Read as much as possible. */ len = read(connection_in, buf, sizeof(buf)); if (len == 0) { /* * Received EOF. The remote host has closed the * connection. */ snprintf(buf, sizeof buf, "Connection to %.300s closed by remote host.\r\n", host); buffer_append(&stderr_buffer, buf, strlen(buf)); quit_pending = 1; return; } /* * There is a kernel bug on Solaris that causes select to * sometimes wake up even though there is no data available. */ if (len < 0 && (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) len = 0; if (len < 0) { /* * An error has encountered. Perhaps there is a * network problem. */ snprintf(buf, sizeof buf, "Read from remote host %.300s: %.100s\r\n", host, strerror(errno)); buffer_append(&stderr_buffer, buf, strlen(buf)); quit_pending = 1; return; } packet_process_incoming(buf, len); } } static void client_status_confirm(int type, Channel *c, void *ctx) { struct channel_reply_ctx *cr = (struct channel_reply_ctx *)ctx; char errmsg[256]; int tochan; /* * If a TTY was explicitly requested, then a failure to allocate * one is fatal. */ if (cr->action == CONFIRM_TTY && (options.request_tty == REQUEST_TTY_FORCE || options.request_tty == REQUEST_TTY_YES)) cr->action = CONFIRM_CLOSE; /* XXX supress on mux _client_ quietmode */ tochan = options.log_level >= SYSLOG_LEVEL_ERROR && c->ctl_chan != -1 && c->extended_usage == CHAN_EXTENDED_WRITE; if (type == SSH2_MSG_CHANNEL_SUCCESS) { debug2("%s request accepted on channel %d", cr->request_type, c->self); } else if (type == SSH2_MSG_CHANNEL_FAILURE) { if (tochan) { snprintf(errmsg, sizeof(errmsg), "%s request failed\r\n", cr->request_type); } else { snprintf(errmsg, sizeof(errmsg), "%s request failed on channel %d", cr->request_type, c->self); } /* If error occurred on primary session channel, then exit */ if (cr->action == CONFIRM_CLOSE && c->self == session_ident) fatal("%s", errmsg); /* * If error occurred on mux client, append to * their stderr. */ if (tochan) { buffer_append(&c->extended, errmsg, strlen(errmsg)); } else error("%s", errmsg); if (cr->action == CONFIRM_TTY) { /* * If a TTY allocation error occurred, then arrange * for the correct TTY to leave raw mode. */ if (c->self == session_ident) leave_raw_mode(0); else mux_tty_alloc_failed(c); } else if (cr->action == CONFIRM_CLOSE) { chan_read_failed(c); chan_write_failed(c); } } free(cr); } static void client_abandon_status_confirm(Channel *c, void *ctx) { free(ctx); } void client_expect_confirm(int id, const char *request, enum confirm_action action) { struct channel_reply_ctx *cr = xcalloc(1, sizeof(*cr)); cr->request_type = request; cr->action = action; channel_register_status_confirm(id, client_status_confirm, client_abandon_status_confirm, cr); } void client_register_global_confirm(global_confirm_cb *cb, void *ctx) { struct global_confirm *gc, *last_gc; /* Coalesce identical callbacks */ last_gc = TAILQ_LAST(&global_confirms, global_confirms); if (last_gc && last_gc->cb == cb && last_gc->ctx == ctx) { if (++last_gc->ref_count >= INT_MAX) fatal("%s: last_gc->ref_count = %d", __func__, last_gc->ref_count); return; } gc = xcalloc(1, sizeof(*gc)); gc->cb = cb; gc->ctx = ctx; gc->ref_count = 1; TAILQ_INSERT_TAIL(&global_confirms, gc, entry); } static void process_cmdline(void) { void (*handler)(int); char *s, *cmd; int ok, delete = 0, local = 0, remote = 0, dynamic = 0; struct Forward fwd; memset(&fwd, 0, sizeof(fwd)); leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); handler = signal(SIGINT, SIG_IGN); cmd = s = read_passphrase("\r\nssh> ", RP_ECHO); if (s == NULL) goto out; while (isspace((u_char)*s)) s++; if (*s == '-') s++; /* Skip cmdline '-', if any */ if (*s == '\0') goto out; if (*s == 'h' || *s == 'H' || *s == '?') { logit("Commands:"); logit(" -L[bind_address:]port:host:hostport " "Request local forward"); logit(" -R[bind_address:]port:host:hostport " "Request remote forward"); logit(" -D[bind_address:]port " "Request dynamic forward"); logit(" -KL[bind_address:]port " "Cancel local forward"); logit(" -KR[bind_address:]port " "Cancel remote forward"); logit(" -KD[bind_address:]port " "Cancel dynamic forward"); if (!options.permit_local_command) goto out; logit(" !args " "Execute local command"); goto out; } if (*s == '!' && options.permit_local_command) { s++; ssh_local_cmd(s); goto out; } if (*s == 'K') { delete = 1; s++; } if (*s == 'L') local = 1; else if (*s == 'R') remote = 1; else if (*s == 'D') dynamic = 1; else { logit("Invalid command."); goto out; } if (delete && !compat20) { logit("Not supported for SSH protocol version 1."); goto out; } while (isspace((u_char)*++s)) ; /* XXX update list of forwards in options */ if (delete) { /* We pass 1 for dynamicfwd to restrict to 1 or 2 fields. */ if (!parse_forward(&fwd, s, 1, 0)) { logit("Bad forwarding close specification."); goto out; } if (remote) ok = channel_request_rforward_cancel(&fwd) == 0; else if (dynamic) ok = channel_cancel_lport_listener(&fwd, 0, &options.fwd_opts) > 0; else ok = channel_cancel_lport_listener(&fwd, CHANNEL_CANCEL_PORT_STATIC, &options.fwd_opts) > 0; if (!ok) { - logit("Unkown port forwarding."); + logit("Unknown port forwarding."); goto out; } logit("Canceled forwarding."); } else { if (!parse_forward(&fwd, s, dynamic, remote)) { logit("Bad forwarding specification."); goto out; } if (local || dynamic) { if (!channel_setup_local_fwd_listener(&fwd, &options.fwd_opts)) { logit("Port forwarding failed."); goto out; } } else { if (channel_request_remote_forwarding(&fwd) < 0) { logit("Port forwarding failed."); goto out; } } logit("Forwarding port."); } out: signal(SIGINT, handler); enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); free(cmd); free(fwd.listen_host); free(fwd.listen_path); free(fwd.connect_host); free(fwd.connect_path); } /* reasons to suppress output of an escape command in help output */ #define SUPPRESS_NEVER 0 /* never suppress, always show */ #define SUPPRESS_PROTO1 1 /* don't show in protocol 1 sessions */ #define SUPPRESS_MUXCLIENT 2 /* don't show in mux client sessions */ #define SUPPRESS_MUXMASTER 4 /* don't show in mux master sessions */ #define SUPPRESS_SYSLOG 8 /* don't show when logging to syslog */ struct escape_help_text { const char *cmd; const char *text; unsigned int flags; }; static struct escape_help_text esc_txt[] = { {".", "terminate session", SUPPRESS_MUXMASTER}, {".", "terminate connection (and any multiplexed sessions)", SUPPRESS_MUXCLIENT}, {"B", "send a BREAK to the remote system", SUPPRESS_PROTO1}, {"C", "open a command line", SUPPRESS_MUXCLIENT}, {"R", "request rekey", SUPPRESS_PROTO1}, {"V/v", "decrease/increase verbosity (LogLevel)", SUPPRESS_MUXCLIENT}, {"^Z", "suspend ssh", SUPPRESS_MUXCLIENT}, {"#", "list forwarded connections", SUPPRESS_NEVER}, {"&", "background ssh (when waiting for connections to terminate)", SUPPRESS_MUXCLIENT}, {"?", "this message", SUPPRESS_NEVER}, }; static void print_escape_help(Buffer *b, int escape_char, int protocol2, int mux_client, int using_stderr) { unsigned int i, suppress_flags; char string[1024]; snprintf(string, sizeof string, "%c?\r\n" "Supported escape sequences:\r\n", escape_char); buffer_append(b, string, strlen(string)); suppress_flags = (protocol2 ? 0 : SUPPRESS_PROTO1) | (mux_client ? SUPPRESS_MUXCLIENT : 0) | (mux_client ? 0 : SUPPRESS_MUXMASTER) | (using_stderr ? 0 : SUPPRESS_SYSLOG); for (i = 0; i < sizeof(esc_txt)/sizeof(esc_txt[0]); i++) { if (esc_txt[i].flags & suppress_flags) continue; snprintf(string, sizeof string, " %c%-3s - %s\r\n", escape_char, esc_txt[i].cmd, esc_txt[i].text); buffer_append(b, string, strlen(string)); } snprintf(string, sizeof string, " %c%c - send the escape character by typing it twice\r\n" "(Note that escapes are only recognized immediately after " "newline.)\r\n", escape_char, escape_char); buffer_append(b, string, strlen(string)); } /* * Process the characters one by one, call with c==NULL for proto1 case. */ static int process_escapes(Channel *c, Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len) { char string[1024]; pid_t pid; int bytes = 0; u_int i; u_char ch; char *s; int *escape_pendingp, escape_char; struct escape_filter_ctx *efc; if (c == NULL) { escape_pendingp = &escape_pending1; escape_char = escape_char1; } else { if (c->filter_ctx == NULL) return 0; efc = (struct escape_filter_ctx *)c->filter_ctx; escape_pendingp = &efc->escape_pending; escape_char = efc->escape_char; } if (len <= 0) return (0); for (i = 0; i < (u_int)len; i++) { /* Get one character at a time. */ ch = buf[i]; if (*escape_pendingp) { /* We have previously seen an escape character. */ /* Clear the flag now. */ *escape_pendingp = 0; /* Process the escaped character. */ switch (ch) { case '.': /* Terminate the connection. */ snprintf(string, sizeof string, "%c.\r\n", escape_char); buffer_append(berr, string, strlen(string)); if (c && c->ctl_chan != -1) { chan_read_failed(c); chan_write_failed(c); if (c->detach_user) c->detach_user(c->self, NULL); c->type = SSH_CHANNEL_ABANDONED; buffer_clear(&c->input); chan_ibuf_empty(c); return 0; } else quit_pending = 1; return -1; case 'Z' - 64: /* XXX support this for mux clients */ if (c && c->ctl_chan != -1) { char b[16]; noescape: if (ch == 'Z' - 64) snprintf(b, sizeof b, "^Z"); else snprintf(b, sizeof b, "%c", ch); snprintf(string, sizeof string, "%c%s escape not available to " "multiplexed sessions\r\n", escape_char, b); buffer_append(berr, string, strlen(string)); continue; } /* Suspend the program. Inform the user */ snprintf(string, sizeof string, "%c^Z [suspend ssh]\r\n", escape_char); buffer_append(berr, string, strlen(string)); /* Restore terminal modes and suspend. */ client_suspend_self(bin, bout, berr); /* We have been continued. */ continue; case 'B': if (compat20) { snprintf(string, sizeof string, "%cB\r\n", escape_char); buffer_append(berr, string, strlen(string)); channel_request_start(c->self, "break", 0); packet_put_int(1000); packet_send(); } continue; case 'R': if (compat20) { if (datafellows & SSH_BUG_NOREKEY) logit("Server does not " "support re-keying"); else need_rekeying = 1; } continue; case 'V': /* FALLTHROUGH */ case 'v': if (c && c->ctl_chan != -1) goto noescape; if (!log_is_on_stderr()) { snprintf(string, sizeof string, "%c%c [Logging to syslog]\r\n", escape_char, ch); buffer_append(berr, string, strlen(string)); continue; } if (ch == 'V' && options.log_level > SYSLOG_LEVEL_QUIET) log_change_level(--options.log_level); if (ch == 'v' && options.log_level < SYSLOG_LEVEL_DEBUG3) log_change_level(++options.log_level); snprintf(string, sizeof string, "%c%c [LogLevel %s]\r\n", escape_char, ch, log_level_name(options.log_level)); buffer_append(berr, string, strlen(string)); continue; case '&': if (c && c->ctl_chan != -1) goto noescape; /* * Detach the program (continue to serve * connections, but put in background and no * more new connections). */ /* Restore tty modes. */ leave_raw_mode( options.request_tty == REQUEST_TTY_FORCE); /* Stop listening for new connections. */ channel_stop_listening(); snprintf(string, sizeof string, "%c& [backgrounded]\n", escape_char); buffer_append(berr, string, strlen(string)); /* Fork into background. */ pid = fork(); if (pid < 0) { error("fork: %.100s", strerror(errno)); continue; } if (pid != 0) { /* This is the parent. */ /* The parent just exits. */ exit(0); } /* The child continues serving connections. */ if (compat20) { buffer_append(bin, "\004", 1); /* fake EOF on stdin */ return -1; } else if (!stdin_eof) { /* * Sending SSH_CMSG_EOF alone does not * always appear to be enough. So we * try to send an EOF character first. */ packet_start(SSH_CMSG_STDIN_DATA); packet_put_string("\004", 1); packet_send(); /* Close stdin. */ stdin_eof = 1; if (buffer_len(bin) == 0) { packet_start(SSH_CMSG_EOF); packet_send(); } } continue; case '?': print_escape_help(berr, escape_char, compat20, (c && c->ctl_chan != -1), log_is_on_stderr()); continue; case '#': snprintf(string, sizeof string, "%c#\r\n", escape_char); buffer_append(berr, string, strlen(string)); s = channel_open_message(); buffer_append(berr, s, strlen(s)); free(s); continue; case 'C': if (c && c->ctl_chan != -1) goto noescape; process_cmdline(); continue; default: if (ch != escape_char) { buffer_put_char(bin, escape_char); bytes++; } /* Escaped characters fall through here */ break; } } else { /* * The previous character was not an escape char. * Check if this is an escape. */ if (last_was_cr && ch == escape_char) { /* * It is. Set the flag and continue to * next character. */ *escape_pendingp = 1; continue; } } /* * Normal character. Record whether it was a newline, * and append it to the buffer. */ last_was_cr = (ch == '\r' || ch == '\n'); buffer_put_char(bin, ch); bytes++; } return bytes; } static void client_process_input(fd_set *readset) { int len; char buf[SSH_IOBUFSZ]; /* Read input from stdin. */ if (FD_ISSET(fileno(stdin), readset)) { /* Read as much as possible. */ len = read(fileno(stdin), buf, sizeof(buf)); if (len < 0 && (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) return; /* we'll try again later */ if (len <= 0) { /* * Received EOF or error. They are treated * similarly, except that an error message is printed * if it was an error condition. */ if (len < 0) { snprintf(buf, sizeof buf, "read: %.100s\r\n", strerror(errno)); buffer_append(&stderr_buffer, buf, strlen(buf)); } /* Mark that we have seen EOF. */ stdin_eof = 1; /* * Send an EOF message to the server unless there is * data in the buffer. If there is data in the * buffer, no message will be sent now. Code * elsewhere will send the EOF when the buffer * becomes empty if stdin_eof is set. */ if (buffer_len(&stdin_buffer) == 0) { packet_start(SSH_CMSG_EOF); packet_send(); } } else if (escape_char1 == SSH_ESCAPECHAR_NONE) { /* * Normal successful read, and no escape character. * Just append the data to buffer. */ buffer_append(&stdin_buffer, buf, len); } else { /* * Normal, successful read. But we have an escape * character and have to process the characters one * by one. */ if (process_escapes(NULL, &stdin_buffer, &stdout_buffer, &stderr_buffer, buf, len) == -1) return; } } } static void client_process_output(fd_set *writeset) { int len; char buf[100]; /* Write buffered output to stdout. */ if (FD_ISSET(fileno(stdout), writeset)) { /* Write as much data as possible. */ len = write(fileno(stdout), buffer_ptr(&stdout_buffer), buffer_len(&stdout_buffer)); if (len <= 0) { if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) len = 0; else { /* * An error or EOF was encountered. Put an * error message to stderr buffer. */ snprintf(buf, sizeof buf, "write stdout: %.50s\r\n", strerror(errno)); buffer_append(&stderr_buffer, buf, strlen(buf)); quit_pending = 1; return; } } /* Consume printed data from the buffer. */ buffer_consume(&stdout_buffer, len); } /* Write buffered output to stderr. */ if (FD_ISSET(fileno(stderr), writeset)) { /* Write as much data as possible. */ len = write(fileno(stderr), buffer_ptr(&stderr_buffer), buffer_len(&stderr_buffer)); if (len <= 0) { if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) len = 0; else { /* * EOF or error, but can't even print * error message. */ quit_pending = 1; return; } } /* Consume printed characters from the buffer. */ buffer_consume(&stderr_buffer, len); } } /* * Get packets from the connection input buffer, and process them as long as * there are packets available. * * Any unknown packets received during the actual * session cause the session to terminate. This is * intended to make debugging easier since no * confirmations are sent. Any compatible protocol * extensions must be negotiated during the * preparatory phase. */ static void client_process_buffered_input_packets(void) { dispatch_run(DISPATCH_NONBLOCK, &quit_pending, active_state); } /* scan buf[] for '~' before sending data to the peer */ /* Helper: allocate a new escape_filter_ctx and fill in its escape char */ void * client_new_escape_filter_ctx(int escape_char) { struct escape_filter_ctx *ret; ret = xcalloc(1, sizeof(*ret)); ret->escape_pending = 0; ret->escape_char = escape_char; return (void *)ret; } /* Free the escape filter context on channel free */ void client_filter_cleanup(int cid, void *ctx) { free(ctx); } int client_simple_escape_filter(Channel *c, char *buf, int len) { if (c->extended_usage != CHAN_EXTENDED_WRITE) return 0; return process_escapes(c, &c->input, &c->output, &c->extended, buf, len); } static void client_channel_closed(int id, void *arg) { channel_cancel_cleanup(id); session_closed = 1; leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); } /* * Implements the interactive session with the server. This is called after * the user has been authenticated, and a command has been started on the * remote host. If escape_char != SSH_ESCAPECHAR_NONE, it is the character * used as an escape character for terminating or suspending the session. */ int client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id) { fd_set *readset = NULL, *writeset = NULL; double start_time, total_time; int r, max_fd = 0, max_fd2 = 0, len; u_int64_t ibytes, obytes; u_int nalloc = 0; char buf[100]; debug("Entering interactive session."); if (options.control_master && !option_clear_or_none(options.control_path)) { debug("pledge: id"); if (pledge("stdio rpath wpath cpath unix inet dns recvfd proc exec id tty", NULL) == -1) fatal("%s pledge(): %s", __func__, strerror(errno)); } else if (options.forward_x11 || options.permit_local_command) { debug("pledge: exec"); if (pledge("stdio rpath wpath cpath unix inet dns proc exec tty", NULL) == -1) fatal("%s pledge(): %s", __func__, strerror(errno)); } else if (options.update_hostkeys) { debug("pledge: filesystem full"); if (pledge("stdio rpath wpath cpath unix inet dns proc tty", NULL) == -1) fatal("%s pledge(): %s", __func__, strerror(errno)); } else if (!option_clear_or_none(options.proxy_command) || fork_after_authentication_flag) { debug("pledge: proc"); if (pledge("stdio cpath unix inet dns proc tty", NULL) == -1) fatal("%s pledge(): %s", __func__, strerror(errno)); } else { debug("pledge: network"); if (pledge("stdio unix inet dns tty", NULL) == -1) fatal("%s pledge(): %s", __func__, strerror(errno)); } start_time = get_current_time(); /* Initialize variables. */ escape_pending1 = 0; last_was_cr = 1; exit_status = -1; stdin_eof = 0; buffer_high = 64 * 1024; connection_in = packet_get_connection_in(); connection_out = packet_get_connection_out(); max_fd = MAXIMUM(connection_in, connection_out); if (!compat20) { /* enable nonblocking unless tty */ if (!isatty(fileno(stdin))) set_nonblock(fileno(stdin)); if (!isatty(fileno(stdout))) set_nonblock(fileno(stdout)); if (!isatty(fileno(stderr))) set_nonblock(fileno(stderr)); max_fd = MAXIMUM(max_fd, fileno(stdin)); max_fd = MAXIMUM(max_fd, fileno(stdout)); max_fd = MAXIMUM(max_fd, fileno(stderr)); } quit_pending = 0; escape_char1 = escape_char_arg; /* Initialize buffers. */ buffer_init(&stdin_buffer); buffer_init(&stdout_buffer); buffer_init(&stderr_buffer); client_init_dispatch(); /* * Set signal handlers, (e.g. to restore non-blocking mode) * but don't overwrite SIG_IGN, matches behaviour from rsh(1) */ if (signal(SIGHUP, SIG_IGN) != SIG_IGN) signal(SIGHUP, signal_handler); if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, signal_handler); if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) signal(SIGQUIT, signal_handler); if (signal(SIGTERM, SIG_IGN) != SIG_IGN) signal(SIGTERM, signal_handler); signal(SIGWINCH, window_change_handler); if (have_pty) enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); if (compat20) { session_ident = ssh2_chan_id; if (session_ident != -1) { if (escape_char_arg != SSH_ESCAPECHAR_NONE) { channel_register_filter(session_ident, client_simple_escape_filter, NULL, client_filter_cleanup, client_new_escape_filter_ctx( escape_char_arg)); } channel_register_cleanup(session_ident, client_channel_closed, 0); } } else { /* Check if we should immediately send eof on stdin. */ client_check_initial_eof_on_stdin(); } /* Main loop of the client for the interactive session mode. */ while (!quit_pending) { /* Process buffered packets sent by the server. */ client_process_buffered_input_packets(); if (compat20 && session_closed && !channel_still_open()) break; if (ssh_packet_is_rekeying(active_state)) { debug("rekeying in progress"); } else if (need_rekeying) { /* manual rekey request */ debug("need rekeying"); if ((r = kex_start_rekex(active_state)) != 0) fatal("%s: kex_start_rekex: %s", __func__, ssh_err(r)); need_rekeying = 0; } else { /* * Make packets of buffered stdin data, and buffer * them for sending to the server. */ if (!compat20) client_make_packets_from_stdin_data(); /* * Make packets from buffered channel data, and * enqueue them for sending to the server. */ if (packet_not_very_much_data_to_write()) channel_output_poll(); /* * Check if the window size has changed, and buffer a * message about it to the server if so. */ client_check_window_change(); if (quit_pending) break; } /* * Wait until we have something to do (something becomes * available on one of the descriptors). */ max_fd2 = max_fd; client_wait_until_can_do_something(&readset, &writeset, &max_fd2, &nalloc, ssh_packet_is_rekeying(active_state)); if (quit_pending) break; /* Do channel operations unless rekeying in progress. */ if (!ssh_packet_is_rekeying(active_state)) channel_after_select(readset, writeset); /* Buffer input from the connection. */ client_process_net_input(readset); if (quit_pending) break; if (!compat20) { /* Buffer data from stdin */ client_process_input(readset); /* * Process output to stdout and stderr. Output to * the connection is processed elsewhere (above). */ client_process_output(writeset); } /* * Send as much buffered packet data as possible to the * sender. */ if (FD_ISSET(connection_out, writeset)) packet_write_poll(); /* * If we are a backgrounded control master, and the * timeout has expired without any active client * connections, then quit. */ if (control_persist_exit_time > 0) { if (monotime() >= control_persist_exit_time) { debug("ControlPersist timeout expired"); break; } } } free(readset); free(writeset); /* Terminate the session. */ /* Stop watching for window change. */ signal(SIGWINCH, SIG_DFL); if (compat20) { packet_start(SSH2_MSG_DISCONNECT); packet_put_int(SSH2_DISCONNECT_BY_APPLICATION); packet_put_cstring("disconnected by user"); packet_put_cstring(""); /* language tag */ packet_send(); packet_write_wait(); } channel_free_all(); if (have_pty) leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); /* restore blocking io */ if (!isatty(fileno(stdin))) unset_nonblock(fileno(stdin)); if (!isatty(fileno(stdout))) unset_nonblock(fileno(stdout)); if (!isatty(fileno(stderr))) unset_nonblock(fileno(stderr)); /* * If there was no shell or command requested, there will be no remote * exit status to be returned. In that case, clear error code if the * connection was deliberately terminated at this end. */ if (no_shell_flag && received_signal == SIGTERM) { received_signal = 0; exit_status = 0; } if (received_signal) fatal("Killed by signal %d.", (int) received_signal); /* * In interactive mode (with pseudo tty) display a message indicating * that the connection has been closed. */ if (have_pty && options.log_level != SYSLOG_LEVEL_QUIET) { snprintf(buf, sizeof buf, "Connection to %.64s closed.\r\n", host); buffer_append(&stderr_buffer, buf, strlen(buf)); } /* Output any buffered data for stdout. */ if (buffer_len(&stdout_buffer) > 0) { len = atomicio(vwrite, fileno(stdout), buffer_ptr(&stdout_buffer), buffer_len(&stdout_buffer)); if (len < 0 || (u_int)len != buffer_len(&stdout_buffer)) error("Write failed flushing stdout buffer."); else buffer_consume(&stdout_buffer, len); } /* Output any buffered data for stderr. */ if (buffer_len(&stderr_buffer) > 0) { len = atomicio(vwrite, fileno(stderr), buffer_ptr(&stderr_buffer), buffer_len(&stderr_buffer)); if (len < 0 || (u_int)len != buffer_len(&stderr_buffer)) error("Write failed flushing stderr buffer."); else buffer_consume(&stderr_buffer, len); } /* Clear and free any buffers. */ explicit_bzero(buf, sizeof(buf)); buffer_free(&stdin_buffer); buffer_free(&stdout_buffer); buffer_free(&stderr_buffer); /* Report bytes transferred, and transfer rates. */ total_time = get_current_time() - start_time; packet_get_bytes(&ibytes, &obytes); verbose("Transferred: sent %llu, received %llu bytes, in %.1f seconds", (unsigned long long)obytes, (unsigned long long)ibytes, total_time); if (total_time > 0) verbose("Bytes per second: sent %.1f, received %.1f", obytes / total_time, ibytes / total_time); /* Return the exit status of the program. */ debug("Exit status %d", exit_status); return exit_status; } /*********/ static int client_input_stdout_data(int type, u_int32_t seq, void *ctxt) { u_int data_len; char *data = packet_get_string(&data_len); packet_check_eom(); buffer_append(&stdout_buffer, data, data_len); explicit_bzero(data, data_len); free(data); return 0; } static int client_input_stderr_data(int type, u_int32_t seq, void *ctxt) { u_int data_len; char *data = packet_get_string(&data_len); packet_check_eom(); buffer_append(&stderr_buffer, data, data_len); explicit_bzero(data, data_len); free(data); return 0; } static int client_input_exit_status(int type, u_int32_t seq, void *ctxt) { exit_status = packet_get_int(); packet_check_eom(); /* Acknowledge the exit. */ packet_start(SSH_CMSG_EXIT_CONFIRMATION); packet_send(); /* * Must wait for packet to be sent since we are * exiting the loop. */ packet_write_wait(); /* Flag that we want to exit. */ quit_pending = 1; return 0; } static int client_input_agent_open(int type, u_int32_t seq, void *ctxt) { Channel *c = NULL; int r, remote_id, sock; /* Read the remote channel number from the message. */ remote_id = packet_get_int(); packet_check_eom(); /* * Get a connection to the local authentication agent (this may again * get forwarded). */ if ((r = ssh_get_authentication_socket(&sock)) != 0 && r != SSH_ERR_AGENT_NOT_PRESENT) debug("%s: ssh_get_authentication_socket: %s", __func__, ssh_err(r)); /* * If we could not connect the agent, send an error message back to * the server. This should never happen unless the agent dies, * because authentication forwarding is only enabled if we have an * agent. */ if (sock >= 0) { c = channel_new("", SSH_CHANNEL_OPEN, sock, sock, -1, 0, 0, 0, "authentication agent connection", 1); c->remote_id = remote_id; c->force_drain = 1; } if (c == NULL) { packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(remote_id); } else { /* Send a confirmation to the remote host. */ debug("Forwarding authentication connection."); packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION); packet_put_int(remote_id); packet_put_int(c->self); } packet_send(); return 0; } static Channel * client_request_forwarded_tcpip(const char *request_type, int rchan, u_int rwindow, u_int rmaxpack) { Channel *c = NULL; struct sshbuf *b = NULL; char *listen_address, *originator_address; u_short listen_port, originator_port; int r; /* Get rest of the packet */ listen_address = packet_get_string(NULL); listen_port = packet_get_int(); originator_address = packet_get_string(NULL); originator_port = packet_get_int(); packet_check_eom(); debug("%s: listen %s port %d, originator %s port %d", __func__, listen_address, listen_port, originator_address, originator_port); c = channel_connect_by_listen_address(listen_address, listen_port, "forwarded-tcpip", originator_address); if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) { if ((b = sshbuf_new()) == NULL) { error("%s: alloc reply", __func__); goto out; } /* reconstruct and send to muxclient */ if ((r = sshbuf_put_u8(b, 0)) != 0 || /* padlen */ (r = sshbuf_put_u8(b, SSH2_MSG_CHANNEL_OPEN)) != 0 || (r = sshbuf_put_cstring(b, request_type)) != 0 || (r = sshbuf_put_u32(b, rchan)) != 0 || (r = sshbuf_put_u32(b, rwindow)) != 0 || (r = sshbuf_put_u32(b, rmaxpack)) != 0 || (r = sshbuf_put_cstring(b, listen_address)) != 0 || (r = sshbuf_put_u32(b, listen_port)) != 0 || (r = sshbuf_put_cstring(b, originator_address)) != 0 || (r = sshbuf_put_u32(b, originator_port)) != 0 || (r = sshbuf_put_stringb(&c->output, b)) != 0) { error("%s: compose for muxclient %s", __func__, ssh_err(r)); goto out; } } out: sshbuf_free(b); free(originator_address); free(listen_address); return c; } static Channel * client_request_forwarded_streamlocal(const char *request_type, int rchan) { Channel *c = NULL; char *listen_path; /* Get the remote path. */ listen_path = packet_get_string(NULL); /* XXX: Skip reserved field for now. */ if (packet_get_string_ptr(NULL) == NULL) fatal("%s: packet_get_string_ptr failed", __func__); packet_check_eom(); debug("%s: %s", __func__, listen_path); c = channel_connect_by_listen_path(listen_path, "forwarded-streamlocal@openssh.com", "forwarded-streamlocal"); free(listen_path); return c; } static Channel * client_request_x11(const char *request_type, int rchan) { Channel *c = NULL; char *originator; u_short originator_port; int sock; if (!options.forward_x11) { error("Warning: ssh server tried X11 forwarding."); error("Warning: this is probably a break-in attempt by a " "malicious server."); return NULL; } if (x11_refuse_time != 0 && (u_int)monotime() >= x11_refuse_time) { verbose("Rejected X11 connection after ForwardX11Timeout " "expired"); return NULL; } originator = packet_get_string(NULL); if (datafellows & SSH_BUG_X11FWD) { debug2("buggy server: x11 request w/o originator_port"); originator_port = 0; } else { originator_port = packet_get_int(); } packet_check_eom(); /* XXX check permission */ debug("client_request_x11: request from %s %d", originator, originator_port); free(originator); sock = x11_connect_display(); if (sock < 0) return NULL; c = channel_new("x11", SSH_CHANNEL_X11_OPEN, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "x11", 1); c->force_drain = 1; return c; } static Channel * client_request_agent(const char *request_type, int rchan) { Channel *c = NULL; int r, sock; if (!options.forward_agent) { error("Warning: ssh server tried agent forwarding."); error("Warning: this is probably a break-in attempt by a " "malicious server."); return NULL; } if ((r = ssh_get_authentication_socket(&sock)) != 0) { if (r != SSH_ERR_AGENT_NOT_PRESENT) debug("%s: ssh_get_authentication_socket: %s", __func__, ssh_err(r)); return NULL; } c = channel_new("authentication agent connection", SSH_CHANNEL_OPEN, sock, sock, -1, CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "authentication agent connection", 1); c->force_drain = 1; return c; } int client_request_tun_fwd(int tun_mode, int local_tun, int remote_tun) { Channel *c; int fd; if (tun_mode == SSH_TUNMODE_NO) return 0; if (!compat20) { error("Tunnel forwarding is not supported for protocol 1"); return -1; } debug("Requesting tun unit %d in mode %d", local_tun, tun_mode); /* Open local tunnel device */ if ((fd = tun_open(local_tun, tun_mode)) == -1) { error("Tunnel device open failed."); return -1; } c = channel_new("tun", SSH_CHANNEL_OPENING, fd, fd, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); c->datagram = 1; #if defined(SSH_TUN_FILTER) if (options.tun_open == SSH_TUNMODE_POINTOPOINT) channel_register_filter(c->self, sys_tun_infilter, sys_tun_outfilter, NULL, NULL); #endif packet_start(SSH2_MSG_CHANNEL_OPEN); packet_put_cstring("tun@openssh.com"); packet_put_int(c->self); packet_put_int(c->local_window_max); packet_put_int(c->local_maxpacket); packet_put_int(tun_mode); packet_put_int(remote_tun); packet_send(); return 0; } /* XXXX move to generic input handler */ static int client_input_channel_open(int type, u_int32_t seq, void *ctxt) { Channel *c = NULL; char *ctype; int rchan; u_int rmaxpack, rwindow, len; ctype = packet_get_string(&len); rchan = packet_get_int(); rwindow = packet_get_int(); rmaxpack = packet_get_int(); debug("client_input_channel_open: ctype %s rchan %d win %d max %d", ctype, rchan, rwindow, rmaxpack); if (strcmp(ctype, "forwarded-tcpip") == 0) { c = client_request_forwarded_tcpip(ctype, rchan, rwindow, rmaxpack); } else if (strcmp(ctype, "forwarded-streamlocal@openssh.com") == 0) { c = client_request_forwarded_streamlocal(ctype, rchan); } else if (strcmp(ctype, "x11") == 0) { c = client_request_x11(ctype, rchan); } else if (strcmp(ctype, "auth-agent@openssh.com") == 0) { c = client_request_agent(ctype, rchan); } if (c != NULL && c->type == SSH_CHANNEL_MUX_CLIENT) { debug3("proxied to downstream: %s", ctype); } else if (c != NULL) { debug("confirm %s", ctype); c->remote_id = rchan; c->remote_window = rwindow; c->remote_maxpacket = rmaxpack; if (c->type != SSH_CHANNEL_CONNECTING) { packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); packet_put_int(c->remote_id); packet_put_int(c->self); packet_put_int(c->local_window); packet_put_int(c->local_maxpacket); packet_send(); } } else { debug("failure %s", ctype); packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(rchan); packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); if (!(datafellows & SSH_BUG_OPENFAILURE)) { packet_put_cstring("open failed"); packet_put_cstring(""); } packet_send(); } free(ctype); return 0; } static int client_input_channel_req(int type, u_int32_t seq, void *ctxt) { Channel *c = NULL; int exitval, id, reply, success = 0; char *rtype; id = packet_get_int(); c = channel_lookup(id); if (channel_proxy_upstream(c, type, seq, ctxt)) return 0; rtype = packet_get_string(NULL); reply = packet_get_char(); debug("client_input_channel_req: channel %d rtype %s reply %d", id, rtype, reply); if (id == -1) { error("client_input_channel_req: request for channel -1"); } else if (c == NULL) { error("client_input_channel_req: channel %d: " "unknown channel", id); } else if (strcmp(rtype, "eow@openssh.com") == 0) { packet_check_eom(); chan_rcvd_eow(c); } else if (strcmp(rtype, "exit-status") == 0) { exitval = packet_get_int(); if (c->ctl_chan != -1) { mux_exit_message(c, exitval); success = 1; } else if (id == session_ident) { /* Record exit value of local session */ success = 1; exit_status = exitval; } else { /* Probably for a mux channel that has already closed */ debug("%s: no sink for exit-status on channel %d", __func__, id); } packet_check_eom(); } if (reply && c != NULL && !(c->flags & CHAN_CLOSE_SENT)) { packet_start(success ? SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); packet_put_int(c->remote_id); packet_send(); } free(rtype); return 0; } struct hostkeys_update_ctx { /* The hostname and (optionally) IP address string for the server */ char *host_str, *ip_str; /* * Keys received from the server and a flag for each indicating * whether they already exist in known_hosts. * keys_seen is filled in by hostkeys_find() and later (for new * keys) by client_global_hostkeys_private_confirm(). */ struct sshkey **keys; int *keys_seen; size_t nkeys; size_t nnew; /* * Keys that are in known_hosts, but were not present in the update * from the server (i.e. scheduled to be deleted). * Filled in by hostkeys_find(). */ struct sshkey **old_keys; size_t nold; }; static void hostkeys_update_ctx_free(struct hostkeys_update_ctx *ctx) { size_t i; if (ctx == NULL) return; for (i = 0; i < ctx->nkeys; i++) sshkey_free(ctx->keys[i]); free(ctx->keys); free(ctx->keys_seen); for (i = 0; i < ctx->nold; i++) sshkey_free(ctx->old_keys[i]); free(ctx->old_keys); free(ctx->host_str); free(ctx->ip_str); free(ctx); } static int hostkeys_find(struct hostkey_foreach_line *l, void *_ctx) { struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; size_t i; struct sshkey **tmp; if (l->status != HKF_STATUS_MATCHED || l->key == NULL || l->key->type == KEY_RSA1) return 0; /* Mark off keys we've already seen for this host */ for (i = 0; i < ctx->nkeys; i++) { if (sshkey_equal(l->key, ctx->keys[i])) { debug3("%s: found %s key at %s:%ld", __func__, sshkey_ssh_name(ctx->keys[i]), l->path, l->linenum); ctx->keys_seen[i] = 1; return 0; } } /* This line contained a key that not offered by the server */ debug3("%s: deprecated %s key at %s:%ld", __func__, sshkey_ssh_name(l->key), l->path, l->linenum); if ((tmp = reallocarray(ctx->old_keys, ctx->nold + 1, sizeof(*ctx->old_keys))) == NULL) fatal("%s: reallocarray failed nold = %zu", __func__, ctx->nold); ctx->old_keys = tmp; ctx->old_keys[ctx->nold++] = l->key; l->key = NULL; return 0; } static void update_known_hosts(struct hostkeys_update_ctx *ctx) { int r, was_raw = 0; int loglevel = options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK ? SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_VERBOSE; char *fp, *response; size_t i; for (i = 0; i < ctx->nkeys; i++) { if (ctx->keys_seen[i] != 2) continue; if ((fp = sshkey_fingerprint(ctx->keys[i], options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) fatal("%s: sshkey_fingerprint failed", __func__); do_log2(loglevel, "Learned new hostkey: %s %s", sshkey_type(ctx->keys[i]), fp); free(fp); } for (i = 0; i < ctx->nold; i++) { if ((fp = sshkey_fingerprint(ctx->old_keys[i], options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) fatal("%s: sshkey_fingerprint failed", __func__); do_log2(loglevel, "Deprecating obsolete hostkey: %s %s", sshkey_type(ctx->old_keys[i]), fp); free(fp); } if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK) { if (get_saved_tio() != NULL) { leave_raw_mode(1); was_raw = 1; } response = NULL; for (i = 0; !quit_pending && i < 3; i++) { free(response); response = read_passphrase("Accept updated hostkeys? " "(yes/no): ", RP_ECHO); if (strcasecmp(response, "yes") == 0) break; else if (quit_pending || response == NULL || strcasecmp(response, "no") == 0) { options.update_hostkeys = 0; break; } else { do_log2(loglevel, "Please enter " "\"yes\" or \"no\""); } } if (quit_pending || i >= 3 || response == NULL) options.update_hostkeys = 0; free(response); if (was_raw) enter_raw_mode(1); } /* * Now that all the keys are verified, we can go ahead and replace * them in known_hosts (assuming SSH_UPDATE_HOSTKEYS_ASK didn't * cancel the operation). */ if (options.update_hostkeys != 0 && (r = hostfile_replace_entries(options.user_hostfiles[0], ctx->host_str, ctx->ip_str, ctx->keys, ctx->nkeys, options.hash_known_hosts, 0, options.fingerprint_hash)) != 0) error("%s: hostfile_replace_entries failed: %s", __func__, ssh_err(r)); } static void client_global_hostkeys_private_confirm(int type, u_int32_t seq, void *_ctx) { struct ssh *ssh = active_state; /* XXX */ struct hostkeys_update_ctx *ctx = (struct hostkeys_update_ctx *)_ctx; size_t i, ndone; struct sshbuf *signdata; int r; const u_char *sig; size_t siglen; if (ctx->nnew == 0) fatal("%s: ctx->nnew == 0", __func__); /* sanity */ if (type != SSH2_MSG_REQUEST_SUCCESS) { error("Server failed to confirm ownership of " "private host keys"); hostkeys_update_ctx_free(ctx); return; } if ((signdata = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); /* Don't want to accidentally accept an unbound signature */ if (ssh->kex->session_id_len == 0) fatal("%s: ssh->kex->session_id_len == 0", __func__); /* * Expect a signature for each of the ctx->nnew private keys we * haven't seen before. They will be in the same order as the * ctx->keys where the corresponding ctx->keys_seen[i] == 0. */ for (ndone = i = 0; i < ctx->nkeys; i++) { if (ctx->keys_seen[i]) continue; /* Prepare data to be signed: session ID, unique string, key */ sshbuf_reset(signdata); if ( (r = sshbuf_put_cstring(signdata, "hostkeys-prove-00@openssh.com")) != 0 || (r = sshbuf_put_string(signdata, ssh->kex->session_id, ssh->kex->session_id_len)) != 0 || (r = sshkey_puts(ctx->keys[i], signdata)) != 0) fatal("%s: failed to prepare signature: %s", __func__, ssh_err(r)); /* Extract and verify signature */ if ((r = sshpkt_get_string_direct(ssh, &sig, &siglen)) != 0) { error("%s: couldn't parse message: %s", __func__, ssh_err(r)); goto out; } if ((r = sshkey_verify(ctx->keys[i], sig, siglen, sshbuf_ptr(signdata), sshbuf_len(signdata), 0)) != 0) { error("%s: server gave bad signature for %s key %zu", __func__, sshkey_type(ctx->keys[i]), i); goto out; } /* Key is good. Mark it as 'seen' */ ctx->keys_seen[i] = 2; ndone++; } if (ndone != ctx->nnew) fatal("%s: ndone != ctx->nnew (%zu / %zu)", __func__, ndone, ctx->nnew); /* Shouldn't happen */ ssh_packet_check_eom(ssh); /* Make the edits to known_hosts */ update_known_hosts(ctx); out: hostkeys_update_ctx_free(ctx); } +/* + * Returns non-zero if the key is accepted by HostkeyAlgorithms. + * Made slightly less trivial by the multiple RSA signature algorithm names. + */ +static int +key_accepted_by_hostkeyalgs(const struct sshkey *key) +{ + const char *ktype = sshkey_ssh_name(key); + const char *hostkeyalgs = options.hostkeyalgorithms != NULL ? + options.hostkeyalgorithms : KEX_DEFAULT_PK_ALG; + + if (key == NULL || key->type == KEY_UNSPEC) + return 0; + if (key->type == KEY_RSA && + (match_pattern_list("rsa-sha2-256", hostkeyalgs, 0) == 1 || + match_pattern_list("rsa-sha2-512", hostkeyalgs, 0) == 1)) + return 1; + return match_pattern_list(ktype, hostkeyalgs, 0) == 1; +} + /* * Handle hostkeys-00@openssh.com global request to inform the client of all * the server's hostkeys. The keys are checked against the user's * HostkeyAlgorithms preference before they are accepted. */ static int client_input_hostkeys(void) { struct ssh *ssh = active_state; /* XXX */ const u_char *blob = NULL; size_t i, len = 0; struct sshbuf *buf = NULL; struct sshkey *key = NULL, **tmp; int r; char *fp; static int hostkeys_seen = 0; /* XXX use struct ssh */ extern struct sockaddr_storage hostaddr; /* XXX from ssh.c */ struct hostkeys_update_ctx *ctx = NULL; if (hostkeys_seen) fatal("%s: server already sent hostkeys", __func__); if (options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK && options.batch_mode) return 1; /* won't ask in batchmode, so don't even try */ if (!options.update_hostkeys || options.num_user_hostfiles <= 0) return 1; ctx = xcalloc(1, sizeof(*ctx)); while (ssh_packet_remaining(ssh) > 0) { sshkey_free(key); key = NULL; if ((r = sshpkt_get_string_direct(ssh, &blob, &len)) != 0) { error("%s: couldn't parse message: %s", __func__, ssh_err(r)); goto out; } if ((r = sshkey_from_blob(blob, len, &key)) != 0) { error("%s: parse key: %s", __func__, ssh_err(r)); goto out; } fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT); debug3("%s: received %s key %s", __func__, sshkey_type(key), fp); free(fp); - /* Check that the key is accepted in HostkeyAlgorithms */ - if (match_pattern_list(sshkey_ssh_name(key), - options.hostkeyalgorithms ? options.hostkeyalgorithms : - KEX_DEFAULT_PK_ALG, 0) != 1) { + if (!key_accepted_by_hostkeyalgs(key)) { debug3("%s: %s key not permitted by HostkeyAlgorithms", __func__, sshkey_ssh_name(key)); continue; } /* Skip certs */ if (sshkey_is_cert(key)) { debug3("%s: %s key is a certificate; skipping", __func__, sshkey_ssh_name(key)); continue; } /* Ensure keys are unique */ for (i = 0; i < ctx->nkeys; i++) { if (sshkey_equal(key, ctx->keys[i])) { error("%s: received duplicated %s host key", __func__, sshkey_ssh_name(key)); goto out; } } /* Key is good, record it */ if ((tmp = reallocarray(ctx->keys, ctx->nkeys + 1, sizeof(*ctx->keys))) == NULL) fatal("%s: reallocarray failed nkeys = %zu", __func__, ctx->nkeys); ctx->keys = tmp; ctx->keys[ctx->nkeys++] = key; key = NULL; } if (ctx->nkeys == 0) { debug("%s: server sent no hostkeys", __func__); goto out; } if ((ctx->keys_seen = calloc(ctx->nkeys, sizeof(*ctx->keys_seen))) == NULL) fatal("%s: calloc failed", __func__); get_hostfile_hostname_ipaddr(host, options.check_host_ip ? (struct sockaddr *)&hostaddr : NULL, options.port, &ctx->host_str, options.check_host_ip ? &ctx->ip_str : NULL); /* Find which keys we already know about. */ if ((r = hostkeys_foreach(options.user_hostfiles[0], hostkeys_find, ctx, ctx->host_str, ctx->ip_str, HKF_WANT_PARSE_KEY|HKF_WANT_MATCH)) != 0) { error("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r)); goto out; } /* Figure out if we have any new keys to add */ ctx->nnew = 0; for (i = 0; i < ctx->nkeys; i++) { if (!ctx->keys_seen[i]) ctx->nnew++; } debug3("%s: %zu keys from server: %zu new, %zu retained. %zu to remove", __func__, ctx->nkeys, ctx->nnew, ctx->nkeys - ctx->nnew, ctx->nold); if (ctx->nnew == 0 && ctx->nold != 0) { /* We have some keys to remove. Just do it. */ update_known_hosts(ctx); } else if (ctx->nnew != 0) { /* * We have received hitherto-unseen keys from the server. * Ask the server to confirm ownership of the private halves. */ debug3("%s: asking server to prove ownership for %zu keys", __func__, ctx->nnew); if ((r = sshpkt_start(ssh, SSH2_MSG_GLOBAL_REQUEST)) != 0 || (r = sshpkt_put_cstring(ssh, "hostkeys-prove-00@openssh.com")) != 0 || (r = sshpkt_put_u8(ssh, 1)) != 0) /* bool: want reply */ fatal("%s: cannot prepare packet: %s", __func__, ssh_err(r)); if ((buf = sshbuf_new()) == NULL) fatal("%s: sshbuf_new", __func__); for (i = 0; i < ctx->nkeys; i++) { if (ctx->keys_seen[i]) continue; sshbuf_reset(buf); if ((r = sshkey_putb(ctx->keys[i], buf)) != 0) fatal("%s: sshkey_putb: %s", __func__, ssh_err(r)); if ((r = sshpkt_put_stringb(ssh, buf)) != 0) fatal("%s: sshpkt_put_string: %s", __func__, ssh_err(r)); } if ((r = sshpkt_send(ssh)) != 0) fatal("%s: sshpkt_send: %s", __func__, ssh_err(r)); client_register_global_confirm( client_global_hostkeys_private_confirm, ctx); ctx = NULL; /* will be freed in callback */ } /* Success */ out: hostkeys_update_ctx_free(ctx); sshkey_free(key); sshbuf_free(buf); /* * NB. Return success for all cases. The server doesn't need to know * what the client does with its hosts file. */ return 1; } static int client_input_global_request(int type, u_int32_t seq, void *ctxt) { char *rtype; int want_reply; int success = 0; rtype = packet_get_cstring(NULL); want_reply = packet_get_char(); debug("client_input_global_request: rtype %s want_reply %d", rtype, want_reply); if (strcmp(rtype, "hostkeys-00@openssh.com") == 0) success = client_input_hostkeys(); if (want_reply) { packet_start(success ? SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); packet_send(); packet_write_wait(); } free(rtype); return 0; } void client_session2_setup(int id, int want_tty, int want_subsystem, const char *term, struct termios *tiop, int in_fd, Buffer *cmd, char **env) { int len; Channel *c = NULL; debug2("%s: id %d", __func__, id); if ((c = channel_lookup(id)) == NULL) fatal("client_session2_setup: channel %d: unknown channel", id); packet_set_interactive(want_tty, options.ip_qos_interactive, options.ip_qos_bulk); if (want_tty) { struct winsize ws; /* Store window size in the packet. */ if (ioctl(in_fd, TIOCGWINSZ, &ws) < 0) memset(&ws, 0, sizeof(ws)); channel_request_start(id, "pty-req", 1); client_expect_confirm(id, "PTY allocation", CONFIRM_TTY); packet_put_cstring(term != NULL ? term : ""); packet_put_int((u_int)ws.ws_col); packet_put_int((u_int)ws.ws_row); packet_put_int((u_int)ws.ws_xpixel); packet_put_int((u_int)ws.ws_ypixel); if (tiop == NULL) tiop = get_saved_tio(); tty_make_modes(-1, tiop); packet_send(); /* XXX wait for reply */ c->client_tty = 1; } /* Transfer any environment variables from client to server */ if (options.num_send_env != 0 && env != NULL) { int i, j, matched; char *name, *val; debug("Sending environment."); for (i = 0; env[i] != NULL; i++) { /* Split */ name = xstrdup(env[i]); if ((val = strchr(name, '=')) == NULL) { free(name); continue; } *val++ = '\0'; matched = 0; for (j = 0; j < options.num_send_env; j++) { if (match_pattern(name, options.send_env[j])) { matched = 1; break; } } if (!matched) { debug3("Ignored env %s", name); free(name); continue; } debug("Sending env %s = %s", name, val); channel_request_start(id, "env", 0); packet_put_cstring(name); packet_put_cstring(val); packet_send(); free(name); } } len = buffer_len(cmd); if (len > 0) { if (len > 900) len = 900; if (want_subsystem) { debug("Sending subsystem: %.*s", len, (u_char*)buffer_ptr(cmd)); channel_request_start(id, "subsystem", 1); client_expect_confirm(id, "subsystem", CONFIRM_CLOSE); } else { debug("Sending command: %.*s", len, (u_char*)buffer_ptr(cmd)); channel_request_start(id, "exec", 1); client_expect_confirm(id, "exec", CONFIRM_CLOSE); } packet_put_string(buffer_ptr(cmd), buffer_len(cmd)); packet_send(); } else { channel_request_start(id, "shell", 1); client_expect_confirm(id, "shell", CONFIRM_CLOSE); packet_send(); } } static void client_init_dispatch_20(void) { dispatch_init(&dispatch_protocol_error); dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); dispatch_set(SSH2_MSG_CHANNEL_OPEN, &client_input_channel_open); dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &client_input_channel_req); dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, &channel_input_status_confirm); dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &channel_input_status_confirm); dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &client_input_global_request); /* rekeying */ dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); /* global request reply messages */ dispatch_set(SSH2_MSG_REQUEST_FAILURE, &client_global_request_reply); dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &client_global_request_reply); } static void client_init_dispatch_13(void) { dispatch_init(NULL); dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close); dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation); dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data); dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open); dispatch_set(SSH_SMSG_EXITSTATUS, &client_input_exit_status); dispatch_set(SSH_SMSG_STDERR_DATA, &client_input_stderr_data); dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data); dispatch_set(SSH_SMSG_AGENT_OPEN, options.forward_agent ? &client_input_agent_open : &deny_input_open); dispatch_set(SSH_SMSG_X11_OPEN, options.forward_x11 ? &x11_input_open : &deny_input_open); } static void client_init_dispatch_15(void) { client_init_dispatch_13(); dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof); dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose); } static void client_init_dispatch(void) { if (compat20) client_init_dispatch_20(); else if (compat13) client_init_dispatch_13(); else client_init_dispatch_15(); } void client_stop_mux(void) { if (options.control_path != NULL && muxserver_sock != -1) unlink(options.control_path); /* * If we are in persist mode, or don't have a shell, signal that we * should close when all active channels are closed. */ if (options.control_persist || no_shell_flag) { session_closed = 1; setproctitle("[stopped mux]"); } } /* client specific fatal cleanup */ void cleanup_exit(int i) { leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); leave_non_blocking(); if (options.control_path != NULL && muxserver_sock != -1) unlink(options.control_path); ssh_kill_proxy_command(); _exit(i); } diff --git a/crypto/openssh/compat.c b/crypto/openssh/compat.c index 69a104fbfe5b..1e80cfa9a73a 100644 --- a/crypto/openssh/compat.c +++ b/crypto/openssh/compat.c @@ -1,325 +1,303 @@ -/* $OpenBSD: compat.c,v 1.99 2016/05/24 02:31:57 dtucker Exp $ */ +/* $OpenBSD: compat.c,v 1.100 2017/02/03 23:01:19 djm Exp $ */ /* * Copyright (c) 1999, 2000, 2001, 2002 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include "xmalloc.h" #include "buffer.h" #include "packet.h" #include "compat.h" #include "log.h" #include "match.h" +#include "kex.h" int compat13 = 0; int compat20 = 0; int datafellows = 0; void enable_compat20(void) { if (compat20) return; debug("Enabling compatibility mode for protocol 2.0"); compat20 = 1; } void enable_compat13(void) { debug("Enabling compatibility mode for protocol 1.3"); compat13 = 1; } /* datafellows bug compatibility */ u_int compat_datafellows(const char *version) { int i; static struct { char *pat; int bugs; } check[] = { { "OpenSSH-2.0*," "OpenSSH-2.1*," "OpenSSH_2.1*," "OpenSSH_2.2*", SSH_OLD_SESSIONID|SSH_BUG_BANNER| SSH_OLD_DHGEX|SSH_BUG_NOREKEY| SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR}, { "OpenSSH_2.3.0*", SSH_BUG_BANNER|SSH_BUG_BIGENDIANAES| SSH_OLD_DHGEX|SSH_BUG_NOREKEY| SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR}, { "OpenSSH_2.3.*", SSH_BUG_BIGENDIANAES|SSH_OLD_DHGEX| SSH_BUG_NOREKEY|SSH_BUG_EXTEOF| SSH_OLD_FORWARD_ADDR}, { "OpenSSH_2.5.0p1*," "OpenSSH_2.5.1p1*", SSH_BUG_BIGENDIANAES|SSH_OLD_DHGEX| SSH_BUG_NOREKEY|SSH_BUG_EXTEOF| SSH_OLD_FORWARD_ADDR}, { "OpenSSH_2.5.0*," "OpenSSH_2.5.1*," "OpenSSH_2.5.2*", SSH_OLD_DHGEX|SSH_BUG_NOREKEY| SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR}, { "OpenSSH_2.5.3*", SSH_BUG_NOREKEY|SSH_BUG_EXTEOF| SSH_OLD_FORWARD_ADDR}, { "OpenSSH_2.*," "OpenSSH_3.0*," "OpenSSH_3.1*", SSH_BUG_EXTEOF|SSH_OLD_FORWARD_ADDR}, { "OpenSSH_3.*", SSH_OLD_FORWARD_ADDR }, { "Sun_SSH_1.0*", SSH_BUG_NOREKEY|SSH_BUG_EXTEOF}, { "OpenSSH_4*", 0 }, { "OpenSSH_5*", SSH_NEW_OPENSSH|SSH_BUG_DYNAMIC_RPORT}, { "OpenSSH_6.6.1*", SSH_NEW_OPENSSH}, { "OpenSSH_6.5*," "OpenSSH_6.6*", SSH_NEW_OPENSSH|SSH_BUG_CURVE25519PAD}, { "OpenSSH*", SSH_NEW_OPENSSH }, { "*MindTerm*", 0 }, { "2.1.0*", SSH_BUG_SIGBLOB|SSH_BUG_HMAC| SSH_OLD_SESSIONID|SSH_BUG_DEBUG| SSH_BUG_RSASIGMD5|SSH_BUG_HBSERVICE| SSH_BUG_FIRSTKEX }, { "2.1 *", SSH_BUG_SIGBLOB|SSH_BUG_HMAC| SSH_OLD_SESSIONID|SSH_BUG_DEBUG| SSH_BUG_RSASIGMD5|SSH_BUG_HBSERVICE| SSH_BUG_FIRSTKEX }, { "2.0.13*," "2.0.14*," "2.0.15*," "2.0.16*," "2.0.17*," "2.0.18*," "2.0.19*", SSH_BUG_SIGBLOB|SSH_BUG_HMAC| SSH_OLD_SESSIONID|SSH_BUG_DEBUG| SSH_BUG_PKSERVICE|SSH_BUG_X11FWD| SSH_BUG_PKOK|SSH_BUG_RSASIGMD5| SSH_BUG_HBSERVICE|SSH_BUG_OPENFAILURE| SSH_BUG_DUMMYCHAN|SSH_BUG_FIRSTKEX }, { "2.0.11*," "2.0.12*", SSH_BUG_SIGBLOB|SSH_BUG_HMAC| SSH_OLD_SESSIONID|SSH_BUG_DEBUG| SSH_BUG_PKSERVICE|SSH_BUG_X11FWD| SSH_BUG_PKAUTH|SSH_BUG_PKOK| SSH_BUG_RSASIGMD5|SSH_BUG_OPENFAILURE| SSH_BUG_DUMMYCHAN|SSH_BUG_FIRSTKEX }, { "2.0.*", SSH_BUG_SIGBLOB|SSH_BUG_HMAC| SSH_OLD_SESSIONID|SSH_BUG_DEBUG| SSH_BUG_PKSERVICE|SSH_BUG_X11FWD| SSH_BUG_PKAUTH|SSH_BUG_PKOK| SSH_BUG_RSASIGMD5|SSH_BUG_OPENFAILURE| SSH_BUG_DERIVEKEY|SSH_BUG_DUMMYCHAN| SSH_BUG_FIRSTKEX }, { "2.2.0*," "2.3.0*", SSH_BUG_HMAC|SSH_BUG_DEBUG| SSH_BUG_RSASIGMD5|SSH_BUG_FIRSTKEX }, { "2.3.*", SSH_BUG_DEBUG|SSH_BUG_RSASIGMD5| SSH_BUG_FIRSTKEX }, { "2.4", SSH_OLD_SESSIONID }, /* Van Dyke */ { "2.*", SSH_BUG_DEBUG|SSH_BUG_FIRSTKEX| SSH_BUG_RFWD_ADDR }, { "3.0.*", SSH_BUG_DEBUG }, { "3.0 SecureCRT*", SSH_OLD_SESSIONID }, { "1.7 SecureFX*", SSH_OLD_SESSIONID }, { "1.2.18*," "1.2.19*," "1.2.20*," "1.2.21*," "1.2.22*", SSH_BUG_IGNOREMSG }, { "1.3.2*", /* F-Secure */ SSH_BUG_IGNOREMSG }, { "Cisco-1.*", SSH_BUG_DHGEX_LARGE| SSH_BUG_HOSTKEYS }, { "*SSH Compatible Server*", /* Netscreen */ SSH_BUG_PASSWORDPAD }, { "*OSU_0*," "OSU_1.0*," "OSU_1.1*," "OSU_1.2*," "OSU_1.3*," "OSU_1.4*," "OSU_1.5alpha1*," "OSU_1.5alpha2*," "OSU_1.5alpha3*", SSH_BUG_PASSWORDPAD }, { "*SSH_Version_Mapper*", SSH_BUG_SCANNER }, { "PuTTY_Local:*," /* dev versions < Sep 2014 */ "PuTTY-Release-0.5*," /* 0.50-0.57, DH-GEX in >=0.52 */ "PuTTY_Release_0.5*," /* 0.58-0.59 */ "PuTTY_Release_0.60*," "PuTTY_Release_0.61*," "PuTTY_Release_0.62*," "PuTTY_Release_0.63*," "PuTTY_Release_0.64*", SSH_OLD_DHGEX }, { "FuTTY*", SSH_OLD_DHGEX }, /* Putty Fork */ { "Probe-*", SSH_BUG_PROBE }, { "TeraTerm SSH*," "TTSSH/1.5.*," "TTSSH/2.1*," "TTSSH/2.2*," "TTSSH/2.3*," "TTSSH/2.4*," "TTSSH/2.5*," "TTSSH/2.6*," "TTSSH/2.70*," "TTSSH/2.71*," "TTSSH/2.72*", SSH_BUG_HOSTKEYS }, { "WinSCP_release_4*," "WinSCP_release_5.0*," "WinSCP_release_5.1*," "WinSCP_release_5.5*," "WinSCP_release_5.6*," "WinSCP_release_5.7," "WinSCP_release_5.7.1," "WinSCP_release_5.7.2," "WinSCP_release_5.7.3," "WinSCP_release_5.7.4", SSH_OLD_DHGEX }, { NULL, 0 } }; /* process table, return first match */ for (i = 0; check[i].pat; i++) { if (match_pattern_list(version, check[i].pat, 0) == 1) { debug("match: %s pat %s compat 0x%08x", version, check[i].pat, check[i].bugs); datafellows = check[i].bugs; /* XXX for now */ return check[i].bugs; } } debug("no match: %s", version); return 0; } #define SEP "," int proto_spec(const char *spec) { char *s, *p, *q; int ret = SSH_PROTO_UNKNOWN; if (spec == NULL) return ret; q = s = strdup(spec); if (s == NULL) return ret; for ((p = strsep(&q, SEP)); p && *p != '\0'; (p = strsep(&q, SEP))) { switch (atoi(p)) { case 1: #ifdef WITH_SSH1 if (ret == SSH_PROTO_UNKNOWN) ret |= SSH_PROTO_1_PREFERRED; ret |= SSH_PROTO_1; #endif break; case 2: ret |= SSH_PROTO_2; break; default: logit("ignoring bad proto spec: '%s'.", p); break; } } free(s); return ret; } -/* - * Filters a proposal string, excluding any algorithm matching the 'filter' - * pattern list. - */ -static char * -filter_proposal(char *proposal, const char *filter) -{ - Buffer b; - char *orig_prop, *fix_prop; - char *cp, *tmp; - - buffer_init(&b); - tmp = orig_prop = xstrdup(proposal); - while ((cp = strsep(&tmp, ",")) != NULL) { - if (match_pattern_list(cp, filter, 0) != 1) { - if (buffer_len(&b) > 0) - buffer_append(&b, ",", 1); - buffer_append(&b, cp, strlen(cp)); - } else - debug2("Compat: skipping algorithm \"%s\"", cp); - } - buffer_append(&b, "\0", 1); - fix_prop = xstrdup((char *)buffer_ptr(&b)); - buffer_free(&b); - free(orig_prop); - - return fix_prop; -} - char * compat_cipher_proposal(char *cipher_prop) { if (!(datafellows & SSH_BUG_BIGENDIANAES)) return cipher_prop; debug2("%s: original cipher proposal: %s", __func__, cipher_prop); - cipher_prop = filter_proposal(cipher_prop, "aes*"); + if ((cipher_prop = match_filter_list(cipher_prop, "aes*")) == NULL) + fatal("match_filter_list failed"); debug2("%s: compat cipher proposal: %s", __func__, cipher_prop); if (*cipher_prop == '\0') fatal("No supported ciphers found"); return cipher_prop; } char * compat_pkalg_proposal(char *pkalg_prop) { if (!(datafellows & SSH_BUG_RSASIGMD5)) return pkalg_prop; debug2("%s: original public key proposal: %s", __func__, pkalg_prop); - pkalg_prop = filter_proposal(pkalg_prop, "ssh-rsa"); + if ((pkalg_prop = match_filter_list(pkalg_prop, "ssh-rsa")) == NULL) + fatal("match_filter_list failed"); debug2("%s: compat public key proposal: %s", __func__, pkalg_prop); if (*pkalg_prop == '\0') fatal("No supported PK algorithms found"); return pkalg_prop; } char * compat_kex_proposal(char *p) { if ((datafellows & (SSH_BUG_CURVE25519PAD|SSH_OLD_DHGEX)) == 0) return p; debug2("%s: original KEX proposal: %s", __func__, p); if ((datafellows & SSH_BUG_CURVE25519PAD) != 0) - p = filter_proposal(p, "curve25519-sha256@libssh.org"); + if ((p = match_filter_list(p, + "curve25519-sha256@libssh.org")) == NULL) + fatal("match_filter_list failed"); if ((datafellows & SSH_OLD_DHGEX) != 0) { - p = filter_proposal(p, "diffie-hellman-group-exchange-sha256"); - p = filter_proposal(p, "diffie-hellman-group-exchange-sha1"); + if ((p = match_filter_list(p, + "diffie-hellman-group-exchange-sha256," + "diffie-hellman-group-exchange-sha1")) == NULL) + fatal("match_filter_list failed"); } debug2("%s: compat KEX proposal: %s", __func__, p); if (*p == '\0') fatal("No supported key exchange algorithms found"); return p; } diff --git a/crypto/openssh/config.h b/crypto/openssh/config.h index 2a76c64af79d..824c844ef0d9 100644 --- a/crypto/openssh/config.h +++ b/crypto/openssh/config.h @@ -1,1771 +1,1774 @@ /* config.h. Generated from config.h.in by configure. */ /* config.h.in. Generated from configure.ac by autoheader. */ /* Define if building universal (internal helper macro) */ /* #undef AC_APPLE_UNIVERSAL_BUILD */ /* Define if you have a getaddrinfo that fails for the all-zeros IPv6 address */ /* #undef AIX_GETNAMEINFO_HACK */ /* Define if your AIX loginfailed() function takes 4 arguments (AIX >= 5.2) */ /* #undef AIX_LOGINFAILED_4ARG */ /* System only supports IPv4 audit records */ /* #undef AU_IPv4 */ /* Define if your resolver libs need this for getrrsetbyname */ /* #undef BIND_8_COMPAT */ /* The system has incomplete BSM API */ /* #undef BROKEN_BSM_API */ /* Define if cmsg_type is not passed correctly */ /* #undef BROKEN_CMSG_TYPE */ /* getaddrinfo is broken (if present) */ /* #undef BROKEN_GETADDRINFO */ /* getgroups(0,NULL) will return -1 */ /* #undef BROKEN_GETGROUPS */ /* FreeBSD glob does not do what we need */ #define BROKEN_GLOB 1 /* Define if you system's inet_ntoa is busted (e.g. Irix gcc issue) */ /* #undef BROKEN_INET_NTOA */ /* ia_uinfo routines not supported by OS yet */ /* #undef BROKEN_LIBIAF */ /* Define if your struct dirent expects you to allocate extra space for d_name */ /* #undef BROKEN_ONE_BYTE_DIRENT_D_NAME */ /* Can't do comparisons on readv */ /* #undef BROKEN_READV_COMPARISON */ /* NetBSD read function is sometimes redirected, breaking atomicio comparisons against it */ /* #undef BROKEN_READ_COMPARISON */ /* realpath does not work with nonexistent files */ #define BROKEN_REALPATH 1 /* Needed for NeXT */ /* #undef BROKEN_SAVED_UIDS */ /* Define if your setregid() is broken */ /* #undef BROKEN_SETREGID */ /* Define if your setresgid() is broken */ /* #undef BROKEN_SETRESGID */ /* Define if your setresuid() is broken */ /* #undef BROKEN_SETRESUID */ /* Define if your setreuid() is broken */ /* #undef BROKEN_SETREUID */ /* LynxOS has broken setvbuf() implementation */ /* #undef BROKEN_SETVBUF */ /* QNX shadow support is broken */ /* #undef BROKEN_SHADOW_EXPIRE */ /* Define if your snprintf is busted */ /* #undef BROKEN_SNPRINTF */ /* strnvis detected broken */ #define BROKEN_STRNVIS 1 /* tcgetattr with ICANON may hang */ /* #undef BROKEN_TCGETATTR_ICANON */ /* updwtmpx is broken (if present) */ /* #undef BROKEN_UPDWTMPX */ /* Define if you have BSD auth support */ /* #undef BSD_AUTH */ /* Define if you want to specify the path to your lastlog file */ /* #undef CONF_LASTLOG_FILE */ /* Define if you want to specify the path to your utmp file */ /* #undef CONF_UTMP_FILE */ /* Define if you want to specify the path to your wtmpx file */ /* #undef CONF_WTMPX_FILE */ /* Define if you want to specify the path to your wtmp file */ /* #undef CONF_WTMP_FILE */ /* Define if your platform needs to skip post auth file descriptor passing */ /* #undef DISABLE_FD_PASSING */ /* Define if you don't want to use lastlog */ #define DISABLE_LASTLOG 1 /* Define if you don't want to use your system's login() call */ /* #undef DISABLE_LOGIN */ /* Define if you don't want to use pututline() etc. to write [uw]tmp */ /* #undef DISABLE_PUTUTLINE */ /* Define if you don't want to use pututxline() etc. to write [uw]tmpx */ /* #undef DISABLE_PUTUTXLINE */ /* Define if you want to disable shadow passwords */ /* #undef DISABLE_SHADOW */ /* Define if you don't want to use utmp */ #define DISABLE_UTMP 1 /* Define if you don't want to use utmpx */ /* #undef DISABLE_UTMPX */ /* Define if you don't want to use wtmp */ #define DISABLE_WTMP 1 /* Define if you don't want to use wtmpx */ #define DISABLE_WTMPX 1 /* Enable for PKCS#11 support */ #define ENABLE_PKCS11 /**/ /* File names may not contain backslash characters */ /* #undef FILESYSTEM_NO_BACKSLASH */ /* fsid_t has member val */ /* #undef FSID_HAS_VAL */ /* fsid_t has member __val */ /* #undef FSID_HAS___VAL */ /* Define to 1 if the `getpgrp' function requires zero arguments. */ #define GETPGRP_VOID 1 /* Conflicting defs for getspnam */ /* #undef GETSPNAM_CONFLICTING_DEFS */ /* Define if your system glob() function has the GLOB_ALTDIRFUNC extension */ #define GLOB_HAS_ALTDIRFUNC 1 /* Define if your system glob() function has gl_matchc options in glob_t */ #define GLOB_HAS_GL_MATCHC 1 /* Define if your system glob() function has gl_statv options in glob_t */ /* #undef GLOB_HAS_GL_STATV */ /* Define this if you want GSSAPI support in the version 2 protocol */ /* #undef GSSAPI */ /* Define if you want to use shadow password expire field */ /* #undef HAS_SHADOW_EXPIRE */ /* Define if your system uses access rights style file descriptor passing */ /* #undef HAVE_ACCRIGHTS_IN_MSGHDR */ /* Define if you have ut_addr in utmp.h */ /* #undef HAVE_ADDR_IN_UTMP */ /* Define if you have ut_addr in utmpx.h */ /* #undef HAVE_ADDR_IN_UTMPX */ /* Define if you have ut_addr_v6 in utmp.h */ /* #undef HAVE_ADDR_V6_IN_UTMP */ /* Define if you have ut_addr_v6 in utmpx.h */ /* #undef HAVE_ADDR_V6_IN_UTMPX */ /* Define to 1 if you have the `arc4random' function. */ #define HAVE_ARC4RANDOM 1 /* Define to 1 if you have the `arc4random_buf' function. */ #define HAVE_ARC4RANDOM_BUF 1 /* Define to 1 if you have the `arc4random_stir' function. */ #define HAVE_ARC4RANDOM_STIR 1 /* Define to 1 if you have the `arc4random_uniform' function. */ #define HAVE_ARC4RANDOM_UNIFORM 1 /* Define to 1 if you have the `asprintf' function. */ #define HAVE_ASPRINTF 1 /* OpenBSD's gcc has bounded */ /* #undef HAVE_ATTRIBUTE__BOUNDED__ */ /* Have attribute nonnull */ #define HAVE_ATTRIBUTE__NONNULL__ 1 /* OpenBSD's gcc has sentinel */ /* #undef HAVE_ATTRIBUTE__SENTINEL__ */ /* Define to 1 if you have the `aug_get_machine' function. */ /* #undef HAVE_AUG_GET_MACHINE */ /* Define to 1 if you have the `b64_ntop' function. */ /* #undef HAVE_B64_NTOP */ /* Define to 1 if you have the `b64_pton' function. */ /* #undef HAVE_B64_PTON */ /* Define if you have the basename function. */ #define HAVE_BASENAME 1 /* Define to 1 if you have the `bcopy' function. */ #define HAVE_BCOPY 1 /* Define to 1 if you have the `bcrypt_pbkdf' function. */ /* #undef HAVE_BCRYPT_PBKDF */ /* Define to 1 if you have the `bindresvport_sa' function. */ #define HAVE_BINDRESVPORT_SA 1 /* Define to 1 if you have the `blf_enc' function. */ /* #undef HAVE_BLF_ENC */ /* Define to 1 if you have the header file. */ /* #undef HAVE_BLF_H */ /* Define to 1 if you have the `Blowfish_expand0state' function. */ /* #undef HAVE_BLOWFISH_EXPAND0STATE */ /* Define to 1 if you have the `Blowfish_expandstate' function. */ /* #undef HAVE_BLOWFISH_EXPANDSTATE */ /* Define to 1 if you have the `Blowfish_initstate' function. */ /* #undef HAVE_BLOWFISH_INITSTATE */ /* Define to 1 if you have the `Blowfish_stream2word' function. */ /* #undef HAVE_BLOWFISH_STREAM2WORD */ /* Define to 1 if you have the `BN_is_prime_ex' function. */ #define HAVE_BN_IS_PRIME_EX 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_BSD_LIBUTIL_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_BSM_AUDIT_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_BSTRING_H */ /* Define to 1 if you have the `cap_rights_limit' function. */ #define HAVE_CAP_RIGHTS_LIMIT 1 /* Define to 1 if you have the `clock' function. */ #define HAVE_CLOCK 1 /* Have clock_gettime */ #define HAVE_CLOCK_GETTIME 1 /* define if you have clock_t data type */ #define HAVE_CLOCK_T 1 /* Define to 1 if you have the `closefrom' function. */ #define HAVE_CLOSEFROM 1 /* Define if gai_strerror() returns const char * */ #define HAVE_CONST_GAI_STRERROR_PROTO 1 /* Define if your system uses ancillary data style file descriptor passing */ #define HAVE_CONTROL_IN_MSGHDR 1 /* Define to 1 if you have the `crypt' function. */ #define HAVE_CRYPT 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_CRYPTO_SHA2_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_CRYPT_H */ /* Define if you are on Cygwin */ /* #undef HAVE_CYGWIN */ /* Define if your libraries define daemon() */ #define HAVE_DAEMON 1 /* Define to 1 if you have the declaration of `AI_NUMERICSERV', and to 0 if you don't. */ #define HAVE_DECL_AI_NUMERICSERV 1 /* Define to 1 if you have the declaration of `authenticate', and to 0 if you don't. */ /* #undef HAVE_DECL_AUTHENTICATE */ /* Define to 1 if you have the declaration of `GLOB_NOMATCH', and to 0 if you don't. */ #define HAVE_DECL_GLOB_NOMATCH 1 /* Define to 1 if you have the declaration of `GSS_C_NT_HOSTBASED_SERVICE', and to 0 if you don't. */ /* #undef HAVE_DECL_GSS_C_NT_HOSTBASED_SERVICE */ /* Define to 1 if you have the declaration of `howmany', and to 0 if you don't. */ #define HAVE_DECL_HOWMANY 1 /* Define to 1 if you have the declaration of `h_errno', and to 0 if you don't. */ #define HAVE_DECL_H_ERRNO 1 /* Define to 1 if you have the declaration of `loginfailed', and to 0 if you don't. */ /* #undef HAVE_DECL_LOGINFAILED */ /* Define to 1 if you have the declaration of `loginrestrictions', and to 0 if you don't. */ /* #undef HAVE_DECL_LOGINRESTRICTIONS */ /* Define to 1 if you have the declaration of `loginsuccess', and to 0 if you don't. */ /* #undef HAVE_DECL_LOGINSUCCESS */ /* Define to 1 if you have the declaration of `MAXSYMLINKS', and to 0 if you don't. */ #define HAVE_DECL_MAXSYMLINKS 1 /* Define to 1 if you have the declaration of `NFDBITS', and to 0 if you don't. */ #define HAVE_DECL_NFDBITS 1 /* Define to 1 if you have the declaration of `offsetof', and to 0 if you don't. */ #define HAVE_DECL_OFFSETOF 1 /* Define to 1 if you have the declaration of `O_NONBLOCK', and to 0 if you don't. */ #define HAVE_DECL_O_NONBLOCK 1 /* Define to 1 if you have the declaration of `passwdexpired', and to 0 if you don't. */ /* #undef HAVE_DECL_PASSWDEXPIRED */ /* Define to 1 if you have the declaration of `setauthdb', and to 0 if you don't. */ /* #undef HAVE_DECL_SETAUTHDB */ /* Define to 1 if you have the declaration of `SHUT_RD', and to 0 if you don't. */ #define HAVE_DECL_SHUT_RD 1 /* Define to 1 if you have the declaration of `writev', and to 0 if you don't. */ #define HAVE_DECL_WRITEV 1 /* Define to 1 if you have the declaration of `_getlong', and to 0 if you don't. */ #define HAVE_DECL__GETLONG 0 /* Define to 1 if you have the declaration of `_getshort', and to 0 if you don't. */ #define HAVE_DECL__GETSHORT 0 /* Define to 1 if you have the `DES_crypt' function. */ #define HAVE_DES_CRYPT 1 /* Define if you have /dev/ptmx */ /* #undef HAVE_DEV_PTMX */ /* Define if you have /dev/ptc */ /* #undef HAVE_DEV_PTS_AND_PTC */ /* Define to 1 if you have the header file. */ #define HAVE_DIRENT_H 1 /* Define to 1 if you have the `dirfd' function. */ #define HAVE_DIRFD 1 /* Define to 1 if you have the `dirname' function. */ #define HAVE_DIRNAME 1 /* Define to 1 if you have the `DSA_generate_parameters_ex' function. */ #define HAVE_DSA_GENERATE_PARAMETERS_EX 1 /* Define to 1 if you have the header file. */ #define HAVE_ELF_H 1 /* Define to 1 if you have the `endgrent' function. */ #define HAVE_ENDGRENT 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_ENDIAN_H */ /* Define to 1 if you have the `endutent' function. */ /* #undef HAVE_ENDUTENT */ /* Define to 1 if you have the `endutxent' function. */ #define HAVE_ENDUTXENT 1 /* Define to 1 if you have the `err' function. */ #define HAVE_ERR 1 /* Define to 1 if you have the `errx' function. */ #define HAVE_ERRX 1 /* Define to 1 if you have the header file. */ #define HAVE_ERR_H 1 /* Define if your system has /etc/default/login */ /* #undef HAVE_ETC_DEFAULT_LOGIN */ /* Define if libcrypto has EVP_CIPHER_CTX_ctrl */ #define HAVE_EVP_CIPHER_CTX_CTRL 1 /* Define to 1 if you have the `EVP_DigestFinal_ex' function. */ #define HAVE_EVP_DIGESTFINAL_EX 1 /* Define to 1 if you have the `EVP_DigestInit_ex' function. */ #define HAVE_EVP_DIGESTINIT_EX 1 /* Define to 1 if you have the `EVP_MD_CTX_cleanup' function. */ #define HAVE_EVP_MD_CTX_CLEANUP 1 /* Define to 1 if you have the `EVP_MD_CTX_copy_ex' function. */ #define HAVE_EVP_MD_CTX_COPY_EX 1 /* Define to 1 if you have the `EVP_MD_CTX_init' function. */ #define HAVE_EVP_MD_CTX_INIT 1 /* Define to 1 if you have the `EVP_ripemd160' function. */ #define HAVE_EVP_RIPEMD160 1 /* Define to 1 if you have the `EVP_sha256' function. */ #define HAVE_EVP_SHA256 1 /* Define if you have ut_exit in utmp.h */ /* #undef HAVE_EXIT_IN_UTMP */ /* Define to 1 if you have the `explicit_bzero' function. */ #define HAVE_EXPLICIT_BZERO 1 /* Define to 1 if you have the `fchmod' function. */ #define HAVE_FCHMOD 1 /* Define to 1 if you have the `fchown' function. */ #define HAVE_FCHOWN 1 /* Use F_CLOSEM fcntl for closefrom */ /* #undef HAVE_FCNTL_CLOSEM */ /* Define to 1 if you have the header file. */ #define HAVE_FCNTL_H 1 /* Define to 1 if the system has the type `fd_mask'. */ #define HAVE_FD_MASK 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_FEATURES_H */ /* Define to 1 if you have the header file. */ #define HAVE_FLOATINGPOINT_H 1 /* Define to 1 if you have the `fmt_scaled' function. */ /* #undef HAVE_FMT_SCALED */ /* Define to 1 if you have the `freeaddrinfo' function. */ #define HAVE_FREEADDRINFO 1 /* Define to 1 if the system has the type `fsblkcnt_t'. */ #define HAVE_FSBLKCNT_T 1 /* Define to 1 if the system has the type `fsfilcnt_t'. */ #define HAVE_FSFILCNT_T 1 /* Define to 1 if you have the `fstatfs' function. */ #define HAVE_FSTATFS 1 /* Define to 1 if you have the `fstatvfs' function. */ #define HAVE_FSTATVFS 1 /* Define to 1 if you have the `futimes' function. */ #define HAVE_FUTIMES 1 /* Define to 1 if you have the `gai_strerror' function. */ #define HAVE_GAI_STRERROR 1 /* Define to 1 if you have the `getaddrinfo' function. */ #define HAVE_GETADDRINFO 1 /* Define to 1 if you have the `getaudit' function. */ /* #undef HAVE_GETAUDIT */ /* Define to 1 if you have the `getaudit_addr' function. */ /* #undef HAVE_GETAUDIT_ADDR */ /* Define to 1 if you have the `getcwd' function. */ #define HAVE_GETCWD 1 /* Define to 1 if you have the `getgrouplist' function. */ #define HAVE_GETGROUPLIST 1 /* Define to 1 if you have the `getgrset' function. */ /* #undef HAVE_GETGRSET */ /* Define to 1 if you have the `getlastlogxbyname' function. */ /* #undef HAVE_GETLASTLOGXBYNAME */ /* Define to 1 if you have the `getluid' function. */ /* #undef HAVE_GETLUID */ /* Define to 1 if you have the `getnameinfo' function. */ #define HAVE_GETNAMEINFO 1 /* Define to 1 if you have the `getopt' function. */ #define HAVE_GETOPT 1 /* Define to 1 if you have the header file. */ #define HAVE_GETOPT_H 1 /* Define if your getopt(3) defines and uses optreset */ #define HAVE_GETOPT_OPTRESET 1 /* Define if your libraries define getpagesize() */ #define HAVE_GETPAGESIZE 1 /* Define to 1 if you have the `getpeereid' function. */ #define HAVE_GETPEEREID 1 /* Define to 1 if you have the `getpeerucred' function. */ /* #undef HAVE_GETPEERUCRED */ /* Define to 1 if you have the `getpgid' function. */ #define HAVE_GETPGID 1 /* Define to 1 if you have the `getpgrp' function. */ #define HAVE_GETPGRP 1 /* Define to 1 if you have the `getpwanam' function. */ /* #undef HAVE_GETPWANAM */ /* Define to 1 if you have the `getrlimit' function. */ #define HAVE_GETRLIMIT 1 /* Define if getrrsetbyname() exists */ /* #undef HAVE_GETRRSETBYNAME */ /* Define to 1 if you have the `getrusage' function. */ /* #undef HAVE_GETRUSAGE */ /* Define to 1 if you have the `getseuserbyname' function. */ /* #undef HAVE_GETSEUSERBYNAME */ /* Define to 1 if you have the `gettimeofday' function. */ #define HAVE_GETTIMEOFDAY 1 /* Define to 1 if you have the `getttyent' function. */ #define HAVE_GETTTYENT 1 /* Define to 1 if you have the `getutent' function. */ /* #undef HAVE_GETUTENT */ /* Define to 1 if you have the `getutid' function. */ /* #undef HAVE_GETUTID */ /* Define to 1 if you have the `getutline' function. */ /* #undef HAVE_GETUTLINE */ /* Define to 1 if you have the `getutxent' function. */ #define HAVE_GETUTXENT 1 /* Define to 1 if you have the `getutxid' function. */ #define HAVE_GETUTXID 1 /* Define to 1 if you have the `getutxline' function. */ #define HAVE_GETUTXLINE 1 /* Define to 1 if you have the `getutxuser' function. */ #define HAVE_GETUTXUSER 1 /* Define to 1 if you have the `get_default_context_with_level' function. */ /* #undef HAVE_GET_DEFAULT_CONTEXT_WITH_LEVEL */ /* Define to 1 if you have the `glob' function. */ #define HAVE_GLOB 1 /* Define to 1 if you have the header file. */ #define HAVE_GLOB_H 1 /* Define to 1 if you have the `group_from_gid' function. */ #define HAVE_GROUP_FROM_GID 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_GSSAPI_GENERIC_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_GSSAPI_GSSAPI_GENERIC_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_GSSAPI_GSSAPI_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_GSSAPI_GSSAPI_KRB5_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_GSSAPI_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_GSSAPI_KRB5_H */ /* Define if HEADER.ad exists in arpa/nameser.h */ #define HAVE_HEADER_AD 1 /* Define to 1 if you have the `HMAC_CTX_init' function. */ #define HAVE_HMAC_CTX_INIT 1 /* Define if you have ut_host in utmp.h */ /* #undef HAVE_HOST_IN_UTMP */ /* Define if you have ut_host in utmpx.h */ #define HAVE_HOST_IN_UTMPX 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_IAF_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_IA_H */ /* Define if you have ut_id in utmp.h */ /* #undef HAVE_ID_IN_UTMP */ /* Define if you have ut_id in utmpx.h */ #define HAVE_ID_IN_UTMPX 1 /* Define to 1 if you have the `inet_aton' function. */ #define HAVE_INET_ATON 1 /* Define to 1 if you have the `inet_ntoa' function. */ #define HAVE_INET_NTOA 1 /* Define to 1 if you have the `inet_ntop' function. */ #define HAVE_INET_NTOP 1 /* Define to 1 if you have the `innetgr' function. */ #define HAVE_INNETGR 1 /* define if you have int64_t data type */ #define HAVE_INT64_T 1 /* Define to 1 if the system has the type `intmax_t'. */ #define HAVE_INTMAX_T 1 /* Define to 1 if you have the header file. */ #define HAVE_INTTYPES_H 1 /* define if you have intxx_t data type */ #define HAVE_INTXX_T 1 /* Define to 1 if the system has the type `in_addr_t'. */ #define HAVE_IN_ADDR_T 1 /* Define to 1 if the system has the type `in_port_t'. */ #define HAVE_IN_PORT_T 1 /* Define if you have isblank(3C). */ #define HAVE_ISBLANK 1 /* Define to 1 if you have the `krb5_cc_new_unique' function. */ /* #undef HAVE_KRB5_CC_NEW_UNIQUE */ /* Define to 1 if you have the `krb5_free_error_message' function. */ /* #undef HAVE_KRB5_FREE_ERROR_MESSAGE */ /* Define to 1 if you have the `krb5_get_error_message' function. */ /* #undef HAVE_KRB5_GET_ERROR_MESSAGE */ /* Define to 1 if you have the header file. */ #define HAVE_LANGINFO_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_LASTLOG_H */ /* Define if you want ldns support */ /* #undef HAVE_LDNS */ /* Define to 1 if you have the header file. */ /* #undef HAVE_LIBAUDIT_H */ /* Define to 1 if you have the `bsm' library (-lbsm). */ /* #undef HAVE_LIBBSM */ /* Define to 1 if you have the `crypt' library (-lcrypt). */ /* #undef HAVE_LIBCRYPT */ /* Define to 1 if you have the `dl' library (-ldl). */ /* #undef HAVE_LIBDL */ /* Define to 1 if you have the header file. */ #define HAVE_LIBGEN_H 1 /* Define if system has libiaf that supports set_id */ /* #undef HAVE_LIBIAF */ /* Define to 1 if you have the `network' library (-lnetwork). */ /* #undef HAVE_LIBNETWORK */ /* Define to 1 if you have the `pam' library (-lpam). */ #define HAVE_LIBPAM 1 /* Define to 1 if you have the `socket' library (-lsocket). */ /* #undef HAVE_LIBSOCKET */ /* Define to 1 if you have the header file. */ #define HAVE_LIBUTIL_H 1 /* Define to 1 if you have the `xnet' library (-lxnet). */ /* #undef HAVE_LIBXNET */ /* Define to 1 if you have the `z' library (-lz). */ #define HAVE_LIBZ 1 /* Define to 1 if you have the header file. */ #define HAVE_LIMITS_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_LINUX_AUDIT_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_LINUX_FILTER_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_LINUX_IF_TUN_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_LINUX_SECCOMP_H */ +/* Define to 1 if you have the `llabs' function. */ +#define HAVE_LLABS 1 + /* Define to 1 if you have the header file. */ #define HAVE_LOCALE_H 1 /* Define to 1 if you have the `login' function. */ /* #undef HAVE_LOGIN */ /* Define to 1 if you have the header file. */ #define HAVE_LOGIN_CAP_H 1 /* Define to 1 if you have the `login_getcapbool' function. */ #define HAVE_LOGIN_GETCAPBOOL 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_LOGIN_H */ /* Define to 1 if you have the `logout' function. */ /* #undef HAVE_LOGOUT */ /* Define to 1 if you have the `logwtmp' function. */ /* #undef HAVE_LOGWTMP */ /* Define to 1 if the system has the type `long double'. */ #define HAVE_LONG_DOUBLE 1 /* Define to 1 if the system has the type `long long'. */ #define HAVE_LONG_LONG 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_MAILLOCK_H */ /* Define to 1 if you have the `mblen' function. */ #define HAVE_MBLEN 1 /* Define to 1 if you have the `mbtowc' function. */ #define HAVE_MBTOWC 1 /* Define to 1 if you have the `md5_crypt' function. */ /* #undef HAVE_MD5_CRYPT */ /* Define if you want to allow MD5 passwords */ /* #undef HAVE_MD5_PASSWORDS */ /* Define to 1 if you have the `memmove' function. */ #define HAVE_MEMMOVE 1 /* Define to 1 if you have the header file. */ #define HAVE_MEMORY_H 1 /* Define to 1 if you have the `memset_s' function. */ -/* #undef HAVE_MEMSET_S */ +#define HAVE_MEMSET_S 1 /* Define to 1 if you have the `mkdtemp' function. */ #define HAVE_MKDTEMP 1 /* define if you have mode_t data type */ #define HAVE_MODE_T 1 /* Some systems put nanosleep outside of libc */ #define HAVE_NANOSLEEP 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_NDIR_H */ /* Define to 1 if you have the header file. */ #define HAVE_NETDB_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_NETGROUP_H */ /* Define to 1 if you have the header file. */ #define HAVE_NET_IF_TUN_H 1 /* Define if you are on NeXT */ /* #undef HAVE_NEXT */ /* Define to 1 if you have the `ngetaddrinfo' function. */ /* #undef HAVE_NGETADDRINFO */ /* Define to 1 if you have the `nl_langinfo' function. */ #define HAVE_NL_LANGINFO 1 /* Define to 1 if you have the `nsleep' function. */ /* #undef HAVE_NSLEEP */ /* Define to 1 if you have the `ogetaddrinfo' function. */ /* #undef HAVE_OGETADDRINFO */ /* Define if you have an old version of PAM which takes only one argument to pam_strerror */ /* #undef HAVE_OLD_PAM */ /* Define to 1 if you have the `openlog_r' function. */ /* #undef HAVE_OPENLOG_R */ /* Define to 1 if you have the `openpty' function. */ #define HAVE_OPENPTY 1 /* Define if your ssl headers are included with #include */ #define HAVE_OPENSSL 1 /* Define if you have Digital Unix Security Integration Architecture */ /* #undef HAVE_OSF_SIA */ /* Define to 1 if you have the `pam_getenvlist' function. */ #define HAVE_PAM_GETENVLIST 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_PAM_PAM_APPL_H */ /* Define to 1 if you have the `pam_putenv' function. */ #define HAVE_PAM_PUTENV 1 /* Define to 1 if you have the header file. */ #define HAVE_PATHS_H 1 /* Define if you have ut_pid in utmp.h */ /* #undef HAVE_PID_IN_UTMP */ /* define if you have pid_t data type */ #define HAVE_PID_T 1 /* Define to 1 if you have the `pledge' function. */ /* #undef HAVE_PLEDGE */ /* Define to 1 if you have the `poll' function. */ #define HAVE_POLL 1 /* Define to 1 if you have the header file. */ #define HAVE_POLL_H 1 /* Define to 1 if you have the `prctl' function. */ /* #undef HAVE_PRCTL */ /* Define to 1 if you have the `priv_basicset' function. */ /* #undef HAVE_PRIV_BASICSET */ /* Define to 1 if you have the header file. */ /* #undef HAVE_PRIV_H */ /* Define if you have /proc/$pid/fd */ /* #undef HAVE_PROC_PID */ /* Define to 1 if you have the `pstat' function. */ /* #undef HAVE_PSTAT */ /* Define to 1 if you have the header file. */ /* #undef HAVE_PTY_H */ /* Define to 1 if you have the `pututline' function. */ /* #undef HAVE_PUTUTLINE */ /* Define to 1 if you have the `pututxline' function. */ #define HAVE_PUTUTXLINE 1 /* Define to 1 if you have the `readpassphrase' function. */ #define HAVE_READPASSPHRASE 1 /* Define to 1 if you have the header file. */ #define HAVE_READPASSPHRASE_H 1 /* Define to 1 if you have the `reallocarray' function. */ #define HAVE_REALLOCARRAY 1 /* Define to 1 if you have the `realpath' function. */ #define HAVE_REALPATH 1 /* Define to 1 if you have the `recvmsg' function. */ #define HAVE_RECVMSG 1 /* sys/resource.h has RLIMIT_NPROC */ #define HAVE_RLIMIT_NPROC /**/ /* Define to 1 if you have the header file. */ #define HAVE_RPC_TYPES_H 1 /* Define to 1 if you have the `rresvport_af' function. */ #define HAVE_RRESVPORT_AF 1 /* Define to 1 if you have the `RSA_generate_key_ex' function. */ #define HAVE_RSA_GENERATE_KEY_EX 1 /* Define to 1 if you have the `RSA_get_default_method' function. */ #define HAVE_RSA_GET_DEFAULT_METHOD 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_SANDBOX_H */ /* Define to 1 if you have the `sandbox_init' function. */ /* #undef HAVE_SANDBOX_INIT */ /* define if you have sa_family_t data type */ #define HAVE_SA_FAMILY_T 1 /* Define to 1 if you have the `scan_scaled' function. */ /* #undef HAVE_SCAN_SCALED */ /* Define if you have SecureWare-based protected password database */ /* #undef HAVE_SECUREWARE */ /* Define to 1 if you have the header file. */ #define HAVE_SECURITY_PAM_APPL_H 1 /* Define to 1 if you have the `sendmsg' function. */ #define HAVE_SENDMSG 1 /* Define to 1 if you have the `setauthdb' function. */ /* #undef HAVE_SETAUTHDB */ /* Define to 1 if you have the `setdtablesize' function. */ /* #undef HAVE_SETDTABLESIZE */ /* Define to 1 if you have the `setegid' function. */ #define HAVE_SETEGID 1 /* Define to 1 if you have the `setenv' function. */ #define HAVE_SETENV 1 /* Define to 1 if you have the `seteuid' function. */ #define HAVE_SETEUID 1 /* Define to 1 if you have the `setgroupent' function. */ #define HAVE_SETGROUPENT 1 /* Define to 1 if you have the `setgroups' function. */ #define HAVE_SETGROUPS 1 /* Define to 1 if you have the `setlinebuf' function. */ #define HAVE_SETLINEBUF 1 /* Define to 1 if you have the `setlogin' function. */ #define HAVE_SETLOGIN 1 /* Define to 1 if you have the `setluid' function. */ /* #undef HAVE_SETLUID */ /* Define to 1 if you have the `setpassent' function. */ #define HAVE_SETPASSENT 1 /* Define to 1 if you have the `setpcred' function. */ /* #undef HAVE_SETPCRED */ /* Define to 1 if you have the `setpflags' function. */ /* #undef HAVE_SETPFLAGS */ /* Define to 1 if you have the `setppriv' function. */ /* #undef HAVE_SETPPRIV */ /* Define to 1 if you have the `setproctitle' function. */ #define HAVE_SETPROCTITLE 1 /* Define to 1 if you have the `setregid' function. */ #define HAVE_SETREGID 1 /* Define to 1 if you have the `setresgid' function. */ #define HAVE_SETRESGID 1 /* Define to 1 if you have the `setresuid' function. */ #define HAVE_SETRESUID 1 /* Define to 1 if you have the `setreuid' function. */ #define HAVE_SETREUID 1 /* Define to 1 if you have the `setrlimit' function. */ #define HAVE_SETRLIMIT 1 /* Define to 1 if you have the `setsid' function. */ #define HAVE_SETSID 1 /* Define to 1 if you have the `setutent' function. */ /* #undef HAVE_SETUTENT */ /* Define to 1 if you have the `setutxdb' function. */ #define HAVE_SETUTXDB 1 /* Define to 1 if you have the `setutxent' function. */ #define HAVE_SETUTXENT 1 /* Define to 1 if you have the `setvbuf' function. */ #define HAVE_SETVBUF 1 /* Define to 1 if you have the `set_id' function. */ /* #undef HAVE_SET_ID */ /* Define to 1 if you have the `SHA256_Update' function. */ #define HAVE_SHA256_UPDATE 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_SHA2_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SHADOW_H */ /* Define to 1 if you have the `sigaction' function. */ #define HAVE_SIGACTION 1 /* Define to 1 if you have the `sigvec' function. */ #define HAVE_SIGVEC 1 /* Define to 1 if the system has the type `sig_atomic_t'. */ #define HAVE_SIG_ATOMIC_T 1 /* define if you have size_t data type */ #define HAVE_SIZE_T 1 /* Define to 1 if you have the `snprintf' function. */ #define HAVE_SNPRINTF 1 /* Define to 1 if you have the `socketpair' function. */ #define HAVE_SOCKETPAIR 1 /* Have PEERCRED socket option */ /* #undef HAVE_SO_PEERCRED */ /* define if you have ssize_t data type */ #define HAVE_SSIZE_T 1 /* Fields in struct sockaddr_storage */ #define HAVE_SS_FAMILY_IN_SS 1 /* Define to 1 if you have the `statfs' function. */ #define HAVE_STATFS 1 /* Define to 1 if you have the `statvfs' function. */ #define HAVE_STATVFS 1 /* Define to 1 if you have the header file. */ #define HAVE_STDDEF_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STDINT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STDLIB_H 1 /* Define to 1 if you have the `strcasestr' function. */ #define HAVE_STRCASESTR 1 /* Define to 1 if you have the `strdup' function. */ #define HAVE_STRDUP 1 /* Define to 1 if you have the `strerror' function. */ #define HAVE_STRERROR 1 /* Define to 1 if you have the `strftime' function. */ #define HAVE_STRFTIME 1 /* Silly mkstemp() */ /* #undef HAVE_STRICT_MKSTEMP */ /* Define to 1 if you have the header file. */ #define HAVE_STRINGS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_STRING_H 1 /* Define to 1 if you have the `strlcat' function. */ #define HAVE_STRLCAT 1 /* Define to 1 if you have the `strlcpy' function. */ #define HAVE_STRLCPY 1 /* Define to 1 if you have the `strmode' function. */ #define HAVE_STRMODE 1 /* Define to 1 if you have the `strnlen' function. */ #define HAVE_STRNLEN 1 /* Define to 1 if you have the `strnvis' function. */ #define HAVE_STRNVIS 1 /* Define to 1 if you have the `strptime' function. */ #define HAVE_STRPTIME 1 /* Define to 1 if you have the `strsep' function. */ #define HAVE_STRSEP 1 /* Define to 1 if you have the `strtoll' function. */ #define HAVE_STRTOLL 1 /* Define to 1 if you have the `strtonum' function. */ #define HAVE_STRTONUM 1 /* Define to 1 if you have the `strtoul' function. */ #define HAVE_STRTOUL 1 /* Define to 1 if you have the `strtoull' function. */ #define HAVE_STRTOULL 1 /* define if you have struct addrinfo data type */ #define HAVE_STRUCT_ADDRINFO 1 /* define if you have struct in6_addr data type */ #define HAVE_STRUCT_IN6_ADDR 1 /* Define to 1 if `pw_change' is a member of `struct passwd'. */ #define HAVE_STRUCT_PASSWD_PW_CHANGE 1 /* Define to 1 if `pw_class' is a member of `struct passwd'. */ #define HAVE_STRUCT_PASSWD_PW_CLASS 1 /* Define to 1 if `pw_expire' is a member of `struct passwd'. */ #define HAVE_STRUCT_PASSWD_PW_EXPIRE 1 /* Define to 1 if `pw_gecos' is a member of `struct passwd'. */ #define HAVE_STRUCT_PASSWD_PW_GECOS 1 /* define if you have struct sockaddr_in6 data type */ #define HAVE_STRUCT_SOCKADDR_IN6 1 /* Define to 1 if `sin6_scope_id' is a member of `struct sockaddr_in6'. */ #define HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID 1 /* define if you have struct sockaddr_storage data type */ #define HAVE_STRUCT_SOCKADDR_STORAGE 1 /* Define to 1 if `st_blksize' is a member of `struct stat'. */ #define HAVE_STRUCT_STAT_ST_BLKSIZE 1 /* Define to 1 if the system has the type `struct timespec'. */ #define HAVE_STRUCT_TIMESPEC 1 /* define if you have struct timeval */ #define HAVE_STRUCT_TIMEVAL 1 /* Define to 1 if you have the `swap32' function. */ /* #undef HAVE_SWAP32 */ /* Define to 1 if you have the `sysconf' function. */ #define HAVE_SYSCONF 1 /* Define if you have syslen in utmpx.h */ /* #undef HAVE_SYSLEN_IN_UTMPX */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_AUDIT_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_BITYPES_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_BSDTTY_H */ /* Define to 1 if you have the header file. */ #define HAVE_SYS_CAPSICUM_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_CDEFS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_DIR_H 1 /* Define if your system defines sys_errlist[] */ #define HAVE_SYS_ERRLIST 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_MMAN_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_MOUNT_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_NDIR_H */ /* Define if your system defines sys_nerr */ #define HAVE_SYS_NERR 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_POLL_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_PRCTL_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_PSTAT_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_PTMS_H */ /* Define to 1 if you have the header file. */ #define HAVE_SYS_PTRACE_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_SELECT_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_STATVFS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_STAT_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_STREAM_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_STROPTS_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_STRTIO_H */ /* Force use of sys/syslog.h on Ultrix */ /* #undef HAVE_SYS_SYSLOG_H */ /* Define to 1 if you have the header file. */ /* #undef HAVE_SYS_SYSMACROS_H */ /* Define to 1 if you have the header file. */ #define HAVE_SYS_TIMERS_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_TIME_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_TYPES_H 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_UN_H 1 /* Define to 1 if you have the `tcgetpgrp' function. */ #define HAVE_TCGETPGRP 1 /* Define to 1 if you have the `tcsendbreak' function. */ #define HAVE_TCSENDBREAK 1 /* Define to 1 if you have the `time' function. */ #define HAVE_TIME 1 /* Define to 1 if you have the header file. */ #define HAVE_TIME_H 1 /* Define if you have ut_time in utmp.h */ /* #undef HAVE_TIME_IN_UTMP */ /* Define if you have ut_time in utmpx.h */ /* #undef HAVE_TIME_IN_UTMPX */ /* Define to 1 if you have the `timingsafe_bcmp' function. */ #define HAVE_TIMINGSAFE_BCMP 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_TMPDIR_H */ /* Define to 1 if you have the `truncate' function. */ #define HAVE_TRUNCATE 1 /* Define to 1 if you have the header file. */ #define HAVE_TTYENT_H 1 /* Define if you have ut_tv in utmp.h */ /* #undef HAVE_TV_IN_UTMP */ /* Define if you have ut_tv in utmpx.h */ #define HAVE_TV_IN_UTMPX 1 /* Define if you have ut_type in utmp.h */ /* #undef HAVE_TYPE_IN_UTMP */ /* Define if you have ut_type in utmpx.h */ #define HAVE_TYPE_IN_UTMPX 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_UCRED_H */ /* Define to 1 if the system has the type `uintmax_t'. */ #define HAVE_UINTMAX_T 1 /* define if you have uintxx_t data type */ #define HAVE_UINTXX_T 1 /* Define to 1 if you have the header file. */ #define HAVE_UNISTD_H 1 /* Define to 1 if you have the `unsetenv' function. */ #define HAVE_UNSETENV 1 /* Define to 1 if the system has the type `unsigned long long'. */ #define HAVE_UNSIGNED_LONG_LONG 1 /* Define to 1 if you have the `updwtmp' function. */ /* #undef HAVE_UPDWTMP */ /* Define to 1 if you have the `updwtmpx' function. */ /* #undef HAVE_UPDWTMPX */ /* Define to 1 if you have the header file. */ /* #undef HAVE_USERSEC_H */ /* Define to 1 if you have the `user_from_uid' function. */ #define HAVE_USER_FROM_UID 1 /* Define to 1 if you have the `usleep' function. */ #define HAVE_USLEEP 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_UTIL_H */ /* Define to 1 if you have the `utimes' function. */ #define HAVE_UTIMES 1 /* Define to 1 if you have the header file. */ #define HAVE_UTIME_H 1 /* Define to 1 if you have the `utmpname' function. */ /* #undef HAVE_UTMPNAME */ /* Define to 1 if you have the `utmpxname' function. */ /* #undef HAVE_UTMPXNAME */ /* Define to 1 if you have the header file. */ #define HAVE_UTMPX_H 1 /* Define to 1 if you have the header file. */ /* #undef HAVE_UTMP_H */ /* define if you have u_char data type */ #define HAVE_U_CHAR 1 /* define if you have u_int data type */ #define HAVE_U_INT 1 /* define if you have u_int64_t data type */ #define HAVE_U_INT64_T 1 /* define if you have u_intxx_t data type */ #define HAVE_U_INTXX_T 1 /* Define to 1 if you have the `vasprintf' function. */ #define HAVE_VASPRINTF 1 /* Define if va_copy exists */ #define HAVE_VA_COPY 1 /* Define to 1 if you have the header file. */ #define HAVE_VIS_H 1 /* Define to 1 if you have the `vsnprintf' function. */ #define HAVE_VSNPRINTF 1 /* Define to 1 if you have the `waitpid' function. */ #define HAVE_WAITPID 1 /* Define to 1 if you have the `warn' function. */ #define HAVE_WARN 1 /* Define to 1 if you have the header file. */ #define HAVE_WCHAR_H 1 /* Define to 1 if you have the `wcwidth' function. */ #define HAVE_WCWIDTH 1 /* Define to 1 if you have the `_getlong' function. */ #define HAVE__GETLONG 1 /* Define to 1 if you have the `_getpty' function. */ /* #undef HAVE__GETPTY */ /* Define to 1 if you have the `_getshort' function. */ #define HAVE__GETSHORT 1 /* Define if you have struct __res_state _res as an extern */ #define HAVE__RES_EXTERN 1 /* Define to 1 if you have the `__b64_ntop' function. */ #define HAVE___B64_NTOP 1 /* Define to 1 if you have the `__b64_pton' function. */ #define HAVE___B64_PTON 1 /* Define if compiler implements __FUNCTION__ */ #define HAVE___FUNCTION__ 1 /* Define if libc defines __progname */ #define HAVE___PROGNAME 1 /* Fields in struct sockaddr_storage */ /* #undef HAVE___SS_FAMILY_IN_SS */ /* Define if __va_copy exists */ #define HAVE___VA_COPY 1 /* Define if compiler implements __func__ */ #define HAVE___func__ 1 /* Define this if you are using the Heimdal version of Kerberos V5 */ /* #undef HEIMDAL */ /* Define if you need to use IP address instead of hostname in $DISPLAY */ /* #undef IPADDR_IN_DISPLAY */ /* Detect IPv4 in IPv6 mapped addresses and treat as IPv4 */ /* #undef IPV4_IN_IPV6 */ /* Define if your system choked on IP TOS setting */ /* #undef IP_TOS_IS_BROKEN */ /* Define if you want Kerberos 5 support */ /* #undef KRB5 */ /* Define if pututxline updates lastlog too */ /* #undef LASTLOG_WRITE_PUTUTXLINE */ /* Define if you want TCP Wrappers support */ /* #undef LIBWRAP */ /* Define to whatever link() returns for "not supported" if it doesn't return EOPNOTSUPP. */ /* #undef LINK_OPNOTSUPP_ERRNO */ /* Adjust Linux out-of-memory killer */ /* #undef LINUX_OOM_ADJUST */ /* max value of long long calculated by configure */ /* #undef LLONG_MAX */ /* min value of long long calculated by configure */ /* #undef LLONG_MIN */ /* Account locked with pw(1) */ #define LOCKED_PASSWD_PREFIX "*LOCKED*" /* String used in /etc/passwd to denote locked account */ /* #undef LOCKED_PASSWD_STRING */ /* String used in /etc/passwd to denote locked account */ /* #undef LOCKED_PASSWD_SUBSTR */ /* Some systems need a utmpx entry for /bin/login to work */ /* #undef LOGIN_NEEDS_UTMPX */ /* Set this to your mail directory if you do not have _PATH_MAILDIR */ /* #undef MAIL_DIRECTORY */ /* Need setpgrp to acquire controlling tty */ /* #undef NEED_SETPGRP */ /* compiler does not accept __attribute__ on return types */ /* #undef NO_ATTRIBUTE_ON_RETURN_TYPE */ /* Define if you don't want to use lastlog in session.c */ /* #undef NO_SSH_LASTLOG */ /* Define to disable UID restoration test */ /* #undef NO_UID_RESTORATION_TEST */ /* Define if X11 doesn't support AF_UNIX sockets on that system */ /* #undef NO_X11_UNIX_SOCKETS */ /* Define if EVP_DigestUpdate returns void */ /* #undef OPENSSL_EVP_DIGESTUPDATE_VOID */ /* OpenSSL has ECC */ #define OPENSSL_HAS_ECC 1 /* libcrypto has NID_X9_62_prime256v1 */ #define OPENSSL_HAS_NISTP256 1 /* libcrypto has NID_secp384r1 */ #define OPENSSL_HAS_NISTP384 1 /* libcrypto has NID_secp521r1 */ #define OPENSSL_HAS_NISTP521 1 /* libcrypto has EVP AES CTR */ #define OPENSSL_HAVE_EVPCTR 1 /* libcrypto has EVP AES GCM */ #define OPENSSL_HAVE_EVPGCM 1 /* libcrypto is missing AES 192 and 256 bit functions */ /* #undef OPENSSL_LOBOTOMISED_AES */ /* Define if you want the OpenSSL internally seeded PRNG only */ #define OPENSSL_PRNG_ONLY 1 /* Define to the address where bug reports for this package should be sent. */ #define PACKAGE_BUGREPORT "openssh-unix-dev@mindrot.org" /* Define to the full name of this package. */ #define PACKAGE_NAME "OpenSSH" /* Define to the full name and version of this package. */ #define PACKAGE_STRING "OpenSSH Portable" /* Define to the one symbol short name of this package. */ #define PACKAGE_TARNAME "openssh" /* Define to the home page for this package. */ #define PACKAGE_URL "" /* Define to the version of this package. */ #define PACKAGE_VERSION "Portable" /* Define if you are using Solaris-derived PAM which passes pam_messages to the conversation function with an extra level of indirection */ /* #undef PAM_SUN_CODEBASE */ /* Work around problematic Linux PAM modules handling of PAM_TTY */ /* #undef PAM_TTY_KLUDGE */ /* must supply username to passwd */ /* #undef PASSWD_NEEDS_USERNAME */ /* System dirs owned by bin (uid 2) */ /* #undef PLATFORM_SYS_DIR_UID */ /* Port number of PRNGD/EGD random number socket */ /* #undef PRNGD_PORT */ /* Location of PRNGD/EGD random number socket */ /* #undef PRNGD_SOCKET */ /* read(1) can return 0 for a non-closed fd */ /* #undef PTY_ZEROREAD */ /* Sandbox using capsicum */ #define SANDBOX_CAPSICUM 1 /* Sandbox using Darwin sandbox_init(3) */ /* #undef SANDBOX_DARWIN */ /* no privsep sandboxing */ /* #undef SANDBOX_NULL */ /* Sandbox using pledge(2) */ /* #undef SANDBOX_PLEDGE */ /* Sandbox using setrlimit(2) */ /* #undef SANDBOX_RLIMIT */ /* Sandbox using seccomp filter */ /* #undef SANDBOX_SECCOMP_FILTER */ /* setrlimit RLIMIT_FSIZE works */ /* #undef SANDBOX_SKIP_RLIMIT_FSIZE */ /* define if setrlimit RLIMIT_NOFILE breaks things */ #define SANDBOX_SKIP_RLIMIT_NOFILE 1 /* Sandbox using Solaris/Illumos privileges */ /* #undef SANDBOX_SOLARIS */ /* Sandbox using systrace(4) */ /* #undef SANDBOX_SYSTRACE */ /* Specify the system call convention in use */ /* #undef SECCOMP_AUDIT_ARCH */ /* Define if your platform breaks doing a seteuid before a setuid */ /* #undef SETEUID_BREAKS_SETUID */ /* The size of `int', as computed by sizeof. */ #define SIZEOF_INT 4 /* The size of `long int', as computed by sizeof. */ #define SIZEOF_LONG_INT 8 /* The size of `long long int', as computed by sizeof. */ #define SIZEOF_LONG_LONG_INT 8 /* The size of `short int', as computed by sizeof. */ #define SIZEOF_SHORT_INT 2 /* Define if you want S/Key support */ /* #undef SKEY */ /* Define if your skeychallenge() function takes 4 arguments (NetBSD) */ /* #undef SKEYCHALLENGE_4ARG */ /* Define as const if snprintf() can declare const char *fmt */ #define SNPRINTF_CONST const /* Define to a Set Process Title type if your system is supported by bsd-setproctitle.c */ /* #undef SPT_TYPE */ /* Define if sshd somehow reacquires a controlling TTY after setsid() */ /* #undef SSHD_ACQUIRES_CTTY */ /* sshd PAM service name */ /* #undef SSHD_PAM_SERVICE */ /* Define if pam_chauthtok wants real uid set to the unpriv'ed user */ /* #undef SSHPAM_CHAUTHTOK_NEEDS_RUID */ /* Use audit debugging module */ /* #undef SSH_AUDIT_EVENTS */ /* Windows is sensitive to read buffer size */ /* #undef SSH_IOBUFSZ */ /* non-privileged user for privilege separation */ #define SSH_PRIVSEP_USER "sshd" /* Use tunnel device compatibility to OpenBSD */ /* #undef SSH_TUN_COMPAT_AF */ /* Open tunnel devices the FreeBSD way */ #define SSH_TUN_FREEBSD 1 /* Open tunnel devices the Linux tun/tap way */ /* #undef SSH_TUN_LINUX */ /* No layer 2 tunnel support */ /* #undef SSH_TUN_NO_L2 */ /* Open tunnel devices the OpenBSD way */ /* #undef SSH_TUN_OPENBSD */ /* Prepend the address family to IP tunnel traffic */ /* #undef SSH_TUN_PREPEND_AF */ /* Define to 1 if you have the ANSI C header files. */ #define STDC_HEADERS 1 /* Define if you want a different $PATH for the superuser */ /* #undef SUPERUSER_PATH */ /* syslog_r function is safe to use in in a signal handler */ /* #undef SYSLOG_R_SAFE_IN_SIGHAND */ /* Support passwords > 8 chars */ /* #undef UNIXWARE_LONG_PASSWORDS */ /* Specify default $PATH */ /* #undef USER_PATH */ /* Define this if you want to use libkafs' AFS support */ /* #undef USE_AFS */ /* Use BSM audit module */ /* #undef USE_BSM_AUDIT */ /* Use btmp to log bad logins */ /* #undef USE_BTMP */ /* Use libedit for sftp */ #define USE_LIBEDIT 1 /* Use Linux audit module */ /* #undef USE_LINUX_AUDIT */ /* Enable OpenSSL engine support */ #define USE_OPENSSL_ENGINE 1 /* Define if you want to enable PAM support */ #define USE_PAM 1 /* Use PIPES instead of a socketpair() */ /* #undef USE_PIPES */ /* Define if you have Solaris privileges */ /* #undef USE_SOLARIS_PRIVS */ /* Define if you have Solaris process contracts */ /* #undef USE_SOLARIS_PROCESS_CONTRACTS */ /* Define if you have Solaris projects */ /* #undef USE_SOLARIS_PROJECTS */ /* Define if you shouldn't strip 'tty' from your ttyname in [uw]tmp */ /* #undef WITH_ABBREV_NO_TTY */ /* Define if you want to enable AIX4's authenticate function */ /* #undef WITH_AIXAUTHENTICATE */ /* Define if you have/want arrays (cluster-wide session managment, not C arrays) */ /* #undef WITH_IRIX_ARRAY */ /* Define if you want IRIX audit trails */ /* #undef WITH_IRIX_AUDIT */ /* Define if you want IRIX kernel jobs */ /* #undef WITH_IRIX_JOBS */ /* Define if you want IRIX project management */ /* #undef WITH_IRIX_PROJECT */ /* use libcrypto for cryptography */ #define WITH_OPENSSL 1 /* Define if you want SELinux support. */ /* #undef WITH_SELINUX */ /* include SSH protocol version 1 support */ /* #undef WITH_SSH1 */ /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ #if defined AC_APPLE_UNIVERSAL_BUILD # if defined __BIG_ENDIAN__ # define WORDS_BIGENDIAN 1 # endif #else # ifndef WORDS_BIGENDIAN /* # undef WORDS_BIGENDIAN */ # endif #endif /* Define if xauth is found in your path */ /* #undef XAUTH_PATH */ /* Enable large inode numbers on Mac OS X 10.5. */ #ifndef _DARWIN_USE_64_BIT_INODE # define _DARWIN_USE_64_BIT_INODE 1 #endif /* Number of bits in a file offset, on hosts where this is settable. */ /* #undef _FILE_OFFSET_BITS */ /* Define for large files, on AIX-style hosts. */ /* #undef _LARGE_FILES */ /* log for bad login attempts */ /* #undef _PATH_BTMP */ /* Full path of your "passwd" program */ #define _PATH_PASSWD_PROG "/usr/bin/passwd" /* Specify location of ssh.pid */ #define _PATH_SSH_PIDDIR "/var/run" /* Define if we don't have struct __res_state in resolv.h */ /* #undef __res_state */ /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus /* #undef inline */ #endif /* type to use in place of socklen_t if not defined */ /* #undef socklen_t */ diff --git a/crypto/openssh/configure.ac b/crypto/openssh/configure.ac index cedc4c53ab78..f303107e1506 100644 --- a/crypto/openssh/configure.ac +++ b/crypto/openssh/configure.ac @@ -1,5202 +1,5214 @@ # $Id: configure.ac,v 1.583 2014/08/26 20:32:01 djm Exp $ # $FreeBSD$ # # Copyright (c) 1999-2004 Damien Miller # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. AC_INIT([OpenSSH], [Portable], [openssh-unix-dev@mindrot.org]) AC_REVISION($Revision: 1.583 $) AC_CONFIG_SRCDIR([ssh.c]) AC_LANG([C]) AC_CONFIG_HEADER([config.h]) AC_PROG_CC AC_CANONICAL_HOST AC_C_BIGENDIAN # Checks for programs. AC_PROG_AWK AC_PROG_CPP AC_PROG_RANLIB AC_PROG_INSTALL AC_PROG_EGREP AC_CHECK_TOOLS([AR], [ar]) AC_PATH_PROG([CAT], [cat]) AC_PATH_PROG([KILL], [kill]) AC_PATH_PROGS([PERL], [perl5 perl]) AC_PATH_PROG([SED], [sed]) AC_SUBST([PERL]) AC_PATH_PROG([ENT], [ent]) AC_SUBST([ENT]) AC_PATH_PROG([TEST_MINUS_S_SH], [bash]) AC_PATH_PROG([TEST_MINUS_S_SH], [ksh]) AC_PATH_PROG([TEST_MINUS_S_SH], [sh]) AC_PATH_PROG([SH], [sh]) AC_PATH_PROG([GROFF], [groff]) AC_PATH_PROG([NROFF], [nroff]) AC_PATH_PROG([MANDOC], [mandoc]) AC_SUBST([TEST_SHELL], [sh]) dnl select manpage formatter if test "x$MANDOC" != "x" ; then MANFMT="$MANDOC" elif test "x$NROFF" != "x" ; then MANFMT="$NROFF -mandoc" elif test "x$GROFF" != "x" ; then MANFMT="$GROFF -mandoc -Tascii" else AC_MSG_WARN([no manpage formatted found]) MANFMT="false" fi AC_SUBST([MANFMT]) dnl for buildpkg.sh AC_PATH_PROG([PATH_GROUPADD_PROG], [groupadd], [groupadd], [/usr/sbin${PATH_SEPARATOR}/etc]) AC_PATH_PROG([PATH_USERADD_PROG], [useradd], [useradd], [/usr/sbin${PATH_SEPARATOR}/etc]) AC_CHECK_PROG([MAKE_PACKAGE_SUPPORTED], [pkgmk], [yes], [no]) if test -x /sbin/sh; then AC_SUBST([STARTUP_SCRIPT_SHELL], [/sbin/sh]) else AC_SUBST([STARTUP_SCRIPT_SHELL], [/bin/sh]) fi # System features AC_SYS_LARGEFILE if test -z "$AR" ; then AC_MSG_ERROR([*** 'ar' missing, please install or fix your \$PATH ***]) fi AC_PATH_PROG([PATH_PASSWD_PROG], [passwd]) if test ! -z "$PATH_PASSWD_PROG" ; then AC_DEFINE_UNQUOTED([_PATH_PASSWD_PROG], ["$PATH_PASSWD_PROG"], [Full path of your "passwd" program]) fi if test -z "$LD" ; then LD=$CC fi AC_SUBST([LD]) AC_C_INLINE AC_CHECK_DECL([LLONG_MAX], [have_llong_max=1], , [#include ]) AC_CHECK_DECL([SYSTR_POLICY_KILL], [have_systr_policy_kill=1], , [ #include #include #include ]) AC_CHECK_DECL([RLIMIT_NPROC], [AC_DEFINE([HAVE_RLIMIT_NPROC], [], [sys/resource.h has RLIMIT_NPROC])], , [ #include #include ]) AC_CHECK_DECL([PR_SET_NO_NEW_PRIVS], [have_linux_no_new_privs=1], , [ #include #include ]) openssl=yes ssh1=no COMMENT_OUT_RSA1="#no ssh1#" AC_ARG_WITH([openssl], [ --without-openssl Disable use of OpenSSL; use only limited internal crypto **EXPERIMENTAL** ], [ if test "x$withval" = "xno" ; then openssl=no ssh1=no fi ] ) AC_MSG_CHECKING([whether OpenSSL will be used for cryptography]) if test "x$openssl" = "xyes" ; then AC_MSG_RESULT([yes]) AC_DEFINE_UNQUOTED([WITH_OPENSSL], [1], [use libcrypto for cryptography]) else AC_MSG_RESULT([no]) fi AC_ARG_WITH([ssh1], [ --with-ssh1 Enable support for SSH protocol 1], [ if test "x$withval" = "xyes" ; then if test "x$openssl" = "xno" ; then AC_MSG_ERROR([Cannot enable SSH protocol 1 with OpenSSL disabled]) fi ssh1=yes COMMENT_OUT_RSA1="" elif test "x$withval" = "xno" ; then ssh1=no else AC_MSG_ERROR([unknown --with-ssh1 argument]) fi ] ) AC_MSG_CHECKING([whether SSH protocol 1 support is enabled]) if test "x$ssh1" = "xyes" ; then AC_MSG_RESULT([yes]) AC_DEFINE_UNQUOTED([WITH_SSH1], [1], [include SSH protocol version 1 support]) AC_SUBST([COMMENT_OUT_RSA1]) else AC_MSG_RESULT([no]) fi use_stack_protector=1 use_toolchain_hardening=1 AC_ARG_WITH([stackprotect], [ --without-stackprotect Don't use compiler's stack protection], [ if test "x$withval" = "xno"; then use_stack_protector=0 fi ]) AC_ARG_WITH([hardening], [ --without-hardening Don't use toolchain hardening flags], [ if test "x$withval" = "xno"; then use_toolchain_hardening=0 fi ]) # We use -Werror for the tests only so that we catch warnings like "this is # on by default" for things like -fPIE. AC_MSG_CHECKING([if $CC supports -Werror]) saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Werror" AC_COMPILE_IFELSE([AC_LANG_SOURCE([[int main(void) { return 0; }]])], [ AC_MSG_RESULT([yes]) WERROR="-Werror"], [ AC_MSG_RESULT([no]) WERROR="" ] ) CFLAGS="$saved_CFLAGS" if test "$GCC" = "yes" || test "$GCC" = "egcs"; then OSSH_CHECK_CFLAG_COMPILE([-Qunused-arguments]) OSSH_CHECK_CFLAG_COMPILE([-Wunknown-warning-option]) OSSH_CHECK_CFLAG_COMPILE([-Wall]) OSSH_CHECK_CFLAG_COMPILE([-Wpointer-arith]) OSSH_CHECK_CFLAG_COMPILE([-Wuninitialized]) OSSH_CHECK_CFLAG_COMPILE([-Wsign-compare]) OSSH_CHECK_CFLAG_COMPILE([-Wformat-security]) OSSH_CHECK_CFLAG_COMPILE([-Wsizeof-pointer-memaccess]) OSSH_CHECK_CFLAG_COMPILE([-Wpointer-sign], [-Wno-pointer-sign]) OSSH_CHECK_CFLAG_COMPILE([-Wunused-result], [-Wno-unused-result]) OSSH_CHECK_CFLAG_COMPILE([-fno-strict-aliasing]) OSSH_CHECK_CFLAG_COMPILE([-D_FORTIFY_SOURCE=2]) if test "x$use_toolchain_hardening" = "x1"; then OSSH_CHECK_LDFLAG_LINK([-Wl,-z,relro]) OSSH_CHECK_LDFLAG_LINK([-Wl,-z,now]) OSSH_CHECK_LDFLAG_LINK([-Wl,-z,noexecstack]) # NB. -ftrapv expects certain support functions to be present in # the compiler library (libgcc or similar) to detect integer operations # that can overflow. We must check that the result of enabling it # actually links. The test program compiled/linked includes a number # of integer operations that should exercise this. OSSH_CHECK_CFLAG_LINK([-ftrapv]) fi AC_MSG_CHECKING([gcc version]) GCC_VER=`$CC -v 2>&1 | $AWK '/gcc version /{print $3}'` case $GCC_VER in 1.*) no_attrib_nonnull=1 ;; 2.8* | 2.9*) no_attrib_nonnull=1 ;; 2.*) no_attrib_nonnull=1 ;; *) ;; esac AC_MSG_RESULT([$GCC_VER]) AC_MSG_CHECKING([if $CC accepts -fno-builtin-memset]) saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -fno-builtin-memset" AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ char b[10]; memset(b, 0, sizeof(b)); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) CFLAGS="$saved_CFLAGS" ] ) # -fstack-protector-all doesn't always work for some GCC versions # and/or platforms, so we test if we can. If it's not supported # on a given platform gcc will emit a warning so we use -Werror. if test "x$use_stack_protector" = "x1"; then for t in -fstack-protector-strong -fstack-protector-all \ -fstack-protector; do AC_MSG_CHECKING([if $CC supports $t]) saved_CFLAGS="$CFLAGS" saved_LDFLAGS="$LDFLAGS" CFLAGS="$CFLAGS $t -Werror" LDFLAGS="$LDFLAGS $t -Werror" AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include ]], [[ char x[256]; snprintf(x, sizeof(x), "XXX"); ]])], [ AC_MSG_RESULT([yes]) CFLAGS="$saved_CFLAGS $t" LDFLAGS="$saved_LDFLAGS $t" AC_MSG_CHECKING([if $t works]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include ]], [[ char x[256]; snprintf(x, sizeof(x), "XXX"); ]])], [ AC_MSG_RESULT([yes]) break ], [ AC_MSG_RESULT([no]) ], [ AC_MSG_WARN([cross compiling: cannot test]) break ] ) ], [ AC_MSG_RESULT([no]) ] ) CFLAGS="$saved_CFLAGS" LDFLAGS="$saved_LDFLAGS" done fi if test -z "$have_llong_max"; then # retry LLONG_MAX with -std=gnu99, needed on some Linuxes unset ac_cv_have_decl_LLONG_MAX saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -std=gnu99" AC_CHECK_DECL([LLONG_MAX], [have_llong_max=1], [CFLAGS="$saved_CFLAGS"], [#include ] ) fi fi AC_MSG_CHECKING([if compiler allows __attribute__ on return types]) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[ #include __attribute__((__unused__)) static void foo(void){return;}]], [[ exit(0); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) AC_DEFINE(NO_ATTRIBUTE_ON_RETURN_TYPE, 1, [compiler does not accept __attribute__ on return types]) ] ) if test "x$no_attrib_nonnull" != "x1" ; then AC_DEFINE([HAVE_ATTRIBUTE__NONNULL__], [1], [Have attribute nonnull]) fi AC_ARG_WITH([rpath], [ --without-rpath Disable auto-added -R linker paths], [ if test "x$withval" = "xno" ; then need_dash_r="" fi if test "x$withval" = "xyes" ; then need_dash_r=1 fi ] ) # Allow user to specify flags AC_ARG_WITH([cflags], [ --with-cflags Specify additional flags to pass to compiler], [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then CFLAGS="$CFLAGS $withval" fi ] ) AC_ARG_WITH([cppflags], [ --with-cppflags Specify additional flags to pass to preprocessor] , [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then CPPFLAGS="$CPPFLAGS $withval" fi ] ) AC_ARG_WITH([ldflags], [ --with-ldflags Specify additional flags to pass to linker], [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then LDFLAGS="$LDFLAGS $withval" fi ] ) AC_ARG_WITH([libs], [ --with-libs Specify additional libraries to link with], [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then LIBS="$LIBS $withval" fi ] ) AC_ARG_WITH([Werror], [ --with-Werror Build main code with -Werror], [ if test -n "$withval" && test "x$withval" != "xno"; then werror_flags="-Werror" if test "x${withval}" != "xyes"; then werror_flags="$withval" fi fi ] ) AC_CHECK_HEADERS([ \ blf.h \ bstring.h \ crypt.h \ crypto/sha2.h \ dirent.h \ endian.h \ elf.h \ err.h \ features.h \ fcntl.h \ floatingpoint.h \ getopt.h \ glob.h \ ia.h \ iaf.h \ inttypes.h \ langinfo.h \ limits.h \ locale.h \ login.h \ maillock.h \ ndir.h \ net/if_tun.h \ netdb.h \ netgroup.h \ pam/pam_appl.h \ paths.h \ poll.h \ pty.h \ readpassphrase.h \ rpc/types.h \ security/pam_appl.h \ sha2.h \ shadow.h \ stddef.h \ stdint.h \ string.h \ strings.h \ sys/audit.h \ sys/bitypes.h \ sys/bsdtty.h \ sys/cdefs.h \ sys/dir.h \ sys/mman.h \ sys/ndir.h \ sys/poll.h \ sys/prctl.h \ sys/pstat.h \ sys/ptrace.h \ sys/select.h \ sys/stat.h \ sys/stream.h \ sys/stropts.h \ sys/strtio.h \ sys/statvfs.h \ sys/sysmacros.h \ sys/time.h \ sys/timers.h \ time.h \ tmpdir.h \ ttyent.h \ ucred.h \ unistd.h \ usersec.h \ util.h \ utime.h \ utmp.h \ utmpx.h \ vis.h \ wchar.h \ ]) # sys/capsicum.h requires sys/types.h AC_CHECK_HEADERS([sys/capsicum.h], [], [], [ #ifdef HAVE_SYS_TYPES_H # include #endif ]) # lastlog.h requires sys/time.h to be included first on Solaris AC_CHECK_HEADERS([lastlog.h], [], [], [ #ifdef HAVE_SYS_TIME_H # include #endif ]) # sys/ptms.h requires sys/stream.h to be included first on Solaris AC_CHECK_HEADERS([sys/ptms.h], [], [], [ #ifdef HAVE_SYS_STREAM_H # include #endif ]) # login_cap.h requires sys/types.h on NetBSD AC_CHECK_HEADERS([login_cap.h], [], [], [ #include ]) # older BSDs need sys/param.h before sys/mount.h AC_CHECK_HEADERS([sys/mount.h], [], [], [ #include ]) # Android requires sys/socket.h to be included before sys/un.h AC_CHECK_HEADERS([sys/un.h], [], [], [ #include #include ]) # Messages for features tested for in target-specific section SIA_MSG="no" SPC_MSG="no" SP_MSG="no" SPP_MSG="no" # Support for Solaris/Illumos privileges (this test is used by both # the --with-solaris-privs option and --with-sandbox=solaris). SOLARIS_PRIVS="no" # Check for some target-specific stuff case "$host" in *-*-aix*) # Some versions of VAC won't allow macro redefinitions at # -qlanglevel=ansi, and autoconf 2.60 sometimes insists on using that # particularly with older versions of vac or xlc. # It also throws errors about null macro argments, but these are # not fatal. AC_MSG_CHECKING([if compiler allows macro redefinitions]) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[ #define testmacro foo #define testmacro bar]], [[ exit(0); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) CC="`echo $CC | sed 's/-qlanglvl\=ansi//g'`" LD="`echo $LD | sed 's/-qlanglvl\=ansi//g'`" CFLAGS="`echo $CFLAGS | sed 's/-qlanglvl\=ansi//g'`" CPPFLAGS="`echo $CPPFLAGS | sed 's/-qlanglvl\=ansi//g'`" ] ) AC_MSG_CHECKING([how to specify blibpath for linker ($LD)]) if (test -z "$blibpath"); then blibpath="/usr/lib:/lib" fi saved_LDFLAGS="$LDFLAGS" if test "$GCC" = "yes"; then flags="-Wl,-blibpath: -Wl,-rpath, -blibpath:" else flags="-blibpath: -Wl,-blibpath: -Wl,-rpath," fi for tryflags in $flags ;do if (test -z "$blibflags"); then LDFLAGS="$saved_LDFLAGS $tryflags$blibpath" AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[]])], [blibflags=$tryflags], []) fi done if (test -z "$blibflags"); then AC_MSG_RESULT([not found]) AC_MSG_ERROR([*** must be able to specify blibpath on AIX - check config.log]) else AC_MSG_RESULT([$blibflags]) fi LDFLAGS="$saved_LDFLAGS" dnl Check for authenticate. Might be in libs.a on older AIXes AC_CHECK_FUNC([authenticate], [AC_DEFINE([WITH_AIXAUTHENTICATE], [1], [Define if you want to enable AIX4's authenticate function])], [AC_CHECK_LIB([s], [authenticate], [ AC_DEFINE([WITH_AIXAUTHENTICATE]) LIBS="$LIBS -ls" ]) ]) dnl Check for various auth function declarations in headers. AC_CHECK_DECLS([authenticate, loginrestrictions, loginsuccess, passwdexpired, setauthdb], , , [#include ]) dnl Check if loginfailed is declared and takes 4 arguments (AIX >= 5.2) AC_CHECK_DECLS([loginfailed], [AC_MSG_CHECKING([if loginfailed takes 4 arguments]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ (void)loginfailed("user","host","tty",0); ]])], [AC_MSG_RESULT([yes]) AC_DEFINE([AIX_LOGINFAILED_4ARG], [1], [Define if your AIX loginfailed() function takes 4 arguments (AIX >= 5.2)])], [AC_MSG_RESULT([no]) ])], [], [#include ] ) AC_CHECK_FUNCS([getgrset setauthdb]) AC_CHECK_DECL([F_CLOSEM], AC_DEFINE([HAVE_FCNTL_CLOSEM], [1], [Use F_CLOSEM fcntl for closefrom]), [], [ #include #include ] ) check_for_aix_broken_getaddrinfo=1 AC_DEFINE([BROKEN_REALPATH], [1], [Define if you have a broken realpath.]) AC_DEFINE([SETEUID_BREAKS_SETUID], [1], [Define if your platform breaks doing a seteuid before a setuid]) AC_DEFINE([BROKEN_SETREUID], [1], [Define if your setreuid() is broken]) AC_DEFINE([BROKEN_SETREGID], [1], [Define if your setregid() is broken]) dnl AIX handles lastlog as part of its login message AC_DEFINE([DISABLE_LASTLOG], [1], [Define if you don't want to use lastlog]) AC_DEFINE([LOGIN_NEEDS_UTMPX], [1], [Some systems need a utmpx entry for /bin/login to work]) AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV], [Define to a Set Process Title type if your system is supported by bsd-setproctitle.c]) AC_DEFINE([SSHPAM_CHAUTHTOK_NEEDS_RUID], [1], [AIX 5.2 and 5.3 (and presumably newer) require this]) AC_DEFINE([PTY_ZEROREAD], [1], [read(1) can return 0 for a non-closed fd]) AC_DEFINE([PLATFORM_SYS_DIR_UID], 2, [System dirs owned by bin (uid 2)]) ;; *-*-android*) AC_DEFINE([DISABLE_UTMP], [1], [Define if you don't want to use utmp]) AC_DEFINE([DISABLE_WTMP], [1], [Define if you don't want to use wtmp]) ;; *-*-cygwin*) check_for_libcrypt_later=1 LIBS="$LIBS /usr/lib/textreadmode.o" AC_DEFINE([HAVE_CYGWIN], [1], [Define if you are on Cygwin]) AC_DEFINE([USE_PIPES], [1], [Use PIPES instead of a socketpair()]) AC_DEFINE([NO_UID_RESTORATION_TEST], [1], [Define to disable UID restoration test]) AC_DEFINE([DISABLE_SHADOW], [1], [Define if you want to disable shadow passwords]) AC_DEFINE([NO_X11_UNIX_SOCKETS], [1], [Define if X11 doesn't support AF_UNIX sockets on that system]) AC_DEFINE([DISABLE_FD_PASSING], [1], [Define if your platform needs to skip post auth file descriptor passing]) AC_DEFINE([SSH_IOBUFSZ], [65535], [Windows is sensitive to read buffer size]) AC_DEFINE([FILESYSTEM_NO_BACKSLASH], [1], [File names may not contain backslash characters]) # Cygwin defines optargs, optargs as declspec(dllimport) for historical # reasons which cause compile warnings, so we disable those warnings. OSSH_CHECK_CFLAG_COMPILE([-Wno-attributes]) ;; *-*-dgux*) AC_DEFINE([IP_TOS_IS_BROKEN], [1], [Define if your system choked on IP TOS setting]) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) ;; *-*-darwin*) use_pie=auto AC_MSG_CHECKING([if we have working getaddrinfo]) AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include main() { if (NSVersionOfRunTimeLibrary("System") >= (60 << 16)) exit(0); else exit(1); } ]])], [AC_MSG_RESULT([working])], [AC_MSG_RESULT([buggy]) AC_DEFINE([BROKEN_GETADDRINFO], [1], [getaddrinfo is broken (if present)]) ], [AC_MSG_RESULT([assume it is working])]) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) AC_DEFINE([BROKEN_GLOB], [1], [OS X glob does not do what we expect]) AC_DEFINE_UNQUOTED([BIND_8_COMPAT], [1], [Define if your resolver libs need this for getrrsetbyname]) AC_DEFINE([SSH_TUN_FREEBSD], [1], [Open tunnel devices the FreeBSD way]) AC_DEFINE([SSH_TUN_COMPAT_AF], [1], [Use tunnel device compatibility to OpenBSD]) AC_DEFINE([SSH_TUN_PREPEND_AF], [1], [Prepend the address family to IP tunnel traffic]) m4_pattern_allow([AU_IPv]) AC_CHECK_DECL([AU_IPv4], [], AC_DEFINE([AU_IPv4], [0], [System only supports IPv4 audit records]) [#include ] AC_DEFINE([LASTLOG_WRITE_PUTUTXLINE], [1], [Define if pututxline updates lastlog too]) ) AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV], [Define to a Set Process Title type if your system is supported by bsd-setproctitle.c]) AC_CHECK_FUNCS([sandbox_init]) AC_CHECK_HEADERS([sandbox.h]) AC_CHECK_LIB([sandbox], [sandbox_apply], [ SSHDLIBS="$SSHDLIBS -lsandbox" ]) ;; *-*-dragonfly*) SSHDLIBS="$SSHDLIBS -lcrypt" TEST_MALLOC_OPTIONS="AFGJPRX" ;; *-*-haiku*) LIBS="$LIBS -lbsd " AC_CHECK_LIB([network], [socket]) AC_DEFINE([HAVE_U_INT64_T]) MANTYPE=man ;; *-*-hpux*) # first we define all of the options common to all HP-UX releases CPPFLAGS="$CPPFLAGS -D_HPUX_SOURCE -D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED=1" IPADDR_IN_DISPLAY=yes AC_DEFINE([USE_PIPES]) AC_DEFINE([LOGIN_NEEDS_UTMPX]) AC_DEFINE([LOCKED_PASSWD_STRING], ["*"], [String used in /etc/passwd to denote locked account]) AC_DEFINE([SPT_TYPE], [SPT_PSTAT]) AC_DEFINE([PLATFORM_SYS_DIR_UID], 2, [System dirs owned by bin (uid 2)]) maildir="/var/mail" LIBS="$LIBS -lsec" AC_CHECK_LIB([xnet], [t_error], , [AC_MSG_ERROR([*** -lxnet needed on HP-UX - check config.log ***])]) # next, we define all of the options specific to major releases case "$host" in *-*-hpux10*) if test -z "$GCC"; then CFLAGS="$CFLAGS -Ae" fi ;; *-*-hpux11*) AC_DEFINE([PAM_SUN_CODEBASE], [1], [Define if you are using Solaris-derived PAM which passes pam_messages to the conversation function with an extra level of indirection]) AC_DEFINE([DISABLE_UTMP], [1], [Define if you don't want to use utmp]) AC_DEFINE([USE_BTMP], [1], [Use btmp to log bad logins]) check_for_hpux_broken_getaddrinfo=1 check_for_conflicting_getspnam=1 ;; esac # lastly, we define options specific to minor releases case "$host" in *-*-hpux10.26) AC_DEFINE([HAVE_SECUREWARE], [1], [Define if you have SecureWare-based protected password database]) disable_ptmx_check=yes LIBS="$LIBS -lsecpw" ;; esac ;; *-*-irix5*) PATH="$PATH:/usr/etc" AC_DEFINE([BROKEN_INET_NTOA], [1], [Define if you system's inet_ntoa is busted (e.g. Irix gcc issue)]) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) AC_DEFINE([WITH_ABBREV_NO_TTY], [1], [Define if you shouldn't strip 'tty' from your ttyname in [uw]tmp]) AC_DEFINE([LOCKED_PASSWD_STRING], ["*LK*"]) ;; *-*-irix6*) PATH="$PATH:/usr/etc" AC_DEFINE([WITH_IRIX_ARRAY], [1], [Define if you have/want arrays (cluster-wide session managment, not C arrays)]) AC_DEFINE([WITH_IRIX_PROJECT], [1], [Define if you want IRIX project management]) AC_DEFINE([WITH_IRIX_AUDIT], [1], [Define if you want IRIX audit trails]) AC_CHECK_FUNC([jlimit_startjob], [AC_DEFINE([WITH_IRIX_JOBS], [1], [Define if you want IRIX kernel jobs])]) AC_DEFINE([BROKEN_INET_NTOA]) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) AC_DEFINE([BROKEN_UPDWTMPX], [1], [updwtmpx is broken (if present)]) AC_DEFINE([WITH_ABBREV_NO_TTY]) AC_DEFINE([LOCKED_PASSWD_STRING], ["*LK*"]) ;; *-*-k*bsd*-gnu | *-*-kopensolaris*-gnu) check_for_libcrypt_later=1 AC_DEFINE([PAM_TTY_KLUDGE]) AC_DEFINE([LOCKED_PASSWD_PREFIX], ["!"]) AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV]) AC_DEFINE([_PATH_BTMP], ["/var/log/btmp"], [log for bad login attempts]) AC_DEFINE([USE_BTMP], [1], [Use btmp to log bad logins]) ;; *-*-linux*) no_dev_ptmx=1 use_pie=auto check_for_libcrypt_later=1 check_for_openpty_ctty_bug=1 + dnl Target SUSv3/POSIX.1-2001 plus BSD specifics. + dnl _DEFAULT_SOURCE is the new name for _BSD_SOURCE + CPPFLAGS="$CPPFLAGS -D_XOPEN_SOURCE=600 -D_BSD_SOURCE -D_DEFAULT_SOURCE" AC_DEFINE([PAM_TTY_KLUDGE], [1], [Work around problematic Linux PAM modules handling of PAM_TTY]) AC_DEFINE([LOCKED_PASSWD_PREFIX], ["!"], [String used in /etc/passwd to denote locked account]) AC_DEFINE([SPT_TYPE], [SPT_REUSEARGV]) AC_DEFINE([LINK_OPNOTSUPP_ERRNO], [EPERM], [Define to whatever link() returns for "not supported" if it doesn't return EOPNOTSUPP.]) AC_DEFINE([_PATH_BTMP], ["/var/log/btmp"], [log for bad login attempts]) AC_DEFINE([USE_BTMP]) AC_DEFINE([LINUX_OOM_ADJUST], [1], [Adjust Linux out-of-memory killer]) inet6_default_4in6=yes case `uname -r` in 1.*|2.0.*) AC_DEFINE([BROKEN_CMSG_TYPE], [1], [Define if cmsg_type is not passed correctly]) ;; esac # tun(4) forwarding compat code AC_CHECK_HEADERS([linux/if_tun.h]) if test "x$ac_cv_header_linux_if_tun_h" = "xyes" ; then AC_DEFINE([SSH_TUN_LINUX], [1], [Open tunnel devices the Linux tun/tap way]) AC_DEFINE([SSH_TUN_COMPAT_AF], [1], [Use tunnel device compatibility to OpenBSD]) AC_DEFINE([SSH_TUN_PREPEND_AF], [1], [Prepend the address family to IP tunnel traffic]) fi AC_CHECK_HEADERS([linux/seccomp.h linux/filter.h linux/audit.h], [], [], [#include ]) AC_MSG_CHECKING([for seccomp architecture]) seccomp_audit_arch= case "$host" in x86_64-*) seccomp_audit_arch=AUDIT_ARCH_X86_64 ;; i*86-*) seccomp_audit_arch=AUDIT_ARCH_I386 ;; arm*-*) seccomp_audit_arch=AUDIT_ARCH_ARM ;; aarch64*-*) seccomp_audit_arch=AUDIT_ARCH_AARCH64 ;; s390x-*) seccomp_audit_arch=AUDIT_ARCH_S390X ;; s390-*) seccomp_audit_arch=AUDIT_ARCH_S390 ;; powerpc64-*) seccomp_audit_arch=AUDIT_ARCH_PPC64 ;; powerpc64le-*) seccomp_audit_arch=AUDIT_ARCH_PPC64LE ;; mips-*) seccomp_audit_arch=AUDIT_ARCH_MIPS ;; mipsel-*) seccomp_audit_arch=AUDIT_ARCH_MIPSEL ;; mips64-*) seccomp_audit_arch=AUDIT_ARCH_MIPS64 ;; mips64el-*) seccomp_audit_arch=AUDIT_ARCH_MIPSEL64 ;; esac if test "x$seccomp_audit_arch" != "x" ; then AC_MSG_RESULT(["$seccomp_audit_arch"]) AC_DEFINE_UNQUOTED([SECCOMP_AUDIT_ARCH], [$seccomp_audit_arch], [Specify the system call convention in use]) else AC_MSG_RESULT([architecture not supported]) fi ;; mips-sony-bsd|mips-sony-newsos4) AC_DEFINE([NEED_SETPGRP], [1], [Need setpgrp to acquire controlling tty]) SONY=1 ;; *-*-netbsd*) check_for_libcrypt_before=1 if test "x$withval" != "xno" ; then need_dash_r=1 fi CPPFLAGS="$CPPFLAGS -D_OPENBSD_SOURCE" AC_DEFINE([SSH_TUN_FREEBSD], [1], [Open tunnel devices the FreeBSD way]) AC_CHECK_HEADER([net/if_tap.h], , AC_DEFINE([SSH_TUN_NO_L2], [1], [No layer 2 tunnel support])) AC_DEFINE([SSH_TUN_PREPEND_AF], [1], [Prepend the address family to IP tunnel traffic]) TEST_MALLOC_OPTIONS="AJRX" AC_DEFINE([BROKEN_READ_COMPARISON], [1], [NetBSD read function is sometimes redirected, breaking atomicio comparisons against it]) ;; *-*-freebsd*) check_for_libcrypt_later=1 AC_DEFINE([LOCKED_PASSWD_PREFIX], ["*LOCKED*"], [Account locked with pw(1)]) AC_DEFINE([SSH_TUN_FREEBSD], [1], [Open tunnel devices the FreeBSD way]) AC_CHECK_HEADER([net/if_tap.h], , AC_DEFINE([SSH_TUN_NO_L2], [1], [No layer 2 tunnel support])) AC_DEFINE([BROKEN_GLOB], [1], [FreeBSD glob does not do what we need]) TEST_MALLOC_OPTIONS="AJRX" # Preauth crypto occasionally uses file descriptors for crypto offload # and will crash if they cannot be opened. AC_DEFINE([SANDBOX_SKIP_RLIMIT_NOFILE], [1], [define if setrlimit RLIMIT_NOFILE breaks things]) ;; *-*-bsdi*) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) ;; *-next-*) conf_lastlog_location="/usr/adm/lastlog" conf_utmp_location=/etc/utmp conf_wtmp_location=/usr/adm/wtmp maildir=/usr/spool/mail AC_DEFINE([HAVE_NEXT], [1], [Define if you are on NeXT]) AC_DEFINE([BROKEN_REALPATH]) AC_DEFINE([USE_PIPES]) AC_DEFINE([BROKEN_SAVED_UIDS], [1], [Needed for NeXT]) ;; *-*-openbsd*) use_pie=auto AC_DEFINE([HAVE_ATTRIBUTE__SENTINEL__], [1], [OpenBSD's gcc has sentinel]) AC_DEFINE([HAVE_ATTRIBUTE__BOUNDED__], [1], [OpenBSD's gcc has bounded]) AC_DEFINE([SSH_TUN_OPENBSD], [1], [Open tunnel devices the OpenBSD way]) AC_DEFINE([SYSLOG_R_SAFE_IN_SIGHAND], [1], [syslog_r function is safe to use in in a signal handler]) TEST_MALLOC_OPTIONS="AFGJPRX" ;; *-*-solaris*) if test "x$withval" != "xno" ; then need_dash_r=1 fi AC_DEFINE([PAM_SUN_CODEBASE]) AC_DEFINE([LOGIN_NEEDS_UTMPX]) AC_DEFINE([PAM_TTY_KLUDGE]) AC_DEFINE([SSHPAM_CHAUTHTOK_NEEDS_RUID], [1], [Define if pam_chauthtok wants real uid set to the unpriv'ed user]) AC_DEFINE([LOCKED_PASSWD_STRING], ["*LK*"]) # Pushing STREAMS modules will cause sshd to acquire a controlling tty. AC_DEFINE([SSHD_ACQUIRES_CTTY], [1], [Define if sshd somehow reacquires a controlling TTY after setsid()]) AC_DEFINE([PASSWD_NEEDS_USERNAME], [1], [must supply username to passwd in case the name is longer than 8 chars]) AC_DEFINE([BROKEN_TCGETATTR_ICANON], [1], [tcgetattr with ICANON may hang]) external_path_file=/etc/default/login # hardwire lastlog location (can't detect it on some versions) conf_lastlog_location="/var/adm/lastlog" AC_MSG_CHECKING([for obsolete utmp and wtmp in solaris2.x]) sol2ver=`echo "$host"| sed -e 's/.*[[0-9]]\.//'` if test "$sol2ver" -ge 8; then AC_MSG_RESULT([yes]) AC_DEFINE([DISABLE_UTMP]) AC_DEFINE([DISABLE_WTMP], [1], [Define if you don't want to use wtmp]) else AC_MSG_RESULT([no]) fi AC_CHECK_FUNCS([setpflags]) AC_CHECK_FUNCS([setppriv]) AC_CHECK_FUNCS([priv_basicset]) AC_CHECK_HEADERS([priv.h]) AC_ARG_WITH([solaris-contracts], [ --with-solaris-contracts Enable Solaris process contracts (experimental)], [ AC_CHECK_LIB([contract], [ct_tmpl_activate], [ AC_DEFINE([USE_SOLARIS_PROCESS_CONTRACTS], [1], [Define if you have Solaris process contracts]) LIBS="$LIBS -lcontract" SPC_MSG="yes" ], ) ], ) AC_ARG_WITH([solaris-projects], [ --with-solaris-projects Enable Solaris projects (experimental)], [ AC_CHECK_LIB([project], [setproject], [ AC_DEFINE([USE_SOLARIS_PROJECTS], [1], [Define if you have Solaris projects]) LIBS="$LIBS -lproject" SP_MSG="yes" ], ) ], ) AC_ARG_WITH([solaris-privs], [ --with-solaris-privs Enable Solaris/Illumos privileges (experimental)], [ AC_MSG_CHECKING([for Solaris/Illumos privilege support]) if test "x$ac_cv_func_setppriv" = "xyes" -a \ "x$ac_cv_header_priv_h" = "xyes" ; then SOLARIS_PRIVS=yes AC_MSG_RESULT([found]) AC_DEFINE([NO_UID_RESTORATION_TEST], [1], [Define to disable UID restoration test]) AC_DEFINE([USE_SOLARIS_PRIVS], [1], [Define if you have Solaris privileges]) SPP_MSG="yes" else AC_MSG_RESULT([not found]) AC_MSG_ERROR([*** must have support for Solaris privileges to use --with-solaris-privs]) fi ], ) TEST_SHELL=$SHELL # let configure find us a capable shell ;; *-*-sunos4*) CPPFLAGS="$CPPFLAGS -DSUNOS4" AC_CHECK_FUNCS([getpwanam]) AC_DEFINE([PAM_SUN_CODEBASE]) conf_utmp_location=/etc/utmp conf_wtmp_location=/var/adm/wtmp conf_lastlog_location=/var/adm/lastlog AC_DEFINE([USE_PIPES]) ;; *-ncr-sysv*) LIBS="$LIBS -lc89" AC_DEFINE([USE_PIPES]) AC_DEFINE([SSHD_ACQUIRES_CTTY]) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) ;; *-sni-sysv*) # /usr/ucblib MUST NOT be searched on ReliantUNIX AC_CHECK_LIB([dl], [dlsym], ,) # -lresolv needs to be at the end of LIBS or DNS lookups break AC_CHECK_LIB([resolv], [res_query], [ LIBS="$LIBS -lresolv" ]) IPADDR_IN_DISPLAY=yes AC_DEFINE([USE_PIPES]) AC_DEFINE([IP_TOS_IS_BROKEN]) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) AC_DEFINE([SSHD_ACQUIRES_CTTY]) external_path_file=/etc/default/login # /usr/ucblib/libucb.a no longer needed on ReliantUNIX # Attention: always take care to bind libsocket and libnsl before libc, # otherwise you will find lots of "SIOCGPGRP errno 22" on syslog ;; # UnixWare 1.x, UnixWare 2.x, and others based on code from Univel. *-*-sysv4.2*) AC_DEFINE([USE_PIPES]) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) AC_DEFINE([PASSWD_NEEDS_USERNAME], [1], [must supply username to passwd]) AC_DEFINE([LOCKED_PASSWD_STRING], ["*LK*"]) TEST_SHELL=$SHELL # let configure find us a capable shell ;; # UnixWare 7.x, OpenUNIX 8 *-*-sysv5*) CPPFLAGS="$CPPFLAGS -Dvsnprintf=_xvsnprintf -Dsnprintf=_xsnprintf" AC_DEFINE([UNIXWARE_LONG_PASSWORDS], [1], [Support passwords > 8 chars]) AC_DEFINE([USE_PIPES]) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_GETADDRINFO]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) AC_DEFINE([PASSWD_NEEDS_USERNAME]) TEST_SHELL=$SHELL # let configure find us a capable shell case "$host" in *-*-sysv5SCO_SV*) # SCO OpenServer 6.x maildir=/var/spool/mail AC_DEFINE([BROKEN_LIBIAF], [1], [ia_uinfo routines not supported by OS yet]) AC_DEFINE([BROKEN_UPDWTMPX]) AC_CHECK_LIB([prot], [getluid], [ LIBS="$LIBS -lprot" AC_CHECK_FUNCS([getluid setluid], , , [-lprot]) AC_DEFINE([HAVE_SECUREWARE]) AC_DEFINE([DISABLE_SHADOW]) ], , ) ;; *) AC_DEFINE([LOCKED_PASSWD_STRING], ["*LK*"]) check_for_libcrypt_later=1 ;; esac ;; *-*-sysv*) ;; # SCO UNIX and OEM versions of SCO UNIX *-*-sco3.2v4*) AC_MSG_ERROR("This Platform is no longer supported.") ;; # SCO OpenServer 5.x *-*-sco3.2v5*) if test -z "$GCC"; then CFLAGS="$CFLAGS -belf" fi LIBS="$LIBS -lprot -lx -ltinfo -lm" no_dev_ptmx=1 AC_DEFINE([USE_PIPES]) AC_DEFINE([HAVE_SECUREWARE]) AC_DEFINE([DISABLE_SHADOW]) AC_DEFINE([DISABLE_FD_PASSING]) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_GETADDRINFO]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) AC_DEFINE([WITH_ABBREV_NO_TTY]) AC_DEFINE([BROKEN_UPDWTMPX]) AC_DEFINE([PASSWD_NEEDS_USERNAME]) AC_CHECK_FUNCS([getluid setluid]) MANTYPE=man TEST_SHELL=$SHELL # let configure find us a capable shell SKIP_DISABLE_LASTLOG_DEFINE=yes ;; *-*-unicosmk*) AC_DEFINE([NO_SSH_LASTLOG], [1], [Define if you don't want to use lastlog in session.c]) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) AC_DEFINE([USE_PIPES]) AC_DEFINE([DISABLE_FD_PASSING]) LDFLAGS="$LDFLAGS" LIBS="$LIBS -lgen -lrsc -lshare -luex -lacm" MANTYPE=cat ;; *-*-unicosmp*) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) AC_DEFINE([WITH_ABBREV_NO_TTY]) AC_DEFINE([USE_PIPES]) AC_DEFINE([DISABLE_FD_PASSING]) LDFLAGS="$LDFLAGS" LIBS="$LIBS -lgen -lacid -ldb" MANTYPE=cat ;; *-*-unicos*) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) AC_DEFINE([USE_PIPES]) AC_DEFINE([DISABLE_FD_PASSING]) AC_DEFINE([NO_SSH_LASTLOG]) LDFLAGS="$LDFLAGS -Wl,-Dmsglevel=334:fatal" LIBS="$LIBS -lgen -lrsc -lshare -luex -lacm" MANTYPE=cat ;; *-dec-osf*) AC_MSG_CHECKING([for Digital Unix SIA]) no_osfsia="" AC_ARG_WITH([osfsia], [ --with-osfsia Enable Digital Unix SIA], [ if test "x$withval" = "xno" ; then AC_MSG_RESULT([disabled]) no_osfsia=1 fi ], ) if test -z "$no_osfsia" ; then if test -f /etc/sia/matrix.conf; then AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_OSF_SIA], [1], [Define if you have Digital Unix Security Integration Architecture]) AC_DEFINE([DISABLE_LOGIN], [1], [Define if you don't want to use your system's login() call]) AC_DEFINE([DISABLE_FD_PASSING]) LIBS="$LIBS -lsecurity -ldb -lm -laud" SIA_MSG="yes" else AC_MSG_RESULT([no]) AC_DEFINE([LOCKED_PASSWD_SUBSTR], ["Nologin"], [String used in /etc/passwd to denote locked account]) fi fi AC_DEFINE([BROKEN_GETADDRINFO]) AC_DEFINE([SETEUID_BREAKS_SETUID]) AC_DEFINE([BROKEN_SETREUID]) AC_DEFINE([BROKEN_SETREGID]) AC_DEFINE([BROKEN_READV_COMPARISON], [1], [Can't do comparisons on readv]) ;; *-*-nto-qnx*) AC_DEFINE([USE_PIPES]) AC_DEFINE([NO_X11_UNIX_SOCKETS]) AC_DEFINE([DISABLE_LASTLOG]) AC_DEFINE([SSHD_ACQUIRES_CTTY]) AC_DEFINE([BROKEN_SHADOW_EXPIRE], [1], [QNX shadow support is broken]) enable_etc_default_login=no # has incompatible /etc/default/login case "$host" in *-*-nto-qnx6*) AC_DEFINE([DISABLE_FD_PASSING]) ;; esac ;; *-*-ultrix*) AC_DEFINE([BROKEN_GETGROUPS], [1], [getgroups(0,NULL) will return -1]) AC_DEFINE([NEED_SETPGRP]) AC_DEFINE([HAVE_SYS_SYSLOG_H], [1], [Force use of sys/syslog.h on Ultrix]) ;; *-*-lynxos) CFLAGS="$CFLAGS -D__NO_INCLUDE_WARN__" AC_DEFINE([BROKEN_SETVBUF], [1], [LynxOS has broken setvbuf() implementation]) ;; esac AC_MSG_CHECKING([compiler and flags for sanity]) AC_RUN_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ exit(0); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) AC_MSG_ERROR([*** compiler cannot create working executables, check config.log ***]) ], [ AC_MSG_WARN([cross compiling: not checking compiler sanity]) ] ) dnl Checks for header files. # Checks for libraries. AC_CHECK_FUNC([setsockopt], , [AC_CHECK_LIB([socket], [setsockopt])]) dnl IRIX and Solaris 2.5.1 have dirname() in libgen AC_CHECK_FUNCS([dirname], [AC_CHECK_HEADERS([libgen.h])] , [ AC_CHECK_LIB([gen], [dirname], [ AC_CACHE_CHECK([for broken dirname], ac_cv_have_broken_dirname, [ save_LIBS="$LIBS" LIBS="$LIBS -lgen" AC_RUN_IFELSE( [AC_LANG_SOURCE([[ #include #include int main(int argc, char **argv) { char *s, buf[32]; strncpy(buf,"/etc", 32); s = dirname(buf); if (!s || strncmp(s, "/", 32) != 0) { exit(1); } else { exit(0); } } ]])], [ ac_cv_have_broken_dirname="no" ], [ ac_cv_have_broken_dirname="yes" ], [ ac_cv_have_broken_dirname="no" ], ) LIBS="$save_LIBS" ]) if test "x$ac_cv_have_broken_dirname" = "xno" ; then LIBS="$LIBS -lgen" AC_DEFINE([HAVE_DIRNAME]) AC_CHECK_HEADERS([libgen.h]) fi ]) ]) AC_CHECK_FUNC([getspnam], , [AC_CHECK_LIB([gen], [getspnam], [LIBS="$LIBS -lgen"])]) AC_SEARCH_LIBS([basename], [gen], [AC_DEFINE([HAVE_BASENAME], [1], [Define if you have the basename function.])]) dnl zlib is required AC_ARG_WITH([zlib], [ --with-zlib=PATH Use zlib in PATH], [ if test "x$withval" = "xno" ; then AC_MSG_ERROR([*** zlib is required ***]) elif test "x$withval" != "xyes"; then if test -d "$withval/lib"; then if test -n "${need_dash_r}"; then LDFLAGS="-L${withval}/lib -R${withval}/lib ${LDFLAGS}" else LDFLAGS="-L${withval}/lib ${LDFLAGS}" fi else if test -n "${need_dash_r}"; then LDFLAGS="-L${withval} -R${withval} ${LDFLAGS}" else LDFLAGS="-L${withval} ${LDFLAGS}" fi fi if test -d "$withval/include"; then CPPFLAGS="-I${withval}/include ${CPPFLAGS}" else CPPFLAGS="-I${withval} ${CPPFLAGS}" fi fi ] ) AC_CHECK_HEADER([zlib.h], ,[AC_MSG_ERROR([*** zlib.h missing - please install first or check config.log ***])]) AC_CHECK_LIB([z], [deflate], , [ saved_CPPFLAGS="$CPPFLAGS" saved_LDFLAGS="$LDFLAGS" save_LIBS="$LIBS" dnl Check default zlib install dir if test -n "${need_dash_r}"; then LDFLAGS="-L/usr/local/lib -R/usr/local/lib ${saved_LDFLAGS}" else LDFLAGS="-L/usr/local/lib ${saved_LDFLAGS}" fi CPPFLAGS="-I/usr/local/include ${saved_CPPFLAGS}" LIBS="$LIBS -lz" AC_TRY_LINK_FUNC([deflate], [AC_DEFINE([HAVE_LIBZ])], [ AC_MSG_ERROR([*** zlib missing - please install first or check config.log ***]) ] ) ] ) AC_ARG_WITH([zlib-version-check], [ --without-zlib-version-check Disable zlib version check], [ if test "x$withval" = "xno" ; then zlib_check_nonfatal=1 fi ] ) AC_MSG_CHECKING([for possibly buggy zlib]) AC_RUN_IFELSE([AC_LANG_PROGRAM([[ #include #include #include ]], [[ int a=0, b=0, c=0, d=0, n, v; n = sscanf(ZLIB_VERSION, "%d.%d.%d.%d", &a, &b, &c, &d); if (n != 3 && n != 4) exit(1); v = a*1000000 + b*10000 + c*100 + d; fprintf(stderr, "found zlib version %s (%d)\n", ZLIB_VERSION, v); /* 1.1.4 is OK */ if (a == 1 && b == 1 && c >= 4) exit(0); /* 1.2.3 and up are OK */ if (v >= 1020300) exit(0); exit(2); ]])], AC_MSG_RESULT([no]), [ AC_MSG_RESULT([yes]) if test -z "$zlib_check_nonfatal" ; then AC_MSG_ERROR([*** zlib too old - check config.log *** Your reported zlib version has known security problems. It's possible your vendor has fixed these problems without changing the version number. If you are sure this is the case, you can disable the check by running "./configure --without-zlib-version-check". If you are in doubt, upgrade zlib to version 1.2.3 or greater. See http://www.gzip.org/zlib/ for details.]) else AC_MSG_WARN([zlib version may have security problems]) fi ], [ AC_MSG_WARN([cross compiling: not checking zlib version]) ] ) dnl UnixWare 2.x AC_CHECK_FUNC([strcasecmp], [], [ AC_CHECK_LIB([resolv], [strcasecmp], [LIBS="$LIBS -lresolv"]) ] ) AC_CHECK_FUNCS([utimes], [], [ AC_CHECK_LIB([c89], [utimes], [AC_DEFINE([HAVE_UTIMES]) LIBS="$LIBS -lc89"]) ] ) dnl Checks for libutil functions AC_CHECK_HEADERS([bsd/libutil.h libutil.h]) AC_SEARCH_LIBS([fmt_scaled], [util bsd]) AC_SEARCH_LIBS([scan_scaled], [util bsd]) AC_SEARCH_LIBS([login], [util bsd]) AC_SEARCH_LIBS([logout], [util bsd]) AC_SEARCH_LIBS([logwtmp], [util bsd]) AC_SEARCH_LIBS([openpty], [util bsd]) AC_SEARCH_LIBS([updwtmp], [util bsd]) AC_CHECK_FUNCS([fmt_scaled scan_scaled login logout openpty updwtmp logwtmp]) # On some platforms, inet_ntop and gethostbyname may be found in libresolv # or libnsl. AC_SEARCH_LIBS([inet_ntop], [resolv nsl]) AC_SEARCH_LIBS([gethostbyname], [resolv nsl]) AC_FUNC_STRFTIME # Check for ALTDIRFUNC glob() extension AC_MSG_CHECKING([for GLOB_ALTDIRFUNC support]) AC_EGREP_CPP([FOUNDIT], [ #include #ifdef GLOB_ALTDIRFUNC FOUNDIT #endif ], [ AC_DEFINE([GLOB_HAS_ALTDIRFUNC], [1], [Define if your system glob() function has the GLOB_ALTDIRFUNC extension]) AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ] ) # Check for g.gl_matchc glob() extension AC_MSG_CHECKING([for gl_matchc field in glob_t]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ glob_t g; g.gl_matchc = 1; ]])], [ AC_DEFINE([GLOB_HAS_GL_MATCHC], [1], [Define if your system glob() function has gl_matchc options in glob_t]) AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ]) # Check for g.gl_statv glob() extension AC_MSG_CHECKING([for gl_statv and GLOB_KEEPSTAT extensions for glob]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ #ifndef GLOB_KEEPSTAT #error "glob does not support GLOB_KEEPSTAT extension" #endif glob_t g; g.gl_statv = NULL; ]])], [ AC_DEFINE([GLOB_HAS_GL_STATV], [1], [Define if your system glob() function has gl_statv options in glob_t]) AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ]) AC_CHECK_DECLS([GLOB_NOMATCH], , , [#include ]) AC_CHECK_DECL([VIS_ALL], , AC_DEFINE(BROKEN_STRNVIS, 1, [missing VIS_ALL]), [#include ]) AC_MSG_CHECKING([whether struct dirent allocates space for d_name]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include ]], [[ struct dirent d; exit(sizeof(d.d_name)<=sizeof(char)); ]])], [AC_MSG_RESULT([yes])], [ AC_MSG_RESULT([no]) AC_DEFINE([BROKEN_ONE_BYTE_DIRENT_D_NAME], [1], [Define if your struct dirent expects you to allocate extra space for d_name]) ], [ AC_MSG_WARN([cross compiling: assuming BROKEN_ONE_BYTE_DIRENT_D_NAME]) AC_DEFINE([BROKEN_ONE_BYTE_DIRENT_D_NAME]) ] ) AC_MSG_CHECKING([for /proc/pid/fd directory]) if test -d "/proc/$$/fd" ; then AC_DEFINE([HAVE_PROC_PID], [1], [Define if you have /proc/$pid/fd]) AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi # Check whether user wants S/Key support SKEY_MSG="no" AC_ARG_WITH([skey], [ --with-skey[[=PATH]] Enable S/Key support (optionally in PATH)], [ if test "x$withval" != "xno" ; then if test "x$withval" != "xyes" ; then CPPFLAGS="$CPPFLAGS -I${withval}/include" LDFLAGS="$LDFLAGS -L${withval}/lib" fi AC_DEFINE([SKEY], [1], [Define if you want S/Key support]) LIBS="-lskey $LIBS" SKEY_MSG="yes" AC_MSG_CHECKING([for s/key support]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include #include ]], [[ char *ff = skey_keyinfo(""); ff=""; exit(0); ]])], [AC_MSG_RESULT([yes])], [ AC_MSG_RESULT([no]) AC_MSG_ERROR([** Incomplete or missing s/key libraries.]) ]) AC_MSG_CHECKING([if skeychallenge takes 4 arguments]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ (void)skeychallenge(NULL,"name","",0); ]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([SKEYCHALLENGE_4ARG], [1], [Define if your skeychallenge() function takes 4 arguments (NetBSD)])], [ AC_MSG_RESULT([no]) ]) fi ] ) # Check whether user wants TCP wrappers support TCPW_MSG="no" AC_ARG_WITH([tcp-wrappers], [ --with-tcp-wrappers[[=PATH]] Enable tcpwrappers support (optionally in PATH)], [ if test "x$withval" != "xno" ; then saved_LIBS="$LIBS" saved_LDFLAGS="$LDFLAGS" saved_CPPFLAGS="$CPPFLAGS" if test -n "${withval}" && \ test "x${withval}" != "xyes"; then if test -d "${withval}/lib"; then if test -n "${need_dash_r}"; then LDFLAGS="-L${withval}/lib -R${withval}/lib ${LDFLAGS}" else LDFLAGS="-L${withval}/lib ${LDFLAGS}" fi else if test -n "${need_dash_r}"; then LDFLAGS="-L${withval} -R${withval} ${LDFLAGS}" else LDFLAGS="-L${withval} ${LDFLAGS}" fi fi if test -d "${withval}/include"; then CPPFLAGS="-I${withval}/include ${CPPFLAGS}" else CPPFLAGS="-I${withval} ${CPPFLAGS}" fi fi LIBS="-lwrap $LIBS" AC_MSG_CHECKING([for libwrap]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include #include #include int deny_severity = 0, allow_severity = 0; ]], [[ hosts_access(0); ]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([LIBWRAP], [1], [Define if you want TCP Wrappers support]) SSHDLIBS="$SSHDLIBS -lwrap" TCPW_MSG="yes" ], [ AC_MSG_ERROR([*** libwrap missing]) - ]) LIBS="$saved_LIBS" fi ] ) # Check whether user wants to use ldns LDNS_MSG="no" AC_ARG_WITH(ldns, [ --with-ldns[[=PATH]] Use ldns for DNSSEC support (optionally in PATH)], - [ - if test "x$withval" != "xno" ; then - - if test "x$withval" != "xyes" ; then - CPPFLAGS="$CPPFLAGS -I${withval}/include" - LDFLAGS="$LDFLAGS -L${withval}/lib" - fi - - AC_DEFINE(HAVE_LDNS, 1, [Define if you want ldns support]) - LIBS="-lldns $LIBS" - LDNS_MSG="yes" + [ + ldns="" + if test "x$withval" = "xyes" ; then + AC_PATH_TOOL([LDNSCONFIG], [ldns-config], [no]) + if test "x$PKGCONFIG" = "xno"; then + CPPFLAGS="$CPPFLAGS -I${withval}/include" + LDFLAGS="$LDFLAGS -L${withval}/lib" + LIBS="-lldns $LIBS" + ldns=yes + else + LIBS="$LIBS `$LDNSCONFIG --libs`" + CPPFLAGS="$CPPFLAGS `$LDNSCONFIG --cflags`" + fi + elif test "x$withval" != "xno" ; then + CPPFLAGS="$CPPFLAGS -I${withval}/include" + LDFLAGS="$LDFLAGS -L${withval}/lib" + LIBS="-lldns $LIBS" + ldns=yes + fi - AC_MSG_CHECKING([for ldns support]) - AC_LINK_IFELSE( - [AC_LANG_SOURCE([[ + # Verify that it works. + if test "x$ldns" = "xyes" ; then + AC_DEFINE(HAVE_LDNS, 1, [Define if you want ldns support]) + LDNS_MSG="yes" + AC_MSG_CHECKING([for ldns support]) + AC_LINK_IFELSE( + [AC_LANG_SOURCE([[ #include #include #include #include int main() { ldns_status status = ldns_verify_trusted(NULL, NULL, NULL, NULL); status=LDNS_STATUS_OK; exit(0); } - ]]) - ], - [AC_MSG_RESULT(yes)], + ]]) + ], + [AC_MSG_RESULT(yes)], [ AC_MSG_RESULT(no) AC_MSG_ERROR([** Incomplete or missing ldns libraries.]) ]) - fi - ] -) + fi +]) # Check whether user wants libedit support LIBEDIT_MSG="no" AC_ARG_WITH([libedit], [ --with-libedit[[=PATH]] Enable libedit support for sftp], [ if test "x$withval" != "xno" ; then if test "x$withval" = "xyes" ; then AC_PATH_TOOL([PKGCONFIG], [pkg-config], [no]) if test "x$PKGCONFIG" != "xno"; then AC_MSG_CHECKING([if $PKGCONFIG knows about libedit]) if "$PKGCONFIG" libedit; then AC_MSG_RESULT([yes]) use_pkgconfig_for_libedit=yes else AC_MSG_RESULT([no]) fi fi else CPPFLAGS="$CPPFLAGS -I${withval}/include" if test -n "${need_dash_r}"; then LDFLAGS="-L${withval}/lib -R${withval}/lib ${LDFLAGS}" else LDFLAGS="-L${withval}/lib ${LDFLAGS}" fi fi if test "x$use_pkgconfig_for_libedit" = "xyes"; then LIBEDIT=`$PKGCONFIG --libs libedit` CPPFLAGS="$CPPFLAGS `$PKGCONFIG --cflags libedit`" else LIBEDIT="-ledit -lcurses" fi OTHERLIBS=`echo $LIBEDIT | sed 's/-ledit//'` AC_CHECK_LIB([edit], [el_init], [ AC_DEFINE([USE_LIBEDIT], [1], [Use libedit for sftp]) LIBEDIT_MSG="yes" AC_SUBST([LIBEDIT]) ], [ AC_MSG_ERROR([libedit not found]) ], [ $OTHERLIBS ] ) AC_MSG_CHECKING([if libedit version is compatible]) AC_COMPILE_IFELSE( [AC_LANG_PROGRAM([[ #include ]], [[ int i = H_SETSIZE; el_init("", NULL, NULL, NULL); exit(0); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) AC_MSG_ERROR([libedit version is not compatible]) ] ) fi ] ) AUDIT_MODULE=none AC_ARG_WITH([audit], [ --with-audit=module Enable audit support (modules=debug,bsm,linux)], [ AC_MSG_CHECKING([for supported audit module]) case "$withval" in bsm) AC_MSG_RESULT([bsm]) AUDIT_MODULE=bsm dnl Checks for headers, libs and functions AC_CHECK_HEADERS([bsm/audit.h], [], [AC_MSG_ERROR([BSM enabled and bsm/audit.h not found])], [ #ifdef HAVE_TIME_H # include #endif ] ) AC_CHECK_LIB([bsm], [getaudit], [], [AC_MSG_ERROR([BSM enabled and required library not found])]) AC_CHECK_FUNCS([getaudit], [], [AC_MSG_ERROR([BSM enabled and required function not found])]) # These are optional AC_CHECK_FUNCS([getaudit_addr aug_get_machine]) AC_DEFINE([USE_BSM_AUDIT], [1], [Use BSM audit module]) if test "$sol2ver" -ge 11; then SSHDLIBS="$SSHDLIBS -lscf" AC_DEFINE([BROKEN_BSM_API], [1], [The system has incomplete BSM API]) fi ;; linux) AC_MSG_RESULT([linux]) AUDIT_MODULE=linux dnl Checks for headers, libs and functions AC_CHECK_HEADERS([libaudit.h]) SSHDLIBS="$SSHDLIBS -laudit" AC_DEFINE([USE_LINUX_AUDIT], [1], [Use Linux audit module]) ;; debug) AUDIT_MODULE=debug AC_MSG_RESULT([debug]) AC_DEFINE([SSH_AUDIT_EVENTS], [1], [Use audit debugging module]) ;; no) AC_MSG_RESULT([no]) ;; *) AC_MSG_ERROR([Unknown audit module $withval]) ;; esac ] ) AC_ARG_WITH([pie], [ --with-pie Build Position Independent Executables if possible], [ if test "x$withval" = "xno"; then use_pie=no fi if test "x$withval" = "xyes"; then use_pie=yes fi ] ) if test "x$use_pie" = "x"; then use_pie=no fi if test "x$use_toolchain_hardening" != "x1" && test "x$use_pie" = "xauto"; then # Turn off automatic PIE when toolchain hardening is off. use_pie=no fi if test "x$use_pie" = "xauto"; then # Automatic PIE requires gcc >= 4.x AC_MSG_CHECKING([for gcc >= 4.x]) AC_COMPILE_IFELSE([AC_LANG_SOURCE([[ #if !defined(__GNUC__) || __GNUC__ < 4 #error gcc is too old #endif ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) use_pie=no ] ) fi if test "x$use_pie" != "xno"; then SAVED_CFLAGS="$CFLAGS" SAVED_LDFLAGS="$LDFLAGS" OSSH_CHECK_CFLAG_COMPILE([-fPIE]) OSSH_CHECK_LDFLAG_LINK([-pie]) # We use both -fPIE and -pie or neither. AC_MSG_CHECKING([whether both -fPIE and -pie are supported]) if echo "x $CFLAGS" | grep ' -fPIE' >/dev/null 2>&1 && \ echo "x $LDFLAGS" | grep ' -pie' >/dev/null 2>&1 ; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) CFLAGS="$SAVED_CFLAGS" LDFLAGS="$SAVED_LDFLAGS" fi fi dnl Checks for library functions. Please keep in alphabetical order AC_CHECK_FUNCS([ \ Blowfish_initstate \ Blowfish_expandstate \ Blowfish_expand0state \ Blowfish_stream2word \ asprintf \ b64_ntop \ __b64_ntop \ b64_pton \ __b64_pton \ bcopy \ bcrypt_pbkdf \ bindresvport_sa \ blf_enc \ cap_rights_limit \ clock \ closefrom \ dirfd \ endgrent \ err \ errx \ explicit_bzero \ fchmod \ fchown \ freeaddrinfo \ fstatfs \ fstatvfs \ futimes \ getaddrinfo \ getcwd \ getgrouplist \ getnameinfo \ getopt \ getpeereid \ getpeerucred \ getpgid \ getpgrp \ _getpty \ getrlimit \ getttyent \ glob \ group_from_gid \ inet_aton \ inet_ntoa \ inet_ntop \ innetgr \ + llabs \ login_getcapbool \ md5_crypt \ memmove \ memset_s \ mkdtemp \ ngetaddrinfo \ nsleep \ ogetaddrinfo \ openlog_r \ pledge \ poll \ prctl \ pstat \ readpassphrase \ reallocarray \ recvmsg \ rresvport_af \ sendmsg \ setdtablesize \ setegid \ setenv \ seteuid \ setgroupent \ setgroups \ setlinebuf \ setlogin \ setpassent\ setpcred \ setproctitle \ setregid \ setreuid \ setrlimit \ setsid \ setvbuf \ sigaction \ sigvec \ snprintf \ socketpair \ statfs \ statvfs \ strcasestr \ strdup \ strerror \ strlcat \ strlcpy \ strmode \ strnlen \ strnvis \ strptime \ strtonum \ strtoll \ strtoul \ strtoull \ swap32 \ sysconf \ tcgetpgrp \ timingsafe_bcmp \ truncate \ unsetenv \ updwtmpx \ user_from_uid \ usleep \ vasprintf \ vsnprintf \ waitpid \ warn \ ]) -dnl Wide character support. Linux man page says it needs _XOPEN_SOURCE. -saved_CFLAGS="$CFLAGS" -CFLAGS="$CFLAGS -D_XOPEN_SOURCE" +dnl Wide character support. AC_CHECK_FUNCS([mblen mbtowc nl_langinfo wcwidth]) -CFLAGS="$saved_CFLAGS" TEST_SSH_UTF8=${TEST_SSH_UTF8:=yes} AC_MSG_CHECKING([for utf8 locale support]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include ]], [[ char *loc = setlocale(LC_CTYPE, "en_US.UTF-8"); if (loc != NULL) exit(0); exit(1); ]])], AC_MSG_RESULT(yes), [AC_MSG_RESULT(no) TEST_SSH_UTF8=no], AC_MSG_WARN([cross compiling: assuming yes]) ) AC_LINK_IFELSE( [AC_LANG_PROGRAM( [[ #include ]], [[ return (isblank('a')); ]])], [AC_DEFINE([HAVE_ISBLANK], [1], [Define if you have isblank(3C).]) ]) disable_pkcs11= AC_ARG_ENABLE([pkcs11], [ --disable-pkcs11 disable PKCS#11 support code [no]], [ if test "x$enableval" = "xno" ; then disable_pkcs11=1 fi ] ) # PKCS11 depends on OpenSSL. if test "x$openssl" = "xyes" && test "x$disable_pkcs11" = "x"; then # PKCS#11 support requires dlopen() and co AC_SEARCH_LIBS([dlopen], [dl], [AC_DEFINE([ENABLE_PKCS11], [], [Enable for PKCS#11 support])] ) fi # IRIX has a const char return value for gai_strerror() AC_CHECK_FUNCS([gai_strerror], [ AC_DEFINE([HAVE_GAI_STRERROR]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #include const char *gai_strerror(int); ]], [[ char *str; str = gai_strerror(0); ]])], [ AC_DEFINE([HAVE_CONST_GAI_STRERROR_PROTO], [1], [Define if gai_strerror() returns const char *])], [])]) AC_SEARCH_LIBS([nanosleep], [rt posix4], [AC_DEFINE([HAVE_NANOSLEEP], [1], [Some systems put nanosleep outside of libc])]) AC_SEARCH_LIBS([clock_gettime], [rt], [AC_DEFINE([HAVE_CLOCK_GETTIME], [1], [Have clock_gettime])]) dnl Make sure prototypes are defined for these before using them. AC_CHECK_DECL([getrusage], [AC_CHECK_FUNCS([getrusage])]) AC_CHECK_DECL([strsep], [AC_CHECK_FUNCS([strsep])], [], [ #ifdef HAVE_STRING_H # include #endif ]) dnl tcsendbreak might be a macro AC_CHECK_DECL([tcsendbreak], [AC_DEFINE([HAVE_TCSENDBREAK])], [AC_CHECK_FUNCS([tcsendbreak])], [#include ] ) AC_CHECK_DECLS([h_errno], , ,[#include ]) AC_CHECK_DECLS([SHUT_RD], , , [ #include #include ]) AC_CHECK_DECLS([O_NONBLOCK], , , [ #include #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_FCNTL_H # include #endif ]) AC_CHECK_DECLS([writev], , , [ #include #include #include ]) AC_CHECK_DECLS([MAXSYMLINKS], , , [ #include ]) AC_CHECK_DECLS([offsetof], , , [ #include ]) # extra bits for select(2) AC_CHECK_DECLS([howmany, NFDBITS], [], [], [[ #include #include #ifdef HAVE_SYS_SYSMACROS_H #include #endif #ifdef HAVE_SYS_SELECT_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_UNISTD_H #include #endif ]]) AC_CHECK_TYPES([fd_mask], [], [], [[ #include #include #ifdef HAVE_SYS_SELECT_H #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_UNISTD_H #include #endif ]]) AC_CHECK_FUNCS([setresuid], [ dnl Some platorms have setresuid that isn't implemented, test for this AC_MSG_CHECKING([if setresuid seems to work]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include ]], [[ errno=0; setresuid(0,0,0); if (errno==ENOSYS) exit(1); else exit(0); ]])], [AC_MSG_RESULT([yes])], [AC_DEFINE([BROKEN_SETRESUID], [1], [Define if your setresuid() is broken]) AC_MSG_RESULT([not implemented])], [AC_MSG_WARN([cross compiling: not checking setresuid])] ) ]) AC_CHECK_FUNCS([setresgid], [ dnl Some platorms have setresgid that isn't implemented, test for this AC_MSG_CHECKING([if setresgid seems to work]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include ]], [[ errno=0; setresgid(0,0,0); if (errno==ENOSYS) exit(1); else exit(0); ]])], [AC_MSG_RESULT([yes])], [AC_DEFINE([BROKEN_SETRESGID], [1], [Define if your setresgid() is broken]) AC_MSG_RESULT([not implemented])], [AC_MSG_WARN([cross compiling: not checking setresuid])] ) ]) AC_CHECK_FUNCS([realpath], [ dnl the sftp v3 spec says SSH_FXP_REALPATH will "canonicalize any given dnl path name", however some implementations of realpath (and some dnl versions of the POSIX spec) do not work on non-existent files, dnl so we use the OpenBSD implementation on those platforms. AC_MSG_CHECKING([if realpath works with non-existent files]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include ]], [[ char buf[PATH_MAX]; if (realpath("/opensshnonexistentfilename1234", buf) == NULL) if (errno == ENOENT) exit(1); exit(0); ]])], [AC_MSG_RESULT([yes])], [AC_DEFINE([BROKEN_REALPATH], [1], [realpath does not work with nonexistent files]) AC_MSG_RESULT([no])], [AC_MSG_WARN([cross compiling: assuming working])] ) ]) dnl Checks for time functions AC_CHECK_FUNCS([gettimeofday time]) dnl Checks for utmp functions AC_CHECK_FUNCS([endutent getutent getutid getutline pututline setutent]) AC_CHECK_FUNCS([utmpname]) dnl Checks for utmpx functions AC_CHECK_FUNCS([endutxent getutxent getutxid getutxline getutxuser pututxline]) AC_CHECK_FUNCS([setutxdb setutxent utmpxname]) dnl Checks for lastlog functions AC_CHECK_FUNCS([getlastlogxbyname]) AC_CHECK_FUNC([daemon], [AC_DEFINE([HAVE_DAEMON], [1], [Define if your libraries define daemon()])], [AC_CHECK_LIB([bsd], [daemon], [LIBS="$LIBS -lbsd"; AC_DEFINE([HAVE_DAEMON])])] ) AC_CHECK_FUNC([getpagesize], [AC_DEFINE([HAVE_GETPAGESIZE], [1], [Define if your libraries define getpagesize()])], [AC_CHECK_LIB([ucb], [getpagesize], [LIBS="$LIBS -lucb"; AC_DEFINE([HAVE_GETPAGESIZE])])] ) # Check for broken snprintf if test "x$ac_cv_func_snprintf" = "xyes" ; then AC_MSG_CHECKING([whether snprintf correctly terminates long strings]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include ]], [[ char b[5]; snprintf(b,5,"123456789"); exit(b[4]!='\0'); ]])], [AC_MSG_RESULT([yes])], [ AC_MSG_RESULT([no]) AC_DEFINE([BROKEN_SNPRINTF], [1], [Define if your snprintf is busted]) AC_MSG_WARN([****** Your snprintf() function is broken, complain to your vendor]) ], [ AC_MSG_WARN([cross compiling: Assuming working snprintf()]) ] ) fi # We depend on vsnprintf returning the right thing on overflow: the # number of characters it tried to create (as per SUSv3) if test "x$ac_cv_func_vsnprintf" = "xyes" ; then AC_MSG_CHECKING([whether vsnprintf returns correct values on overflow]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include int x_snprintf(char *str, size_t count, const char *fmt, ...) { size_t ret; va_list ap; va_start(ap, fmt); ret = vsnprintf(str, count, fmt, ap); va_end(ap); return ret; } ]], [[ char x[1]; if (x_snprintf(x, 1, "%s %d", "hello", 12345) != 11) return 1; if (x_snprintf(NULL, 0, "%s %d", "hello", 12345) != 11) return 1; return 0; ]])], [AC_MSG_RESULT([yes])], [ AC_MSG_RESULT([no]) AC_DEFINE([BROKEN_SNPRINTF], [1], [Define if your snprintf is busted]) AC_MSG_WARN([****** Your vsnprintf() function is broken, complain to your vendor]) ], [ AC_MSG_WARN([cross compiling: Assuming working vsnprintf()]) ] ) fi # On systems where [v]snprintf is broken, but is declared in stdio, # check that the fmt argument is const char * or just char *. # This is only useful for when BROKEN_SNPRINTF AC_MSG_CHECKING([whether snprintf can declare const char *fmt]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include int snprintf(char *a, size_t b, const char *c, ...) { return 0; } ]], [[ snprintf(0, 0, 0); ]])], [AC_MSG_RESULT([yes]) AC_DEFINE([SNPRINTF_CONST], [const], [Define as const if snprintf() can declare const char *fmt])], [AC_MSG_RESULT([no]) AC_DEFINE([SNPRINTF_CONST], [/* not const */])]) # Check for missing getpeereid (or equiv) support NO_PEERCHECK="" if test "x$ac_cv_func_getpeereid" != "xyes" -a "x$ac_cv_func_getpeerucred" != "xyes"; then AC_MSG_CHECKING([whether system supports SO_PEERCRED getsockopt]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[int i = SO_PEERCRED;]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_SO_PEERCRED], [1], [Have PEERCRED socket option]) ], [AC_MSG_RESULT([no]) NO_PEERCHECK=1 ]) fi dnl see whether mkstemp() requires XXXXXX if test "x$ac_cv_func_mkdtemp" = "xyes" ; then AC_MSG_CHECKING([for (overly) strict mkstemp]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include ]], [[ char template[]="conftest.mkstemp-test"; if (mkstemp(template) == -1) exit(1); unlink(template); exit(0); ]])], [ AC_MSG_RESULT([no]) ], [ AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_STRICT_MKSTEMP], [1], [Silly mkstemp()]) ], [ AC_MSG_RESULT([yes]) AC_DEFINE([HAVE_STRICT_MKSTEMP]) ] ) fi dnl make sure that openpty does not reacquire controlling terminal if test ! -z "$check_for_openpty_ctty_bug"; then AC_MSG_CHECKING([if openpty correctly handles controlling tty]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include #include ]], [[ pid_t pid; int fd, ptyfd, ttyfd, status; pid = fork(); if (pid < 0) { /* failed */ exit(1); } else if (pid > 0) { /* parent */ waitpid(pid, &status, 0); if (WIFEXITED(status)) exit(WEXITSTATUS(status)); else exit(2); } else { /* child */ close(0); close(1); close(2); setsid(); openpty(&ptyfd, &ttyfd, NULL, NULL, NULL); fd = open("/dev/tty", O_RDWR | O_NOCTTY); if (fd >= 0) exit(3); /* Acquired ctty: broken */ else exit(0); /* Did not acquire ctty: OK */ } ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) AC_DEFINE([SSHD_ACQUIRES_CTTY]) ], [ AC_MSG_RESULT([cross-compiling, assuming yes]) ] ) fi if test "x$ac_cv_func_getaddrinfo" = "xyes" && \ test "x$check_for_hpux_broken_getaddrinfo" = "x1"; then AC_MSG_CHECKING([if getaddrinfo seems to work]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include #include #include #define TEST_PORT "2222" ]], [[ int err, sock; struct addrinfo *gai_ai, *ai, hints; char ntop[NI_MAXHOST], strport[NI_MAXSERV], *name = NULL; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; err = getaddrinfo(name, TEST_PORT, &hints, &gai_ai); if (err != 0) { fprintf(stderr, "getaddrinfo failed (%s)", gai_strerror(err)); exit(1); } for (ai = gai_ai; ai != NULL; ai = ai->ai_next) { if (ai->ai_family != AF_INET6) continue; err = getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV); if (err != 0) { if (err == EAI_SYSTEM) perror("getnameinfo EAI_SYSTEM"); else fprintf(stderr, "getnameinfo failed: %s\n", gai_strerror(err)); exit(2); } sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sock < 0) perror("socket"); if (bind(sock, ai->ai_addr, ai->ai_addrlen) < 0) { if (errno == EBADF) exit(3); } } exit(0); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) AC_DEFINE([BROKEN_GETADDRINFO]) ], [ AC_MSG_RESULT([cross-compiling, assuming yes]) ] ) fi if test "x$ac_cv_func_getaddrinfo" = "xyes" && \ test "x$check_for_aix_broken_getaddrinfo" = "x1"; then AC_MSG_CHECKING([if getaddrinfo seems to work]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include #include #include #define TEST_PORT "2222" ]], [[ int err, sock; struct addrinfo *gai_ai, *ai, hints; char ntop[NI_MAXHOST], strport[NI_MAXSERV], *name = NULL; memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; err = getaddrinfo(name, TEST_PORT, &hints, &gai_ai); if (err != 0) { fprintf(stderr, "getaddrinfo failed (%s)", gai_strerror(err)); exit(1); } for (ai = gai_ai; ai != NULL; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue; err = getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV); if (ai->ai_family == AF_INET && err != 0) { perror("getnameinfo"); exit(2); } } exit(0); ]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([AIX_GETNAMEINFO_HACK], [1], [Define if you have a getaddrinfo that fails for the all-zeros IPv6 address]) ], [ AC_MSG_RESULT([no]) AC_DEFINE([BROKEN_GETADDRINFO]) ], [ AC_MSG_RESULT([cross-compiling, assuming no]) ] ) fi if test "x$ac_cv_func_getaddrinfo" = "xyes"; then AC_CHECK_DECLS(AI_NUMERICSERV, , , [#include #include #include ]) fi if test "x$check_for_conflicting_getspnam" = "x1"; then AC_MSG_CHECKING([for conflicting getspnam in shadow.h]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ exit(0); ]])], [ AC_MSG_RESULT([no]) ], [ AC_MSG_RESULT([yes]) AC_DEFINE([GETSPNAM_CONFLICTING_DEFS], [1], [Conflicting defs for getspnam]) ] ) fi dnl NetBSD added an strnvis and unfortunately made it incompatible with the dnl existing one in OpenBSD and Linux's libbsd (the former having existed dnl for over ten years). Despite this incompatibility being reported during dnl development (see http://gnats.netbsd.org/44977) they still shipped it. dnl Even more unfortunately FreeBSD and later MacOS picked up this incompatible dnl implementation. Try to detect this mess, and assume the only safe option dnl if we're cross compiling. dnl dnl OpenBSD, 2001: strnvis(char *dst, const char *src, size_t dlen, int flag); dnl NetBSD: 2012, strnvis(char *dst, size_t dlen, const char *src, int flag); if test "x$ac_cv_func_strnvis" = "xyes"; then AC_MSG_CHECKING([for working strnvis]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include #include static void sighandler(int sig) { _exit(1); } ]], [[ char dst[16]; signal(SIGSEGV, sighandler); if (strnvis(dst, "src", 4, 0) && strcmp(dst, "src") == 0) exit(0); exit(1) ]])], [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) AC_DEFINE([BROKEN_STRNVIS], [1], [strnvis detected broken])], [AC_MSG_WARN([cross compiling: assuming broken]) AC_DEFINE([BROKEN_STRNVIS], [1], [strnvis assumed broken])] ) fi AC_FUNC_GETPGRP # Search for OpenSSL saved_CPPFLAGS="$CPPFLAGS" saved_LDFLAGS="$LDFLAGS" AC_ARG_WITH([ssl-dir], [ --with-ssl-dir=PATH Specify path to OpenSSL installation ], [ if test "x$openssl" = "xno" ; then AC_MSG_ERROR([cannot use --with-ssl-dir when OpenSSL disabled]) fi if test "x$withval" != "xno" ; then case "$withval" in # Relative paths ./*|../*) withval="`pwd`/$withval" esac if test -d "$withval/lib"; then if test -n "${need_dash_r}"; then LDFLAGS="-L${withval}/lib -R${withval}/lib ${LDFLAGS}" else LDFLAGS="-L${withval}/lib ${LDFLAGS}" fi elif test -d "$withval/lib64"; then if test -n "${need_dash_r}"; then LDFLAGS="-L${withval}/lib64 -R${withval}/lib64 ${LDFLAGS}" else LDFLAGS="-L${withval}/lib64 ${LDFLAGS}" fi else if test -n "${need_dash_r}"; then LDFLAGS="-L${withval} -R${withval} ${LDFLAGS}" else LDFLAGS="-L${withval} ${LDFLAGS}" fi fi if test -d "$withval/include"; then CPPFLAGS="-I${withval}/include ${CPPFLAGS}" else CPPFLAGS="-I${withval} ${CPPFLAGS}" fi fi ] ) AC_ARG_WITH([openssl-header-check], [ --without-openssl-header-check Disable OpenSSL version consistency check], [ if test "x$withval" = "xno" ; then openssl_check_nonfatal=1 fi ] ) openssl_engine=no AC_ARG_WITH([ssl-engine], [ --with-ssl-engine Enable OpenSSL (hardware) ENGINE support ], [ if test "x$withval" != "xno" ; then if test "x$openssl" = "xno" ; then AC_MSG_ERROR([cannot use --with-ssl-engine when OpenSSL disabled]) fi openssl_engine=yes fi ] ) if test "x$openssl" = "xyes" ; then LIBS="-lcrypto $LIBS" AC_TRY_LINK_FUNC([RAND_add], [AC_DEFINE([HAVE_OPENSSL], [1], [Define if your ssl headers are included with #include ])], [ dnl Check default openssl install dir if test -n "${need_dash_r}"; then LDFLAGS="-L/usr/local/ssl/lib -R/usr/local/ssl/lib ${saved_LDFLAGS}" else LDFLAGS="-L/usr/local/ssl/lib ${saved_LDFLAGS}" fi CPPFLAGS="-I/usr/local/ssl/include ${saved_CPPFLAGS}" AC_CHECK_HEADER([openssl/opensslv.h], , [AC_MSG_ERROR([*** OpenSSL headers missing - please install first or check config.log ***])]) AC_TRY_LINK_FUNC([RAND_add], [AC_DEFINE([HAVE_OPENSSL])], [ AC_MSG_ERROR([*** Can't find recent OpenSSL libcrypto (see config.log for details) ***]) ] ) ] ) # Determine OpenSSL header version AC_MSG_CHECKING([OpenSSL header version]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include #include #define DATA "conftest.sslincver" ]], [[ FILE *fd; int rc; fd = fopen(DATA,"w"); if(fd == NULL) exit(1); if ((rc = fprintf(fd, "%08lx (%s)\n", (unsigned long)OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_TEXT)) < 0) exit(1); exit(0); ]])], [ ssl_header_ver=`cat conftest.sslincver` AC_MSG_RESULT([$ssl_header_ver]) ], [ AC_MSG_RESULT([not found]) AC_MSG_ERROR([OpenSSL version header not found.]) ], [ AC_MSG_WARN([cross compiling: not checking]) ] ) # Determine OpenSSL library version AC_MSG_CHECKING([OpenSSL library version]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include #include #define DATA "conftest.ssllibver" ]], [[ FILE *fd; int rc; fd = fopen(DATA,"w"); if(fd == NULL) exit(1); if ((rc = fprintf(fd, "%08lx (%s)\n", (unsigned long)SSLeay(), SSLeay_version(SSLEAY_VERSION))) < 0) exit(1); exit(0); ]])], [ ssl_library_ver=`cat conftest.ssllibver` # Check version is supported. case "$ssl_library_ver" in - 0090[[0-7]]*|009080[[0-5]]*) - AC_MSG_ERROR([OpenSSL >= 0.9.8f required (have "$ssl_library_ver")]) + 10000*|0*) + AC_MSG_ERROR([OpenSSL >= 1.0.1 required (have "$ssl_library_ver")]) ;; *) ;; esac AC_MSG_RESULT([$ssl_library_ver]) ], [ AC_MSG_RESULT([not found]) AC_MSG_ERROR([OpenSSL library not found.]) ], [ AC_MSG_WARN([cross compiling: not checking]) ] ) # Sanity check OpenSSL headers AC_MSG_CHECKING([whether OpenSSL's headers match the library]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include ]], [[ exit(SSLeay() == OPENSSL_VERSION_NUMBER ? 0 : 1); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) if test "x$openssl_check_nonfatal" = "x"; then AC_MSG_ERROR([Your OpenSSL headers do not match your library. Check config.log for details. If you are sure your installation is consistent, you can disable the check by running "./configure --without-openssl-header-check". Also see contrib/findssl.sh for help identifying header/library mismatches. ]) else AC_MSG_WARN([Your OpenSSL headers do not match your library. Check config.log for details. Also see contrib/findssl.sh for help identifying header/library mismatches.]) fi ], [ AC_MSG_WARN([cross compiling: not checking]) ] ) AC_MSG_CHECKING([if programs using OpenSSL functions will link]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include ]], [[ SSLeay_add_all_algorithms(); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) saved_LIBS="$LIBS" LIBS="$LIBS -ldl" AC_MSG_CHECKING([if programs using OpenSSL need -ldl]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include ]], [[ SSLeay_add_all_algorithms(); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) LIBS="$saved_LIBS" ] ) ] ) AC_CHECK_FUNCS([ \ BN_is_prime_ex \ DSA_generate_parameters_ex \ EVP_DigestInit_ex \ EVP_DigestFinal_ex \ EVP_MD_CTX_init \ EVP_MD_CTX_cleanup \ EVP_MD_CTX_copy_ex \ HMAC_CTX_init \ RSA_generate_key_ex \ RSA_get_default_method \ ]) if test "x$openssl_engine" = "xyes" ; then AC_MSG_CHECKING([for OpenSSL ENGINE support]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ ENGINE_load_builtin_engines(); ENGINE_register_all_complete(); ]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([USE_OPENSSL_ENGINE], [1], [Enable OpenSSL engine support]) ], [ AC_MSG_ERROR([OpenSSL ENGINE support not found]) ]) fi # Check for OpenSSL without EVP_aes_{192,256}_cbc AC_MSG_CHECKING([whether OpenSSL has crippled AES support]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include #include ]], [[ exit(EVP_aes_192_cbc() == NULL || EVP_aes_256_cbc() == NULL); ]])], [ AC_MSG_RESULT([no]) ], [ AC_MSG_RESULT([yes]) AC_DEFINE([OPENSSL_LOBOTOMISED_AES], [1], [libcrypto is missing AES 192 and 256 bit functions]) ] ) # Check for OpenSSL with EVP_aes_*ctr AC_MSG_CHECKING([whether OpenSSL has AES CTR via EVP]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include #include ]], [[ exit(EVP_aes_128_ctr() == NULL || EVP_aes_192_cbc() == NULL || EVP_aes_256_cbc() == NULL); ]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([OPENSSL_HAVE_EVPCTR], [1], [libcrypto has EVP AES CTR]) ], [ AC_MSG_RESULT([no]) ] ) # Check for OpenSSL with EVP_aes_*gcm AC_MSG_CHECKING([whether OpenSSL has AES GCM via EVP]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include #include ]], [[ exit(EVP_aes_128_gcm() == NULL || EVP_aes_256_gcm() == NULL || EVP_CTRL_GCM_SET_IV_FIXED == 0 || EVP_CTRL_GCM_IV_GEN == 0 || EVP_CTRL_GCM_SET_TAG == 0 || EVP_CTRL_GCM_GET_TAG == 0 || EVP_CIPHER_CTX_ctrl(NULL, 0, 0, NULL) == 0); ]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([OPENSSL_HAVE_EVPGCM], [1], [libcrypto has EVP AES GCM]) ], [ AC_MSG_RESULT([no]) unsupported_algorithms="$unsupported_cipers \ aes128-gcm@openssh.com \ aes256-gcm@openssh.com" ] ) AC_SEARCH_LIBS([EVP_CIPHER_CTX_ctrl], [crypto], [AC_DEFINE([HAVE_EVP_CIPHER_CTX_CTRL], [1], [Define if libcrypto has EVP_CIPHER_CTX_ctrl])]) AC_MSG_CHECKING([if EVP_DigestUpdate returns an int]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include #include ]], [[ if(EVP_DigestUpdate(NULL, NULL,0)) exit(0); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) AC_DEFINE([OPENSSL_EVP_DIGESTUPDATE_VOID], [1], [Define if EVP_DigestUpdate returns void]) ] ) # Some systems want crypt() from libcrypt, *not* the version in OpenSSL, # because the system crypt() is more featureful. if test "x$check_for_libcrypt_before" = "x1"; then AC_CHECK_LIB([crypt], [crypt]) fi # Some Linux systems (Slackware) need crypt() from libcrypt, *not* the # version in OpenSSL. if test "x$check_for_libcrypt_later" = "x1"; then AC_CHECK_LIB([crypt], [crypt], [LIBS="$LIBS -lcrypt"]) fi AC_CHECK_FUNCS([crypt DES_crypt]) # Search for SHA256 support in libc and/or OpenSSL AC_CHECK_FUNCS([SHA256_Update EVP_sha256], , [unsupported_algorithms="$unsupported_algorithms \ hmac-sha2-256 \ hmac-sha2-512 \ diffie-hellman-group-exchange-sha256 \ hmac-sha2-256-etm@openssh.com \ hmac-sha2-512-etm@openssh.com" ] ) # Search for RIPE-MD support in OpenSSL AC_CHECK_FUNCS([EVP_ripemd160], , [unsupported_algorithms="$unsupported_algorithms \ hmac-ripemd160 \ hmac-ripemd160@openssh.com \ hmac-ripemd160-etm@openssh.com" ] ) # Check complete ECC support in OpenSSL AC_MSG_CHECKING([whether OpenSSL has NID_X9_62_prime256v1]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include #include #include #include #if OPENSSL_VERSION_NUMBER < 0x0090807f /* 0.9.8g */ # error "OpenSSL < 0.9.8g has unreliable ECC code" #endif ]], [[ EC_KEY *e = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); const EVP_MD *m = EVP_sha256(); /* We need this too */ ]])], [ AC_MSG_RESULT([yes]) enable_nistp256=1 ], [ AC_MSG_RESULT([no]) ] ) AC_MSG_CHECKING([whether OpenSSL has NID_secp384r1]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include #include #include #include #if OPENSSL_VERSION_NUMBER < 0x0090807f /* 0.9.8g */ # error "OpenSSL < 0.9.8g has unreliable ECC code" #endif ]], [[ EC_KEY *e = EC_KEY_new_by_curve_name(NID_secp384r1); const EVP_MD *m = EVP_sha384(); /* We need this too */ ]])], [ AC_MSG_RESULT([yes]) enable_nistp384=1 ], [ AC_MSG_RESULT([no]) ] ) AC_MSG_CHECKING([whether OpenSSL has NID_secp521r1]) AC_LINK_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include #include #include #include #if OPENSSL_VERSION_NUMBER < 0x0090807f /* 0.9.8g */ # error "OpenSSL < 0.9.8g has unreliable ECC code" #endif ]], [[ EC_KEY *e = EC_KEY_new_by_curve_name(NID_secp521r1); const EVP_MD *m = EVP_sha512(); /* We need this too */ ]])], [ AC_MSG_RESULT([yes]) AC_MSG_CHECKING([if OpenSSL's NID_secp521r1 is functional]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include #include #include #include ]],[[ EC_KEY *e = EC_KEY_new_by_curve_name(NID_secp521r1); const EVP_MD *m = EVP_sha512(); /* We need this too */ exit(e == NULL || m == NULL); ]])], [ AC_MSG_RESULT([yes]) enable_nistp521=1 ], [ AC_MSG_RESULT([no]) ], [ AC_MSG_WARN([cross-compiling: assuming yes]) enable_nistp521=1 ] )], AC_MSG_RESULT([no]) ) COMMENT_OUT_ECC="#no ecc#" TEST_SSH_ECC=no if test x$enable_nistp256 = x1 || test x$enable_nistp384 = x1 || \ test x$enable_nistp521 = x1; then AC_DEFINE(OPENSSL_HAS_ECC, [1], [OpenSSL has ECC]) fi if test x$enable_nistp256 = x1; then AC_DEFINE([OPENSSL_HAS_NISTP256], [1], [libcrypto has NID_X9_62_prime256v1]) TEST_SSH_ECC=yes COMMENT_OUT_ECC="" else unsupported_algorithms="$unsupported_algorithms \ ecdsa-sha2-nistp256 \ ecdh-sha2-nistp256 \ ecdsa-sha2-nistp256-cert-v01@openssh.com" fi if test x$enable_nistp384 = x1; then AC_DEFINE([OPENSSL_HAS_NISTP384], [1], [libcrypto has NID_secp384r1]) TEST_SSH_ECC=yes COMMENT_OUT_ECC="" else unsupported_algorithms="$unsupported_algorithms \ ecdsa-sha2-nistp384 \ ecdh-sha2-nistp384 \ ecdsa-sha2-nistp384-cert-v01@openssh.com" fi if test x$enable_nistp521 = x1; then AC_DEFINE([OPENSSL_HAS_NISTP521], [1], [libcrypto has NID_secp521r1]) TEST_SSH_ECC=yes COMMENT_OUT_ECC="" else unsupported_algorithms="$unsupported_algorithms \ ecdh-sha2-nistp521 \ ecdsa-sha2-nistp521 \ ecdsa-sha2-nistp521-cert-v01@openssh.com" fi AC_SUBST([TEST_SSH_ECC]) AC_SUBST([COMMENT_OUT_ECC]) else AC_CHECK_LIB([crypt], [crypt], [LIBS="$LIBS -lcrypt"]) AC_CHECK_FUNCS([crypt]) fi AC_CHECK_FUNCS([ \ arc4random \ arc4random_buf \ arc4random_stir \ arc4random_uniform \ ]) saved_LIBS="$LIBS" AC_CHECK_LIB([iaf], [ia_openinfo], [ LIBS="$LIBS -liaf" AC_CHECK_FUNCS([set_id], [SSHDLIBS="$SSHDLIBS -liaf" AC_DEFINE([HAVE_LIBIAF], [1], [Define if system has libiaf that supports set_id]) ]) ]) LIBS="$saved_LIBS" ### Configure cryptographic random number support # Check wheter OpenSSL seeds itself if test "x$openssl" = "xyes" ; then AC_MSG_CHECKING([whether OpenSSL's PRNG is internally seeded]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include ]], [[ exit(RAND_status() == 1 ? 0 : 1); ]])], [ OPENSSL_SEEDS_ITSELF=yes AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ], [ AC_MSG_WARN([cross compiling: assuming yes]) # This is safe, since we will fatal() at runtime if # OpenSSL is not seeded correctly. OPENSSL_SEEDS_ITSELF=yes ] ) fi # PRNGD TCP socket AC_ARG_WITH([prngd-port], [ --with-prngd-port=PORT read entropy from PRNGD/EGD TCP localhost:PORT], [ case "$withval" in no) withval="" ;; [[0-9]]*) ;; *) AC_MSG_ERROR([You must specify a numeric port number for --with-prngd-port]) ;; esac if test ! -z "$withval" ; then PRNGD_PORT="$withval" AC_DEFINE_UNQUOTED([PRNGD_PORT], [$PRNGD_PORT], [Port number of PRNGD/EGD random number socket]) fi ] ) # PRNGD Unix domain socket AC_ARG_WITH([prngd-socket], [ --with-prngd-socket=FILE read entropy from PRNGD/EGD socket FILE (default=/var/run/egd-pool)], [ case "$withval" in yes) withval="/var/run/egd-pool" ;; no) withval="" ;; /*) ;; *) AC_MSG_ERROR([You must specify an absolute path to the entropy socket]) ;; esac if test ! -z "$withval" ; then if test ! -z "$PRNGD_PORT" ; then AC_MSG_ERROR([You may not specify both a PRNGD/EGD port and socket]) fi if test ! -r "$withval" ; then AC_MSG_WARN([Entropy socket is not readable]) fi PRNGD_SOCKET="$withval" AC_DEFINE_UNQUOTED([PRNGD_SOCKET], ["$PRNGD_SOCKET"], [Location of PRNGD/EGD random number socket]) fi ], [ # Check for existing socket only if we don't have a random device already if test "x$OPENSSL_SEEDS_ITSELF" != "xyes" ; then AC_MSG_CHECKING([for PRNGD/EGD socket]) # Insert other locations here for sock in /var/run/egd-pool /dev/egd-pool /etc/entropy; do if test -r $sock && $TEST_MINUS_S_SH -c "test -S $sock -o -p $sock" ; then PRNGD_SOCKET="$sock" AC_DEFINE_UNQUOTED([PRNGD_SOCKET], ["$PRNGD_SOCKET"]) break; fi done if test ! -z "$PRNGD_SOCKET" ; then AC_MSG_RESULT([$PRNGD_SOCKET]) else AC_MSG_RESULT([not found]) fi fi ] ) # Which randomness source do we use? if test ! -z "$PRNGD_PORT" ; then RAND_MSG="PRNGd port $PRNGD_PORT" elif test ! -z "$PRNGD_SOCKET" ; then RAND_MSG="PRNGd socket $PRNGD_SOCKET" elif test ! -z "$OPENSSL_SEEDS_ITSELF" ; then AC_DEFINE([OPENSSL_PRNG_ONLY], [1], [Define if you want the OpenSSL internally seeded PRNG only]) RAND_MSG="OpenSSL internal ONLY" elif test "x$openssl" = "xno" ; then AC_MSG_WARN([OpenSSH will use /dev/urandom as a source of random numbers. It will fail if this device is not supported or accessible]) else AC_MSG_ERROR([OpenSSH has no source of random numbers. Please configure OpenSSL with an entropy source or re-run configure using one of the --with-prngd-port or --with-prngd-socket options]) fi # Check for PAM libs PAM_MSG="no" AC_ARG_WITH([pam], [ --with-pam Enable PAM support ], [ if test "x$withval" != "xno" ; then if test "x$ac_cv_header_security_pam_appl_h" != "xyes" && \ test "x$ac_cv_header_pam_pam_appl_h" != "xyes" ; then AC_MSG_ERROR([PAM headers not found]) fi saved_LIBS="$LIBS" AC_CHECK_LIB([dl], [dlopen], , ) AC_CHECK_LIB([pam], [pam_set_item], , [AC_MSG_ERROR([*** libpam missing])]) AC_CHECK_FUNCS([pam_getenvlist]) AC_CHECK_FUNCS([pam_putenv]) LIBS="$saved_LIBS" PAM_MSG="yes" SSHDLIBS="$SSHDLIBS -lpam" AC_DEFINE([USE_PAM], [1], [Define if you want to enable PAM support]) if test $ac_cv_lib_dl_dlopen = yes; then case "$LIBS" in *-ldl*) # libdl already in LIBS ;; *) SSHDLIBS="$SSHDLIBS -ldl" ;; esac fi fi ] ) AC_ARG_WITH([pam-service], [ --with-pam-service=name Specify PAM service name ], [ if test "x$withval" != "xno" && \ test "x$withval" != "xyes" ; then AC_DEFINE_UNQUOTED([SSHD_PAM_SERVICE], ["$withval"], [sshd PAM service name]) fi ] ) # Check for older PAM if test "x$PAM_MSG" = "xyes" ; then # Check PAM strerror arguments (old PAM) AC_MSG_CHECKING([whether pam_strerror takes only one argument]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #if defined(HAVE_SECURITY_PAM_APPL_H) #include #elif defined (HAVE_PAM_PAM_APPL_H) #include #endif ]], [[ (void)pam_strerror((pam_handle_t *)NULL, -1); ]])], [AC_MSG_RESULT([no])], [ AC_DEFINE([HAVE_OLD_PAM], [1], [Define if you have an old version of PAM which takes only one argument to pam_strerror]) AC_MSG_RESULT([yes]) PAM_MSG="yes (old library)" ]) fi case "$host" in *-*-cygwin*) SSH_PRIVSEP_USER=CYGWIN_SSH_PRIVSEP_USER ;; *) SSH_PRIVSEP_USER=sshd ;; esac AC_ARG_WITH([privsep-user], [ --with-privsep-user=user Specify non-privileged user for privilege separation], [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then SSH_PRIVSEP_USER=$withval fi ] ) if test "x$SSH_PRIVSEP_USER" = "xCYGWIN_SSH_PRIVSEP_USER" ; then AC_DEFINE_UNQUOTED([SSH_PRIVSEP_USER], [CYGWIN_SSH_PRIVSEP_USER], [Cygwin function to fetch non-privileged user for privilege separation]) else AC_DEFINE_UNQUOTED([SSH_PRIVSEP_USER], ["$SSH_PRIVSEP_USER"], [non-privileged user for privilege separation]) fi AC_SUBST([SSH_PRIVSEP_USER]) if test "x$have_linux_no_new_privs" = "x1" ; then AC_CHECK_DECL([SECCOMP_MODE_FILTER], [have_seccomp_filter=1], , [ #include #include ]) fi if test "x$have_seccomp_filter" = "x1" ; then AC_MSG_CHECKING([kernel for seccomp_filter support]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include #include #include #include #include ]], [[ int i = $seccomp_audit_arch; errno = 0; prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, NULL, 0, 0); exit(errno == EFAULT ? 0 : 1); ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) # Disable seccomp filter as a target have_seccomp_filter=0 ] ) fi # Decide which sandbox style to use sandbox_arg="" AC_ARG_WITH([sandbox], [ --with-sandbox=style Specify privilege separation sandbox (no, capsicum, darwin, rlimit, seccomp_filter, systrace, pledge)], [ if test "x$withval" = "xyes" ; then sandbox_arg="" else sandbox_arg="$withval" fi ] ) # Some platforms (seems to be the ones that have a kernel poll(2)-type # function with which they implement select(2)) use an extra file descriptor # when calling select(2), which means we can't use the rlimit sandbox. AC_MSG_CHECKING([if select works with descriptor rlimit]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #ifdef HAVE_SYS_TIME_H # include #endif #include #ifdef HAVE_SYS_SELECT_H # include #endif #include #include #include ]],[[ struct rlimit rl_zero; int fd, r; fd_set fds; struct timeval tv; fd = open("/dev/null", O_RDONLY); FD_ZERO(&fds); FD_SET(fd, &fds); rl_zero.rlim_cur = rl_zero.rlim_max = 0; setrlimit(RLIMIT_FSIZE, &rl_zero); setrlimit(RLIMIT_NOFILE, &rl_zero); tv.tv_sec = 1; tv.tv_usec = 0; r = select(fd+1, &fds, NULL, NULL, &tv); exit (r == -1 ? 1 : 0); ]])], [AC_MSG_RESULT([yes]) select_works_with_rlimit=yes], [AC_MSG_RESULT([no]) select_works_with_rlimit=no], [AC_MSG_WARN([cross compiling: assuming yes])] ) AC_MSG_CHECKING([if setrlimit(RLIMIT_NOFILE,{0,0}) works]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include ]],[[ struct rlimit rl_zero; int fd, r; fd_set fds; rl_zero.rlim_cur = rl_zero.rlim_max = 0; r = setrlimit(RLIMIT_NOFILE, &rl_zero); exit (r == -1 ? 1 : 0); ]])], [AC_MSG_RESULT([yes]) rlimit_nofile_zero_works=yes], [AC_MSG_RESULT([no]) rlimit_nofile_zero_works=no], [AC_MSG_WARN([cross compiling: assuming yes])] ) AC_MSG_CHECKING([if setrlimit RLIMIT_FSIZE works]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #include ]],[[ struct rlimit rl_zero; rl_zero.rlim_cur = rl_zero.rlim_max = 0; exit(setrlimit(RLIMIT_FSIZE, &rl_zero) != 0); ]])], [AC_MSG_RESULT([yes])], [AC_MSG_RESULT([no]) AC_DEFINE(SANDBOX_SKIP_RLIMIT_FSIZE, 1, [setrlimit RLIMIT_FSIZE works])], [AC_MSG_WARN([cross compiling: assuming yes])] ) if test "x$sandbox_arg" = "xpledge" || \ ( test -z "$sandbox_arg" && test "x$ac_cv_func_pledge" = "xyes" ) ; then test "x$ac_cv_func_pledge" != "xyes" && \ AC_MSG_ERROR([pledge sandbox requires pledge(2) support]) SANDBOX_STYLE="pledge" AC_DEFINE([SANDBOX_PLEDGE], [1], [Sandbox using pledge(2)]) elif test "x$sandbox_arg" = "xsystrace" || \ ( test -z "$sandbox_arg" && test "x$have_systr_policy_kill" = "x1" ) ; then test "x$have_systr_policy_kill" != "x1" && \ AC_MSG_ERROR([systrace sandbox requires systrace headers and SYSTR_POLICY_KILL support]) SANDBOX_STYLE="systrace" AC_DEFINE([SANDBOX_SYSTRACE], [1], [Sandbox using systrace(4)]) elif test "x$sandbox_arg" = "xdarwin" || \ ( test -z "$sandbox_arg" && test "x$ac_cv_func_sandbox_init" = "xyes" && \ test "x$ac_cv_header_sandbox_h" = "xyes") ; then test "x$ac_cv_func_sandbox_init" != "xyes" -o \ "x$ac_cv_header_sandbox_h" != "xyes" && \ AC_MSG_ERROR([Darwin seatbelt sandbox requires sandbox.h and sandbox_init function]) SANDBOX_STYLE="darwin" AC_DEFINE([SANDBOX_DARWIN], [1], [Sandbox using Darwin sandbox_init(3)]) elif test "x$sandbox_arg" = "xseccomp_filter" || \ ( test -z "$sandbox_arg" && \ test "x$have_seccomp_filter" = "x1" && \ test "x$ac_cv_header_elf_h" = "xyes" && \ test "x$ac_cv_header_linux_audit_h" = "xyes" && \ test "x$ac_cv_header_linux_filter_h" = "xyes" && \ test "x$seccomp_audit_arch" != "x" && \ test "x$have_linux_no_new_privs" = "x1" && \ test "x$ac_cv_func_prctl" = "xyes" ) ; then test "x$seccomp_audit_arch" = "x" && \ AC_MSG_ERROR([seccomp_filter sandbox not supported on $host]) test "x$have_linux_no_new_privs" != "x1" && \ AC_MSG_ERROR([seccomp_filter sandbox requires PR_SET_NO_NEW_PRIVS]) test "x$have_seccomp_filter" != "x1" && \ AC_MSG_ERROR([seccomp_filter sandbox requires seccomp headers]) test "x$ac_cv_func_prctl" != "xyes" && \ AC_MSG_ERROR([seccomp_filter sandbox requires prctl function]) SANDBOX_STYLE="seccomp_filter" AC_DEFINE([SANDBOX_SECCOMP_FILTER], [1], [Sandbox using seccomp filter]) elif test "x$sandbox_arg" = "xcapsicum" || \ ( test -z "$sandbox_arg" && \ test "x$ac_cv_header_sys_capsicum_h" = "xyes" && \ test "x$ac_cv_func_cap_rights_limit" = "xyes") ; then test "x$ac_cv_header_sys_capsicum_h" != "xyes" && \ AC_MSG_ERROR([capsicum sandbox requires sys/capsicum.h header]) test "x$ac_cv_func_cap_rights_limit" != "xyes" && \ AC_MSG_ERROR([capsicum sandbox requires cap_rights_limit function]) SANDBOX_STYLE="capsicum" AC_DEFINE([SANDBOX_CAPSICUM], [1], [Sandbox using capsicum]) elif test "x$sandbox_arg" = "xrlimit" || \ ( test -z "$sandbox_arg" && test "x$ac_cv_func_setrlimit" = "xyes" && \ test "x$select_works_with_rlimit" = "xyes" && \ test "x$rlimit_nofile_zero_works" = "xyes" ) ; then test "x$ac_cv_func_setrlimit" != "xyes" && \ AC_MSG_ERROR([rlimit sandbox requires setrlimit function]) test "x$select_works_with_rlimit" != "xyes" && \ AC_MSG_ERROR([rlimit sandbox requires select to work with rlimit]) SANDBOX_STYLE="rlimit" AC_DEFINE([SANDBOX_RLIMIT], [1], [Sandbox using setrlimit(2)]) elif test "x$sandbox_arg" = "xsolaris" || \ ( test -z "$sandbox_arg" && test "x$SOLARIS_PRIVS" = "xyes" ) ; then SANDBOX_STYLE="solaris" AC_DEFINE([SANDBOX_SOLARIS], [1], [Sandbox using Solaris/Illumos privileges]) elif test -z "$sandbox_arg" || test "x$sandbox_arg" = "xno" || \ test "x$sandbox_arg" = "xnone" || test "x$sandbox_arg" = "xnull" ; then SANDBOX_STYLE="none" AC_DEFINE([SANDBOX_NULL], [1], [no privsep sandboxing]) else AC_MSG_ERROR([unsupported --with-sandbox]) fi # Cheap hack to ensure NEWS-OS libraries are arranged right. if test ! -z "$SONY" ; then LIBS="$LIBS -liberty"; fi # Check for long long datatypes AC_CHECK_TYPES([long long, unsigned long long, long double]) # Check datatype sizes AC_CHECK_SIZEOF([short int], [2]) AC_CHECK_SIZEOF([int], [4]) AC_CHECK_SIZEOF([long int], [4]) AC_CHECK_SIZEOF([long long int], [8]) # Sanity check long long for some platforms (AIX) if test "x$ac_cv_sizeof_long_long_int" = "x4" ; then ac_cv_sizeof_long_long_int=0 fi # compute LLONG_MIN and LLONG_MAX if we don't know them. if test -z "$have_llong_max"; then AC_MSG_CHECKING([for max value of long long]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include /* Why is this so damn hard? */ #ifdef __GNUC__ # undef __GNUC__ #endif #define __USE_ISOC99 #include #define DATA "conftest.llminmax" #define my_abs(a) ((a) < 0 ? ((a) * -1) : (a)) /* * printf in libc on some platforms (eg old Tru64) does not understand %lld so * we do this the hard way. */ static int fprint_ll(FILE *f, long long n) { unsigned int i; int l[sizeof(long long) * 8]; if (n < 0) if (fprintf(f, "-") < 0) return -1; for (i = 0; n != 0; i++) { l[i] = my_abs(n % 10); n /= 10; } do { if (fprintf(f, "%d", l[--i]) < 0) return -1; } while (i != 0); if (fprintf(f, " ") < 0) return -1; return 0; } ]], [[ FILE *f; long long i, llmin, llmax = 0; if((f = fopen(DATA,"w")) == NULL) exit(1); #if defined(LLONG_MIN) && defined(LLONG_MAX) fprintf(stderr, "Using system header for LLONG_MIN and LLONG_MAX\n"); llmin = LLONG_MIN; llmax = LLONG_MAX; #else fprintf(stderr, "Calculating LLONG_MIN and LLONG_MAX\n"); /* This will work on one's complement and two's complement */ for (i = 1; i > llmax; i <<= 1, i++) llmax = i; llmin = llmax + 1LL; /* wrap */ #endif /* Sanity check */ if (llmin + 1 < llmin || llmin - 1 < llmin || llmax + 1 > llmax || llmax - 1 > llmax || llmin == llmax || llmin == 0 || llmax == 0 || llmax < LONG_MAX || llmin > LONG_MIN) { fprintf(f, "unknown unknown\n"); exit(2); } if (fprint_ll(f, llmin) < 0) exit(3); if (fprint_ll(f, llmax) < 0) exit(4); if (fclose(f) < 0) exit(5); exit(0); ]])], [ llong_min=`$AWK '{print $1}' conftest.llminmax` llong_max=`$AWK '{print $2}' conftest.llminmax` AC_MSG_RESULT([$llong_max]) AC_DEFINE_UNQUOTED([LLONG_MAX], [${llong_max}LL], [max value of long long calculated by configure]) AC_MSG_CHECKING([for min value of long long]) AC_MSG_RESULT([$llong_min]) AC_DEFINE_UNQUOTED([LLONG_MIN], [${llong_min}LL], [min value of long long calculated by configure]) ], [ AC_MSG_RESULT([not found]) ], [ AC_MSG_WARN([cross compiling: not checking]) ] ) fi # More checks for data types AC_CACHE_CHECK([for u_int type], ac_cv_have_u_int, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ u_int a; a = 1;]])], [ ac_cv_have_u_int="yes" ], [ ac_cv_have_u_int="no" ]) ]) if test "x$ac_cv_have_u_int" = "xyes" ; then AC_DEFINE([HAVE_U_INT], [1], [define if you have u_int data type]) have_u_int=1 fi AC_CACHE_CHECK([for intXX_t types], ac_cv_have_intxx_t, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ int8_t a; int16_t b; int32_t c; a = b = c = 1;]])], [ ac_cv_have_intxx_t="yes" ], [ ac_cv_have_intxx_t="no" ]) ]) if test "x$ac_cv_have_intxx_t" = "xyes" ; then AC_DEFINE([HAVE_INTXX_T], [1], [define if you have intxx_t data type]) have_intxx_t=1 fi if (test -z "$have_intxx_t" && \ test "x$ac_cv_header_stdint_h" = "xyes") then AC_MSG_CHECKING([for intXX_t types in stdint.h]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ int8_t a; int16_t b; int32_t c; a = b = c = 1;]])], [ AC_DEFINE([HAVE_INTXX_T]) AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ]) fi AC_CACHE_CHECK([for int64_t type], ac_cv_have_int64_t, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #ifdef HAVE_STDINT_H # include #endif #include #ifdef HAVE_SYS_BITYPES_H # include #endif ]], [[ int64_t a; a = 1; ]])], [ ac_cv_have_int64_t="yes" ], [ ac_cv_have_int64_t="no" ]) ]) if test "x$ac_cv_have_int64_t" = "xyes" ; then AC_DEFINE([HAVE_INT64_T], [1], [define if you have int64_t data type]) fi AC_CACHE_CHECK([for u_intXX_t types], ac_cv_have_u_intxx_t, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ u_int8_t a; u_int16_t b; u_int32_t c; a = b = c = 1;]])], [ ac_cv_have_u_intxx_t="yes" ], [ ac_cv_have_u_intxx_t="no" ]) ]) if test "x$ac_cv_have_u_intxx_t" = "xyes" ; then AC_DEFINE([HAVE_U_INTXX_T], [1], [define if you have u_intxx_t data type]) have_u_intxx_t=1 fi if test -z "$have_u_intxx_t" ; then AC_MSG_CHECKING([for u_intXX_t types in sys/socket.h]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ u_int8_t a; u_int16_t b; u_int32_t c; a = b = c = 1;]])], [ AC_DEFINE([HAVE_U_INTXX_T]) AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ]) fi AC_CACHE_CHECK([for u_int64_t types], ac_cv_have_u_int64_t, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ u_int64_t a; a = 1;]])], [ ac_cv_have_u_int64_t="yes" ], [ ac_cv_have_u_int64_t="no" ]) ]) if test "x$ac_cv_have_u_int64_t" = "xyes" ; then AC_DEFINE([HAVE_U_INT64_T], [1], [define if you have u_int64_t data type]) have_u_int64_t=1 fi if (test -z "$have_u_int64_t" && \ test "x$ac_cv_header_sys_bitypes_h" = "xyes") then AC_MSG_CHECKING([for u_int64_t type in sys/bitypes.h]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ u_int64_t a; a = 1]])], [ AC_DEFINE([HAVE_U_INT64_T]) AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ]) fi if test -z "$have_u_intxx_t" ; then AC_CACHE_CHECK([for uintXX_t types], ac_cv_have_uintxx_t, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ uint8_t a; uint16_t b; uint32_t c; a = b = c = 1; ]])], [ ac_cv_have_uintxx_t="yes" ], [ ac_cv_have_uintxx_t="no" ]) ]) if test "x$ac_cv_have_uintxx_t" = "xyes" ; then AC_DEFINE([HAVE_UINTXX_T], [1], [define if you have uintxx_t data type]) fi fi if (test -z "$have_uintxx_t" && \ test "x$ac_cv_header_stdint_h" = "xyes") then AC_MSG_CHECKING([for uintXX_t types in stdint.h]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ uint8_t a; uint16_t b; uint32_t c; a = b = c = 1;]])], [ AC_DEFINE([HAVE_UINTXX_T]) AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ]) fi if (test -z "$have_uintxx_t" && \ test "x$ac_cv_header_inttypes_h" = "xyes") then AC_MSG_CHECKING([for uintXX_t types in inttypes.h]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ uint8_t a; uint16_t b; uint32_t c; a = b = c = 1;]])], [ AC_DEFINE([HAVE_UINTXX_T]) AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) ]) fi if (test -z "$have_u_intxx_t" || test -z "$have_intxx_t" && \ test "x$ac_cv_header_sys_bitypes_h" = "xyes") then AC_MSG_CHECKING([for intXX_t and u_intXX_t types in sys/bitypes.h]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ int8_t a; int16_t b; int32_t c; u_int8_t e; u_int16_t f; u_int32_t g; a = b = c = e = f = g = 1; ]])], [ AC_DEFINE([HAVE_U_INTXX_T]) AC_DEFINE([HAVE_INTXX_T]) AC_MSG_RESULT([yes]) ], [AC_MSG_RESULT([no]) ]) fi AC_CACHE_CHECK([for u_char], ac_cv_have_u_char, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ u_char foo; foo = 125; ]])], [ ac_cv_have_u_char="yes" ], [ ac_cv_have_u_char="no" ]) ]) if test "x$ac_cv_have_u_char" = "xyes" ; then AC_DEFINE([HAVE_U_CHAR], [1], [define if you have u_char data type]) fi AC_CHECK_TYPES([intmax_t, uintmax_t], , , [ #include #include ]) TYPE_SOCKLEN_T AC_CHECK_TYPES([sig_atomic_t], , , [#include ]) AC_CHECK_TYPES([fsblkcnt_t, fsfilcnt_t], , , [ #include #ifdef HAVE_SYS_BITYPES_H #include #endif #ifdef HAVE_SYS_STATFS_H #include #endif #ifdef HAVE_SYS_STATVFS_H #include #endif ]) AC_CHECK_TYPES([in_addr_t, in_port_t], , , [#include #include ]) AC_CACHE_CHECK([for size_t], ac_cv_have_size_t, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ size_t foo; foo = 1235; ]])], [ ac_cv_have_size_t="yes" ], [ ac_cv_have_size_t="no" ]) ]) if test "x$ac_cv_have_size_t" = "xyes" ; then AC_DEFINE([HAVE_SIZE_T], [1], [define if you have size_t data type]) fi AC_CACHE_CHECK([for ssize_t], ac_cv_have_ssize_t, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ ssize_t foo; foo = 1235; ]])], [ ac_cv_have_ssize_t="yes" ], [ ac_cv_have_ssize_t="no" ]) ]) if test "x$ac_cv_have_ssize_t" = "xyes" ; then AC_DEFINE([HAVE_SSIZE_T], [1], [define if you have ssize_t data type]) fi AC_CACHE_CHECK([for clock_t], ac_cv_have_clock_t, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ clock_t foo; foo = 1235; ]])], [ ac_cv_have_clock_t="yes" ], [ ac_cv_have_clock_t="no" ]) ]) if test "x$ac_cv_have_clock_t" = "xyes" ; then AC_DEFINE([HAVE_CLOCK_T], [1], [define if you have clock_t data type]) fi AC_CACHE_CHECK([for sa_family_t], ac_cv_have_sa_family_t, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ sa_family_t foo; foo = 1235; ]])], [ ac_cv_have_sa_family_t="yes" ], [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #include ]], [[ sa_family_t foo; foo = 1235; ]])], [ ac_cv_have_sa_family_t="yes" ], [ ac_cv_have_sa_family_t="no" ] ) ]) ]) if test "x$ac_cv_have_sa_family_t" = "xyes" ; then AC_DEFINE([HAVE_SA_FAMILY_T], [1], [define if you have sa_family_t data type]) fi AC_CACHE_CHECK([for pid_t], ac_cv_have_pid_t, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ pid_t foo; foo = 1235; ]])], [ ac_cv_have_pid_t="yes" ], [ ac_cv_have_pid_t="no" ]) ]) if test "x$ac_cv_have_pid_t" = "xyes" ; then AC_DEFINE([HAVE_PID_T], [1], [define if you have pid_t data type]) fi AC_CACHE_CHECK([for mode_t], ac_cv_have_mode_t, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ mode_t foo; foo = 1235; ]])], [ ac_cv_have_mode_t="yes" ], [ ac_cv_have_mode_t="no" ]) ]) if test "x$ac_cv_have_mode_t" = "xyes" ; then AC_DEFINE([HAVE_MODE_T], [1], [define if you have mode_t data type]) fi AC_CACHE_CHECK([for struct sockaddr_storage], ac_cv_have_struct_sockaddr_storage, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ struct sockaddr_storage s; ]])], [ ac_cv_have_struct_sockaddr_storage="yes" ], [ ac_cv_have_struct_sockaddr_storage="no" ]) ]) if test "x$ac_cv_have_struct_sockaddr_storage" = "xyes" ; then AC_DEFINE([HAVE_STRUCT_SOCKADDR_STORAGE], [1], [define if you have struct sockaddr_storage data type]) fi AC_CACHE_CHECK([for struct sockaddr_in6], ac_cv_have_struct_sockaddr_in6, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ struct sockaddr_in6 s; s.sin6_family = 0; ]])], [ ac_cv_have_struct_sockaddr_in6="yes" ], [ ac_cv_have_struct_sockaddr_in6="no" ]) ]) if test "x$ac_cv_have_struct_sockaddr_in6" = "xyes" ; then AC_DEFINE([HAVE_STRUCT_SOCKADDR_IN6], [1], [define if you have struct sockaddr_in6 data type]) fi AC_CACHE_CHECK([for struct in6_addr], ac_cv_have_struct_in6_addr, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ struct in6_addr s; s.s6_addr[0] = 0; ]])], [ ac_cv_have_struct_in6_addr="yes" ], [ ac_cv_have_struct_in6_addr="no" ]) ]) if test "x$ac_cv_have_struct_in6_addr" = "xyes" ; then AC_DEFINE([HAVE_STRUCT_IN6_ADDR], [1], [define if you have struct in6_addr data type]) dnl Now check for sin6_scope_id AC_CHECK_MEMBERS([struct sockaddr_in6.sin6_scope_id], , , [ #ifdef HAVE_SYS_TYPES_H #include #endif #include ]) fi AC_CACHE_CHECK([for struct addrinfo], ac_cv_have_struct_addrinfo, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #include ]], [[ struct addrinfo s; s.ai_flags = AI_PASSIVE; ]])], [ ac_cv_have_struct_addrinfo="yes" ], [ ac_cv_have_struct_addrinfo="no" ]) ]) if test "x$ac_cv_have_struct_addrinfo" = "xyes" ; then AC_DEFINE([HAVE_STRUCT_ADDRINFO], [1], [define if you have struct addrinfo data type]) fi AC_CACHE_CHECK([for struct timeval], ac_cv_have_struct_timeval, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ struct timeval tv; tv.tv_sec = 1;]])], [ ac_cv_have_struct_timeval="yes" ], [ ac_cv_have_struct_timeval="no" ]) ]) if test "x$ac_cv_have_struct_timeval" = "xyes" ; then AC_DEFINE([HAVE_STRUCT_TIMEVAL], [1], [define if you have struct timeval]) have_struct_timeval=1 fi AC_CHECK_TYPES([struct timespec]) # We need int64_t or else certian parts of the compile will fail. if test "x$ac_cv_have_int64_t" = "xno" && \ test "x$ac_cv_sizeof_long_int" != "x8" && \ test "x$ac_cv_sizeof_long_long_int" = "x0" ; then echo "OpenSSH requires int64_t support. Contact your vendor or install" echo "an alternative compiler (I.E., GCC) before continuing." echo "" exit 1; else dnl test snprintf (broken on SCO w/gcc) AC_RUN_IFELSE( [AC_LANG_SOURCE([[ #include #include #ifdef HAVE_SNPRINTF main() { char buf[50]; char expected_out[50]; int mazsize = 50 ; #if (SIZEOF_LONG_INT == 8) long int num = 0x7fffffffffffffff; #else long long num = 0x7fffffffffffffffll; #endif strcpy(expected_out, "9223372036854775807"); snprintf(buf, mazsize, "%lld", num); if(strcmp(buf, expected_out) != 0) exit(1); exit(0); } #else main() { exit(0); } #endif ]])], [ true ], [ AC_DEFINE([BROKEN_SNPRINTF]) ], AC_MSG_WARN([cross compiling: Assuming working snprintf()]) ) fi dnl Checks for structure members OSSH_CHECK_HEADER_FOR_FIELD([ut_host], [utmp.h], [HAVE_HOST_IN_UTMP]) OSSH_CHECK_HEADER_FOR_FIELD([ut_host], [utmpx.h], [HAVE_HOST_IN_UTMPX]) OSSH_CHECK_HEADER_FOR_FIELD([syslen], [utmpx.h], [HAVE_SYSLEN_IN_UTMPX]) OSSH_CHECK_HEADER_FOR_FIELD([ut_pid], [utmp.h], [HAVE_PID_IN_UTMP]) OSSH_CHECK_HEADER_FOR_FIELD([ut_type], [utmp.h], [HAVE_TYPE_IN_UTMP]) OSSH_CHECK_HEADER_FOR_FIELD([ut_type], [utmpx.h], [HAVE_TYPE_IN_UTMPX]) OSSH_CHECK_HEADER_FOR_FIELD([ut_tv], [utmp.h], [HAVE_TV_IN_UTMP]) OSSH_CHECK_HEADER_FOR_FIELD([ut_id], [utmp.h], [HAVE_ID_IN_UTMP]) OSSH_CHECK_HEADER_FOR_FIELD([ut_id], [utmpx.h], [HAVE_ID_IN_UTMPX]) OSSH_CHECK_HEADER_FOR_FIELD([ut_addr], [utmp.h], [HAVE_ADDR_IN_UTMP]) OSSH_CHECK_HEADER_FOR_FIELD([ut_addr], [utmpx.h], [HAVE_ADDR_IN_UTMPX]) OSSH_CHECK_HEADER_FOR_FIELD([ut_addr_v6], [utmp.h], [HAVE_ADDR_V6_IN_UTMP]) OSSH_CHECK_HEADER_FOR_FIELD([ut_addr_v6], [utmpx.h], [HAVE_ADDR_V6_IN_UTMPX]) OSSH_CHECK_HEADER_FOR_FIELD([ut_exit], [utmp.h], [HAVE_EXIT_IN_UTMP]) OSSH_CHECK_HEADER_FOR_FIELD([ut_time], [utmp.h], [HAVE_TIME_IN_UTMP]) OSSH_CHECK_HEADER_FOR_FIELD([ut_time], [utmpx.h], [HAVE_TIME_IN_UTMPX]) OSSH_CHECK_HEADER_FOR_FIELD([ut_tv], [utmpx.h], [HAVE_TV_IN_UTMPX]) AC_CHECK_MEMBERS([struct stat.st_blksize]) AC_CHECK_MEMBERS([struct passwd.pw_gecos, struct passwd.pw_class, struct passwd.pw_change, struct passwd.pw_expire], [], [], [[ #include #include ]]) AC_CHECK_MEMBER([struct __res_state.retrans], [], [AC_DEFINE([__res_state], [state], [Define if we don't have struct __res_state in resolv.h])], [[ #include #if HAVE_SYS_TYPES_H # include #endif #include #include #include ]]) AC_CACHE_CHECK([for ss_family field in struct sockaddr_storage], ac_cv_have_ss_family_in_struct_ss, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ struct sockaddr_storage s; s.ss_family = 1; ]])], [ ac_cv_have_ss_family_in_struct_ss="yes" ], [ ac_cv_have_ss_family_in_struct_ss="no" ]) ]) if test "x$ac_cv_have_ss_family_in_struct_ss" = "xyes" ; then AC_DEFINE([HAVE_SS_FAMILY_IN_SS], [1], [Fields in struct sockaddr_storage]) fi AC_CACHE_CHECK([for __ss_family field in struct sockaddr_storage], ac_cv_have___ss_family_in_struct_ss, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ struct sockaddr_storage s; s.__ss_family = 1; ]])], [ ac_cv_have___ss_family_in_struct_ss="yes" ], [ ac_cv_have___ss_family_in_struct_ss="no" ]) ]) if test "x$ac_cv_have___ss_family_in_struct_ss" = "xyes" ; then AC_DEFINE([HAVE___SS_FAMILY_IN_SS], [1], [Fields in struct sockaddr_storage]) fi dnl make sure we're using the real structure members and not defines AC_CACHE_CHECK([for msg_accrights field in struct msghdr], ac_cv_have_accrights_in_msghdr, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #include ]], [[ #ifdef msg_accrights #error "msg_accrights is a macro" exit(1); #endif struct msghdr m; m.msg_accrights = 0; exit(0); ]])], [ ac_cv_have_accrights_in_msghdr="yes" ], [ ac_cv_have_accrights_in_msghdr="no" ] ) ]) if test "x$ac_cv_have_accrights_in_msghdr" = "xyes" ; then AC_DEFINE([HAVE_ACCRIGHTS_IN_MSGHDR], [1], [Define if your system uses access rights style file descriptor passing]) fi AC_MSG_CHECKING([if struct statvfs.f_fsid is integral type]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_MOUNT_H #include #endif #ifdef HAVE_SYS_STATVFS_H #include #endif ]], [[ struct statvfs s; s.f_fsid = 0; ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) AC_MSG_CHECKING([if fsid_t has member val]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ fsid_t t; t.val[0] = 0; ]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([FSID_HAS_VAL], [1], [fsid_t has member val]) ], [ AC_MSG_RESULT([no]) ]) AC_MSG_CHECKING([if f_fsid has member __val]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include ]], [[ fsid_t t; t.__val[0] = 0; ]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([FSID_HAS___VAL], [1], [fsid_t has member __val]) ], [ AC_MSG_RESULT([no]) ]) ]) AC_CACHE_CHECK([for msg_control field in struct msghdr], ac_cv_have_control_in_msghdr, [ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #include ]], [[ #ifdef msg_control #error "msg_control is a macro" exit(1); #endif struct msghdr m; m.msg_control = 0; exit(0); ]])], [ ac_cv_have_control_in_msghdr="yes" ], [ ac_cv_have_control_in_msghdr="no" ] ) ]) if test "x$ac_cv_have_control_in_msghdr" = "xyes" ; then AC_DEFINE([HAVE_CONTROL_IN_MSGHDR], [1], [Define if your system uses ancillary data style file descriptor passing]) fi AC_CACHE_CHECK([if libc defines __progname], ac_cv_libc_defines___progname, [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[ extern char *__progname; printf("%s", __progname); ]])], [ ac_cv_libc_defines___progname="yes" ], [ ac_cv_libc_defines___progname="no" ]) ]) if test "x$ac_cv_libc_defines___progname" = "xyes" ; then AC_DEFINE([HAVE___PROGNAME], [1], [Define if libc defines __progname]) fi AC_CACHE_CHECK([whether $CC implements __FUNCTION__], ac_cv_cc_implements___FUNCTION__, [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ printf("%s", __FUNCTION__); ]])], [ ac_cv_cc_implements___FUNCTION__="yes" ], [ ac_cv_cc_implements___FUNCTION__="no" ]) ]) if test "x$ac_cv_cc_implements___FUNCTION__" = "xyes" ; then AC_DEFINE([HAVE___FUNCTION__], [1], [Define if compiler implements __FUNCTION__]) fi AC_CACHE_CHECK([whether $CC implements __func__], ac_cv_cc_implements___func__, [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ printf("%s", __func__); ]])], [ ac_cv_cc_implements___func__="yes" ], [ ac_cv_cc_implements___func__="no" ]) ]) if test "x$ac_cv_cc_implements___func__" = "xyes" ; then AC_DEFINE([HAVE___func__], [1], [Define if compiler implements __func__]) fi AC_CACHE_CHECK([whether va_copy exists], ac_cv_have_va_copy, [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include va_list x,y; ]], [[ va_copy(x,y); ]])], [ ac_cv_have_va_copy="yes" ], [ ac_cv_have_va_copy="no" ]) ]) if test "x$ac_cv_have_va_copy" = "xyes" ; then AC_DEFINE([HAVE_VA_COPY], [1], [Define if va_copy exists]) fi AC_CACHE_CHECK([whether __va_copy exists], ac_cv_have___va_copy, [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include va_list x,y; ]], [[ __va_copy(x,y); ]])], [ ac_cv_have___va_copy="yes" ], [ ac_cv_have___va_copy="no" ]) ]) if test "x$ac_cv_have___va_copy" = "xyes" ; then AC_DEFINE([HAVE___VA_COPY], [1], [Define if __va_copy exists]) fi AC_CACHE_CHECK([whether getopt has optreset support], ac_cv_have_getopt_optreset, [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ extern int optreset; optreset = 0; ]])], [ ac_cv_have_getopt_optreset="yes" ], [ ac_cv_have_getopt_optreset="no" ]) ]) if test "x$ac_cv_have_getopt_optreset" = "xyes" ; then AC_DEFINE([HAVE_GETOPT_OPTRESET], [1], [Define if your getopt(3) defines and uses optreset]) fi AC_CACHE_CHECK([if libc defines sys_errlist], ac_cv_libc_defines_sys_errlist, [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[ extern const char *const sys_errlist[]; printf("%s", sys_errlist[0]);]])], [ ac_cv_libc_defines_sys_errlist="yes" ], [ ac_cv_libc_defines_sys_errlist="no" ]) ]) if test "x$ac_cv_libc_defines_sys_errlist" = "xyes" ; then AC_DEFINE([HAVE_SYS_ERRLIST], [1], [Define if your system defines sys_errlist[]]) fi AC_CACHE_CHECK([if libc defines sys_nerr], ac_cv_libc_defines_sys_nerr, [ AC_LINK_IFELSE([AC_LANG_PROGRAM([[]], [[ extern int sys_nerr; printf("%i", sys_nerr);]])], [ ac_cv_libc_defines_sys_nerr="yes" ], [ ac_cv_libc_defines_sys_nerr="no" ]) ]) if test "x$ac_cv_libc_defines_sys_nerr" = "xyes" ; then AC_DEFINE([HAVE_SYS_NERR], [1], [Define if your system defines sys_nerr]) fi # Check libraries needed by DNS fingerprint support AC_SEARCH_LIBS([getrrsetbyname], [resolv], [AC_DEFINE([HAVE_GETRRSETBYNAME], [1], [Define if getrrsetbyname() exists])], [ # Needed by our getrrsetbyname() AC_SEARCH_LIBS([res_query], [resolv]) AC_SEARCH_LIBS([dn_expand], [resolv]) AC_MSG_CHECKING([if res_query will link]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include #include #include #include ]], [[ res_query (0, 0, 0, 0, 0); ]])], AC_MSG_RESULT([yes]), [AC_MSG_RESULT([no]) saved_LIBS="$LIBS" LIBS="$LIBS -lresolv" AC_MSG_CHECKING([for res_query in -lresolv]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #include #include #include #include ]], [[ res_query (0, 0, 0, 0, 0); ]])], [AC_MSG_RESULT([yes])], [LIBS="$saved_LIBS" AC_MSG_RESULT([no])]) ]) AC_CHECK_FUNCS([_getshort _getlong]) AC_CHECK_DECLS([_getshort, _getlong], , , [#include #include ]) AC_CHECK_MEMBER([HEADER.ad], [AC_DEFINE([HAVE_HEADER_AD], [1], [Define if HEADER.ad exists in arpa/nameser.h])], , [#include ]) ]) AC_MSG_CHECKING([if struct __res_state _res is an extern]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include #if HAVE_SYS_TYPES_H # include #endif #include #include #include extern struct __res_state _res; ]], [[ struct __res_state *volatile p = &_res; /* force resolution of _res */ return 0; ]],)], [AC_MSG_RESULT([yes]) AC_DEFINE([HAVE__RES_EXTERN], [1], [Define if you have struct __res_state _res as an extern]) ], [ AC_MSG_RESULT([no]) ] ) # Check whether user wants SELinux support SELINUX_MSG="no" LIBSELINUX="" AC_ARG_WITH([selinux], [ --with-selinux Enable SELinux support], [ if test "x$withval" != "xno" ; then save_LIBS="$LIBS" AC_DEFINE([WITH_SELINUX], [1], [Define if you want SELinux support.]) SELINUX_MSG="yes" AC_CHECK_HEADER([selinux/selinux.h], , AC_MSG_ERROR([SELinux support requires selinux.h header])) AC_CHECK_LIB([selinux], [setexeccon], [ LIBSELINUX="-lselinux" LIBS="$LIBS -lselinux" ], AC_MSG_ERROR([SELinux support requires libselinux library])) SSHLIBS="$SSHLIBS $LIBSELINUX" SSHDLIBS="$SSHDLIBS $LIBSELINUX" AC_CHECK_FUNCS([getseuserbyname get_default_context_with_level]) LIBS="$save_LIBS" fi ] ) AC_SUBST([SSHLIBS]) AC_SUBST([SSHDLIBS]) # Check whether user wants Kerberos 5 support KRB5_MSG="no" AC_ARG_WITH([kerberos5], [ --with-kerberos5=PATH Enable Kerberos 5 support], [ if test "x$withval" != "xno" ; then if test "x$withval" = "xyes" ; then KRB5ROOT="/usr/local" else KRB5ROOT=${withval} fi AC_DEFINE([KRB5], [1], [Define if you want Kerberos 5 support]) KRB5_MSG="yes" AC_PATH_TOOL([KRB5CONF], [krb5-config], [$KRB5ROOT/bin/krb5-config], [$KRB5ROOT/bin:$PATH]) if test -x $KRB5CONF ; then K5CFLAGS="`$KRB5CONF --cflags`" K5LIBS="`$KRB5CONF --libs`" CPPFLAGS="$CPPFLAGS $K5CFLAGS" AC_MSG_CHECKING([for gssapi support]) if $KRB5CONF | grep gssapi >/dev/null ; then AC_MSG_RESULT([yes]) AC_DEFINE([GSSAPI], [1], [Define this if you want GSSAPI support in the version 2 protocol]) GSSCFLAGS="`$KRB5CONF --cflags gssapi`" GSSLIBS="`$KRB5CONF --libs gssapi`" CPPFLAGS="$CPPFLAGS $GSSCFLAGS" else AC_MSG_RESULT([no]) fi AC_MSG_CHECKING([whether we are using Heimdal]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ char *tmp = heimdal_version; ]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([HEIMDAL], [1], [Define this if you are using the Heimdal version of Kerberos V5]) ], [AC_MSG_RESULT([no]) ]) else CPPFLAGS="$CPPFLAGS -I${KRB5ROOT}/include" LDFLAGS="$LDFLAGS -L${KRB5ROOT}/lib" AC_MSG_CHECKING([whether we are using Heimdal]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include ]], [[ char *tmp = heimdal_version; ]])], [ AC_MSG_RESULT([yes]) AC_DEFINE([HEIMDAL]) K5LIBS="-lkrb5" K5LIBS="$K5LIBS -lcom_err -lasn1" AC_CHECK_LIB([roken], [net_write], [K5LIBS="$K5LIBS -lroken"]) AC_CHECK_LIB([des], [des_cbc_encrypt], [K5LIBS="$K5LIBS -ldes"]) ], [ AC_MSG_RESULT([no]) K5LIBS="-lkrb5 -lk5crypto -lcom_err" ]) AC_SEARCH_LIBS([dn_expand], [resolv]) AC_CHECK_LIB([gssapi_krb5], [gss_init_sec_context], [ AC_DEFINE([GSSAPI]) GSSLIBS="-lgssapi_krb5" ], [ AC_CHECK_LIB([gssapi], [gss_init_sec_context], [ AC_DEFINE([GSSAPI]) GSSLIBS="-lgssapi" ], [ AC_CHECK_LIB([gss], [gss_init_sec_context], [ AC_DEFINE([GSSAPI]) GSSLIBS="-lgss" ], AC_MSG_WARN([Cannot find any suitable gss-api library - build may fail])) ]) ]) AC_CHECK_HEADER([gssapi.h], , [ unset ac_cv_header_gssapi_h CPPFLAGS="$CPPFLAGS -I${KRB5ROOT}/include/gssapi" AC_CHECK_HEADERS([gssapi.h], , AC_MSG_WARN([Cannot find any suitable gss-api header - build may fail]) ) ] ) oldCPP="$CPPFLAGS" CPPFLAGS="$CPPFLAGS -I${KRB5ROOT}/include/gssapi" AC_CHECK_HEADER([gssapi_krb5.h], , [ CPPFLAGS="$oldCPP" ]) fi if test ! -z "$need_dash_r" ; then LDFLAGS="$LDFLAGS -R${KRB5ROOT}/lib" fi if test ! -z "$blibpath" ; then blibpath="$blibpath:${KRB5ROOT}/lib" fi AC_CHECK_HEADERS([gssapi.h gssapi/gssapi.h]) AC_CHECK_HEADERS([gssapi_krb5.h gssapi/gssapi_krb5.h]) AC_CHECK_HEADERS([gssapi_generic.h gssapi/gssapi_generic.h]) AC_SEARCH_LIBS([k_hasafs], [kafs], [AC_DEFINE([USE_AFS], [1], [Define this if you want to use libkafs' AFS support])]) AC_CHECK_DECLS([GSS_C_NT_HOSTBASED_SERVICE], [], [], [[ #ifdef HAVE_GSSAPI_H # include #elif defined(HAVE_GSSAPI_GSSAPI_H) # include #endif #ifdef HAVE_GSSAPI_GENERIC_H # include #elif defined(HAVE_GSSAPI_GSSAPI_GENERIC_H) # include #endif ]]) saved_LIBS="$LIBS" LIBS="$LIBS $K5LIBS" AC_CHECK_FUNCS([krb5_cc_new_unique krb5_get_error_message krb5_free_error_message]) LIBS="$saved_LIBS" fi ] ) AC_SUBST([GSSLIBS]) AC_SUBST([K5LIBS]) # Looking for programs, paths and files PRIVSEP_PATH=/var/empty AC_ARG_WITH([privsep-path], [ --with-privsep-path=xxx Path for privilege separation chroot (default=/var/empty)], [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then PRIVSEP_PATH=$withval fi ] ) AC_SUBST([PRIVSEP_PATH]) AC_ARG_WITH([xauth], [ --with-xauth=PATH Specify path to xauth program ], [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then xauth_path=$withval fi ], [ TestPath="$PATH" TestPath="${TestPath}${PATH_SEPARATOR}/usr/X/bin" TestPath="${TestPath}${PATH_SEPARATOR}/usr/bin/X11" TestPath="${TestPath}${PATH_SEPARATOR}/usr/X11R6/bin" TestPath="${TestPath}${PATH_SEPARATOR}/usr/openwin/bin" AC_PATH_PROG([xauth_path], [xauth], , [$TestPath]) if (test ! -z "$xauth_path" && test -x "/usr/openwin/bin/xauth") ; then xauth_path="/usr/openwin/bin/xauth" fi ] ) STRIP_OPT=-s AC_ARG_ENABLE([strip], [ --disable-strip Disable calling strip(1) on install], [ if test "x$enableval" = "xno" ; then STRIP_OPT= fi ] ) AC_SUBST([STRIP_OPT]) if test -z "$xauth_path" ; then XAUTH_PATH="undefined" AC_SUBST([XAUTH_PATH]) else AC_DEFINE_UNQUOTED([XAUTH_PATH], ["$xauth_path"], [Define if xauth is found in your path]) XAUTH_PATH=$xauth_path AC_SUBST([XAUTH_PATH]) fi dnl # --with-maildir=/path/to/mail gets top priority. dnl # if maildir is set in the platform case statement above we use that. dnl # Otherwise we run a program to get the dir from system headers. dnl # We first look for _PATH_MAILDIR then MAILDIR then _PATH_MAIL dnl # If we find _PATH_MAILDIR we do nothing because that is what dnl # session.c expects anyway. Otherwise we set to the value found dnl # stripping any trailing slash. If for some strage reason our program dnl # does not find what it needs, we default to /var/spool/mail. # Check for mail directory AC_ARG_WITH([maildir], [ --with-maildir=/path/to/mail Specify your system mail directory], [ if test "X$withval" != X && test "x$withval" != xno && \ test "x${withval}" != xyes; then AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["$withval"], [Set this to your mail directory if you do not have _PATH_MAILDIR]) fi ],[ if test "X$maildir" != "X"; then AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["$maildir"]) else AC_MSG_CHECKING([Discovering system mail directory]) AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include #include #ifdef HAVE_PATHS_H #include #endif #ifdef HAVE_MAILLOCK_H #include #endif #define DATA "conftest.maildir" ]], [[ FILE *fd; int rc; fd = fopen(DATA,"w"); if(fd == NULL) exit(1); #if defined (_PATH_MAILDIR) if ((rc = fprintf(fd ,"_PATH_MAILDIR:%s\n", _PATH_MAILDIR)) <0) exit(1); #elif defined (MAILDIR) if ((rc = fprintf(fd ,"MAILDIR:%s\n", MAILDIR)) <0) exit(1); #elif defined (_PATH_MAIL) if ((rc = fprintf(fd ,"_PATH_MAIL:%s\n", _PATH_MAIL)) <0) exit(1); #else exit (2); #endif exit(0); ]])], [ maildir_what=`awk -F: '{print $1}' conftest.maildir` maildir=`awk -F: '{print $2}' conftest.maildir \ | sed 's|/$||'` AC_MSG_RESULT([Using: $maildir from $maildir_what]) if test "x$maildir_what" != "x_PATH_MAILDIR"; then AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["$maildir"]) fi ], [ if test "X$ac_status" = "X2";then # our test program didn't find it. Default to /var/spool/mail AC_MSG_RESULT([Using: default value of /var/spool/mail]) AC_DEFINE_UNQUOTED([MAIL_DIRECTORY], ["/var/spool/mail"]) else AC_MSG_RESULT([*** not found ***]) fi ], [ AC_MSG_WARN([cross compiling: use --with-maildir=/path/to/mail]) ] ) fi ] ) # maildir if test ! -z "$cross_compiling" && test "x$cross_compiling" = "xyes"; then AC_MSG_WARN([cross compiling: Disabling /dev/ptmx test]) disable_ptmx_check=yes fi if test -z "$no_dev_ptmx" ; then if test "x$disable_ptmx_check" != "xyes" ; then AC_CHECK_FILE(["/dev/ptmx"], [ AC_DEFINE_UNQUOTED([HAVE_DEV_PTMX], [1], [Define if you have /dev/ptmx]) have_dev_ptmx=1 ] ) fi fi if test ! -z "$cross_compiling" && test "x$cross_compiling" != "xyes"; then AC_CHECK_FILE(["/dev/ptc"], [ AC_DEFINE_UNQUOTED([HAVE_DEV_PTS_AND_PTC], [1], [Define if you have /dev/ptc]) have_dev_ptc=1 ] ) else AC_MSG_WARN([cross compiling: Disabling /dev/ptc test]) fi # Options from here on. Some of these are preset by platform above AC_ARG_WITH([mantype], [ --with-mantype=man|cat|doc Set man page type], [ case "$withval" in man|cat|doc) MANTYPE=$withval ;; *) AC_MSG_ERROR([invalid man type: $withval]) ;; esac ] ) if test -z "$MANTYPE"; then TestPath="/usr/bin${PATH_SEPARATOR}/usr/ucb" AC_PATH_PROGS([NROFF], [nroff awf], [/bin/false], [$TestPath]) if ${NROFF} -mdoc ${srcdir}/ssh.1 >/dev/null 2>&1; then MANTYPE=doc elif ${NROFF} -man ${srcdir}/ssh.1 >/dev/null 2>&1; then MANTYPE=man else MANTYPE=cat fi fi AC_SUBST([MANTYPE]) if test "$MANTYPE" = "doc"; then mansubdir=man; else mansubdir=$MANTYPE; fi AC_SUBST([mansubdir]) # Check whether to enable MD5 passwords MD5_MSG="no" AC_ARG_WITH([md5-passwords], [ --with-md5-passwords Enable use of MD5 passwords], [ if test "x$withval" != "xno" ; then AC_DEFINE([HAVE_MD5_PASSWORDS], [1], [Define if you want to allow MD5 passwords]) MD5_MSG="yes" fi ] ) # Whether to disable shadow password support AC_ARG_WITH([shadow], [ --without-shadow Disable shadow password support], [ if test "x$withval" = "xno" ; then AC_DEFINE([DISABLE_SHADOW]) disable_shadow=yes fi ] ) if test -z "$disable_shadow" ; then AC_MSG_CHECKING([if the systems has expire shadow information]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include struct spwd sp; ]], [[ sp.sp_expire = sp.sp_lstchg = sp.sp_inact = 0; ]])], [ sp_expire_available=yes ], [ ]) if test "x$sp_expire_available" = "xyes" ; then AC_MSG_RESULT([yes]) AC_DEFINE([HAS_SHADOW_EXPIRE], [1], [Define if you want to use shadow password expire field]) else AC_MSG_RESULT([no]) fi fi # Use ip address instead of hostname in $DISPLAY if test ! -z "$IPADDR_IN_DISPLAY" ; then DISPLAY_HACK_MSG="yes" AC_DEFINE([IPADDR_IN_DISPLAY], [1], [Define if you need to use IP address instead of hostname in $DISPLAY]) else DISPLAY_HACK_MSG="no" AC_ARG_WITH([ipaddr-display], [ --with-ipaddr-display Use ip address instead of hostname in $DISPLAY], [ if test "x$withval" != "xno" ; then AC_DEFINE([IPADDR_IN_DISPLAY]) DISPLAY_HACK_MSG="yes" fi ] ) fi # check for /etc/default/login and use it if present. AC_ARG_ENABLE([etc-default-login], [ --disable-etc-default-login Disable using PATH from /etc/default/login [no]], [ if test "x$enableval" = "xno"; then AC_MSG_NOTICE([/etc/default/login handling disabled]) etc_default_login=no else etc_default_login=yes fi ], [ if test ! -z "$cross_compiling" && test "x$cross_compiling" = "xyes"; then AC_MSG_WARN([cross compiling: not checking /etc/default/login]) etc_default_login=no else etc_default_login=yes fi ] ) if test "x$etc_default_login" != "xno"; then AC_CHECK_FILE(["/etc/default/login"], [ external_path_file=/etc/default/login ]) if test "x$external_path_file" = "x/etc/default/login"; then AC_DEFINE([HAVE_ETC_DEFAULT_LOGIN], [1], [Define if your system has /etc/default/login]) fi fi dnl BSD systems use /etc/login.conf so --with-default-path= has no effect if test $ac_cv_func_login_getcapbool = "yes" && \ test $ac_cv_header_login_cap_h = "yes" ; then external_path_file=/etc/login.conf fi # Whether to mess with the default path SERVER_PATH_MSG="(default)" AC_ARG_WITH([default-path], [ --with-default-path= Specify default $PATH environment for server], [ if test "x$external_path_file" = "x/etc/login.conf" ; then AC_MSG_WARN([ --with-default-path=PATH has no effect on this system. Edit /etc/login.conf instead.]) elif test "x$withval" != "xno" ; then if test ! -z "$external_path_file" ; then AC_MSG_WARN([ --with-default-path=PATH will only be used if PATH is not defined in $external_path_file .]) fi user_path="$withval" SERVER_PATH_MSG="$withval" fi ], [ if test "x$external_path_file" = "x/etc/login.conf" ; then AC_MSG_WARN([Make sure the path to scp is in /etc/login.conf]) else if test ! -z "$external_path_file" ; then AC_MSG_WARN([ If PATH is defined in $external_path_file, ensure the path to scp is included, otherwise scp will not work.]) fi AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ /* find out what STDPATH is */ #include #ifdef HAVE_PATHS_H # include #endif #ifndef _PATH_STDPATH # ifdef _PATH_USERPATH /* Irix */ # define _PATH_STDPATH _PATH_USERPATH # else # define _PATH_STDPATH "/usr/bin:/bin:/usr/sbin:/sbin" # endif #endif #include #include #include #define DATA "conftest.stdpath" ]], [[ FILE *fd; int rc; fd = fopen(DATA,"w"); if(fd == NULL) exit(1); if ((rc = fprintf(fd,"%s", _PATH_STDPATH)) < 0) exit(1); exit(0); ]])], [ user_path=`cat conftest.stdpath` ], [ user_path="/usr/bin:/bin:/usr/sbin:/sbin" ], [ user_path="/usr/bin:/bin:/usr/sbin:/sbin" ] ) # make sure $bindir is in USER_PATH so scp will work t_bindir="${bindir}" while echo "${t_bindir}" | egrep '\$\{|NONE/' >/dev/null 2>&1; do t_bindir=`eval echo ${t_bindir}` case $t_bindir in NONE/*) t_bindir=`echo $t_bindir | sed "s~NONE~$prefix~"` ;; esac case $t_bindir in NONE/*) t_bindir=`echo $t_bindir | sed "s~NONE~$ac_default_prefix~"` ;; esac done echo $user_path | grep ":$t_bindir" > /dev/null 2>&1 if test $? -ne 0 ; then echo $user_path | grep "^$t_bindir" > /dev/null 2>&1 if test $? -ne 0 ; then user_path=$user_path:$t_bindir AC_MSG_RESULT([Adding $t_bindir to USER_PATH so scp will work]) fi fi fi ] ) if test "x$external_path_file" != "x/etc/login.conf" ; then AC_DEFINE_UNQUOTED([USER_PATH], ["$user_path"], [Specify default $PATH]) AC_SUBST([user_path]) fi # Set superuser path separately to user path AC_ARG_WITH([superuser-path], [ --with-superuser-path= Specify different path for super-user], [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then AC_DEFINE_UNQUOTED([SUPERUSER_PATH], ["$withval"], [Define if you want a different $PATH for the superuser]) superuser_path=$withval fi ] ) AC_MSG_CHECKING([if we need to convert IPv4 in IPv6-mapped addresses]) IPV4_IN6_HACK_MSG="no" AC_ARG_WITH(4in6, [ --with-4in6 Check for and convert IPv4 in IPv6 mapped addresses], [ if test "x$withval" != "xno" ; then AC_MSG_RESULT([yes]) AC_DEFINE([IPV4_IN_IPV6], [1], [Detect IPv4 in IPv6 mapped addresses and treat as IPv4]) IPV4_IN6_HACK_MSG="yes" else AC_MSG_RESULT([no]) fi ], [ if test "x$inet6_default_4in6" = "xyes"; then AC_MSG_RESULT([yes (default)]) AC_DEFINE([IPV4_IN_IPV6]) IPV4_IN6_HACK_MSG="yes" else AC_MSG_RESULT([no (default)]) fi ] ) # Whether to enable BSD auth support BSD_AUTH_MSG=no AC_ARG_WITH([bsd-auth], [ --with-bsd-auth Enable BSD auth support], [ if test "x$withval" != "xno" ; then AC_DEFINE([BSD_AUTH], [1], [Define if you have BSD auth support]) BSD_AUTH_MSG=yes fi ] ) # Where to place sshd.pid piddir=/var/run # make sure the directory exists if test ! -d $piddir ; then piddir=`eval echo ${sysconfdir}` case $piddir in NONE/*) piddir=`echo $piddir | sed "s~NONE~$ac_default_prefix~"` ;; esac fi AC_ARG_WITH([pid-dir], [ --with-pid-dir=PATH Specify location of ssh.pid file], [ if test -n "$withval" && test "x$withval" != "xno" && \ test "x${withval}" != "xyes"; then piddir=$withval if test ! -d $piddir ; then AC_MSG_WARN([** no $piddir directory on this system **]) fi fi ] ) AC_DEFINE_UNQUOTED([_PATH_SSH_PIDDIR], ["$piddir"], [Specify location of ssh.pid]) AC_SUBST([piddir]) dnl allow user to disable some login recording features AC_ARG_ENABLE([lastlog], [ --disable-lastlog disable use of lastlog even if detected [no]], [ if test "x$enableval" = "xno" ; then AC_DEFINE([DISABLE_LASTLOG]) fi ] ) AC_ARG_ENABLE([utmp], [ --disable-utmp disable use of utmp even if detected [no]], [ if test "x$enableval" = "xno" ; then AC_DEFINE([DISABLE_UTMP]) fi ] ) AC_ARG_ENABLE([utmpx], [ --disable-utmpx disable use of utmpx even if detected [no]], [ if test "x$enableval" = "xno" ; then AC_DEFINE([DISABLE_UTMPX], [1], [Define if you don't want to use utmpx]) fi ] ) AC_ARG_ENABLE([wtmp], [ --disable-wtmp disable use of wtmp even if detected [no]], [ if test "x$enableval" = "xno" ; then AC_DEFINE([DISABLE_WTMP]) fi ] ) AC_ARG_ENABLE([wtmpx], [ --disable-wtmpx disable use of wtmpx even if detected [no]], [ if test "x$enableval" = "xno" ; then AC_DEFINE([DISABLE_WTMPX], [1], [Define if you don't want to use wtmpx]) fi ] ) AC_ARG_ENABLE([libutil], [ --disable-libutil disable use of libutil (login() etc.) [no]], [ if test "x$enableval" = "xno" ; then AC_DEFINE([DISABLE_LOGIN]) fi ] ) AC_ARG_ENABLE([pututline], [ --disable-pututline disable use of pututline() etc. ([uw]tmp) [no]], [ if test "x$enableval" = "xno" ; then AC_DEFINE([DISABLE_PUTUTLINE], [1], [Define if you don't want to use pututline() etc. to write [uw]tmp]) fi ] ) AC_ARG_ENABLE([pututxline], [ --disable-pututxline disable use of pututxline() etc. ([uw]tmpx) [no]], [ if test "x$enableval" = "xno" ; then AC_DEFINE([DISABLE_PUTUTXLINE], [1], [Define if you don't want to use pututxline() etc. to write [uw]tmpx]) fi ] ) AC_ARG_WITH([lastlog], [ --with-lastlog=FILE|DIR specify lastlog location [common locations]], [ if test "x$withval" = "xno" ; then AC_DEFINE([DISABLE_LASTLOG]) elif test -n "$withval" && test "x${withval}" != "xyes"; then conf_lastlog_location=$withval fi ] ) dnl lastlog, [uw]tmpx? detection dnl NOTE: set the paths in the platform section to avoid the dnl need for command-line parameters dnl lastlog and [uw]tmp are subject to a file search if all else fails dnl lastlog detection dnl NOTE: the code itself will detect if lastlog is a directory AC_MSG_CHECKING([if your system defines LASTLOG_FILE]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #ifdef HAVE_LASTLOG_H # include #endif #ifdef HAVE_PATHS_H # include #endif #ifdef HAVE_LOGIN_H # include #endif ]], [[ char *lastlog = LASTLOG_FILE; ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) AC_MSG_CHECKING([if your system defines _PATH_LASTLOG]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #ifdef HAVE_LASTLOG_H # include #endif #ifdef HAVE_PATHS_H # include #endif ]], [[ char *lastlog = _PATH_LASTLOG; ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) system_lastlog_path=no ]) ]) if test -z "$conf_lastlog_location"; then if test x"$system_lastlog_path" = x"no" ; then for f in /var/log/lastlog /usr/adm/lastlog /var/adm/lastlog /etc/security/lastlog ; do if (test -d "$f" || test -f "$f") ; then conf_lastlog_location=$f fi done if test -z "$conf_lastlog_location"; then AC_MSG_WARN([** Cannot find lastlog **]) dnl Don't define DISABLE_LASTLOG - that means we don't try wtmp/wtmpx fi fi fi if test -n "$conf_lastlog_location"; then AC_DEFINE_UNQUOTED([CONF_LASTLOG_FILE], ["$conf_lastlog_location"], [Define if you want to specify the path to your lastlog file]) fi dnl utmp detection AC_MSG_CHECKING([if your system defines UTMP_FILE]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #ifdef HAVE_PATHS_H # include #endif ]], [[ char *utmp = UTMP_FILE; ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) system_utmp_path=no ]) if test -z "$conf_utmp_location"; then if test x"$system_utmp_path" = x"no" ; then for f in /etc/utmp /usr/adm/utmp /var/run/utmp; do if test -f $f ; then conf_utmp_location=$f fi done if test -z "$conf_utmp_location"; then AC_DEFINE([DISABLE_UTMP]) fi fi fi if test -n "$conf_utmp_location"; then AC_DEFINE_UNQUOTED([CONF_UTMP_FILE], ["$conf_utmp_location"], [Define if you want to specify the path to your utmp file]) fi dnl wtmp detection AC_MSG_CHECKING([if your system defines WTMP_FILE]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #ifdef HAVE_PATHS_H # include #endif ]], [[ char *wtmp = WTMP_FILE; ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) system_wtmp_path=no ]) if test -z "$conf_wtmp_location"; then if test x"$system_wtmp_path" = x"no" ; then for f in /usr/adm/wtmp /var/log/wtmp; do if test -f $f ; then conf_wtmp_location=$f fi done if test -z "$conf_wtmp_location"; then AC_DEFINE([DISABLE_WTMP]) fi fi fi if test -n "$conf_wtmp_location"; then AC_DEFINE_UNQUOTED([CONF_WTMP_FILE], ["$conf_wtmp_location"], [Define if you want to specify the path to your wtmp file]) fi dnl wtmpx detection AC_MSG_CHECKING([if your system defines WTMPX_FILE]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include #include #ifdef HAVE_UTMPX_H #include #endif #ifdef HAVE_PATHS_H # include #endif ]], [[ char *wtmpx = WTMPX_FILE; ]])], [ AC_MSG_RESULT([yes]) ], [ AC_MSG_RESULT([no]) system_wtmpx_path=no ]) if test -z "$conf_wtmpx_location"; then if test x"$system_wtmpx_path" = x"no" ; then AC_DEFINE([DISABLE_WTMPX]) fi else AC_DEFINE_UNQUOTED([CONF_WTMPX_FILE], ["$conf_wtmpx_location"], [Define if you want to specify the path to your wtmpx file]) fi if test ! -z "$blibpath" ; then LDFLAGS="$LDFLAGS $blibflags$blibpath" AC_MSG_WARN([Please check and edit blibpath in LDFLAGS in Makefile]) fi AC_CHECK_MEMBER([struct lastlog.ll_line], [], [ if test x$SKIP_DISABLE_LASTLOG_DEFINE != "xyes" ; then AC_DEFINE([DISABLE_LASTLOG]) fi ], [ #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_UTMP_H #include #endif #ifdef HAVE_UTMPX_H #include #endif #ifdef HAVE_LASTLOG_H #include #endif ]) AC_CHECK_MEMBER([struct utmp.ut_line], [], [ AC_DEFINE([DISABLE_UTMP]) AC_DEFINE([DISABLE_WTMP]) ], [ #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_UTMP_H #include #endif #ifdef HAVE_UTMPX_H #include #endif #ifdef HAVE_LASTLOG_H #include #endif ]) dnl Adding -Werror to CFLAGS early prevents configure tests from running. dnl Add now. CFLAGS="$CFLAGS $werror_flags" if test "x$ac_cv_func_getaddrinfo" != "xyes" ; then TEST_SSH_IPV6=no else TEST_SSH_IPV6=yes fi AC_CHECK_DECL([BROKEN_GETADDRINFO], [TEST_SSH_IPV6=no]) AC_SUBST([TEST_SSH_IPV6], [$TEST_SSH_IPV6]) AC_SUBST([TEST_SSH_UTF8], [$TEST_SSH_UTF8]) AC_SUBST([TEST_MALLOC_OPTIONS], [$TEST_MALLOC_OPTIONS]) AC_SUBST([UNSUPPORTED_ALGORITHMS], [$unsupported_algorithms]) AC_EXEEXT AC_CONFIG_FILES([Makefile buildpkg.sh opensshd.init openssh.xml \ openbsd-compat/Makefile openbsd-compat/regress/Makefile \ survey.sh]) AC_OUTPUT # Print summary of options # Someone please show me a better way :) A=`eval echo ${prefix}` ; A=`eval echo ${A}` B=`eval echo ${bindir}` ; B=`eval echo ${B}` C=`eval echo ${sbindir}` ; C=`eval echo ${C}` D=`eval echo ${sysconfdir}` ; D=`eval echo ${D}` E=`eval echo ${libexecdir}/ssh-askpass` ; E=`eval echo ${E}` F=`eval echo ${mandir}/${mansubdir}X` ; F=`eval echo ${F}` G=`eval echo ${piddir}` ; G=`eval echo ${G}` H=`eval echo ${PRIVSEP_PATH}` ; H=`eval echo ${H}` I=`eval echo ${user_path}` ; I=`eval echo ${I}` J=`eval echo ${superuser_path}` ; J=`eval echo ${J}` echo "" echo "OpenSSH has been configured with the following options:" echo " User binaries: $B" echo " System binaries: $C" echo " Configuration files: $D" echo " Askpass program: $E" echo " Manual pages: $F" echo " PID file: $G" echo " Privilege separation chroot path: $H" if test "x$external_path_file" = "x/etc/login.conf" ; then echo " At runtime, sshd will use the path defined in $external_path_file" echo " Make sure the path to scp is present, otherwise scp will not work" else echo " sshd default user PATH: $I" if test ! -z "$external_path_file"; then echo " (If PATH is set in $external_path_file it will be used instead. If" echo " used, ensure the path to scp is present, otherwise scp will not work.)" fi fi if test ! -z "$superuser_path" ; then echo " sshd superuser user PATH: $J" fi echo " Manpage format: $MANTYPE" echo " PAM support: $PAM_MSG" echo " OSF SIA support: $SIA_MSG" echo " KerberosV support: $KRB5_MSG" echo " SELinux support: $SELINUX_MSG" echo " Smartcard support: $SCARD_MSG" echo " S/KEY support: $SKEY_MSG" echo " TCP Wrappers support: $TCPW_MSG" echo " MD5 password support: $MD5_MSG" echo " libedit support: $LIBEDIT_MSG" +echo " libldns support: $LDNS_MSG" echo " Solaris process contract support: $SPC_MSG" echo " Solaris project support: $SP_MSG" echo " Solaris privilege support: $SPP_MSG" echo " IP address in \$DISPLAY hack: $DISPLAY_HACK_MSG" echo " Translate v4 in v6 hack: $IPV4_IN6_HACK_MSG" echo " BSD Auth support: $BSD_AUTH_MSG" echo " Random number source: $RAND_MSG" echo " Privsep sandbox style: $SANDBOX_STYLE" echo "" echo " Host: ${host}" echo " Compiler: ${CC}" echo " Compiler flags: ${CFLAGS}" echo "Preprocessor flags: ${CPPFLAGS}" echo " Linker flags: ${LDFLAGS}" echo " Libraries: ${LIBS}" if test ! -z "${SSHDLIBS}"; then echo " +for sshd: ${SSHDLIBS}" fi if test ! -z "${SSHLIBS}"; then echo " +for ssh: ${SSHLIBS}" fi echo "" if test "x$MAKE_PACKAGE_SUPPORTED" = "xyes" ; then echo "SVR4 style packages are supported with \"make package\"" echo "" fi if test "x$PAM_MSG" = "xyes" ; then echo "PAM is enabled. You may need to install a PAM control file " echo "for sshd, otherwise password authentication may fail. " echo "Example PAM control files can be found in the contrib/ " echo "subdirectory" echo "" fi if test ! -z "$NO_PEERCHECK" ; then echo "WARNING: the operating system that you are using does not" echo "appear to support getpeereid(), getpeerucred() or the" echo "SO_PEERCRED getsockopt() option. These facilities are used to" echo "enforce security checks to prevent unauthorised connections to" echo "ssh-agent. Their absence increases the risk that a malicious" echo "user can connect to your agent." echo "" fi if test "$AUDIT_MODULE" = "bsm" ; then echo "WARNING: BSM audit support is currently considered EXPERIMENTAL." echo "See the Solaris section in README.platform for details." fi diff --git a/crypto/openssh/contrib/cygwin/ssh-host-config b/crypto/openssh/contrib/cygwin/ssh-host-config index d934d09b5430..db6aaa08a032 100644 --- a/crypto/openssh/contrib/cygwin/ssh-host-config +++ b/crypto/openssh/contrib/cygwin/ssh-host-config @@ -1,718 +1,693 @@ #!/bin/bash # # ssh-host-config, Copyright 2000-2014 Red Hat Inc. # # This file is part of the Cygwin port of OpenSSH. # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. # IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR # THE USE OR OTHER DEALINGS IN THE SOFTWARE. # ====================================================================== # Initialization # ====================================================================== CSIH_SCRIPT=/usr/share/csih/cygwin-service-installation-helper.sh # List of apps used. This is checkad for existance in csih_sanity_check # Don't use *any* transient commands before sourcing the csih helper script, # otherwise the sanity checks are short-circuited. declare -a csih_required_commands=( /usr/bin/basename coreutils /usr/bin/cat coreutils /usr/bin/chmod coreutils /usr/bin/dirname coreutils /usr/bin/id coreutils /usr/bin/mv coreutils /usr/bin/rm coreutils /usr/bin/cygpath cygwin /usr/bin/mkpasswd cygwin /usr/bin/mount cygwin /usr/bin/ps cygwin /usr/bin/umount cygwin /usr/bin/cmp diffutils /usr/bin/grep grep /usr/bin/awk gawk /usr/bin/ssh-keygen openssh /usr/sbin/sshd openssh /usr/bin/sed sed ) csih_sanity_check_server=yes source ${CSIH_SCRIPT} PROGNAME=$(/usr/bin/basename $0) _tdir=$(/usr/bin/dirname $0) PROGDIR=$(cd $_tdir && pwd) # Subdirectory where the new package is being installed PREFIX=/usr # Directory where the config files are stored SYSCONFDIR=/etc LOCALSTATEDIR=/var sshd_config_configured=no port_number=22 service_name=sshd strictmodes=yes -privsep_used=yes cygwin_value="" user_account= password_value= opt_force=no # ====================================================================== # Routine: update_services_file # ====================================================================== update_services_file() { local _my_etcdir="/ssh-host-config.$$" local _win_etcdir local _services local _spaces local _serv_tmp local _wservices local ret=0 _win_etcdir="${SYSTEMROOT}\\system32\\drivers\\etc" _services="${_my_etcdir}/services" _spaces=" #" _serv_tmp="${_my_etcdir}/srv.out.$$" /usr/bin/mount -o text,posix=0,noacl -f "${_win_etcdir}" "${_my_etcdir}" # Depends on the above mount _wservices=`cygpath -w "${_services}"` # Add ssh 22/tcp and ssh 22/udp to services if [ `/usr/bin/grep -q 'ssh[[:space:]][[:space:]]*22' "${_services}"; echo $?` -ne 0 ] then if /usr/bin/awk '{ if ( $2 ~ /^23\/tcp/ ) print "ssh 22/tcp'"${_spaces}"'SSH Remote Login Protocol\nssh 22/udp'"${_spaces}"'SSH Remote Login Protocol"; print $0; }' < "${_services}" > "${_serv_tmp}" then if /usr/bin/mv "${_serv_tmp}" "${_services}" then csih_inform "Added ssh to ${_wservices}" else csih_warning "Adding ssh to ${_wservices} failed!" let ++ret fi /usr/bin/rm -f "${_serv_tmp}" else csih_warning "Adding ssh to ${_wservices} failed!" let ++ret fi fi /usr/bin/umount "${_my_etcdir}" return $ret } # --- End of update_services_file --- # # ====================================================================== # Routine: sshd_strictmodes # MODIFIES: strictmodes # ====================================================================== sshd_strictmodes() { if [ "${sshd_config_configured}" != "yes" ] then echo csih_inform "StrictModes is set to 'yes' by default." csih_inform "This is the recommended setting, but it requires that the POSIX" csih_inform "permissions of the user's home directory, the user's .ssh" csih_inform "directory, and the user's ssh key files are tight so that" csih_inform "only the user has write permissions." csih_inform "On the other hand, StrictModes don't work well with default" csih_inform "Windows permissions of a home directory mounted with the" csih_inform "'noacl' option, and they don't work at all if the home" csih_inform "directory is on a FAT or FAT32 partition." if ! csih_request "Should StrictModes be used?" then strictmodes=no fi fi return 0 } # ====================================================================== # Routine: sshd_privsep -# MODIFIES: privsep_used +# Try to create ssshd user account # ====================================================================== sshd_privsep() { local ret=0 if [ "${sshd_config_configured}" != "yes" ] then - echo - csih_inform "Privilege separation is set to 'sandbox' by default since" - csih_inform "OpenSSH 6.1. This is unsupported by Cygwin and has to be set" - csih_inform "to 'yes' or 'no'." - csih_inform "However, using privilege separation requires a non-privileged account" - csih_inform "called 'sshd'." - csih_inform "For more info on privilege separation read /usr/share/doc/openssh/README.privsep." - if csih_request "Should privilege separation be used?" + if ! csih_create_unprivileged_user sshd then - privsep_used=yes - if ! csih_create_unprivileged_user sshd - then - csih_error_recoverable "Couldn't create user 'sshd'!" - csih_error_recoverable "Privilege separation set to 'no' again!" - csih_error_recoverable "Check your ${SYSCONFDIR}/sshd_config file!" - let ++ret - privsep_used=no - fi - else - privsep_used=no + csih_error_recoverable "Could not create user 'sshd'!" + csih_error_recoverable "You will not be able to run an sshd service" + csih_error_recoverable "under a privileged account successfully." + csih_error_recoverable "Make sure to create a non-privileged user 'sshd'" + csih_error_recoverable "manually before trying to run the service!" + let ++ret fi fi return $ret } # --- End of sshd_privsep --- # # ====================================================================== # Routine: sshd_config_tweak # ====================================================================== sshd_config_tweak() { local ret=0 # Modify sshd_config csih_inform "Updating ${SYSCONFDIR}/sshd_config file" if [ "${port_number}" -ne 22 ] then /usr/bin/sed -i -e "s/^#\?[[:space:]]*Port[[:space:]].*/Port ${port_number}/" \ ${SYSCONFDIR}/sshd_config if [ $? -ne 0 ] then csih_warning "Setting listening port to ${port_number} failed!" csih_warning "Check your ${SYSCONFDIR}/sshd_config file!" let ++ret fi fi if [ "${strictmodes}" = "no" ] then /usr/bin/sed -i -e "s/^#\?[[:space:]]*StrictModes[[:space:]].*/StrictModes no/" \ ${SYSCONFDIR}/sshd_config if [ $? -ne 0 ] then csih_warning "Setting StrictModes to 'no' failed!" csih_warning "Check your ${SYSCONFDIR}/sshd_config file!" let ++ret fi fi - if [ "${sshd_config_configured}" != "yes" ] - then - /usr/bin/sed -i -e " - s/^#\?UsePrivilegeSeparation .*/UsePrivilegeSeparation ${privsep_used}/" \ - ${SYSCONFDIR}/sshd_config - if [ $? -ne 0 ] - then - csih_warning "Setting privilege separation failed!" - csih_warning "Check your ${SYSCONFDIR}/sshd_config file!" - let ++ret - fi - fi return $ret } # --- End of sshd_config_tweak --- # # ====================================================================== # Routine: update_inetd_conf # ====================================================================== update_inetd_conf() { local _inetcnf="${SYSCONFDIR}/inetd.conf" local _inetcnf_tmp="${SYSCONFDIR}/inetd.conf.$$" local _inetcnf_dir="${SYSCONFDIR}/inetd.d" local _sshd_inetd_conf="${_inetcnf_dir}/sshd-inetd" local _sshd_inetd_conf_tmp="${_inetcnf_dir}/sshd-inetd.$$" local _with_comment=1 local ret=0 if [ -d "${_inetcnf_dir}" ] then # we have inetutils-1.5 inetd.d support if [ -f "${_inetcnf}" ] then /usr/bin/grep -q '^[[:space:]]*ssh' "${_inetcnf}" && _with_comment=0 # check for sshd OR ssh in top-level inetd.conf file, and remove # will be replaced by a file in inetd.d/ if [ $(/usr/bin/grep -q '^[# \t]*ssh' "${_inetcnf}"; echo $?) -eq 0 ] then /usr/bin/grep -v '^[# \t]*ssh' "${_inetcnf}" >> "${_inetcnf_tmp}" if [ -f "${_inetcnf_tmp}" ] then if /usr/bin/mv "${_inetcnf_tmp}" "${_inetcnf}" then csih_inform "Removed ssh[d] from ${_inetcnf}" else csih_warning "Removing ssh[d] from ${_inetcnf} failed!" let ++ret fi /usr/bin/rm -f "${_inetcnf_tmp}" else csih_warning "Removing ssh[d] from ${_inetcnf} failed!" let ++ret fi fi fi csih_install_config "${_sshd_inetd_conf}" "${SYSCONFDIR}/defaults" if /usr/bin/cmp "${SYSCONFDIR}/defaults${_sshd_inetd_conf}" "${_sshd_inetd_conf}" >/dev/null 2>&1 then if [ "${_with_comment}" -eq 0 ] then /usr/bin/sed -e 's/@COMMENT@[[:space:]]*//' < "${_sshd_inetd_conf}" > "${_sshd_inetd_conf_tmp}" else /usr/bin/sed -e 's/@COMMENT@[[:space:]]*/# /' < "${_sshd_inetd_conf}" > "${_sshd_inetd_conf_tmp}" fi if /usr/bin/mv "${_sshd_inetd_conf_tmp}" "${_sshd_inetd_conf}" then csih_inform "Updated ${_sshd_inetd_conf}" else csih_warning "Updating ${_sshd_inetd_conf} failed!" let ++ret fi fi elif [ -f "${_inetcnf}" ] then /usr/bin/grep -q '^[[:space:]]*sshd' "${_inetcnf}" && _with_comment=0 # check for sshd in top-level inetd.conf file, and remove # will be replaced by a file in inetd.d/ if [ `/usr/bin/grep -q '^#\?[[:space:]]*sshd' "${_inetcnf}"; echo $?` -eq 0 ] then /usr/bin/grep -v '^#\?[[:space:]]*sshd' "${_inetcnf}" >> "${_inetcnf_tmp}" if [ -f "${_inetcnf_tmp}" ] then if /usr/bin/mv "${_inetcnf_tmp}" "${_inetcnf}" then csih_inform "Removed sshd from ${_inetcnf}" else csih_warning "Removing sshd from ${_inetcnf} failed!" let ++ret fi /usr/bin/rm -f "${_inetcnf_tmp}" else csih_warning "Removing sshd from ${_inetcnf} failed!" let ++ret fi fi # Add ssh line to inetd.conf if [ `/usr/bin/grep -q '^[# \t]*ssh' "${_inetcnf}"; echo $?` -ne 0 ] then if [ "${_with_comment}" -eq 0 ] then echo 'ssh stream tcp nowait root /usr/sbin/sshd sshd -i' >> "${_inetcnf}" else echo '# ssh stream tcp nowait root /usr/sbin/sshd sshd -i' >> "${_inetcnf}" fi if [ $? -eq 0 ] then csih_inform "Added ssh to ${_inetcnf}" else csih_warning "Adding ssh to ${_inetcnf} failed!" let ++ret fi fi fi return $ret } # --- End of update_inetd_conf --- # # ====================================================================== # Routine: check_service_files_ownership # Checks that the files in /etc and /var belong to the right owner # ====================================================================== check_service_files_ownership() { local run_service_as=$1 local ret=0 if [ -z "${run_service_as}" ] then accnt_name=$(/usr/bin/cygrunsrv -VQ sshd | /usr/bin/sed -ne 's/^Account *: *//gp') if [ "${accnt_name}" = "LocalSystem" ] then # Convert "LocalSystem" to "SYSTEM" as is the correct account name run_service_as="SYSTEM" else dom="${accnt_name%%\\*}" accnt_name="${accnt_name#*\\}" if [ "${dom}" = '.' ] then # Check local account run_service_as=$(/usr/bin/mkpasswd -l -u "${accnt_name}" | /usr/bin/awk -F: '{print $1;}') else # Check domain run_service_as=$(/usr/bin/mkpasswd -d "${dom}" -u "${accnt_name}" | /usr/bin/awk -F: '{print $1;}') fi fi if [ -z "${run_service_as}" ] then csih_warning "Couldn't determine name of user running sshd service from account database!" csih_warning "As a result, this script cannot make sure that the files used" csih_warning "by the sshd service belong to the user running the service." return 1 fi fi for i in "${SYSCONFDIR}"/ssh_config "${SYSCONFDIR}"/sshd_config "${SYSCONFDIR}"/ssh_host_*key "${SYSCONFDIR}"/ssh_host_*key.pub do if [ -f "$i" ] then if ! chown "${run_service_as}".544 "$i" >/dev/null 2>&1 then csih_warning "Couldn't change owner of $i!" let ++ret fi fi done if ! chown "${run_service_as}".544 ${LOCALSTATEDIR}/empty >/dev/null 2>&1 then csih_warning "Couldn't change owner of ${LOCALSTATEDIR}/empty!" let ++ret fi if ! chown "${run_service_as}".544 ${LOCALSTATEDIR}/log/lastlog >/dev/null 2>&1 then csih_warning "Couldn't change owner of ${LOCALSTATEDIR}/log/lastlog!" let ++ret fi if [ -f ${LOCALSTATEDIR}/log/sshd.log ] then if ! chown "${run_service_as}".544 ${LOCALSTATEDIR}/log/sshd.log >/dev/null 2>&1 then csih_warning "Couldn't change owner of ${LOCALSTATEDIR}/log/sshd.log!" let ++ret fi fi if [ $ret -ne 0 ] then csih_warning "Couldn't change owner of important files to ${run_service_as}!" csih_warning "This may cause the sshd service to fail! Please make sure that" csih_warning "you have suufficient permissions to change the ownership of files" csih_warning "and try to run the ssh-host-config script again." fi return $ret } # --- End of check_service_files_ownership --- # # ====================================================================== # Routine: install_service # Install sshd as a service # ====================================================================== install_service() { local run_service_as local password local ret=0 echo if /usr/bin/cygrunsrv -Q ${service_name} >/dev/null 2>&1 then csih_inform "Sshd service is already installed." check_service_files_ownership "" || let ret+=$? else echo -e "${_csih_QUERY_STR} Do you want to install sshd as a service?" if csih_request "(Say \"no\" if it is already installed as a service)" then csih_get_cygenv "${cygwin_value}" if ( csih_is_nt2003 || [ "$csih_FORCE_PRIVILEGED_USER" = "yes" ] ) then csih_inform "On Windows Server 2003, Windows Vista, and above, the" csih_inform "SYSTEM account cannot setuid to other users -- a capability" csih_inform "sshd requires. You need to have or to create a privileged" csih_inform "account. This script will help you do so." echo [ "${opt_force}" = "yes" ] && opt_f=-f [ -n "${user_account}" ] && opt_u="-u ""${user_account}""" csih_select_privileged_username ${opt_f} ${opt_u} sshd if ! csih_create_privileged_user "${password_value}" then csih_error_recoverable "There was a serious problem creating a privileged user." csih_request "Do you want to proceed anyway?" || exit 1 let ++ret fi fi # Never returns empty if NT or above run_service_as=$(csih_service_should_run_as) if [ "${run_service_as}" = "${csih_PRIVILEGED_USERNAME}" ] then password="${csih_PRIVILEGED_PASSWORD}" if [ -z "${password}" ] then csih_get_value "Please enter the password for user '${run_service_as}':" "-s" password="${csih_value}" fi fi # At this point, we either have $run_service_as = "system" and # $password is empty, or $run_service_as is some privileged user and # (hopefully) $password contains the correct password. So, from here # out, we use '-z "${password}"' to discriminate the two cases. csih_check_user "${run_service_as}" if [ -n "${csih_cygenv}" ] then cygwin_env=( -e "CYGWIN=${csih_cygenv}" ) fi if [ -z "${password}" ] then if /usr/bin/cygrunsrv -I ${service_name} -d "CYGWIN ${service_name}" -p /usr/sbin/sshd \ -a "-D" -y tcpip "${cygwin_env[@]}" then echo csih_inform "The sshd service has been installed under the LocalSystem" csih_inform "account (also known as SYSTEM). To start the service now, call" csih_inform "\`net start sshd' or \`cygrunsrv -S sshd'. Otherwise, it" csih_inform "will start automatically after the next reboot." fi else if /usr/bin/cygrunsrv -I ${service_name} -d "CYGWIN ${service_name}" -p /usr/sbin/sshd \ -a "-D" -y tcpip "${cygwin_env[@]}" \ -u "${run_service_as}" -w "${password}" then /usr/bin/editrights -u "${run_service_as}" -a SeServiceLogonRight echo csih_inform "The sshd service has been installed under the '${run_service_as}'" csih_inform "account. To start the service now, call \`net start ${service_name}' or" csih_inform "\`cygrunsrv -S ${service_name}'. Otherwise, it will start automatically" csih_inform "after the next reboot." fi fi if /usr/bin/cygrunsrv -Q ${service_name} >/dev/null 2>&1 then check_service_files_ownership "${run_service_as}" || let ret+=$? else csih_error_recoverable "Installing sshd as a service failed!" let ++ret fi fi # user allowed us to install as service fi # service not yet installed return $ret } # --- End of install_service --- # # ====================================================================== # Main Entry Point # ====================================================================== # Check how the script has been started. If # (1) it has been started by giving the full path and # that path is /etc/postinstall, OR # (2) Otherwise, if the environment variable # SSH_HOST_CONFIG_AUTO_ANSWER_NO is set # then set auto_answer to "no". This allows automatic # creation of the config files in /etc w/o overwriting # them if they already exist. In both cases, color # escape sequences are suppressed, so as to prevent # cluttering setup's logfiles. if [ "$PROGDIR" = "/etc/postinstall" ] then csih_auto_answer="no" csih_disable_color opt_force=yes fi if [ -n "${SSH_HOST_CONFIG_AUTO_ANSWER_NO}" ] then csih_auto_answer="no" csih_disable_color opt_force=yes fi # ====================================================================== # Parse options # ====================================================================== while : do case $# in 0) break ;; esac option=$1 shift case "${option}" in -d | --debug ) set -x csih_trace_on ;; -y | --yes ) csih_auto_answer=yes opt_force=yes ;; -n | --no ) csih_auto_answer=no opt_force=yes ;; -c | --cygwin ) cygwin_value="$1" shift ;; -N | --name ) service_name=$1 shift ;; -p | --port ) port_number=$1 shift ;; -u | --user ) user_account="$1" shift ;; -w | --pwd ) password_value="$1" shift ;; --privileged ) csih_FORCE_PRIVILEGED_USER=yes ;; *) echo "usage: ${progname} [OPTION]..." echo echo "This script creates an OpenSSH host configuration." echo echo "Options:" echo " --debug -d Enable shell's debug output." echo " --yes -y Answer all questions with \"yes\" automatically." echo " --no -n Answer all questions with \"no\" automatically." echo " --cygwin -c Use \"options\" as value for CYGWIN environment var." echo " --name -N sshd windows service name." echo " --port -p sshd listens on port n." echo " --user -u privileged user for service, default 'cyg_server'." echo " --pwd -w Use \"pwd\" as password for privileged user." echo " --privileged On Windows XP, require privileged user" echo " instead of LocalSystem for sshd service." echo exit 1 ;; esac done # ====================================================================== # Action! # ====================================================================== # Check for running ssh/sshd processes first. Refuse to do anything while # some ssh processes are still running if /usr/bin/ps -ef | /usr/bin/grep -q '/sshd\?$' then echo csih_error "There are still ssh processes running. Please shut them down first." fi # Make sure the user is running in an administrative context admin=$(/usr/bin/id -G | /usr/bin/grep -Eq '\<544\>' && echo yes || echo no) if [ "${admin}" != "yes" ] then echo csih_warning "Running this script typically requires administrator privileges!" csih_warning "However, it seems your account does not have these privileges." csih_warning "Here's the list of groups in your user token:" echo /usr/bin/id -Gnz | xargs -0n1 echo " " echo csih_warning "This usually means you're running this script from a non-admin" csih_warning "desktop session, or in a non-elevated shell under UAC control." echo csih_warning "Make sure you have the appropriate privileges right now," csih_warning "otherwise parts of this script will probably fail!" echo echo -e "${_csih_QUERY_STR} Are you sure you want to continue? (Say \"no\" if you're not sure" if ! csih_request "you have the required privileges)" then echo csih_inform "Ok. Exiting. Make sure to switch to an administrative account" csih_inform "or to start this script from an elevated shell." exit 1 fi fi echo warning_cnt=0 # Create /var/log/lastlog if not already exists if [ -e ${LOCALSTATEDIR}/log/lastlog -a ! -f ${LOCALSTATEDIR}/log/lastlog ] then echo csih_error_multi "${LOCALSTATEDIR}/log/lastlog exists, but is not a file." \ "Cannot create ssh host configuration." fi if [ ! -e ${LOCALSTATEDIR}/log/lastlog ] then /usr/bin/cat /dev/null > ${LOCALSTATEDIR}/log/lastlog if ! /usr/bin/chmod 644 ${LOCALSTATEDIR}/log/lastlog >/dev/null 2>&1 then csih_warning "Can't set permissions on ${LOCALSTATEDIR}/log/lastlog!" let ++warning_cnt fi fi # Create /var/empty file used as chroot jail for privilege separation csih_make_dir "${LOCALSTATEDIR}/empty" "Cannot create ${LOCALSTATEDIR}/empty directory." if ! /usr/bin/chmod 755 "${LOCALSTATEDIR}/empty" >/dev/null 2>&1 then csih_warning "Can't set permissions on ${LOCALSTATEDIR}/empty!" let ++warning_cnt fi # generate missing host keys csih_inform "Generating missing SSH host keys" /usr/bin/ssh-keygen -A || let warning_cnt+=$? # handle ssh_config csih_install_config "${SYSCONFDIR}/ssh_config" "${SYSCONFDIR}/defaults" || let ++warning_cnt if /usr/bin/cmp "${SYSCONFDIR}/ssh_config" "${SYSCONFDIR}/defaults/${SYSCONFDIR}/ssh_config" >/dev/null 2>&1 then if [ "${port_number}" != "22" ] then csih_inform "Updating ${SYSCONFDIR}/ssh_config file with requested port" echo "Host localhost" >> ${SYSCONFDIR}/ssh_config echo " Port ${port_number}" >> ${SYSCONFDIR}/ssh_config fi fi -# handle sshd_config (and privsep) +# handle sshd_config csih_install_config "${SYSCONFDIR}/sshd_config" "${SYSCONFDIR}/defaults" || let ++warning_cnt if ! /usr/bin/cmp "${SYSCONFDIR}/sshd_config" "${SYSCONFDIR}/defaults/${SYSCONFDIR}/sshd_config" >/dev/null 2>&1 then sshd_config_configured=yes fi sshd_strictmodes || let warning_cnt+=$? sshd_privsep || let warning_cnt+=$? sshd_config_tweak || let warning_cnt+=$? update_services_file || let warning_cnt+=$? update_inetd_conf || let warning_cnt+=$? install_service || let warning_cnt+=$? echo if [ $warning_cnt -eq 0 ] then csih_inform "Host configuration finished. Have fun!" else csih_warning "Host configuration exited with ${warning_cnt} errors or warnings!" csih_warning "Make sure that all problems reported are fixed," csih_warning "then re-run ssh-host-config." fi exit $warning_cnt diff --git a/crypto/openssh/contrib/redhat/openssh.spec b/crypto/openssh/contrib/redhat/openssh.spec index 666097c5e619..7de45457a742 100644 --- a/crypto/openssh/contrib/redhat/openssh.spec +++ b/crypto/openssh/contrib/redhat/openssh.spec @@ -1,808 +1,808 @@ -%define ver 7.4p1 +%define ver 7.5p1 %define rel 1 # OpenSSH privilege separation requires a user & group ID %define sshd_uid 74 %define sshd_gid 74 # Version of ssh-askpass %define aversion 1.2.4.1 # Do we want to disable building of x11-askpass? (1=yes 0=no) %define no_x11_askpass 0 # Do we want to disable building of gnome-askpass? (1=yes 0=no) %define no_gnome_askpass 0 # Do we want to link against a static libcrypto? (1=yes 0=no) %define static_libcrypto 0 # Do we want smartcard support (1=yes 0=no) %define scard 0 # Use GTK2 instead of GNOME in gnome-ssh-askpass %define gtk2 1 # Is this build for RHL 6.x? %define build6x 0 # Do we want kerberos5 support (1=yes 0=no) %define kerberos5 1 # Reserve options to override askpass settings with: # rpm -ba|--rebuild --define 'skip_xxx 1' %{?skip_x11_askpass:%define no_x11_askpass 1} %{?skip_gnome_askpass:%define no_gnome_askpass 1} # Add option to build without GTK2 for older platforms with only GTK+. # RedHat <= 7.2 and Red Hat Advanced Server 2.1 are examples. # rpm -ba|--rebuild --define 'no_gtk2 1' %{?no_gtk2:%define gtk2 0} # Is this a build for RHL 6.x or earlier? %{?build_6x:%define build6x 1} # If this is RHL 6.x, the default configuration has sysconfdir in /usr/etc. %if %{build6x} %define _sysconfdir /etc %endif # Options for static OpenSSL link: # rpm -ba|--rebuild --define "static_openssl 1" %{?static_openssl:%define static_libcrypto 1} # Options for Smartcard support: (needs libsectok and openssl-engine) # rpm -ba|--rebuild --define "smartcard 1" %{?smartcard:%define scard 1} # Is this a build for the rescue CD (without PAM, with MD5)? (1=yes 0=no) %define rescue 0 %{?build_rescue:%define rescue 1} # Turn off some stuff for resuce builds %if %{rescue} %define kerberos5 0 %endif Summary: The OpenSSH implementation of SSH protocol versions 1 and 2. Name: openssh Version: %{ver} %if %{rescue} Release: %{rel}rescue %else Release: %{rel} %endif URL: https://www.openssh.com/portable.html Source0: https://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-%{version}.tar.gz %if ! %{no_x11_askpass} Source1: http://www.jmknoble.net/software/x11-ssh-askpass/x11-ssh-askpass-%{aversion}.tar.gz %endif License: BSD Group: Applications/Internet BuildRoot: %{_tmppath}/%{name}-%{version}-buildroot Obsoletes: ssh %if %{build6x} PreReq: initscripts >= 5.00 %else Requires: initscripts >= 5.20 %endif BuildRequires: perl, openssl-devel BuildRequires: /bin/login %if ! %{build6x} BuildRequires: glibc-devel, pam %else BuildRequires: /usr/include/security/pam_appl.h %endif %if ! %{no_x11_askpass} BuildRequires: /usr/include/X11/Xlib.h %endif %if ! %{no_gnome_askpass} BuildRequires: pkgconfig %endif %if %{kerberos5} BuildRequires: krb5-devel BuildRequires: krb5-libs %endif %package clients Summary: OpenSSH clients. Requires: openssh = %{version}-%{release} Group: Applications/Internet Obsoletes: ssh-clients %package server Summary: The OpenSSH server daemon. Group: System Environment/Daemons Obsoletes: ssh-server Requires: openssh = %{version}-%{release}, chkconfig >= 0.9 %if ! %{build6x} Requires: /etc/pam.d/system-auth %endif %package askpass Summary: A passphrase dialog for OpenSSH and X. Group: Applications/Internet Requires: openssh = %{version}-%{release} Obsoletes: ssh-extras %package askpass-gnome Summary: A passphrase dialog for OpenSSH, X, and GNOME. Group: Applications/Internet Requires: openssh = %{version}-%{release} Obsoletes: ssh-extras %description SSH (Secure SHell) is a program for logging into and executing commands on a remote machine. SSH is intended to replace rlogin and rsh, and to provide secure encrypted communications between two untrusted hosts over an insecure network. X11 connections and arbitrary TCP/IP ports can also be forwarded over the secure channel. OpenSSH is OpenBSD's version of the last free version of SSH, bringing it up to date in terms of security and features, as well as removing all patented algorithms to separate libraries. This package includes the core files necessary for both the OpenSSH client and server. To make this package useful, you should also install openssh-clients, openssh-server, or both. %description clients OpenSSH is a free version of SSH (Secure SHell), a program for logging into and executing commands on a remote machine. This package includes the clients necessary to make encrypted connections to SSH servers. You'll also need to install the openssh package on OpenSSH clients. %description server OpenSSH is a free version of SSH (Secure SHell), a program for logging into and executing commands on a remote machine. This package contains the secure shell daemon (sshd). The sshd daemon allows SSH clients to securely connect to your SSH server. You also need to have the openssh package installed. %description askpass OpenSSH is a free version of SSH (Secure SHell), a program for logging into and executing commands on a remote machine. This package contains an X11 passphrase dialog for OpenSSH. %description askpass-gnome OpenSSH is a free version of SSH (Secure SHell), a program for logging into and executing commands on a remote machine. This package contains an X11 passphrase dialog for OpenSSH and the GNOME GUI desktop environment. %prep %if ! %{no_x11_askpass} %setup -q -a 1 %else %setup -q %endif %build %if %{rescue} CFLAGS="$RPM_OPT_FLAGS -Os"; export CFLAGS %endif %if %{kerberos5} K5DIR=`rpm -ql krb5-devel | grep 'include/krb5\.h' | sed 's,\/include\/krb5.h,,'` echo K5DIR=$K5DIR %endif %configure \ --sysconfdir=%{_sysconfdir}/ssh \ --libexecdir=%{_libexecdir}/openssh \ --datadir=%{_datadir}/openssh \ --with-default-path=/usr/local/bin:/bin:/usr/bin \ --with-superuser-path=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin \ --with-privsep-path=%{_var}/empty/sshd \ --with-md5-passwords \ %if %{scard} --with-smartcard \ %endif %if %{rescue} --without-pam \ %else --with-pam \ %endif %if %{kerberos5} --with-kerberos5=$K5DIR \ %endif %if %{static_libcrypto} perl -pi -e "s|-lcrypto|%{_libdir}/libcrypto.a|g" Makefile %endif make %if ! %{no_x11_askpass} pushd x11-ssh-askpass-%{aversion} %configure --libexecdir=%{_libexecdir}/openssh xmkmf -a make popd %endif # Define a variable to toggle gnome1/gtk2 building. This is necessary # because RPM doesn't handle nested %if statements. %if %{gtk2} gtk2=yes %else gtk2=no %endif %if ! %{no_gnome_askpass} pushd contrib if [ $gtk2 = yes ] ; then make gnome-ssh-askpass2 mv gnome-ssh-askpass2 gnome-ssh-askpass else make gnome-ssh-askpass1 mv gnome-ssh-askpass1 gnome-ssh-askpass fi popd %endif %install rm -rf $RPM_BUILD_ROOT mkdir -p -m755 $RPM_BUILD_ROOT%{_sysconfdir}/ssh mkdir -p -m755 $RPM_BUILD_ROOT%{_libexecdir}/openssh mkdir -p -m755 $RPM_BUILD_ROOT%{_var}/empty/sshd make install DESTDIR=$RPM_BUILD_ROOT install -d $RPM_BUILD_ROOT/etc/pam.d/ install -d $RPM_BUILD_ROOT/etc/rc.d/init.d install -d $RPM_BUILD_ROOT%{_libexecdir}/openssh %if %{build6x} install -m644 contrib/redhat/sshd.pam.old $RPM_BUILD_ROOT/etc/pam.d/sshd %else install -m644 contrib/redhat/sshd.pam $RPM_BUILD_ROOT/etc/pam.d/sshd %endif install -m755 contrib/redhat/sshd.init $RPM_BUILD_ROOT/etc/rc.d/init.d/sshd %if ! %{no_x11_askpass} install -s x11-ssh-askpass-%{aversion}/x11-ssh-askpass $RPM_BUILD_ROOT%{_libexecdir}/openssh/x11-ssh-askpass ln -s x11-ssh-askpass $RPM_BUILD_ROOT%{_libexecdir}/openssh/ssh-askpass %endif %if ! %{no_gnome_askpass} install -s contrib/gnome-ssh-askpass $RPM_BUILD_ROOT%{_libexecdir}/openssh/gnome-ssh-askpass %endif %if ! %{scard} rm -f $RPM_BUILD_ROOT/usr/share/openssh/Ssh.bin %endif %if ! %{no_gnome_askpass} install -m 755 -d $RPM_BUILD_ROOT%{_sysconfdir}/profile.d/ install -m 755 contrib/redhat/gnome-ssh-askpass.csh $RPM_BUILD_ROOT%{_sysconfdir}/profile.d/ install -m 755 contrib/redhat/gnome-ssh-askpass.sh $RPM_BUILD_ROOT%{_sysconfdir}/profile.d/ %endif perl -pi -e "s|$RPM_BUILD_ROOT||g" $RPM_BUILD_ROOT%{_mandir}/man*/* %clean rm -rf $RPM_BUILD_ROOT %triggerun server -- ssh-server if [ "$1" != 0 -a -r /var/run/sshd.pid ] ; then touch /var/run/sshd.restart fi %triggerun server -- openssh-server < 2.5.0p1 # Count the number of HostKey and HostDsaKey statements we have. gawk 'BEGIN {IGNORECASE=1} /^hostkey/ || /^hostdsakey/ {sawhostkey = sawhostkey + 1} END {exit sawhostkey}' /etc/ssh/sshd_config # And if we only found one, we know the client was relying on the old default # behavior, which loaded the the SSH2 DSA host key when HostDsaKey wasn't # specified. Now that HostKey is used for both SSH1 and SSH2 keys, specifying # one nullifies the default, which would have loaded both. if [ $? -eq 1 ] ; then echo HostKey /etc/ssh/ssh_host_rsa_key >> /etc/ssh/sshd_config echo HostKey /etc/ssh/ssh_host_dsa_key >> /etc/ssh/sshd_config fi %triggerpostun server -- ssh-server if [ "$1" != 0 ] ; then /sbin/chkconfig --add sshd if test -f /var/run/sshd.restart ; then rm -f /var/run/sshd.restart /sbin/service sshd start > /dev/null 2>&1 || : fi fi %pre server %{_sbindir}/groupadd -r -g %{sshd_gid} sshd 2>/dev/null || : %{_sbindir}/useradd -d /var/empty/sshd -s /bin/false -u %{sshd_uid} \ -g sshd -M -r sshd 2>/dev/null || : %post server /sbin/chkconfig --add sshd %postun server /sbin/service sshd condrestart > /dev/null 2>&1 || : %preun server if [ "$1" = 0 ] then /sbin/service sshd stop > /dev/null 2>&1 || : /sbin/chkconfig --del sshd fi %files %defattr(-,root,root) %doc CREDITS ChangeLog INSTALL LICENCE OVERVIEW README* PROTOCOL* TODO %attr(0755,root,root) %{_bindir}/scp %attr(0644,root,root) %{_mandir}/man1/scp.1* %attr(0755,root,root) %dir %{_sysconfdir}/ssh %attr(0600,root,root) %config(noreplace) %{_sysconfdir}/ssh/moduli %if ! %{rescue} %attr(0755,root,root) %{_bindir}/ssh-keygen %attr(0644,root,root) %{_mandir}/man1/ssh-keygen.1* %attr(0755,root,root) %dir %{_libexecdir}/openssh %attr(4711,root,root) %{_libexecdir}/openssh/ssh-keysign %attr(0755,root,root) %{_libexecdir}/openssh/ssh-pkcs11-helper %attr(0644,root,root) %{_mandir}/man8/ssh-keysign.8* %attr(0644,root,root) %{_mandir}/man8/ssh-pkcs11-helper.8* %endif %if %{scard} %attr(0755,root,root) %dir %{_datadir}/openssh %attr(0644,root,root) %{_datadir}/openssh/Ssh.bin %endif %files clients %defattr(-,root,root) %attr(0755,root,root) %{_bindir}/ssh %attr(0644,root,root) %{_mandir}/man1/ssh.1* %attr(0644,root,root) %{_mandir}/man5/ssh_config.5* %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/ssh/ssh_config %if ! %{rescue} %attr(2755,root,nobody) %{_bindir}/ssh-agent %attr(0755,root,root) %{_bindir}/ssh-add %attr(0755,root,root) %{_bindir}/ssh-keyscan %attr(0755,root,root) %{_bindir}/sftp %attr(0644,root,root) %{_mandir}/man1/ssh-agent.1* %attr(0644,root,root) %{_mandir}/man1/ssh-add.1* %attr(0644,root,root) %{_mandir}/man1/ssh-keyscan.1* %attr(0644,root,root) %{_mandir}/man1/sftp.1* %endif %if ! %{rescue} %files server %defattr(-,root,root) %dir %attr(0111,root,root) %{_var}/empty/sshd %attr(0755,root,root) %{_sbindir}/sshd %attr(0755,root,root) %{_libexecdir}/openssh/sftp-server %attr(0644,root,root) %{_mandir}/man8/sshd.8* %attr(0644,root,root) %{_mandir}/man5/moduli.5* %attr(0644,root,root) %{_mandir}/man5/sshd_config.5* %attr(0644,root,root) %{_mandir}/man8/sftp-server.8* %attr(0755,root,root) %dir %{_sysconfdir}/ssh %attr(0600,root,root) %config(noreplace) %{_sysconfdir}/ssh/sshd_config %attr(0600,root,root) %config(noreplace) /etc/pam.d/sshd %attr(0755,root,root) %config /etc/rc.d/init.d/sshd %endif %if ! %{no_x11_askpass} %files askpass %defattr(-,root,root) %doc x11-ssh-askpass-%{aversion}/README %doc x11-ssh-askpass-%{aversion}/ChangeLog %doc x11-ssh-askpass-%{aversion}/SshAskpass*.ad %attr(0755,root,root) %{_libexecdir}/openssh/ssh-askpass %attr(0755,root,root) %{_libexecdir}/openssh/x11-ssh-askpass %endif %if ! %{no_gnome_askpass} %files askpass-gnome %defattr(-,root,root) %attr(0755,root,root) %config %{_sysconfdir}/profile.d/gnome-ssh-askpass.* %attr(0755,root,root) %{_libexecdir}/openssh/gnome-ssh-askpass %endif %changelog * Wed Jul 14 2010 Tim Rice - test for skip_x11_askpass (line 77) should have been for no_x11_askpass * Mon Jun 2 2003 Damien Miller - Remove noip6 option. This may be controlled at run-time in client config file using new AddressFamily directive * Mon May 12 2003 Damien Miller - Don't install profile.d scripts when not building with GNOME/GTK askpass (patch from bet@rahul.net) * Wed Oct 01 2002 Damien Miller - Install ssh-agent setgid nobody to prevent ptrace() key theft attacks * Mon Sep 30 2002 Damien Miller - Use contrib/ Makefile for building askpass programs * Fri Jun 21 2002 Damien Miller - Merge in spec changes from seba@iq.pl (Sebastian Pachuta) - Add new {ssh,sshd}_config.5 manpages - Add new ssh-keysign program and remove setuid from ssh client * Fri May 10 2002 Damien Miller - Merge in spec changes from RedHat, reorgansie a little - Add Privsep user, group and directory * Thu Mar 7 2002 Nalin Dahyabhai 3.1p1-2 - bump and grind (through the build system) * Thu Mar 7 2002 Nalin Dahyabhai 3.1p1-1 - require sharutils for building (mindrot #137) - require db1-devel only when building for 6.x (#55105), which probably won't work anyway (3.1 requires OpenSSL 0.9.6 to build), but what the heck - require pam-devel by file (not by package name) again - add Markus's patch to compile with OpenSSL 0.9.5a (from http://bugzilla.mindrot.org/show_bug.cgi?id=141) and apply it if we're building for 6.x * Thu Mar 7 2002 Nalin Dahyabhai 3.1p1-0 - update to 3.1p1 * Tue Mar 5 2002 Nalin Dahyabhai SNAP-20020305 - update to SNAP-20020305 - drop debug patch, fixed upstream * Wed Feb 20 2002 Nalin Dahyabhai SNAP-20020220 - update to SNAP-20020220 for testing purposes (you've been warned, if there's anything to be warned about, gss patches won't apply, I don't mind) * Wed Feb 13 2002 Nalin Dahyabhai 3.0.2p1-3 - add patches from Simon Wilkinson and Nicolas Williams for GSSAPI key exchange, authentication, and named key support * Wed Jan 23 2002 Nalin Dahyabhai 3.0.2p1-2 - remove dependency on db1-devel, which has just been swallowed up whole by gnome-libs-devel * Sun Dec 29 2001 Nalin Dahyabhai - adjust build dependencies so that build6x actually works right (fix from Hugo van der Kooij) * Tue Dec 4 2001 Nalin Dahyabhai 3.0.2p1-1 - update to 3.0.2p1 * Fri Nov 16 2001 Nalin Dahyabhai 3.0.1p1-1 - update to 3.0.1p1 * Tue Nov 13 2001 Nalin Dahyabhai - update to current CVS (not for use in distribution) * Thu Nov 8 2001 Nalin Dahyabhai 3.0p1-1 - merge some of Damien Miller changes from the upstream 3.0p1 spec file and init script * Wed Nov 7 2001 Nalin Dahyabhai - update to 3.0p1 - update to x11-ssh-askpass 1.2.4.1 - change build dependency on a file from pam-devel to the pam-devel package - replace primes with moduli * Thu Sep 27 2001 Nalin Dahyabhai 2.9p2-9 - incorporate fix from Markus Friedl's advisory for IP-based authorization bugs * Thu Sep 13 2001 Bernhard Rosenkraenzer 2.9p2-8 - Merge changes to rescue build from current sysadmin survival cd * Thu Sep 6 2001 Nalin Dahyabhai 2.9p2-7 - fix scp's server's reporting of file sizes, and build with the proper preprocessor define to get large-file capable open(), stat(), etc. (sftp has been doing this correctly all along) (#51827) - configure without --with-ipv4-default on RHL 7.x and newer (#45987,#52247) - pull cvs patch to fix support for /etc/nologin for non-PAM logins (#47298) - mark profile.d scriptlets as config files (#42337) - refer to Jason Stone's mail for zsh workaround for exit-hanging quasi-bug - change a couple of log() statements to debug() statements (#50751) - pull cvs patch to add -t flag to sshd (#28611) - clear fd_sets correctly (one bit per FD, not one byte per FD) (#43221) * Mon Aug 20 2001 Nalin Dahyabhai 2.9p2-6 - add db1-devel as a BuildPrerequisite (noted by Hans Ecke) * Thu Aug 16 2001 Nalin Dahyabhai - pull cvs patch to fix remote port forwarding with protocol 2 * Thu Aug 9 2001 Nalin Dahyabhai - pull cvs patch to add session initialization to no-pty sessions - pull cvs patch to not cut off challengeresponse auth needlessly - refuse to do X11 forwarding if xauth isn't there, handy if you enable it by default on a system that doesn't have X installed (#49263) * Wed Aug 8 2001 Nalin Dahyabhai - don't apply patches to code we don't intend to build (spotted by Matt Galgoci) * Mon Aug 6 2001 Nalin Dahyabhai - pass OPTIONS correctly to initlog (#50151) * Wed Jul 25 2001 Nalin Dahyabhai - switch to x11-ssh-askpass 1.2.2 * Wed Jul 11 2001 Nalin Dahyabhai - rebuild in new environment * Mon Jun 25 2001 Nalin Dahyabhai - disable the gssapi patch * Mon Jun 18 2001 Nalin Dahyabhai - update to 2.9p2 - refresh to a new version of the gssapi patch * Thu Jun 7 2001 Nalin Dahyabhai - change Copyright: BSD to License: BSD - add Markus Friedl's unverified patch for the cookie file deletion problem so that we can verify it - drop patch to check if xauth is present (was folded into cookie patch) - don't apply gssapi patches for the errata candidate - clear supplemental groups list at startup * Fri May 25 2001 Nalin Dahyabhai - fix an error parsing the new default sshd_config - add a fix from Markus Friedl (via openssh-unix-dev) for ssh-keygen not dealing with comments right * Thu May 24 2001 Nalin Dahyabhai - add in Simon Wilkinson's GSSAPI patch to give it some testing in-house, to be removed before the next beta cycle because it's a big departure from the upstream version * Thu May 3 2001 Nalin Dahyabhai - finish marking strings in the init script for translation - modify init script to source /etc/sysconfig/sshd and pass $OPTIONS to sshd at startup (change merged from openssh.com init script, originally by Pekka Savola) - refuse to do X11 forwarding if xauth isn't there, handy if you enable it by default on a system that doesn't have X installed * Wed May 2 2001 Nalin Dahyabhai - update to 2.9 - drop various patches that came from or went upstream or to or from CVS * Wed Apr 18 2001 Nalin Dahyabhai - only require initscripts 5.00 on 6.2 (reported by Peter Bieringer) * Sun Apr 8 2001 Preston Brown - remove explicit openssl requirement, fixes builddistro issue - make initscript stop() function wait until sshd really dead to avoid races in condrestart * Mon Apr 2 2001 Nalin Dahyabhai - mention that challengereponse supports PAM, so disabling password doesn't limit users to pubkey and rsa auth (#34378) - bypass the daemon() function in the init script and call initlog directly, because daemon() won't start a daemon it detects is already running (like open connections) - require the version of openssl we had when we were built * Fri Mar 23 2001 Nalin Dahyabhai - make do_pam_setcred() smart enough to know when to establish creds and when to reinitialize them - add in a couple of other fixes from Damien for inclusion in the errata * Thu Mar 22 2001 Nalin Dahyabhai - update to 2.5.2p2 - call setcred() again after initgroups, because the "creds" could actually be group memberships * Tue Mar 20 2001 Nalin Dahyabhai - update to 2.5.2p1 (includes endianness fixes in the rijndael implementation) - don't enable challenge-response by default until we find a way to not have too many userauth requests (we may make up to six pubkey and up to three password attempts as it is) - remove build dependency on rsh to match openssh.com's packages more closely * Sat Mar 3 2001 Nalin Dahyabhai - remove dependency on openssl -- would need to be too precise * Fri Mar 2 2001 Nalin Dahyabhai - rebuild in new environment * Mon Feb 26 2001 Nalin Dahyabhai - Revert the patch to move pam_open_session. - Init script and spec file changes from Pekka Savola. (#28750) - Patch sftp to recognize '-o protocol' arguments. (#29540) * Thu Feb 22 2001 Nalin Dahyabhai - Chuck the closing patch. - Add a trigger to add host keys for protocol 2 to the config file, now that configuration file syntax requires us to specify it with HostKey if we specify any other HostKey values, which we do. * Tue Feb 20 2001 Nalin Dahyabhai - Redo patch to move pam_open_session after the server setuid()s to the user. - Rework the nopam patch to use be picked up by autoconf. * Mon Feb 19 2001 Nalin Dahyabhai - Update for 2.5.1p1. - Add init script mods from Pekka Savola. - Tweak the init script to match the CVS contrib script more closely. - Redo patch to ssh-add to try to adding both identity and id_dsa to also try adding id_rsa. * Fri Feb 16 2001 Nalin Dahyabhai - Update for 2.5.0p1. - Use $RPM_OPT_FLAGS instead of -O when building gnome-ssh-askpass - Resync with parts of Damien Miller's openssh.spec from CVS, including update of x11 askpass to 1.2.0. - Only require openssl (don't prereq) because we generate keys in the init script now. * Tue Feb 13 2001 Nalin Dahyabhai - Don't open a PAM session until we've forked and become the user (#25690). - Apply Andrew Bartlett's patch for letting pam_authenticate() know which host the user is attempting a login from. - Resync with parts of Damien Miller's openssh.spec from CVS. - Don't expose KbdInt responses in debug messages (from CVS). - Detect and handle errors in rsa_{public,private}_decrypt (from CVS). * Wed Feb 7 2001 Trond Eivind Glomsrxd - i18n-tweak to initscript. * Tue Jan 23 2001 Nalin Dahyabhai - More gettextizing. - Close all files after going into daemon mode (needs more testing). - Extract patch from CVS to handle auth banners (in the client). - Extract patch from CVS to handle compat weirdness. * Fri Jan 19 2001 Nalin Dahyabhai - Finish with the gettextizing. * Thu Jan 18 2001 Nalin Dahyabhai - Fix a bug in auth2-pam.c (#23877) - Gettextize the init script. * Wed Dec 20 2000 Nalin Dahyabhai - Incorporate a switch for using PAM configs for 6.x, just in case. * Tue Dec 5 2000 Nalin Dahyabhai - Incorporate Bero's changes for a build specifically for rescue CDs. * Wed Nov 29 2000 Nalin Dahyabhai - Don't treat pam_setcred() failure as fatal unless pam_authenticate() has succeeded, to allow public-key authentication after a failure with "none" authentication. (#21268) * Tue Nov 28 2000 Nalin Dahyabhai - Update to x11-askpass 1.1.1. (#21301) - Don't second-guess fixpaths, which causes paths to get fixed twice. (#21290) * Mon Nov 27 2000 Nalin Dahyabhai - Merge multiple PAM text messages into subsequent prompts when possible when doing keyboard-interactive authentication. * Sun Nov 26 2000 Nalin Dahyabhai - Disable the built-in MD5 password support. We're using PAM. - Take a crack at doing keyboard-interactive authentication with PAM, and enable use of it in the default client configuration so that the client will try it when the server disallows password authentication. - Build with debugging flags. Build root policies strip all binaries anyway. * Tue Nov 21 2000 Nalin Dahyabhai - Use DESTDIR instead of %%makeinstall. - Remove /usr/X11R6/bin from the path-fixing patch. * Mon Nov 20 2000 Nalin Dahyabhai - Add the primes file from the latest snapshot to the main package (#20884). - Add the dev package to the prereq list (#19984). - Remove the default path and mimic login's behavior in the server itself. * Fri Nov 17 2000 Nalin Dahyabhai - Resync with conditional options in Damien Miller's .spec file for an errata. - Change libexecdir from %%{_libexecdir}/ssh to %%{_libexecdir}/openssh. * Tue Nov 7 2000 Nalin Dahyabhai - Update to OpenSSH 2.3.0p1. - Update to x11-askpass 1.1.0. - Enable keyboard-interactive authentication. * Mon Oct 30 2000 Nalin Dahyabhai - Update to ssh-askpass-x11 1.0.3. - Change authentication related messages to be private (#19966). * Tue Oct 10 2000 Nalin Dahyabhai - Patch ssh-keygen to be able to list signatures for DSA public key files it generates. * Thu Oct 5 2000 Nalin Dahyabhai - Add BuildRequires on /usr/include/security/pam_appl.h to be sure we always build PAM authentication in. - Try setting SSH_ASKPASS if gnome-ssh-askpass is installed. - Clean out no-longer-used patches. - Patch ssh-add to try to add both identity and id_dsa, and to error only when neither exists. * Mon Oct 2 2000 Nalin Dahyabhai - Update x11-askpass to 1.0.2. (#17835) - Add BuildRequiress for /bin/login and /usr/bin/rsh so that configure will always find them in the right place. (#17909) - Set the default path to be the same as the one supplied by /bin/login, but add /usr/X11R6/bin. (#17909) - Try to handle obsoletion of ssh-server more cleanly. Package names are different, but init script name isn't. (#17865) * Wed Sep 6 2000 Nalin Dahyabhai - Update to 2.2.0p1. (#17835) - Tweak the init script to allow proper restarting. (#18023) * Wed Aug 23 2000 Nalin Dahyabhai - Update to 20000823 snapshot. - Change subpackage requirements from %%{version} to %%{version}-%%{release} - Back out the pipe patch. * Mon Jul 17 2000 Nalin Dahyabhai - Update to 2.1.1p4, which includes fixes for config file parsing problems. - Move the init script back. - Add Damien's quick fix for wackiness. * Wed Jul 12 2000 Nalin Dahyabhai - Update to 2.1.1p3, which includes fixes for X11 forwarding and strtok(). * Thu Jul 6 2000 Nalin Dahyabhai - Move condrestart to server postun. - Move key generation to init script. - Actually use the right patch for moving the key generation to the init script. - Clean up the init script a bit. * Wed Jul 5 2000 Nalin Dahyabhai - Fix X11 forwarding, from mail post by Chan Shih-Ping Richard. * Sun Jul 2 2000 Nalin Dahyabhai - Update to 2.1.1p2. - Use of strtok() considered harmful. * Sat Jul 1 2000 Nalin Dahyabhai - Get the build root out of the man pages. * Thu Jun 29 2000 Nalin Dahyabhai - Add and use condrestart support in the init script. - Add newer initscripts as a prereq. * Tue Jun 27 2000 Nalin Dahyabhai - Build in new environment (release 2) - Move -clients subpackage to Applications/Internet group * Fri Jun 9 2000 Nalin Dahyabhai - Update to 2.2.1p1 * Sat Jun 3 2000 Nalin Dahyabhai - Patch to build with neither RSA nor RSAref. - Miscellaneous FHS-compliance tweaks. - Fix for possibly-compressed man pages. * Wed Mar 15 2000 Damien Miller - Updated for new location - Updated for new gnome-ssh-askpass build * Sun Dec 26 1999 Damien Miller - Added Jim Knoble's askpass * Mon Nov 15 1999 Damien Miller - Split subpackages further based on patch from jim knoble * Sat Nov 13 1999 Damien Miller - Added 'Obsoletes' directives * Tue Nov 09 1999 Damien Miller - Use make install - Subpackages * Mon Nov 08 1999 Damien Miller - Added links for slogin - Fixed perms on manpages * Sat Oct 30 1999 Damien Miller - Renamed init script * Fri Oct 29 1999 Damien Miller - Back to old binary names * Thu Oct 28 1999 Damien Miller - Use autoconf - New binary names * Wed Oct 27 1999 Damien Miller - Initial RPMification, based on Jan "Yenya" Kasprzak's spec. diff --git a/crypto/openssh/contrib/suse/openssh.spec b/crypto/openssh/contrib/suse/openssh.spec index 4c4bbb69cb3e..e62be39d0aac 100644 --- a/crypto/openssh/contrib/suse/openssh.spec +++ b/crypto/openssh/contrib/suse/openssh.spec @@ -1,241 +1,241 @@ # Default values for additional components %define build_x11_askpass 1 # Define the UID/GID to use for privilege separation %define sshd_gid 65 %define sshd_uid 71 # The version of x11-ssh-askpass to use %define xversion 1.2.4.1 # Allow the ability to override defaults with -D skip_xxx=1 %{?skip_x11_askpass:%define build_x11_askpass 0} Summary: OpenSSH, a free Secure Shell (SSH) protocol implementation Name: openssh -Version: 7.4p1 +Version: 7.5p1 URL: https://www.openssh.com/ Release: 1 Source0: openssh-%{version}.tar.gz Source1: x11-ssh-askpass-%{xversion}.tar.gz License: BSD Group: Productivity/Networking/SSH BuildRoot: %{_tmppath}/openssh-%{version}-buildroot PreReq: openssl Obsoletes: ssh Provides: ssh # # (Build[ing] Prereq[uisites] only work for RPM 2.95 and newer.) # building prerequisites -- stuff for # OpenSSL (openssl-devel), # and Gnome (glibdev, gtkdev, and gnlibsd) # BuildPrereq: openssl BuildPrereq: zlib-devel #BuildPrereq: glibdev #BuildPrereq: gtkdev #BuildPrereq: gnlibsd %package askpass Summary: A passphrase dialog for OpenSSH and the X window System. Group: Productivity/Networking/SSH Requires: openssh = %{version} Obsoletes: ssh-extras Provides: openssh:${_libdir}/ssh/ssh-askpass %if %{build_x11_askpass} BuildPrereq: XFree86-devel %endif %description Ssh (Secure Shell) is a program for logging into a remote machine and for executing commands in a remote machine. It is intended to replace rlogin and rsh, and provide secure encrypted communications between two untrusted hosts over an insecure network. X11 connections and arbitrary TCP/IP ports can also be forwarded over the secure channel. OpenSSH is OpenBSD's rework of the last free version of SSH, bringing it up to date in terms of security and features, as well as removing all patented algorithms to seperate libraries (OpenSSL). This package includes all files necessary for both the OpenSSH client and server. %description askpass Ssh (Secure Shell) is a program for logging into a remote machine and for executing commands in a remote machine. It is intended to replace rlogin and rsh, and provide secure encrypted communications between two untrusted hosts over an insecure network. X11 connections and arbitrary TCP/IP ports can also be forwarded over the secure channel. OpenSSH is OpenBSD's rework of the last free version of SSH, bringing it up to date in terms of security and features, as well as removing all patented algorithms to seperate libraries (OpenSSL). This package contains an X Window System passphrase dialog for OpenSSH. %changelog * Wed Oct 26 2005 Iain Morgan - Removed accidental inclusion of --without-zlib-version-check * Tue Oct 25 2005 Iain Morgan - Overhaul to deal with newer versions of SuSE and OpenSSH * Mon Jun 12 2000 Damien Miller - Glob manpages to catch compressed files * Wed Mar 15 2000 Damien Miller - Updated for new location - Updated for new gnome-ssh-askpass build * Sun Dec 26 1999 Chris Saia - Made symlink to gnome-ssh-askpass called ssh-askpass * Wed Nov 24 1999 Chris Saia - Removed patches that included /etc/pam.d/sshd, /sbin/init.d/rc.sshd, and /var/adm/fillup-templates/rc.config.sshd, since Damien merged these into his released tarfile - Changed permissions on ssh_config in the install procedure to 644 from 600 even though it was correct in the %files section and thus right in the RPMs - Postinstall script for the server now only prints "Generating SSH host key..." if we need to actually do this, in order to eliminate a confusing message if an SSH host key is already in place - Marked all manual pages as %doc(umentation) * Mon Nov 22 1999 Chris Saia - Added flag to configure daemon with TCP Wrappers support - Added building prerequisites (works in RPM 3.0 and newer) * Thu Nov 18 1999 Chris Saia - Made this package correct for SuSE. - Changed instances of pam_pwdb.so to pam_unix.so, since it works more properly with SuSE, and lib_pwdb.so isn't installed by default. * Mon Nov 15 1999 Damien Miller - Split subpackages further based on patch from jim knoble * Sat Nov 13 1999 Damien Miller - Added 'Obsoletes' directives * Tue Nov 09 1999 Damien Miller - Use make install - Subpackages * Mon Nov 08 1999 Damien Miller - Added links for slogin - Fixed perms on manpages * Sat Oct 30 1999 Damien Miller - Renamed init script * Fri Oct 29 1999 Damien Miller - Back to old binary names * Thu Oct 28 1999 Damien Miller - Use autoconf - New binary names * Wed Oct 27 1999 Damien Miller - Initial RPMification, based on Jan "Yenya" Kasprzak's spec. %prep %if %{build_x11_askpass} %setup -q -a 1 %else %setup -q %endif %build CFLAGS="$RPM_OPT_FLAGS" \ %configure --prefix=/usr \ --sysconfdir=%{_sysconfdir}/ssh \ --mandir=%{_mandir} \ --with-privsep-path=/var/lib/empty \ --with-pam \ --libexecdir=%{_libdir}/ssh make %if %{build_x11_askpass} cd x11-ssh-askpass-%{xversion} %configure --mandir=/usr/X11R6/man \ --libexecdir=%{_libdir}/ssh xmkmf -a make cd .. %endif %install rm -rf $RPM_BUILD_ROOT make install DESTDIR=$RPM_BUILD_ROOT/ install -d $RPM_BUILD_ROOT/etc/pam.d/ install -d $RPM_BUILD_ROOT/etc/init.d/ install -d $RPM_BUILD_ROOT/var/adm/fillup-templates install -m644 contrib/sshd.pam.generic $RPM_BUILD_ROOT/etc/pam.d/sshd install -m744 contrib/suse/rc.sshd $RPM_BUILD_ROOT/etc/init.d/sshd install -m744 contrib/suse/sysconfig.ssh \ $RPM_BUILD_ROOT/var/adm/fillup-templates %if %{build_x11_askpass} cd x11-ssh-askpass-%{xversion} make install install.man BINDIR=%{_libdir}/ssh DESTDIR=$RPM_BUILD_ROOT/ rm -f $RPM_BUILD_ROOT/usr/share/Ssh.bin %endif %clean rm -rf $RPM_BUILD_ROOT %pre /usr/sbin/groupadd -g %{sshd_gid} -o -r sshd 2> /dev/null || : /usr/sbin/useradd -r -o -g sshd -u %{sshd_uid} -s /bin/false -c "SSH Privilege Separation User" -d /var/lib/sshd sshd 2> /dev/null || : %post /usr/bin/ssh-keygen -A %{fillup_and_insserv -n -y ssh sshd} %run_permissions %verifyscript %verify_permissions -e /etc/ssh/sshd_config -e /etc/ssh/ssh_config -e /usr/bin/ssh %preun %stop_on_removal sshd %postun %restart_on_update sshd %{insserv_cleanup} %files %defattr(-,root,root) %doc ChangeLog OVERVIEW README* PROTOCOL* %doc TODO CREDITS LICENCE %attr(0755,root,root) %dir %{_sysconfdir}/ssh %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/ssh/ssh_config %attr(0600,root,root) %config(noreplace) %{_sysconfdir}/ssh/sshd_config %attr(0600,root,root) %config(noreplace) %{_sysconfdir}/ssh/moduli %attr(0644,root,root) %config(noreplace) /etc/pam.d/sshd %attr(0755,root,root) %config /etc/init.d/sshd %attr(0755,root,root) %{_bindir}/ssh-keygen %attr(0755,root,root) %{_bindir}/scp %attr(0755,root,root) %{_bindir}/ssh %attr(0755,root,root) %{_bindir}/ssh-agent %attr(0755,root,root) %{_bindir}/ssh-add %attr(0755,root,root) %{_bindir}/ssh-keyscan %attr(0755,root,root) %{_bindir}/sftp %attr(0755,root,root) %{_sbindir}/sshd %attr(0755,root,root) %dir %{_libdir}/ssh %attr(0755,root,root) %{_libdir}/ssh/sftp-server %attr(4711,root,root) %{_libdir}/ssh/ssh-keysign %attr(0755,root,root) %{_libdir}/ssh/ssh-pkcs11-helper %attr(0644,root,root) %doc %{_mandir}/man1/scp.1* %attr(0644,root,root) %doc %{_mandir}/man1/sftp.1* %attr(0644,root,root) %doc %{_mandir}/man1/ssh.1* %attr(0644,root,root) %doc %{_mandir}/man1/ssh-add.1* %attr(0644,root,root) %doc %{_mandir}/man1/ssh-agent.1* %attr(0644,root,root) %doc %{_mandir}/man1/ssh-keygen.1* %attr(0644,root,root) %doc %{_mandir}/man1/ssh-keyscan.1* %attr(0644,root,root) %doc %{_mandir}/man5/moduli.5* %attr(0644,root,root) %doc %{_mandir}/man5/ssh_config.5* %attr(0644,root,root) %doc %{_mandir}/man5/sshd_config.5* %attr(0644,root,root) %doc %{_mandir}/man8/sftp-server.8* %attr(0644,root,root) %doc %{_mandir}/man8/ssh-keysign.8* %attr(0644,root,root) %doc %{_mandir}/man8/ssh-pkcs11-helper.8* %attr(0644,root,root) %doc %{_mandir}/man8/sshd.8* %attr(0644,root,root) /var/adm/fillup-templates/sysconfig.ssh %if %{build_x11_askpass} %files askpass %defattr(-,root,root) %doc x11-ssh-askpass-%{xversion}/README %doc x11-ssh-askpass-%{xversion}/ChangeLog %doc x11-ssh-askpass-%{xversion}/SshAskpass*.ad %attr(0755,root,root) %{_libdir}/ssh/ssh-askpass %attr(0755,root,root) %{_libdir}/ssh/x11-ssh-askpass %attr(0644,root,root) %doc /usr/X11R6/man/man1/ssh-askpass.1x* %attr(0644,root,root) %doc /usr/X11R6/man/man1/x11-ssh-askpass.1x* %attr(0644,root,root) %config /usr/X11R6/lib/X11/app-defaults/SshAskpass %endif diff --git a/crypto/openssh/digest-openssl.c b/crypto/openssh/digest-openssl.c index 13b63c2f006d..c55ceb93f9d4 100644 --- a/crypto/openssh/digest-openssl.c +++ b/crypto/openssh/digest-openssl.c @@ -1,205 +1,205 @@ -/* $OpenBSD: digest-openssl.c,v 1.5 2014/12/21 22:27:56 djm Exp $ */ +/* $OpenBSD: digest-openssl.c,v 1.6 2017/03/10 02:59:51 dtucker Exp $ */ /* * Copyright (c) 2013 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #ifdef WITH_OPENSSL #include #include #include #include #include #include "openbsd-compat/openssl-compat.h" #include "sshbuf.h" #include "digest.h" #include "ssherr.h" #ifndef HAVE_EVP_RIPEMD160 # define EVP_ripemd160 NULL #endif /* HAVE_EVP_RIPEMD160 */ #ifndef HAVE_EVP_SHA256 # define EVP_sha256 NULL # define EVP_sha384 NULL # define EVP_sha512 NULL #endif /* HAVE_EVP_SHA256 */ struct ssh_digest_ctx { int alg; EVP_MD_CTX mdctx; }; struct ssh_digest { int id; const char *name; size_t digest_len; const EVP_MD *(*mdfunc)(void); }; /* NB. Indexed directly by algorithm number */ const struct ssh_digest digests[] = { { SSH_DIGEST_MD5, "MD5", 16, EVP_md5 }, { SSH_DIGEST_RIPEMD160, "RIPEMD160", 20, EVP_ripemd160 }, { SSH_DIGEST_SHA1, "SHA1", 20, EVP_sha1 }, { SSH_DIGEST_SHA256, "SHA256", 32, EVP_sha256 }, { SSH_DIGEST_SHA384, "SHA384", 48, EVP_sha384 }, { SSH_DIGEST_SHA512, "SHA512", 64, EVP_sha512 }, { -1, NULL, 0, NULL }, }; static const struct ssh_digest * ssh_digest_by_alg(int alg) { if (alg < 0 || alg >= SSH_DIGEST_MAX) return NULL; if (digests[alg].id != alg) /* sanity */ return NULL; if (digests[alg].mdfunc == NULL) return NULL; return &(digests[alg]); } int ssh_digest_alg_by_name(const char *name) { int alg; for (alg = 0; digests[alg].id != -1; alg++) { if (strcasecmp(name, digests[alg].name) == 0) return digests[alg].id; } return -1; } const char * ssh_digest_alg_name(int alg) { const struct ssh_digest *digest = ssh_digest_by_alg(alg); return digest == NULL ? NULL : digest->name; } size_t ssh_digest_bytes(int alg) { const struct ssh_digest *digest = ssh_digest_by_alg(alg); return digest == NULL ? 0 : digest->digest_len; } size_t ssh_digest_blocksize(struct ssh_digest_ctx *ctx) { return EVP_MD_CTX_block_size(&ctx->mdctx); } struct ssh_digest_ctx * ssh_digest_start(int alg) { const struct ssh_digest *digest = ssh_digest_by_alg(alg); struct ssh_digest_ctx *ret; if (digest == NULL || ((ret = calloc(1, sizeof(*ret))) == NULL)) return NULL; ret->alg = alg; EVP_MD_CTX_init(&ret->mdctx); if (EVP_DigestInit_ex(&ret->mdctx, digest->mdfunc(), NULL) != 1) { free(ret); return NULL; } return ret; } int ssh_digest_copy_state(struct ssh_digest_ctx *from, struct ssh_digest_ctx *to) { if (from->alg != to->alg) return SSH_ERR_INVALID_ARGUMENT; /* we have bcopy-style order while openssl has memcpy-style */ if (!EVP_MD_CTX_copy_ex(&to->mdctx, &from->mdctx)) return SSH_ERR_LIBCRYPTO_ERROR; return 0; } int ssh_digest_update(struct ssh_digest_ctx *ctx, const void *m, size_t mlen) { if (EVP_DigestUpdate(&ctx->mdctx, m, mlen) != 1) return SSH_ERR_LIBCRYPTO_ERROR; return 0; } int ssh_digest_update_buffer(struct ssh_digest_ctx *ctx, const struct sshbuf *b) { return ssh_digest_update(ctx, sshbuf_ptr(b), sshbuf_len(b)); } int ssh_digest_final(struct ssh_digest_ctx *ctx, u_char *d, size_t dlen) { const struct ssh_digest *digest = ssh_digest_by_alg(ctx->alg); u_int l = dlen; - if (dlen > UINT_MAX) + if (digest == NULL || dlen > UINT_MAX) return SSH_ERR_INVALID_ARGUMENT; if (dlen < digest->digest_len) /* No truncation allowed */ return SSH_ERR_INVALID_ARGUMENT; if (EVP_DigestFinal_ex(&ctx->mdctx, d, &l) != 1) return SSH_ERR_LIBCRYPTO_ERROR; if (l != digest->digest_len) /* sanity */ return SSH_ERR_INTERNAL_ERROR; return 0; } void ssh_digest_free(struct ssh_digest_ctx *ctx) { if (ctx != NULL) { EVP_MD_CTX_cleanup(&ctx->mdctx); explicit_bzero(ctx, sizeof(*ctx)); free(ctx); } } int ssh_digest_memory(int alg, const void *m, size_t mlen, u_char *d, size_t dlen) { const struct ssh_digest *digest = ssh_digest_by_alg(alg); u_int mdlen; if (digest == NULL) return SSH_ERR_INVALID_ARGUMENT; if (dlen > UINT_MAX) return SSH_ERR_INVALID_ARGUMENT; if (dlen < digest->digest_len) return SSH_ERR_INVALID_ARGUMENT; mdlen = dlen; if (!EVP_Digest(m, mlen, d, &mdlen, digest->mdfunc(), NULL)) return SSH_ERR_LIBCRYPTO_ERROR; return 0; } int ssh_digest_buffer(int alg, const struct sshbuf *b, u_char *d, size_t dlen) { return ssh_digest_memory(alg, sshbuf_ptr(b), sshbuf_len(b), d, dlen); } #endif /* WITH_OPENSSL */ diff --git a/crypto/openssh/freebsd-configure.sh b/crypto/openssh/freebsd-configure.sh index e9420c7580c7..d2c63e170ffe 100755 --- a/crypto/openssh/freebsd-configure.sh +++ b/crypto/openssh/freebsd-configure.sh @@ -1,44 +1,44 @@ #!/bin/sh # # $FreeBSD$ # configure_args=" --prefix=/usr --sysconfdir=/etc/ssh --with-pam --with-ssl-dir=/usr --with-tcp-wrappers --with-libedit --with-ssl-engine --without-xauth -" +" set -e # make sure configure uses the correct compiler export CC=$(echo ".include " | make -f /dev/stdin -VCC) export CPP=$(echo ".include " | make -f /dev/stdin -VCPP) unset CFLAGS CPPFLAGS LDFLAGS LIBS # regenerate configure and config.h.in autoheader autoconf # reset PATH to avoid picking up the wrong libraries export PATH=/bin:/sbin:/usr/bin:/usr/sbin unset LD_LIBRARY_PATH # generate config.h with krb5 and stash it sh configure $configure_args --with-kerberos5=/usr mv config.log config.log.orig mv config.h config.h.orig # generate config.h without krb5 sh configure $configure_args --without-kerberos5 # extract the difference echo '/* $Free''BSD$ */' > krb5_config.h diff -u config.h.orig config.h | sed -n '/^-#define/s/^-//p' | grep -Ff /dev/stdin config.h.orig >> krb5_config.h diff --git a/crypto/openssh/hostfile.c b/crypto/openssh/hostfile.c index 4548fbab3d80..e23faa9696af 100644 --- a/crypto/openssh/hostfile.c +++ b/crypto/openssh/hostfile.c @@ -1,851 +1,856 @@ -/* $OpenBSD: hostfile.c,v 1.67 2016/09/17 18:00:27 tedu Exp $ */ +/* $OpenBSD: hostfile.c,v 1.68 2017/03/10 04:26:06 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Functions for manipulating the known hosts files. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * * Copyright (c) 1999, 2000 Markus Friedl. All rights reserved. * Copyright (c) 1999 Niels Provos. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #include #include "xmalloc.h" #include "match.h" #include "sshkey.h" #include "hostfile.h" #include "log.h" #include "misc.h" #include "ssherr.h" #include "digest.h" #include "hmac.h" struct hostkeys { struct hostkey_entry *entries; u_int num_entries; }; /* XXX hmac is too easy to dictionary attack; use bcrypt? */ static int extract_salt(const char *s, u_int l, u_char *salt, size_t salt_len) { char *p, *b64salt; u_int b64len; int ret; if (l < sizeof(HASH_MAGIC) - 1) { debug2("extract_salt: string too short"); return (-1); } if (strncmp(s, HASH_MAGIC, sizeof(HASH_MAGIC) - 1) != 0) { debug2("extract_salt: invalid magic identifier"); return (-1); } s += sizeof(HASH_MAGIC) - 1; l -= sizeof(HASH_MAGIC) - 1; if ((p = memchr(s, HASH_DELIM, l)) == NULL) { debug2("extract_salt: missing salt termination character"); return (-1); } b64len = p - s; /* Sanity check */ if (b64len == 0 || b64len > 1024) { debug2("extract_salt: bad encoded salt length %u", b64len); return (-1); } b64salt = xmalloc(1 + b64len); memcpy(b64salt, s, b64len); b64salt[b64len] = '\0'; ret = __b64_pton(b64salt, salt, salt_len); free(b64salt); if (ret == -1) { debug2("extract_salt: salt decode error"); return (-1); } if (ret != (int)ssh_hmac_bytes(SSH_DIGEST_SHA1)) { debug2("extract_salt: expected salt len %zd, got %d", ssh_hmac_bytes(SSH_DIGEST_SHA1), ret); return (-1); } return (0); } char * host_hash(const char *host, const char *name_from_hostfile, u_int src_len) { struct ssh_hmac_ctx *ctx; u_char salt[256], result[256]; char uu_salt[512], uu_result[512]; static char encoded[1024]; u_int len; len = ssh_digest_bytes(SSH_DIGEST_SHA1); if (name_from_hostfile == NULL) { /* Create new salt */ arc4random_buf(salt, len); } else { /* Extract salt from known host entry */ if (extract_salt(name_from_hostfile, src_len, salt, sizeof(salt)) == -1) return (NULL); } if ((ctx = ssh_hmac_start(SSH_DIGEST_SHA1)) == NULL || ssh_hmac_init(ctx, salt, len) < 0 || ssh_hmac_update(ctx, host, strlen(host)) < 0 || ssh_hmac_final(ctx, result, sizeof(result))) fatal("%s: ssh_hmac failed", __func__); ssh_hmac_free(ctx); if (__b64_ntop(salt, len, uu_salt, sizeof(uu_salt)) == -1 || __b64_ntop(result, len, uu_result, sizeof(uu_result)) == -1) fatal("%s: __b64_ntop failed", __func__); snprintf(encoded, sizeof(encoded), "%s%s%c%s", HASH_MAGIC, uu_salt, HASH_DELIM, uu_result); return (encoded); } /* * Parses an RSA (number of bits, e, n) or DSA key from a string. Moves the * pointer over the key. Skips any whitespace at the beginning and at end. */ int hostfile_read_key(char **cpp, u_int *bitsp, struct sshkey *ret) { char *cp; int r; /* Skip leading whitespace. */ for (cp = *cpp; *cp == ' ' || *cp == '\t'; cp++) ; if ((r = sshkey_read(ret, &cp)) != 0) return 0; /* Skip trailing whitespace. */ for (; *cp == ' ' || *cp == '\t'; cp++) ; /* Return results. */ *cpp = cp; if (bitsp != NULL) *bitsp = sshkey_size(ret); return 1; } static HostkeyMarker check_markers(char **cpp) { char marker[32], *sp, *cp = *cpp; int ret = MRK_NONE; while (*cp == '@') { /* Only one marker is allowed */ if (ret != MRK_NONE) return MRK_ERROR; /* Markers are terminated by whitespace */ if ((sp = strchr(cp, ' ')) == NULL && (sp = strchr(cp, '\t')) == NULL) return MRK_ERROR; /* Extract marker for comparison */ if (sp <= cp + 1 || sp >= cp + sizeof(marker)) return MRK_ERROR; memcpy(marker, cp, sp - cp); marker[sp - cp] = '\0'; if (strcmp(marker, CA_MARKER) == 0) ret = MRK_CA; else if (strcmp(marker, REVOKE_MARKER) == 0) ret = MRK_REVOKE; else return MRK_ERROR; /* Skip past marker and any whitespace that follows it */ cp = sp; for (; *cp == ' ' || *cp == '\t'; cp++) ; } *cpp = cp; return ret; } struct hostkeys * init_hostkeys(void) { struct hostkeys *ret = xcalloc(1, sizeof(*ret)); ret->entries = NULL; return ret; } struct load_callback_ctx { const char *host; u_long num_loaded; struct hostkeys *hostkeys; }; static int record_hostkey(struct hostkey_foreach_line *l, void *_ctx) { struct load_callback_ctx *ctx = (struct load_callback_ctx *)_ctx; struct hostkeys *hostkeys = ctx->hostkeys; struct hostkey_entry *tmp; if (l->status == HKF_STATUS_INVALID) { /* XXX make this verbose() in the future */ debug("%s:%ld: parse error in hostkeys file", l->path, l->linenum); return 0; } debug3("%s: found %skey type %s in file %s:%lu", __func__, l->marker == MRK_NONE ? "" : (l->marker == MRK_CA ? "ca " : "revoked "), sshkey_type(l->key), l->path, l->linenum); if ((tmp = reallocarray(hostkeys->entries, hostkeys->num_entries + 1, sizeof(*hostkeys->entries))) == NULL) return SSH_ERR_ALLOC_FAIL; hostkeys->entries = tmp; hostkeys->entries[hostkeys->num_entries].host = xstrdup(ctx->host); hostkeys->entries[hostkeys->num_entries].file = xstrdup(l->path); hostkeys->entries[hostkeys->num_entries].line = l->linenum; hostkeys->entries[hostkeys->num_entries].key = l->key; l->key = NULL; /* steal it */ hostkeys->entries[hostkeys->num_entries].marker = l->marker; hostkeys->num_entries++; ctx->num_loaded++; return 0; } void load_hostkeys(struct hostkeys *hostkeys, const char *host, const char *path) { int r; struct load_callback_ctx ctx; ctx.host = host; ctx.num_loaded = 0; ctx.hostkeys = hostkeys; if ((r = hostkeys_foreach(path, record_hostkey, &ctx, host, NULL, HKF_WANT_MATCH|HKF_WANT_PARSE_KEY)) != 0) { if (r != SSH_ERR_SYSTEM_ERROR && errno != ENOENT) debug("%s: hostkeys_foreach failed for %s: %s", __func__, path, ssh_err(r)); } if (ctx.num_loaded != 0) debug3("%s: loaded %lu keys from %s", __func__, ctx.num_loaded, host); } void free_hostkeys(struct hostkeys *hostkeys) { u_int i; for (i = 0; i < hostkeys->num_entries; i++) { free(hostkeys->entries[i].host); free(hostkeys->entries[i].file); sshkey_free(hostkeys->entries[i].key); explicit_bzero(hostkeys->entries + i, sizeof(*hostkeys->entries)); } free(hostkeys->entries); explicit_bzero(hostkeys, sizeof(*hostkeys)); free(hostkeys); } static int check_key_not_revoked(struct hostkeys *hostkeys, struct sshkey *k) { int is_cert = sshkey_is_cert(k); u_int i; for (i = 0; i < hostkeys->num_entries; i++) { if (hostkeys->entries[i].marker != MRK_REVOKE) continue; if (sshkey_equal_public(k, hostkeys->entries[i].key)) return -1; if (is_cert && sshkey_equal_public(k->cert->signature_key, hostkeys->entries[i].key)) return -1; } return 0; } /* * Match keys against a specified key, or look one up by key type. * * If looking for a keytype (key == NULL) and one is found then return * HOST_FOUND, otherwise HOST_NEW. * * If looking for a key (key != NULL): * 1. If the key is a cert and a matching CA is found, return HOST_OK * 2. If the key is not a cert and a matching key is found, return HOST_OK * 3. If no key matches but a key with a different type is found, then * return HOST_CHANGED * 4. If no matching keys are found, then return HOST_NEW. * * Finally, check any found key is not revoked. */ static HostStatus check_hostkeys_by_key_or_type(struct hostkeys *hostkeys, struct sshkey *k, int keytype, const struct hostkey_entry **found) { u_int i; HostStatus end_return = HOST_NEW; int want_cert = sshkey_is_cert(k); HostkeyMarker want_marker = want_cert ? MRK_CA : MRK_NONE; int proto = (k ? k->type : keytype) == KEY_RSA1 ? 1 : 2; if (found != NULL) *found = NULL; for (i = 0; i < hostkeys->num_entries; i++) { if (proto == 1 && hostkeys->entries[i].key->type != KEY_RSA1) continue; if (proto == 2 && hostkeys->entries[i].key->type == KEY_RSA1) continue; if (hostkeys->entries[i].marker != want_marker) continue; if (k == NULL) { if (hostkeys->entries[i].key->type != keytype) continue; end_return = HOST_FOUND; if (found != NULL) *found = hostkeys->entries + i; k = hostkeys->entries[i].key; break; } if (want_cert) { if (sshkey_equal_public(k->cert->signature_key, hostkeys->entries[i].key)) { /* A matching CA exists */ end_return = HOST_OK; if (found != NULL) *found = hostkeys->entries + i; break; } } else { if (sshkey_equal(k, hostkeys->entries[i].key)) { end_return = HOST_OK; if (found != NULL) *found = hostkeys->entries + i; break; } /* A non-maching key exists */ end_return = HOST_CHANGED; if (found != NULL) *found = hostkeys->entries + i; } } if (check_key_not_revoked(hostkeys, k) != 0) { end_return = HOST_REVOKED; if (found != NULL) *found = NULL; } return end_return; } HostStatus check_key_in_hostkeys(struct hostkeys *hostkeys, struct sshkey *key, const struct hostkey_entry **found) { if (key == NULL) fatal("no key to look up"); return check_hostkeys_by_key_or_type(hostkeys, key, 0, found); } int lookup_key_in_hostkeys_by_type(struct hostkeys *hostkeys, int keytype, const struct hostkey_entry **found) { return (check_hostkeys_by_key_or_type(hostkeys, NULL, keytype, found) == HOST_FOUND); } static int write_host_entry(FILE *f, const char *host, const char *ip, const struct sshkey *key, int store_hash) { int r, success = 0; - char *hashed_host = NULL; + char *hashed_host = NULL, *lhost; + + lhost = xstrdup(host); + lowercase(lhost); if (store_hash) { - if ((hashed_host = host_hash(host, NULL, 0)) == NULL) { + if ((hashed_host = host_hash(lhost, NULL, 0)) == NULL) { error("%s: host_hash failed", __func__); + free(lhost); return 0; } fprintf(f, "%s ", hashed_host); } else if (ip != NULL) - fprintf(f, "%s,%s ", host, ip); - else - fprintf(f, "%s ", host); - + fprintf(f, "%s,%s ", lhost, ip); + else { + fprintf(f, "%s ", lhost); + } + free(lhost); if ((r = sshkey_write(key, f)) == 0) success = 1; else error("%s: sshkey_write failed: %s", __func__, ssh_err(r)); fputc('\n', f); return success; } /* * Appends an entry to the host file. Returns false if the entry could not * be appended. */ int add_host_to_hostfile(const char *filename, const char *host, const struct sshkey *key, int store_hash) { FILE *f; int success; if (key == NULL) return 1; /* XXX ? */ f = fopen(filename, "a"); if (!f) return 0; success = write_host_entry(f, host, NULL, key, store_hash); fclose(f); return success; } struct host_delete_ctx { FILE *out; int quiet; const char *host; int *skip_keys; /* XXX split for host/ip? might want to ensure both */ struct sshkey * const *keys; size_t nkeys; int modified; }; static int host_delete(struct hostkey_foreach_line *l, void *_ctx) { struct host_delete_ctx *ctx = (struct host_delete_ctx *)_ctx; int loglevel = ctx->quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_VERBOSE; size_t i; if (l->status == HKF_STATUS_MATCHED) { if (l->marker != MRK_NONE) { /* Don't remove CA and revocation lines */ fprintf(ctx->out, "%s\n", l->line); return 0; } /* XXX might need a knob for this later */ /* Don't remove RSA1 keys */ if (l->key->type == KEY_RSA1) { fprintf(ctx->out, "%s\n", l->line); return 0; } /* * If this line contains one of the keys that we will be * adding later, then don't change it and mark the key for * skipping. */ for (i = 0; i < ctx->nkeys; i++) { if (sshkey_equal(ctx->keys[i], l->key)) { ctx->skip_keys[i] = 1; fprintf(ctx->out, "%s\n", l->line); debug3("%s: %s key already at %s:%ld", __func__, sshkey_type(l->key), l->path, l->linenum); return 0; } } /* * Hostname matches and has no CA/revoke marker, delete it * by *not* writing the line to ctx->out. */ do_log2(loglevel, "%s%s%s:%ld: Removed %s key for host %s", ctx->quiet ? __func__ : "", ctx->quiet ? ": " : "", l->path, l->linenum, sshkey_type(l->key), ctx->host); ctx->modified = 1; return 0; } /* Retain non-matching hosts and invalid lines when deleting */ if (l->status == HKF_STATUS_INVALID) { do_log2(loglevel, "%s%s%s:%ld: invalid known_hosts entry", ctx->quiet ? __func__ : "", ctx->quiet ? ": " : "", l->path, l->linenum); } fprintf(ctx->out, "%s\n", l->line); return 0; } int hostfile_replace_entries(const char *filename, const char *host, const char *ip, struct sshkey **keys, size_t nkeys, int store_hash, int quiet, int hash_alg) { int r, fd, oerrno = 0; int loglevel = quiet ? SYSLOG_LEVEL_DEBUG1 : SYSLOG_LEVEL_VERBOSE; struct host_delete_ctx ctx; char *fp, *temp = NULL, *back = NULL; mode_t omask; size_t i; omask = umask(077); memset(&ctx, 0, sizeof(ctx)); ctx.host = host; ctx.quiet = quiet; if ((ctx.skip_keys = calloc(nkeys, sizeof(*ctx.skip_keys))) == NULL) return SSH_ERR_ALLOC_FAIL; ctx.keys = keys; ctx.nkeys = nkeys; ctx.modified = 0; /* * Prepare temporary file for in-place deletion. */ if ((r = asprintf(&temp, "%s.XXXXXXXXXXX", filename)) < 0 || (r = asprintf(&back, "%s.old", filename)) < 0) { r = SSH_ERR_ALLOC_FAIL; goto fail; } if ((fd = mkstemp(temp)) == -1) { oerrno = errno; error("%s: mkstemp: %s", __func__, strerror(oerrno)); r = SSH_ERR_SYSTEM_ERROR; goto fail; } if ((ctx.out = fdopen(fd, "w")) == NULL) { oerrno = errno; close(fd); error("%s: fdopen: %s", __func__, strerror(oerrno)); r = SSH_ERR_SYSTEM_ERROR; goto fail; } /* Remove all entries for the specified host from the file */ if ((r = hostkeys_foreach(filename, host_delete, &ctx, host, ip, HKF_WANT_PARSE_KEY)) != 0) { error("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r)); goto fail; } /* Add the requested keys */ for (i = 0; i < nkeys; i++) { if (ctx.skip_keys[i]) continue; if ((fp = sshkey_fingerprint(keys[i], hash_alg, SSH_FP_DEFAULT)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto fail; } do_log2(loglevel, "%s%sAdding new key for %s to %s: %s %s", quiet ? __func__ : "", quiet ? ": " : "", host, filename, sshkey_ssh_name(keys[i]), fp); free(fp); if (!write_host_entry(ctx.out, host, ip, keys[i], store_hash)) { r = SSH_ERR_INTERNAL_ERROR; goto fail; } ctx.modified = 1; } fclose(ctx.out); ctx.out = NULL; if (ctx.modified) { /* Backup the original file and replace it with the temporary */ if (unlink(back) == -1 && errno != ENOENT) { oerrno = errno; error("%s: unlink %.100s: %s", __func__, back, strerror(errno)); r = SSH_ERR_SYSTEM_ERROR; goto fail; } if (link(filename, back) == -1) { oerrno = errno; error("%s: link %.100s to %.100s: %s", __func__, filename, back, strerror(errno)); r = SSH_ERR_SYSTEM_ERROR; goto fail; } if (rename(temp, filename) == -1) { oerrno = errno; error("%s: rename \"%s\" to \"%s\": %s", __func__, temp, filename, strerror(errno)); r = SSH_ERR_SYSTEM_ERROR; goto fail; } } else { /* No changes made; just delete the temporary file */ if (unlink(temp) != 0) error("%s: unlink \"%s\": %s", __func__, temp, strerror(errno)); } /* success */ r = 0; fail: if (temp != NULL && r != 0) unlink(temp); free(temp); free(back); if (ctx.out != NULL) fclose(ctx.out); free(ctx.skip_keys); umask(omask); if (r == SSH_ERR_SYSTEM_ERROR) errno = oerrno; return r; } static int match_maybe_hashed(const char *host, const char *names, int *was_hashed) { int hashed = *names == HASH_DELIM; const char *hashed_host; size_t nlen = strlen(names); if (was_hashed != NULL) *was_hashed = hashed; if (hashed) { if ((hashed_host = host_hash(host, names, nlen)) == NULL) return -1; return nlen == strlen(hashed_host) && strncmp(hashed_host, names, nlen) == 0; } return match_hostname(host, names) == 1; } int hostkeys_foreach(const char *path, hostkeys_foreach_fn *callback, void *ctx, const char *host, const char *ip, u_int options) { FILE *f; char line[8192], oline[8192], ktype[128]; u_long linenum = 0; char *cp, *cp2; u_int kbits; int hashed; int s, r = 0; struct hostkey_foreach_line lineinfo; size_t l; memset(&lineinfo, 0, sizeof(lineinfo)); if (host == NULL && (options & HKF_WANT_MATCH) != 0) return SSH_ERR_INVALID_ARGUMENT; if ((f = fopen(path, "r")) == NULL) return SSH_ERR_SYSTEM_ERROR; debug3("%s: reading file \"%s\"", __func__, path); while (read_keyfile_line(f, path, line, sizeof(line), &linenum) == 0) { line[strcspn(line, "\n")] = '\0'; strlcpy(oline, line, sizeof(oline)); sshkey_free(lineinfo.key); memset(&lineinfo, 0, sizeof(lineinfo)); lineinfo.path = path; lineinfo.linenum = linenum; lineinfo.line = oline; lineinfo.marker = MRK_NONE; lineinfo.status = HKF_STATUS_OK; lineinfo.keytype = KEY_UNSPEC; /* Skip any leading whitespace, comments and empty lines. */ for (cp = line; *cp == ' ' || *cp == '\t'; cp++) ; if (!*cp || *cp == '#' || *cp == '\n') { if ((options & HKF_WANT_MATCH) == 0) { lineinfo.status = HKF_STATUS_COMMENT; if ((r = callback(&lineinfo, ctx)) != 0) break; } continue; } if ((lineinfo.marker = check_markers(&cp)) == MRK_ERROR) { verbose("%s: invalid marker at %s:%lu", __func__, path, linenum); if ((options & HKF_WANT_MATCH) == 0) goto bad; continue; } /* Find the end of the host name portion. */ for (cp2 = cp; *cp2 && *cp2 != ' ' && *cp2 != '\t'; cp2++) ; lineinfo.hosts = cp; *cp2++ = '\0'; /* Check if the host name matches. */ if (host != NULL) { if ((s = match_maybe_hashed(host, lineinfo.hosts, &hashed)) == -1) { debug2("%s: %s:%ld: bad host hash \"%.32s\"", __func__, path, linenum, lineinfo.hosts); goto bad; } if (s == 1) { lineinfo.status = HKF_STATUS_MATCHED; lineinfo.match |= HKF_MATCH_HOST | (hashed ? HKF_MATCH_HOST_HASHED : 0); } /* Try matching IP address if supplied */ if (ip != NULL) { if ((s = match_maybe_hashed(ip, lineinfo.hosts, &hashed)) == -1) { debug2("%s: %s:%ld: bad ip hash " "\"%.32s\"", __func__, path, linenum, lineinfo.hosts); goto bad; } if (s == 1) { lineinfo.status = HKF_STATUS_MATCHED; lineinfo.match |= HKF_MATCH_IP | (hashed ? HKF_MATCH_IP_HASHED : 0); } } /* * Skip this line if host matching requested and * neither host nor address matched. */ if ((options & HKF_WANT_MATCH) != 0 && lineinfo.status != HKF_STATUS_MATCHED) continue; } /* Got a match. Skip host name and any following whitespace */ for (; *cp2 == ' ' || *cp2 == '\t'; cp2++) ; if (*cp2 == '\0' || *cp2 == '#') { debug2("%s:%ld: truncated before key type", path, linenum); goto bad; } lineinfo.rawkey = cp = cp2; if ((options & HKF_WANT_PARSE_KEY) != 0) { /* * Extract the key from the line. This will skip * any leading whitespace. Ignore badly formatted * lines. */ if ((lineinfo.key = sshkey_new(KEY_UNSPEC)) == NULL) { error("%s: sshkey_new failed", __func__); r = SSH_ERR_ALLOC_FAIL; break; } if (!hostfile_read_key(&cp, &kbits, lineinfo.key)) { #ifdef WITH_SSH1 sshkey_free(lineinfo.key); lineinfo.key = sshkey_new(KEY_RSA1); if (lineinfo.key == NULL) { error("%s: sshkey_new fail", __func__); r = SSH_ERR_ALLOC_FAIL; break; } if (!hostfile_read_key(&cp, &kbits, lineinfo.key)) goto bad; #else goto bad; #endif } lineinfo.keytype = lineinfo.key->type; lineinfo.comment = cp; } else { /* Extract and parse key type */ l = strcspn(lineinfo.rawkey, " \t"); if (l <= 1 || l >= sizeof(ktype) || lineinfo.rawkey[l] == '\0') goto bad; memcpy(ktype, lineinfo.rawkey, l); ktype[l] = '\0'; lineinfo.keytype = sshkey_type_from_name(ktype); /* * Assume RSA1 if the first component is a short * decimal number. */ if (lineinfo.keytype == KEY_UNSPEC && l < 8 && strspn(ktype, "0123456789") == l) lineinfo.keytype = KEY_RSA1; /* * Check that something other than whitespace follows * the key type. This won't catch all corruption, but * it does catch trivial truncation. */ cp2 += l; /* Skip past key type */ for (; *cp2 == ' ' || *cp2 == '\t'; cp2++) ; if (*cp2 == '\0' || *cp2 == '#') { debug2("%s:%ld: truncated after key type", path, linenum); lineinfo.keytype = KEY_UNSPEC; } if (lineinfo.keytype == KEY_UNSPEC) { bad: sshkey_free(lineinfo.key); lineinfo.key = NULL; lineinfo.status = HKF_STATUS_INVALID; if ((r = callback(&lineinfo, ctx)) != 0) break; continue; } } if ((r = callback(&lineinfo, ctx)) != 0) break; } sshkey_free(lineinfo.key); fclose(f); return r; } diff --git a/crypto/openssh/kex.c b/crypto/openssh/kex.c index 6a94bc535bd7..cf4ac0dc574d 100644 --- a/crypto/openssh/kex.c +++ b/crypto/openssh/kex.c @@ -1,1027 +1,1040 @@ -/* $OpenBSD: kex.c,v 1.127 2016/10/10 19:28:48 markus Exp $ */ +/* $OpenBSD: kex.c,v 1.131 2017/03/15 07:07:39 markus Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #ifdef WITH_OPENSSL #include #include #endif #include "ssh2.h" #include "packet.h" #include "compat.h" #include "cipher.h" #include "sshkey.h" #include "kex.h" #include "log.h" #include "mac.h" #include "match.h" #include "misc.h" #include "dispatch.h" #include "monitor.h" #include "ssherr.h" #include "sshbuf.h" #include "digest.h" #if OPENSSL_VERSION_NUMBER >= 0x00907000L # if defined(HAVE_EVP_SHA256) # define evp_ssh_sha256 EVP_sha256 # else extern const EVP_MD *evp_ssh_sha256(void); # endif #endif /* prototype */ static int kex_choose_conf(struct ssh *); static int kex_input_newkeys(int, u_int32_t, void *); static const char *proposal_names[PROPOSAL_MAX] = { "KEX algorithms", "host key algorithms", "ciphers ctos", "ciphers stoc", "MACs ctos", "MACs stoc", "compression ctos", "compression stoc", "languages ctos", "languages stoc", }; struct kexalg { char *name; u_int type; int ec_nid; int hash_alg; }; static const struct kexalg kexalgs[] = { #ifdef WITH_OPENSSL { KEX_DH1, KEX_DH_GRP1_SHA1, 0, SSH_DIGEST_SHA1 }, { KEX_DH14_SHA1, KEX_DH_GRP14_SHA1, 0, SSH_DIGEST_SHA1 }, { KEX_DH14_SHA256, KEX_DH_GRP14_SHA256, 0, SSH_DIGEST_SHA256 }, { KEX_DH16_SHA512, KEX_DH_GRP16_SHA512, 0, SSH_DIGEST_SHA512 }, { KEX_DH18_SHA512, KEX_DH_GRP18_SHA512, 0, SSH_DIGEST_SHA512 }, { KEX_DHGEX_SHA1, KEX_DH_GEX_SHA1, 0, SSH_DIGEST_SHA1 }, #ifdef HAVE_EVP_SHA256 { KEX_DHGEX_SHA256, KEX_DH_GEX_SHA256, 0, SSH_DIGEST_SHA256 }, #endif /* HAVE_EVP_SHA256 */ #ifdef OPENSSL_HAS_ECC { KEX_ECDH_SHA2_NISTP256, KEX_ECDH_SHA2, NID_X9_62_prime256v1, SSH_DIGEST_SHA256 }, { KEX_ECDH_SHA2_NISTP384, KEX_ECDH_SHA2, NID_secp384r1, SSH_DIGEST_SHA384 }, # ifdef OPENSSL_HAS_NISTP521 { KEX_ECDH_SHA2_NISTP521, KEX_ECDH_SHA2, NID_secp521r1, SSH_DIGEST_SHA512 }, # endif /* OPENSSL_HAS_NISTP521 */ #endif /* OPENSSL_HAS_ECC */ #endif /* WITH_OPENSSL */ #if defined(HAVE_EVP_SHA256) || !defined(WITH_OPENSSL) { KEX_CURVE25519_SHA256, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, { KEX_CURVE25519_SHA256_OLD, KEX_C25519_SHA256, 0, SSH_DIGEST_SHA256 }, #endif /* HAVE_EVP_SHA256 || !WITH_OPENSSL */ { NULL, -1, -1, -1}, }; char * kex_alg_list(char sep) { char *ret = NULL, *tmp; size_t nlen, rlen = 0; const struct kexalg *k; for (k = kexalgs; k->name != NULL; k++) { if (ret != NULL) ret[rlen++] = sep; nlen = strlen(k->name); if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) { free(ret); return NULL; } ret = tmp; memcpy(ret + rlen, k->name, nlen + 1); rlen += nlen; } return ret; } static const struct kexalg * kex_alg_by_name(const char *name) { const struct kexalg *k; for (k = kexalgs; k->name != NULL; k++) { if (strcmp(k->name, name) == 0) return k; } return NULL; } /* Validate KEX method name list */ int kex_names_valid(const char *names) { char *s, *cp, *p; if (names == NULL || strcmp(names, "") == 0) return 0; if ((s = cp = strdup(names)) == NULL) return 0; for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) { if (kex_alg_by_name(p) == NULL) { error("Unsupported KEX algorithm \"%.100s\"", p); free(s); return 0; } } debug3("kex names ok: [%s]", names); free(s); return 1; } /* * Concatenate algorithm names, avoiding duplicates in the process. * Caller must free returned string. */ char * kex_names_cat(const char *a, const char *b) { - char *ret = NULL, *tmp = NULL, *cp, *p; + char *ret = NULL, *tmp = NULL, *cp, *p, *m; size_t len; if (a == NULL || *a == '\0') return NULL; if (b == NULL || *b == '\0') return strdup(a); if (strlen(b) > 1024*1024) return NULL; len = strlen(a) + strlen(b) + 2; if ((tmp = cp = strdup(b)) == NULL || (ret = calloc(1, len)) == NULL) { free(tmp); return NULL; } strlcpy(ret, a, len); for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) { - if (match_list(ret, p, NULL) != NULL) + if ((m = match_list(ret, p, NULL)) != NULL) { + free(m); continue; /* Algorithm already present */ + } if (strlcat(ret, ",", len) >= len || strlcat(ret, p, len) >= len) { free(tmp); free(ret); return NULL; /* Shouldn't happen */ } } free(tmp); return ret; } /* * Assemble a list of algorithms from a default list and a string from a * configuration file. The user-provided string may begin with '+' to - * indicate that it should be appended to the default. + * indicate that it should be appended to the default or '-' that the + * specified names should be removed. */ int kex_assemble_names(const char *def, char **list) { char *ret; if (list == NULL || *list == NULL || **list == '\0') { *list = strdup(def); return 0; } - if (**list != '+') { - return 0; + if (**list == '+') { + if ((ret = kex_names_cat(def, *list + 1)) == NULL) + return SSH_ERR_ALLOC_FAIL; + free(*list); + *list = ret; + } else if (**list == '-') { + if ((ret = match_filter_list(def, *list + 1)) == NULL) + return SSH_ERR_ALLOC_FAIL; + free(*list); + *list = ret; } - if ((ret = kex_names_cat(def, *list + 1)) == NULL) - return SSH_ERR_ALLOC_FAIL; - free(*list); - *list = ret; return 0; } /* put algorithm proposal into buffer */ int kex_prop2buf(struct sshbuf *b, char *proposal[PROPOSAL_MAX]) { u_int i; int r; sshbuf_reset(b); /* * add a dummy cookie, the cookie will be overwritten by * kex_send_kexinit(), each time a kexinit is set */ for (i = 0; i < KEX_COOKIE_LEN; i++) { if ((r = sshbuf_put_u8(b, 0)) != 0) return r; } for (i = 0; i < PROPOSAL_MAX; i++) { if ((r = sshbuf_put_cstring(b, proposal[i])) != 0) return r; } if ((r = sshbuf_put_u8(b, 0)) != 0 || /* first_kex_packet_follows */ (r = sshbuf_put_u32(b, 0)) != 0) /* uint32 reserved */ return r; return 0; } /* parse buffer and return algorithm proposal */ int kex_buf2prop(struct sshbuf *raw, int *first_kex_follows, char ***propp) { struct sshbuf *b = NULL; u_char v; u_int i; char **proposal = NULL; int r; *propp = NULL; if ((proposal = calloc(PROPOSAL_MAX, sizeof(char *))) == NULL) return SSH_ERR_ALLOC_FAIL; if ((b = sshbuf_fromb(raw)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = sshbuf_consume(b, KEX_COOKIE_LEN)) != 0) /* skip cookie */ goto out; /* extract kex init proposal strings */ for (i = 0; i < PROPOSAL_MAX; i++) { if ((r = sshbuf_get_cstring(b, &(proposal[i]), NULL)) != 0) goto out; debug2("%s: %s", proposal_names[i], proposal[i]); } /* first kex follows / reserved */ if ((r = sshbuf_get_u8(b, &v)) != 0 || /* first_kex_follows */ (r = sshbuf_get_u32(b, &i)) != 0) /* reserved */ goto out; if (first_kex_follows != NULL) *first_kex_follows = v; debug2("first_kex_follows %d ", v); debug2("reserved %u ", i); r = 0; *propp = proposal; out: if (r != 0 && proposal != NULL) kex_prop_free(proposal); sshbuf_free(b); return r; } void kex_prop_free(char **proposal) { u_int i; if (proposal == NULL) return; for (i = 0; i < PROPOSAL_MAX; i++) free(proposal[i]); free(proposal); } /* ARGSUSED */ static int kex_protocol_error(int type, u_int32_t seq, void *ctxt) { struct ssh *ssh = active_state; /* XXX */ int r; error("kex protocol error: type %d seq %u", type, seq); if ((r = sshpkt_start(ssh, SSH2_MSG_UNIMPLEMENTED)) != 0 || (r = sshpkt_put_u32(ssh, seq)) != 0 || (r = sshpkt_send(ssh)) != 0) return r; return 0; } static void kex_reset_dispatch(struct ssh *ssh) { ssh_dispatch_range(ssh, SSH2_MSG_TRANSPORT_MIN, SSH2_MSG_TRANSPORT_MAX, &kex_protocol_error); - ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); } static int kex_send_ext_info(struct ssh *ssh) { int r; char *algs; - if ((algs = sshkey_alg_list(0, 1, ',')) == NULL) + if ((algs = sshkey_alg_list(0, 1, 1, ',')) == NULL) return SSH_ERR_ALLOC_FAIL; if ((r = sshpkt_start(ssh, SSH2_MSG_EXT_INFO)) != 0 || (r = sshpkt_put_u32(ssh, 1)) != 0 || (r = sshpkt_put_cstring(ssh, "server-sig-algs")) != 0 || (r = sshpkt_put_cstring(ssh, algs)) != 0 || (r = sshpkt_send(ssh)) != 0) goto out; /* success */ r = 0; out: free(algs); return r; } int kex_send_newkeys(struct ssh *ssh) { int r; kex_reset_dispatch(ssh); if ((r = sshpkt_start(ssh, SSH2_MSG_NEWKEYS)) != 0 || (r = sshpkt_send(ssh)) != 0) return r; debug("SSH2_MSG_NEWKEYS sent"); debug("expecting SSH2_MSG_NEWKEYS"); ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_input_newkeys); if (ssh->kex->ext_info_c) if ((r = kex_send_ext_info(ssh)) != 0) return r; return 0; } int kex_input_ext_info(int type, u_int32_t seq, void *ctxt) { struct ssh *ssh = ctxt; struct kex *kex = ssh->kex; u_int32_t i, ninfo; char *name, *val, *found; int r; debug("SSH2_MSG_EXT_INFO received"); ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &kex_protocol_error); if ((r = sshpkt_get_u32(ssh, &ninfo)) != 0) return r; for (i = 0; i < ninfo; i++) { if ((r = sshpkt_get_cstring(ssh, &name, NULL)) != 0) return r; if ((r = sshpkt_get_cstring(ssh, &val, NULL)) != 0) { free(name); return r; } debug("%s: %s=<%s>", __func__, name, val); if (strcmp(name, "server-sig-algs") == 0) { found = match_list("rsa-sha2-256", val, NULL); if (found) { kex->rsa_sha2 = 256; free(found); } found = match_list("rsa-sha2-512", val, NULL); if (found) { kex->rsa_sha2 = 512; free(found); } } free(name); free(val); } return sshpkt_get_end(ssh); } static int kex_input_newkeys(int type, u_int32_t seq, void *ctxt) { struct ssh *ssh = ctxt; struct kex *kex = ssh->kex; int r; debug("SSH2_MSG_NEWKEYS received"); ssh_dispatch_set(ssh, SSH2_MSG_NEWKEYS, &kex_protocol_error); + ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); if ((r = sshpkt_get_end(ssh)) != 0) return r; if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0) return r; kex->done = 1; sshbuf_reset(kex->peer); /* sshbuf_reset(kex->my); */ kex->flags &= ~KEX_INIT_SENT; free(kex->name); kex->name = NULL; return 0; } int kex_send_kexinit(struct ssh *ssh) { u_char *cookie; struct kex *kex = ssh->kex; int r; if (kex == NULL) return SSH_ERR_INTERNAL_ERROR; if (kex->flags & KEX_INIT_SENT) return 0; kex->done = 0; /* generate a random cookie */ if (sshbuf_len(kex->my) < KEX_COOKIE_LEN) return SSH_ERR_INVALID_FORMAT; if ((cookie = sshbuf_mutable_ptr(kex->my)) == NULL) return SSH_ERR_INTERNAL_ERROR; arc4random_buf(cookie, KEX_COOKIE_LEN); if ((r = sshpkt_start(ssh, SSH2_MSG_KEXINIT)) != 0 || (r = sshpkt_putb(ssh, kex->my)) != 0 || (r = sshpkt_send(ssh)) != 0) return r; debug("SSH2_MSG_KEXINIT sent"); kex->flags |= KEX_INIT_SENT; return 0; } /* ARGSUSED */ int kex_input_kexinit(int type, u_int32_t seq, void *ctxt) { struct ssh *ssh = ctxt; struct kex *kex = ssh->kex; const u_char *ptr; u_int i; size_t dlen; int r; debug("SSH2_MSG_KEXINIT received"); if (kex == NULL) return SSH_ERR_INVALID_ARGUMENT; ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, NULL); ptr = sshpkt_ptr(ssh, &dlen); if ((r = sshbuf_put(kex->peer, ptr, dlen)) != 0) return r; /* discard packet */ for (i = 0; i < KEX_COOKIE_LEN; i++) if ((r = sshpkt_get_u8(ssh, NULL)) != 0) return r; for (i = 0; i < PROPOSAL_MAX; i++) if ((r = sshpkt_get_string(ssh, NULL, NULL)) != 0) return r; /* * XXX RFC4253 sec 7: "each side MAY guess" - currently no supported * KEX method has the server move first, but a server might be using * a custom method or one that we otherwise don't support. We should * be prepared to remember first_kex_follows here so we can eat a * packet later. * XXX2 - RFC4253 is kind of ambiguous on what first_kex_follows means * for cases where the server *doesn't* go first. I guess we should * ignore it when it is set for these cases, which is what we do now. */ if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || /* first_kex_follows */ (r = sshpkt_get_u32(ssh, NULL)) != 0 || /* reserved */ (r = sshpkt_get_end(ssh)) != 0) return r; if (!(kex->flags & KEX_INIT_SENT)) if ((r = kex_send_kexinit(ssh)) != 0) return r; if ((r = kex_choose_conf(ssh)) != 0) return r; if (kex->kex_type < KEX_MAX && kex->kex[kex->kex_type] != NULL) return (kex->kex[kex->kex_type])(ssh); return SSH_ERR_INTERNAL_ERROR; } int kex_new(struct ssh *ssh, char *proposal[PROPOSAL_MAX], struct kex **kexp) { struct kex *kex; int r; *kexp = NULL; if ((kex = calloc(1, sizeof(*kex))) == NULL) return SSH_ERR_ALLOC_FAIL; if ((kex->peer = sshbuf_new()) == NULL || (kex->my = sshbuf_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = kex_prop2buf(kex->my, proposal)) != 0) goto out; kex->done = 0; kex_reset_dispatch(ssh); + ssh_dispatch_set(ssh, SSH2_MSG_KEXINIT, &kex_input_kexinit); r = 0; *kexp = kex; out: if (r != 0) kex_free(kex); return r; } void kex_free_newkeys(struct newkeys *newkeys) { if (newkeys == NULL) return; if (newkeys->enc.key) { explicit_bzero(newkeys->enc.key, newkeys->enc.key_len); free(newkeys->enc.key); newkeys->enc.key = NULL; } if (newkeys->enc.iv) { explicit_bzero(newkeys->enc.iv, newkeys->enc.iv_len); free(newkeys->enc.iv); newkeys->enc.iv = NULL; } free(newkeys->enc.name); explicit_bzero(&newkeys->enc, sizeof(newkeys->enc)); free(newkeys->comp.name); explicit_bzero(&newkeys->comp, sizeof(newkeys->comp)); mac_clear(&newkeys->mac); if (newkeys->mac.key) { explicit_bzero(newkeys->mac.key, newkeys->mac.key_len); free(newkeys->mac.key); newkeys->mac.key = NULL; } free(newkeys->mac.name); explicit_bzero(&newkeys->mac, sizeof(newkeys->mac)); explicit_bzero(newkeys, sizeof(*newkeys)); free(newkeys); } void kex_free(struct kex *kex) { u_int mode; #ifdef WITH_OPENSSL if (kex->dh) DH_free(kex->dh); #ifdef OPENSSL_HAS_ECC if (kex->ec_client_key) EC_KEY_free(kex->ec_client_key); #endif /* OPENSSL_HAS_ECC */ #endif /* WITH_OPENSSL */ for (mode = 0; mode < MODE_MAX; mode++) { kex_free_newkeys(kex->newkeys[mode]); kex->newkeys[mode] = NULL; } sshbuf_free(kex->peer); sshbuf_free(kex->my); free(kex->session_id); free(kex->client_version_string); free(kex->server_version_string); free(kex->failed_choice); free(kex->hostkey_alg); free(kex->name); free(kex); } int kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX]) { int r; if ((r = kex_new(ssh, proposal, &ssh->kex)) != 0) return r; if ((r = kex_send_kexinit(ssh)) != 0) { /* we start */ kex_free(ssh->kex); ssh->kex = NULL; return r; } return 0; } /* * Request key re-exchange, returns 0 on success or a ssherr.h error * code otherwise. Must not be called if KEX is incomplete or in-progress. */ int kex_start_rekex(struct ssh *ssh) { if (ssh->kex == NULL) { error("%s: no kex", __func__); return SSH_ERR_INTERNAL_ERROR; } if (ssh->kex->done == 0) { error("%s: requested twice", __func__); return SSH_ERR_INTERNAL_ERROR; } ssh->kex->done = 0; return kex_send_kexinit(ssh); } static int choose_enc(struct sshenc *enc, char *client, char *server) { char *name = match_list(client, server, NULL); if (name == NULL) return SSH_ERR_NO_CIPHER_ALG_MATCH; - if ((enc->cipher = cipher_by_name(name)) == NULL) + if ((enc->cipher = cipher_by_name(name)) == NULL) { + free(name); return SSH_ERR_INTERNAL_ERROR; + } enc->name = name; enc->enabled = 0; enc->iv = NULL; enc->iv_len = cipher_ivlen(enc->cipher); enc->key = NULL; enc->key_len = cipher_keylen(enc->cipher); enc->block_size = cipher_blocksize(enc->cipher); return 0; } static int choose_mac(struct ssh *ssh, struct sshmac *mac, char *client, char *server) { char *name = match_list(client, server, NULL); if (name == NULL) return SSH_ERR_NO_MAC_ALG_MATCH; - if (mac_setup(mac, name) < 0) + if (mac_setup(mac, name) < 0) { + free(name); return SSH_ERR_INTERNAL_ERROR; + } /* truncate the key */ if (ssh->compat & SSH_BUG_HMAC) mac->key_len = 16; mac->name = name; mac->key = NULL; mac->enabled = 0; return 0; } static int choose_comp(struct sshcomp *comp, char *client, char *server) { char *name = match_list(client, server, NULL); if (name == NULL) return SSH_ERR_NO_COMPRESS_ALG_MATCH; if (strcmp(name, "zlib@openssh.com") == 0) { comp->type = COMP_DELAYED; } else if (strcmp(name, "zlib") == 0) { comp->type = COMP_ZLIB; } else if (strcmp(name, "none") == 0) { comp->type = COMP_NONE; } else { + free(name); return SSH_ERR_INTERNAL_ERROR; } comp->name = name; return 0; } static int choose_kex(struct kex *k, char *client, char *server) { const struct kexalg *kexalg; k->name = match_list(client, server, NULL); debug("kex: algorithm: %s", k->name ? k->name : "(no match)"); if (k->name == NULL) return SSH_ERR_NO_KEX_ALG_MATCH; if ((kexalg = kex_alg_by_name(k->name)) == NULL) return SSH_ERR_INTERNAL_ERROR; k->kex_type = kexalg->type; k->hash_alg = kexalg->hash_alg; k->ec_nid = kexalg->ec_nid; return 0; } static int choose_hostkeyalg(struct kex *k, char *client, char *server) { k->hostkey_alg = match_list(client, server, NULL); debug("kex: host key algorithm: %s", k->hostkey_alg ? k->hostkey_alg : "(no match)"); if (k->hostkey_alg == NULL) return SSH_ERR_NO_HOSTKEY_ALG_MATCH; k->hostkey_type = sshkey_type_from_name(k->hostkey_alg); if (k->hostkey_type == KEY_UNSPEC) return SSH_ERR_INTERNAL_ERROR; k->hostkey_nid = sshkey_ecdsa_nid_from_name(k->hostkey_alg); return 0; } static int proposals_match(char *my[PROPOSAL_MAX], char *peer[PROPOSAL_MAX]) { static int check[] = { PROPOSAL_KEX_ALGS, PROPOSAL_SERVER_HOST_KEY_ALGS, -1 }; int *idx; char *p; for (idx = &check[0]; *idx != -1; idx++) { if ((p = strchr(my[*idx], ',')) != NULL) *p = '\0'; if ((p = strchr(peer[*idx], ',')) != NULL) *p = '\0'; if (strcmp(my[*idx], peer[*idx]) != 0) { debug2("proposal mismatch: my %s peer %s", my[*idx], peer[*idx]); return (0); } } debug2("proposals match"); return (1); } static int kex_choose_conf(struct ssh *ssh) { struct kex *kex = ssh->kex; struct newkeys *newkeys; char **my = NULL, **peer = NULL; char **cprop, **sprop; int nenc, nmac, ncomp; u_int mode, ctos, need, dh_need, authlen; int r, first_kex_follows; debug2("local %s KEXINIT proposal", kex->server ? "server" : "client"); if ((r = kex_buf2prop(kex->my, NULL, &my)) != 0) goto out; debug2("peer %s KEXINIT proposal", kex->server ? "client" : "server"); if ((r = kex_buf2prop(kex->peer, &first_kex_follows, &peer)) != 0) goto out; if (kex->server) { cprop=peer; sprop=my; } else { cprop=my; sprop=peer; } /* Check whether client supports ext_info_c */ if (kex->server) { char *ext; ext = match_list("ext-info-c", peer[PROPOSAL_KEX_ALGS], NULL); kex->ext_info_c = (ext != NULL); free(ext); } /* Algorithm Negotiation */ if ((r = choose_kex(kex, cprop[PROPOSAL_KEX_ALGS], sprop[PROPOSAL_KEX_ALGS])) != 0) { kex->failed_choice = peer[PROPOSAL_KEX_ALGS]; peer[PROPOSAL_KEX_ALGS] = NULL; goto out; } if ((r = choose_hostkeyalg(kex, cprop[PROPOSAL_SERVER_HOST_KEY_ALGS], sprop[PROPOSAL_SERVER_HOST_KEY_ALGS])) != 0) { kex->failed_choice = peer[PROPOSAL_SERVER_HOST_KEY_ALGS]; peer[PROPOSAL_SERVER_HOST_KEY_ALGS] = NULL; goto out; } for (mode = 0; mode < MODE_MAX; mode++) { if ((newkeys = calloc(1, sizeof(*newkeys))) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } kex->newkeys[mode] = newkeys; ctos = (!kex->server && mode == MODE_OUT) || (kex->server && mode == MODE_IN); nenc = ctos ? PROPOSAL_ENC_ALGS_CTOS : PROPOSAL_ENC_ALGS_STOC; nmac = ctos ? PROPOSAL_MAC_ALGS_CTOS : PROPOSAL_MAC_ALGS_STOC; ncomp = ctos ? PROPOSAL_COMP_ALGS_CTOS : PROPOSAL_COMP_ALGS_STOC; if ((r = choose_enc(&newkeys->enc, cprop[nenc], sprop[nenc])) != 0) { kex->failed_choice = peer[nenc]; peer[nenc] = NULL; goto out; } authlen = cipher_authlen(newkeys->enc.cipher); /* ignore mac for authenticated encryption */ if (authlen == 0 && (r = choose_mac(ssh, &newkeys->mac, cprop[nmac], sprop[nmac])) != 0) { kex->failed_choice = peer[nmac]; peer[nmac] = NULL; goto out; } if ((r = choose_comp(&newkeys->comp, cprop[ncomp], sprop[ncomp])) != 0) { kex->failed_choice = peer[ncomp]; peer[ncomp] = NULL; goto out; } debug("kex: %s cipher: %s MAC: %s compression: %s", ctos ? "client->server" : "server->client", newkeys->enc.name, authlen == 0 ? newkeys->mac.name : "", newkeys->comp.name); } need = dh_need = 0; for (mode = 0; mode < MODE_MAX; mode++) { newkeys = kex->newkeys[mode]; need = MAXIMUM(need, newkeys->enc.key_len); need = MAXIMUM(need, newkeys->enc.block_size); need = MAXIMUM(need, newkeys->enc.iv_len); need = MAXIMUM(need, newkeys->mac.key_len); dh_need = MAXIMUM(dh_need, cipher_seclen(newkeys->enc.cipher)); dh_need = MAXIMUM(dh_need, newkeys->enc.block_size); dh_need = MAXIMUM(dh_need, newkeys->enc.iv_len); dh_need = MAXIMUM(dh_need, newkeys->mac.key_len); } /* XXX need runden? */ kex->we_need = need; kex->dh_need = dh_need; /* ignore the next message if the proposals do not match */ if (first_kex_follows && !proposals_match(my, peer) && !(ssh->compat & SSH_BUG_FIRSTKEX)) ssh->dispatch_skip_packets = 1; r = 0; out: kex_prop_free(my); kex_prop_free(peer); return r; } static int derive_key(struct ssh *ssh, int id, u_int need, u_char *hash, u_int hashlen, const struct sshbuf *shared_secret, u_char **keyp) { struct kex *kex = ssh->kex; struct ssh_digest_ctx *hashctx = NULL; char c = id; u_int have; size_t mdsz; u_char *digest; int r; if ((mdsz = ssh_digest_bytes(kex->hash_alg)) == 0) return SSH_ERR_INVALID_ARGUMENT; if ((digest = calloc(1, ROUNDUP(need, mdsz))) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } /* K1 = HASH(K || H || "A" || session_id) */ if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL || ssh_digest_update_buffer(hashctx, shared_secret) != 0 || ssh_digest_update(hashctx, hash, hashlen) != 0 || ssh_digest_update(hashctx, &c, 1) != 0 || ssh_digest_update(hashctx, kex->session_id, kex->session_id_len) != 0 || ssh_digest_final(hashctx, digest, mdsz) != 0) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } ssh_digest_free(hashctx); hashctx = NULL; /* * expand key: * Kn = HASH(K || H || K1 || K2 || ... || Kn-1) * Key = K1 || K2 || ... || Kn */ for (have = mdsz; need > have; have += mdsz) { if ((hashctx = ssh_digest_start(kex->hash_alg)) == NULL || ssh_digest_update_buffer(hashctx, shared_secret) != 0 || ssh_digest_update(hashctx, hash, hashlen) != 0 || ssh_digest_update(hashctx, digest, have) != 0 || ssh_digest_final(hashctx, digest + have, mdsz) != 0) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } ssh_digest_free(hashctx); hashctx = NULL; } #ifdef DEBUG_KEX fprintf(stderr, "key '%c'== ", c); dump_digest("key", digest, need); #endif *keyp = digest; digest = NULL; r = 0; out: free(digest); ssh_digest_free(hashctx); return r; } #define NKEYS 6 int kex_derive_keys(struct ssh *ssh, u_char *hash, u_int hashlen, const struct sshbuf *shared_secret) { struct kex *kex = ssh->kex; u_char *keys[NKEYS]; u_int i, j, mode, ctos; int r; for (i = 0; i < NKEYS; i++) { if ((r = derive_key(ssh, 'A'+i, kex->we_need, hash, hashlen, shared_secret, &keys[i])) != 0) { for (j = 0; j < i; j++) free(keys[j]); return r; } } for (mode = 0; mode < MODE_MAX; mode++) { ctos = (!kex->server && mode == MODE_OUT) || (kex->server && mode == MODE_IN); kex->newkeys[mode]->enc.iv = keys[ctos ? 0 : 1]; kex->newkeys[mode]->enc.key = keys[ctos ? 2 : 3]; kex->newkeys[mode]->mac.key = keys[ctos ? 4 : 5]; } return 0; } #ifdef WITH_OPENSSL int kex_derive_keys_bn(struct ssh *ssh, u_char *hash, u_int hashlen, const BIGNUM *secret) { struct sshbuf *shared_secret; int r; if ((shared_secret = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; if ((r = sshbuf_put_bignum2(shared_secret, secret)) == 0) r = kex_derive_keys(ssh, hash, hashlen, shared_secret); sshbuf_free(shared_secret); return r; } #endif #ifdef WITH_SSH1 int derive_ssh1_session_id(BIGNUM *host_modulus, BIGNUM *server_modulus, u_int8_t cookie[8], u_int8_t id[16]) { u_int8_t hbuf[2048], sbuf[2048], obuf[SSH_DIGEST_MAX_LENGTH]; struct ssh_digest_ctx *hashctx = NULL; size_t hlen, slen; int r; hlen = BN_num_bytes(host_modulus); slen = BN_num_bytes(server_modulus); if (hlen < (512 / 8) || (u_int)hlen > sizeof(hbuf) || slen < (512 / 8) || (u_int)slen > sizeof(sbuf)) return SSH_ERR_KEY_BITS_MISMATCH; if (BN_bn2bin(host_modulus, hbuf) <= 0 || BN_bn2bin(server_modulus, sbuf) <= 0) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } if ((hashctx = ssh_digest_start(SSH_DIGEST_MD5)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if (ssh_digest_update(hashctx, hbuf, hlen) != 0 || ssh_digest_update(hashctx, sbuf, slen) != 0 || ssh_digest_update(hashctx, cookie, 8) != 0 || ssh_digest_final(hashctx, obuf, sizeof(obuf)) != 0) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } memcpy(id, obuf, ssh_digest_bytes(SSH_DIGEST_MD5)); r = 0; out: ssh_digest_free(hashctx); explicit_bzero(hbuf, sizeof(hbuf)); explicit_bzero(sbuf, sizeof(sbuf)); explicit_bzero(obuf, sizeof(obuf)); return r; } #endif #if defined(DEBUG_KEX) || defined(DEBUG_KEXDH) || defined(DEBUG_KEXECDH) void dump_digest(char *msg, u_char *digest, int len) { fprintf(stderr, "%s\n", msg); sshbuf_dump_data(digest, len, stderr); } #endif diff --git a/crypto/openssh/krl.c b/crypto/openssh/krl.c index e271a1934407..3f28178b7b3c 100644 --- a/crypto/openssh/krl.c +++ b/crypto/openssh/krl.c @@ -1,1297 +1,1298 @@ /* * Copyright (c) 2012 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* $OpenBSD: krl.c,v 1.38 2016/09/12 01:22:38 deraadt Exp $ */ +/* $OpenBSD: krl.c,v 1.39 2017/03/10 07:18:32 dtucker Exp $ */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include "sshbuf.h" #include "ssherr.h" #include "sshkey.h" #include "authfile.h" #include "misc.h" #include "log.h" #include "digest.h" #include "bitmap.h" #include "krl.h" /* #define DEBUG_KRL */ #ifdef DEBUG_KRL # define KRL_DBG(x) debug3 x #else # define KRL_DBG(x) #endif /* * Trees of revoked serial numbers, key IDs and keys. This allows * quick searching, querying and producing lists in canonical order. */ /* Tree of serial numbers. XXX make smarter: really need a real sparse bitmap */ struct revoked_serial { u_int64_t lo, hi; RB_ENTRY(revoked_serial) tree_entry; }; static int serial_cmp(struct revoked_serial *a, struct revoked_serial *b); RB_HEAD(revoked_serial_tree, revoked_serial); RB_GENERATE_STATIC(revoked_serial_tree, revoked_serial, tree_entry, serial_cmp); /* Tree of key IDs */ struct revoked_key_id { char *key_id; RB_ENTRY(revoked_key_id) tree_entry; }; static int key_id_cmp(struct revoked_key_id *a, struct revoked_key_id *b); RB_HEAD(revoked_key_id_tree, revoked_key_id); RB_GENERATE_STATIC(revoked_key_id_tree, revoked_key_id, tree_entry, key_id_cmp); /* Tree of blobs (used for keys and fingerprints) */ struct revoked_blob { u_char *blob; size_t len; RB_ENTRY(revoked_blob) tree_entry; }; static int blob_cmp(struct revoked_blob *a, struct revoked_blob *b); RB_HEAD(revoked_blob_tree, revoked_blob); RB_GENERATE_STATIC(revoked_blob_tree, revoked_blob, tree_entry, blob_cmp); /* Tracks revoked certs for a single CA */ struct revoked_certs { struct sshkey *ca_key; struct revoked_serial_tree revoked_serials; struct revoked_key_id_tree revoked_key_ids; TAILQ_ENTRY(revoked_certs) entry; }; TAILQ_HEAD(revoked_certs_list, revoked_certs); struct ssh_krl { u_int64_t krl_version; u_int64_t generated_date; u_int64_t flags; char *comment; struct revoked_blob_tree revoked_keys; struct revoked_blob_tree revoked_sha1s; struct revoked_certs_list revoked_certs; }; /* Return equal if a and b overlap */ static int serial_cmp(struct revoked_serial *a, struct revoked_serial *b) { if (a->hi >= b->lo && a->lo <= b->hi) return 0; return a->lo < b->lo ? -1 : 1; } static int key_id_cmp(struct revoked_key_id *a, struct revoked_key_id *b) { return strcmp(a->key_id, b->key_id); } static int blob_cmp(struct revoked_blob *a, struct revoked_blob *b) { int r; if (a->len != b->len) { if ((r = memcmp(a->blob, b->blob, MINIMUM(a->len, b->len))) != 0) return r; return a->len > b->len ? 1 : -1; } else return memcmp(a->blob, b->blob, a->len); } struct ssh_krl * ssh_krl_init(void) { struct ssh_krl *krl; if ((krl = calloc(1, sizeof(*krl))) == NULL) return NULL; RB_INIT(&krl->revoked_keys); RB_INIT(&krl->revoked_sha1s); TAILQ_INIT(&krl->revoked_certs); return krl; } static void revoked_certs_free(struct revoked_certs *rc) { struct revoked_serial *rs, *trs; struct revoked_key_id *rki, *trki; RB_FOREACH_SAFE(rs, revoked_serial_tree, &rc->revoked_serials, trs) { RB_REMOVE(revoked_serial_tree, &rc->revoked_serials, rs); free(rs); } RB_FOREACH_SAFE(rki, revoked_key_id_tree, &rc->revoked_key_ids, trki) { RB_REMOVE(revoked_key_id_tree, &rc->revoked_key_ids, rki); free(rki->key_id); free(rki); } sshkey_free(rc->ca_key); } void ssh_krl_free(struct ssh_krl *krl) { struct revoked_blob *rb, *trb; struct revoked_certs *rc, *trc; if (krl == NULL) return; free(krl->comment); RB_FOREACH_SAFE(rb, revoked_blob_tree, &krl->revoked_keys, trb) { RB_REMOVE(revoked_blob_tree, &krl->revoked_keys, rb); free(rb->blob); free(rb); } RB_FOREACH_SAFE(rb, revoked_blob_tree, &krl->revoked_sha1s, trb) { RB_REMOVE(revoked_blob_tree, &krl->revoked_sha1s, rb); free(rb->blob); free(rb); } TAILQ_FOREACH_SAFE(rc, &krl->revoked_certs, entry, trc) { TAILQ_REMOVE(&krl->revoked_certs, rc, entry); revoked_certs_free(rc); } } void ssh_krl_set_version(struct ssh_krl *krl, u_int64_t version) { krl->krl_version = version; } int ssh_krl_set_comment(struct ssh_krl *krl, const char *comment) { free(krl->comment); if ((krl->comment = strdup(comment)) == NULL) return SSH_ERR_ALLOC_FAIL; return 0; } /* * Find the revoked_certs struct for a CA key. If allow_create is set then * create a new one in the tree if one did not exist already. */ static int revoked_certs_for_ca_key(struct ssh_krl *krl, const struct sshkey *ca_key, struct revoked_certs **rcp, int allow_create) { struct revoked_certs *rc; int r; *rcp = NULL; TAILQ_FOREACH(rc, &krl->revoked_certs, entry) { if ((ca_key == NULL && rc->ca_key == NULL) || sshkey_equal(rc->ca_key, ca_key)) { *rcp = rc; return 0; } } if (!allow_create) return 0; /* If this CA doesn't exist in the list then add it now */ if ((rc = calloc(1, sizeof(*rc))) == NULL) return SSH_ERR_ALLOC_FAIL; if (ca_key == NULL) rc->ca_key = NULL; else if ((r = sshkey_from_private(ca_key, &rc->ca_key)) != 0) { free(rc); return r; } RB_INIT(&rc->revoked_serials); RB_INIT(&rc->revoked_key_ids); TAILQ_INSERT_TAIL(&krl->revoked_certs, rc, entry); KRL_DBG(("%s: new CA %s", __func__, ca_key == NULL ? "*" : sshkey_type(ca_key))); *rcp = rc; return 0; } static int insert_serial_range(struct revoked_serial_tree *rt, u_int64_t lo, u_int64_t hi) { struct revoked_serial rs, *ers, *crs, *irs; KRL_DBG(("%s: insert %llu:%llu", __func__, lo, hi)); memset(&rs, 0, sizeof(rs)); rs.lo = lo; rs.hi = hi; ers = RB_NFIND(revoked_serial_tree, rt, &rs); if (ers == NULL || serial_cmp(ers, &rs) != 0) { /* No entry matches. Just insert */ if ((irs = malloc(sizeof(rs))) == NULL) return SSH_ERR_ALLOC_FAIL; memcpy(irs, &rs, sizeof(*irs)); ers = RB_INSERT(revoked_serial_tree, rt, irs); if (ers != NULL) { KRL_DBG(("%s: bad: ers != NULL", __func__)); /* Shouldn't happen */ free(irs); return SSH_ERR_INTERNAL_ERROR; } ers = irs; } else { KRL_DBG(("%s: overlap found %llu:%llu", __func__, ers->lo, ers->hi)); /* * The inserted entry overlaps an existing one. Grow the * existing entry. */ if (ers->lo > lo) ers->lo = lo; if (ers->hi < hi) ers->hi = hi; } /* * The inserted or revised range might overlap or abut adjacent ones; * coalesce as necessary. */ /* Check predecessors */ while ((crs = RB_PREV(revoked_serial_tree, rt, ers)) != NULL) { KRL_DBG(("%s: pred %llu:%llu", __func__, crs->lo, crs->hi)); if (ers->lo != 0 && crs->hi < ers->lo - 1) break; /* This entry overlaps. */ if (crs->lo < ers->lo) { ers->lo = crs->lo; KRL_DBG(("%s: pred extend %llu:%llu", __func__, ers->lo, ers->hi)); } RB_REMOVE(revoked_serial_tree, rt, crs); free(crs); } /* Check successors */ while ((crs = RB_NEXT(revoked_serial_tree, rt, ers)) != NULL) { KRL_DBG(("%s: succ %llu:%llu", __func__, crs->lo, crs->hi)); if (ers->hi != (u_int64_t)-1 && crs->lo > ers->hi + 1) break; /* This entry overlaps. */ if (crs->hi > ers->hi) { ers->hi = crs->hi; KRL_DBG(("%s: succ extend %llu:%llu", __func__, ers->lo, ers->hi)); } RB_REMOVE(revoked_serial_tree, rt, crs); free(crs); } KRL_DBG(("%s: done, final %llu:%llu", __func__, ers->lo, ers->hi)); return 0; } int ssh_krl_revoke_cert_by_serial(struct ssh_krl *krl, const struct sshkey *ca_key, u_int64_t serial) { return ssh_krl_revoke_cert_by_serial_range(krl, ca_key, serial, serial); } int ssh_krl_revoke_cert_by_serial_range(struct ssh_krl *krl, const struct sshkey *ca_key, u_int64_t lo, u_int64_t hi) { struct revoked_certs *rc; int r; if (lo > hi || lo == 0) return SSH_ERR_INVALID_ARGUMENT; if ((r = revoked_certs_for_ca_key(krl, ca_key, &rc, 1)) != 0) return r; return insert_serial_range(&rc->revoked_serials, lo, hi); } int ssh_krl_revoke_cert_by_key_id(struct ssh_krl *krl, const struct sshkey *ca_key, const char *key_id) { struct revoked_key_id *rki, *erki; struct revoked_certs *rc; int r; if ((r = revoked_certs_for_ca_key(krl, ca_key, &rc, 1)) != 0) return r; KRL_DBG(("%s: revoke %s", __func__, key_id)); if ((rki = calloc(1, sizeof(*rki))) == NULL || (rki->key_id = strdup(key_id)) == NULL) { free(rki); return SSH_ERR_ALLOC_FAIL; } erki = RB_INSERT(revoked_key_id_tree, &rc->revoked_key_ids, rki); if (erki != NULL) { free(rki->key_id); free(rki); } return 0; } /* Convert "key" to a public key blob without any certificate information */ static int plain_key_blob(const struct sshkey *key, u_char **blob, size_t *blen) { struct sshkey *kcopy; int r; if ((r = sshkey_from_private(key, &kcopy)) != 0) return r; if (sshkey_is_cert(kcopy)) { if ((r = sshkey_drop_cert(kcopy)) != 0) { sshkey_free(kcopy); return r; } } r = sshkey_to_blob(kcopy, blob, blen); sshkey_free(kcopy); return r; } /* Revoke a key blob. Ownership of blob is transferred to the tree */ static int revoke_blob(struct revoked_blob_tree *rbt, u_char *blob, size_t len) { struct revoked_blob *rb, *erb; if ((rb = calloc(1, sizeof(*rb))) == NULL) return SSH_ERR_ALLOC_FAIL; rb->blob = blob; rb->len = len; erb = RB_INSERT(revoked_blob_tree, rbt, rb); if (erb != NULL) { free(rb->blob); free(rb); } return 0; } int ssh_krl_revoke_key_explicit(struct ssh_krl *krl, const struct sshkey *key) { u_char *blob; size_t len; int r; debug3("%s: revoke type %s", __func__, sshkey_type(key)); if ((r = plain_key_blob(key, &blob, &len)) != 0) return r; return revoke_blob(&krl->revoked_keys, blob, len); } int ssh_krl_revoke_key_sha1(struct ssh_krl *krl, const struct sshkey *key) { u_char *blob; size_t len; int r; debug3("%s: revoke type %s by sha1", __func__, sshkey_type(key)); if ((r = sshkey_fingerprint_raw(key, SSH_DIGEST_SHA1, &blob, &len)) != 0) return r; return revoke_blob(&krl->revoked_sha1s, blob, len); } int ssh_krl_revoke_key(struct ssh_krl *krl, const struct sshkey *key) { if (!sshkey_is_cert(key)) return ssh_krl_revoke_key_sha1(krl, key); if (key->cert->serial == 0) { return ssh_krl_revoke_cert_by_key_id(krl, key->cert->signature_key, key->cert->key_id); } else { return ssh_krl_revoke_cert_by_serial(krl, key->cert->signature_key, key->cert->serial); } } /* * Select the most compact section type to emit next in a KRL based on * the current section type, the run length of contiguous revoked serial * numbers and the gaps from the last and to the next revoked serial. * Applies a mostly-accurate bit cost model to select the section type * that will minimise the size of the resultant KRL. */ static int choose_next_state(int current_state, u_int64_t contig, int final, u_int64_t last_gap, u_int64_t next_gap, int *force_new_section) { int new_state; u_int64_t cost, cost_list, cost_range, cost_bitmap, cost_bitmap_restart; /* * Avoid unsigned overflows. * The limits are high enough to avoid confusing the calculations. */ contig = MINIMUM(contig, 1ULL<<31); last_gap = MINIMUM(last_gap, 1ULL<<31); next_gap = MINIMUM(next_gap, 1ULL<<31); /* * Calculate the cost to switch from the current state to candidates. * NB. range sections only ever contain a single range, so their * switching cost is independent of the current_state. */ cost_list = cost_bitmap = cost_bitmap_restart = 0; cost_range = 8; switch (current_state) { case KRL_SECTION_CERT_SERIAL_LIST: cost_bitmap_restart = cost_bitmap = 8 + 64; break; case KRL_SECTION_CERT_SERIAL_BITMAP: cost_list = 8; cost_bitmap_restart = 8 + 64; break; case KRL_SECTION_CERT_SERIAL_RANGE: case 0: cost_bitmap_restart = cost_bitmap = 8 + 64; cost_list = 8; } /* Estimate base cost in bits of each section type */ cost_list += 64 * contig + (final ? 0 : 8+64); cost_range += (2 * 64) + (final ? 0 : 8+64); cost_bitmap += last_gap + contig + (final ? 0 : MINIMUM(next_gap, 8+64)); cost_bitmap_restart += contig + (final ? 0 : MINIMUM(next_gap, 8+64)); /* Convert to byte costs for actual comparison */ cost_list = (cost_list + 7) / 8; cost_bitmap = (cost_bitmap + 7) / 8; cost_bitmap_restart = (cost_bitmap_restart + 7) / 8; cost_range = (cost_range + 7) / 8; /* Now pick the best choice */ *force_new_section = 0; new_state = KRL_SECTION_CERT_SERIAL_BITMAP; cost = cost_bitmap; if (cost_range < cost) { new_state = KRL_SECTION_CERT_SERIAL_RANGE; cost = cost_range; } if (cost_list < cost) { new_state = KRL_SECTION_CERT_SERIAL_LIST; cost = cost_list; } if (cost_bitmap_restart < cost) { new_state = KRL_SECTION_CERT_SERIAL_BITMAP; *force_new_section = 1; cost = cost_bitmap_restart; } KRL_DBG(("%s: contig %llu last_gap %llu next_gap %llu final %d, costs:" "list %llu range %llu bitmap %llu new bitmap %llu, " "selected 0x%02x%s", __func__, (long long unsigned)contig, (long long unsigned)last_gap, (long long unsigned)next_gap, final, (long long unsigned)cost_list, (long long unsigned)cost_range, (long long unsigned)cost_bitmap, (long long unsigned)cost_bitmap_restart, new_state, *force_new_section ? " restart" : "")); return new_state; } static int put_bitmap(struct sshbuf *buf, struct bitmap *bitmap) { size_t len; u_char *blob; int r; len = bitmap_nbytes(bitmap); if ((blob = malloc(len)) == NULL) return SSH_ERR_ALLOC_FAIL; if (bitmap_to_string(bitmap, blob, len) != 0) { free(blob); return SSH_ERR_INTERNAL_ERROR; } r = sshbuf_put_bignum2_bytes(buf, blob, len); free(blob); return r; } /* Generate a KRL_SECTION_CERTIFICATES KRL section */ static int revoked_certs_generate(struct revoked_certs *rc, struct sshbuf *buf) { int final, force_new_sect, r = SSH_ERR_INTERNAL_ERROR; u_int64_t i, contig, gap, last = 0, bitmap_start = 0; struct revoked_serial *rs, *nrs; struct revoked_key_id *rki; int next_state, state = 0; struct sshbuf *sect; struct bitmap *bitmap = NULL; if ((sect = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; /* Store the header: optional CA scope key, reserved */ if (rc->ca_key == NULL) { if ((r = sshbuf_put_string(buf, NULL, 0)) != 0) goto out; } else { if ((r = sshkey_puts(rc->ca_key, buf)) != 0) goto out; } if ((r = sshbuf_put_string(buf, NULL, 0)) != 0) goto out; /* Store the revoked serials. */ for (rs = RB_MIN(revoked_serial_tree, &rc->revoked_serials); rs != NULL; rs = RB_NEXT(revoked_serial_tree, &rc->revoked_serials, rs)) { KRL_DBG(("%s: serial %llu:%llu state 0x%02x", __func__, (long long unsigned)rs->lo, (long long unsigned)rs->hi, state)); /* Check contiguous length and gap to next section (if any) */ nrs = RB_NEXT(revoked_serial_tree, &rc->revoked_serials, rs); final = nrs == NULL; gap = nrs == NULL ? 0 : nrs->lo - rs->hi; contig = 1 + (rs->hi - rs->lo); /* Choose next state based on these */ next_state = choose_next_state(state, contig, final, state == 0 ? 0 : rs->lo - last, gap, &force_new_sect); /* * If the current section is a range section or has a different * type to the next section, then finish it off now. */ if (state != 0 && (force_new_sect || next_state != state || state == KRL_SECTION_CERT_SERIAL_RANGE)) { KRL_DBG(("%s: finish state 0x%02x", __func__, state)); switch (state) { case KRL_SECTION_CERT_SERIAL_LIST: case KRL_SECTION_CERT_SERIAL_RANGE: break; case KRL_SECTION_CERT_SERIAL_BITMAP: if ((r = put_bitmap(sect, bitmap)) != 0) goto out; bitmap_free(bitmap); bitmap = NULL; break; } if ((r = sshbuf_put_u8(buf, state)) != 0 || (r = sshbuf_put_stringb(buf, sect)) != 0) goto out; sshbuf_reset(sect); } /* If we are starting a new section then prepare it now */ if (next_state != state || force_new_sect) { KRL_DBG(("%s: start state 0x%02x", __func__, next_state)); state = next_state; sshbuf_reset(sect); switch (state) { case KRL_SECTION_CERT_SERIAL_LIST: case KRL_SECTION_CERT_SERIAL_RANGE: break; case KRL_SECTION_CERT_SERIAL_BITMAP: if ((bitmap = bitmap_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } bitmap_start = rs->lo; if ((r = sshbuf_put_u64(sect, bitmap_start)) != 0) goto out; break; } } /* Perform section-specific processing */ switch (state) { case KRL_SECTION_CERT_SERIAL_LIST: for (i = 0; i < contig; i++) { if ((r = sshbuf_put_u64(sect, rs->lo + i)) != 0) goto out; } break; case KRL_SECTION_CERT_SERIAL_RANGE: if ((r = sshbuf_put_u64(sect, rs->lo)) != 0 || (r = sshbuf_put_u64(sect, rs->hi)) != 0) goto out; break; case KRL_SECTION_CERT_SERIAL_BITMAP: if (rs->lo - bitmap_start > INT_MAX) { error("%s: insane bitmap gap", __func__); goto out; } for (i = 0; i < contig; i++) { if (bitmap_set_bit(bitmap, rs->lo + i - bitmap_start) != 0) { r = SSH_ERR_ALLOC_FAIL; goto out; } } break; } last = rs->hi; } /* Flush the remaining section, if any */ if (state != 0) { KRL_DBG(("%s: serial final flush for state 0x%02x", __func__, state)); switch (state) { case KRL_SECTION_CERT_SERIAL_LIST: case KRL_SECTION_CERT_SERIAL_RANGE: break; case KRL_SECTION_CERT_SERIAL_BITMAP: if ((r = put_bitmap(sect, bitmap)) != 0) goto out; bitmap_free(bitmap); bitmap = NULL; break; } if ((r = sshbuf_put_u8(buf, state)) != 0 || (r = sshbuf_put_stringb(buf, sect)) != 0) goto out; } KRL_DBG(("%s: serial done ", __func__)); /* Now output a section for any revocations by key ID */ sshbuf_reset(sect); RB_FOREACH(rki, revoked_key_id_tree, &rc->revoked_key_ids) { KRL_DBG(("%s: key ID %s", __func__, rki->key_id)); if ((r = sshbuf_put_cstring(sect, rki->key_id)) != 0) goto out; } if (sshbuf_len(sect) != 0) { if ((r = sshbuf_put_u8(buf, KRL_SECTION_CERT_KEY_ID)) != 0 || (r = sshbuf_put_stringb(buf, sect)) != 0) goto out; } r = 0; out: bitmap_free(bitmap); sshbuf_free(sect); return r; } int ssh_krl_to_blob(struct ssh_krl *krl, struct sshbuf *buf, const struct sshkey **sign_keys, u_int nsign_keys) { int r = SSH_ERR_INTERNAL_ERROR; struct revoked_certs *rc; struct revoked_blob *rb; struct sshbuf *sect; u_char *sblob = NULL; size_t slen, i; if (krl->generated_date == 0) krl->generated_date = time(NULL); if ((sect = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; /* Store the header */ if ((r = sshbuf_put(buf, KRL_MAGIC, sizeof(KRL_MAGIC) - 1)) != 0 || (r = sshbuf_put_u32(buf, KRL_FORMAT_VERSION)) != 0 || (r = sshbuf_put_u64(buf, krl->krl_version)) != 0 || (r = sshbuf_put_u64(buf, krl->generated_date)) != 0 || (r = sshbuf_put_u64(buf, krl->flags)) != 0 || (r = sshbuf_put_string(buf, NULL, 0)) != 0 || (r = sshbuf_put_cstring(buf, krl->comment)) != 0) goto out; /* Store sections for revoked certificates */ TAILQ_FOREACH(rc, &krl->revoked_certs, entry) { sshbuf_reset(sect); if ((r = revoked_certs_generate(rc, sect)) != 0) goto out; if ((r = sshbuf_put_u8(buf, KRL_SECTION_CERTIFICATES)) != 0 || (r = sshbuf_put_stringb(buf, sect)) != 0) goto out; } /* Finally, output sections for revocations by public key/hash */ sshbuf_reset(sect); RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_keys) { KRL_DBG(("%s: key len %zu ", __func__, rb->len)); if ((r = sshbuf_put_string(sect, rb->blob, rb->len)) != 0) goto out; } if (sshbuf_len(sect) != 0) { if ((r = sshbuf_put_u8(buf, KRL_SECTION_EXPLICIT_KEY)) != 0 || (r = sshbuf_put_stringb(buf, sect)) != 0) goto out; } sshbuf_reset(sect); RB_FOREACH(rb, revoked_blob_tree, &krl->revoked_sha1s) { KRL_DBG(("%s: hash len %zu ", __func__, rb->len)); if ((r = sshbuf_put_string(sect, rb->blob, rb->len)) != 0) goto out; } if (sshbuf_len(sect) != 0) { if ((r = sshbuf_put_u8(buf, KRL_SECTION_FINGERPRINT_SHA1)) != 0 || (r = sshbuf_put_stringb(buf, sect)) != 0) goto out; } for (i = 0; i < nsign_keys; i++) { KRL_DBG(("%s: signature key %s", __func__, sshkey_ssh_name(sign_keys[i]))); if ((r = sshbuf_put_u8(buf, KRL_SECTION_SIGNATURE)) != 0 || (r = sshkey_puts(sign_keys[i], buf)) != 0) goto out; if ((r = sshkey_sign(sign_keys[i], &sblob, &slen, sshbuf_ptr(buf), sshbuf_len(buf), NULL, 0)) != 0) goto out; KRL_DBG(("%s: signature sig len %zu", __func__, slen)); if ((r = sshbuf_put_string(buf, sblob, slen)) != 0) goto out; } r = 0; out: free(sblob); sshbuf_free(sect); return r; } static void format_timestamp(u_int64_t timestamp, char *ts, size_t nts) { time_t t; struct tm *tm; t = timestamp; tm = localtime(&t); if (tm == NULL) strlcpy(ts, "", nts); else { *ts = '\0'; strftime(ts, nts, "%Y%m%dT%H%M%S", tm); } } static int parse_revoked_certs(struct sshbuf *buf, struct ssh_krl *krl) { int r = SSH_ERR_INTERNAL_ERROR; u_char type; const u_char *blob; size_t blen, nbits; struct sshbuf *subsect = NULL; u_int64_t serial, serial_lo, serial_hi; struct bitmap *bitmap = NULL; char *key_id = NULL; struct sshkey *ca_key = NULL; if ((subsect = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; /* Header: key, reserved */ if ((r = sshbuf_get_string_direct(buf, &blob, &blen)) != 0 || (r = sshbuf_skip_string(buf)) != 0) goto out; if (blen != 0 && (r = sshkey_from_blob(blob, blen, &ca_key)) != 0) goto out; while (sshbuf_len(buf) > 0) { sshbuf_free(subsect); subsect = NULL; if ((r = sshbuf_get_u8(buf, &type)) != 0 || (r = sshbuf_froms(buf, &subsect)) != 0) goto out; KRL_DBG(("%s: subsection type 0x%02x", __func__, type)); /* sshbuf_dump(subsect, stderr); */ switch (type) { case KRL_SECTION_CERT_SERIAL_LIST: while (sshbuf_len(subsect) > 0) { if ((r = sshbuf_get_u64(subsect, &serial)) != 0) goto out; if ((r = ssh_krl_revoke_cert_by_serial(krl, ca_key, serial)) != 0) goto out; } break; case KRL_SECTION_CERT_SERIAL_RANGE: if ((r = sshbuf_get_u64(subsect, &serial_lo)) != 0 || (r = sshbuf_get_u64(subsect, &serial_hi)) != 0) goto out; if ((r = ssh_krl_revoke_cert_by_serial_range(krl, ca_key, serial_lo, serial_hi)) != 0) goto out; break; case KRL_SECTION_CERT_SERIAL_BITMAP: if ((bitmap = bitmap_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = sshbuf_get_u64(subsect, &serial_lo)) != 0 || (r = sshbuf_get_bignum2_bytes_direct(subsect, &blob, &blen)) != 0) goto out; if (bitmap_from_string(bitmap, blob, blen) != 0) { r = SSH_ERR_INVALID_FORMAT; goto out; } nbits = bitmap_nbits(bitmap); for (serial = 0; serial < (u_int64_t)nbits; serial++) { if (serial > 0 && serial_lo + serial == 0) { error("%s: bitmap wraps u64", __func__); r = SSH_ERR_INVALID_FORMAT; goto out; } if (!bitmap_test_bit(bitmap, serial)) continue; if ((r = ssh_krl_revoke_cert_by_serial(krl, ca_key, serial_lo + serial)) != 0) goto out; } bitmap_free(bitmap); bitmap = NULL; break; case KRL_SECTION_CERT_KEY_ID: while (sshbuf_len(subsect) > 0) { if ((r = sshbuf_get_cstring(subsect, &key_id, NULL)) != 0) goto out; if ((r = ssh_krl_revoke_cert_by_key_id(krl, ca_key, key_id)) != 0) goto out; free(key_id); key_id = NULL; } break; default: error("Unsupported KRL certificate section %u", type); r = SSH_ERR_INVALID_FORMAT; goto out; } if (sshbuf_len(subsect) > 0) { error("KRL certificate section contains unparsed data"); r = SSH_ERR_INVALID_FORMAT; goto out; } } r = 0; out: if (bitmap != NULL) bitmap_free(bitmap); free(key_id); sshkey_free(ca_key); sshbuf_free(subsect); return r; } /* Attempt to parse a KRL, checking its signature (if any) with sign_ca_keys. */ int ssh_krl_from_blob(struct sshbuf *buf, struct ssh_krl **krlp, const struct sshkey **sign_ca_keys, size_t nsign_ca_keys) { struct sshbuf *copy = NULL, *sect = NULL; struct ssh_krl *krl = NULL; char timestamp[64]; int r = SSH_ERR_INTERNAL_ERROR, sig_seen; struct sshkey *key = NULL, **ca_used = NULL, **tmp_ca_used; u_char type, *rdata = NULL; const u_char *blob; size_t i, j, sig_off, sects_off, rlen, blen, nca_used; u_int format_version; nca_used = 0; *krlp = NULL; if (sshbuf_len(buf) < sizeof(KRL_MAGIC) - 1 || memcmp(sshbuf_ptr(buf), KRL_MAGIC, sizeof(KRL_MAGIC) - 1) != 0) { debug3("%s: not a KRL", __func__); return SSH_ERR_KRL_BAD_MAGIC; } /* Take a copy of the KRL buffer so we can verify its signature later */ if ((copy = sshbuf_fromb(buf)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = sshbuf_consume(copy, sizeof(KRL_MAGIC) - 1)) != 0) goto out; if ((krl = ssh_krl_init()) == NULL) { error("%s: alloc failed", __func__); goto out; } if ((r = sshbuf_get_u32(copy, &format_version)) != 0) goto out; if (format_version != KRL_FORMAT_VERSION) { r = SSH_ERR_INVALID_FORMAT; goto out; } if ((r = sshbuf_get_u64(copy, &krl->krl_version)) != 0 || (r = sshbuf_get_u64(copy, &krl->generated_date)) != 0 || (r = sshbuf_get_u64(copy, &krl->flags)) != 0 || (r = sshbuf_skip_string(copy)) != 0 || (r = sshbuf_get_cstring(copy, &krl->comment, NULL)) != 0) goto out; format_timestamp(krl->generated_date, timestamp, sizeof(timestamp)); debug("KRL version %llu generated at %s%s%s", (long long unsigned)krl->krl_version, timestamp, *krl->comment ? ": " : "", krl->comment); /* * 1st pass: verify signatures, if any. This is done to avoid * detailed parsing of data whose provenance is unverified. */ sig_seen = 0; if (sshbuf_len(buf) < sshbuf_len(copy)) { /* Shouldn't happen */ r = SSH_ERR_INTERNAL_ERROR; goto out; } sects_off = sshbuf_len(buf) - sshbuf_len(copy); while (sshbuf_len(copy) > 0) { if ((r = sshbuf_get_u8(copy, &type)) != 0 || (r = sshbuf_get_string_direct(copy, &blob, &blen)) != 0) goto out; KRL_DBG(("%s: first pass, section 0x%02x", __func__, type)); if (type != KRL_SECTION_SIGNATURE) { if (sig_seen) { error("KRL contains non-signature section " "after signature"); r = SSH_ERR_INVALID_FORMAT; goto out; } /* Not interested for now. */ continue; } sig_seen = 1; /* First string component is the signing key */ if ((r = sshkey_from_blob(blob, blen, &key)) != 0) { r = SSH_ERR_INVALID_FORMAT; goto out; } if (sshbuf_len(buf) < sshbuf_len(copy)) { /* Shouldn't happen */ r = SSH_ERR_INTERNAL_ERROR; goto out; } sig_off = sshbuf_len(buf) - sshbuf_len(copy); /* Second string component is the signature itself */ if ((r = sshbuf_get_string_direct(copy, &blob, &blen)) != 0) { r = SSH_ERR_INVALID_FORMAT; goto out; } /* Check signature over entire KRL up to this point */ if ((r = sshkey_verify(key, blob, blen, sshbuf_ptr(buf), sig_off, 0)) != 0) goto out; /* Check if this key has already signed this KRL */ for (i = 0; i < nca_used; i++) { if (sshkey_equal(ca_used[i], key)) { error("KRL signed more than once with " "the same key"); r = SSH_ERR_INVALID_FORMAT; goto out; } } /* Record keys used to sign the KRL */ tmp_ca_used = reallocarray(ca_used, nca_used + 1, sizeof(*ca_used)); if (tmp_ca_used == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } ca_used = tmp_ca_used; ca_used[nca_used++] = key; key = NULL; } if (sshbuf_len(copy) != 0) { /* Shouldn't happen */ r = SSH_ERR_INTERNAL_ERROR; goto out; } /* * 2nd pass: parse and load the KRL, skipping the header to the point * where the section start. */ sshbuf_free(copy); if ((copy = sshbuf_fromb(buf)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = sshbuf_consume(copy, sects_off)) != 0) goto out; while (sshbuf_len(copy) > 0) { sshbuf_free(sect); sect = NULL; if ((r = sshbuf_get_u8(copy, &type)) != 0 || (r = sshbuf_froms(copy, §)) != 0) goto out; KRL_DBG(("%s: second pass, section 0x%02x", __func__, type)); switch (type) { case KRL_SECTION_CERTIFICATES: if ((r = parse_revoked_certs(sect, krl)) != 0) goto out; break; case KRL_SECTION_EXPLICIT_KEY: case KRL_SECTION_FINGERPRINT_SHA1: while (sshbuf_len(sect) > 0) { if ((r = sshbuf_get_string(sect, &rdata, &rlen)) != 0) goto out; if (type == KRL_SECTION_FINGERPRINT_SHA1 && rlen != 20) { error("%s: bad SHA1 length", __func__); r = SSH_ERR_INVALID_FORMAT; goto out; } if ((r = revoke_blob( type == KRL_SECTION_EXPLICIT_KEY ? &krl->revoked_keys : &krl->revoked_sha1s, rdata, rlen)) != 0) goto out; rdata = NULL; /* revoke_blob frees rdata */ } break; case KRL_SECTION_SIGNATURE: /* Handled above, but still need to stay in synch */ - sshbuf_reset(sect); + sshbuf_free(sect); sect = NULL; if ((r = sshbuf_skip_string(copy)) != 0) goto out; break; default: error("Unsupported KRL section %u", type); r = SSH_ERR_INVALID_FORMAT; goto out; } if (sect != NULL && sshbuf_len(sect) > 0) { error("KRL section contains unparsed data"); r = SSH_ERR_INVALID_FORMAT; goto out; } } /* Check that the key(s) used to sign the KRL weren't revoked */ sig_seen = 0; for (i = 0; i < nca_used; i++) { if (ssh_krl_check_key(krl, ca_used[i]) == 0) sig_seen = 1; else { sshkey_free(ca_used[i]); ca_used[i] = NULL; } } if (nca_used && !sig_seen) { error("All keys used to sign KRL were revoked"); r = SSH_ERR_KEY_REVOKED; goto out; } /* If we have CA keys, then verify that one was used to sign the KRL */ if (sig_seen && nsign_ca_keys != 0) { sig_seen = 0; for (i = 0; !sig_seen && i < nsign_ca_keys; i++) { for (j = 0; j < nca_used; j++) { if (ca_used[j] == NULL) continue; if (sshkey_equal(ca_used[j], sign_ca_keys[i])) { sig_seen = 1; break; } } } if (!sig_seen) { r = SSH_ERR_SIGNATURE_INVALID; error("KRL not signed with any trusted key"); goto out; } } *krlp = krl; r = 0; out: if (r != 0) ssh_krl_free(krl); for (i = 0; i < nca_used; i++) sshkey_free(ca_used[i]); free(ca_used); free(rdata); sshkey_free(key); sshbuf_free(copy); sshbuf_free(sect); return r; } /* Checks certificate serial number and key ID revocation */ static int is_cert_revoked(const struct sshkey *key, struct revoked_certs *rc) { struct revoked_serial rs, *ers; struct revoked_key_id rki, *erki; /* Check revocation by cert key ID */ memset(&rki, 0, sizeof(rki)); rki.key_id = key->cert->key_id; erki = RB_FIND(revoked_key_id_tree, &rc->revoked_key_ids, &rki); if (erki != NULL) { KRL_DBG(("%s: revoked by key ID", __func__)); return SSH_ERR_KEY_REVOKED; } /* * Zero serials numbers are ignored (it's the default when the * CA doesn't specify one). */ if (key->cert->serial == 0) return 0; memset(&rs, 0, sizeof(rs)); rs.lo = rs.hi = key->cert->serial; ers = RB_FIND(revoked_serial_tree, &rc->revoked_serials, &rs); if (ers != NULL) { KRL_DBG(("%s: revoked serial %llu matched %llu:%llu", __func__, key->cert->serial, ers->lo, ers->hi)); return SSH_ERR_KEY_REVOKED; } return 0; } /* Checks whether a given key/cert is revoked. Does not check its CA */ static int is_key_revoked(struct ssh_krl *krl, const struct sshkey *key) { struct revoked_blob rb, *erb; struct revoked_certs *rc; int r; /* Check explicitly revoked hashes first */ memset(&rb, 0, sizeof(rb)); if ((r = sshkey_fingerprint_raw(key, SSH_DIGEST_SHA1, &rb.blob, &rb.len)) != 0) return r; erb = RB_FIND(revoked_blob_tree, &krl->revoked_sha1s, &rb); free(rb.blob); if (erb != NULL) { KRL_DBG(("%s: revoked by key SHA1", __func__)); return SSH_ERR_KEY_REVOKED; } /* Next, explicit keys */ memset(&rb, 0, sizeof(rb)); if ((r = plain_key_blob(key, &rb.blob, &rb.len)) != 0) return r; erb = RB_FIND(revoked_blob_tree, &krl->revoked_keys, &rb); free(rb.blob); if (erb != NULL) { KRL_DBG(("%s: revoked by explicit key", __func__)); return SSH_ERR_KEY_REVOKED; } if (!sshkey_is_cert(key)) return 0; /* Check cert revocation for the specified CA */ if ((r = revoked_certs_for_ca_key(krl, key->cert->signature_key, &rc, 0)) != 0) return r; if (rc != NULL) { if ((r = is_cert_revoked(key, rc)) != 0) return r; } /* Check cert revocation for the wildcard CA */ if ((r = revoked_certs_for_ca_key(krl, NULL, &rc, 0)) != 0) return r; if (rc != NULL) { if ((r = is_cert_revoked(key, rc)) != 0) return r; } KRL_DBG(("%s: %llu no match", __func__, key->cert->serial)); return 0; } int ssh_krl_check_key(struct ssh_krl *krl, const struct sshkey *key) { int r; KRL_DBG(("%s: checking key", __func__)); if ((r = is_key_revoked(krl, key)) != 0) return r; if (sshkey_is_cert(key)) { debug2("%s: checking CA key", __func__); if ((r = is_key_revoked(krl, key->cert->signature_key)) != 0) return r; } KRL_DBG(("%s: key okay", __func__)); return 0; } int ssh_krl_file_contains_key(const char *path, const struct sshkey *key) { struct sshbuf *krlbuf = NULL; struct ssh_krl *krl = NULL; int oerrno = 0, r, fd; if (path == NULL) return 0; if ((krlbuf = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; if ((fd = open(path, O_RDONLY)) == -1) { r = SSH_ERR_SYSTEM_ERROR; oerrno = errno; goto out; } if ((r = sshkey_load_file(fd, krlbuf)) != 0) { oerrno = errno; goto out; } if ((r = ssh_krl_from_blob(krlbuf, &krl, NULL, 0)) != 0) goto out; debug2("%s: checking KRL %s", __func__, path); r = ssh_krl_check_key(krl, key); out: - close(fd); + if (fd != -1) + close(fd); sshbuf_free(krlbuf); ssh_krl_free(krl); if (r != 0) errno = oerrno; return r; } diff --git a/crypto/openssh/log.c b/crypto/openssh/log.c index 2b59c4274454..d0f86cf6fb40 100644 --- a/crypto/openssh/log.c +++ b/crypto/openssh/log.c @@ -1,470 +1,471 @@ -/* $OpenBSD: log.c,v 1.48 2016/07/15 05:01:58 dtucker Exp $ */ +/* $OpenBSD: log.c,v 1.49 2017/03/10 03:15:58 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) # include #endif #include "log.h" static LogLevel log_level = SYSLOG_LEVEL_INFO; static int log_on_stderr = 1; static int log_stderr_fd = STDERR_FILENO; static int log_facility = LOG_AUTH; static char *argv0; static log_handler_fn *log_handler; static void *log_handler_ctx; extern char *__progname; #define LOG_SYSLOG_VIS (VIS_CSTYLE|VIS_NL|VIS_TAB|VIS_OCTAL) #define LOG_STDERR_VIS (VIS_SAFE|VIS_OCTAL) /* textual representation of log-facilities/levels */ static struct { const char *name; SyslogFacility val; } log_facilities[] = { { "DAEMON", SYSLOG_FACILITY_DAEMON }, { "USER", SYSLOG_FACILITY_USER }, { "AUTH", SYSLOG_FACILITY_AUTH }, #ifdef LOG_AUTHPRIV { "AUTHPRIV", SYSLOG_FACILITY_AUTHPRIV }, #endif { "LOCAL0", SYSLOG_FACILITY_LOCAL0 }, { "LOCAL1", SYSLOG_FACILITY_LOCAL1 }, { "LOCAL2", SYSLOG_FACILITY_LOCAL2 }, { "LOCAL3", SYSLOG_FACILITY_LOCAL3 }, { "LOCAL4", SYSLOG_FACILITY_LOCAL4 }, { "LOCAL5", SYSLOG_FACILITY_LOCAL5 }, { "LOCAL6", SYSLOG_FACILITY_LOCAL6 }, { "LOCAL7", SYSLOG_FACILITY_LOCAL7 }, { NULL, SYSLOG_FACILITY_NOT_SET } }; static struct { const char *name; LogLevel val; } log_levels[] = { { "QUIET", SYSLOG_LEVEL_QUIET }, { "FATAL", SYSLOG_LEVEL_FATAL }, { "ERROR", SYSLOG_LEVEL_ERROR }, { "INFO", SYSLOG_LEVEL_INFO }, { "VERBOSE", SYSLOG_LEVEL_VERBOSE }, { "DEBUG", SYSLOG_LEVEL_DEBUG1 }, { "DEBUG1", SYSLOG_LEVEL_DEBUG1 }, { "DEBUG2", SYSLOG_LEVEL_DEBUG2 }, { "DEBUG3", SYSLOG_LEVEL_DEBUG3 }, { NULL, SYSLOG_LEVEL_NOT_SET } }; SyslogFacility log_facility_number(char *name) { int i; if (name != NULL) for (i = 0; log_facilities[i].name; i++) if (strcasecmp(log_facilities[i].name, name) == 0) return log_facilities[i].val; return SYSLOG_FACILITY_NOT_SET; } const char * log_facility_name(SyslogFacility facility) { u_int i; for (i = 0; log_facilities[i].name; i++) if (log_facilities[i].val == facility) return log_facilities[i].name; return NULL; } LogLevel log_level_number(char *name) { int i; if (name != NULL) for (i = 0; log_levels[i].name; i++) if (strcasecmp(log_levels[i].name, name) == 0) return log_levels[i].val; return SYSLOG_LEVEL_NOT_SET; } const char * log_level_name(LogLevel level) { u_int i; for (i = 0; log_levels[i].name != NULL; i++) if (log_levels[i].val == level) return log_levels[i].name; return NULL; } /* Error messages that should be logged. */ void error(const char *fmt,...) { va_list args; va_start(args, fmt); do_log(SYSLOG_LEVEL_ERROR, fmt, args); va_end(args); } void sigdie(const char *fmt,...) { #ifdef DO_LOG_SAFE_IN_SIGHAND va_list args; va_start(args, fmt); do_log(SYSLOG_LEVEL_FATAL, fmt, args); va_end(args); #endif _exit(1); } void logdie(const char *fmt,...) { va_list args; va_start(args, fmt); do_log(SYSLOG_LEVEL_INFO, fmt, args); va_end(args); cleanup_exit(255); } /* Log this message (information that usually should go to the log). */ void logit(const char *fmt,...) { va_list args; va_start(args, fmt); do_log(SYSLOG_LEVEL_INFO, fmt, args); va_end(args); } /* More detailed messages (information that does not need to go to the log). */ void verbose(const char *fmt,...) { va_list args; va_start(args, fmt); do_log(SYSLOG_LEVEL_VERBOSE, fmt, args); va_end(args); } /* Debugging messages that should not be logged during normal operation. */ void debug(const char *fmt,...) { va_list args; va_start(args, fmt); do_log(SYSLOG_LEVEL_DEBUG1, fmt, args); va_end(args); } void debug2(const char *fmt,...) { va_list args; va_start(args, fmt); do_log(SYSLOG_LEVEL_DEBUG2, fmt, args); va_end(args); } void debug3(const char *fmt,...) { va_list args; va_start(args, fmt); do_log(SYSLOG_LEVEL_DEBUG3, fmt, args); va_end(args); } /* * Initialize the log. */ void log_init(char *av0, LogLevel level, SyslogFacility facility, int on_stderr) { #if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT) struct syslog_data sdata = SYSLOG_DATA_INIT; #endif argv0 = av0; switch (level) { case SYSLOG_LEVEL_QUIET: case SYSLOG_LEVEL_FATAL: case SYSLOG_LEVEL_ERROR: case SYSLOG_LEVEL_INFO: case SYSLOG_LEVEL_VERBOSE: case SYSLOG_LEVEL_DEBUG1: case SYSLOG_LEVEL_DEBUG2: case SYSLOG_LEVEL_DEBUG3: log_level = level; break; default: fprintf(stderr, "Unrecognized internal syslog level code %d\n", (int) level); exit(1); } log_handler = NULL; log_handler_ctx = NULL; log_on_stderr = on_stderr; if (on_stderr) return; switch (facility) { case SYSLOG_FACILITY_DAEMON: log_facility = LOG_DAEMON; break; case SYSLOG_FACILITY_USER: log_facility = LOG_USER; break; case SYSLOG_FACILITY_AUTH: log_facility = LOG_AUTH; break; #ifdef LOG_AUTHPRIV case SYSLOG_FACILITY_AUTHPRIV: log_facility = LOG_AUTHPRIV; break; #endif case SYSLOG_FACILITY_LOCAL0: log_facility = LOG_LOCAL0; break; case SYSLOG_FACILITY_LOCAL1: log_facility = LOG_LOCAL1; break; case SYSLOG_FACILITY_LOCAL2: log_facility = LOG_LOCAL2; break; case SYSLOG_FACILITY_LOCAL3: log_facility = LOG_LOCAL3; break; case SYSLOG_FACILITY_LOCAL4: log_facility = LOG_LOCAL4; break; case SYSLOG_FACILITY_LOCAL5: log_facility = LOG_LOCAL5; break; case SYSLOG_FACILITY_LOCAL6: log_facility = LOG_LOCAL6; break; case SYSLOG_FACILITY_LOCAL7: log_facility = LOG_LOCAL7; break; default: fprintf(stderr, "Unrecognized internal syslog facility code %d\n", (int) facility); exit(1); } /* * If an external library (eg libwrap) attempts to use syslog * immediately after reexec, syslog may be pointing to the wrong * facility, so we force an open/close of syslog here. */ #if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT) openlog_r(argv0 ? argv0 : __progname, LOG_PID, log_facility, &sdata); closelog_r(&sdata); #else openlog(argv0 ? argv0 : __progname, LOG_PID, log_facility); closelog(); #endif } void log_change_level(LogLevel new_log_level) { /* no-op if log_init has not been called */ if (argv0 == NULL) return; log_init(argv0, new_log_level, log_facility, log_on_stderr); } int log_is_on_stderr(void) { return log_on_stderr && log_stderr_fd == STDERR_FILENO; } /* redirect what would usually get written to stderr to specified file */ void log_redirect_stderr_to(const char *logfile) { int fd; if ((fd = open(logfile, O_WRONLY|O_CREAT|O_APPEND, 0600)) == -1) { fprintf(stderr, "Couldn't open logfile %s: %s\n", logfile, strerror(errno)); exit(1); } log_stderr_fd = fd; } #define MSGBUFSIZ 1024 void set_log_handler(log_handler_fn *handler, void *ctx) { log_handler = handler; log_handler_ctx = ctx; } void do_log2(LogLevel level, const char *fmt,...) { va_list args; va_start(args, fmt); do_log(level, fmt, args); va_end(args); } void do_log(LogLevel level, const char *fmt, va_list args) { #if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT) struct syslog_data sdata = SYSLOG_DATA_INIT; #endif char msgbuf[MSGBUFSIZ]; char fmtbuf[MSGBUFSIZ]; char *txt = NULL; int pri = LOG_INFO; int saved_errno = errno; log_handler_fn *tmp_handler; if (level > log_level) return; switch (level) { case SYSLOG_LEVEL_FATAL: if (!log_on_stderr) txt = "fatal"; pri = LOG_CRIT; break; case SYSLOG_LEVEL_ERROR: if (!log_on_stderr) txt = "error"; pri = LOG_ERR; break; case SYSLOG_LEVEL_INFO: pri = LOG_INFO; break; case SYSLOG_LEVEL_VERBOSE: pri = LOG_INFO; break; case SYSLOG_LEVEL_DEBUG1: txt = "debug1"; pri = LOG_DEBUG; break; case SYSLOG_LEVEL_DEBUG2: txt = "debug2"; pri = LOG_DEBUG; break; case SYSLOG_LEVEL_DEBUG3: txt = "debug3"; pri = LOG_DEBUG; break; default: txt = "internal error"; pri = LOG_ERR; break; } if (txt != NULL && log_handler == NULL) { snprintf(fmtbuf, sizeof(fmtbuf), "%s: %s", txt, fmt); vsnprintf(msgbuf, sizeof(msgbuf), fmtbuf, args); } else { vsnprintf(msgbuf, sizeof(msgbuf), fmt, args); } strnvis(fmtbuf, msgbuf, sizeof(fmtbuf), log_on_stderr ? LOG_STDERR_VIS : LOG_SYSLOG_VIS); if (log_handler != NULL) { /* Avoid recursion */ tmp_handler = log_handler; log_handler = NULL; tmp_handler(level, fmtbuf, log_handler_ctx); log_handler = tmp_handler; } else if (log_on_stderr) { - snprintf(msgbuf, sizeof msgbuf, "%s\r\n", fmtbuf); + snprintf(msgbuf, sizeof msgbuf, "%.*s\r\n", + (int)sizeof msgbuf - 3, fmtbuf); (void)write(log_stderr_fd, msgbuf, strlen(msgbuf)); } else { #if defined(HAVE_OPENLOG_R) && defined(SYSLOG_DATA_INIT) openlog_r(argv0 ? argv0 : __progname, LOG_PID, log_facility, &sdata); syslog_r(pri, &sdata, "%.500s", fmtbuf); closelog_r(&sdata); #else openlog(argv0 ? argv0 : __progname, LOG_PID, log_facility); syslog(pri, "%.500s", fmtbuf); closelog(); #endif } errno = saved_errno; } diff --git a/crypto/openssh/match.c b/crypto/openssh/match.c index c15dcd1ef30f..3cf40306b025 100644 --- a/crypto/openssh/match.c +++ b/crypto/openssh/match.c @@ -1,286 +1,326 @@ -/* $OpenBSD: match.c,v 1.33 2016/11/06 05:46:37 djm Exp $ */ +/* $OpenBSD: match.c,v 1.37 2017/03/10 04:24:55 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Simple pattern matching, with '*' and '?' as wildcards. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include +#include #include "xmalloc.h" #include "match.h" +#include "misc.h" /* * Returns true if the given string matches the pattern (which may contain ? * and * as wildcards), and zero if it does not match. */ int match_pattern(const char *s, const char *pattern) { for (;;) { /* If at end of pattern, accept if also at end of string. */ if (!*pattern) return !*s; if (*pattern == '*') { /* Skip the asterisk. */ pattern++; /* If at end of pattern, accept immediately. */ if (!*pattern) return 1; /* If next character in pattern is known, optimize. */ if (*pattern != '?' && *pattern != '*') { /* * Look instances of the next character in * pattern, and try to match starting from * those. */ for (; *s; s++) if (*s == *pattern && match_pattern(s + 1, pattern + 1)) return 1; /* Failed. */ return 0; } /* * Move ahead one character at a time and try to * match at each position. */ for (; *s; s++) if (match_pattern(s, pattern)) return 1; /* Failed. */ return 0; } /* * There must be at least one more character in the string. * If we are at the end, fail. */ if (!*s) return 0; /* Check if the next character of the string is acceptable. */ if (*pattern != '?' && *pattern != *s) return 0; /* Move to the next character, both in string and in pattern. */ s++; pattern++; } /* NOTREACHED */ } /* * Tries to match the string against the * comma-separated sequence of subpatterns (each possibly preceded by ! to * indicate negation). Returns -1 if negation matches, 1 if there is * a positive match, 0 if there is no match at all. */ int match_pattern_list(const char *string, const char *pattern, int dolower) { char sub[1024]; int negated; int got_positive; u_int i, subi, len = strlen(pattern); got_positive = 0; for (i = 0; i < len;) { /* Check if the subpattern is negated. */ if (pattern[i] == '!') { negated = 1; i++; } else negated = 0; /* * Extract the subpattern up to a comma or end. Convert the * subpattern to lowercase. */ for (subi = 0; i < len && subi < sizeof(sub) - 1 && pattern[i] != ','; subi++, i++) sub[subi] = dolower && isupper((u_char)pattern[i]) ? tolower((u_char)pattern[i]) : pattern[i]; /* If subpattern too long, return failure (no match). */ if (subi >= sizeof(sub) - 1) return 0; - /* If the subpattern was terminated by a comma, skip the comma. */ + /* If the subpattern was terminated by a comma, then skip it. */ if (i < len && pattern[i] == ',') i++; /* Null-terminate the subpattern. */ sub[subi] = '\0'; /* Try to match the subpattern against the string. */ if (match_pattern(string, sub)) { if (negated) return -1; /* Negative */ else got_positive = 1; /* Positive */ } } /* * Return success if got a positive match. If there was a negative * match, we have already returned -1 and never get here. */ return got_positive; } /* * Tries to match the host name (which must be in all lowercase) against the * comma-separated sequence of subpatterns (each possibly preceded by ! to * indicate negation). Returns -1 if negation matches, 1 if there is * a positive match, 0 if there is no match at all. */ int match_hostname(const char *host, const char *pattern) { - return match_pattern_list(host, pattern, 1); + char *hostcopy = xstrdup(host); + int r; + + lowercase(hostcopy); + r = match_pattern_list(hostcopy, pattern, 1); + free(hostcopy); + return r; } /* * returns 0 if we get a negative match for the hostname or the ip * or if we get no match at all. returns -1 on error, or 1 on * successful match. */ int match_host_and_ip(const char *host, const char *ipaddr, const char *patterns) { int mhost, mip; if ((mip = addr_match_list(ipaddr, patterns)) == -2) return -1; /* error in ipaddr match */ else if (host == NULL || ipaddr == NULL || mip == -1) return 0; /* negative ip address match, or testing pattern */ /* negative hostname match */ if ((mhost = match_hostname(host, patterns)) == -1) return 0; /* no match at all */ if (mhost == 0 && mip == 0) return 0; return 1; } /* * Match user, user@host_or_ip, user@host_or_ip_list against pattern. * If user, host and ipaddr are all NULL then validate pattern/ * Returns -1 on invalid pattern, 0 on no match, 1 on match. */ int match_user(const char *user, const char *host, const char *ipaddr, const char *pattern) { char *p, *pat; int ret; /* test mode */ if (user == NULL && host == NULL && ipaddr == NULL) { if ((p = strchr(pattern, '@')) != NULL && match_host_and_ip(NULL, NULL, p + 1) < 0) return -1; return 0; } if ((p = strchr(pattern,'@')) == NULL) return match_pattern(user, pattern); pat = xstrdup(pattern); p = strchr(pat, '@'); *p++ = '\0'; if ((ret = match_pattern(user, pat)) == 1) ret = match_host_and_ip(host, ipaddr, p); free(pat); return ret; } /* * Returns first item from client-list that is also supported by server-list, * caller must free the returned string. */ #define MAX_PROP 40 #define SEP "," char * match_list(const char *client, const char *server, u_int *next) { char *sproposals[MAX_PROP]; char *c, *s, *p, *ret, *cp, *sp; int i, j, nproposals; c = cp = xstrdup(client); s = sp = xstrdup(server); for ((p = strsep(&sp, SEP)), i=0; p && *p != '\0'; (p = strsep(&sp, SEP)), i++) { if (i < MAX_PROP) sproposals[i] = p; else break; } nproposals = i; for ((p = strsep(&cp, SEP)), i=0; p && *p != '\0'; (p = strsep(&cp, SEP)), i++) { for (j = 0; j < nproposals; j++) { if (strcmp(p, sproposals[j]) == 0) { ret = xstrdup(p); if (next != NULL) *next = (cp == NULL) ? strlen(c) : (u_int)(cp - c); free(c); free(s); return ret; } } } if (next != NULL) *next = strlen(c); free(c); free(s); return NULL; } + +/* + * Filters a comma-separated list of strings, excluding any entry matching + * the 'filter' pattern list. Caller must free returned string. + */ +char * +match_filter_list(const char *proposal, const char *filter) +{ + size_t len = strlen(proposal) + 1; + char *fix_prop = malloc(len); + char *orig_prop = strdup(proposal); + char *cp, *tmp; + + if (fix_prop == NULL || orig_prop == NULL) { + free(orig_prop); + free(fix_prop); + return NULL; + } + + tmp = orig_prop; + *fix_prop = '\0'; + while ((cp = strsep(&tmp, ",")) != NULL) { + if (match_pattern_list(cp, filter, 0) != 1) { + if (*fix_prop != '\0') + strlcat(fix_prop, ",", len); + strlcat(fix_prop, cp, len); + } + } + free(orig_prop); + return fix_prop; +} + diff --git a/crypto/openssh/match.h b/crypto/openssh/match.h index db97ca8f7a28..937ba0412770 100644 --- a/crypto/openssh/match.h +++ b/crypto/openssh/match.h @@ -1,27 +1,28 @@ -/* $OpenBSD: match.h,v 1.16 2015/05/04 06:10:48 djm Exp $ */ +/* $OpenBSD: match.h,v 1.17 2017/02/03 23:01:19 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #ifndef MATCH_H #define MATCH_H int match_pattern(const char *, const char *); int match_pattern_list(const char *, const char *, int); int match_hostname(const char *, const char *); int match_host_and_ip(const char *, const char *, const char *); int match_user(const char *, const char *, const char *, const char *); char *match_list(const char *, const char *, u_int *); +char *match_filter_list(const char *, const char *); /* addrmatch.c */ int addr_match_list(const char *, const char *); int addr_match_cidr_list(const char *, const char *); #endif diff --git a/crypto/openssh/misc.c b/crypto/openssh/misc.c index 8f320340b17c..3ec74c7954f0 100644 --- a/crypto/openssh/misc.c +++ b/crypto/openssh/misc.c @@ -1,1288 +1,1293 @@ -/* $OpenBSD: misc.c,v 1.107 2016/11/30 00:28:31 dtucker Exp $ */ +/* $OpenBSD: misc.c,v 1.109 2017/03/14 00:55:37 dtucker Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2005,2006 Damien Miller. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_PATHS_H # include #include #endif #ifdef SSH_TUN_OPENBSD #include #endif #include "xmalloc.h" #include "misc.h" #include "log.h" #include "ssh.h" /* remove newline at end of string */ char * chop(char *s) { char *t = s; while (*t) { if (*t == '\n' || *t == '\r') { *t = '\0'; return s; } t++; } return s; } /* set/unset filedescriptor to non-blocking */ int set_nonblock(int fd) { int val; val = fcntl(fd, F_GETFL); if (val < 0) { error("fcntl(%d, F_GETFL): %s", fd, strerror(errno)); return (-1); } if (val & O_NONBLOCK) { debug3("fd %d is O_NONBLOCK", fd); return (0); } debug2("fd %d setting O_NONBLOCK", fd); val |= O_NONBLOCK; if (fcntl(fd, F_SETFL, val) == -1) { debug("fcntl(%d, F_SETFL, O_NONBLOCK): %s", fd, strerror(errno)); return (-1); } return (0); } int unset_nonblock(int fd) { int val; val = fcntl(fd, F_GETFL); if (val < 0) { error("fcntl(%d, F_GETFL): %s", fd, strerror(errno)); return (-1); } if (!(val & O_NONBLOCK)) { debug3("fd %d is not O_NONBLOCK", fd); return (0); } debug("fd %d clearing O_NONBLOCK", fd); val &= ~O_NONBLOCK; if (fcntl(fd, F_SETFL, val) == -1) { debug("fcntl(%d, F_SETFL, ~O_NONBLOCK): %s", fd, strerror(errno)); return (-1); } return (0); } const char * ssh_gai_strerror(int gaierr) { if (gaierr == EAI_SYSTEM && errno != 0) return strerror(errno); return gai_strerror(gaierr); } /* disable nagle on socket */ void set_nodelay(int fd) { int opt; socklen_t optlen; optlen = sizeof opt; if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, &optlen) == -1) { debug("getsockopt TCP_NODELAY: %.100s", strerror(errno)); return; } if (opt == 1) { debug2("fd %d is TCP_NODELAY", fd); return; } opt = 1; debug2("fd %d setting TCP_NODELAY", fd); if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) error("setsockopt TCP_NODELAY: %.100s", strerror(errno)); } /* Characters considered whitespace in strsep calls. */ #define WHITESPACE " \t\r\n" #define QUOTE "\"" /* return next token in configuration line */ char * strdelim(char **s) { char *old; int wspace = 0; if (*s == NULL) return NULL; old = *s; *s = strpbrk(*s, WHITESPACE QUOTE "="); if (*s == NULL) return (old); if (*s[0] == '\"') { memmove(*s, *s + 1, strlen(*s)); /* move nul too */ /* Find matching quote */ if ((*s = strpbrk(*s, QUOTE)) == NULL) { return (NULL); /* no matching quote */ } else { *s[0] = '\0'; *s += strspn(*s + 1, WHITESPACE) + 1; return (old); } } /* Allow only one '=' to be skipped */ if (*s[0] == '=') wspace = 1; *s[0] = '\0'; /* Skip any extra whitespace after first token */ *s += strspn(*s + 1, WHITESPACE) + 1; if (*s[0] == '=' && !wspace) *s += strspn(*s + 1, WHITESPACE) + 1; return (old); } struct passwd * pwcopy(struct passwd *pw) { struct passwd *copy = xcalloc(1, sizeof(*copy)); copy->pw_name = xstrdup(pw->pw_name); copy->pw_passwd = xstrdup(pw->pw_passwd); #ifdef HAVE_STRUCT_PASSWD_PW_GECOS copy->pw_gecos = xstrdup(pw->pw_gecos); #endif copy->pw_uid = pw->pw_uid; copy->pw_gid = pw->pw_gid; #ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE copy->pw_expire = pw->pw_expire; #endif #ifdef HAVE_STRUCT_PASSWD_PW_CHANGE copy->pw_change = pw->pw_change; #endif #ifdef HAVE_STRUCT_PASSWD_PW_CLASS copy->pw_class = xstrdup(pw->pw_class); #endif copy->pw_dir = xstrdup(pw->pw_dir); copy->pw_shell = xstrdup(pw->pw_shell); return copy; } /* * Convert ASCII string to TCP/IP port number. * Port must be >=0 and <=65535. * Return -1 if invalid. */ int a2port(const char *s) { long long port; const char *errstr; port = strtonum(s, 0, 65535, &errstr); if (errstr != NULL) return -1; return (int)port; } int a2tun(const char *s, int *remote) { const char *errstr = NULL; char *sp, *ep; int tun; if (remote != NULL) { *remote = SSH_TUNID_ANY; sp = xstrdup(s); if ((ep = strchr(sp, ':')) == NULL) { free(sp); return (a2tun(s, NULL)); } ep[0] = '\0'; ep++; *remote = a2tun(ep, NULL); tun = a2tun(sp, NULL); free(sp); return (*remote == SSH_TUNID_ERR ? *remote : tun); } if (strcasecmp(s, "any") == 0) return (SSH_TUNID_ANY); tun = strtonum(s, 0, SSH_TUNID_MAX, &errstr); if (errstr != NULL) return (SSH_TUNID_ERR); return (tun); } #define SECONDS 1 #define MINUTES (SECONDS * 60) #define HOURS (MINUTES * 60) #define DAYS (HOURS * 24) #define WEEKS (DAYS * 7) /* * Convert a time string into seconds; format is * a sequence of: * time[qualifier] * * Valid time qualifiers are: * seconds * s|S seconds * m|M minutes * h|H hours * d|D days * w|W weeks * * Examples: * 90m 90 minutes * 1h30m 90 minutes * 2d 2 days * 1w 1 week * * Return -1 if time string is invalid. */ long convtime(const char *s) { - long total, secs; + long total, secs, multiplier = 1; const char *p; char *endp; errno = 0; total = 0; p = s; if (p == NULL || *p == '\0') return -1; while (*p) { secs = strtol(p, &endp, 10); if (p == endp || (errno == ERANGE && (secs == LONG_MIN || secs == LONG_MAX)) || secs < 0) return -1; switch (*endp++) { case '\0': endp--; break; case 's': case 'S': break; case 'm': case 'M': - secs *= MINUTES; + multiplier = MINUTES; break; case 'h': case 'H': - secs *= HOURS; + multiplier = HOURS; break; case 'd': case 'D': - secs *= DAYS; + multiplier = DAYS; break; case 'w': case 'W': - secs *= WEEKS; + multiplier = WEEKS; break; default: return -1; } + if (secs >= LONG_MAX / multiplier) + return -1; + secs *= multiplier; + if (total >= LONG_MAX - secs) + return -1; total += secs; if (total < 0) return -1; p = endp; } return total; } /* * Returns a standardized host+port identifier string. * Caller must free returned string. */ char * put_host_port(const char *host, u_short port) { char *hoststr; if (port == 0 || port == SSH_DEFAULT_PORT) return(xstrdup(host)); if (asprintf(&hoststr, "[%s]:%d", host, (int)port) < 0) fatal("put_host_port: asprintf: %s", strerror(errno)); debug3("put_host_port: %s", hoststr); return hoststr; } /* * Search for next delimiter between hostnames/addresses and ports. * Argument may be modified (for termination). * Returns *cp if parsing succeeds. * *cp is set to the start of the next delimiter, if one was found. * If this is the last field, *cp is set to NULL. */ char * hpdelim(char **cp) { char *s, *old; if (cp == NULL || *cp == NULL) return NULL; old = s = *cp; if (*s == '[') { if ((s = strchr(s, ']')) == NULL) return NULL; else s++; } else if ((s = strpbrk(s, ":/")) == NULL) s = *cp + strlen(*cp); /* skip to end (see first case below) */ switch (*s) { case '\0': *cp = NULL; /* no more fields*/ break; case ':': case '/': *s = '\0'; /* terminate */ *cp = s + 1; break; default: return NULL; } return old; } char * cleanhostname(char *host) { if (*host == '[' && host[strlen(host) - 1] == ']') { host[strlen(host) - 1] = '\0'; return (host + 1); } else return host; } char * colon(char *cp) { int flag = 0; if (*cp == ':') /* Leading colon is part of file name. */ return NULL; if (*cp == '[') flag = 1; for (; *cp; ++cp) { if (*cp == '@' && *(cp+1) == '[') flag = 1; if (*cp == ']' && *(cp+1) == ':' && flag) return (cp+1); if (*cp == ':' && !flag) return (cp); if (*cp == '/') return NULL; } return NULL; } /* * Parse a [user@]host[:port] string. * Caller must free returned user and host. * Any of the pointer return arguments may be NULL (useful for syntax checking). * If user was not specified then *userp will be set to NULL. * If port was not specified then *portp will be -1. * Returns 0 on success, -1 on failure. */ int parse_user_host_port(const char *s, char **userp, char **hostp, int *portp) { char *sdup, *cp, *tmp; char *user = NULL, *host = NULL; int port = -1, ret = -1; if (userp != NULL) *userp = NULL; if (hostp != NULL) *hostp = NULL; if (portp != NULL) *portp = -1; if ((sdup = tmp = strdup(s)) == NULL) return -1; /* Extract optional username */ if ((cp = strchr(tmp, '@')) != NULL) { *cp = '\0'; if (*tmp == '\0') goto out; if ((user = strdup(tmp)) == NULL) goto out; tmp = cp + 1; } /* Extract mandatory hostname */ if ((cp = hpdelim(&tmp)) == NULL || *cp == '\0') goto out; host = xstrdup(cleanhostname(cp)); /* Convert and verify optional port */ if (tmp != NULL && *tmp != '\0') { if ((port = a2port(tmp)) <= 0) goto out; } /* Success */ if (userp != NULL) { *userp = user; user = NULL; } if (hostp != NULL) { *hostp = host; host = NULL; } if (portp != NULL) *portp = port; ret = 0; out: free(sdup); free(user); free(host); return ret; } /* function to assist building execv() arguments */ void addargs(arglist *args, char *fmt, ...) { va_list ap; char *cp; u_int nalloc; int r; va_start(ap, fmt); r = vasprintf(&cp, fmt, ap); va_end(ap); if (r == -1) fatal("addargs: argument too long"); nalloc = args->nalloc; if (args->list == NULL) { nalloc = 32; args->num = 0; } else if (args->num+2 >= nalloc) nalloc *= 2; args->list = xreallocarray(args->list, nalloc, sizeof(char *)); args->nalloc = nalloc; args->list[args->num++] = cp; args->list[args->num] = NULL; } void replacearg(arglist *args, u_int which, char *fmt, ...) { va_list ap; char *cp; int r; va_start(ap, fmt); r = vasprintf(&cp, fmt, ap); va_end(ap); if (r == -1) fatal("replacearg: argument too long"); if (which >= args->num) fatal("replacearg: tried to replace invalid arg %d >= %d", which, args->num); free(args->list[which]); args->list[which] = cp; } void freeargs(arglist *args) { u_int i; if (args->list != NULL) { for (i = 0; i < args->num; i++) free(args->list[i]); free(args->list); args->nalloc = args->num = 0; args->list = NULL; } } /* * Expands tildes in the file name. Returns data allocated by xmalloc. * Warning: this calls getpw*. */ char * tilde_expand_filename(const char *filename, uid_t uid) { const char *path, *sep; char user[128], *ret; struct passwd *pw; u_int len, slash; if (*filename != '~') return (xstrdup(filename)); filename++; path = strchr(filename, '/'); if (path != NULL && path > filename) { /* ~user/path */ slash = path - filename; if (slash > sizeof(user) - 1) fatal("tilde_expand_filename: ~username too long"); memcpy(user, filename, slash); user[slash] = '\0'; if ((pw = getpwnam(user)) == NULL) fatal("tilde_expand_filename: No such user %s", user); } else if ((pw = getpwuid(uid)) == NULL) /* ~/path */ fatal("tilde_expand_filename: No such uid %ld", (long)uid); /* Make sure directory has a trailing '/' */ len = strlen(pw->pw_dir); if (len == 0 || pw->pw_dir[len - 1] != '/') sep = "/"; else sep = ""; /* Skip leading '/' from specified path */ if (path != NULL) filename = path + 1; if (xasprintf(&ret, "%s%s%s", pw->pw_dir, sep, filename) >= PATH_MAX) fatal("tilde_expand_filename: Path too long"); return (ret); } /* * Expand a string with a set of %[char] escapes. A number of escapes may be * specified as (char *escape_chars, char *replacement) pairs. The list must * be terminated by a NULL escape_char. Returns replaced string in memory * allocated by xmalloc. */ char * percent_expand(const char *string, ...) { #define EXPAND_MAX_KEYS 16 u_int num_keys, i, j; struct { const char *key; const char *repl; } keys[EXPAND_MAX_KEYS]; char buf[4096]; va_list ap; /* Gather keys */ va_start(ap, string); for (num_keys = 0; num_keys < EXPAND_MAX_KEYS; num_keys++) { keys[num_keys].key = va_arg(ap, char *); if (keys[num_keys].key == NULL) break; keys[num_keys].repl = va_arg(ap, char *); if (keys[num_keys].repl == NULL) fatal("%s: NULL replacement", __func__); } if (num_keys == EXPAND_MAX_KEYS && va_arg(ap, char *) != NULL) fatal("%s: too many keys", __func__); va_end(ap); /* Expand string */ *buf = '\0'; for (i = 0; *string != '\0'; string++) { if (*string != '%') { append: buf[i++] = *string; if (i >= sizeof(buf)) fatal("%s: string too long", __func__); buf[i] = '\0'; continue; } string++; /* %% case */ if (*string == '%') goto append; if (*string == '\0') fatal("%s: invalid format", __func__); for (j = 0; j < num_keys; j++) { if (strchr(keys[j].key, *string) != NULL) { i = strlcat(buf, keys[j].repl, sizeof(buf)); if (i >= sizeof(buf)) fatal("%s: string too long", __func__); break; } } if (j >= num_keys) fatal("%s: unknown key %%%c", __func__, *string); } return (xstrdup(buf)); #undef EXPAND_MAX_KEYS } /* * Read an entire line from a public key file into a static buffer, discarding * lines that exceed the buffer size. Returns 0 on success, -1 on failure. */ int read_keyfile_line(FILE *f, const char *filename, char *buf, size_t bufsz, u_long *lineno) { while (fgets(buf, bufsz, f) != NULL) { if (buf[0] == '\0') continue; (*lineno)++; if (buf[strlen(buf) - 1] == '\n' || feof(f)) { return 0; } else { debug("%s: %s line %lu exceeds size limit", __func__, filename, *lineno); /* discard remainder of line */ while (fgetc(f) != '\n' && !feof(f)) ; /* nothing */ } } return -1; } int tun_open(int tun, int mode) { #if defined(CUSTOM_SYS_TUN_OPEN) return (sys_tun_open(tun, mode)); #elif defined(SSH_TUN_OPENBSD) struct ifreq ifr; char name[100]; int fd = -1, sock; const char *tunbase = "tun"; if (mode == SSH_TUNMODE_ETHERNET) tunbase = "tap"; /* Open the tunnel device */ if (tun <= SSH_TUNID_MAX) { snprintf(name, sizeof(name), "/dev/%s%d", tunbase, tun); fd = open(name, O_RDWR); } else if (tun == SSH_TUNID_ANY) { for (tun = 100; tun >= 0; tun--) { snprintf(name, sizeof(name), "/dev/%s%d", tunbase, tun); if ((fd = open(name, O_RDWR)) >= 0) break; } } else { debug("%s: invalid tunnel %u", __func__, tun); return -1; } if (fd < 0) { debug("%s: %s open: %s", __func__, name, strerror(errno)); return -1; } debug("%s: %s mode %d fd %d", __func__, name, mode, fd); /* Bring interface up if it is not already */ snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", tunbase, tun); if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) goto failed; if (ioctl(sock, SIOCGIFFLAGS, &ifr) == -1) { debug("%s: get interface %s flags: %s", __func__, ifr.ifr_name, strerror(errno)); goto failed; } if (!(ifr.ifr_flags & IFF_UP)) { ifr.ifr_flags |= IFF_UP; if (ioctl(sock, SIOCSIFFLAGS, &ifr) == -1) { debug("%s: activate interface %s: %s", __func__, ifr.ifr_name, strerror(errno)); goto failed; } } close(sock); return fd; failed: if (fd >= 0) close(fd); if (sock >= 0) close(sock); return -1; #else error("Tunnel interfaces are not supported on this platform"); return (-1); #endif } void sanitise_stdfd(void) { int nullfd, dupfd; if ((nullfd = dupfd = open(_PATH_DEVNULL, O_RDWR)) == -1) { fprintf(stderr, "Couldn't open /dev/null: %s\n", strerror(errno)); exit(1); } while (++dupfd <= STDERR_FILENO) { /* Only populate closed fds. */ if (fcntl(dupfd, F_GETFL) == -1 && errno == EBADF) { if (dup2(nullfd, dupfd) == -1) { fprintf(stderr, "dup2: %s\n", strerror(errno)); exit(1); } } } if (nullfd > STDERR_FILENO) close(nullfd); } char * tohex(const void *vp, size_t l) { const u_char *p = (const u_char *)vp; char b[3], *r; size_t i, hl; if (l > 65536) return xstrdup("tohex: length > 65536"); hl = l * 2 + 1; r = xcalloc(1, hl); for (i = 0; i < l; i++) { snprintf(b, sizeof(b), "%02x", p[i]); strlcat(r, b, hl); } return (r); } u_int64_t get_u64(const void *vp) { const u_char *p = (const u_char *)vp; u_int64_t v; v = (u_int64_t)p[0] << 56; v |= (u_int64_t)p[1] << 48; v |= (u_int64_t)p[2] << 40; v |= (u_int64_t)p[3] << 32; v |= (u_int64_t)p[4] << 24; v |= (u_int64_t)p[5] << 16; v |= (u_int64_t)p[6] << 8; v |= (u_int64_t)p[7]; return (v); } u_int32_t get_u32(const void *vp) { const u_char *p = (const u_char *)vp; u_int32_t v; v = (u_int32_t)p[0] << 24; v |= (u_int32_t)p[1] << 16; v |= (u_int32_t)p[2] << 8; v |= (u_int32_t)p[3]; return (v); } u_int32_t get_u32_le(const void *vp) { const u_char *p = (const u_char *)vp; u_int32_t v; v = (u_int32_t)p[0]; v |= (u_int32_t)p[1] << 8; v |= (u_int32_t)p[2] << 16; v |= (u_int32_t)p[3] << 24; return (v); } u_int16_t get_u16(const void *vp) { const u_char *p = (const u_char *)vp; u_int16_t v; v = (u_int16_t)p[0] << 8; v |= (u_int16_t)p[1]; return (v); } void put_u64(void *vp, u_int64_t v) { u_char *p = (u_char *)vp; p[0] = (u_char)(v >> 56) & 0xff; p[1] = (u_char)(v >> 48) & 0xff; p[2] = (u_char)(v >> 40) & 0xff; p[3] = (u_char)(v >> 32) & 0xff; p[4] = (u_char)(v >> 24) & 0xff; p[5] = (u_char)(v >> 16) & 0xff; p[6] = (u_char)(v >> 8) & 0xff; p[7] = (u_char)v & 0xff; } void put_u32(void *vp, u_int32_t v) { u_char *p = (u_char *)vp; p[0] = (u_char)(v >> 24) & 0xff; p[1] = (u_char)(v >> 16) & 0xff; p[2] = (u_char)(v >> 8) & 0xff; p[3] = (u_char)v & 0xff; } void put_u32_le(void *vp, u_int32_t v) { u_char *p = (u_char *)vp; p[0] = (u_char)v & 0xff; p[1] = (u_char)(v >> 8) & 0xff; p[2] = (u_char)(v >> 16) & 0xff; p[3] = (u_char)(v >> 24) & 0xff; } void put_u16(void *vp, u_int16_t v) { u_char *p = (u_char *)vp; p[0] = (u_char)(v >> 8) & 0xff; p[1] = (u_char)v & 0xff; } void ms_subtract_diff(struct timeval *start, int *ms) { struct timeval diff, finish; gettimeofday(&finish, NULL); timersub(&finish, start, &diff); *ms -= (diff.tv_sec * 1000) + (diff.tv_usec / 1000); } void ms_to_timeval(struct timeval *tv, int ms) { if (ms < 0) ms = 0; tv->tv_sec = ms / 1000; tv->tv_usec = (ms % 1000) * 1000; } time_t monotime(void) { #if defined(HAVE_CLOCK_GETTIME) && \ (defined(CLOCK_MONOTONIC) || defined(CLOCK_BOOTTIME)) struct timespec ts; static int gettime_failed = 0; if (!gettime_failed) { #if defined(CLOCK_BOOTTIME) if (clock_gettime(CLOCK_BOOTTIME, &ts) == 0) return (ts.tv_sec); #endif #if defined(CLOCK_MONOTONIC) if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) return (ts.tv_sec); #endif debug3("clock_gettime: %s", strerror(errno)); gettime_failed = 1; } #endif /* HAVE_CLOCK_GETTIME && (CLOCK_MONOTONIC || CLOCK_BOOTTIME */ return time(NULL); } double monotime_double(void) { #if defined(HAVE_CLOCK_GETTIME) && \ (defined(CLOCK_MONOTONIC) || defined(CLOCK_BOOTTIME)) struct timespec ts; static int gettime_failed = 0; if (!gettime_failed) { #if defined(CLOCK_BOOTTIME) if (clock_gettime(CLOCK_BOOTTIME, &ts) == 0) return (ts.tv_sec + (double)ts.tv_nsec / 1000000000); #endif #if defined(CLOCK_MONOTONIC) if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) return (ts.tv_sec + (double)ts.tv_nsec / 1000000000); #endif debug3("clock_gettime: %s", strerror(errno)); gettime_failed = 1; } #endif /* HAVE_CLOCK_GETTIME && (CLOCK_MONOTONIC || CLOCK_BOOTTIME */ return (double)time(NULL); } void bandwidth_limit_init(struct bwlimit *bw, u_int64_t kbps, size_t buflen) { bw->buflen = buflen; bw->rate = kbps; bw->thresh = bw->rate; bw->lamt = 0; timerclear(&bw->bwstart); timerclear(&bw->bwend); } /* Callback from read/write loop to insert bandwidth-limiting delays */ void bandwidth_limit(struct bwlimit *bw, size_t read_len) { u_int64_t waitlen; struct timespec ts, rm; if (!timerisset(&bw->bwstart)) { gettimeofday(&bw->bwstart, NULL); return; } bw->lamt += read_len; if (bw->lamt < bw->thresh) return; gettimeofday(&bw->bwend, NULL); timersub(&bw->bwend, &bw->bwstart, &bw->bwend); if (!timerisset(&bw->bwend)) return; bw->lamt *= 8; waitlen = (double)1000000L * bw->lamt / bw->rate; bw->bwstart.tv_sec = waitlen / 1000000L; bw->bwstart.tv_usec = waitlen % 1000000L; if (timercmp(&bw->bwstart, &bw->bwend, >)) { timersub(&bw->bwstart, &bw->bwend, &bw->bwend); /* Adjust the wait time */ if (bw->bwend.tv_sec) { bw->thresh /= 2; if (bw->thresh < bw->buflen / 4) bw->thresh = bw->buflen / 4; } else if (bw->bwend.tv_usec < 10000) { bw->thresh *= 2; if (bw->thresh > bw->buflen * 8) bw->thresh = bw->buflen * 8; } TIMEVAL_TO_TIMESPEC(&bw->bwend, &ts); while (nanosleep(&ts, &rm) == -1) { if (errno != EINTR) break; ts = rm; } } bw->lamt = 0; gettimeofday(&bw->bwstart, NULL); } /* Make a template filename for mk[sd]temp() */ void mktemp_proto(char *s, size_t len) { const char *tmpdir; int r; if ((tmpdir = getenv("TMPDIR")) != NULL) { r = snprintf(s, len, "%s/ssh-XXXXXXXXXXXX", tmpdir); if (r > 0 && (size_t)r < len) return; } r = snprintf(s, len, "/tmp/ssh-XXXXXXXXXXXX"); if (r < 0 || (size_t)r >= len) fatal("%s: template string too short", __func__); } static const struct { const char *name; int value; } ipqos[] = { { "af11", IPTOS_DSCP_AF11 }, { "af12", IPTOS_DSCP_AF12 }, { "af13", IPTOS_DSCP_AF13 }, { "af21", IPTOS_DSCP_AF21 }, { "af22", IPTOS_DSCP_AF22 }, { "af23", IPTOS_DSCP_AF23 }, { "af31", IPTOS_DSCP_AF31 }, { "af32", IPTOS_DSCP_AF32 }, { "af33", IPTOS_DSCP_AF33 }, { "af41", IPTOS_DSCP_AF41 }, { "af42", IPTOS_DSCP_AF42 }, { "af43", IPTOS_DSCP_AF43 }, { "cs0", IPTOS_DSCP_CS0 }, { "cs1", IPTOS_DSCP_CS1 }, { "cs2", IPTOS_DSCP_CS2 }, { "cs3", IPTOS_DSCP_CS3 }, { "cs4", IPTOS_DSCP_CS4 }, { "cs5", IPTOS_DSCP_CS5 }, { "cs6", IPTOS_DSCP_CS6 }, { "cs7", IPTOS_DSCP_CS7 }, { "ef", IPTOS_DSCP_EF }, { "lowdelay", IPTOS_LOWDELAY }, { "throughput", IPTOS_THROUGHPUT }, { "reliability", IPTOS_RELIABILITY }, { NULL, -1 } }; int parse_ipqos(const char *cp) { u_int i; char *ep; long val; if (cp == NULL) return -1; for (i = 0; ipqos[i].name != NULL; i++) { if (strcasecmp(cp, ipqos[i].name) == 0) return ipqos[i].value; } /* Try parsing as an integer */ val = strtol(cp, &ep, 0); if (*cp == '\0' || *ep != '\0' || val < 0 || val > 255) return -1; return val; } const char * iptos2str(int iptos) { int i; static char iptos_str[sizeof "0xff"]; for (i = 0; ipqos[i].name != NULL; i++) { if (ipqos[i].value == iptos) return ipqos[i].name; } snprintf(iptos_str, sizeof iptos_str, "0x%02x", iptos); return iptos_str; } void lowercase(char *s) { for (; *s; s++) *s = tolower((u_char)*s); } int unix_listener(const char *path, int backlog, int unlink_first) { struct sockaddr_un sunaddr; int saved_errno, sock; memset(&sunaddr, 0, sizeof(sunaddr)); sunaddr.sun_family = AF_UNIX; if (strlcpy(sunaddr.sun_path, path, sizeof(sunaddr.sun_path)) >= sizeof(sunaddr.sun_path)) { error("%s: \"%s\" too long for Unix domain socket", __func__, path); errno = ENAMETOOLONG; return -1; } sock = socket(PF_UNIX, SOCK_STREAM, 0); if (sock < 0) { saved_errno = errno; error("socket: %.100s", strerror(errno)); errno = saved_errno; return -1; } if (unlink_first == 1) { if (unlink(path) != 0 && errno != ENOENT) error("unlink(%s): %.100s", path, strerror(errno)); } if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) { saved_errno = errno; error("bind: %.100s", strerror(errno)); close(sock); error("%s: cannot bind to path: %s", __func__, path); errno = saved_errno; return -1; } if (listen(sock, backlog) < 0) { saved_errno = errno; error("listen: %.100s", strerror(errno)); close(sock); unlink(path); error("%s: cannot listen on path: %s", __func__, path); errno = saved_errno; return -1; } return sock; } void sock_set_v6only(int s) { #if defined(IPV6_V6ONLY) && !defined(__OpenBSD__) int on = 1; debug3("%s: set socket %d IPV6_V6ONLY", __func__, s); if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)) == -1) error("setsockopt IPV6_V6ONLY: %s", strerror(errno)); #endif } /* * Compares two strings that maybe be NULL. Returns non-zero if strings * are both NULL or are identical, returns zero otherwise. */ static int strcmp_maybe_null(const char *a, const char *b) { if ((a == NULL && b != NULL) || (a != NULL && b == NULL)) return 0; if (a != NULL && strcmp(a, b) != 0) return 0; return 1; } /* * Compare two forwards, returning non-zero if they are identical or * zero otherwise. */ int forward_equals(const struct Forward *a, const struct Forward *b) { if (strcmp_maybe_null(a->listen_host, b->listen_host) == 0) return 0; if (a->listen_port != b->listen_port) return 0; if (strcmp_maybe_null(a->listen_path, b->listen_path) == 0) return 0; if (strcmp_maybe_null(a->connect_host, b->connect_host) == 0) return 0; if (a->connect_port != b->connect_port) return 0; if (strcmp_maybe_null(a->connect_path, b->connect_path) == 0) return 0; /* allocated_port and handle are not checked */ return 1; } static int ipport_reserved(void) { #if __FreeBSD__ int old, ret; size_t len = sizeof(old); ret = sysctlbyname("net.inet.ip.portrange.reservedhigh", &old, &len, NULL, 0); if (ret == 0) return (old + 1); #endif return (IPPORT_RESERVED); } /* returns 1 if bind to specified port by specified user is permitted */ int bind_permitted(int port, uid_t uid) { if (port < ipport_reserved() && uid != 0) return 0; return 1; } /* returns 1 if process is already daemonized, 0 otherwise */ int daemonized(void) { int fd; if ((fd = open(_PATH_TTY, O_RDONLY | O_NOCTTY)) >= 0) { close(fd); return 0; /* have controlling terminal */ } if (getppid() != 1) return 0; /* parent is not init */ if (getsid(0) != getpid()) return 0; /* not session leader */ debug3("already daemonized"); return 1; } diff --git a/crypto/openssh/monitor.c b/crypto/openssh/monitor.c index 43f484709b8b..96d22b7e40e9 100644 --- a/crypto/openssh/monitor.c +++ b/crypto/openssh/monitor.c @@ -1,1790 +1,1795 @@ -/* $OpenBSD: monitor.c,v 1.166 2016/09/28 16:33:06 djm Exp $ */ +/* $OpenBSD: monitor.c,v 1.167 2017/02/03 23:05:57 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include "openbsd-compat/sys-tree.h" #include #include #include #include #ifdef HAVE_PATHS_H #include #endif #include #include #ifdef HAVE_STDINT_H #include #endif #include #include #include #include #include #ifdef HAVE_POLL_H #include #else # ifdef HAVE_SYS_POLL_H # include # endif #endif #ifdef SKEY #include #endif #ifdef WITH_OPENSSL #include #endif #include "openbsd-compat/sys-queue.h" #include "atomicio.h" #include "xmalloc.h" #include "ssh.h" #include "key.h" #include "buffer.h" #include "hostfile.h" #include "auth.h" #include "cipher.h" #include "kex.h" #include "dh.h" #include "auth-pam.h" #ifdef TARGET_OS_MAC /* XXX Broken krb5 headers on Mac */ #undef TARGET_OS_MAC #include "zlib.h" #define TARGET_OS_MAC 1 #else #include "zlib.h" #endif #include "packet.h" #include "auth-options.h" #include "sshpty.h" #include "channels.h" #include "session.h" #include "sshlogin.h" #include "canohost.h" #include "log.h" #include "misc.h" #include "servconf.h" #include "monitor.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "monitor_wrap.h" #include "monitor_fdpass.h" #include "compat.h" #include "ssh2.h" #include "authfd.h" #include "match.h" #include "ssherr.h" #ifdef GSSAPI static Gssctxt *gsscontext = NULL; #endif /* Imports */ extern ServerOptions options; extern u_int utmp_len; extern u_char session_id[]; extern Buffer auth_debug; extern int auth_debug_init; extern Buffer loginmsg; /* State exported from the child */ static struct sshbuf *child_state; /* Functions on the monitor that answer unprivileged requests */ int mm_answer_moduli(int, Buffer *); int mm_answer_sign(int, Buffer *); int mm_answer_pwnamallow(int, Buffer *); int mm_answer_auth2_read_banner(int, Buffer *); int mm_answer_authserv(int, Buffer *); int mm_answer_authpassword(int, Buffer *); int mm_answer_bsdauthquery(int, Buffer *); int mm_answer_bsdauthrespond(int, Buffer *); int mm_answer_skeyquery(int, Buffer *); int mm_answer_skeyrespond(int, Buffer *); int mm_answer_keyallowed(int, Buffer *); int mm_answer_keyverify(int, Buffer *); int mm_answer_pty(int, Buffer *); int mm_answer_pty_cleanup(int, Buffer *); int mm_answer_term(int, Buffer *); int mm_answer_rsa_keyallowed(int, Buffer *); int mm_answer_rsa_challenge(int, Buffer *); int mm_answer_rsa_response(int, Buffer *); int mm_answer_sesskey(int, Buffer *); int mm_answer_sessid(int, Buffer *); #ifdef USE_PAM int mm_answer_pam_start(int, Buffer *); int mm_answer_pam_account(int, Buffer *); int mm_answer_pam_init_ctx(int, Buffer *); int mm_answer_pam_query(int, Buffer *); int mm_answer_pam_respond(int, Buffer *); int mm_answer_pam_free_ctx(int, Buffer *); #endif #ifdef GSSAPI int mm_answer_gss_setup_ctx(int, Buffer *); int mm_answer_gss_accept_ctx(int, Buffer *); int mm_answer_gss_userok(int, Buffer *); int mm_answer_gss_checkmic(int, Buffer *); #endif #ifdef SSH_AUDIT_EVENTS int mm_answer_audit_event(int, Buffer *); int mm_answer_audit_command(int, Buffer *); #endif static int monitor_read_log(struct monitor *); static Authctxt *authctxt; /* local state for key verify */ static u_char *key_blob = NULL; static u_int key_bloblen = 0; static int key_blobtype = MM_NOKEY; static char *hostbased_cuser = NULL; static char *hostbased_chost = NULL; static char *auth_method = "unknown"; static char *auth_submethod = NULL; static u_int session_id2_len = 0; static u_char *session_id2 = NULL; static pid_t monitor_child_pid; struct mon_table { enum monitor_reqtype type; int flags; int (*f)(int, Buffer *); }; #define MON_ISAUTH 0x0004 /* Required for Authentication */ #define MON_AUTHDECIDE 0x0008 /* Decides Authentication */ #define MON_ONCE 0x0010 /* Disable after calling */ #define MON_ALOG 0x0020 /* Log auth attempt without authenticating */ #define MON_AUTH (MON_ISAUTH|MON_AUTHDECIDE) #define MON_PERMIT 0x1000 /* Request is permitted */ struct mon_table mon_dispatch_proto20[] = { #ifdef WITH_OPENSSL {MONITOR_REQ_MODULI, MON_ONCE, mm_answer_moduli}, #endif {MONITOR_REQ_SIGN, MON_ONCE, mm_answer_sign}, {MONITOR_REQ_PWNAM, MON_ONCE, mm_answer_pwnamallow}, {MONITOR_REQ_AUTHSERV, MON_ONCE, mm_answer_authserv}, {MONITOR_REQ_AUTH2_READ_BANNER, MON_ONCE, mm_answer_auth2_read_banner}, {MONITOR_REQ_AUTHPASSWORD, MON_AUTH, mm_answer_authpassword}, #ifdef USE_PAM {MONITOR_REQ_PAM_START, MON_ONCE, mm_answer_pam_start}, {MONITOR_REQ_PAM_ACCOUNT, 0, mm_answer_pam_account}, {MONITOR_REQ_PAM_INIT_CTX, MON_ONCE, mm_answer_pam_init_ctx}, {MONITOR_REQ_PAM_QUERY, 0, mm_answer_pam_query}, {MONITOR_REQ_PAM_RESPOND, MON_ONCE, mm_answer_pam_respond}, {MONITOR_REQ_PAM_FREE_CTX, MON_ONCE|MON_AUTHDECIDE, mm_answer_pam_free_ctx}, #endif #ifdef SSH_AUDIT_EVENTS {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, #endif #ifdef BSD_AUTH {MONITOR_REQ_BSDAUTHQUERY, MON_ISAUTH, mm_answer_bsdauthquery}, {MONITOR_REQ_BSDAUTHRESPOND, MON_AUTH, mm_answer_bsdauthrespond}, #endif #ifdef SKEY {MONITOR_REQ_SKEYQUERY, MON_ISAUTH, mm_answer_skeyquery}, {MONITOR_REQ_SKEYRESPOND, MON_AUTH, mm_answer_skeyrespond}, #endif {MONITOR_REQ_KEYALLOWED, MON_ISAUTH, mm_answer_keyallowed}, {MONITOR_REQ_KEYVERIFY, MON_AUTH, mm_answer_keyverify}, #ifdef GSSAPI {MONITOR_REQ_GSSSETUP, MON_ISAUTH, mm_answer_gss_setup_ctx}, {MONITOR_REQ_GSSSTEP, 0, mm_answer_gss_accept_ctx}, {MONITOR_REQ_GSSUSEROK, MON_ONCE|MON_AUTHDECIDE, mm_answer_gss_userok}, {MONITOR_REQ_GSSCHECKMIC, MON_ONCE, mm_answer_gss_checkmic}, #endif {0, 0, NULL} }; struct mon_table mon_dispatch_postauth20[] = { #ifdef WITH_OPENSSL {MONITOR_REQ_MODULI, 0, mm_answer_moduli}, #endif {MONITOR_REQ_SIGN, 0, mm_answer_sign}, {MONITOR_REQ_PTY, 0, mm_answer_pty}, {MONITOR_REQ_PTYCLEANUP, 0, mm_answer_pty_cleanup}, {MONITOR_REQ_TERM, 0, mm_answer_term}, #ifdef SSH_AUDIT_EVENTS {MONITOR_REQ_AUDIT_EVENT, MON_PERMIT, mm_answer_audit_event}, {MONITOR_REQ_AUDIT_COMMAND, MON_PERMIT, mm_answer_audit_command}, #endif {0, 0, NULL} }; struct mon_table *mon_dispatch; /* Specifies if a certain message is allowed at the moment */ static void monitor_permit(struct mon_table *ent, enum monitor_reqtype type, int permit) { while (ent->f != NULL) { if (ent->type == type) { ent->flags &= ~MON_PERMIT; ent->flags |= permit ? MON_PERMIT : 0; return; } ent++; } } static void monitor_permit_authentications(int permit) { struct mon_table *ent = mon_dispatch; while (ent->f != NULL) { if (ent->flags & MON_AUTH) { ent->flags &= ~MON_PERMIT; ent->flags |= permit ? MON_PERMIT : 0; } ent++; } } void monitor_child_preauth(Authctxt *_authctxt, struct monitor *pmonitor) { + struct ssh *ssh = active_state; /* XXX */ struct mon_table *ent; int authenticated = 0, partial = 0; debug3("preauth child monitor started"); close(pmonitor->m_recvfd); close(pmonitor->m_log_sendfd); pmonitor->m_log_sendfd = pmonitor->m_recvfd = -1; authctxt = _authctxt; memset(authctxt, 0, sizeof(*authctxt)); authctxt->loginmsg = &loginmsg; mon_dispatch = mon_dispatch_proto20; /* Permit requests for moduli and signatures */ monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); /* The first few requests do not require asynchronous access */ while (!authenticated) { partial = 0; auth_method = "unknown"; auth_submethod = NULL; authenticated = (monitor_read(pmonitor, mon_dispatch, &ent) == 1); /* Special handling for multiple required authentications */ if (options.num_auth_methods != 0) { if (authenticated && !auth2_update_methods_lists(authctxt, auth_method, auth_submethod)) { debug3("%s: method %s: partial", __func__, auth_method); authenticated = 0; partial = 1; } } if (authenticated) { if (!(ent->flags & MON_AUTHDECIDE)) fatal("%s: unexpected authentication from %d", __func__, ent->type); if (authctxt->pw->pw_uid == 0 && !auth_root_allowed(auth_method)) authenticated = 0; #ifdef USE_PAM /* PAM needs to perform account checks after auth */ if (options.use_pam && authenticated) { Buffer m; buffer_init(&m); mm_request_receive_expect(pmonitor->m_sendfd, MONITOR_REQ_PAM_ACCOUNT, &m); authenticated = mm_answer_pam_account(pmonitor->m_sendfd, &m); buffer_free(&m); } #endif } if (ent->flags & (MON_AUTHDECIDE|MON_ALOG)) { auth_log(authctxt, authenticated, partial, auth_method, auth_submethod); if (!partial && !authenticated) authctxt->failures++; } } if (!authctxt->valid) fatal("%s: authenticated invalid user", __func__); if (strcmp(auth_method, "unknown") == 0) fatal("%s: authentication method name unknown", __func__); debug("%s: %s has been authenticated by privileged process", __func__, authctxt->user); + ssh_packet_set_log_preamble(ssh, "user %s", authctxt->user); mm_get_keystate(pmonitor); /* Drain any buffered messages from the child */ while (pmonitor->m_log_recvfd != -1 && monitor_read_log(pmonitor) == 0) ; close(pmonitor->m_sendfd); close(pmonitor->m_log_recvfd); pmonitor->m_sendfd = pmonitor->m_log_recvfd = -1; } static void monitor_set_child_handler(pid_t pid) { monitor_child_pid = pid; } static void monitor_child_handler(int sig) { kill(monitor_child_pid, sig); } void monitor_child_postauth(struct monitor *pmonitor) { close(pmonitor->m_recvfd); pmonitor->m_recvfd = -1; monitor_set_child_handler(pmonitor->m_pid); signal(SIGHUP, &monitor_child_handler); signal(SIGTERM, &monitor_child_handler); signal(SIGINT, &monitor_child_handler); #ifdef SIGXFSZ signal(SIGXFSZ, SIG_IGN); #endif mon_dispatch = mon_dispatch_postauth20; /* Permit requests for moduli and signatures */ monitor_permit(mon_dispatch, MONITOR_REQ_MODULI, 1); monitor_permit(mon_dispatch, MONITOR_REQ_SIGN, 1); monitor_permit(mon_dispatch, MONITOR_REQ_TERM, 1); if (!no_pty_flag) { monitor_permit(mon_dispatch, MONITOR_REQ_PTY, 1); monitor_permit(mon_dispatch, MONITOR_REQ_PTYCLEANUP, 1); } for (;;) monitor_read(pmonitor, mon_dispatch, NULL); } static int monitor_read_log(struct monitor *pmonitor) { Buffer logmsg; u_int len, level; char *msg; buffer_init(&logmsg); /* Read length */ buffer_append_space(&logmsg, 4); if (atomicio(read, pmonitor->m_log_recvfd, buffer_ptr(&logmsg), buffer_len(&logmsg)) != buffer_len(&logmsg)) { if (errno == EPIPE) { buffer_free(&logmsg); debug("%s: child log fd closed", __func__); close(pmonitor->m_log_recvfd); pmonitor->m_log_recvfd = -1; return -1; } fatal("%s: log fd read: %s", __func__, strerror(errno)); } len = buffer_get_int(&logmsg); if (len <= 4 || len > 8192) fatal("%s: invalid log message length %u", __func__, len); /* Read severity, message */ buffer_clear(&logmsg); buffer_append_space(&logmsg, len); if (atomicio(read, pmonitor->m_log_recvfd, buffer_ptr(&logmsg), buffer_len(&logmsg)) != buffer_len(&logmsg)) fatal("%s: log fd read: %s", __func__, strerror(errno)); /* Log it */ level = buffer_get_int(&logmsg); msg = buffer_get_string(&logmsg, NULL); if (log_level_name(level) == NULL) fatal("%s: invalid log level %u (corrupted message?)", __func__, level); do_log2(level, "%s [preauth]", msg); buffer_free(&logmsg); free(msg); return 0; } int monitor_read(struct monitor *pmonitor, struct mon_table *ent, struct mon_table **pent) { Buffer m; int ret; u_char type; struct pollfd pfd[2]; for (;;) { memset(&pfd, 0, sizeof(pfd)); pfd[0].fd = pmonitor->m_sendfd; pfd[0].events = POLLIN; pfd[1].fd = pmonitor->m_log_recvfd; pfd[1].events = pfd[1].fd == -1 ? 0 : POLLIN; if (poll(pfd, pfd[1].fd == -1 ? 1 : 2, -1) == -1) { if (errno == EINTR || errno == EAGAIN) continue; fatal("%s: poll: %s", __func__, strerror(errno)); } if (pfd[1].revents) { /* * Drain all log messages before processing next * monitor request. */ monitor_read_log(pmonitor); continue; } if (pfd[0].revents) break; /* Continues below */ } buffer_init(&m); mm_request_receive(pmonitor->m_sendfd, &m); type = buffer_get_char(&m); debug3("%s: checking request %d", __func__, type); while (ent->f != NULL) { if (ent->type == type) break; ent++; } if (ent->f != NULL) { if (!(ent->flags & MON_PERMIT)) fatal("%s: unpermitted request %d", __func__, type); ret = (*ent->f)(pmonitor->m_sendfd, &m); buffer_free(&m); /* The child may use this request only once, disable it */ if (ent->flags & MON_ONCE) { debug2("%s: %d used once, disabling now", __func__, type); ent->flags &= ~MON_PERMIT; } if (pent != NULL) *pent = ent; return ret; } fatal("%s: unsupported request: %d", __func__, type); /* NOTREACHED */ return (-1); } /* allowed key state */ static int monitor_allowed_key(u_char *blob, u_int bloblen) { /* make sure key is allowed */ if (key_blob == NULL || key_bloblen != bloblen || timingsafe_bcmp(key_blob, blob, key_bloblen)) return (0); return (1); } static void monitor_reset_key_state(void) { /* reset state */ free(key_blob); free(hostbased_cuser); free(hostbased_chost); key_blob = NULL; key_bloblen = 0; key_blobtype = MM_NOKEY; hostbased_cuser = NULL; hostbased_chost = NULL; } #ifdef WITH_OPENSSL int mm_answer_moduli(int sock, Buffer *m) { DH *dh; int min, want, max; min = buffer_get_int(m); want = buffer_get_int(m); max = buffer_get_int(m); debug3("%s: got parameters: %d %d %d", __func__, min, want, max); /* We need to check here, too, in case the child got corrupted */ if (max < min || want < min || max < want) fatal("%s: bad parameters: %d %d %d", __func__, min, want, max); buffer_clear(m); dh = choose_dh(min, want, max); if (dh == NULL) { buffer_put_char(m, 0); return (0); } else { /* Send first bignum */ buffer_put_char(m, 1); buffer_put_bignum2(m, dh->p); buffer_put_bignum2(m, dh->g); DH_free(dh); } mm_request_send(sock, MONITOR_ANS_MODULI, m); return (0); } #endif int mm_answer_sign(int sock, Buffer *m) { struct ssh *ssh = active_state; /* XXX */ extern int auth_sock; /* XXX move to state struct? */ struct sshkey *key; struct sshbuf *sigbuf = NULL; u_char *p = NULL, *signature = NULL; char *alg = NULL; size_t datlen, siglen, alglen; int r, is_proof = 0; u_int keyid; const char proof_req[] = "hostkeys-prove-00@openssh.com"; debug3("%s", __func__); if ((r = sshbuf_get_u32(m, &keyid)) != 0 || (r = sshbuf_get_string(m, &p, &datlen)) != 0 || (r = sshbuf_get_cstring(m, &alg, &alglen)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (keyid > INT_MAX) fatal("%s: invalid key ID", __func__); /* * Supported KEX types use SHA1 (20 bytes), SHA256 (32 bytes), * SHA384 (48 bytes) and SHA512 (64 bytes). * * Otherwise, verify the signature request is for a hostkey * proof. * * XXX perform similar check for KEX signature requests too? * it's not trivial, since what is signed is the hash, rather * than the full kex structure... */ if (datlen != 20 && datlen != 32 && datlen != 48 && datlen != 64) { /* * Construct expected hostkey proof and compare it to what * the client sent us. */ if (session_id2_len == 0) /* hostkeys is never first */ fatal("%s: bad data length: %zu", __func__, datlen); if ((key = get_hostkey_public_by_index(keyid, ssh)) == NULL) fatal("%s: no hostkey for index %d", __func__, keyid); if ((sigbuf = sshbuf_new()) == NULL) fatal("%s: sshbuf_new", __func__); if ((r = sshbuf_put_cstring(sigbuf, proof_req)) != 0 || (r = sshbuf_put_string(sigbuf, session_id2, session_id2_len)) != 0 || (r = sshkey_puts(key, sigbuf)) != 0) fatal("%s: couldn't prepare private key " "proof buffer: %s", __func__, ssh_err(r)); if (datlen != sshbuf_len(sigbuf) || memcmp(p, sshbuf_ptr(sigbuf), sshbuf_len(sigbuf)) != 0) fatal("%s: bad data length: %zu, hostkey proof len %zu", __func__, datlen, sshbuf_len(sigbuf)); sshbuf_free(sigbuf); is_proof = 1; } /* save session id, it will be passed on the first call */ if (session_id2_len == 0) { session_id2_len = datlen; session_id2 = xmalloc(session_id2_len); memcpy(session_id2, p, session_id2_len); } if ((key = get_hostkey_by_index(keyid)) != NULL) { if ((r = sshkey_sign(key, &signature, &siglen, p, datlen, alg, datafellows)) != 0) fatal("%s: sshkey_sign failed: %s", __func__, ssh_err(r)); } else if ((key = get_hostkey_public_by_index(keyid, ssh)) != NULL && auth_sock > 0) { if ((r = ssh_agent_sign(auth_sock, key, &signature, &siglen, p, datlen, alg, datafellows)) != 0) { fatal("%s: ssh_agent_sign failed: %s", __func__, ssh_err(r)); } } else fatal("%s: no hostkey from index %d", __func__, keyid); debug3("%s: %s signature %p(%zu)", __func__, is_proof ? "KEX" : "hostkey proof", signature, siglen); sshbuf_reset(m); if ((r = sshbuf_put_string(m, signature, siglen)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); free(alg); free(p); free(signature); mm_request_send(sock, MONITOR_ANS_SIGN, m); /* Turn on permissions for getpwnam */ monitor_permit(mon_dispatch, MONITOR_REQ_PWNAM, 1); return (0); } /* Retrieves the password entry and also checks if the user is permitted */ int mm_answer_pwnamallow(int sock, Buffer *m) { + struct ssh *ssh = active_state; /* XXX */ char *username; struct passwd *pwent; int allowed = 0; u_int i; debug3("%s", __func__); if (authctxt->attempt++ != 0) fatal("%s: multiple attempts for getpwnam", __func__); username = buffer_get_string(m, NULL); pwent = getpwnamallow(username); authctxt->user = xstrdup(username); setproctitle("%s [priv]", pwent ? username : "unknown"); free(username); buffer_clear(m); if (pwent == NULL) { buffer_put_char(m, 0); authctxt->pw = fakepw(); goto out; } allowed = 1; authctxt->pw = pwent; authctxt->valid = 1; buffer_put_char(m, 1); buffer_put_string(m, pwent, sizeof(struct passwd)); buffer_put_cstring(m, pwent->pw_name); buffer_put_cstring(m, "*"); #ifdef HAVE_STRUCT_PASSWD_PW_GECOS buffer_put_cstring(m, pwent->pw_gecos); #endif #ifdef HAVE_STRUCT_PASSWD_PW_CLASS buffer_put_cstring(m, pwent->pw_class); #endif buffer_put_cstring(m, pwent->pw_dir); buffer_put_cstring(m, pwent->pw_shell); out: + ssh_packet_set_log_preamble(ssh, "%suser %s", + authctxt->valid ? "authenticating" : "invalid ", authctxt->user); buffer_put_string(m, &options, sizeof(options)); #define M_CP_STROPT(x) do { \ if (options.x != NULL) \ buffer_put_cstring(m, options.x); \ } while (0) #define M_CP_STRARRAYOPT(x, nx) do { \ for (i = 0; i < options.nx; i++) \ buffer_put_cstring(m, options.x[i]); \ } while (0) /* See comment in servconf.h */ COPY_MATCH_STRING_OPTS(); #undef M_CP_STROPT #undef M_CP_STRARRAYOPT /* Create valid auth method lists */ if (auth2_setup_methods_lists(authctxt) != 0) { /* * The monitor will continue long enough to let the child * run to it's packet_disconnect(), but it must not allow any * authentication to succeed. */ debug("%s: no valid authentication method lists", __func__); } debug3("%s: sending MONITOR_ANS_PWNAM: %d", __func__, allowed); mm_request_send(sock, MONITOR_ANS_PWNAM, m); /* Allow service/style information on the auth context */ monitor_permit(mon_dispatch, MONITOR_REQ_AUTHSERV, 1); monitor_permit(mon_dispatch, MONITOR_REQ_AUTH2_READ_BANNER, 1); #ifdef USE_PAM if (options.use_pam) monitor_permit(mon_dispatch, MONITOR_REQ_PAM_START, 1); #endif return (0); } int mm_answer_auth2_read_banner(int sock, Buffer *m) { char *banner; buffer_clear(m); banner = auth2_read_banner(); buffer_put_cstring(m, banner != NULL ? banner : ""); mm_request_send(sock, MONITOR_ANS_AUTH2_READ_BANNER, m); free(banner); return (0); } int mm_answer_authserv(int sock, Buffer *m) { monitor_permit_authentications(1); authctxt->service = buffer_get_string(m, NULL); authctxt->style = buffer_get_string(m, NULL); debug3("%s: service=%s, style=%s", __func__, authctxt->service, authctxt->style); if (strlen(authctxt->style) == 0) { free(authctxt->style); authctxt->style = NULL; } return (0); } int mm_answer_authpassword(int sock, Buffer *m) { static int call_count; char *passwd; int authenticated; u_int plen; if (!options.password_authentication) fatal("%s: password authentication not enabled", __func__); passwd = buffer_get_string(m, &plen); /* Only authenticate if the context is valid */ authenticated = options.password_authentication && auth_password(authctxt, passwd); explicit_bzero(passwd, strlen(passwd)); free(passwd); buffer_clear(m); buffer_put_int(m, authenticated); #ifdef USE_PAM buffer_put_int(m, sshpam_get_maxtries_reached()); #endif debug3("%s: sending result %d", __func__, authenticated); mm_request_send(sock, MONITOR_ANS_AUTHPASSWORD, m); call_count++; if (plen == 0 && call_count == 1) auth_method = "none"; else auth_method = "password"; /* Causes monitor loop to terminate if authenticated */ return (authenticated); } #ifdef BSD_AUTH int mm_answer_bsdauthquery(int sock, Buffer *m) { char *name, *infotxt; u_int numprompts; u_int *echo_on; char **prompts; u_int success; if (!options.kbd_interactive_authentication) fatal("%s: kbd-int authentication not enabled", __func__); success = bsdauth_query(authctxt, &name, &infotxt, &numprompts, &prompts, &echo_on) < 0 ? 0 : 1; buffer_clear(m); buffer_put_int(m, success); if (success) buffer_put_cstring(m, prompts[0]); debug3("%s: sending challenge success: %u", __func__, success); mm_request_send(sock, MONITOR_ANS_BSDAUTHQUERY, m); if (success) { free(name); free(infotxt); free(prompts); free(echo_on); } return (0); } int mm_answer_bsdauthrespond(int sock, Buffer *m) { char *response; int authok; if (!options.kbd_interactive_authentication) fatal("%s: kbd-int authentication not enabled", __func__); if (authctxt->as == NULL) fatal("%s: no bsd auth session", __func__); response = buffer_get_string(m, NULL); authok = options.challenge_response_authentication && auth_userresponse(authctxt->as, response, 0); authctxt->as = NULL; debug3("%s: <%s> = <%d>", __func__, response, authok); free(response); buffer_clear(m); buffer_put_int(m, authok); debug3("%s: sending authenticated: %d", __func__, authok); mm_request_send(sock, MONITOR_ANS_BSDAUTHRESPOND, m); auth_method = "keyboard-interactive"; auth_submethod = "bsdauth"; return (authok != 0); } #endif #ifdef SKEY int mm_answer_skeyquery(int sock, Buffer *m) { struct skey skey; char challenge[1024]; u_int success; success = _compat_skeychallenge(&skey, authctxt->user, challenge, sizeof(challenge)) < 0 ? 0 : 1; buffer_clear(m); buffer_put_int(m, success); if (success) buffer_put_cstring(m, challenge); debug3("%s: sending challenge success: %u", __func__, success); mm_request_send(sock, MONITOR_ANS_SKEYQUERY, m); return (0); } int mm_answer_skeyrespond(int sock, Buffer *m) { char *response; int authok; response = buffer_get_string(m, NULL); authok = (options.challenge_response_authentication && authctxt->valid && skey_haskey(authctxt->pw->pw_name) == 0 && skey_passcheck(authctxt->pw->pw_name, response) != -1); free(response); buffer_clear(m); buffer_put_int(m, authok); debug3("%s: sending authenticated: %d", __func__, authok); mm_request_send(sock, MONITOR_ANS_SKEYRESPOND, m); auth_method = "keyboard-interactive"; auth_submethod = "skey"; return (authok != 0); } #endif #ifdef USE_PAM int mm_answer_pam_start(int sock, Buffer *m) { if (!options.use_pam) fatal("UsePAM not set, but ended up in %s anyway", __func__); start_pam(authctxt); monitor_permit(mon_dispatch, MONITOR_REQ_PAM_ACCOUNT, 1); if (options.kbd_interactive_authentication) monitor_permit(mon_dispatch, MONITOR_REQ_PAM_INIT_CTX, 1); return (0); } int mm_answer_pam_account(int sock, Buffer *m) { u_int ret; if (!options.use_pam) fatal("%s: PAM not enabled", __func__); ret = do_pam_account(); buffer_put_int(m, ret); buffer_put_string(m, buffer_ptr(&loginmsg), buffer_len(&loginmsg)); mm_request_send(sock, MONITOR_ANS_PAM_ACCOUNT, m); return (ret); } static void *sshpam_ctxt, *sshpam_authok; extern KbdintDevice sshpam_device; int mm_answer_pam_init_ctx(int sock, Buffer *m) { debug3("%s", __func__); if (!options.kbd_interactive_authentication) fatal("%s: kbd-int authentication not enabled", __func__); if (sshpam_ctxt != NULL) fatal("%s: already called", __func__); sshpam_ctxt = (sshpam_device.init_ctx)(authctxt); sshpam_authok = NULL; buffer_clear(m); if (sshpam_ctxt != NULL) { monitor_permit(mon_dispatch, MONITOR_REQ_PAM_FREE_CTX, 1); monitor_permit(mon_dispatch, MONITOR_REQ_PAM_QUERY, 1); buffer_put_int(m, 1); } else { buffer_put_int(m, 0); } mm_request_send(sock, MONITOR_ANS_PAM_INIT_CTX, m); return (0); } int mm_answer_pam_query(int sock, Buffer *m) { char *name = NULL, *info = NULL, **prompts = NULL; u_int i, num = 0, *echo_on = 0; int ret; debug3("%s", __func__); sshpam_authok = NULL; if (sshpam_ctxt == NULL) fatal("%s: no context", __func__); ret = (sshpam_device.query)(sshpam_ctxt, &name, &info, &num, &prompts, &echo_on); if (ret == 0 && num == 0) sshpam_authok = sshpam_ctxt; if (num > 1 || name == NULL || info == NULL) fatal("sshpam_device.query failed"); monitor_permit(mon_dispatch, MONITOR_REQ_PAM_RESPOND, 1); buffer_clear(m); buffer_put_int(m, ret); buffer_put_cstring(m, name); free(name); buffer_put_cstring(m, info); free(info); buffer_put_int(m, sshpam_get_maxtries_reached()); buffer_put_int(m, num); for (i = 0; i < num; ++i) { buffer_put_cstring(m, prompts[i]); free(prompts[i]); buffer_put_int(m, echo_on[i]); } free(prompts); free(echo_on); auth_method = "keyboard-interactive"; auth_submethod = "pam"; mm_request_send(sock, MONITOR_ANS_PAM_QUERY, m); return (0); } int mm_answer_pam_respond(int sock, Buffer *m) { char **resp; u_int i, num; int ret; debug3("%s", __func__); if (sshpam_ctxt == NULL) fatal("%s: no context", __func__); sshpam_authok = NULL; num = buffer_get_int(m); if (num > 0) { resp = xcalloc(num, sizeof(char *)); for (i = 0; i < num; ++i) resp[i] = buffer_get_string(m, NULL); ret = (sshpam_device.respond)(sshpam_ctxt, num, resp); for (i = 0; i < num; ++i) free(resp[i]); free(resp); } else { ret = (sshpam_device.respond)(sshpam_ctxt, num, NULL); } buffer_clear(m); buffer_put_int(m, ret); mm_request_send(sock, MONITOR_ANS_PAM_RESPOND, m); auth_method = "keyboard-interactive"; auth_submethod = "pam"; if (ret == 0) sshpam_authok = sshpam_ctxt; return (0); } int mm_answer_pam_free_ctx(int sock, Buffer *m) { int r = sshpam_authok != NULL && sshpam_authok == sshpam_ctxt; debug3("%s", __func__); if (sshpam_ctxt == NULL) fatal("%s: no context", __func__); (sshpam_device.free_ctx)(sshpam_ctxt); sshpam_ctxt = sshpam_authok = NULL; buffer_clear(m); mm_request_send(sock, MONITOR_ANS_PAM_FREE_CTX, m); /* Allow another attempt */ monitor_permit(mon_dispatch, MONITOR_REQ_PAM_INIT_CTX, 1); auth_method = "keyboard-interactive"; auth_submethod = "pam"; return r; } #endif int mm_answer_keyallowed(int sock, Buffer *m) { Key *key; char *cuser, *chost; u_char *blob; u_int bloblen, pubkey_auth_attempt; enum mm_keytype type = 0; int allowed = 0; debug3("%s entering", __func__); type = buffer_get_int(m); cuser = buffer_get_string(m, NULL); chost = buffer_get_string(m, NULL); blob = buffer_get_string(m, &bloblen); pubkey_auth_attempt = buffer_get_int(m); key = key_from_blob(blob, bloblen); debug3("%s: key_from_blob: %p", __func__, key); if (key != NULL && authctxt->valid) { /* These should not make it past the privsep child */ if (key_type_plain(key->type) == KEY_RSA && (datafellows & SSH_BUG_RSASIGMD5) != 0) fatal("%s: passed a SSH_BUG_RSASIGMD5 key", __func__); switch (type) { case MM_USERKEY: allowed = options.pubkey_authentication && !auth2_userkey_already_used(authctxt, key) && match_pattern_list(sshkey_ssh_name(key), options.pubkey_key_types, 0) == 1 && user_key_allowed(authctxt->pw, key, pubkey_auth_attempt); pubkey_auth_info(authctxt, key, NULL); auth_method = "publickey"; if (options.pubkey_authentication && (!pubkey_auth_attempt || allowed != 1)) auth_clear_options(); break; case MM_HOSTKEY: allowed = options.hostbased_authentication && match_pattern_list(sshkey_ssh_name(key), options.hostbased_key_types, 0) == 1 && hostbased_key_allowed(authctxt->pw, cuser, chost, key); pubkey_auth_info(authctxt, key, "client user \"%.100s\", client host \"%.100s\"", cuser, chost); auth_method = "hostbased"; break; default: fatal("%s: unknown key type %d", __func__, type); break; } } debug3("%s: key %p is %s", __func__, key, allowed ? "allowed" : "not allowed"); if (key != NULL) key_free(key); /* clear temporarily storage (used by verify) */ monitor_reset_key_state(); if (allowed) { /* Save temporarily for comparison in verify */ key_blob = blob; key_bloblen = bloblen; key_blobtype = type; hostbased_cuser = cuser; hostbased_chost = chost; } else { /* Log failed attempt */ auth_log(authctxt, 0, 0, auth_method, NULL); free(blob); free(cuser); free(chost); } buffer_clear(m); buffer_put_int(m, allowed); buffer_put_int(m, forced_command != NULL); mm_request_send(sock, MONITOR_ANS_KEYALLOWED, m); return (0); } static int monitor_valid_userblob(u_char *data, u_int datalen) { Buffer b; u_char *p; char *userstyle, *cp; u_int len; int fail = 0; buffer_init(&b); buffer_append(&b, data, datalen); if (datafellows & SSH_OLD_SESSIONID) { p = buffer_ptr(&b); len = buffer_len(&b); if ((session_id2 == NULL) || (len < session_id2_len) || (timingsafe_bcmp(p, session_id2, session_id2_len) != 0)) fail++; buffer_consume(&b, session_id2_len); } else { p = buffer_get_string(&b, &len); if ((session_id2 == NULL) || (len != session_id2_len) || (timingsafe_bcmp(p, session_id2, session_id2_len) != 0)) fail++; free(p); } if (buffer_get_char(&b) != SSH2_MSG_USERAUTH_REQUEST) fail++; cp = buffer_get_cstring(&b, NULL); xasprintf(&userstyle, "%s%s%s", authctxt->user, authctxt->style ? ":" : "", authctxt->style ? authctxt->style : ""); if (strcmp(userstyle, cp) != 0) { logit("wrong user name passed to monitor: " "expected %s != %.100s", userstyle, cp); fail++; } free(userstyle); free(cp); buffer_skip_string(&b); if (datafellows & SSH_BUG_PKAUTH) { if (!buffer_get_char(&b)) fail++; } else { cp = buffer_get_cstring(&b, NULL); if (strcmp("publickey", cp) != 0) fail++; free(cp); if (!buffer_get_char(&b)) fail++; buffer_skip_string(&b); } buffer_skip_string(&b); if (buffer_len(&b) != 0) fail++; buffer_free(&b); return (fail == 0); } static int monitor_valid_hostbasedblob(u_char *data, u_int datalen, char *cuser, char *chost) { Buffer b; char *p, *userstyle; u_int len; int fail = 0; buffer_init(&b); buffer_append(&b, data, datalen); p = buffer_get_string(&b, &len); if ((session_id2 == NULL) || (len != session_id2_len) || (timingsafe_bcmp(p, session_id2, session_id2_len) != 0)) fail++; free(p); if (buffer_get_char(&b) != SSH2_MSG_USERAUTH_REQUEST) fail++; p = buffer_get_cstring(&b, NULL); xasprintf(&userstyle, "%s%s%s", authctxt->user, authctxt->style ? ":" : "", authctxt->style ? authctxt->style : ""); if (strcmp(userstyle, p) != 0) { logit("wrong user name passed to monitor: expected %s != %.100s", userstyle, p); fail++; } free(userstyle); free(p); buffer_skip_string(&b); /* service */ p = buffer_get_cstring(&b, NULL); if (strcmp(p, "hostbased") != 0) fail++; free(p); buffer_skip_string(&b); /* pkalg */ buffer_skip_string(&b); /* pkblob */ /* verify client host, strip trailing dot if necessary */ p = buffer_get_string(&b, NULL); if (((len = strlen(p)) > 0) && p[len - 1] == '.') p[len - 1] = '\0'; if (strcmp(p, chost) != 0) fail++; free(p); /* verify client user */ p = buffer_get_string(&b, NULL); if (strcmp(p, cuser) != 0) fail++; free(p); if (buffer_len(&b) != 0) fail++; buffer_free(&b); return (fail == 0); } int mm_answer_keyverify(int sock, Buffer *m) { Key *key; u_char *signature, *data, *blob; u_int signaturelen, datalen, bloblen; int verified = 0; int valid_data = 0; blob = buffer_get_string(m, &bloblen); signature = buffer_get_string(m, &signaturelen); data = buffer_get_string(m, &datalen); if (hostbased_cuser == NULL || hostbased_chost == NULL || !monitor_allowed_key(blob, bloblen)) fatal("%s: bad key, not previously allowed", __func__); key = key_from_blob(blob, bloblen); if (key == NULL) fatal("%s: bad public key blob", __func__); switch (key_blobtype) { case MM_USERKEY: valid_data = monitor_valid_userblob(data, datalen); break; case MM_HOSTKEY: valid_data = monitor_valid_hostbasedblob(data, datalen, hostbased_cuser, hostbased_chost); break; default: valid_data = 0; break; } if (!valid_data) fatal("%s: bad signature data blob", __func__); verified = key_verify(key, signature, signaturelen, data, datalen); debug3("%s: key %p signature %s", __func__, key, (verified == 1) ? "verified" : "unverified"); /* If auth was successful then record key to ensure it isn't reused */ if (verified == 1 && key_blobtype == MM_USERKEY) auth2_record_userkey(authctxt, key); else key_free(key); free(blob); free(signature); free(data); auth_method = key_blobtype == MM_USERKEY ? "publickey" : "hostbased"; monitor_reset_key_state(); buffer_clear(m); buffer_put_int(m, verified); mm_request_send(sock, MONITOR_ANS_KEYVERIFY, m); return (verified == 1); } static void mm_record_login(Session *s, struct passwd *pw) { struct ssh *ssh = active_state; /* XXX */ socklen_t fromlen; struct sockaddr_storage from; /* * Get IP address of client. If the connection is not a socket, let * the address be 0.0.0.0. */ memset(&from, 0, sizeof(from)); fromlen = sizeof(from); if (packet_connection_is_on_socket()) { if (getpeername(packet_get_connection_in(), (struct sockaddr *)&from, &fromlen) < 0) { debug("getpeername: %.100s", strerror(errno)); cleanup_exit(255); } } /* Record that there was a login on that tty from the remote host. */ record_login(s->pid, s->tty, pw->pw_name, pw->pw_uid, session_get_remote_name_or_ip(ssh, utmp_len, options.use_dns), (struct sockaddr *)&from, fromlen); } static void mm_session_close(Session *s) { debug3("%s: session %d pid %ld", __func__, s->self, (long)s->pid); if (s->ttyfd != -1) { debug3("%s: tty %s ptyfd %d", __func__, s->tty, s->ptyfd); session_pty_cleanup2(s); } session_unused(s->self); } int mm_answer_pty(int sock, Buffer *m) { extern struct monitor *pmonitor; Session *s; int res, fd0; debug3("%s entering", __func__); buffer_clear(m); s = session_new(); if (s == NULL) goto error; s->authctxt = authctxt; s->pw = authctxt->pw; s->pid = pmonitor->m_pid; res = pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty)); if (res == 0) goto error; pty_setowner(authctxt->pw, s->tty); buffer_put_int(m, 1); buffer_put_cstring(m, s->tty); /* We need to trick ttyslot */ if (dup2(s->ttyfd, 0) == -1) fatal("%s: dup2", __func__); mm_record_login(s, authctxt->pw); /* Now we can close the file descriptor again */ close(0); /* send messages generated by record_login */ buffer_put_string(m, buffer_ptr(&loginmsg), buffer_len(&loginmsg)); buffer_clear(&loginmsg); mm_request_send(sock, MONITOR_ANS_PTY, m); if (mm_send_fd(sock, s->ptyfd) == -1 || mm_send_fd(sock, s->ttyfd) == -1) fatal("%s: send fds failed", __func__); /* make sure nothing uses fd 0 */ if ((fd0 = open(_PATH_DEVNULL, O_RDONLY)) < 0) fatal("%s: open(/dev/null): %s", __func__, strerror(errno)); if (fd0 != 0) error("%s: fd0 %d != 0", __func__, fd0); /* slave is not needed */ close(s->ttyfd); s->ttyfd = s->ptyfd; /* no need to dup() because nobody closes ptyfd */ s->ptymaster = s->ptyfd; debug3("%s: tty %s ptyfd %d", __func__, s->tty, s->ttyfd); return (0); error: if (s != NULL) mm_session_close(s); buffer_put_int(m, 0); mm_request_send(sock, MONITOR_ANS_PTY, m); return (0); } int mm_answer_pty_cleanup(int sock, Buffer *m) { Session *s; char *tty; debug3("%s entering", __func__); tty = buffer_get_string(m, NULL); if ((s = session_by_tty(tty)) != NULL) mm_session_close(s); buffer_clear(m); free(tty); return (0); } int mm_answer_term(int sock, Buffer *req) { extern struct monitor *pmonitor; int res, status; debug3("%s: tearing down sessions", __func__); /* The child is terminating */ session_destroy_all(&mm_session_close); #ifdef USE_PAM if (options.use_pam) sshpam_cleanup(); #endif while (waitpid(pmonitor->m_pid, &status, 0) == -1) if (errno != EINTR) exit(1); res = WIFEXITED(status) ? WEXITSTATUS(status) : 1; /* Terminate process */ exit(res); } #ifdef SSH_AUDIT_EVENTS /* Report that an audit event occurred */ int mm_answer_audit_event(int socket, Buffer *m) { ssh_audit_event_t event; debug3("%s entering", __func__); event = buffer_get_int(m); switch(event) { case SSH_AUTH_FAIL_PUBKEY: case SSH_AUTH_FAIL_HOSTBASED: case SSH_AUTH_FAIL_GSSAPI: case SSH_LOGIN_EXCEED_MAXTRIES: case SSH_LOGIN_ROOT_DENIED: case SSH_CONNECTION_CLOSE: case SSH_INVALID_USER: audit_event(event); break; default: fatal("Audit event type %d not permitted", event); } return (0); } int mm_answer_audit_command(int socket, Buffer *m) { u_int len; char *cmd; debug3("%s entering", __func__); cmd = buffer_get_string(m, &len); /* sanity check command, if so how? */ audit_run_command(cmd); free(cmd); return (0); } #endif /* SSH_AUDIT_EVENTS */ void monitor_apply_keystate(struct monitor *pmonitor) { struct ssh *ssh = active_state; /* XXX */ struct kex *kex; int r; debug3("%s: packet_set_state", __func__); if ((r = ssh_packet_set_state(ssh, child_state)) != 0) fatal("%s: packet_set_state: %s", __func__, ssh_err(r)); sshbuf_free(child_state); child_state = NULL; if ((kex = ssh->kex) != NULL) { /* XXX set callbacks */ #ifdef WITH_OPENSSL kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; kex->kex[KEX_DH_GRP14_SHA256] = kexdh_server; kex->kex[KEX_DH_GRP16_SHA512] = kexdh_server; kex->kex[KEX_DH_GRP18_SHA512] = kexdh_server; kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; # ifdef OPENSSL_HAS_ECC kex->kex[KEX_ECDH_SHA2] = kexecdh_server; # endif #endif /* WITH_OPENSSL */ kex->kex[KEX_C25519_SHA256] = kexc25519_server; kex->load_host_public_key=&get_hostkey_public_by_type; kex->load_host_private_key=&get_hostkey_private_by_type; kex->host_key_index=&get_hostkey_index; kex->sign = sshd_hostkey_sign; } } /* This function requries careful sanity checking */ void mm_get_keystate(struct monitor *pmonitor) { debug3("%s: Waiting for new keys", __func__); if ((child_state = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); mm_request_receive_expect(pmonitor->m_sendfd, MONITOR_REQ_KEYEXPORT, child_state); debug3("%s: GOT new keys", __func__); } /* XXX */ #define FD_CLOSEONEXEC(x) do { \ if (fcntl(x, F_SETFD, FD_CLOEXEC) == -1) \ fatal("fcntl(%d, F_SETFD)", x); \ } while (0) static void monitor_openfds(struct monitor *mon, int do_logfds) { int pair[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) fatal("%s: socketpair: %s", __func__, strerror(errno)); FD_CLOSEONEXEC(pair[0]); FD_CLOSEONEXEC(pair[1]); mon->m_recvfd = pair[0]; mon->m_sendfd = pair[1]; if (do_logfds) { if (pipe(pair) == -1) fatal("%s: pipe: %s", __func__, strerror(errno)); FD_CLOSEONEXEC(pair[0]); FD_CLOSEONEXEC(pair[1]); mon->m_log_recvfd = pair[0]; mon->m_log_sendfd = pair[1]; } else mon->m_log_recvfd = mon->m_log_sendfd = -1; } #define MM_MEMSIZE 65536 struct monitor * monitor_init(void) { struct monitor *mon; mon = xcalloc(1, sizeof(*mon)); monitor_openfds(mon, 1); return mon; } void monitor_reinit(struct monitor *mon) { monitor_openfds(mon, 0); } #ifdef GSSAPI int mm_answer_gss_setup_ctx(int sock, Buffer *m) { gss_OID_desc goid; OM_uint32 major; u_int len; if (!options.gss_authentication) fatal("%s: GSSAPI authentication not enabled", __func__); goid.elements = buffer_get_string(m, &len); goid.length = len; major = ssh_gssapi_server_ctx(&gsscontext, &goid); free(goid.elements); buffer_clear(m); buffer_put_int(m, major); mm_request_send(sock, MONITOR_ANS_GSSSETUP, m); /* Now we have a context, enable the step */ monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 1); return (0); } int mm_answer_gss_accept_ctx(int sock, Buffer *m) { gss_buffer_desc in; gss_buffer_desc out = GSS_C_EMPTY_BUFFER; OM_uint32 major, minor; OM_uint32 flags = 0; /* GSI needs this */ u_int len; if (!options.gss_authentication) fatal("%s: GSSAPI authentication not enabled", __func__); in.value = buffer_get_string(m, &len); in.length = len; major = ssh_gssapi_accept_ctx(gsscontext, &in, &out, &flags); free(in.value); buffer_clear(m); buffer_put_int(m, major); buffer_put_string(m, out.value, out.length); buffer_put_int(m, flags); mm_request_send(sock, MONITOR_ANS_GSSSTEP, m); gss_release_buffer(&minor, &out); if (major == GSS_S_COMPLETE) { monitor_permit(mon_dispatch, MONITOR_REQ_GSSSTEP, 0); monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); monitor_permit(mon_dispatch, MONITOR_REQ_GSSCHECKMIC, 1); } return (0); } int mm_answer_gss_checkmic(int sock, Buffer *m) { gss_buffer_desc gssbuf, mic; OM_uint32 ret; u_int len; if (!options.gss_authentication) fatal("%s: GSSAPI authentication not enabled", __func__); gssbuf.value = buffer_get_string(m, &len); gssbuf.length = len; mic.value = buffer_get_string(m, &len); mic.length = len; ret = ssh_gssapi_checkmic(gsscontext, &gssbuf, &mic); free(gssbuf.value); free(mic.value); buffer_clear(m); buffer_put_int(m, ret); mm_request_send(sock, MONITOR_ANS_GSSCHECKMIC, m); if (!GSS_ERROR(ret)) monitor_permit(mon_dispatch, MONITOR_REQ_GSSUSEROK, 1); return (0); } int mm_answer_gss_userok(int sock, Buffer *m) { int authenticated; if (!options.gss_authentication) fatal("%s: GSSAPI authentication not enabled", __func__); authenticated = authctxt->valid && ssh_gssapi_userok(authctxt->user); buffer_clear(m); buffer_put_int(m, authenticated); debug3("%s: sending result %d", __func__, authenticated); mm_request_send(sock, MONITOR_ANS_GSSUSEROK, m); auth_method = "gssapi-with-mic"; /* Monitor loop will terminate if authenticated */ return (authenticated); } #endif /* GSSAPI */ diff --git a/crypto/openssh/mux.c b/crypto/openssh/mux.c index b21df1562e3d..74644a27844a 100644 --- a/crypto/openssh/mux.c +++ b/crypto/openssh/mux.c @@ -1,2271 +1,2268 @@ -/* $OpenBSD: mux.c,v 1.63 2016/10/19 23:21:56 dtucker Exp $ */ +/* $OpenBSD: mux.c,v 1.64 2017/01/21 11:32:04 guenther Exp $ */ /* * Copyright (c) 2002-2008 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* ssh session multiplexing support */ /* * TODO: * - Better signalling from master to slave, especially passing of * error messages * - Better fall-back from mux slave error to new connection. * - ExitOnForwardingFailure * - Maybe extension mechanisms for multi-X11/multi-agent forwarding * - Support ~^Z in mux slaves. * - Inspect or control sessions in master. * - If we ever support the "signal" channel request, send signals on * sessions in master. */ #include "includes.h" __RCSID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_PATHS_H #include #endif #ifdef HAVE_POLL_H #include #else # ifdef HAVE_SYS_POLL_H # include # endif #endif #ifdef HAVE_UTIL_H # include #endif #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "log.h" #include "ssh.h" #include "ssh2.h" #include "pathnames.h" #include "misc.h" #include "match.h" #include "buffer.h" #include "channels.h" #include "msg.h" #include "packet.h" #include "monitor_fdpass.h" #include "sshpty.h" #include "key.h" #include "readconf.h" #include "clientloop.h" #include "ssherr.h" /* from ssh.c */ extern int tty_flag; extern Options options; extern int stdin_null_flag; extern char *host; extern int subsystem_flag; extern Buffer command; extern volatile sig_atomic_t quit_pending; /* Context for session open confirmation callback */ struct mux_session_confirm_ctx { u_int want_tty; u_int want_subsys; u_int want_x_fwd; u_int want_agent_fwd; Buffer cmd; char *term; struct termios tio; char **env; u_int rid; }; /* Context for stdio fwd open confirmation callback */ struct mux_stdio_confirm_ctx { u_int rid; }; /* Context for global channel callback */ struct mux_channel_confirm_ctx { u_int cid; /* channel id */ u_int rid; /* request id */ int fid; /* forward id */ }; /* fd to control socket */ int muxserver_sock = -1; /* client request id */ u_int muxclient_request_id = 0; /* Multiplexing control command */ u_int muxclient_command = 0; /* Set when signalled. */ static volatile sig_atomic_t muxclient_terminate = 0; /* PID of multiplex server */ static u_int muxserver_pid = 0; static Channel *mux_listener_channel = NULL; struct mux_master_state { int hello_rcvd; }; /* mux protocol messages */ #define MUX_MSG_HELLO 0x00000001 #define MUX_C_NEW_SESSION 0x10000002 #define MUX_C_ALIVE_CHECK 0x10000004 #define MUX_C_TERMINATE 0x10000005 #define MUX_C_OPEN_FWD 0x10000006 #define MUX_C_CLOSE_FWD 0x10000007 #define MUX_C_NEW_STDIO_FWD 0x10000008 #define MUX_C_STOP_LISTENING 0x10000009 #define MUX_C_PROXY 0x1000000f #define MUX_S_OK 0x80000001 #define MUX_S_PERMISSION_DENIED 0x80000002 #define MUX_S_FAILURE 0x80000003 #define MUX_S_EXIT_MESSAGE 0x80000004 #define MUX_S_ALIVE 0x80000005 #define MUX_S_SESSION_OPENED 0x80000006 #define MUX_S_REMOTE_PORT 0x80000007 #define MUX_S_TTY_ALLOC_FAIL 0x80000008 #define MUX_S_PROXY 0x8000000f /* type codes for MUX_C_OPEN_FWD and MUX_C_CLOSE_FWD */ #define MUX_FWD_LOCAL 1 #define MUX_FWD_REMOTE 2 #define MUX_FWD_DYNAMIC 3 static void mux_session_confirm(int, int, void *); static void mux_stdio_confirm(int, int, void *); static int process_mux_master_hello(u_int, Channel *, Buffer *, Buffer *); static int process_mux_new_session(u_int, Channel *, Buffer *, Buffer *); static int process_mux_alive_check(u_int, Channel *, Buffer *, Buffer *); static int process_mux_terminate(u_int, Channel *, Buffer *, Buffer *); static int process_mux_open_fwd(u_int, Channel *, Buffer *, Buffer *); static int process_mux_close_fwd(u_int, Channel *, Buffer *, Buffer *); static int process_mux_stdio_fwd(u_int, Channel *, Buffer *, Buffer *); static int process_mux_stop_listening(u_int, Channel *, Buffer *, Buffer *); static int process_mux_proxy(u_int, Channel *, Buffer *, Buffer *); static const struct { u_int type; int (*handler)(u_int, Channel *, Buffer *, Buffer *); } mux_master_handlers[] = { { MUX_MSG_HELLO, process_mux_master_hello }, { MUX_C_NEW_SESSION, process_mux_new_session }, { MUX_C_ALIVE_CHECK, process_mux_alive_check }, { MUX_C_TERMINATE, process_mux_terminate }, { MUX_C_OPEN_FWD, process_mux_open_fwd }, { MUX_C_CLOSE_FWD, process_mux_close_fwd }, { MUX_C_NEW_STDIO_FWD, process_mux_stdio_fwd }, { MUX_C_STOP_LISTENING, process_mux_stop_listening }, { MUX_C_PROXY, process_mux_proxy }, { 0, NULL } }; /* Cleanup callback fired on closure of mux slave _session_ channel */ /* ARGSUSED */ static void mux_master_session_cleanup_cb(int cid, void *unused) { Channel *cc, *c = channel_by_id(cid); debug3("%s: entering for channel %d", __func__, cid); if (c == NULL) fatal("%s: channel_by_id(%i) == NULL", __func__, cid); if (c->ctl_chan != -1) { if ((cc = channel_by_id(c->ctl_chan)) == NULL) fatal("%s: channel %d missing control channel %d", __func__, c->self, c->ctl_chan); c->ctl_chan = -1; cc->remote_id = -1; chan_rcvd_oclose(cc); } channel_cancel_cleanup(c->self); } /* Cleanup callback fired on closure of mux slave _control_ channel */ /* ARGSUSED */ static void mux_master_control_cleanup_cb(int cid, void *unused) { Channel *sc, *c = channel_by_id(cid); debug3("%s: entering for channel %d", __func__, cid); if (c == NULL) fatal("%s: channel_by_id(%i) == NULL", __func__, cid); if (c->remote_id != -1) { if ((sc = channel_by_id(c->remote_id)) == NULL) fatal("%s: channel %d missing session channel %d", __func__, c->self, c->remote_id); c->remote_id = -1; sc->ctl_chan = -1; if (sc->type != SSH_CHANNEL_OPEN && sc->type != SSH_CHANNEL_OPENING) { debug2("%s: channel %d: not open", __func__, sc->self); chan_mark_dead(sc); } else { if (sc->istate == CHAN_INPUT_OPEN) chan_read_failed(sc); if (sc->ostate == CHAN_OUTPUT_OPEN) chan_write_failed(sc); } } channel_cancel_cleanup(c->self); } /* Check mux client environment variables before passing them to mux master. */ static int env_permitted(char *env) { int i, ret; char name[1024], *cp; if ((cp = strchr(env, '=')) == NULL || cp == env) return 0; ret = snprintf(name, sizeof(name), "%.*s", (int)(cp - env), env); if (ret <= 0 || (size_t)ret >= sizeof(name)) { error("env_permitted: name '%.100s...' too long", env); return 0; } for (i = 0; i < options.num_send_env; i++) if (match_pattern(name, options.send_env[i])) return 1; return 0; } /* Mux master protocol message handlers */ static int process_mux_master_hello(u_int rid, Channel *c, Buffer *m, Buffer *r) { u_int ver; struct mux_master_state *state = (struct mux_master_state *)c->mux_ctx; if (state == NULL) fatal("%s: channel %d: c->mux_ctx == NULL", __func__, c->self); if (state->hello_rcvd) { error("%s: HELLO received twice", __func__); return -1; } if (buffer_get_int_ret(&ver, m) != 0) { malf: error("%s: malformed message", __func__); return -1; } if (ver != SSHMUX_VER) { error("Unsupported multiplexing protocol version %d " "(expected %d)", ver, SSHMUX_VER); return -1; } debug2("%s: channel %d slave version %u", __func__, c->self, ver); /* No extensions are presently defined */ while (buffer_len(m) > 0) { char *name = buffer_get_string_ret(m, NULL); char *value = buffer_get_string_ret(m, NULL); if (name == NULL || value == NULL) { free(name); free(value); goto malf; } debug2("Unrecognised slave extension \"%s\"", name); free(name); free(value); } state->hello_rcvd = 1; return 0; } static int process_mux_new_session(u_int rid, Channel *c, Buffer *m, Buffer *r) { Channel *nc; struct mux_session_confirm_ctx *cctx; char *reserved, *cmd, *cp; u_int i, j, len, env_len, escape_char, window, packetmax; int new_fd[3]; /* Reply for SSHMUX_COMMAND_OPEN */ cctx = xcalloc(1, sizeof(*cctx)); cctx->term = NULL; cctx->rid = rid; cmd = reserved = NULL; cctx->env = NULL; env_len = 0; if ((reserved = buffer_get_string_ret(m, NULL)) == NULL || buffer_get_int_ret(&cctx->want_tty, m) != 0 || buffer_get_int_ret(&cctx->want_x_fwd, m) != 0 || buffer_get_int_ret(&cctx->want_agent_fwd, m) != 0 || buffer_get_int_ret(&cctx->want_subsys, m) != 0 || buffer_get_int_ret(&escape_char, m) != 0 || (cctx->term = buffer_get_string_ret(m, &len)) == NULL || (cmd = buffer_get_string_ret(m, &len)) == NULL) { malf: free(cmd); free(reserved); for (j = 0; j < env_len; j++) free(cctx->env[j]); free(cctx->env); free(cctx->term); free(cctx); error("%s: malformed message", __func__); return -1; } free(reserved); reserved = NULL; while (buffer_len(m) > 0) { #define MUX_MAX_ENV_VARS 4096 if ((cp = buffer_get_string_ret(m, &len)) == NULL) goto malf; if (!env_permitted(cp)) { free(cp); continue; } cctx->env = xreallocarray(cctx->env, env_len + 2, sizeof(*cctx->env)); cctx->env[env_len++] = cp; cctx->env[env_len] = NULL; if (env_len > MUX_MAX_ENV_VARS) { error(">%d environment variables received, ignoring " "additional", MUX_MAX_ENV_VARS); break; } } debug2("%s: channel %d: request tty %d, X %d, agent %d, subsys %d, " "term \"%s\", cmd \"%s\", env %u", __func__, c->self, cctx->want_tty, cctx->want_x_fwd, cctx->want_agent_fwd, cctx->want_subsys, cctx->term, cmd, env_len); buffer_init(&cctx->cmd); buffer_append(&cctx->cmd, cmd, strlen(cmd)); free(cmd); cmd = NULL; /* Gather fds from client */ for(i = 0; i < 3; i++) { if ((new_fd[i] = mm_receive_fd(c->sock)) == -1) { error("%s: failed to receive fd %d from slave", __func__, i); for (j = 0; j < i; j++) close(new_fd[j]); for (j = 0; j < env_len; j++) free(cctx->env[j]); free(cctx->env); free(cctx->term); buffer_free(&cctx->cmd); free(cctx); /* prepare reply */ buffer_put_int(r, MUX_S_FAILURE); buffer_put_int(r, rid); buffer_put_cstring(r, "did not receive file descriptors"); return -1; } } debug3("%s: got fds stdin %d, stdout %d, stderr %d", __func__, new_fd[0], new_fd[1], new_fd[2]); /* XXX support multiple child sessions in future */ if (c->remote_id != -1) { debug2("%s: session already open", __func__); /* prepare reply */ buffer_put_int(r, MUX_S_FAILURE); buffer_put_int(r, rid); buffer_put_cstring(r, "Multiple sessions not supported"); cleanup: close(new_fd[0]); close(new_fd[1]); close(new_fd[2]); free(cctx->term); if (env_len != 0) { for (i = 0; i < env_len; i++) free(cctx->env[i]); free(cctx->env); } buffer_free(&cctx->cmd); free(cctx); return 0; } if (options.control_master == SSHCTL_MASTER_ASK || options.control_master == SSHCTL_MASTER_AUTO_ASK) { if (!ask_permission("Allow shared connection to %s? ", host)) { debug2("%s: session refused by user", __func__); /* prepare reply */ buffer_put_int(r, MUX_S_PERMISSION_DENIED); buffer_put_int(r, rid); buffer_put_cstring(r, "Permission denied"); goto cleanup; } } /* Try to pick up ttymodes from client before it goes raw */ if (cctx->want_tty && tcgetattr(new_fd[0], &cctx->tio) == -1) error("%s: tcgetattr: %s", __func__, strerror(errno)); /* enable nonblocking unless tty */ if (!isatty(new_fd[0])) set_nonblock(new_fd[0]); if (!isatty(new_fd[1])) set_nonblock(new_fd[1]); if (!isatty(new_fd[2])) set_nonblock(new_fd[2]); window = CHAN_SES_WINDOW_DEFAULT; packetmax = CHAN_SES_PACKET_DEFAULT; if (cctx->want_tty) { window >>= 1; packetmax >>= 1; } nc = channel_new("session", SSH_CHANNEL_OPENING, new_fd[0], new_fd[1], new_fd[2], window, packetmax, CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0); nc->ctl_chan = c->self; /* link session -> control channel */ c->remote_id = nc->self; /* link control -> session channel */ if (cctx->want_tty && escape_char != 0xffffffff) { channel_register_filter(nc->self, client_simple_escape_filter, NULL, client_filter_cleanup, client_new_escape_filter_ctx((int)escape_char)); } debug2("%s: channel_new: %d linked to control channel %d", __func__, nc->self, nc->ctl_chan); channel_send_open(nc->self); channel_register_open_confirm(nc->self, mux_session_confirm, cctx); c->mux_pause = 1; /* stop handling messages until open_confirm done */ channel_register_cleanup(nc->self, mux_master_session_cleanup_cb, 1); /* reply is deferred, sent by mux_session_confirm */ return 0; } static int process_mux_alive_check(u_int rid, Channel *c, Buffer *m, Buffer *r) { debug2("%s: channel %d: alive check", __func__, c->self); /* prepare reply */ buffer_put_int(r, MUX_S_ALIVE); buffer_put_int(r, rid); buffer_put_int(r, (u_int)getpid()); return 0; } static int process_mux_terminate(u_int rid, Channel *c, Buffer *m, Buffer *r) { debug2("%s: channel %d: terminate request", __func__, c->self); if (options.control_master == SSHCTL_MASTER_ASK || options.control_master == SSHCTL_MASTER_AUTO_ASK) { if (!ask_permission("Terminate shared connection to %s? ", host)) { debug2("%s: termination refused by user", __func__); buffer_put_int(r, MUX_S_PERMISSION_DENIED); buffer_put_int(r, rid); buffer_put_cstring(r, "Permission denied"); return 0; } } quit_pending = 1; buffer_put_int(r, MUX_S_OK); buffer_put_int(r, rid); /* XXX exit happens too soon - message never makes it to client */ return 0; } static char * format_forward(u_int ftype, struct Forward *fwd) { char *ret; switch (ftype) { case MUX_FWD_LOCAL: xasprintf(&ret, "local forward %.200s:%d -> %.200s:%d", (fwd->listen_path != NULL) ? fwd->listen_path : (fwd->listen_host == NULL) ? (options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") : fwd->listen_host, fwd->listen_port, (fwd->connect_path != NULL) ? fwd->connect_path : fwd->connect_host, fwd->connect_port); break; case MUX_FWD_DYNAMIC: xasprintf(&ret, "dynamic forward %.200s:%d -> *", (fwd->listen_host == NULL) ? (options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") : fwd->listen_host, fwd->listen_port); break; case MUX_FWD_REMOTE: xasprintf(&ret, "remote forward %.200s:%d -> %.200s:%d", (fwd->listen_path != NULL) ? fwd->listen_path : (fwd->listen_host == NULL) ? "LOCALHOST" : fwd->listen_host, fwd->listen_port, (fwd->connect_path != NULL) ? fwd->connect_path : fwd->connect_host, fwd->connect_port); break; default: fatal("%s: unknown forward type %u", __func__, ftype); } return ret; } static int compare_host(const char *a, const char *b) { if (a == NULL && b == NULL) return 1; if (a == NULL || b == NULL) return 0; return strcmp(a, b) == 0; } static int compare_forward(struct Forward *a, struct Forward *b) { if (!compare_host(a->listen_host, b->listen_host)) return 0; if (!compare_host(a->listen_path, b->listen_path)) return 0; if (a->listen_port != b->listen_port) return 0; if (!compare_host(a->connect_host, b->connect_host)) return 0; if (!compare_host(a->connect_path, b->connect_path)) return 0; if (a->connect_port != b->connect_port) return 0; return 1; } static void mux_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) { struct mux_channel_confirm_ctx *fctx = ctxt; char *failmsg = NULL; struct Forward *rfwd; Channel *c; Buffer out; if ((c = channel_by_id(fctx->cid)) == NULL) { /* no channel for reply */ error("%s: unknown channel", __func__); return; } buffer_init(&out); if (fctx->fid >= options.num_remote_forwards || (options.remote_forwards[fctx->fid].connect_path == NULL && options.remote_forwards[fctx->fid].connect_host == NULL)) { xasprintf(&failmsg, "unknown forwarding id %d", fctx->fid); goto fail; } rfwd = &options.remote_forwards[fctx->fid]; debug("%s: %s for: listen %d, connect %s:%d", __func__, type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure", rfwd->listen_port, rfwd->connect_path ? rfwd->connect_path : rfwd->connect_host, rfwd->connect_port); if (type == SSH2_MSG_REQUEST_SUCCESS) { if (rfwd->listen_port == 0) { rfwd->allocated_port = packet_get_int(); debug("Allocated port %u for mux remote forward" " to %s:%d", rfwd->allocated_port, rfwd->connect_host, rfwd->connect_port); buffer_put_int(&out, MUX_S_REMOTE_PORT); buffer_put_int(&out, fctx->rid); buffer_put_int(&out, rfwd->allocated_port); channel_update_permitted_opens(rfwd->handle, rfwd->allocated_port); } else { buffer_put_int(&out, MUX_S_OK); buffer_put_int(&out, fctx->rid); } goto out; } else { if (rfwd->listen_port == 0) channel_update_permitted_opens(rfwd->handle, -1); if (rfwd->listen_path != NULL) xasprintf(&failmsg, "remote port forwarding failed for " "listen path %s", rfwd->listen_path); else xasprintf(&failmsg, "remote port forwarding failed for " "listen port %d", rfwd->listen_port); debug2("%s: clearing registered forwarding for listen %d, " "connect %s:%d", __func__, rfwd->listen_port, rfwd->connect_path ? rfwd->connect_path : rfwd->connect_host, rfwd->connect_port); free(rfwd->listen_host); free(rfwd->listen_path); free(rfwd->connect_host); free(rfwd->connect_path); memset(rfwd, 0, sizeof(*rfwd)); } fail: error("%s: %s", __func__, failmsg); buffer_put_int(&out, MUX_S_FAILURE); buffer_put_int(&out, fctx->rid); buffer_put_cstring(&out, failmsg); free(failmsg); out: buffer_put_string(&c->output, buffer_ptr(&out), buffer_len(&out)); buffer_free(&out); if (c->mux_pause <= 0) fatal("%s: mux_pause %d", __func__, c->mux_pause); c->mux_pause = 0; /* start processing messages again */ } static int process_mux_open_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) { struct Forward fwd; char *fwd_desc = NULL; char *listen_addr, *connect_addr; u_int ftype; u_int lport, cport; int i, ret = 0, freefwd = 1; memset(&fwd, 0, sizeof(fwd)); /* XXX - lport/cport check redundant */ if (buffer_get_int_ret(&ftype, m) != 0 || (listen_addr = buffer_get_string_ret(m, NULL)) == NULL || buffer_get_int_ret(&lport, m) != 0 || (connect_addr = buffer_get_string_ret(m, NULL)) == NULL || buffer_get_int_ret(&cport, m) != 0 || (lport != (u_int)PORT_STREAMLOCAL && lport > 65535) || (cport != (u_int)PORT_STREAMLOCAL && cport > 65535)) { error("%s: malformed message", __func__); ret = -1; goto out; } if (*listen_addr == '\0') { free(listen_addr); listen_addr = NULL; } if (*connect_addr == '\0') { free(connect_addr); connect_addr = NULL; } memset(&fwd, 0, sizeof(fwd)); fwd.listen_port = lport; if (fwd.listen_port == PORT_STREAMLOCAL) fwd.listen_path = listen_addr; else fwd.listen_host = listen_addr; fwd.connect_port = cport; if (fwd.connect_port == PORT_STREAMLOCAL) fwd.connect_path = connect_addr; else fwd.connect_host = connect_addr; debug2("%s: channel %d: request %s", __func__, c->self, (fwd_desc = format_forward(ftype, &fwd))); if (ftype != MUX_FWD_LOCAL && ftype != MUX_FWD_REMOTE && ftype != MUX_FWD_DYNAMIC) { logit("%s: invalid forwarding type %u", __func__, ftype); invalid: free(listen_addr); free(connect_addr); buffer_put_int(r, MUX_S_FAILURE); buffer_put_int(r, rid); buffer_put_cstring(r, "Invalid forwarding request"); return 0; } if (ftype == MUX_FWD_DYNAMIC && fwd.listen_path) { logit("%s: streamlocal and dynamic forwards " "are mutually exclusive", __func__); goto invalid; } if (fwd.listen_port != PORT_STREAMLOCAL && fwd.listen_port >= 65536) { logit("%s: invalid listen port %u", __func__, fwd.listen_port); goto invalid; } if ((fwd.connect_port != PORT_STREAMLOCAL && fwd.connect_port >= 65536) || (ftype != MUX_FWD_DYNAMIC && ftype != MUX_FWD_REMOTE && fwd.connect_port == 0)) { logit("%s: invalid connect port %u", __func__, fwd.connect_port); goto invalid; } if (ftype != MUX_FWD_DYNAMIC && fwd.connect_host == NULL && fwd.connect_path == NULL) { logit("%s: missing connect host", __func__); goto invalid; } /* Skip forwards that have already been requested */ switch (ftype) { case MUX_FWD_LOCAL: case MUX_FWD_DYNAMIC: for (i = 0; i < options.num_local_forwards; i++) { if (compare_forward(&fwd, options.local_forwards + i)) { exists: debug2("%s: found existing forwarding", __func__); buffer_put_int(r, MUX_S_OK); buffer_put_int(r, rid); goto out; } } break; case MUX_FWD_REMOTE: for (i = 0; i < options.num_remote_forwards; i++) { if (compare_forward(&fwd, options.remote_forwards + i)) { if (fwd.listen_port != 0) goto exists; debug2("%s: found allocated port", __func__); buffer_put_int(r, MUX_S_REMOTE_PORT); buffer_put_int(r, rid); buffer_put_int(r, options.remote_forwards[i].allocated_port); goto out; } } break; } if (options.control_master == SSHCTL_MASTER_ASK || options.control_master == SSHCTL_MASTER_AUTO_ASK) { if (!ask_permission("Open %s on %s?", fwd_desc, host)) { debug2("%s: forwarding refused by user", __func__); buffer_put_int(r, MUX_S_PERMISSION_DENIED); buffer_put_int(r, rid); buffer_put_cstring(r, "Permission denied"); goto out; } } if (ftype == MUX_FWD_LOCAL || ftype == MUX_FWD_DYNAMIC) { if (!channel_setup_local_fwd_listener(&fwd, &options.fwd_opts)) { fail: logit("slave-requested %s failed", fwd_desc); buffer_put_int(r, MUX_S_FAILURE); buffer_put_int(r, rid); buffer_put_cstring(r, "Port forwarding failed"); goto out; } add_local_forward(&options, &fwd); freefwd = 0; } else { struct mux_channel_confirm_ctx *fctx; fwd.handle = channel_request_remote_forwarding(&fwd); if (fwd.handle < 0) goto fail; add_remote_forward(&options, &fwd); fctx = xcalloc(1, sizeof(*fctx)); fctx->cid = c->self; fctx->rid = rid; fctx->fid = options.num_remote_forwards - 1; client_register_global_confirm(mux_confirm_remote_forward, fctx); freefwd = 0; c->mux_pause = 1; /* wait for mux_confirm_remote_forward */ /* delayed reply in mux_confirm_remote_forward */ goto out; } buffer_put_int(r, MUX_S_OK); buffer_put_int(r, rid); out: free(fwd_desc); if (freefwd) { free(fwd.listen_host); free(fwd.listen_path); free(fwd.connect_host); free(fwd.connect_path); } return ret; } static int process_mux_close_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) { struct Forward fwd, *found_fwd; char *fwd_desc = NULL; const char *error_reason = NULL; char *listen_addr = NULL, *connect_addr = NULL; u_int ftype; int i, ret = 0; u_int lport, cport; memset(&fwd, 0, sizeof(fwd)); if (buffer_get_int_ret(&ftype, m) != 0 || (listen_addr = buffer_get_string_ret(m, NULL)) == NULL || buffer_get_int_ret(&lport, m) != 0 || (connect_addr = buffer_get_string_ret(m, NULL)) == NULL || buffer_get_int_ret(&cport, m) != 0 || (lport != (u_int)PORT_STREAMLOCAL && lport > 65535) || (cport != (u_int)PORT_STREAMLOCAL && cport > 65535)) { error("%s: malformed message", __func__); ret = -1; goto out; } if (*listen_addr == '\0') { free(listen_addr); listen_addr = NULL; } if (*connect_addr == '\0') { free(connect_addr); connect_addr = NULL; } memset(&fwd, 0, sizeof(fwd)); fwd.listen_port = lport; if (fwd.listen_port == PORT_STREAMLOCAL) fwd.listen_path = listen_addr; else fwd.listen_host = listen_addr; fwd.connect_port = cport; if (fwd.connect_port == PORT_STREAMLOCAL) fwd.connect_path = connect_addr; else fwd.connect_host = connect_addr; debug2("%s: channel %d: request cancel %s", __func__, c->self, (fwd_desc = format_forward(ftype, &fwd))); /* make sure this has been requested */ found_fwd = NULL; switch (ftype) { case MUX_FWD_LOCAL: case MUX_FWD_DYNAMIC: for (i = 0; i < options.num_local_forwards; i++) { if (compare_forward(&fwd, options.local_forwards + i)) { found_fwd = options.local_forwards + i; break; } } break; case MUX_FWD_REMOTE: for (i = 0; i < options.num_remote_forwards; i++) { if (compare_forward(&fwd, options.remote_forwards + i)) { found_fwd = options.remote_forwards + i; break; } } break; } if (found_fwd == NULL) error_reason = "port not forwarded"; else if (ftype == MUX_FWD_REMOTE) { /* * This shouldn't fail unless we confused the host/port * between options.remote_forwards and permitted_opens. * However, for dynamic allocated listen ports we need * to use the actual listen port. */ if (channel_request_rforward_cancel(found_fwd) == -1) error_reason = "port not in permitted opens"; } else { /* local and dynamic forwards */ /* Ditto */ if (channel_cancel_lport_listener(&fwd, fwd.connect_port, &options.fwd_opts) == -1) error_reason = "port not found"; } if (error_reason == NULL) { buffer_put_int(r, MUX_S_OK); buffer_put_int(r, rid); free(found_fwd->listen_host); free(found_fwd->listen_path); free(found_fwd->connect_host); free(found_fwd->connect_path); found_fwd->listen_host = found_fwd->connect_host = NULL; found_fwd->listen_path = found_fwd->connect_path = NULL; found_fwd->listen_port = found_fwd->connect_port = 0; } else { buffer_put_int(r, MUX_S_FAILURE); buffer_put_int(r, rid); buffer_put_cstring(r, error_reason); } out: free(fwd_desc); free(listen_addr); free(connect_addr); return ret; } static int process_mux_stdio_fwd(u_int rid, Channel *c, Buffer *m, Buffer *r) { Channel *nc; char *reserved, *chost; u_int cport, i, j; int new_fd[2]; struct mux_stdio_confirm_ctx *cctx; chost = reserved = NULL; if ((reserved = buffer_get_string_ret(m, NULL)) == NULL || (chost = buffer_get_string_ret(m, NULL)) == NULL || buffer_get_int_ret(&cport, m) != 0) { free(reserved); free(chost); error("%s: malformed message", __func__); return -1; } free(reserved); debug2("%s: channel %d: request stdio fwd to %s:%u", __func__, c->self, chost, cport); /* Gather fds from client */ for(i = 0; i < 2; i++) { if ((new_fd[i] = mm_receive_fd(c->sock)) == -1) { error("%s: failed to receive fd %d from slave", __func__, i); for (j = 0; j < i; j++) close(new_fd[j]); free(chost); /* prepare reply */ buffer_put_int(r, MUX_S_FAILURE); buffer_put_int(r, rid); buffer_put_cstring(r, "did not receive file descriptors"); return -1; } } debug3("%s: got fds stdin %d, stdout %d", __func__, new_fd[0], new_fd[1]); /* XXX support multiple child sessions in future */ if (c->remote_id != -1) { debug2("%s: session already open", __func__); /* prepare reply */ buffer_put_int(r, MUX_S_FAILURE); buffer_put_int(r, rid); buffer_put_cstring(r, "Multiple sessions not supported"); cleanup: close(new_fd[0]); close(new_fd[1]); free(chost); return 0; } if (options.control_master == SSHCTL_MASTER_ASK || options.control_master == SSHCTL_MASTER_AUTO_ASK) { if (!ask_permission("Allow forward to %s:%u? ", chost, cport)) { debug2("%s: stdio fwd refused by user", __func__); /* prepare reply */ buffer_put_int(r, MUX_S_PERMISSION_DENIED); buffer_put_int(r, rid); buffer_put_cstring(r, "Permission denied"); goto cleanup; } } /* enable nonblocking unless tty */ if (!isatty(new_fd[0])) set_nonblock(new_fd[0]); if (!isatty(new_fd[1])) set_nonblock(new_fd[1]); nc = channel_connect_stdio_fwd(chost, cport, new_fd[0], new_fd[1]); nc->ctl_chan = c->self; /* link session -> control channel */ c->remote_id = nc->self; /* link control -> session channel */ debug2("%s: channel_new: %d linked to control channel %d", __func__, nc->self, nc->ctl_chan); channel_register_cleanup(nc->self, mux_master_session_cleanup_cb, 1); cctx = xcalloc(1, sizeof(*cctx)); cctx->rid = rid; channel_register_open_confirm(nc->self, mux_stdio_confirm, cctx); c->mux_pause = 1; /* stop handling messages until open_confirm done */ /* reply is deferred, sent by mux_session_confirm */ return 0; } /* Callback on open confirmation in mux master for a mux stdio fwd session. */ static void mux_stdio_confirm(int id, int success, void *arg) { struct mux_stdio_confirm_ctx *cctx = arg; Channel *c, *cc; Buffer reply; if (cctx == NULL) fatal("%s: cctx == NULL", __func__); if ((c = channel_by_id(id)) == NULL) fatal("%s: no channel for id %d", __func__, id); if ((cc = channel_by_id(c->ctl_chan)) == NULL) fatal("%s: channel %d lacks control channel %d", __func__, id, c->ctl_chan); if (!success) { debug3("%s: sending failure reply", __func__); /* prepare reply */ buffer_init(&reply); buffer_put_int(&reply, MUX_S_FAILURE); buffer_put_int(&reply, cctx->rid); buffer_put_cstring(&reply, "Session open refused by peer"); goto done; } debug3("%s: sending success reply", __func__); /* prepare reply */ buffer_init(&reply); buffer_put_int(&reply, MUX_S_SESSION_OPENED); buffer_put_int(&reply, cctx->rid); buffer_put_int(&reply, c->self); done: /* Send reply */ buffer_put_string(&cc->output, buffer_ptr(&reply), buffer_len(&reply)); buffer_free(&reply); if (cc->mux_pause <= 0) fatal("%s: mux_pause %d", __func__, cc->mux_pause); cc->mux_pause = 0; /* start processing messages again */ c->open_confirm_ctx = NULL; free(cctx); } static int process_mux_stop_listening(u_int rid, Channel *c, Buffer *m, Buffer *r) { debug("%s: channel %d: stop listening", __func__, c->self); if (options.control_master == SSHCTL_MASTER_ASK || options.control_master == SSHCTL_MASTER_AUTO_ASK) { if (!ask_permission("Disable further multiplexing on shared " "connection to %s? ", host)) { debug2("%s: stop listen refused by user", __func__); buffer_put_int(r, MUX_S_PERMISSION_DENIED); buffer_put_int(r, rid); buffer_put_cstring(r, "Permission denied"); return 0; } } if (mux_listener_channel != NULL) { channel_free(mux_listener_channel); client_stop_mux(); free(options.control_path); options.control_path = NULL; mux_listener_channel = NULL; muxserver_sock = -1; } /* prepare reply */ buffer_put_int(r, MUX_S_OK); buffer_put_int(r, rid); return 0; } static int process_mux_proxy(u_int rid, Channel *c, Buffer *m, Buffer *r) { debug("%s: channel %d: proxy request", __func__, c->self); c->mux_rcb = channel_proxy_downstream; buffer_put_int(r, MUX_S_PROXY); buffer_put_int(r, rid); return 0; } /* Channel callbacks fired on read/write from mux slave fd */ static int mux_master_read_cb(Channel *c) { struct mux_master_state *state = (struct mux_master_state *)c->mux_ctx; Buffer in, out; const u_char *ptr; u_int type, rid, have, i; int ret = -1; /* Setup ctx and */ if (c->mux_ctx == NULL) { state = xcalloc(1, sizeof(*state)); c->mux_ctx = state; channel_register_cleanup(c->self, mux_master_control_cleanup_cb, 0); /* Send hello */ buffer_init(&out); buffer_put_int(&out, MUX_MSG_HELLO); buffer_put_int(&out, SSHMUX_VER); /* no extensions */ buffer_put_string(&c->output, buffer_ptr(&out), buffer_len(&out)); buffer_free(&out); debug3("%s: channel %d: hello sent", __func__, c->self); return 0; } buffer_init(&in); buffer_init(&out); /* Channel code ensures that we receive whole packets */ if ((ptr = buffer_get_string_ptr_ret(&c->input, &have)) == NULL) { malf: error("%s: malformed message", __func__); goto out; } buffer_append(&in, ptr, have); if (buffer_get_int_ret(&type, &in) != 0) goto malf; debug3("%s: channel %d packet type 0x%08x len %u", __func__, c->self, type, buffer_len(&in)); if (type == MUX_MSG_HELLO) rid = 0; else { if (!state->hello_rcvd) { error("%s: expected MUX_MSG_HELLO(0x%08x), " "received 0x%08x", __func__, MUX_MSG_HELLO, type); goto out; } if (buffer_get_int_ret(&rid, &in) != 0) goto malf; } for (i = 0; mux_master_handlers[i].handler != NULL; i++) { if (type == mux_master_handlers[i].type) { ret = mux_master_handlers[i].handler(rid, c, &in, &out); break; } } if (mux_master_handlers[i].handler == NULL) { error("%s: unsupported mux message 0x%08x", __func__, type); buffer_put_int(&out, MUX_S_FAILURE); buffer_put_int(&out, rid); buffer_put_cstring(&out, "unsupported request"); ret = 0; } /* Enqueue reply packet */ if (buffer_len(&out) != 0) { buffer_put_string(&c->output, buffer_ptr(&out), buffer_len(&out)); } out: buffer_free(&in); buffer_free(&out); return ret; } void mux_exit_message(Channel *c, int exitval) { Buffer m; Channel *mux_chan; debug3("%s: channel %d: exit message, exitval %d", __func__, c->self, exitval); if ((mux_chan = channel_by_id(c->ctl_chan)) == NULL) fatal("%s: channel %d missing mux channel %d", __func__, c->self, c->ctl_chan); /* Append exit message packet to control socket output queue */ buffer_init(&m); buffer_put_int(&m, MUX_S_EXIT_MESSAGE); buffer_put_int(&m, c->self); buffer_put_int(&m, exitval); buffer_put_string(&mux_chan->output, buffer_ptr(&m), buffer_len(&m)); buffer_free(&m); } void mux_tty_alloc_failed(Channel *c) { Buffer m; Channel *mux_chan; debug3("%s: channel %d: TTY alloc failed", __func__, c->self); if ((mux_chan = channel_by_id(c->ctl_chan)) == NULL) fatal("%s: channel %d missing mux channel %d", __func__, c->self, c->ctl_chan); /* Append exit message packet to control socket output queue */ buffer_init(&m); buffer_put_int(&m, MUX_S_TTY_ALLOC_FAIL); buffer_put_int(&m, c->self); buffer_put_string(&mux_chan->output, buffer_ptr(&m), buffer_len(&m)); buffer_free(&m); } /* Prepare a mux master to listen on a Unix domain socket. */ void muxserver_listen(void) { mode_t old_umask; char *orig_control_path = options.control_path; char rbuf[16+1]; u_int i, r; int oerrno; if (options.control_path == NULL || options.control_master == SSHCTL_MASTER_NO) return; debug("setting up multiplex master socket"); /* * Use a temporary path before listen so we can pseudo-atomically * establish the listening socket in its final location to avoid * other processes racing in between bind() and listen() and hitting * an unready socket. */ for (i = 0; i < sizeof(rbuf) - 1; i++) { r = arc4random_uniform(26+26+10); rbuf[i] = (r < 26) ? 'a' + r : (r < 26*2) ? 'A' + r - 26 : '0' + r - 26 - 26; } rbuf[sizeof(rbuf) - 1] = '\0'; options.control_path = NULL; xasprintf(&options.control_path, "%s.%s", orig_control_path, rbuf); debug3("%s: temporary control path %s", __func__, options.control_path); old_umask = umask(0177); muxserver_sock = unix_listener(options.control_path, 64, 0); oerrno = errno; umask(old_umask); if (muxserver_sock < 0) { if (oerrno == EINVAL || oerrno == EADDRINUSE) { error("ControlSocket %s already exists, " "disabling multiplexing", options.control_path); disable_mux_master: if (muxserver_sock != -1) { close(muxserver_sock); muxserver_sock = -1; } free(orig_control_path); free(options.control_path); options.control_path = NULL; options.control_master = SSHCTL_MASTER_NO; return; } else { /* unix_listener() logs the error */ cleanup_exit(255); } } /* Now atomically "move" the mux socket into position */ if (link(options.control_path, orig_control_path) != 0) { if (errno != EEXIST) { fatal("%s: link mux listener %s => %s: %s", __func__, options.control_path, orig_control_path, strerror(errno)); } error("ControlSocket %s already exists, disabling multiplexing", orig_control_path); unlink(options.control_path); goto disable_mux_master; } unlink(options.control_path); free(options.control_path); options.control_path = orig_control_path; set_nonblock(muxserver_sock); mux_listener_channel = channel_new("mux listener", SSH_CHANNEL_MUX_LISTENER, muxserver_sock, muxserver_sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, options.control_path, 1); mux_listener_channel->mux_rcb = mux_master_read_cb; debug3("%s: mux listener channel %d fd %d", __func__, mux_listener_channel->self, mux_listener_channel->sock); } /* Callback on open confirmation in mux master for a mux client session. */ static void mux_session_confirm(int id, int success, void *arg) { struct mux_session_confirm_ctx *cctx = arg; const char *display; Channel *c, *cc; int i; Buffer reply; if (cctx == NULL) fatal("%s: cctx == NULL", __func__); if ((c = channel_by_id(id)) == NULL) fatal("%s: no channel for id %d", __func__, id); if ((cc = channel_by_id(c->ctl_chan)) == NULL) fatal("%s: channel %d lacks control channel %d", __func__, id, c->ctl_chan); if (!success) { debug3("%s: sending failure reply", __func__); /* prepare reply */ buffer_init(&reply); buffer_put_int(&reply, MUX_S_FAILURE); buffer_put_int(&reply, cctx->rid); buffer_put_cstring(&reply, "Session open refused by peer"); goto done; } display = getenv("DISPLAY"); if (cctx->want_x_fwd && options.forward_x11 && display != NULL) { char *proto, *data; /* Get reasonable local authentication information. */ if (client_x11_get_proto(display, options.xauth_location, options.forward_x11_trusted, options.forward_x11_timeout, &proto, &data) == 0) { /* Request forwarding with authentication spoofing. */ debug("Requesting X11 forwarding with authentication " "spoofing."); x11_request_forwarding_with_spoofing(id, display, proto, data, 1); /* XXX exit_on_forward_failure */ client_expect_confirm(id, "X11 forwarding", CONFIRM_WARN); } } if (cctx->want_agent_fwd && options.forward_agent) { debug("Requesting authentication agent forwarding."); channel_request_start(id, "auth-agent-req@openssh.com", 0); packet_send(); } client_session2_setup(id, cctx->want_tty, cctx->want_subsys, cctx->term, &cctx->tio, c->rfd, &cctx->cmd, cctx->env); debug3("%s: sending success reply", __func__); /* prepare reply */ buffer_init(&reply); buffer_put_int(&reply, MUX_S_SESSION_OPENED); buffer_put_int(&reply, cctx->rid); buffer_put_int(&reply, c->self); done: /* Send reply */ buffer_put_string(&cc->output, buffer_ptr(&reply), buffer_len(&reply)); buffer_free(&reply); if (cc->mux_pause <= 0) fatal("%s: mux_pause %d", __func__, cc->mux_pause); cc->mux_pause = 0; /* start processing messages again */ c->open_confirm_ctx = NULL; buffer_free(&cctx->cmd); free(cctx->term); if (cctx->env != NULL) { for (i = 0; cctx->env[i] != NULL; i++) free(cctx->env[i]); free(cctx->env); } free(cctx); } /* ** Multiplexing client support */ /* Exit signal handler */ static void control_client_sighandler(int signo) { muxclient_terminate = signo; } /* * Relay signal handler - used to pass some signals from mux client to * mux master. */ static void control_client_sigrelay(int signo) { int save_errno = errno; if (muxserver_pid > 1) kill(muxserver_pid, signo); errno = save_errno; } static int mux_client_read(int fd, Buffer *b, u_int need) { u_int have; ssize_t len; u_char *p; struct pollfd pfd; pfd.fd = fd; pfd.events = POLLIN; p = buffer_append_space(b, need); for (have = 0; have < need; ) { if (muxclient_terminate) { errno = EINTR; return -1; } len = read(fd, p + have, need - have); if (len < 0) { switch (errno) { #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) case EWOULDBLOCK: #endif case EAGAIN: (void)poll(&pfd, 1, -1); /* FALLTHROUGH */ case EINTR: continue; default: return -1; } } if (len == 0) { errno = EPIPE; return -1; } have += (u_int)len; } return 0; } static int mux_client_write_packet(int fd, Buffer *m) { Buffer queue; u_int have, need; int oerrno, len; u_char *ptr; struct pollfd pfd; pfd.fd = fd; pfd.events = POLLOUT; buffer_init(&queue); buffer_put_string(&queue, buffer_ptr(m), buffer_len(m)); need = buffer_len(&queue); ptr = buffer_ptr(&queue); for (have = 0; have < need; ) { if (muxclient_terminate) { buffer_free(&queue); errno = EINTR; return -1; } len = write(fd, ptr + have, need - have); if (len < 0) { switch (errno) { #if defined(EWOULDBLOCK) && (EWOULDBLOCK != EAGAIN) case EWOULDBLOCK: #endif case EAGAIN: (void)poll(&pfd, 1, -1); /* FALLTHROUGH */ case EINTR: continue; default: oerrno = errno; buffer_free(&queue); errno = oerrno; return -1; } } if (len == 0) { buffer_free(&queue); errno = EPIPE; return -1; } have += (u_int)len; } buffer_free(&queue); return 0; } static int mux_client_read_packet(int fd, Buffer *m) { Buffer queue; u_int need, have; const u_char *ptr; int oerrno; buffer_init(&queue); if (mux_client_read(fd, &queue, 4) != 0) { if ((oerrno = errno) == EPIPE) debug3("%s: read header failed: %s", __func__, strerror(errno)); buffer_free(&queue); errno = oerrno; return -1; } need = get_u32(buffer_ptr(&queue)); if (mux_client_read(fd, &queue, need) != 0) { oerrno = errno; debug3("%s: read body failed: %s", __func__, strerror(errno)); buffer_free(&queue); errno = oerrno; return -1; } ptr = buffer_get_string_ptr(&queue, &have); buffer_append(m, ptr, have); buffer_free(&queue); return 0; } static int mux_client_hello_exchange(int fd) { Buffer m; u_int type, ver; buffer_init(&m); buffer_put_int(&m, MUX_MSG_HELLO); buffer_put_int(&m, SSHMUX_VER); /* no extensions */ if (mux_client_write_packet(fd, &m) != 0) fatal("%s: write packet: %s", __func__, strerror(errno)); buffer_clear(&m); /* Read their HELLO */ if (mux_client_read_packet(fd, &m) != 0) { buffer_free(&m); return -1; } type = buffer_get_int(&m); if (type != MUX_MSG_HELLO) fatal("%s: expected HELLO (%u) received %u", __func__, MUX_MSG_HELLO, type); ver = buffer_get_int(&m); if (ver != SSHMUX_VER) fatal("Unsupported multiplexing protocol version %d " "(expected %d)", ver, SSHMUX_VER); debug2("%s: master version %u", __func__, ver); /* No extensions are presently defined */ while (buffer_len(&m) > 0) { char *name = buffer_get_string(&m, NULL); char *value = buffer_get_string(&m, NULL); debug2("Unrecognised master extension \"%s\"", name); free(name); free(value); } buffer_free(&m); return 0; } static u_int mux_client_request_alive(int fd) { Buffer m; char *e; u_int pid, type, rid; debug3("%s: entering", __func__); buffer_init(&m); buffer_put_int(&m, MUX_C_ALIVE_CHECK); buffer_put_int(&m, muxclient_request_id); if (mux_client_write_packet(fd, &m) != 0) fatal("%s: write packet: %s", __func__, strerror(errno)); buffer_clear(&m); /* Read their reply */ if (mux_client_read_packet(fd, &m) != 0) { buffer_free(&m); return 0; } type = buffer_get_int(&m); if (type != MUX_S_ALIVE) { e = buffer_get_string(&m, NULL); fatal("%s: master returned error: %s", __func__, e); } if ((rid = buffer_get_int(&m)) != muxclient_request_id) fatal("%s: out of sequence reply: my id %u theirs %u", __func__, muxclient_request_id, rid); pid = buffer_get_int(&m); buffer_free(&m); debug3("%s: done pid = %u", __func__, pid); muxclient_request_id++; return pid; } static void mux_client_request_terminate(int fd) { Buffer m; char *e; u_int type, rid; debug3("%s: entering", __func__); buffer_init(&m); buffer_put_int(&m, MUX_C_TERMINATE); buffer_put_int(&m, muxclient_request_id); if (mux_client_write_packet(fd, &m) != 0) fatal("%s: write packet: %s", __func__, strerror(errno)); buffer_clear(&m); /* Read their reply */ if (mux_client_read_packet(fd, &m) != 0) { /* Remote end exited already */ if (errno == EPIPE) { buffer_free(&m); return; } fatal("%s: read from master failed: %s", __func__, strerror(errno)); } type = buffer_get_int(&m); if ((rid = buffer_get_int(&m)) != muxclient_request_id) fatal("%s: out of sequence reply: my id %u theirs %u", __func__, muxclient_request_id, rid); switch (type) { case MUX_S_OK: break; case MUX_S_PERMISSION_DENIED: e = buffer_get_string(&m, NULL); fatal("Master refused termination request: %s", e); case MUX_S_FAILURE: e = buffer_get_string(&m, NULL); fatal("%s: termination request failed: %s", __func__, e); default: fatal("%s: unexpected response from master 0x%08x", __func__, type); } buffer_free(&m); muxclient_request_id++; } static int mux_client_forward(int fd, int cancel_flag, u_int ftype, struct Forward *fwd) { Buffer m; char *e, *fwd_desc; u_int type, rid; fwd_desc = format_forward(ftype, fwd); debug("Requesting %s %s", cancel_flag ? "cancellation of" : "forwarding of", fwd_desc); free(fwd_desc); buffer_init(&m); buffer_put_int(&m, cancel_flag ? MUX_C_CLOSE_FWD : MUX_C_OPEN_FWD); buffer_put_int(&m, muxclient_request_id); buffer_put_int(&m, ftype); if (fwd->listen_path != NULL) { buffer_put_cstring(&m, fwd->listen_path); } else { buffer_put_cstring(&m, fwd->listen_host == NULL ? "" : (*fwd->listen_host == '\0' ? "*" : fwd->listen_host)); } buffer_put_int(&m, fwd->listen_port); if (fwd->connect_path != NULL) { buffer_put_cstring(&m, fwd->connect_path); } else { buffer_put_cstring(&m, fwd->connect_host == NULL ? "" : fwd->connect_host); } buffer_put_int(&m, fwd->connect_port); if (mux_client_write_packet(fd, &m) != 0) fatal("%s: write packet: %s", __func__, strerror(errno)); buffer_clear(&m); /* Read their reply */ if (mux_client_read_packet(fd, &m) != 0) { buffer_free(&m); return -1; } type = buffer_get_int(&m); if ((rid = buffer_get_int(&m)) != muxclient_request_id) fatal("%s: out of sequence reply: my id %u theirs %u", __func__, muxclient_request_id, rid); switch (type) { case MUX_S_OK: break; case MUX_S_REMOTE_PORT: if (cancel_flag) fatal("%s: got MUX_S_REMOTE_PORT for cancel", __func__); fwd->allocated_port = buffer_get_int(&m); verbose("Allocated port %u for remote forward to %s:%d", fwd->allocated_port, fwd->connect_host ? fwd->connect_host : "", fwd->connect_port); if (muxclient_command == SSHMUX_COMMAND_FORWARD) fprintf(stdout, "%i\n", fwd->allocated_port); break; case MUX_S_PERMISSION_DENIED: e = buffer_get_string(&m, NULL); buffer_free(&m); error("Master refused forwarding request: %s", e); return -1; case MUX_S_FAILURE: e = buffer_get_string(&m, NULL); buffer_free(&m); error("%s: forwarding request failed: %s", __func__, e); return -1; default: fatal("%s: unexpected response from master 0x%08x", __func__, type); } buffer_free(&m); muxclient_request_id++; return 0; } static int mux_client_forwards(int fd, int cancel_flag) { int i, ret = 0; debug3("%s: %s forwardings: %d local, %d remote", __func__, cancel_flag ? "cancel" : "request", options.num_local_forwards, options.num_remote_forwards); /* XXX ExitOnForwardingFailure */ for (i = 0; i < options.num_local_forwards; i++) { if (mux_client_forward(fd, cancel_flag, options.local_forwards[i].connect_port == 0 ? MUX_FWD_DYNAMIC : MUX_FWD_LOCAL, options.local_forwards + i) != 0) ret = -1; } for (i = 0; i < options.num_remote_forwards; i++) { if (mux_client_forward(fd, cancel_flag, MUX_FWD_REMOTE, options.remote_forwards + i) != 0) ret = -1; } return ret; } static int mux_client_request_session(int fd) { Buffer m; char *e, *term; u_int i, rid, sid, esid, exitval, type, exitval_seen; extern char **environ; int devnull, rawmode; debug3("%s: entering", __func__); if ((muxserver_pid = mux_client_request_alive(fd)) == 0) { error("%s: master alive request failed", __func__); return -1; } signal(SIGPIPE, SIG_IGN); if (stdin_null_flag) { if ((devnull = open(_PATH_DEVNULL, O_RDONLY)) == -1) fatal("open(/dev/null): %s", strerror(errno)); if (dup2(devnull, STDIN_FILENO) == -1) fatal("dup2: %s", strerror(errno)); if (devnull > STDERR_FILENO) close(devnull); } term = getenv("TERM"); buffer_init(&m); buffer_put_int(&m, MUX_C_NEW_SESSION); buffer_put_int(&m, muxclient_request_id); buffer_put_cstring(&m, ""); /* reserved */ buffer_put_int(&m, tty_flag); buffer_put_int(&m, options.forward_x11); buffer_put_int(&m, options.forward_agent); buffer_put_int(&m, subsystem_flag); buffer_put_int(&m, options.escape_char == SSH_ESCAPECHAR_NONE ? 0xffffffff : (u_int)options.escape_char); buffer_put_cstring(&m, term == NULL ? "" : term); buffer_put_string(&m, buffer_ptr(&command), buffer_len(&command)); if (options.num_send_env > 0 && environ != NULL) { /* Pass environment */ for (i = 0; environ[i] != NULL; i++) { if (env_permitted(environ[i])) { buffer_put_cstring(&m, environ[i]); } } } if (mux_client_write_packet(fd, &m) != 0) fatal("%s: write packet: %s", __func__, strerror(errno)); /* Send the stdio file descriptors */ if (mm_send_fd(fd, STDIN_FILENO) == -1 || mm_send_fd(fd, STDOUT_FILENO) == -1 || mm_send_fd(fd, STDERR_FILENO) == -1) fatal("%s: send fds failed", __func__); debug3("%s: session request sent", __func__); /* Read their reply */ buffer_clear(&m); if (mux_client_read_packet(fd, &m) != 0) { error("%s: read from master failed: %s", __func__, strerror(errno)); buffer_free(&m); return -1; } type = buffer_get_int(&m); if ((rid = buffer_get_int(&m)) != muxclient_request_id) fatal("%s: out of sequence reply: my id %u theirs %u", __func__, muxclient_request_id, rid); switch (type) { case MUX_S_SESSION_OPENED: sid = buffer_get_int(&m); debug("%s: master session id: %u", __func__, sid); break; case MUX_S_PERMISSION_DENIED: e = buffer_get_string(&m, NULL); buffer_free(&m); error("Master refused session request: %s", e); return -1; case MUX_S_FAILURE: e = buffer_get_string(&m, NULL); buffer_free(&m); error("%s: session request failed: %s", __func__, e); return -1; default: buffer_free(&m); error("%s: unexpected response from master 0x%08x", __func__, type); return -1; } muxclient_request_id++; if (pledge("stdio proc tty", NULL) == -1) fatal("%s pledge(): %s", __func__, strerror(errno)); platform_pledge_mux(); signal(SIGHUP, control_client_sighandler); signal(SIGINT, control_client_sighandler); signal(SIGTERM, control_client_sighandler); signal(SIGWINCH, control_client_sigrelay); rawmode = tty_flag; if (tty_flag) enter_raw_mode(options.request_tty == REQUEST_TTY_FORCE); /* * Stick around until the controlee closes the client_fd. * Before it does, it is expected to write an exit message. * This process must read the value and wait for the closure of * the client_fd; if this one closes early, the multiplex master will * terminate early too (possibly losing data). */ for (exitval = 255, exitval_seen = 0;;) { buffer_clear(&m); if (mux_client_read_packet(fd, &m) != 0) break; type = buffer_get_int(&m); switch (type) { case MUX_S_TTY_ALLOC_FAIL: if ((esid = buffer_get_int(&m)) != sid) fatal("%s: tty alloc fail on unknown session: " "my id %u theirs %u", __func__, sid, esid); leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); rawmode = 0; continue; case MUX_S_EXIT_MESSAGE: if ((esid = buffer_get_int(&m)) != sid) fatal("%s: exit on unknown session: " "my id %u theirs %u", __func__, sid, esid); if (exitval_seen) fatal("%s: exitval sent twice", __func__); exitval = buffer_get_int(&m); exitval_seen = 1; continue; default: e = buffer_get_string(&m, NULL); fatal("%s: master returned error: %s", __func__, e); } } close(fd); if (rawmode) leave_raw_mode(options.request_tty == REQUEST_TTY_FORCE); if (muxclient_terminate) { debug2("Exiting on signal %ld", (long)muxclient_terminate); exitval = 255; } else if (!exitval_seen) { debug2("Control master terminated unexpectedly"); exitval = 255; } else debug2("Received exit status from master %d", exitval); if (tty_flag && options.log_level != SYSLOG_LEVEL_QUIET) fprintf(stderr, "Shared connection to %s closed.\r\n", host); exit(exitval); } static int mux_client_proxy(int fd) { Buffer m; char *e; u_int type, rid; buffer_init(&m); buffer_put_int(&m, MUX_C_PROXY); buffer_put_int(&m, muxclient_request_id); if (mux_client_write_packet(fd, &m) != 0) fatal("%s: write packet: %s", __func__, strerror(errno)); buffer_clear(&m); /* Read their reply */ if (mux_client_read_packet(fd, &m) != 0) { buffer_free(&m); return 0; } type = buffer_get_int(&m); if (type != MUX_S_PROXY) { e = buffer_get_string(&m, NULL); fatal("%s: master returned error: %s", __func__, e); } if ((rid = buffer_get_int(&m)) != muxclient_request_id) fatal("%s: out of sequence reply: my id %u theirs %u", __func__, muxclient_request_id, rid); buffer_free(&m); debug3("%s: done", __func__); muxclient_request_id++; return 0; } static int mux_client_request_stdio_fwd(int fd) { Buffer m; char *e; u_int type, rid, sid; int devnull; debug3("%s: entering", __func__); if ((muxserver_pid = mux_client_request_alive(fd)) == 0) { error("%s: master alive request failed", __func__); return -1; } signal(SIGPIPE, SIG_IGN); if (stdin_null_flag) { if ((devnull = open(_PATH_DEVNULL, O_RDONLY)) == -1) fatal("open(/dev/null): %s", strerror(errno)); if (dup2(devnull, STDIN_FILENO) == -1) fatal("dup2: %s", strerror(errno)); if (devnull > STDERR_FILENO) close(devnull); } buffer_init(&m); buffer_put_int(&m, MUX_C_NEW_STDIO_FWD); buffer_put_int(&m, muxclient_request_id); buffer_put_cstring(&m, ""); /* reserved */ buffer_put_cstring(&m, options.stdio_forward_host); buffer_put_int(&m, options.stdio_forward_port); if (mux_client_write_packet(fd, &m) != 0) fatal("%s: write packet: %s", __func__, strerror(errno)); /* Send the stdio file descriptors */ if (mm_send_fd(fd, STDIN_FILENO) == -1 || mm_send_fd(fd, STDOUT_FILENO) == -1) fatal("%s: send fds failed", __func__); if (pledge("stdio proc tty", NULL) == -1) fatal("%s pledge(): %s", __func__, strerror(errno)); platform_pledge_mux(); debug3("%s: stdio forward request sent", __func__); /* Read their reply */ buffer_clear(&m); if (mux_client_read_packet(fd, &m) != 0) { error("%s: read from master failed: %s", __func__, strerror(errno)); buffer_free(&m); return -1; } type = buffer_get_int(&m); if ((rid = buffer_get_int(&m)) != muxclient_request_id) fatal("%s: out of sequence reply: my id %u theirs %u", __func__, muxclient_request_id, rid); switch (type) { case MUX_S_SESSION_OPENED: sid = buffer_get_int(&m); debug("%s: master session id: %u", __func__, sid); break; case MUX_S_PERMISSION_DENIED: e = buffer_get_string(&m, NULL); buffer_free(&m); fatal("Master refused stdio forwarding request: %s", e); case MUX_S_FAILURE: e = buffer_get_string(&m, NULL); buffer_free(&m); fatal("Stdio forwarding request failed: %s", e); default: buffer_free(&m); error("%s: unexpected response from master 0x%08x", __func__, type); return -1; } muxclient_request_id++; signal(SIGHUP, control_client_sighandler); signal(SIGINT, control_client_sighandler); signal(SIGTERM, control_client_sighandler); signal(SIGWINCH, control_client_sigrelay); /* * Stick around until the controlee closes the client_fd. */ buffer_clear(&m); if (mux_client_read_packet(fd, &m) != 0) { if (errno == EPIPE || (errno == EINTR && muxclient_terminate != 0)) return 0; fatal("%s: mux_client_read_packet: %s", __func__, strerror(errno)); } fatal("%s: master returned unexpected message %u", __func__, type); } static void mux_client_request_stop_listening(int fd) { Buffer m; char *e; u_int type, rid; debug3("%s: entering", __func__); buffer_init(&m); buffer_put_int(&m, MUX_C_STOP_LISTENING); buffer_put_int(&m, muxclient_request_id); if (mux_client_write_packet(fd, &m) != 0) fatal("%s: write packet: %s", __func__, strerror(errno)); buffer_clear(&m); /* Read their reply */ if (mux_client_read_packet(fd, &m) != 0) fatal("%s: read from master failed: %s", __func__, strerror(errno)); type = buffer_get_int(&m); if ((rid = buffer_get_int(&m)) != muxclient_request_id) fatal("%s: out of sequence reply: my id %u theirs %u", __func__, muxclient_request_id, rid); switch (type) { case MUX_S_OK: break; case MUX_S_PERMISSION_DENIED: e = buffer_get_string(&m, NULL); fatal("Master refused stop listening request: %s", e); case MUX_S_FAILURE: e = buffer_get_string(&m, NULL); fatal("%s: stop listening request failed: %s", __func__, e); default: fatal("%s: unexpected response from master 0x%08x", __func__, type); } buffer_free(&m); muxclient_request_id++; } /* Multiplex client main loop. */ int muxclient(const char *path) { struct sockaddr_un addr; - socklen_t sun_len; int sock; u_int pid; if (muxclient_command == 0) { if (options.stdio_forward_host != NULL) muxclient_command = SSHMUX_COMMAND_STDIO_FWD; else muxclient_command = SSHMUX_COMMAND_OPEN; } switch (options.control_master) { case SSHCTL_MASTER_AUTO: case SSHCTL_MASTER_AUTO_ASK: debug("auto-mux: Trying existing master"); /* FALLTHROUGH */ case SSHCTL_MASTER_NO: break; default: return -1; } memset(&addr, '\0', sizeof(addr)); addr.sun_family = AF_UNIX; - sun_len = offsetof(struct sockaddr_un, sun_path) + - strlen(path) + 1; if (strlcpy(addr.sun_path, path, sizeof(addr.sun_path)) >= sizeof(addr.sun_path)) fatal("ControlPath too long ('%s' >= %u bytes)", path, (unsigned int)sizeof(addr.sun_path)); if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) fatal("%s socket(): %s", __func__, strerror(errno)); - if (connect(sock, (struct sockaddr *)&addr, sun_len) == -1) { + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) { switch (muxclient_command) { case SSHMUX_COMMAND_OPEN: case SSHMUX_COMMAND_STDIO_FWD: break; default: fatal("Control socket connect(%.100s): %s", path, strerror(errno)); } if (errno == ECONNREFUSED && options.control_master != SSHCTL_MASTER_NO) { debug("Stale control socket %.100s, unlinking", path); unlink(path); } else if (errno == ENOENT) { debug("Control socket \"%.100s\" does not exist", path); } else { error("Control socket connect(%.100s): %s", path, strerror(errno)); } close(sock); return -1; } set_nonblock(sock); if (mux_client_hello_exchange(sock) != 0) { error("%s: master hello exchange failed", __func__); close(sock); return -1; } switch (muxclient_command) { case SSHMUX_COMMAND_ALIVE_CHECK: if ((pid = mux_client_request_alive(sock)) == 0) fatal("%s: master alive check failed", __func__); fprintf(stderr, "Master running (pid=%u)\r\n", pid); exit(0); case SSHMUX_COMMAND_TERMINATE: mux_client_request_terminate(sock); if (options.log_level != SYSLOG_LEVEL_QUIET) fprintf(stderr, "Exit request sent.\r\n"); exit(0); case SSHMUX_COMMAND_FORWARD: if (mux_client_forwards(sock, 0) != 0) fatal("%s: master forward request failed", __func__); exit(0); case SSHMUX_COMMAND_OPEN: if (mux_client_forwards(sock, 0) != 0) { error("%s: master forward request failed", __func__); return -1; } mux_client_request_session(sock); return -1; case SSHMUX_COMMAND_STDIO_FWD: mux_client_request_stdio_fwd(sock); exit(0); case SSHMUX_COMMAND_STOP: mux_client_request_stop_listening(sock); if (options.log_level != SYSLOG_LEVEL_QUIET) fprintf(stderr, "Stop listening request sent.\r\n"); exit(0); case SSHMUX_COMMAND_CANCEL_FWD: if (mux_client_forwards(sock, 1) != 0) error("%s: master cancel forward request failed", __func__); exit(0); case SSHMUX_COMMAND_PROXY: mux_client_proxy(sock); return (sock); default: fatal("unrecognised muxclient_command %d", muxclient_command); } } diff --git a/crypto/openssh/openbsd-compat/bsd-misc.c b/crypto/openssh/openbsd-compat/bsd-misc.c index 6f3bc8f1d4f0..cfd73260ae18 100644 --- a/crypto/openssh/openbsd-compat/bsd-misc.c +++ b/crypto/openssh/openbsd-compat/bsd-misc.c @@ -1,303 +1,311 @@ /* * Copyright (c) 1999-2004 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #ifdef HAVE_SYS_SELECT_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include #include #include #ifndef HAVE___PROGNAME char *__progname; #endif /* * NB. duplicate __progname in case it is an alias for argv[0] * Otherwise it may get clobbered by setproctitle() */ char *ssh_get_progname(char *argv0) { char *p, *q; #ifdef HAVE___PROGNAME extern char *__progname; p = __progname; #else if (argv0 == NULL) return ("unknown"); /* XXX */ p = strrchr(argv0, '/'); if (p == NULL) p = argv0; else p++; #endif if ((q = strdup(p)) == NULL) { perror("strdup"); exit(1); } return q; } #ifndef HAVE_SETLOGIN int setlogin(const char *name) { return (0); } #endif /* !HAVE_SETLOGIN */ #ifndef HAVE_INNETGR int innetgr(const char *netgroup, const char *host, const char *user, const char *domain) { return (0); } #endif /* HAVE_INNETGR */ #if !defined(HAVE_SETEUID) && defined(HAVE_SETREUID) int seteuid(uid_t euid) { return (setreuid(-1, euid)); } #endif /* !defined(HAVE_SETEUID) && defined(HAVE_SETREUID) */ #if !defined(HAVE_SETEGID) && defined(HAVE_SETRESGID) int setegid(uid_t egid) { return(setresgid(-1, egid, -1)); } #endif /* !defined(HAVE_SETEGID) && defined(HAVE_SETRESGID) */ #if !defined(HAVE_STRERROR) && defined(HAVE_SYS_ERRLIST) && defined(HAVE_SYS_NERR) const char *strerror(int e) { extern int sys_nerr; extern char *sys_errlist[]; if ((e >= 0) && (e < sys_nerr)) return (sys_errlist[e]); return ("unlisted error"); } #endif #ifndef HAVE_UTIMES int utimes(char *filename, struct timeval *tvp) { struct utimbuf ub; ub.actime = tvp[0].tv_sec; ub.modtime = tvp[1].tv_sec; return (utime(filename, &ub)); } #endif #ifndef HAVE_TRUNCATE int truncate(const char *path, off_t length) { int fd, ret, saverrno; fd = open(path, O_WRONLY); if (fd < 0) return (-1); ret = ftruncate(fd, length); saverrno = errno; close(fd); if (ret == -1) errno = saverrno; return(ret); } #endif /* HAVE_TRUNCATE */ #if !defined(HAVE_NANOSLEEP) && !defined(HAVE_NSLEEP) int nanosleep(const struct timespec *req, struct timespec *rem) { int rc, saverrno; extern int errno; struct timeval tstart, tstop, tremain, time2wait; TIMESPEC_TO_TIMEVAL(&time2wait, req) (void) gettimeofday(&tstart, NULL); rc = select(0, NULL, NULL, NULL, &time2wait); if (rc == -1) { saverrno = errno; (void) gettimeofday (&tstop, NULL); errno = saverrno; tremain.tv_sec = time2wait.tv_sec - (tstop.tv_sec - tstart.tv_sec); tremain.tv_usec = time2wait.tv_usec - (tstop.tv_usec - tstart.tv_usec); tremain.tv_sec += tremain.tv_usec / 1000000L; tremain.tv_usec %= 1000000L; } else { tremain.tv_sec = 0; tremain.tv_usec = 0; } if (rem != NULL) TIMEVAL_TO_TIMESPEC(&tremain, rem) return(rc); } #endif #if !defined(HAVE_USLEEP) int usleep(unsigned int useconds) { struct timespec ts; ts.tv_sec = useconds / 1000000; ts.tv_nsec = (useconds % 1000000) * 1000; return nanosleep(&ts, NULL); } #endif #ifndef HAVE_TCGETPGRP pid_t tcgetpgrp(int fd) { int ctty_pgrp; if (ioctl(fd, TIOCGPGRP, &ctty_pgrp) == -1) return(-1); else return(ctty_pgrp); } #endif /* HAVE_TCGETPGRP */ #ifndef HAVE_TCSENDBREAK int tcsendbreak(int fd, int duration) { # if defined(TIOCSBRK) && defined(TIOCCBRK) struct timeval sleepytime; sleepytime.tv_sec = 0; sleepytime.tv_usec = 400000; if (ioctl(fd, TIOCSBRK, 0) == -1) return (-1); (void)select(0, 0, 0, 0, &sleepytime); if (ioctl(fd, TIOCCBRK, 0) == -1) return (-1); return (0); # else return -1; # endif } #endif /* HAVE_TCSENDBREAK */ mysig_t mysignal(int sig, mysig_t act) { #ifdef HAVE_SIGACTION struct sigaction sa, osa; if (sigaction(sig, NULL, &osa) == -1) return (mysig_t) -1; if (osa.sa_handler != act) { memset(&sa, 0, sizeof(sa)); sigemptyset(&sa.sa_mask); sa.sa_flags = 0; #ifdef SA_INTERRUPT if (sig == SIGALRM) sa.sa_flags |= SA_INTERRUPT; #endif sa.sa_handler = act; if (sigaction(sig, &sa, NULL) == -1) return (mysig_t) -1; } return (osa.sa_handler); #else #undef signal return (signal(sig, act)); #endif } #ifndef HAVE_STRDUP char * strdup(const char *str) { size_t len; char *cp; len = strlen(str) + 1; cp = malloc(len); if (cp != NULL) return(memcpy(cp, str, len)); return NULL; } #endif #ifndef HAVE_ISBLANK int isblank(int c) { return (c == ' ' || c == '\t'); } #endif #ifndef HAVE_GETPGID pid_t getpgid(pid_t pid) { #if defined(HAVE_GETPGRP) && !defined(GETPGRP_VOID) return getpgrp(pid); #elif defined(HAVE_GETPGRP) if (pid == 0) return getpgrp(); #endif errno = ESRCH; return -1; } #endif #ifndef HAVE_PLEDGE int pledge(const char *promises, const char *paths[]) { return 0; } #endif #ifndef HAVE_MBTOWC /* a mbtowc that only supports ASCII */ int mbtowc(wchar_t *pwc, const char *s, size_t n) { if (s == NULL || *s == '\0') return 0; /* ASCII is not state-dependent */ if (*s < 0 || *s > 0x7f || n < 1) { errno = EOPNOTSUPP; return -1; } if (pwc != NULL) *pwc = *s; return 1; } #endif + +#ifndef HAVE_LLABS +long long +llabs(long long j) +{ + return (j < 0 ? -j : j); +} +#endif diff --git a/crypto/openssh/openbsd-compat/bsd-misc.h b/crypto/openssh/openbsd-compat/bsd-misc.h index 6f08b09faaa6..70a538f04e2d 100644 --- a/crypto/openssh/openbsd-compat/bsd-misc.h +++ b/crypto/openssh/openbsd-compat/bsd-misc.h @@ -1,138 +1,142 @@ /* * Copyright (c) 1999-2004 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef _BSD_MISC_H #define _BSD_MISC_H #include "includes.h" char *ssh_get_progname(char *); #ifndef HAVE_SETSID #define setsid() setpgrp(0, getpid()) #endif /* !HAVE_SETSID */ #ifndef HAVE_SETENV int setenv(const char *, const char *, int); #endif /* !HAVE_SETENV */ #ifndef HAVE_SETLOGIN int setlogin(const char *); #endif /* !HAVE_SETLOGIN */ #ifndef HAVE_INNETGR int innetgr(const char *, const char *, const char *, const char *); #endif /* HAVE_INNETGR */ #if !defined(HAVE_SETEUID) && defined(HAVE_SETREUID) int seteuid(uid_t); #endif /* !defined(HAVE_SETEUID) && defined(HAVE_SETREUID) */ #if !defined(HAVE_SETEGID) && defined(HAVE_SETRESGID) int setegid(uid_t); #endif /* !defined(HAVE_SETEGID) && defined(HAVE_SETRESGID) */ #if !defined(HAVE_STRERROR) && defined(HAVE_SYS_ERRLIST) && defined(HAVE_SYS_NERR) const char *strerror(int); #endif #if !defined(HAVE_SETLINEBUF) #define setlinebuf(a) (setvbuf((a), NULL, _IOLBF, 0)) #endif #ifndef HAVE_UTIMES #ifndef HAVE_STRUCT_TIMEVAL struct timeval { long tv_sec; long tv_usec; } #endif /* HAVE_STRUCT_TIMEVAL */ int utimes(char *, struct timeval *); #endif /* HAVE_UTIMES */ #ifndef HAVE_TRUNCATE int truncate (const char *, off_t); #endif /* HAVE_TRUNCATE */ #if !defined(HAVE_NANOSLEEP) && !defined(HAVE_NSLEEP) #ifndef HAVE_STRUCT_TIMESPEC struct timespec { time_t tv_sec; long tv_nsec; }; #endif int nanosleep(const struct timespec *, struct timespec *); #endif #ifndef HAVE_USLEEP int usleep(unsigned int useconds); #endif #ifndef HAVE_TCGETPGRP pid_t tcgetpgrp(int); #endif #ifndef HAVE_TCSENDBREAK int tcsendbreak(int, int); #endif #ifndef HAVE_UNSETENV int unsetenv(const char *); #endif /* wrapper for signal interface */ typedef void (*mysig_t)(int); mysig_t mysignal(int sig, mysig_t act); #define signal(a,b) mysignal(a,b) #ifndef HAVE_ISBLANK int isblank(int); #endif #ifndef HAVE_GETPGID pid_t getpgid(pid_t); #endif #ifndef HAVE_ENDGRENT # define endgrent() do { } while(0) #endif #ifndef HAVE_KRB5_GET_ERROR_MESSAGE # define krb5_get_error_message krb5_get_err_text #endif #ifndef HAVE_KRB5_FREE_ERROR_MESSAGE # define krb5_free_error_message(a,b) do { } while(0) #endif #ifndef HAVE_PLEDGE int pledge(const char *promises, const char *paths[]); #endif /* bsd-err.h */ #ifndef HAVE_ERR void err(int, const char *, ...) __attribute__((format(printf, 2, 3))); #endif #ifndef HAVE_ERRX void errx(int, const char *, ...) __attribute__((format(printf, 2, 3))); #endif #ifndef HAVE_WARN void warn(const char *, ...) __attribute__((format(printf, 1, 2))); #endif +#ifndef HAVE_LLABS +long long llabs(long long); +#endif + #endif /* _BSD_MISC_H */ diff --git a/crypto/openssh/openbsd-compat/fmt_scaled.c b/crypto/openssh/openbsd-compat/fmt_scaled.c index edd682a49823..e5533b2de90c 100644 --- a/crypto/openssh/openbsd-compat/fmt_scaled.c +++ b/crypto/openssh/openbsd-compat/fmt_scaled.c @@ -1,274 +1,290 @@ -/* $OpenBSD: fmt_scaled.c,v 1.9 2007/03/20 03:42:52 tedu Exp $ */ +/* $OpenBSD: fmt_scaled.c,v 1.13 2017/03/11 23:37:23 djm Exp $ */ /* * Copyright (c) 2001, 2002, 2003 Ian F. Darwin. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* OPENBSD ORIGINAL: lib/libutil/fmt_scaled.c */ /* * fmt_scaled: Format numbers scaled for human comprehension * scan_scaled: Scan numbers in this format. * * "Human-readable" output uses 4 digits max, and puts a unit suffix at * the end. Makes output compact and easy-to-read esp. on huge disks. * Formatting code was originally in OpenBSD "df", converted to library routine. * Scanning code written for OpenBSD libutil. */ #include "includes.h" #ifndef HAVE_FMT_SCALED #include #include #include #include #include #include typedef enum { NONE = 0, KILO = 1, MEGA = 2, GIGA = 3, TERA = 4, PETA = 5, EXA = 6 } unit_type; /* These three arrays MUST be in sync! XXX make a struct */ static unit_type units[] = { NONE, KILO, MEGA, GIGA, TERA, PETA, EXA }; static char scale_chars[] = "BKMGTPE"; static long long scale_factors[] = { 1LL, 1024LL, 1024LL*1024, 1024LL*1024*1024, 1024LL*1024*1024*1024, 1024LL*1024*1024*1024*1024, 1024LL*1024*1024*1024*1024*1024, }; #define SCALE_LENGTH (sizeof(units)/sizeof(units[0])) #define MAX_DIGITS (SCALE_LENGTH * 3) /* XXX strlen(sprintf("%lld", -1)? */ -/** Convert the given input string "scaled" into numeric in "result". +/* Convert the given input string "scaled" into numeric in "result". * Return 0 on success, -1 and errno set on error. */ int scan_scaled(char *scaled, long long *result) { char *p = scaled; int sign = 0; unsigned int i, ndigits = 0, fract_digits = 0; long long scale_fact = 1, whole = 0, fpart = 0; /* Skip leading whitespace */ - while (isascii(*p) && isspace(*p)) + while (isascii((unsigned char)*p) && isspace((unsigned char)*p)) ++p; /* Then at most one leading + or - */ while (*p == '-' || *p == '+') { if (*p == '-') { if (sign) { errno = EINVAL; return -1; } sign = -1; ++p; } else if (*p == '+') { if (sign) { errno = EINVAL; return -1; } sign = +1; ++p; } } /* Main loop: Scan digits, find decimal point, if present. * We don't allow exponentials, so no scientific notation * (but note that E for Exa might look like e to some!). * Advance 'p' to end, to get scale factor. */ - for (; isascii(*p) && (isdigit(*p) || *p=='.'); ++p) { + for (; isascii((unsigned char)*p) && + (isdigit((unsigned char)*p) || *p=='.'); ++p) { if (*p == '.') { if (fract_digits > 0) { /* oops, more than one '.' */ errno = EINVAL; return -1; } fract_digits = 1; continue; } i = (*p) - '0'; /* whew! finally a digit we can use */ if (fract_digits > 0) { if (fract_digits >= MAX_DIGITS-1) /* ignore extra fractional digits */ continue; fract_digits++; /* for later scaling */ + if (fpart >= LLONG_MAX / 10) { + errno = ERANGE; + return -1; + } fpart *= 10; fpart += i; } else { /* normal digit */ if (++ndigits >= MAX_DIGITS) { errno = ERANGE; return -1; } + if (whole >= LLONG_MAX / 10) { + errno = ERANGE; + return -1; + } whole *= 10; whole += i; } } if (sign) { whole *= sign; fpart *= sign; } /* If no scale factor given, we're done. fraction is discarded. */ if (!*p) { *result = whole; return 0; } /* Validate scale factor, and scale whole and fraction by it. */ for (i = 0; i < SCALE_LENGTH; i++) { - /** Are we there yet? */ + /* Are we there yet? */ if (*p == scale_chars[i] || - *p == tolower(scale_chars[i])) { + *p == tolower((unsigned char)scale_chars[i])) { /* If it ends with alphanumerics after the scale char, bad. */ - if (isalnum(*(p+1))) { + if (isalnum((unsigned char)*(p+1))) { errno = EINVAL; return -1; } scale_fact = scale_factors[i]; + if (whole >= LLONG_MAX / scale_fact) { + errno = ERANGE; + return -1; + } + /* scale whole part */ whole *= scale_fact; /* truncate fpart so it does't overflow. * then scale fractional part. */ while (fpart >= LLONG_MAX / scale_fact) { fpart /= 10; fract_digits--; } fpart *= scale_fact; if (fract_digits > 0) { for (i = 0; i < fract_digits -1; i++) fpart /= 10; } whole += fpart; *result = whole; return 0; } } - errno = ERANGE; + + /* Invalid unit or character */ + errno = EINVAL; return -1; } /* Format the given "number" into human-readable form in "result". * Result must point to an allocated buffer of length FMT_SCALED_STRSIZE. * Return 0 on success, -1 and errno set if error. */ int fmt_scaled(long long number, char *result) { long long abval, fract = 0; unsigned int i; unit_type unit = NONE; - abval = (number < 0LL) ? -number : number; /* no long long_abs yet */ + abval = llabs(number); /* Not every negative long long has a positive representation. * Also check for numbers that are just too darned big to format */ if (abval < 0 || abval / 1024 >= scale_factors[SCALE_LENGTH-1]) { errno = ERANGE; return -1; } /* scale whole part; get unscaled fraction */ for (i = 0; i < SCALE_LENGTH; i++) { if (abval/1024 < scale_factors[i]) { unit = units[i]; fract = (i == 0) ? 0 : abval % scale_factors[i]; number /= scale_factors[i]; if (i > 0) fract /= scale_factors[i - 1]; break; } } fract = (10 * fract + 512) / 1024; /* if the result would be >= 10, round main number */ if (fract == 10) { if (number >= 0) number++; else number--; fract = 0; } if (number == 0) strlcpy(result, "0B", FMT_SCALED_STRSIZE); else if (unit == NONE || number >= 100 || number <= -100) { if (fract >= 5) { if (number >= 0) number++; else number--; } (void)snprintf(result, FMT_SCALED_STRSIZE, "%lld%c", number, scale_chars[unit]); } else (void)snprintf(result, FMT_SCALED_STRSIZE, "%lld.%1lld%c", number, fract, scale_chars[unit]); return 0; } #ifdef MAIN /* * This is the original version of the program in the man page. * Copy-and-paste whatever you need from it. */ int main(int argc, char **argv) { char *cinput = "1.5K", buf[FMT_SCALED_STRSIZE]; long long ninput = 10483892, result; if (scan_scaled(cinput, &result) == 0) printf("\"%s\" -> %lld\n", cinput, result); else perror(cinput); if (fmt_scaled(ninput, buf) == 0) printf("%lld -> \"%s\"\n", ninput, buf); else fprintf(stderr, "%lld invalid (%s)\n", ninput, strerror(errno)); return 0; } #endif #endif /* HAVE_FMT_SCALED */ diff --git a/crypto/openssh/packet.c b/crypto/openssh/packet.c index 9ef5778baffc..b92fc9facb11 100644 --- a/crypto/openssh/packet.c +++ b/crypto/openssh/packet.c @@ -1,3029 +1,3059 @@ -/* $OpenBSD: packet.c,v 1.243 2016/10/11 21:47:45 djm Exp $ */ +/* $OpenBSD: packet.c,v 1.247 2017/03/11 13:07:35 markus Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * This file contains code implementing the packet protocol and communication * with the other side. This same code is used both on client and server side. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * * SSH2 packet format added by Markus Friedl. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" __RCSID("$FreeBSD$"); #include #include "openbsd-compat/sys-queue.h" #include #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "buffer.h" /* typedefs XXX */ #include "key.h" /* typedefs XXX */ #include "xmalloc.h" #include "crc32.h" #include "deattack.h" #include "compat.h" #include "ssh1.h" #include "ssh2.h" #include "cipher.h" #include "sshkey.h" #include "kex.h" #include "digest.h" #include "mac.h" #include "log.h" #include "canohost.h" #include "misc.h" #include "channels.h" #include "ssh.h" #include "packet.h" #include "ssherr.h" #include "sshbuf.h" #include "blacklist_client.h" #ifdef PACKET_DEBUG #define DBG(x) x #else #define DBG(x) #endif #define PACKET_MAX_SIZE (256 * 1024) struct packet_state { u_int32_t seqnr; u_int32_t packets; u_int64_t blocks; u_int64_t bytes; }; struct packet { TAILQ_ENTRY(packet) next; u_char type; struct sshbuf *payload; }; struct session_state { /* * This variable contains the file descriptors used for * communicating with the other side. connection_in is used for * reading; connection_out for writing. These can be the same * descriptor, in which case it is assumed to be a socket. */ int connection_in; int connection_out; /* Protocol flags for the remote side. */ u_int remote_protocol_flags; /* Encryption context for receiving data. Only used for decryption. */ struct sshcipher_ctx *receive_context; /* Encryption context for sending data. Only used for encryption. */ struct sshcipher_ctx *send_context; /* Buffer for raw input data from the socket. */ struct sshbuf *input; /* Buffer for raw output data going to the socket. */ struct sshbuf *output; /* Buffer for the partial outgoing packet being constructed. */ struct sshbuf *outgoing_packet; /* Buffer for the incoming packet currently being processed. */ struct sshbuf *incoming_packet; /* Scratch buffer for packet compression/decompression. */ struct sshbuf *compression_buffer; /* Incoming/outgoing compression dictionaries */ z_stream compression_in_stream; z_stream compression_out_stream; int compression_in_started; int compression_out_started; int compression_in_failures; int compression_out_failures; /* * Flag indicating whether packet compression/decompression is * enabled. */ int packet_compression; /* default maximum packet size */ u_int max_packet_size; /* Flag indicating whether this module has been initialized. */ int initialized; /* Set to true if the connection is interactive. */ int interactive_mode; /* Set to true if we are the server side. */ int server_side; /* Set to true if we are authenticated. */ int after_authentication; int keep_alive_timeouts; /* The maximum time that we will wait to send or receive a packet */ int packet_timeout_ms; /* Session key information for Encryption and MAC */ struct newkeys *newkeys[MODE_MAX]; struct packet_state p_read, p_send; /* Volume-based rekeying */ u_int64_t max_blocks_in, max_blocks_out, rekey_limit; /* Time-based rekeying */ u_int32_t rekey_interval; /* how often in seconds */ time_t rekey_time; /* time of last rekeying */ /* Session key for protocol v1 */ u_char ssh1_key[SSH_SESSION_KEY_LENGTH]; u_int ssh1_keylen; /* roundup current message to extra_pad bytes */ u_char extra_pad; /* XXX discard incoming data after MAC error */ u_int packet_discard; size_t packet_discard_mac_already; struct sshmac *packet_discard_mac; /* Used in packet_read_poll2() */ u_int packlen; /* Used in packet_send2 */ int rekeying; /* Used in ssh_packet_send_mux() */ int mux; /* Used in packet_set_interactive */ int set_interactive_called; /* Used in packet_set_maxsize */ int set_maxsize_called; /* One-off warning about weak ciphers */ int cipher_warning_done; /* SSH1 CRC compensation attack detector */ struct deattack_ctx deattack; /* Hook for fuzzing inbound packets */ ssh_packet_hook_fn *hook_in; void *hook_in_ctx; TAILQ_HEAD(, packet) outgoing; }; struct ssh * ssh_alloc_session_state(void) { struct ssh *ssh = NULL; struct session_state *state = NULL; if ((ssh = calloc(1, sizeof(*ssh))) == NULL || (state = calloc(1, sizeof(*state))) == NULL || (state->input = sshbuf_new()) == NULL || (state->output = sshbuf_new()) == NULL || (state->outgoing_packet = sshbuf_new()) == NULL || (state->incoming_packet = sshbuf_new()) == NULL) goto fail; TAILQ_INIT(&state->outgoing); TAILQ_INIT(&ssh->private_keys); TAILQ_INIT(&ssh->public_keys); state->connection_in = -1; state->connection_out = -1; state->max_packet_size = 32768; state->packet_timeout_ms = -1; state->p_send.packets = state->p_read.packets = 0; state->initialized = 1; /* * ssh_packet_send2() needs to queue packets until * we've done the initial key exchange. */ state->rekeying = 1; ssh->state = state; return ssh; fail: if (state) { sshbuf_free(state->input); sshbuf_free(state->output); sshbuf_free(state->incoming_packet); sshbuf_free(state->outgoing_packet); free(state); } free(ssh); return NULL; } void ssh_packet_set_input_hook(struct ssh *ssh, ssh_packet_hook_fn *hook, void *ctx) { ssh->state->hook_in = hook; ssh->state->hook_in_ctx = ctx; } /* Returns nonzero if rekeying is in progress */ int ssh_packet_is_rekeying(struct ssh *ssh) { return compat20 && (ssh->state->rekeying || (ssh->kex != NULL && ssh->kex->done == 0)); } /* * Sets the descriptors used for communication. Disables encryption until * packet_set_encryption_key is called. */ struct ssh * ssh_packet_set_connection(struct ssh *ssh, int fd_in, int fd_out) { struct session_state *state; const struct sshcipher *none = cipher_by_name("none"); int r; if (none == NULL) { error("%s: cannot load cipher 'none'", __func__); return NULL; } if (ssh == NULL) ssh = ssh_alloc_session_state(); if (ssh == NULL) { error("%s: cound not allocate state", __func__); return NULL; } state = ssh->state; state->connection_in = fd_in; state->connection_out = fd_out; if ((r = cipher_init(&state->send_context, none, (const u_char *)"", 0, NULL, 0, CIPHER_ENCRYPT)) != 0 || (r = cipher_init(&state->receive_context, none, (const u_char *)"", 0, NULL, 0, CIPHER_DECRYPT)) != 0) { error("%s: cipher_init failed: %s", __func__, ssh_err(r)); free(ssh); /* XXX need ssh_free_session_state? */ return NULL; } state->newkeys[MODE_IN] = state->newkeys[MODE_OUT] = NULL; deattack_init(&state->deattack); /* * Cache the IP address of the remote connection for use in error * messages that might be generated after the connection has closed. */ (void)ssh_remote_ipaddr(ssh); return ssh; } void ssh_packet_set_timeout(struct ssh *ssh, int timeout, int count) { struct session_state *state = ssh->state; if (timeout <= 0 || count <= 0) { state->packet_timeout_ms = -1; return; } if ((INT_MAX / 1000) / count < timeout) state->packet_timeout_ms = INT_MAX; else state->packet_timeout_ms = timeout * count * 1000; } void ssh_packet_set_mux(struct ssh *ssh) { ssh->state->mux = 1; ssh->state->rekeying = 0; } int ssh_packet_get_mux(struct ssh *ssh) { return ssh->state->mux; } +int +ssh_packet_set_log_preamble(struct ssh *ssh, const char *fmt, ...) +{ + va_list args; + int r; + + free(ssh->log_preamble); + if (fmt == NULL) + ssh->log_preamble = NULL; + else { + va_start(args, fmt); + r = vasprintf(&ssh->log_preamble, fmt, args); + va_end(args); + if (r < 0 || ssh->log_preamble == NULL) + return SSH_ERR_ALLOC_FAIL; + } + return 0; +} + int ssh_packet_stop_discard(struct ssh *ssh) { struct session_state *state = ssh->state; int r; if (state->packet_discard_mac) { char buf[1024]; size_t dlen = PACKET_MAX_SIZE; if (dlen > state->packet_discard_mac_already) dlen -= state->packet_discard_mac_already; memset(buf, 'a', sizeof(buf)); while (sshbuf_len(state->incoming_packet) < dlen) if ((r = sshbuf_put(state->incoming_packet, buf, sizeof(buf))) != 0) return r; (void) mac_compute(state->packet_discard_mac, state->p_read.seqnr, sshbuf_ptr(state->incoming_packet), dlen, NULL, 0); } logit("Finished discarding for %.200s port %d", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); return SSH_ERR_MAC_INVALID; } static int ssh_packet_start_discard(struct ssh *ssh, struct sshenc *enc, struct sshmac *mac, size_t mac_already, u_int discard) { struct session_state *state = ssh->state; int r; if (enc == NULL || !cipher_is_cbc(enc->cipher) || (mac && mac->etm)) { if ((r = sshpkt_disconnect(ssh, "Packet corrupt")) != 0) return r; return SSH_ERR_MAC_INVALID; } /* * Record number of bytes over which the mac has already * been computed in order to minimize timing attacks. */ if (mac && mac->enabled) { state->packet_discard_mac = mac; state->packet_discard_mac_already = mac_already; } if (sshbuf_len(state->input) >= discard) return ssh_packet_stop_discard(ssh); state->packet_discard = discard - sshbuf_len(state->input); return 0; } /* Returns 1 if remote host is connected via socket, 0 if not. */ int ssh_packet_connection_is_on_socket(struct ssh *ssh) { struct session_state *state = ssh->state; struct sockaddr_storage from, to; socklen_t fromlen, tolen; if (state->connection_in == -1 || state->connection_out == -1) return 0; /* filedescriptors in and out are the same, so it's a socket */ if (state->connection_in == state->connection_out) return 1; fromlen = sizeof(from); memset(&from, 0, sizeof(from)); if (getpeername(state->connection_in, (struct sockaddr *)&from, &fromlen) < 0) return 0; tolen = sizeof(to); memset(&to, 0, sizeof(to)); if (getpeername(state->connection_out, (struct sockaddr *)&to, &tolen) < 0) return 0; if (fromlen != tolen || memcmp(&from, &to, fromlen) != 0) return 0; if (from.ss_family != AF_INET && from.ss_family != AF_INET6) return 0; return 1; } void ssh_packet_get_bytes(struct ssh *ssh, u_int64_t *ibytes, u_int64_t *obytes) { if (ibytes) *ibytes = ssh->state->p_read.bytes; if (obytes) *obytes = ssh->state->p_send.bytes; } int ssh_packet_connection_af(struct ssh *ssh) { struct sockaddr_storage to; socklen_t tolen = sizeof(to); memset(&to, 0, sizeof(to)); if (getsockname(ssh->state->connection_out, (struct sockaddr *)&to, &tolen) < 0) return 0; #ifdef IPV4_IN_IPV6 if (to.ss_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&to)->sin6_addr)) return AF_INET; #endif return to.ss_family; } /* Sets the connection into non-blocking mode. */ void ssh_packet_set_nonblocking(struct ssh *ssh) { /* Set the socket into non-blocking mode. */ set_nonblock(ssh->state->connection_in); if (ssh->state->connection_out != ssh->state->connection_in) set_nonblock(ssh->state->connection_out); } /* Returns the socket used for reading. */ int ssh_packet_get_connection_in(struct ssh *ssh) { return ssh->state->connection_in; } /* Returns the descriptor used for writing. */ int ssh_packet_get_connection_out(struct ssh *ssh) { return ssh->state->connection_out; } /* * Returns the IP-address of the remote host as a string. The returned * string must not be freed. */ const char * ssh_remote_ipaddr(struct ssh *ssh) { const int sock = ssh->state->connection_in; /* Check whether we have cached the ipaddr. */ if (ssh->remote_ipaddr == NULL) { if (ssh_packet_connection_is_on_socket(ssh)) { ssh->remote_ipaddr = get_peer_ipaddr(sock); ssh->remote_port = get_peer_port(sock); ssh->local_ipaddr = get_local_ipaddr(sock); ssh->local_port = get_local_port(sock); } else { ssh->remote_ipaddr = strdup("UNKNOWN"); ssh->remote_port = 65535; ssh->local_ipaddr = strdup("UNKNOWN"); ssh->local_port = 65535; } } return ssh->remote_ipaddr; } /* Returns the port number of the remote host. */ int ssh_remote_port(struct ssh *ssh) { (void)ssh_remote_ipaddr(ssh); /* Will lookup and cache. */ return ssh->remote_port; } /* * Returns the IP-address of the local host as a string. The returned * string must not be freed. */ const char * ssh_local_ipaddr(struct ssh *ssh) { (void)ssh_remote_ipaddr(ssh); /* Will lookup and cache. */ return ssh->local_ipaddr; } /* Returns the port number of the local host. */ int ssh_local_port(struct ssh *ssh) { (void)ssh_remote_ipaddr(ssh); /* Will lookup and cache. */ return ssh->local_port; } /* Closes the connection and clears and frees internal data structures. */ void ssh_packet_close(struct ssh *ssh) { struct session_state *state = ssh->state; u_int mode; if (!state->initialized) return; state->initialized = 0; if (state->connection_in == state->connection_out) { shutdown(state->connection_out, SHUT_RDWR); close(state->connection_out); } else { close(state->connection_in); close(state->connection_out); } sshbuf_free(state->input); sshbuf_free(state->output); sshbuf_free(state->outgoing_packet); sshbuf_free(state->incoming_packet); for (mode = 0; mode < MODE_MAX; mode++) kex_free_newkeys(state->newkeys[mode]); if (state->compression_buffer) { sshbuf_free(state->compression_buffer); if (state->compression_out_started) { z_streamp stream = &state->compression_out_stream; debug("compress outgoing: " "raw data %llu, compressed %llu, factor %.2f", (unsigned long long)stream->total_in, (unsigned long long)stream->total_out, stream->total_in == 0 ? 0.0 : (double) stream->total_out / stream->total_in); if (state->compression_out_failures == 0) deflateEnd(stream); } if (state->compression_in_started) { z_streamp stream = &state->compression_out_stream; debug("compress incoming: " "raw data %llu, compressed %llu, factor %.2f", (unsigned long long)stream->total_out, (unsigned long long)stream->total_in, stream->total_out == 0 ? 0.0 : (double) stream->total_in / stream->total_out); if (state->compression_in_failures == 0) inflateEnd(stream); } } cipher_free(state->send_context); cipher_free(state->receive_context); state->send_context = state->receive_context = NULL; free(ssh->remote_ipaddr); ssh->remote_ipaddr = NULL; free(ssh->state); ssh->state = NULL; } /* Sets remote side protocol flags. */ void ssh_packet_set_protocol_flags(struct ssh *ssh, u_int protocol_flags) { ssh->state->remote_protocol_flags = protocol_flags; } /* Returns the remote protocol flags set earlier by the above function. */ u_int ssh_packet_get_protocol_flags(struct ssh *ssh) { return ssh->state->remote_protocol_flags; } /* * Starts packet compression from the next packet on in both directions. * Level is compression level 1 (fastest) - 9 (slow, best) as in gzip. */ static int ssh_packet_init_compression(struct ssh *ssh) { if (!ssh->state->compression_buffer && ((ssh->state->compression_buffer = sshbuf_new()) == NULL)) return SSH_ERR_ALLOC_FAIL; return 0; } static int start_compression_out(struct ssh *ssh, int level) { if (level < 1 || level > 9) return SSH_ERR_INVALID_ARGUMENT; debug("Enabling compression at level %d.", level); if (ssh->state->compression_out_started == 1) deflateEnd(&ssh->state->compression_out_stream); switch (deflateInit(&ssh->state->compression_out_stream, level)) { case Z_OK: ssh->state->compression_out_started = 1; break; case Z_MEM_ERROR: return SSH_ERR_ALLOC_FAIL; default: return SSH_ERR_INTERNAL_ERROR; } return 0; } static int start_compression_in(struct ssh *ssh) { if (ssh->state->compression_in_started == 1) inflateEnd(&ssh->state->compression_in_stream); switch (inflateInit(&ssh->state->compression_in_stream)) { case Z_OK: ssh->state->compression_in_started = 1; break; case Z_MEM_ERROR: return SSH_ERR_ALLOC_FAIL; default: return SSH_ERR_INTERNAL_ERROR; } return 0; } int ssh_packet_start_compression(struct ssh *ssh, int level) { int r; if (ssh->state->packet_compression && !compat20) return SSH_ERR_INTERNAL_ERROR; ssh->state->packet_compression = 1; if ((r = ssh_packet_init_compression(ssh)) != 0 || (r = start_compression_in(ssh)) != 0 || (r = start_compression_out(ssh, level)) != 0) return r; return 0; } /* XXX remove need for separate compression buffer */ static int compress_buffer(struct ssh *ssh, struct sshbuf *in, struct sshbuf *out) { u_char buf[4096]; int r, status; if (ssh->state->compression_out_started != 1) return SSH_ERR_INTERNAL_ERROR; /* This case is not handled below. */ if (sshbuf_len(in) == 0) return 0; /* Input is the contents of the input buffer. */ if ((ssh->state->compression_out_stream.next_in = sshbuf_mutable_ptr(in)) == NULL) return SSH_ERR_INTERNAL_ERROR; ssh->state->compression_out_stream.avail_in = sshbuf_len(in); /* Loop compressing until deflate() returns with avail_out != 0. */ do { /* Set up fixed-size output buffer. */ ssh->state->compression_out_stream.next_out = buf; ssh->state->compression_out_stream.avail_out = sizeof(buf); /* Compress as much data into the buffer as possible. */ status = deflate(&ssh->state->compression_out_stream, Z_PARTIAL_FLUSH); switch (status) { case Z_MEM_ERROR: return SSH_ERR_ALLOC_FAIL; case Z_OK: /* Append compressed data to output_buffer. */ if ((r = sshbuf_put(out, buf, sizeof(buf) - ssh->state->compression_out_stream.avail_out)) != 0) return r; break; case Z_STREAM_ERROR: default: ssh->state->compression_out_failures++; return SSH_ERR_INVALID_FORMAT; } } while (ssh->state->compression_out_stream.avail_out == 0); return 0; } static int uncompress_buffer(struct ssh *ssh, struct sshbuf *in, struct sshbuf *out) { u_char buf[4096]; int r, status; if (ssh->state->compression_in_started != 1) return SSH_ERR_INTERNAL_ERROR; if ((ssh->state->compression_in_stream.next_in = sshbuf_mutable_ptr(in)) == NULL) return SSH_ERR_INTERNAL_ERROR; ssh->state->compression_in_stream.avail_in = sshbuf_len(in); for (;;) { /* Set up fixed-size output buffer. */ ssh->state->compression_in_stream.next_out = buf; ssh->state->compression_in_stream.avail_out = sizeof(buf); status = inflate(&ssh->state->compression_in_stream, Z_PARTIAL_FLUSH); switch (status) { case Z_OK: if ((r = sshbuf_put(out, buf, sizeof(buf) - ssh->state->compression_in_stream.avail_out)) != 0) return r; break; case Z_BUF_ERROR: /* * Comments in zlib.h say that we should keep calling * inflate() until we get an error. This appears to * be the error that we get. */ return 0; case Z_DATA_ERROR: return SSH_ERR_INVALID_FORMAT; case Z_MEM_ERROR: return SSH_ERR_ALLOC_FAIL; case Z_STREAM_ERROR: default: ssh->state->compression_in_failures++; return SSH_ERR_INTERNAL_ERROR; } } /* NOTREACHED */ } /* * Causes any further packets to be encrypted using the given key. The same * key is used for both sending and reception. However, both directions are * encrypted independently of each other. */ void ssh_packet_set_encryption_key(struct ssh *ssh, const u_char *key, u_int keylen, int number) { #ifndef WITH_SSH1 fatal("no SSH protocol 1 support"); #else /* WITH_SSH1 */ struct session_state *state = ssh->state; const struct sshcipher *cipher = cipher_by_number(number); int r; const char *wmsg; if (cipher == NULL) fatal("%s: unknown cipher number %d", __func__, number); if (keylen < 20) fatal("%s: keylen too small: %d", __func__, keylen); if (keylen > SSH_SESSION_KEY_LENGTH) fatal("%s: keylen too big: %d", __func__, keylen); memcpy(state->ssh1_key, key, keylen); state->ssh1_keylen = keylen; if ((r = cipher_init(&state->send_context, cipher, key, keylen, NULL, 0, CIPHER_ENCRYPT)) != 0 || (r = cipher_init(&state->receive_context, cipher, key, keylen, NULL, 0, CIPHER_DECRYPT) != 0)) fatal("%s: cipher_init failed: %s", __func__, ssh_err(r)); if (!state->cipher_warning_done && ((wmsg = cipher_warning_message(state->send_context)) != NULL || (wmsg = cipher_warning_message(state->send_context)) != NULL)) { error("Warning: %s", wmsg); state->cipher_warning_done = 1; } #endif /* WITH_SSH1 */ } /* * Finalizes and sends the packet. If the encryption key has been set, * encrypts the packet before sending. */ int ssh_packet_send1(struct ssh *ssh) { struct session_state *state = ssh->state; u_char buf[8], *cp; int r, padding, len; u_int checksum; /* * If using packet compression, compress the payload of the outgoing * packet. */ if (state->packet_compression) { sshbuf_reset(state->compression_buffer); /* Skip padding. */ if ((r = sshbuf_consume(state->outgoing_packet, 8)) != 0) goto out; /* padding */ if ((r = sshbuf_put(state->compression_buffer, "\0\0\0\0\0\0\0\0", 8)) != 0) goto out; if ((r = compress_buffer(ssh, state->outgoing_packet, state->compression_buffer)) != 0) goto out; sshbuf_reset(state->outgoing_packet); if ((r = sshbuf_putb(state->outgoing_packet, state->compression_buffer)) != 0) goto out; } /* Compute packet length without padding (add checksum, remove padding). */ len = sshbuf_len(state->outgoing_packet) + 4 - 8; /* Insert padding. Initialized to zero in packet_start1() */ padding = 8 - len % 8; if (!cipher_ctx_is_plaintext(state->send_context)) { cp = sshbuf_mutable_ptr(state->outgoing_packet); if (cp == NULL) { r = SSH_ERR_INTERNAL_ERROR; goto out; } arc4random_buf(cp + 8 - padding, padding); } if ((r = sshbuf_consume(state->outgoing_packet, 8 - padding)) != 0) goto out; /* Add check bytes. */ checksum = ssh_crc32(sshbuf_ptr(state->outgoing_packet), sshbuf_len(state->outgoing_packet)); POKE_U32(buf, checksum); if ((r = sshbuf_put(state->outgoing_packet, buf, 4)) != 0) goto out; #ifdef PACKET_DEBUG fprintf(stderr, "packet_send plain: "); sshbuf_dump(state->outgoing_packet, stderr); #endif /* Append to output. */ POKE_U32(buf, len); if ((r = sshbuf_put(state->output, buf, 4)) != 0) goto out; if ((r = sshbuf_reserve(state->output, sshbuf_len(state->outgoing_packet), &cp)) != 0) goto out; if ((r = cipher_crypt(state->send_context, 0, cp, sshbuf_ptr(state->outgoing_packet), sshbuf_len(state->outgoing_packet), 0, 0)) != 0) goto out; #ifdef PACKET_DEBUG fprintf(stderr, "encrypted: "); sshbuf_dump(state->output, stderr); #endif state->p_send.packets++; state->p_send.bytes += len + sshbuf_len(state->outgoing_packet); sshbuf_reset(state->outgoing_packet); /* * Note that the packet is now only buffered in output. It won't be * actually sent until ssh_packet_write_wait or ssh_packet_write_poll * is called. */ r = 0; out: return r; } int ssh_set_newkeys(struct ssh *ssh, int mode) { struct session_state *state = ssh->state; struct sshenc *enc; struct sshmac *mac; struct sshcomp *comp; struct sshcipher_ctx **ccp; struct packet_state *ps; u_int64_t *max_blocks; const char *wmsg, *dir; int r, crypt_type; debug2("set_newkeys: mode %d", mode); if (mode == MODE_OUT) { dir = "output"; ccp = &state->send_context; crypt_type = CIPHER_ENCRYPT; ps = &state->p_send; max_blocks = &state->max_blocks_out; } else { dir = "input"; ccp = &state->receive_context; crypt_type = CIPHER_DECRYPT; ps = &state->p_read; max_blocks = &state->max_blocks_in; } if (state->newkeys[mode] != NULL) { debug("%s: rekeying after %llu %s blocks" " (%llu bytes total)", __func__, (unsigned long long)ps->blocks, dir, (unsigned long long)ps->bytes); cipher_free(*ccp); *ccp = NULL; enc = &state->newkeys[mode]->enc; mac = &state->newkeys[mode]->mac; comp = &state->newkeys[mode]->comp; mac_clear(mac); explicit_bzero(enc->iv, enc->iv_len); explicit_bzero(enc->key, enc->key_len); explicit_bzero(mac->key, mac->key_len); free(enc->name); free(enc->iv); free(enc->key); free(mac->name); free(mac->key); free(comp->name); free(state->newkeys[mode]); } /* note that both bytes and the seqnr are not reset */ ps->packets = ps->blocks = 0; /* move newkeys from kex to state */ if ((state->newkeys[mode] = ssh->kex->newkeys[mode]) == NULL) return SSH_ERR_INTERNAL_ERROR; ssh->kex->newkeys[mode] = NULL; enc = &state->newkeys[mode]->enc; mac = &state->newkeys[mode]->mac; comp = &state->newkeys[mode]->comp; if (cipher_authlen(enc->cipher) == 0) { if ((r = mac_init(mac)) != 0) return r; } mac->enabled = 1; DBG(debug("cipher_init_context: %d", mode)); if ((r = cipher_init(ccp, enc->cipher, enc->key, enc->key_len, enc->iv, enc->iv_len, crypt_type)) != 0) return r; if (!state->cipher_warning_done && (wmsg = cipher_warning_message(*ccp)) != NULL) { error("Warning: %s", wmsg); state->cipher_warning_done = 1; } /* Deleting the keys does not gain extra security */ /* explicit_bzero(enc->iv, enc->block_size); explicit_bzero(enc->key, enc->key_len); explicit_bzero(mac->key, mac->key_len); */ if ((comp->type == COMP_ZLIB || (comp->type == COMP_DELAYED && state->after_authentication)) && comp->enabled == 0) { if ((r = ssh_packet_init_compression(ssh)) < 0) return r; if (mode == MODE_OUT) { if ((r = start_compression_out(ssh, 6)) != 0) return r; } else { if ((r = start_compression_in(ssh)) != 0) return r; } comp->enabled = 1; } /* * The 2^(blocksize*2) limit is too expensive for 3DES, * blowfish, etc, so enforce a 1GB limit for small blocksizes. */ if (enc->block_size >= 16) *max_blocks = (u_int64_t)1 << (enc->block_size*2); else *max_blocks = ((u_int64_t)1 << 30) / enc->block_size; if (state->rekey_limit) *max_blocks = MINIMUM(*max_blocks, state->rekey_limit / enc->block_size); debug("rekey after %llu blocks", (unsigned long long)*max_blocks); return 0; } #define MAX_PACKETS (1U<<31) static int ssh_packet_need_rekeying(struct ssh *ssh, u_int outbound_packet_len) { struct session_state *state = ssh->state; u_int32_t out_blocks; /* XXX client can't cope with rekeying pre-auth */ if (!state->after_authentication) return 0; /* Haven't keyed yet or KEX in progress. */ if (ssh->kex == NULL || ssh_packet_is_rekeying(ssh)) return 0; /* Peer can't rekey */ if (ssh->compat & SSH_BUG_NOREKEY) return 0; /* * Permit one packet in or out per rekey - this allows us to * make progress when rekey limits are very small. */ if (state->p_send.packets == 0 && state->p_read.packets == 0) return 0; /* Time-based rekeying */ if (state->rekey_interval != 0 && - state->rekey_time + state->rekey_interval <= monotime()) + (int64_t)state->rekey_time + state->rekey_interval <= monotime()) return 1; /* Always rekey when MAX_PACKETS sent in either direction */ if (state->p_send.packets > MAX_PACKETS || state->p_read.packets > MAX_PACKETS) return 1; /* Rekey after (cipher-specific) maxiumum blocks */ out_blocks = ROUNDUP(outbound_packet_len, state->newkeys[MODE_OUT]->enc.block_size); return (state->max_blocks_out && (state->p_send.blocks + out_blocks > state->max_blocks_out)) || (state->max_blocks_in && (state->p_read.blocks > state->max_blocks_in)); } /* * Delayed compression for SSH2 is enabled after authentication: * This happens on the server side after a SSH2_MSG_USERAUTH_SUCCESS is sent, * and on the client side after a SSH2_MSG_USERAUTH_SUCCESS is received. */ static int ssh_packet_enable_delayed_compress(struct ssh *ssh) { struct session_state *state = ssh->state; struct sshcomp *comp = NULL; int r, mode; /* * Remember that we are past the authentication step, so rekeying * with COMP_DELAYED will turn on compression immediately. */ state->after_authentication = 1; for (mode = 0; mode < MODE_MAX; mode++) { /* protocol error: USERAUTH_SUCCESS received before NEWKEYS */ if (state->newkeys[mode] == NULL) continue; comp = &state->newkeys[mode]->comp; if (comp && !comp->enabled && comp->type == COMP_DELAYED) { if ((r = ssh_packet_init_compression(ssh)) != 0) return r; if (mode == MODE_OUT) { if ((r = start_compression_out(ssh, 6)) != 0) return r; } else { if ((r = start_compression_in(ssh)) != 0) return r; } comp->enabled = 1; } } return 0; } /* Used to mute debug logging for noisy packet types */ int ssh_packet_log_type(u_char type) { switch (type) { case SSH2_MSG_CHANNEL_DATA: case SSH2_MSG_CHANNEL_EXTENDED_DATA: case SSH2_MSG_CHANNEL_WINDOW_ADJUST: return 0; default: return 1; } } /* * Finalize packet in SSH2 format (compress, mac, encrypt, enqueue) */ int ssh_packet_send2_wrapped(struct ssh *ssh) { struct session_state *state = ssh->state; u_char type, *cp, macbuf[SSH_DIGEST_MAX_LENGTH]; u_char tmp, padlen, pad = 0; u_int authlen = 0, aadlen = 0; u_int len; struct sshenc *enc = NULL; struct sshmac *mac = NULL; struct sshcomp *comp = NULL; int r, block_size; if (state->newkeys[MODE_OUT] != NULL) { enc = &state->newkeys[MODE_OUT]->enc; mac = &state->newkeys[MODE_OUT]->mac; comp = &state->newkeys[MODE_OUT]->comp; /* disable mac for authenticated encryption */ if ((authlen = cipher_authlen(enc->cipher)) != 0) mac = NULL; } block_size = enc ? enc->block_size : 8; aadlen = (mac && mac->enabled && mac->etm) || authlen ? 4 : 0; type = (sshbuf_ptr(state->outgoing_packet))[5]; if (ssh_packet_log_type(type)) debug3("send packet: type %u", type); #ifdef PACKET_DEBUG fprintf(stderr, "plain: "); sshbuf_dump(state->outgoing_packet, stderr); #endif if (comp && comp->enabled) { len = sshbuf_len(state->outgoing_packet); /* skip header, compress only payload */ if ((r = sshbuf_consume(state->outgoing_packet, 5)) != 0) goto out; sshbuf_reset(state->compression_buffer); if ((r = compress_buffer(ssh, state->outgoing_packet, state->compression_buffer)) != 0) goto out; sshbuf_reset(state->outgoing_packet); if ((r = sshbuf_put(state->outgoing_packet, "\0\0\0\0\0", 5)) != 0 || (r = sshbuf_putb(state->outgoing_packet, state->compression_buffer)) != 0) goto out; DBG(debug("compression: raw %d compressed %zd", len, sshbuf_len(state->outgoing_packet))); } /* sizeof (packet_len + pad_len + payload) */ len = sshbuf_len(state->outgoing_packet); /* * calc size of padding, alloc space, get random data, * minimum padding is 4 bytes */ len -= aadlen; /* packet length is not encrypted for EtM modes */ padlen = block_size - (len % block_size); if (padlen < 4) padlen += block_size; if (state->extra_pad) { tmp = state->extra_pad; state->extra_pad = ROUNDUP(state->extra_pad, block_size); /* check if roundup overflowed */ if (state->extra_pad < tmp) return SSH_ERR_INVALID_ARGUMENT; tmp = (len + padlen) % state->extra_pad; /* Check whether pad calculation below will underflow */ if (tmp > state->extra_pad) return SSH_ERR_INVALID_ARGUMENT; pad = state->extra_pad - tmp; DBG(debug3("%s: adding %d (len %d padlen %d extra_pad %d)", __func__, pad, len, padlen, state->extra_pad)); tmp = padlen; padlen += pad; /* Check whether padlen calculation overflowed */ if (padlen < tmp) return SSH_ERR_INVALID_ARGUMENT; /* overflow */ state->extra_pad = 0; } if ((r = sshbuf_reserve(state->outgoing_packet, padlen, &cp)) != 0) goto out; if (enc && !cipher_ctx_is_plaintext(state->send_context)) { /* random padding */ arc4random_buf(cp, padlen); } else { /* clear padding */ explicit_bzero(cp, padlen); } /* sizeof (packet_len + pad_len + payload + padding) */ len = sshbuf_len(state->outgoing_packet); cp = sshbuf_mutable_ptr(state->outgoing_packet); if (cp == NULL) { r = SSH_ERR_INTERNAL_ERROR; goto out; } /* packet_length includes payload, padding and padding length field */ POKE_U32(cp, len - 4); cp[4] = padlen; DBG(debug("send: len %d (includes padlen %d, aadlen %d)", len, padlen, aadlen)); /* compute MAC over seqnr and packet(length fields, payload, padding) */ if (mac && mac->enabled && !mac->etm) { if ((r = mac_compute(mac, state->p_send.seqnr, sshbuf_ptr(state->outgoing_packet), len, macbuf, sizeof(macbuf))) != 0) goto out; DBG(debug("done calc MAC out #%d", state->p_send.seqnr)); } /* encrypt packet and append to output buffer. */ if ((r = sshbuf_reserve(state->output, sshbuf_len(state->outgoing_packet) + authlen, &cp)) != 0) goto out; if ((r = cipher_crypt(state->send_context, state->p_send.seqnr, cp, sshbuf_ptr(state->outgoing_packet), len - aadlen, aadlen, authlen)) != 0) goto out; /* append unencrypted MAC */ if (mac && mac->enabled) { if (mac->etm) { /* EtM: compute mac over aadlen + cipher text */ if ((r = mac_compute(mac, state->p_send.seqnr, cp, len, macbuf, sizeof(macbuf))) != 0) goto out; DBG(debug("done calc MAC(EtM) out #%d", state->p_send.seqnr)); } if ((r = sshbuf_put(state->output, macbuf, mac->mac_len)) != 0) goto out; } #ifdef PACKET_DEBUG fprintf(stderr, "encrypted: "); sshbuf_dump(state->output, stderr); #endif /* increment sequence number for outgoing packets */ if (++state->p_send.seqnr == 0) logit("outgoing seqnr wraps around"); if (++state->p_send.packets == 0) if (!(ssh->compat & SSH_BUG_NOREKEY)) return SSH_ERR_NEED_REKEY; state->p_send.blocks += len / block_size; state->p_send.bytes += len; sshbuf_reset(state->outgoing_packet); if (type == SSH2_MSG_NEWKEYS) r = ssh_set_newkeys(ssh, MODE_OUT); else if (type == SSH2_MSG_USERAUTH_SUCCESS && state->server_side) r = ssh_packet_enable_delayed_compress(ssh); else r = 0; out: return r; } /* returns non-zero if the specified packet type is usec by KEX */ static int ssh_packet_type_is_kex(u_char type) { return type >= SSH2_MSG_TRANSPORT_MIN && type <= SSH2_MSG_TRANSPORT_MAX && type != SSH2_MSG_SERVICE_REQUEST && type != SSH2_MSG_SERVICE_ACCEPT && type != SSH2_MSG_EXT_INFO; } int ssh_packet_send2(struct ssh *ssh) { struct session_state *state = ssh->state; struct packet *p; u_char type; int r, need_rekey; if (sshbuf_len(state->outgoing_packet) < 6) return SSH_ERR_INTERNAL_ERROR; type = sshbuf_ptr(state->outgoing_packet)[5]; need_rekey = !ssh_packet_type_is_kex(type) && ssh_packet_need_rekeying(ssh, sshbuf_len(state->outgoing_packet)); /* * During rekeying we can only send key exchange messages. * Queue everything else. */ if ((need_rekey || state->rekeying) && !ssh_packet_type_is_kex(type)) { if (need_rekey) debug3("%s: rekex triggered", __func__); debug("enqueue packet: %u", type); p = calloc(1, sizeof(*p)); if (p == NULL) return SSH_ERR_ALLOC_FAIL; p->type = type; p->payload = state->outgoing_packet; TAILQ_INSERT_TAIL(&state->outgoing, p, next); state->outgoing_packet = sshbuf_new(); if (state->outgoing_packet == NULL) return SSH_ERR_ALLOC_FAIL; if (need_rekey) { /* * This packet triggered a rekey, so send the * KEXINIT now. * NB. reenters this function via kex_start_rekex(). */ return kex_start_rekex(ssh); } return 0; } /* rekeying starts with sending KEXINIT */ if (type == SSH2_MSG_KEXINIT) state->rekeying = 1; if ((r = ssh_packet_send2_wrapped(ssh)) != 0) return r; /* after a NEWKEYS message we can send the complete queue */ if (type == SSH2_MSG_NEWKEYS) { state->rekeying = 0; state->rekey_time = monotime(); while ((p = TAILQ_FIRST(&state->outgoing))) { type = p->type; /* * If this packet triggers a rekex, then skip the * remaining packets in the queue for now. * NB. re-enters this function via kex_start_rekex. */ if (ssh_packet_need_rekeying(ssh, sshbuf_len(p->payload))) { debug3("%s: queued packet triggered rekex", __func__); return kex_start_rekex(ssh); } debug("dequeue packet: %u", type); sshbuf_free(state->outgoing_packet); state->outgoing_packet = p->payload; TAILQ_REMOVE(&state->outgoing, p, next); memset(p, 0, sizeof(*p)); free(p); if ((r = ssh_packet_send2_wrapped(ssh)) != 0) return r; } } return 0; } /* * Waits until a packet has been received, and returns its type. Note that * no other data is processed until this returns, so this function should not * be used during the interactive session. */ int ssh_packet_read_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) { struct session_state *state = ssh->state; int len, r, ms_remain; fd_set *setp; char buf[8192]; struct timeval timeout, start, *timeoutp = NULL; DBG(debug("packet_read()")); setp = calloc(howmany(state->connection_in + 1, NFDBITS), sizeof(fd_mask)); if (setp == NULL) return SSH_ERR_ALLOC_FAIL; /* * Since we are blocking, ensure that all written packets have * been sent. */ if ((r = ssh_packet_write_wait(ssh)) != 0) goto out; /* Stay in the loop until we have received a complete packet. */ for (;;) { /* Try to read a packet from the buffer. */ r = ssh_packet_read_poll_seqnr(ssh, typep, seqnr_p); if (r != 0) break; if (!compat20 && ( *typep == SSH_SMSG_SUCCESS || *typep == SSH_SMSG_FAILURE || *typep == SSH_CMSG_EOF || *typep == SSH_CMSG_EXIT_CONFIRMATION)) if ((r = sshpkt_get_end(ssh)) != 0) break; /* If we got a packet, return it. */ if (*typep != SSH_MSG_NONE) break; /* * Otherwise, wait for some data to arrive, add it to the * buffer, and try again. */ memset(setp, 0, howmany(state->connection_in + 1, NFDBITS) * sizeof(fd_mask)); FD_SET(state->connection_in, setp); if (state->packet_timeout_ms > 0) { ms_remain = state->packet_timeout_ms; timeoutp = &timeout; } /* Wait for some data to arrive. */ for (;;) { if (state->packet_timeout_ms != -1) { ms_to_timeval(&timeout, ms_remain); gettimeofday(&start, NULL); } if ((r = select(state->connection_in + 1, setp, NULL, NULL, timeoutp)) >= 0) break; if (errno != EAGAIN && errno != EINTR && errno != EWOULDBLOCK) break; if (state->packet_timeout_ms == -1) continue; ms_subtract_diff(&start, &ms_remain); if (ms_remain <= 0) { r = 0; break; } } - if (r == 0) - return SSH_ERR_CONN_TIMEOUT; + if (r == 0) { + r = SSH_ERR_CONN_TIMEOUT; + goto out; + } /* Read data from the socket. */ len = read(state->connection_in, buf, sizeof(buf)); if (len == 0) { r = SSH_ERR_CONN_CLOSED; goto out; } if (len < 0) { r = SSH_ERR_SYSTEM_ERROR; goto out; } /* Append it to the buffer. */ if ((r = ssh_packet_process_incoming(ssh, buf, len)) != 0) goto out; } out: free(setp); return r; } int ssh_packet_read(struct ssh *ssh) { u_char type; int r; if ((r = ssh_packet_read_seqnr(ssh, &type, NULL)) != 0) fatal("%s: %s", __func__, ssh_err(r)); return type; } /* * Waits until a packet has been received, verifies that its type matches * that given, and gives a fatal error and exits if there is a mismatch. */ int ssh_packet_read_expect(struct ssh *ssh, u_int expected_type) { int r; u_char type; if ((r = ssh_packet_read_seqnr(ssh, &type, NULL)) != 0) return r; if (type != expected_type) { if ((r = sshpkt_disconnect(ssh, "Protocol error: expected packet type %d, got %d", expected_type, type)) != 0) return r; return SSH_ERR_PROTOCOL_ERROR; } return 0; } /* Checks if a full packet is available in the data received so far via * packet_process_incoming. If so, reads the packet; otherwise returns * SSH_MSG_NONE. This does not wait for data from the connection. * * SSH_MSG_DISCONNECT is handled specially here. Also, * SSH_MSG_IGNORE messages are skipped by this function and are never returned * to higher levels. */ int ssh_packet_read_poll1(struct ssh *ssh, u_char *typep) { struct session_state *state = ssh->state; u_int len, padded_len; const char *emsg; const u_char *cp; u_char *p; u_int checksum, stored_checksum; int r; *typep = SSH_MSG_NONE; /* Check if input size is less than minimum packet size. */ if (sshbuf_len(state->input) < 4 + 8) return 0; /* Get length of incoming packet. */ len = PEEK_U32(sshbuf_ptr(state->input)); if (len < 1 + 2 + 2 || len > 256 * 1024) { if ((r = sshpkt_disconnect(ssh, "Bad packet length %u", len)) != 0) return r; return SSH_ERR_CONN_CORRUPT; } padded_len = (len + 8) & ~7; /* Check if the packet has been entirely received. */ if (sshbuf_len(state->input) < 4 + padded_len) return 0; /* The entire packet is in buffer. */ /* Consume packet length. */ if ((r = sshbuf_consume(state->input, 4)) != 0) goto out; /* * Cryptographic attack detector for ssh * (C)1998 CORE-SDI, Buenos Aires Argentina * Ariel Futoransky(futo@core-sdi.com) */ if (!cipher_ctx_is_plaintext(state->receive_context)) { emsg = NULL; switch (detect_attack(&state->deattack, sshbuf_ptr(state->input), padded_len)) { case DEATTACK_OK: break; case DEATTACK_DETECTED: emsg = "crc32 compensation attack detected"; break; case DEATTACK_DOS_DETECTED: emsg = "deattack denial of service detected"; break; default: emsg = "deattack error"; break; } if (emsg != NULL) { error("%s", emsg); if ((r = sshpkt_disconnect(ssh, "%s", emsg)) != 0 || (r = ssh_packet_write_wait(ssh)) != 0) return r; return SSH_ERR_CONN_CORRUPT; } } /* Decrypt data to incoming_packet. */ sshbuf_reset(state->incoming_packet); if ((r = sshbuf_reserve(state->incoming_packet, padded_len, &p)) != 0) goto out; if ((r = cipher_crypt(state->receive_context, 0, p, sshbuf_ptr(state->input), padded_len, 0, 0)) != 0) goto out; if ((r = sshbuf_consume(state->input, padded_len)) != 0) goto out; #ifdef PACKET_DEBUG fprintf(stderr, "read_poll plain: "); sshbuf_dump(state->incoming_packet, stderr); #endif /* Compute packet checksum. */ checksum = ssh_crc32(sshbuf_ptr(state->incoming_packet), sshbuf_len(state->incoming_packet) - 4); /* Skip padding. */ if ((r = sshbuf_consume(state->incoming_packet, 8 - len % 8)) != 0) goto out; /* Test check bytes. */ if (len != sshbuf_len(state->incoming_packet)) { error("%s: len %d != sshbuf_len %zd", __func__, len, sshbuf_len(state->incoming_packet)); if ((r = sshpkt_disconnect(ssh, "invalid packet length")) != 0 || (r = ssh_packet_write_wait(ssh)) != 0) return r; return SSH_ERR_CONN_CORRUPT; } cp = sshbuf_ptr(state->incoming_packet) + len - 4; stored_checksum = PEEK_U32(cp); if (checksum != stored_checksum) { error("Corrupted check bytes on input"); if ((r = sshpkt_disconnect(ssh, "connection corrupted")) != 0 || (r = ssh_packet_write_wait(ssh)) != 0) return r; return SSH_ERR_CONN_CORRUPT; } if ((r = sshbuf_consume_end(state->incoming_packet, 4)) < 0) goto out; if (state->packet_compression) { sshbuf_reset(state->compression_buffer); if ((r = uncompress_buffer(ssh, state->incoming_packet, state->compression_buffer)) != 0) goto out; sshbuf_reset(state->incoming_packet); if ((r = sshbuf_putb(state->incoming_packet, state->compression_buffer)) != 0) goto out; } state->p_read.packets++; state->p_read.bytes += padded_len + 4; if ((r = sshbuf_get_u8(state->incoming_packet, typep)) != 0) goto out; if (*typep < SSH_MSG_MIN || *typep > SSH_MSG_MAX) { error("Invalid ssh1 packet type: %d", *typep); if ((r = sshpkt_disconnect(ssh, "invalid packet type")) != 0 || (r = ssh_packet_write_wait(ssh)) != 0) return r; return SSH_ERR_PROTOCOL_ERROR; } r = 0; out: return r; } static int ssh_packet_read_poll2_mux(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) { struct session_state *state = ssh->state; const u_char *cp; size_t need; int r; if (ssh->kex) return SSH_ERR_INTERNAL_ERROR; *typep = SSH_MSG_NONE; cp = sshbuf_ptr(state->input); if (state->packlen == 0) { if (sshbuf_len(state->input) < 4 + 1) return 0; /* packet is incomplete */ state->packlen = PEEK_U32(cp); if (state->packlen < 4 + 1 || state->packlen > PACKET_MAX_SIZE) return SSH_ERR_MESSAGE_INCOMPLETE; } need = state->packlen + 4; if (sshbuf_len(state->input) < need) return 0; /* packet is incomplete */ sshbuf_reset(state->incoming_packet); if ((r = sshbuf_put(state->incoming_packet, cp + 4, state->packlen)) != 0 || (r = sshbuf_consume(state->input, need)) != 0 || (r = sshbuf_get_u8(state->incoming_packet, NULL)) != 0 || (r = sshbuf_get_u8(state->incoming_packet, typep)) != 0) return r; if (ssh_packet_log_type(*typep)) debug3("%s: type %u", __func__, *typep); /* sshbuf_dump(state->incoming_packet, stderr); */ /* reset for next packet */ state->packlen = 0; return r; } int ssh_packet_read_poll2(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) { struct session_state *state = ssh->state; u_int padlen, need; u_char *cp; u_int maclen, aadlen = 0, authlen = 0, block_size; struct sshenc *enc = NULL; struct sshmac *mac = NULL; struct sshcomp *comp = NULL; int r; if (state->mux) return ssh_packet_read_poll2_mux(ssh, typep, seqnr_p); *typep = SSH_MSG_NONE; if (state->packet_discard) return 0; if (state->newkeys[MODE_IN] != NULL) { enc = &state->newkeys[MODE_IN]->enc; mac = &state->newkeys[MODE_IN]->mac; comp = &state->newkeys[MODE_IN]->comp; /* disable mac for authenticated encryption */ if ((authlen = cipher_authlen(enc->cipher)) != 0) mac = NULL; } maclen = mac && mac->enabled ? mac->mac_len : 0; block_size = enc ? enc->block_size : 8; aadlen = (mac && mac->enabled && mac->etm) || authlen ? 4 : 0; if (aadlen && state->packlen == 0) { if (cipher_get_length(state->receive_context, &state->packlen, state->p_read.seqnr, sshbuf_ptr(state->input), sshbuf_len(state->input)) != 0) return 0; if (state->packlen < 1 + 4 || state->packlen > PACKET_MAX_SIZE) { #ifdef PACKET_DEBUG sshbuf_dump(state->input, stderr); #endif logit("Bad packet length %u.", state->packlen); if ((r = sshpkt_disconnect(ssh, "Packet corrupt")) != 0) return r; return SSH_ERR_CONN_CORRUPT; } sshbuf_reset(state->incoming_packet); } else if (state->packlen == 0) { /* * check if input size is less than the cipher block size, * decrypt first block and extract length of incoming packet */ if (sshbuf_len(state->input) < block_size) return 0; sshbuf_reset(state->incoming_packet); if ((r = sshbuf_reserve(state->incoming_packet, block_size, &cp)) != 0) goto out; if ((r = cipher_crypt(state->receive_context, state->p_send.seqnr, cp, sshbuf_ptr(state->input), block_size, 0, 0)) != 0) goto out; state->packlen = PEEK_U32(sshbuf_ptr(state->incoming_packet)); if (state->packlen < 1 + 4 || state->packlen > PACKET_MAX_SIZE) { #ifdef PACKET_DEBUG fprintf(stderr, "input: \n"); sshbuf_dump(state->input, stderr); fprintf(stderr, "incoming_packet: \n"); sshbuf_dump(state->incoming_packet, stderr); #endif logit("Bad packet length %u.", state->packlen); return ssh_packet_start_discard(ssh, enc, mac, 0, PACKET_MAX_SIZE); } if ((r = sshbuf_consume(state->input, block_size)) != 0) goto out; } DBG(debug("input: packet len %u", state->packlen+4)); if (aadlen) { /* only the payload is encrypted */ need = state->packlen; } else { /* * the payload size and the payload are encrypted, but we * have a partial packet of block_size bytes */ need = 4 + state->packlen - block_size; } DBG(debug("partial packet: block %d, need %d, maclen %d, authlen %d," " aadlen %d", block_size, need, maclen, authlen, aadlen)); if (need % block_size != 0) { logit("padding error: need %d block %d mod %d", need, block_size, need % block_size); return ssh_packet_start_discard(ssh, enc, mac, 0, PACKET_MAX_SIZE - block_size); } /* * check if the entire packet has been received and * decrypt into incoming_packet: * 'aadlen' bytes are unencrypted, but authenticated. * 'need' bytes are encrypted, followed by either * 'authlen' bytes of authentication tag or * 'maclen' bytes of message authentication code. */ if (sshbuf_len(state->input) < aadlen + need + authlen + maclen) return 0; /* packet is incomplete */ #ifdef PACKET_DEBUG fprintf(stderr, "read_poll enc/full: "); sshbuf_dump(state->input, stderr); #endif /* EtM: check mac over encrypted input */ if (mac && mac->enabled && mac->etm) { if ((r = mac_check(mac, state->p_read.seqnr, sshbuf_ptr(state->input), aadlen + need, sshbuf_ptr(state->input) + aadlen + need + authlen, maclen)) != 0) { if (r == SSH_ERR_MAC_INVALID) logit("Corrupted MAC on input."); goto out; } } if ((r = sshbuf_reserve(state->incoming_packet, aadlen + need, &cp)) != 0) goto out; if ((r = cipher_crypt(state->receive_context, state->p_read.seqnr, cp, sshbuf_ptr(state->input), need, aadlen, authlen)) != 0) goto out; if ((r = sshbuf_consume(state->input, aadlen + need + authlen)) != 0) goto out; if (mac && mac->enabled) { /* Not EtM: check MAC over cleartext */ if (!mac->etm && (r = mac_check(mac, state->p_read.seqnr, sshbuf_ptr(state->incoming_packet), sshbuf_len(state->incoming_packet), sshbuf_ptr(state->input), maclen)) != 0) { if (r != SSH_ERR_MAC_INVALID) goto out; logit("Corrupted MAC on input."); - if (need > PACKET_MAX_SIZE) + if (need + block_size > PACKET_MAX_SIZE) return SSH_ERR_INTERNAL_ERROR; return ssh_packet_start_discard(ssh, enc, mac, sshbuf_len(state->incoming_packet), - PACKET_MAX_SIZE - need); + PACKET_MAX_SIZE - need - block_size); } /* Remove MAC from input buffer */ DBG(debug("MAC #%d ok", state->p_read.seqnr)); if ((r = sshbuf_consume(state->input, mac->mac_len)) != 0) goto out; } if (seqnr_p != NULL) *seqnr_p = state->p_read.seqnr; if (++state->p_read.seqnr == 0) logit("incoming seqnr wraps around"); if (++state->p_read.packets == 0) if (!(ssh->compat & SSH_BUG_NOREKEY)) return SSH_ERR_NEED_REKEY; state->p_read.blocks += (state->packlen + 4) / block_size; state->p_read.bytes += state->packlen + 4; /* get padlen */ padlen = sshbuf_ptr(state->incoming_packet)[4]; DBG(debug("input: padlen %d", padlen)); if (padlen < 4) { if ((r = sshpkt_disconnect(ssh, "Corrupted padlen %d on input.", padlen)) != 0 || (r = ssh_packet_write_wait(ssh)) != 0) return r; return SSH_ERR_CONN_CORRUPT; } /* skip packet size + padlen, discard padding */ if ((r = sshbuf_consume(state->incoming_packet, 4 + 1)) != 0 || ((r = sshbuf_consume_end(state->incoming_packet, padlen)) != 0)) goto out; DBG(debug("input: len before de-compress %zd", sshbuf_len(state->incoming_packet))); if (comp && comp->enabled) { sshbuf_reset(state->compression_buffer); if ((r = uncompress_buffer(ssh, state->incoming_packet, state->compression_buffer)) != 0) goto out; sshbuf_reset(state->incoming_packet); if ((r = sshbuf_putb(state->incoming_packet, state->compression_buffer)) != 0) goto out; DBG(debug("input: len after de-compress %zd", sshbuf_len(state->incoming_packet))); } /* * get packet type, implies consume. * return length of payload (without type field) */ if ((r = sshbuf_get_u8(state->incoming_packet, typep)) != 0) goto out; if (ssh_packet_log_type(*typep)) debug3("receive packet: type %u", *typep); if (*typep < SSH2_MSG_MIN || *typep >= SSH2_MSG_LOCAL_MIN) { if ((r = sshpkt_disconnect(ssh, "Invalid ssh2 packet type: %d", *typep)) != 0 || (r = ssh_packet_write_wait(ssh)) != 0) return r; return SSH_ERR_PROTOCOL_ERROR; } if (state->hook_in != NULL && (r = state->hook_in(ssh, state->incoming_packet, typep, state->hook_in_ctx)) != 0) return r; if (*typep == SSH2_MSG_USERAUTH_SUCCESS && !state->server_side) r = ssh_packet_enable_delayed_compress(ssh); else r = 0; #ifdef PACKET_DEBUG fprintf(stderr, "read/plain[%d]:\r\n", *typep); sshbuf_dump(state->incoming_packet, stderr); #endif /* reset for next packet */ state->packlen = 0; /* do we need to rekey? */ if (ssh_packet_need_rekeying(ssh, 0)) { debug3("%s: rekex triggered", __func__); if ((r = kex_start_rekex(ssh)) != 0) return r; } out: return r; } int ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p) { struct session_state *state = ssh->state; u_int reason, seqnr; int r; u_char *msg; for (;;) { msg = NULL; if (compat20) { r = ssh_packet_read_poll2(ssh, typep, seqnr_p); if (r != 0) return r; if (*typep) { state->keep_alive_timeouts = 0; DBG(debug("received packet type %d", *typep)); } switch (*typep) { case SSH2_MSG_IGNORE: debug3("Received SSH2_MSG_IGNORE"); break; case SSH2_MSG_DEBUG: if ((r = sshpkt_get_u8(ssh, NULL)) != 0 || (r = sshpkt_get_string(ssh, &msg, NULL)) != 0 || (r = sshpkt_get_string(ssh, NULL, NULL)) != 0) { free(msg); return r; } debug("Remote: %.900s", msg); free(msg); break; case SSH2_MSG_DISCONNECT: if ((r = sshpkt_get_u32(ssh, &reason)) != 0 || (r = sshpkt_get_string(ssh, &msg, NULL)) != 0) return r; /* Ignore normal client exit notifications */ do_log2(ssh->state->server_side && reason == SSH2_DISCONNECT_BY_APPLICATION ? SYSLOG_LEVEL_INFO : SYSLOG_LEVEL_ERROR, "Received disconnect from %s port %d:" "%u: %.400s", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), reason, msg); free(msg); return SSH_ERR_DISCONNECTED; case SSH2_MSG_UNIMPLEMENTED: if ((r = sshpkt_get_u32(ssh, &seqnr)) != 0) return r; debug("Received SSH2_MSG_UNIMPLEMENTED for %u", seqnr); break; default: return 0; } } else { r = ssh_packet_read_poll1(ssh, typep); switch (*typep) { case SSH_MSG_NONE: return SSH_MSG_NONE; case SSH_MSG_IGNORE: break; case SSH_MSG_DEBUG: if ((r = sshpkt_get_string(ssh, &msg, NULL)) != 0) return r; debug("Remote: %.900s", msg); free(msg); break; case SSH_MSG_DISCONNECT: if ((r = sshpkt_get_string(ssh, &msg, NULL)) != 0) return r; logit("Received disconnect from %s port %d: " "%.400s", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), msg); free(msg); return SSH_ERR_DISCONNECTED; default: DBG(debug("received packet type %d", *typep)); return 0; } } } } /* * Buffers the given amount of input characters. This is intended to be used * together with packet_read_poll. */ int ssh_packet_process_incoming(struct ssh *ssh, const char *buf, u_int len) { struct session_state *state = ssh->state; int r; if (state->packet_discard) { state->keep_alive_timeouts = 0; /* ?? */ if (len >= state->packet_discard) { if ((r = ssh_packet_stop_discard(ssh)) != 0) return r; } state->packet_discard -= len; return 0; } if ((r = sshbuf_put(ssh->state->input, buf, len)) != 0) return r; return 0; } int ssh_packet_remaining(struct ssh *ssh) { return sshbuf_len(ssh->state->incoming_packet); } /* * Sends a diagnostic message from the server to the client. This message * can be sent at any time (but not while constructing another message). The * message is printed immediately, but only if the client is being executed * in verbose mode. These messages are primarily intended to ease debugging * authentication problems. The length of the formatted message must not * exceed 1024 bytes. This will automatically call ssh_packet_write_wait. */ void ssh_packet_send_debug(struct ssh *ssh, const char *fmt,...) { char buf[1024]; va_list args; int r; if (compat20 && (ssh->compat & SSH_BUG_DEBUG)) return; va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); if (compat20) { if ((r = sshpkt_start(ssh, SSH2_MSG_DEBUG)) != 0 || (r = sshpkt_put_u8(ssh, 0)) != 0 || /* always display */ (r = sshpkt_put_cstring(ssh, buf)) != 0 || (r = sshpkt_put_cstring(ssh, "")) != 0 || (r = sshpkt_send(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); } else { if ((r = sshpkt_start(ssh, SSH_MSG_DEBUG)) != 0 || (r = sshpkt_put_cstring(ssh, buf)) != 0 || (r = sshpkt_send(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); } if ((r = ssh_packet_write_wait(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); } +static void +fmt_connection_id(struct ssh *ssh, char *s, size_t l) +{ + snprintf(s, l, "%.200s%s%s port %d", + ssh->log_preamble ? ssh->log_preamble : "", + ssh->log_preamble ? " " : "", + ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); +} + /* * Pretty-print connection-terminating errors and exit. */ void sshpkt_fatal(struct ssh *ssh, const char *tag, int r) { + char remote_id[512]; + + fmt_connection_id(ssh, remote_id, sizeof(remote_id)); + switch (r) { case SSH_ERR_CONN_CLOSED: - logdie("Connection closed by %.200s port %d", - ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); + logdie("Connection closed by %s", remote_id); case SSH_ERR_CONN_TIMEOUT: - logdie("Connection %s %.200s port %d timed out", - ssh->state->server_side ? "from" : "to", - ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); + logdie("Connection %s %s timed out", + ssh->state->server_side ? "from" : "to", remote_id); case SSH_ERR_DISCONNECTED: - logdie("Disconnected from %.200s port %d", - ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); + logdie("Disconnected from %s", remote_id); case SSH_ERR_SYSTEM_ERROR: if (errno == ECONNRESET) - logdie("Connection reset by %.200s port %d", - ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); + logdie("Connection reset by %s", remote_id); /* FALLTHROUGH */ case SSH_ERR_NO_CIPHER_ALG_MATCH: case SSH_ERR_NO_MAC_ALG_MATCH: case SSH_ERR_NO_COMPRESS_ALG_MATCH: case SSH_ERR_NO_KEX_ALG_MATCH: case SSH_ERR_NO_HOSTKEY_ALG_MATCH: if (ssh && ssh->kex && ssh->kex->failed_choice) { BLACKLIST_NOTIFY(BLACKLIST_AUTH_FAIL, "ssh"); - logdie("Unable to negotiate with %.200s port %d: %s. " - "Their offer: %s", ssh_remote_ipaddr(ssh), - ssh_remote_port(ssh), ssh_err(r), + logdie("Unable to negotiate with %s: %s. " + "Their offer: %s", remote_id, ssh_err(r), ssh->kex->failed_choice); } /* FALLTHROUGH */ default: - logdie("%s%sConnection %s %.200s port %d: %s", + logdie("%s%sConnection %s %s: %s", tag != NULL ? tag : "", tag != NULL ? ": " : "", ssh->state->server_side ? "from" : "to", - ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), ssh_err(r)); + remote_id, ssh_err(r)); } } /* * Logs the error plus constructs and sends a disconnect packet, closes the * connection, and exits. This function never returns. The error message * should not contain a newline. The length of the formatted message must * not exceed 1024 bytes. */ void ssh_packet_disconnect(struct ssh *ssh, const char *fmt,...) { - char buf[1024]; + char buf[1024], remote_id[512]; va_list args; static int disconnecting = 0; int r; if (disconnecting) /* Guard against recursive invocations. */ fatal("packet_disconnect called recursively."); disconnecting = 1; /* * Format the message. Note that the caller must make sure the * message is of limited size. */ + fmt_connection_id(ssh, remote_id, sizeof(remote_id)); va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); /* Display the error locally */ - logit("Disconnecting: %.100s", buf); + logit("Disconnecting %s: %.100s", remote_id, buf); /* * Send the disconnect message to the other side, and wait * for it to get sent. */ if ((r = sshpkt_disconnect(ssh, "%s", buf)) != 0) sshpkt_fatal(ssh, __func__, r); if ((r = ssh_packet_write_wait(ssh)) != 0) sshpkt_fatal(ssh, __func__, r); /* Close the connection. */ ssh_packet_close(ssh); cleanup_exit(255); } /* * Checks if there is any buffered output, and tries to write some of * the output. */ int ssh_packet_write_poll(struct ssh *ssh) { struct session_state *state = ssh->state; int len = sshbuf_len(state->output); int r; if (len > 0) { len = write(state->connection_out, sshbuf_ptr(state->output), len); if (len == -1) { if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) return 0; return SSH_ERR_SYSTEM_ERROR; } if (len == 0) return SSH_ERR_CONN_CLOSED; if ((r = sshbuf_consume(state->output, len)) != 0) return r; } return 0; } /* * Calls packet_write_poll repeatedly until all pending output data has been * written. */ int ssh_packet_write_wait(struct ssh *ssh) { fd_set *setp; int ret, r, ms_remain = 0; struct timeval start, timeout, *timeoutp = NULL; struct session_state *state = ssh->state; setp = calloc(howmany(state->connection_out + 1, NFDBITS), sizeof(fd_mask)); if (setp == NULL) return SSH_ERR_ALLOC_FAIL; if ((r = ssh_packet_write_poll(ssh)) != 0) { free(setp); return r; } while (ssh_packet_have_data_to_write(ssh)) { memset(setp, 0, howmany(state->connection_out + 1, NFDBITS) * sizeof(fd_mask)); FD_SET(state->connection_out, setp); if (state->packet_timeout_ms > 0) { ms_remain = state->packet_timeout_ms; timeoutp = &timeout; } for (;;) { if (state->packet_timeout_ms != -1) { ms_to_timeval(&timeout, ms_remain); gettimeofday(&start, NULL); } if ((ret = select(state->connection_out + 1, NULL, setp, NULL, timeoutp)) >= 0) break; if (errno != EAGAIN && errno != EINTR && errno != EWOULDBLOCK) break; if (state->packet_timeout_ms == -1) continue; ms_subtract_diff(&start, &ms_remain); if (ms_remain <= 0) { ret = 0; break; } } if (ret == 0) { free(setp); return SSH_ERR_CONN_TIMEOUT; } if ((r = ssh_packet_write_poll(ssh)) != 0) { free(setp); return r; } } free(setp); return 0; } /* Returns true if there is buffered data to write to the connection. */ int ssh_packet_have_data_to_write(struct ssh *ssh) { return sshbuf_len(ssh->state->output) != 0; } /* Returns true if there is not too much data to write to the connection. */ int ssh_packet_not_very_much_data_to_write(struct ssh *ssh) { if (ssh->state->interactive_mode) return sshbuf_len(ssh->state->output) < 16384; else return sshbuf_len(ssh->state->output) < 128 * 1024; } void ssh_packet_set_tos(struct ssh *ssh, int tos) { #ifndef IP_TOS_IS_BROKEN if (!ssh_packet_connection_is_on_socket(ssh)) return; switch (ssh_packet_connection_af(ssh)) { # ifdef IP_TOS case AF_INET: debug3("%s: set IP_TOS 0x%02x", __func__, tos); if (setsockopt(ssh->state->connection_in, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)) < 0) error("setsockopt IP_TOS %d: %.100s:", tos, strerror(errno)); break; # endif /* IP_TOS */ # ifdef IPV6_TCLASS case AF_INET6: debug3("%s: set IPV6_TCLASS 0x%02x", __func__, tos); if (setsockopt(ssh->state->connection_in, IPPROTO_IPV6, IPV6_TCLASS, &tos, sizeof(tos)) < 0) error("setsockopt IPV6_TCLASS %d: %.100s:", tos, strerror(errno)); break; # endif /* IPV6_TCLASS */ } #endif /* IP_TOS_IS_BROKEN */ } /* Informs that the current session is interactive. Sets IP flags for that. */ void ssh_packet_set_interactive(struct ssh *ssh, int interactive, int qos_interactive, int qos_bulk) { struct session_state *state = ssh->state; if (state->set_interactive_called) return; state->set_interactive_called = 1; /* Record that we are in interactive mode. */ state->interactive_mode = interactive; /* Only set socket options if using a socket. */ if (!ssh_packet_connection_is_on_socket(ssh)) return; set_nodelay(state->connection_in); ssh_packet_set_tos(ssh, interactive ? qos_interactive : qos_bulk); } /* Returns true if the current connection is interactive. */ int ssh_packet_is_interactive(struct ssh *ssh) { return ssh->state->interactive_mode; } int ssh_packet_set_maxsize(struct ssh *ssh, u_int s) { struct session_state *state = ssh->state; if (state->set_maxsize_called) { logit("packet_set_maxsize: called twice: old %d new %d", state->max_packet_size, s); return -1; } if (s < 4 * 1024 || s > 1024 * 1024) { logit("packet_set_maxsize: bad size %d", s); return -1; } state->set_maxsize_called = 1; debug("packet_set_maxsize: setting to %d", s); state->max_packet_size = s; return s; } int ssh_packet_inc_alive_timeouts(struct ssh *ssh) { return ++ssh->state->keep_alive_timeouts; } void ssh_packet_set_alive_timeouts(struct ssh *ssh, int ka) { ssh->state->keep_alive_timeouts = ka; } u_int ssh_packet_get_maxsize(struct ssh *ssh) { return ssh->state->max_packet_size; } /* * 9.2. Ignored Data Message * * byte SSH_MSG_IGNORE * string data * * All implementations MUST understand (and ignore) this message at any * time (after receiving the protocol version). No implementation is * required to send them. This message can be used as an additional * protection measure against advanced traffic analysis techniques. */ void ssh_packet_send_ignore(struct ssh *ssh, int nbytes) { u_int32_t rnd = 0; int r, i; if ((r = sshpkt_start(ssh, compat20 ? SSH2_MSG_IGNORE : SSH_MSG_IGNORE)) != 0 || (r = sshpkt_put_u32(ssh, nbytes)) != 0) fatal("%s: %s", __func__, ssh_err(r)); for (i = 0; i < nbytes; i++) { if (i % 4 == 0) rnd = arc4random(); if ((r = sshpkt_put_u8(ssh, (u_char)rnd & 0xff)) != 0) fatal("%s: %s", __func__, ssh_err(r)); rnd >>= 8; } } void -ssh_packet_set_rekey_limits(struct ssh *ssh, u_int64_t bytes, time_t seconds) +ssh_packet_set_rekey_limits(struct ssh *ssh, u_int64_t bytes, u_int32_t seconds) { - debug3("rekey after %llu bytes, %d seconds", (unsigned long long)bytes, - (int)seconds); + debug3("rekey after %llu bytes, %u seconds", (unsigned long long)bytes, + (unsigned int)seconds); ssh->state->rekey_limit = bytes; ssh->state->rekey_interval = seconds; } time_t ssh_packet_get_rekey_timeout(struct ssh *ssh) { time_t seconds; seconds = ssh->state->rekey_time + ssh->state->rekey_interval - monotime(); return (seconds <= 0 ? 1 : seconds); } void ssh_packet_set_server(struct ssh *ssh) { ssh->state->server_side = 1; } void ssh_packet_set_authenticated(struct ssh *ssh) { ssh->state->after_authentication = 1; } void * ssh_packet_get_input(struct ssh *ssh) { return (void *)ssh->state->input; } void * ssh_packet_get_output(struct ssh *ssh) { return (void *)ssh->state->output; } /* Reset after_authentication and reset compression in post-auth privsep */ static int ssh_packet_set_postauth(struct ssh *ssh) { int r; debug("%s: called", __func__); /* This was set in net child, but is not visible in user child */ ssh->state->after_authentication = 1; ssh->state->rekeying = 0; if ((r = ssh_packet_enable_delayed_compress(ssh)) != 0) return r; return 0; } /* Packet state (de-)serialization for privsep */ /* turn kex into a blob for packet state serialization */ static int kex_to_blob(struct sshbuf *m, struct kex *kex) { int r; if ((r = sshbuf_put_string(m, kex->session_id, kex->session_id_len)) != 0 || (r = sshbuf_put_u32(m, kex->we_need)) != 0 || (r = sshbuf_put_u32(m, kex->hostkey_type)) != 0 || (r = sshbuf_put_u32(m, kex->kex_type)) != 0 || (r = sshbuf_put_stringb(m, kex->my)) != 0 || (r = sshbuf_put_stringb(m, kex->peer)) != 0 || (r = sshbuf_put_u32(m, kex->flags)) != 0 || (r = sshbuf_put_cstring(m, kex->client_version_string)) != 0 || (r = sshbuf_put_cstring(m, kex->server_version_string)) != 0) return r; return 0; } /* turn key exchange results into a blob for packet state serialization */ static int newkeys_to_blob(struct sshbuf *m, struct ssh *ssh, int mode) { struct sshbuf *b; struct sshcipher_ctx *cc; struct sshcomp *comp; struct sshenc *enc; struct sshmac *mac; struct newkeys *newkey; int r; if ((newkey = ssh->state->newkeys[mode]) == NULL) return SSH_ERR_INTERNAL_ERROR; enc = &newkey->enc; mac = &newkey->mac; comp = &newkey->comp; cc = (mode == MODE_OUT) ? ssh->state->send_context : ssh->state->receive_context; if ((r = cipher_get_keyiv(cc, enc->iv, enc->iv_len)) != 0) return r; if ((b = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; /* The cipher struct is constant and shared, you export pointer */ if ((r = sshbuf_put_cstring(b, enc->name)) != 0 || (r = sshbuf_put(b, &enc->cipher, sizeof(enc->cipher))) != 0 || (r = sshbuf_put_u32(b, enc->enabled)) != 0 || (r = sshbuf_put_u32(b, enc->block_size)) != 0 || (r = sshbuf_put_string(b, enc->key, enc->key_len)) != 0 || (r = sshbuf_put_string(b, enc->iv, enc->iv_len)) != 0) goto out; if (cipher_authlen(enc->cipher) == 0) { if ((r = sshbuf_put_cstring(b, mac->name)) != 0 || (r = sshbuf_put_u32(b, mac->enabled)) != 0 || (r = sshbuf_put_string(b, mac->key, mac->key_len)) != 0) goto out; } if ((r = sshbuf_put_u32(b, comp->type)) != 0 || (r = sshbuf_put_cstring(b, comp->name)) != 0) goto out; r = sshbuf_put_stringb(m, b); out: sshbuf_free(b); return r; } /* serialize packet state into a blob */ int ssh_packet_get_state(struct ssh *ssh, struct sshbuf *m) { struct session_state *state = ssh->state; u_char *p; size_t slen, rlen; int r, ssh1cipher; if (!compat20) { ssh1cipher = cipher_ctx_get_number(state->receive_context); slen = cipher_get_keyiv_len(state->send_context); rlen = cipher_get_keyiv_len(state->receive_context); if ((r = sshbuf_put_u32(m, state->remote_protocol_flags)) != 0 || (r = sshbuf_put_u32(m, ssh1cipher)) != 0 || (r = sshbuf_put_string(m, state->ssh1_key, state->ssh1_keylen)) != 0 || (r = sshbuf_put_u32(m, slen)) != 0 || (r = sshbuf_reserve(m, slen, &p)) != 0 || (r = cipher_get_keyiv(state->send_context, p, slen)) != 0 || (r = sshbuf_put_u32(m, rlen)) != 0 || (r = sshbuf_reserve(m, rlen, &p)) != 0 || (r = cipher_get_keyiv(state->receive_context, p, rlen)) != 0) return r; } else { if ((r = kex_to_blob(m, ssh->kex)) != 0 || (r = newkeys_to_blob(m, ssh, MODE_OUT)) != 0 || (r = newkeys_to_blob(m, ssh, MODE_IN)) != 0 || (r = sshbuf_put_u64(m, state->rekey_limit)) != 0 || (r = sshbuf_put_u32(m, state->rekey_interval)) != 0 || (r = sshbuf_put_u32(m, state->p_send.seqnr)) != 0 || (r = sshbuf_put_u64(m, state->p_send.blocks)) != 0 || (r = sshbuf_put_u32(m, state->p_send.packets)) != 0 || (r = sshbuf_put_u64(m, state->p_send.bytes)) != 0 || (r = sshbuf_put_u32(m, state->p_read.seqnr)) != 0 || (r = sshbuf_put_u64(m, state->p_read.blocks)) != 0 || (r = sshbuf_put_u32(m, state->p_read.packets)) != 0 || (r = sshbuf_put_u64(m, state->p_read.bytes)) != 0) return r; } slen = cipher_get_keycontext(state->send_context, NULL); rlen = cipher_get_keycontext(state->receive_context, NULL); if ((r = sshbuf_put_u32(m, slen)) != 0 || (r = sshbuf_reserve(m, slen, &p)) != 0) return r; if (cipher_get_keycontext(state->send_context, p) != (int)slen) return SSH_ERR_INTERNAL_ERROR; if ((r = sshbuf_put_u32(m, rlen)) != 0 || (r = sshbuf_reserve(m, rlen, &p)) != 0) return r; if (cipher_get_keycontext(state->receive_context, p) != (int)rlen) return SSH_ERR_INTERNAL_ERROR; if ((r = sshbuf_put_stringb(m, state->input)) != 0 || (r = sshbuf_put_stringb(m, state->output)) != 0) return r; return 0; } /* restore key exchange results from blob for packet state de-serialization */ static int newkeys_from_blob(struct sshbuf *m, struct ssh *ssh, int mode) { struct sshbuf *b = NULL; struct sshcomp *comp; struct sshenc *enc; struct sshmac *mac; struct newkeys *newkey = NULL; size_t keylen, ivlen, maclen; int r; if ((newkey = calloc(1, sizeof(*newkey))) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = sshbuf_froms(m, &b)) != 0) goto out; #ifdef DEBUG_PK sshbuf_dump(b, stderr); #endif enc = &newkey->enc; mac = &newkey->mac; comp = &newkey->comp; if ((r = sshbuf_get_cstring(b, &enc->name, NULL)) != 0 || (r = sshbuf_get(b, &enc->cipher, sizeof(enc->cipher))) != 0 || (r = sshbuf_get_u32(b, (u_int *)&enc->enabled)) != 0 || (r = sshbuf_get_u32(b, &enc->block_size)) != 0 || (r = sshbuf_get_string(b, &enc->key, &keylen)) != 0 || (r = sshbuf_get_string(b, &enc->iv, &ivlen)) != 0) goto out; if (cipher_authlen(enc->cipher) == 0) { if ((r = sshbuf_get_cstring(b, &mac->name, NULL)) != 0) goto out; if ((r = mac_setup(mac, mac->name)) != 0) goto out; if ((r = sshbuf_get_u32(b, (u_int *)&mac->enabled)) != 0 || (r = sshbuf_get_string(b, &mac->key, &maclen)) != 0) goto out; if (maclen > mac->key_len) { r = SSH_ERR_INVALID_FORMAT; goto out; } mac->key_len = maclen; } if ((r = sshbuf_get_u32(b, &comp->type)) != 0 || (r = sshbuf_get_cstring(b, &comp->name, NULL)) != 0) goto out; if (enc->name == NULL || cipher_by_name(enc->name) != enc->cipher) { r = SSH_ERR_INVALID_FORMAT; goto out; } if (sshbuf_len(b) != 0) { r = SSH_ERR_INVALID_FORMAT; goto out; } enc->key_len = keylen; enc->iv_len = ivlen; ssh->kex->newkeys[mode] = newkey; newkey = NULL; r = 0; out: free(newkey); sshbuf_free(b); return r; } /* restore kex from blob for packet state de-serialization */ static int kex_from_blob(struct sshbuf *m, struct kex **kexp) { struct kex *kex; int r; if ((kex = calloc(1, sizeof(struct kex))) == NULL || (kex->my = sshbuf_new()) == NULL || (kex->peer = sshbuf_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = sshbuf_get_string(m, &kex->session_id, &kex->session_id_len)) != 0 || (r = sshbuf_get_u32(m, &kex->we_need)) != 0 || (r = sshbuf_get_u32(m, (u_int *)&kex->hostkey_type)) != 0 || (r = sshbuf_get_u32(m, &kex->kex_type)) != 0 || (r = sshbuf_get_stringb(m, kex->my)) != 0 || (r = sshbuf_get_stringb(m, kex->peer)) != 0 || (r = sshbuf_get_u32(m, &kex->flags)) != 0 || (r = sshbuf_get_cstring(m, &kex->client_version_string, NULL)) != 0 || (r = sshbuf_get_cstring(m, &kex->server_version_string, NULL)) != 0) goto out; kex->server = 1; kex->done = 1; r = 0; out: if (r != 0 || kexp == NULL) { if (kex != NULL) { sshbuf_free(kex->my); sshbuf_free(kex->peer); free(kex); } if (kexp != NULL) *kexp = NULL; } else { *kexp = kex; } return r; } /* * Restore packet state from content of blob 'm' (de-serialization). * Note that 'm' will be partially consumed on parsing or any other errors. */ int ssh_packet_set_state(struct ssh *ssh, struct sshbuf *m) { struct session_state *state = ssh->state; const u_char *ssh1key, *ivin, *ivout, *keyin, *keyout, *input, *output; size_t ssh1keylen, rlen, slen, ilen, olen; int r; u_int ssh1cipher = 0; if (!compat20) { if ((r = sshbuf_get_u32(m, &state->remote_protocol_flags)) != 0 || (r = sshbuf_get_u32(m, &ssh1cipher)) != 0 || (r = sshbuf_get_string_direct(m, &ssh1key, &ssh1keylen)) != 0 || (r = sshbuf_get_string_direct(m, &ivout, &slen)) != 0 || (r = sshbuf_get_string_direct(m, &ivin, &rlen)) != 0) return r; if (ssh1cipher > INT_MAX) return SSH_ERR_KEY_UNKNOWN_CIPHER; ssh_packet_set_encryption_key(ssh, ssh1key, ssh1keylen, (int)ssh1cipher); if (cipher_get_keyiv_len(state->send_context) != (int)slen || cipher_get_keyiv_len(state->receive_context) != (int)rlen) return SSH_ERR_INVALID_FORMAT; if ((r = cipher_set_keyiv(state->send_context, ivout)) != 0 || (r = cipher_set_keyiv(state->receive_context, ivin)) != 0) return r; } else { if ((r = kex_from_blob(m, &ssh->kex)) != 0 || (r = newkeys_from_blob(m, ssh, MODE_OUT)) != 0 || (r = newkeys_from_blob(m, ssh, MODE_IN)) != 0 || (r = sshbuf_get_u64(m, &state->rekey_limit)) != 0 || (r = sshbuf_get_u32(m, &state->rekey_interval)) != 0 || (r = sshbuf_get_u32(m, &state->p_send.seqnr)) != 0 || (r = sshbuf_get_u64(m, &state->p_send.blocks)) != 0 || (r = sshbuf_get_u32(m, &state->p_send.packets)) != 0 || (r = sshbuf_get_u64(m, &state->p_send.bytes)) != 0 || (r = sshbuf_get_u32(m, &state->p_read.seqnr)) != 0 || (r = sshbuf_get_u64(m, &state->p_read.blocks)) != 0 || (r = sshbuf_get_u32(m, &state->p_read.packets)) != 0 || (r = sshbuf_get_u64(m, &state->p_read.bytes)) != 0) return r; /* * We set the time here so that in post-auth privsep slave we * count from the completion of the authentication. */ state->rekey_time = monotime(); /* XXX ssh_set_newkeys overrides p_read.packets? XXX */ if ((r = ssh_set_newkeys(ssh, MODE_IN)) != 0 || (r = ssh_set_newkeys(ssh, MODE_OUT)) != 0) return r; } if ((r = sshbuf_get_string_direct(m, &keyout, &slen)) != 0 || (r = sshbuf_get_string_direct(m, &keyin, &rlen)) != 0) return r; if (cipher_get_keycontext(state->send_context, NULL) != (int)slen || cipher_get_keycontext(state->receive_context, NULL) != (int)rlen) return SSH_ERR_INVALID_FORMAT; cipher_set_keycontext(state->send_context, keyout); cipher_set_keycontext(state->receive_context, keyin); if ((r = ssh_packet_set_postauth(ssh)) != 0) return r; sshbuf_reset(state->input); sshbuf_reset(state->output); if ((r = sshbuf_get_string_direct(m, &input, &ilen)) != 0 || (r = sshbuf_get_string_direct(m, &output, &olen)) != 0 || (r = sshbuf_put(state->input, input, ilen)) != 0 || (r = sshbuf_put(state->output, output, olen)) != 0) return r; if (sshbuf_len(m)) return SSH_ERR_INVALID_FORMAT; debug3("%s: done", __func__); return 0; } /* NEW API */ /* put data to the outgoing packet */ int sshpkt_put(struct ssh *ssh, const void *v, size_t len) { return sshbuf_put(ssh->state->outgoing_packet, v, len); } int sshpkt_putb(struct ssh *ssh, const struct sshbuf *b) { return sshbuf_putb(ssh->state->outgoing_packet, b); } int sshpkt_put_u8(struct ssh *ssh, u_char val) { return sshbuf_put_u8(ssh->state->outgoing_packet, val); } int sshpkt_put_u32(struct ssh *ssh, u_int32_t val) { return sshbuf_put_u32(ssh->state->outgoing_packet, val); } int sshpkt_put_u64(struct ssh *ssh, u_int64_t val) { return sshbuf_put_u64(ssh->state->outgoing_packet, val); } int sshpkt_put_string(struct ssh *ssh, const void *v, size_t len) { return sshbuf_put_string(ssh->state->outgoing_packet, v, len); } int sshpkt_put_cstring(struct ssh *ssh, const void *v) { return sshbuf_put_cstring(ssh->state->outgoing_packet, v); } int sshpkt_put_stringb(struct ssh *ssh, const struct sshbuf *v) { return sshbuf_put_stringb(ssh->state->outgoing_packet, v); } #ifdef WITH_OPENSSL #ifdef OPENSSL_HAS_ECC int sshpkt_put_ec(struct ssh *ssh, const EC_POINT *v, const EC_GROUP *g) { return sshbuf_put_ec(ssh->state->outgoing_packet, v, g); } #endif /* OPENSSL_HAS_ECC */ #ifdef WITH_SSH1 int sshpkt_put_bignum1(struct ssh *ssh, const BIGNUM *v) { return sshbuf_put_bignum1(ssh->state->outgoing_packet, v); } #endif /* WITH_SSH1 */ int sshpkt_put_bignum2(struct ssh *ssh, const BIGNUM *v) { return sshbuf_put_bignum2(ssh->state->outgoing_packet, v); } #endif /* WITH_OPENSSL */ /* fetch data from the incoming packet */ int sshpkt_get(struct ssh *ssh, void *valp, size_t len) { return sshbuf_get(ssh->state->incoming_packet, valp, len); } int sshpkt_get_u8(struct ssh *ssh, u_char *valp) { return sshbuf_get_u8(ssh->state->incoming_packet, valp); } int sshpkt_get_u32(struct ssh *ssh, u_int32_t *valp) { return sshbuf_get_u32(ssh->state->incoming_packet, valp); } int sshpkt_get_u64(struct ssh *ssh, u_int64_t *valp) { return sshbuf_get_u64(ssh->state->incoming_packet, valp); } int sshpkt_get_string(struct ssh *ssh, u_char **valp, size_t *lenp) { return sshbuf_get_string(ssh->state->incoming_packet, valp, lenp); } int sshpkt_get_string_direct(struct ssh *ssh, const u_char **valp, size_t *lenp) { return sshbuf_get_string_direct(ssh->state->incoming_packet, valp, lenp); } int sshpkt_get_cstring(struct ssh *ssh, char **valp, size_t *lenp) { return sshbuf_get_cstring(ssh->state->incoming_packet, valp, lenp); } #ifdef WITH_OPENSSL #ifdef OPENSSL_HAS_ECC int sshpkt_get_ec(struct ssh *ssh, EC_POINT *v, const EC_GROUP *g) { return sshbuf_get_ec(ssh->state->incoming_packet, v, g); } #endif /* OPENSSL_HAS_ECC */ #ifdef WITH_SSH1 int sshpkt_get_bignum1(struct ssh *ssh, BIGNUM *v) { return sshbuf_get_bignum1(ssh->state->incoming_packet, v); } #endif /* WITH_SSH1 */ int sshpkt_get_bignum2(struct ssh *ssh, BIGNUM *v) { return sshbuf_get_bignum2(ssh->state->incoming_packet, v); } #endif /* WITH_OPENSSL */ int sshpkt_get_end(struct ssh *ssh) { if (sshbuf_len(ssh->state->incoming_packet) > 0) return SSH_ERR_UNEXPECTED_TRAILING_DATA; return 0; } const u_char * sshpkt_ptr(struct ssh *ssh, size_t *lenp) { if (lenp != NULL) *lenp = sshbuf_len(ssh->state->incoming_packet); return sshbuf_ptr(ssh->state->incoming_packet); } /* start a new packet */ int sshpkt_start(struct ssh *ssh, u_char type) { u_char buf[9]; int len; DBG(debug("packet_start[%d]", type)); len = compat20 ? 6 : 9; memset(buf, 0, len - 1); buf[len - 1] = type; sshbuf_reset(ssh->state->outgoing_packet); return sshbuf_put(ssh->state->outgoing_packet, buf, len); } static int ssh_packet_send_mux(struct ssh *ssh) { struct session_state *state = ssh->state; u_char type, *cp; size_t len; int r; if (ssh->kex) return SSH_ERR_INTERNAL_ERROR; len = sshbuf_len(state->outgoing_packet); if (len < 6) return SSH_ERR_INTERNAL_ERROR; cp = sshbuf_mutable_ptr(state->outgoing_packet); type = cp[5]; if (ssh_packet_log_type(type)) debug3("%s: type %u", __func__, type); /* drop everything, but the connection protocol */ if (type >= SSH2_MSG_CONNECTION_MIN && type <= SSH2_MSG_CONNECTION_MAX) { POKE_U32(cp, len - 4); if ((r = sshbuf_putb(state->output, state->outgoing_packet)) != 0) return r; /* sshbuf_dump(state->output, stderr); */ } sshbuf_reset(state->outgoing_packet); return 0; } /* send it */ int sshpkt_send(struct ssh *ssh) { if (ssh->state && ssh->state->mux) return ssh_packet_send_mux(ssh); if (compat20) return ssh_packet_send2(ssh); else return ssh_packet_send1(ssh); } int sshpkt_disconnect(struct ssh *ssh, const char *fmt,...) { char buf[1024]; va_list args; int r; va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); if (compat20) { if ((r = sshpkt_start(ssh, SSH2_MSG_DISCONNECT)) != 0 || (r = sshpkt_put_u32(ssh, SSH2_DISCONNECT_PROTOCOL_ERROR)) != 0 || (r = sshpkt_put_cstring(ssh, buf)) != 0 || (r = sshpkt_put_cstring(ssh, "")) != 0 || (r = sshpkt_send(ssh)) != 0) return r; } else { if ((r = sshpkt_start(ssh, SSH_MSG_DISCONNECT)) != 0 || (r = sshpkt_put_cstring(ssh, buf)) != 0 || (r = sshpkt_send(ssh)) != 0) return r; } return 0; } /* roundup current message to pad bytes */ int sshpkt_add_padding(struct ssh *ssh, u_char pad) { ssh->state->extra_pad = pad; return 0; } diff --git a/crypto/openssh/packet.h b/crypto/openssh/packet.h index bfe7da61579a..0d25b352c73f 100644 --- a/crypto/openssh/packet.h +++ b/crypto/openssh/packet.h @@ -1,211 +1,216 @@ -/* $OpenBSD: packet.h,v 1.74 2016/10/11 21:47:45 djm Exp $ */ +/* $OpenBSD: packet.h,v 1.76 2017/02/03 23:03:33 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Interface for the packet protocol functions. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #ifndef PACKET_H #define PACKET_H #include #ifdef WITH_OPENSSL # include # ifdef OPENSSL_HAS_ECC # include # else /* OPENSSL_HAS_ECC */ # define EC_KEY void # define EC_GROUP void # define EC_POINT void # endif /* OPENSSL_HAS_ECC */ #else /* WITH_OPENSSL */ # define BIGNUM void # define EC_KEY void # define EC_GROUP void # define EC_POINT void #endif /* WITH_OPENSSL */ #include #include "openbsd-compat/sys-queue.h" struct kex; struct sshkey; struct sshbuf; struct session_state; /* private session data */ #include "dispatch.h" /* typedef, DISPATCH_MAX */ struct key_entry { TAILQ_ENTRY(key_entry) next; struct sshkey *key; }; struct ssh { /* Session state */ struct session_state *state; /* Key exchange */ struct kex *kex; /* cached local and remote ip addresses and ports */ char *remote_ipaddr; int remote_port; char *local_ipaddr; int local_port; + /* Optional preamble for log messages (e.g. username) */ + char *log_preamble; + /* Dispatcher table */ dispatch_fn *dispatch[DISPATCH_MAX]; /* number of packets to ignore in the dispatcher */ int dispatch_skip_packets; /* datafellows */ int compat; /* Lists for private and public keys */ TAILQ_HEAD(, key_entry) private_keys; TAILQ_HEAD(, key_entry) public_keys; /* APP data */ void *app_data; }; typedef int (ssh_packet_hook_fn)(struct ssh *, struct sshbuf *, u_char *, void *); struct ssh *ssh_alloc_session_state(void); struct ssh *ssh_packet_set_connection(struct ssh *, int, int); void ssh_packet_set_timeout(struct ssh *, int, int); int ssh_packet_stop_discard(struct ssh *); int ssh_packet_connection_af(struct ssh *); void ssh_packet_set_nonblocking(struct ssh *); int ssh_packet_get_connection_in(struct ssh *); int ssh_packet_get_connection_out(struct ssh *); void ssh_packet_close(struct ssh *); void ssh_packet_set_encryption_key(struct ssh *, const u_char *, u_int, int); void ssh_packet_set_input_hook(struct ssh *, ssh_packet_hook_fn *, void *); int ssh_packet_is_rekeying(struct ssh *); void ssh_packet_set_protocol_flags(struct ssh *, u_int); u_int ssh_packet_get_protocol_flags(struct ssh *); int ssh_packet_start_compression(struct ssh *, int); void ssh_packet_set_tos(struct ssh *, int); void ssh_packet_set_interactive(struct ssh *, int, int, int); int ssh_packet_is_interactive(struct ssh *); void ssh_packet_set_server(struct ssh *); void ssh_packet_set_authenticated(struct ssh *); void ssh_packet_set_mux(struct ssh *); int ssh_packet_get_mux(struct ssh *); +int ssh_packet_set_log_preamble(struct ssh *, const char *, ...) + __attribute__((format(printf, 2, 3))); int ssh_packet_log_type(u_char); int ssh_packet_send1(struct ssh *); int ssh_packet_send2_wrapped(struct ssh *); int ssh_packet_send2(struct ssh *); int ssh_packet_read(struct ssh *); int ssh_packet_read_expect(struct ssh *, u_int type); int ssh_packet_read_poll(struct ssh *); int ssh_packet_read_poll1(struct ssh *, u_char *); int ssh_packet_read_poll2(struct ssh *, u_char *, u_int32_t *seqnr_p); int ssh_packet_process_incoming(struct ssh *, const char *buf, u_int len); int ssh_packet_read_seqnr(struct ssh *, u_char *, u_int32_t *seqnr_p); int ssh_packet_read_poll_seqnr(struct ssh *, u_char *, u_int32_t *seqnr_p); const void *ssh_packet_get_string_ptr(struct ssh *, u_int *length_ptr); void ssh_packet_disconnect(struct ssh *, const char *fmt, ...) __attribute__((format(printf, 2, 3))) __attribute__((noreturn)); void ssh_packet_send_debug(struct ssh *, const char *fmt, ...) __attribute__((format(printf, 2, 3))); int ssh_set_newkeys(struct ssh *, int mode); void ssh_packet_get_bytes(struct ssh *, u_int64_t *, u_int64_t *); int ssh_packet_write_poll(struct ssh *); int ssh_packet_write_wait(struct ssh *); int ssh_packet_have_data_to_write(struct ssh *); int ssh_packet_not_very_much_data_to_write(struct ssh *); int ssh_packet_connection_is_on_socket(struct ssh *); int ssh_packet_remaining(struct ssh *); void ssh_packet_send_ignore(struct ssh *, int); void tty_make_modes(int, struct termios *); void tty_parse_modes(int, int *); void ssh_packet_set_alive_timeouts(struct ssh *, int); int ssh_packet_inc_alive_timeouts(struct ssh *); int ssh_packet_set_maxsize(struct ssh *, u_int); u_int ssh_packet_get_maxsize(struct ssh *); int ssh_packet_get_state(struct ssh *, struct sshbuf *); int ssh_packet_set_state(struct ssh *, struct sshbuf *); const char *ssh_remote_ipaddr(struct ssh *); int ssh_remote_port(struct ssh *); const char *ssh_local_ipaddr(struct ssh *); int ssh_local_port(struct ssh *); -void ssh_packet_set_rekey_limits(struct ssh *, u_int64_t, time_t); +void ssh_packet_set_rekey_limits(struct ssh *, u_int64_t, u_int32_t); time_t ssh_packet_get_rekey_timeout(struct ssh *); void *ssh_packet_get_input(struct ssh *); void *ssh_packet_get_output(struct ssh *); /* new API */ int sshpkt_start(struct ssh *ssh, u_char type); int sshpkt_send(struct ssh *ssh); int sshpkt_disconnect(struct ssh *, const char *fmt, ...) __attribute__((format(printf, 2, 3))); int sshpkt_add_padding(struct ssh *, u_char); void sshpkt_fatal(struct ssh *ssh, const char *tag, int r); int sshpkt_put(struct ssh *ssh, const void *v, size_t len); int sshpkt_putb(struct ssh *ssh, const struct sshbuf *b); int sshpkt_put_u8(struct ssh *ssh, u_char val); int sshpkt_put_u32(struct ssh *ssh, u_int32_t val); int sshpkt_put_u64(struct ssh *ssh, u_int64_t val); int sshpkt_put_string(struct ssh *ssh, const void *v, size_t len); int sshpkt_put_cstring(struct ssh *ssh, const void *v); int sshpkt_put_stringb(struct ssh *ssh, const struct sshbuf *v); int sshpkt_put_ec(struct ssh *ssh, const EC_POINT *v, const EC_GROUP *g); int sshpkt_put_bignum1(struct ssh *ssh, const BIGNUM *v); int sshpkt_put_bignum2(struct ssh *ssh, const BIGNUM *v); int sshpkt_get(struct ssh *ssh, void *valp, size_t len); int sshpkt_get_u8(struct ssh *ssh, u_char *valp); int sshpkt_get_u32(struct ssh *ssh, u_int32_t *valp); int sshpkt_get_u64(struct ssh *ssh, u_int64_t *valp); int sshpkt_get_string(struct ssh *ssh, u_char **valp, size_t *lenp); int sshpkt_get_string_direct(struct ssh *ssh, const u_char **valp, size_t *lenp); int sshpkt_get_cstring(struct ssh *ssh, char **valp, size_t *lenp); int sshpkt_get_ec(struct ssh *ssh, EC_POINT *v, const EC_GROUP *g); int sshpkt_get_bignum1(struct ssh *ssh, BIGNUM *v); int sshpkt_get_bignum2(struct ssh *ssh, BIGNUM *v); int sshpkt_get_end(struct ssh *ssh); const u_char *sshpkt_ptr(struct ssh *, size_t *lenp); /* OLD API */ extern struct ssh *active_state; #include "opacket.h" #if !defined(WITH_OPENSSL) # undef BIGNUM # undef EC_KEY # undef EC_GROUP # undef EC_POINT #elif !defined(OPENSSL_HAS_ECC) # undef EC_KEY # undef EC_GROUP # undef EC_POINT #endif #endif /* PACKET_H */ diff --git a/crypto/openssh/pathnames.h b/crypto/openssh/pathnames.h index 44b0ea3cc2a4..c2dc08da9330 100644 --- a/crypto/openssh/pathnames.h +++ b/crypto/openssh/pathnames.h @@ -1,182 +1,173 @@ /* $OpenBSD: pathnames.h,v 1.25 2016/03/31 05:24:06 dtucker Exp $ */ /* $FreeBSD$ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #define ETCDIR "/etc" #ifndef SSHDIR #define SSHDIR ETCDIR "/ssh" #endif #ifndef _PATH_SSH_PIDDIR #define _PATH_SSH_PIDDIR "/var/run" #endif /* * System-wide file containing host keys of known hosts. This file should be * world-readable. */ #define _PATH_SSH_SYSTEM_HOSTFILE SSHDIR "/ssh_known_hosts" /* backward compat for protocol 2 */ #define _PATH_SSH_SYSTEM_HOSTFILE2 SSHDIR "/ssh_known_hosts2" /* * Of these, ssh_host_key must be readable only by root, whereas ssh_config * should be world-readable. */ #define _PATH_SERVER_CONFIG_FILE SSHDIR "/sshd_config" #define _PATH_HOST_CONFIG_FILE SSHDIR "/ssh_config" #define _PATH_HOST_KEY_FILE SSHDIR "/ssh_host_key" #define _PATH_HOST_DSA_KEY_FILE SSHDIR "/ssh_host_dsa_key" #define _PATH_HOST_ECDSA_KEY_FILE SSHDIR "/ssh_host_ecdsa_key" #define _PATH_HOST_ED25519_KEY_FILE SSHDIR "/ssh_host_ed25519_key" #define _PATH_HOST_RSA_KEY_FILE SSHDIR "/ssh_host_rsa_key" #define _PATH_DH_MODULI SSHDIR "/moduli" #ifndef _PATH_SSH_PROGRAM #define _PATH_SSH_PROGRAM "/usr/bin/ssh" #endif /* * The process id of the daemon listening for connections is saved here to * make it easier to kill the correct daemon when necessary. */ #define _PATH_SSH_DAEMON_PID_FILE _PATH_SSH_PIDDIR "/sshd.pid" /* * The directory in user's home directory in which the files reside. The * directory should be world-readable (though not all files are). */ #define _PATH_SSH_USER_DIR ".ssh" /* * Per-user file containing host keys of known hosts. This file need not be * readable by anyone except the user him/herself, though this does not * contain anything particularly secret. */ #define _PATH_SSH_USER_HOSTFILE "~/" _PATH_SSH_USER_DIR "/known_hosts" /* backward compat for protocol 2 */ #define _PATH_SSH_USER_HOSTFILE2 "~/" _PATH_SSH_USER_DIR "/known_hosts2" /* * Name of the default file containing client-side authentication key. This * file should only be readable by the user him/herself. */ #define _PATH_SSH_CLIENT_IDENTITY _PATH_SSH_USER_DIR "/identity" #define _PATH_SSH_CLIENT_ID_DSA _PATH_SSH_USER_DIR "/id_dsa" #define _PATH_SSH_CLIENT_ID_ECDSA _PATH_SSH_USER_DIR "/id_ecdsa" #define _PATH_SSH_CLIENT_ID_RSA _PATH_SSH_USER_DIR "/id_rsa" #define _PATH_SSH_CLIENT_ID_ED25519 _PATH_SSH_USER_DIR "/id_ed25519" /* * Configuration file in user's home directory. This file need not be * readable by anyone but the user him/herself, but does not contain anything * particularly secret. If the user's home directory resides on an NFS * volume where root is mapped to nobody, this may need to be world-readable. */ #define _PATH_SSH_USER_CONFFILE _PATH_SSH_USER_DIR "/config" /* * File containing a list of those rsa keys that permit logging in as this * user. This file need not be readable by anyone but the user him/herself, * but does not contain anything particularly secret. If the user's home * directory resides on an NFS volume where root is mapped to nobody, this * may need to be world-readable. (This file is read by the daemon which is * running as root.) */ #define _PATH_SSH_USER_PERMITTED_KEYS _PATH_SSH_USER_DIR "/authorized_keys" /* backward compat for protocol v2 */ #define _PATH_SSH_USER_PERMITTED_KEYS2 _PATH_SSH_USER_DIR "/authorized_keys2" /* * Per-user and system-wide ssh "rc" files. These files are executed with * /bin/sh before starting the shell or command if they exist. They will be * passed "proto cookie" as arguments if X11 forwarding with spoofing is in * use. xauth will be run if neither of these exists. */ #define _PATH_SSH_USER_RC _PATH_SSH_USER_DIR "/rc" #define _PATH_SSH_SYSTEM_RC SSHDIR "/sshrc" /* * Ssh-only version of /etc/hosts.equiv. Additionally, the daemon may use * ~/.rhosts and /etc/hosts.equiv if rhosts authentication is enabled. */ #define _PATH_SSH_HOSTS_EQUIV SSHDIR "/shosts.equiv" #define _PATH_RHOSTS_EQUIV "/etc/hosts.equiv" /* * Default location of askpass */ #ifndef _PATH_SSH_ASKPASS_DEFAULT #define _PATH_SSH_ASKPASS_DEFAULT "/usr/local/bin/ssh-askpass" #endif /* Location of ssh-keysign for hostbased authentication */ #ifndef _PATH_SSH_KEY_SIGN #define _PATH_SSH_KEY_SIGN "/usr/libexec/ssh-keysign" #endif /* Location of ssh-pkcs11-helper to support keys in tokens */ #ifndef _PATH_SSH_PKCS11_HELPER #define _PATH_SSH_PKCS11_HELPER "/usr/libexec/ssh-pkcs11-helper" #endif /* xauth for X11 forwarding */ #ifndef _PATH_XAUTH #define _PATH_XAUTH "/usr/local/bin/xauth" #endif /* UNIX domain socket for X11 server; displaynum will replace %u */ #ifndef _PATH_UNIX_X #define _PATH_UNIX_X "/tmp/.X11-unix/X%u" #endif /* for scp */ #ifndef _PATH_CP #define _PATH_CP "cp" #endif /* for sftp */ #ifndef _PATH_SFTP_SERVER #define _PATH_SFTP_SERVER "/usr/libexec/sftp-server" #endif /* chroot directory for unprivileged user when UsePrivilegeSeparation=yes */ #ifndef _PATH_PRIVSEP_CHROOT_DIR #define _PATH_PRIVSEP_CHROOT_DIR "/var/empty" #endif /* for passwd change */ #ifndef _PATH_PASSWD_PROG #define _PATH_PASSWD_PROG "/usr/bin/passwd" #endif #ifndef _PATH_LS #define _PATH_LS "ls" #endif -/* path to login program */ -#ifndef LOGIN_PROGRAM -# ifdef LOGIN_PROGRAM_FALLBACK -# define LOGIN_PROGRAM LOGIN_PROGRAM_FALLBACK -# else -# define LOGIN_PROGRAM "/usr/bin/login" -# endif -#endif /* LOGIN_PROGRAM */ - /* Askpass program define */ #ifndef ASKPASS_PROGRAM #define ASKPASS_PROGRAM "/usr/lib/ssh/ssh-askpass" #endif /* ASKPASS_PROGRAM */ diff --git a/crypto/openssh/readconf.c b/crypto/openssh/readconf.c index 4b4ad1ed818a..b4e384d9ff3f 100644 --- a/crypto/openssh/readconf.c +++ b/crypto/openssh/readconf.c @@ -1,2697 +1,2725 @@ -/* $OpenBSD: readconf.c,v 1.262 2016/10/25 04:08:13 jsg Exp $ */ +/* $OpenBSD: readconf.c,v 1.270 2017/03/10 04:27:32 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Functions for reading the configuration files. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #include "includes.h" __RCSID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_PATHS_H # include #endif #include #include #include #include #include #include #ifdef USE_SYSTEM_GLOB # include #else # include "openbsd-compat/glob.h" #endif #ifdef HAVE_UTIL_H #include #endif #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) # include #endif #include "xmalloc.h" #include "ssh.h" #include "compat.h" #include "cipher.h" #include "pathnames.h" #include "log.h" #include "sshkey.h" #include "misc.h" #include "readconf.h" #include "match.h" #include "kex.h" #include "mac.h" #include "uidswap.h" #include "myproposal.h" #include "digest.h" #include "version.h" /* Format of the configuration file: # Configuration data is parsed as follows: # 1. command line options # 2. user-specific file # 3. system-wide file # Any configuration value is only changed the first time it is set. # Thus, host-specific definitions should be at the beginning of the # configuration file, and defaults at the end. # Host-specific declarations. These may override anything above. A single # host may match multiple declarations; these are processed in the order # that they are given in. Host *.ngs.fi ngs.fi User foo Host fake.com HostName another.host.name.real.org User blaah Port 34289 ForwardX11 no ForwardAgent no Host books.com RemoteForward 9999 shadows.cs.hut.fi:9999 - Cipher 3des + Ciphers 3des-cbc Host fascist.blob.com Port 23123 User tylonen PasswordAuthentication no Host puukko.hut.fi User t35124p ProxyCommand ssh-proxy %h %p Host *.fr PublicKeyAuthentication no Host *.su - Cipher none + Ciphers aes128-ctr PasswordAuthentication no Host vpn.fake.com Tunnel yes TunnelDevice 3 # Defaults for various options Host * ForwardAgent no ForwardX11 no PasswordAuthentication yes RSAAuthentication yes RhostsRSAAuthentication yes StrictHostKeyChecking yes TcpKeepAlive no IdentityFile ~/.ssh/identity Port 22 EscapeChar ~ */ static int read_config_file_depth(const char *filename, struct passwd *pw, const char *host, const char *original_host, Options *options, int flags, int *activep, int depth); static int process_config_line_depth(Options *options, struct passwd *pw, const char *host, const char *original_host, char *line, const char *filename, int linenum, int *activep, int flags, int depth); /* Keyword tokens. */ typedef enum { oBadOption, oVersionAddendum, oHost, oMatch, oInclude, oForwardAgent, oForwardX11, oForwardX11Trusted, oForwardX11Timeout, oGatewayPorts, oExitOnForwardFailure, oPasswordAuthentication, oRSAAuthentication, oChallengeResponseAuthentication, oXAuthLocation, oIdentityFile, oHostName, oPort, oCipher, oRemoteForward, oLocalForward, oCertificateFile, oAddKeysToAgent, oIdentityAgent, oUser, oEscapeChar, oRhostsRSAAuthentication, oProxyCommand, oGlobalKnownHostsFile, oUserKnownHostsFile, oConnectionAttempts, oBatchMode, oCheckHostIP, oStrictHostKeyChecking, oCompression, oCompressionLevel, oTCPKeepAlive, oNumberOfPasswordPrompts, oUsePrivilegedPort, oLogLevel, oCiphers, oProtocol, oMacs, oPubkeyAuthentication, oKbdInteractiveAuthentication, oKbdInteractiveDevices, oHostKeyAlias, oDynamicForward, oPreferredAuthentications, oHostbasedAuthentication, oHostKeyAlgorithms, oBindAddress, oPKCS11Provider, oClearAllForwardings, oNoHostAuthenticationForLocalhost, oEnableSSHKeysign, oRekeyLimit, oVerifyHostKeyDNS, oConnectTimeout, oAddressFamily, oGssAuthentication, oGssDelegateCreds, oServerAliveInterval, oServerAliveCountMax, oIdentitiesOnly, oSendEnv, oControlPath, oControlMaster, oControlPersist, oHashKnownHosts, oTunnel, oTunnelDevice, oLocalCommand, oPermitLocalCommand, oVisualHostKey, oKexAlgorithms, oIPQoS, oRequestTTY, oIgnoreUnknown, oProxyUseFdpass, oCanonicalDomains, oCanonicalizeHostname, oCanonicalizeMaxDots, oCanonicalizeFallbackLocal, oCanonicalizePermittedCNAMEs, oStreamLocalBindMask, oStreamLocalBindUnlink, oRevokedHostKeys, oFingerprintHash, oUpdateHostkeys, oHostbasedKeyTypes, oPubkeyAcceptedKeyTypes, oProxyJump, oIgnoredUnknownOption, oDeprecated, oUnsupported } OpCodes; /* Textual representations of the tokens. */ static struct { const char *name; OpCodes opcode; } keywords[] = { + /* Deprecated options */ + { "fallbacktorsh", oDeprecated }, + { "globalknownhostsfile2", oDeprecated }, + { "rhostsauthentication", oDeprecated }, + { "userknownhostsfile2", oDeprecated }, + { "useroaming", oDeprecated }, + { "usersh", oDeprecated }, + + /* Unsupported options */ + { "afstokenpassing", oUnsupported }, + { "kerberosauthentication", oUnsupported }, + { "kerberostgtpassing", oUnsupported }, + + /* Sometimes-unsupported options */ +#if defined(GSSAPI) + { "gssapiauthentication", oGssAuthentication }, + { "gssapidelegatecredentials", oGssDelegateCreds }, +# else + { "gssapiauthentication", oUnsupported }, + { "gssapidelegatecredentials", oUnsupported }, +#endif +#ifdef ENABLE_PKCS11 + { "smartcarddevice", oPKCS11Provider }, + { "pkcs11provider", oPKCS11Provider }, +# else + { "smartcarddevice", oUnsupported }, + { "pkcs11provider", oUnsupported }, +#endif +#ifdef WITH_SSH1 + { "rsaauthentication", oRSAAuthentication }, + { "rhostsrsaauthentication", oRhostsRSAAuthentication }, + { "compressionlevel", oCompressionLevel }, +# else + { "rsaauthentication", oUnsupported }, + { "rhostsrsaauthentication", oUnsupported }, + { "compressionlevel", oUnsupported }, +#endif + { "forwardagent", oForwardAgent }, { "forwardx11", oForwardX11 }, { "forwardx11trusted", oForwardX11Trusted }, { "forwardx11timeout", oForwardX11Timeout }, { "exitonforwardfailure", oExitOnForwardFailure }, { "xauthlocation", oXAuthLocation }, { "gatewayports", oGatewayPorts }, { "useprivilegedport", oUsePrivilegedPort }, - { "rhostsauthentication", oDeprecated }, { "passwordauthentication", oPasswordAuthentication }, { "kbdinteractiveauthentication", oKbdInteractiveAuthentication }, { "kbdinteractivedevices", oKbdInteractiveDevices }, - { "rsaauthentication", oRSAAuthentication }, { "pubkeyauthentication", oPubkeyAuthentication }, { "dsaauthentication", oPubkeyAuthentication }, /* alias */ - { "rhostsrsaauthentication", oRhostsRSAAuthentication }, { "hostbasedauthentication", oHostbasedAuthentication }, { "challengeresponseauthentication", oChallengeResponseAuthentication }, { "skeyauthentication", oChallengeResponseAuthentication }, /* alias */ { "tisauthentication", oChallengeResponseAuthentication }, /* alias */ - { "kerberosauthentication", oUnsupported }, - { "kerberostgtpassing", oUnsupported }, - { "afstokenpassing", oUnsupported }, -#if defined(GSSAPI) - { "gssapiauthentication", oGssAuthentication }, - { "gssapidelegatecredentials", oGssDelegateCreds }, -#else - { "gssapiauthentication", oUnsupported }, - { "gssapidelegatecredentials", oUnsupported }, -#endif - { "fallbacktorsh", oDeprecated }, - { "usersh", oDeprecated }, { "identityfile", oIdentityFile }, { "identityfile2", oIdentityFile }, /* obsolete */ { "identitiesonly", oIdentitiesOnly }, { "certificatefile", oCertificateFile }, { "addkeystoagent", oAddKeysToAgent }, { "identityagent", oIdentityAgent }, { "hostname", oHostName }, { "hostkeyalias", oHostKeyAlias }, { "proxycommand", oProxyCommand }, { "port", oPort }, { "cipher", oCipher }, { "ciphers", oCiphers }, { "macs", oMacs }, { "protocol", oProtocol }, { "remoteforward", oRemoteForward }, { "localforward", oLocalForward }, { "user", oUser }, { "host", oHost }, { "match", oMatch }, { "escapechar", oEscapeChar }, { "globalknownhostsfile", oGlobalKnownHostsFile }, - { "globalknownhostsfile2", oDeprecated }, { "userknownhostsfile", oUserKnownHostsFile }, - { "userknownhostsfile2", oDeprecated }, { "connectionattempts", oConnectionAttempts }, { "batchmode", oBatchMode }, { "checkhostip", oCheckHostIP }, { "stricthostkeychecking", oStrictHostKeyChecking }, { "compression", oCompression }, - { "compressionlevel", oCompressionLevel }, { "tcpkeepalive", oTCPKeepAlive }, { "keepalive", oTCPKeepAlive }, /* obsolete */ { "numberofpasswordprompts", oNumberOfPasswordPrompts }, { "loglevel", oLogLevel }, { "dynamicforward", oDynamicForward }, { "preferredauthentications", oPreferredAuthentications }, { "hostkeyalgorithms", oHostKeyAlgorithms }, { "bindaddress", oBindAddress }, -#ifdef ENABLE_PKCS11 - { "smartcarddevice", oPKCS11Provider }, - { "pkcs11provider", oPKCS11Provider }, -#else - { "smartcarddevice", oUnsupported }, - { "pkcs11provider", oUnsupported }, -#endif { "clearallforwardings", oClearAllForwardings }, { "enablesshkeysign", oEnableSSHKeysign }, { "verifyhostkeydns", oVerifyHostKeyDNS }, { "nohostauthenticationforlocalhost", oNoHostAuthenticationForLocalhost }, { "rekeylimit", oRekeyLimit }, { "connecttimeout", oConnectTimeout }, { "addressfamily", oAddressFamily }, { "serveraliveinterval", oServerAliveInterval }, { "serveralivecountmax", oServerAliveCountMax }, { "sendenv", oSendEnv }, { "controlpath", oControlPath }, { "controlmaster", oControlMaster }, { "controlpersist", oControlPersist }, { "hashknownhosts", oHashKnownHosts }, { "include", oInclude }, { "tunnel", oTunnel }, { "tunneldevice", oTunnelDevice }, { "localcommand", oLocalCommand }, { "permitlocalcommand", oPermitLocalCommand }, { "visualhostkey", oVisualHostKey }, - { "useroaming", oDeprecated }, { "kexalgorithms", oKexAlgorithms }, { "ipqos", oIPQoS }, { "requesttty", oRequestTTY }, { "proxyusefdpass", oProxyUseFdpass }, { "canonicaldomains", oCanonicalDomains }, { "canonicalizefallbacklocal", oCanonicalizeFallbackLocal }, { "canonicalizehostname", oCanonicalizeHostname }, { "canonicalizemaxdots", oCanonicalizeMaxDots }, { "canonicalizepermittedcnames", oCanonicalizePermittedCNAMEs }, { "streamlocalbindmask", oStreamLocalBindMask }, { "streamlocalbindunlink", oStreamLocalBindUnlink }, { "revokedhostkeys", oRevokedHostKeys }, { "fingerprinthash", oFingerprintHash }, { "updatehostkeys", oUpdateHostkeys }, { "hostbasedkeytypes", oHostbasedKeyTypes }, { "pubkeyacceptedkeytypes", oPubkeyAcceptedKeyTypes }, { "ignoreunknown", oIgnoreUnknown }, { "proxyjump", oProxyJump }, { "hpndisabled", oDeprecated }, { "hpnbuffersize", oDeprecated }, { "tcprcvbufpoll", oDeprecated }, { "tcprcvbuf", oDeprecated }, { "noneenabled", oUnsupported }, { "noneswitch", oUnsupported }, { "versionaddendum", oVersionAddendum }, { NULL, oBadOption } }; /* * Adds a local TCP/IP port forward to options. Never returns if there is an * error. */ void add_local_forward(Options *options, const struct Forward *newfwd) { struct Forward *fwd; extern uid_t original_real_uid; int i, ipport_reserved; if (!bind_permitted(newfwd->listen_port, original_real_uid) && newfwd->listen_path == NULL) fatal("Privileged ports can only be forwarded by root."); /* Don't add duplicates */ for (i = 0; i < options->num_local_forwards; i++) { if (forward_equals(newfwd, options->local_forwards + i)) return; } options->local_forwards = xreallocarray(options->local_forwards, options->num_local_forwards + 1, sizeof(*options->local_forwards)); fwd = &options->local_forwards[options->num_local_forwards++]; fwd->listen_host = newfwd->listen_host; fwd->listen_port = newfwd->listen_port; fwd->listen_path = newfwd->listen_path; fwd->connect_host = newfwd->connect_host; fwd->connect_port = newfwd->connect_port; fwd->connect_path = newfwd->connect_path; } /* * Adds a remote TCP/IP port forward to options. Never returns if there is * an error. */ void add_remote_forward(Options *options, const struct Forward *newfwd) { struct Forward *fwd; int i; /* Don't add duplicates */ for (i = 0; i < options->num_remote_forwards; i++) { if (forward_equals(newfwd, options->remote_forwards + i)) return; } options->remote_forwards = xreallocarray(options->remote_forwards, options->num_remote_forwards + 1, sizeof(*options->remote_forwards)); fwd = &options->remote_forwards[options->num_remote_forwards++]; fwd->listen_host = newfwd->listen_host; fwd->listen_port = newfwd->listen_port; fwd->listen_path = newfwd->listen_path; fwd->connect_host = newfwd->connect_host; fwd->connect_port = newfwd->connect_port; fwd->connect_path = newfwd->connect_path; fwd->handle = newfwd->handle; fwd->allocated_port = 0; } static void clear_forwardings(Options *options) { int i; for (i = 0; i < options->num_local_forwards; i++) { free(options->local_forwards[i].listen_host); free(options->local_forwards[i].listen_path); free(options->local_forwards[i].connect_host); free(options->local_forwards[i].connect_path); } if (options->num_local_forwards > 0) { free(options->local_forwards); options->local_forwards = NULL; } options->num_local_forwards = 0; for (i = 0; i < options->num_remote_forwards; i++) { free(options->remote_forwards[i].listen_host); free(options->remote_forwards[i].listen_path); free(options->remote_forwards[i].connect_host); free(options->remote_forwards[i].connect_path); } if (options->num_remote_forwards > 0) { free(options->remote_forwards); options->remote_forwards = NULL; } options->num_remote_forwards = 0; options->tun_open = SSH_TUNMODE_NO; } void add_certificate_file(Options *options, const char *path, int userprovided) { int i; if (options->num_certificate_files >= SSH_MAX_CERTIFICATE_FILES) fatal("Too many certificate files specified (max %d)", SSH_MAX_CERTIFICATE_FILES); /* Avoid registering duplicates */ for (i = 0; i < options->num_certificate_files; i++) { if (options->certificate_file_userprovided[i] == userprovided && strcmp(options->certificate_files[i], path) == 0) { debug2("%s: ignoring duplicate key %s", __func__, path); return; } } options->certificate_file_userprovided[options->num_certificate_files] = userprovided; options->certificate_files[options->num_certificate_files++] = xstrdup(path); } void add_identity_file(Options *options, const char *dir, const char *filename, int userprovided) { char *path; int i; if (options->num_identity_files >= SSH_MAX_IDENTITY_FILES) fatal("Too many identity files specified (max %d)", SSH_MAX_IDENTITY_FILES); if (dir == NULL) /* no dir, filename is absolute */ path = xstrdup(filename); else (void)xasprintf(&path, "%.100s%.100s", dir, filename); /* Avoid registering duplicates */ for (i = 0; i < options->num_identity_files; i++) { if (options->identity_file_userprovided[i] == userprovided && strcmp(options->identity_files[i], path) == 0) { debug2("%s: ignoring duplicate key %s", __func__, path); free(path); return; } } options->identity_file_userprovided[options->num_identity_files] = userprovided; options->identity_files[options->num_identity_files++] = path; } int default_ssh_port(void) { static int port; struct servent *sp; if (port == 0) { sp = getservbyname(SSH_SERVICE_NAME, "tcp"); port = sp ? ntohs(sp->s_port) : SSH_DEFAULT_PORT; } return port; } /* * Execute a command in a shell. * Return its exit status or -1 on abnormal exit. */ static int execute_in_shell(const char *cmd) { char *shell; pid_t pid; int devnull, status; extern uid_t original_real_uid; if ((shell = getenv("SHELL")) == NULL) shell = _PATH_BSHELL; /* Need this to redirect subprocess stdin/out */ if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) fatal("open(/dev/null): %s", strerror(errno)); debug("Executing command: '%.500s'", cmd); /* Fork and execute the command. */ if ((pid = fork()) == 0) { char *argv[4]; /* Child. Permanently give up superuser privileges. */ permanently_drop_suid(original_real_uid); /* Redirect child stdin and stdout. Leave stderr */ if (dup2(devnull, STDIN_FILENO) == -1) fatal("dup2: %s", strerror(errno)); if (dup2(devnull, STDOUT_FILENO) == -1) fatal("dup2: %s", strerror(errno)); if (devnull > STDERR_FILENO) close(devnull); closefrom(STDERR_FILENO + 1); argv[0] = shell; argv[1] = "-c"; argv[2] = xstrdup(cmd); argv[3] = NULL; execv(argv[0], argv); error("Unable to execute '%.100s': %s", cmd, strerror(errno)); /* Die with signal to make this error apparent to parent. */ signal(SIGTERM, SIG_DFL); kill(getpid(), SIGTERM); _exit(1); } /* Parent. */ if (pid < 0) fatal("%s: fork: %.100s", __func__, strerror(errno)); close(devnull); while (waitpid(pid, &status, 0) == -1) { if (errno != EINTR && errno != EAGAIN) fatal("%s: waitpid: %s", __func__, strerror(errno)); } if (!WIFEXITED(status)) { error("command '%.100s' exited abnormally", cmd); return -1; } debug3("command returned status %d", WEXITSTATUS(status)); return WEXITSTATUS(status); } /* * Parse and execute a Match directive. */ static int match_cfg_line(Options *options, char **condition, struct passwd *pw, const char *host_arg, const char *original_host, int post_canon, const char *filename, int linenum) { char *arg, *oattrib, *attrib, *cmd, *cp = *condition, *host, *criteria; const char *ruser; int r, port, this_result, result = 1, attributes = 0, negate; char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV]; /* * Configuration is likely to be incomplete at this point so we * must be prepared to use default values. */ port = options->port <= 0 ? default_ssh_port() : options->port; ruser = options->user == NULL ? pw->pw_name : options->user; if (post_canon) { host = xstrdup(options->hostname); } else if (options->hostname != NULL) { /* NB. Please keep in sync with ssh.c:main() */ host = percent_expand(options->hostname, "h", host_arg, (char *)NULL); } else { host = xstrdup(host_arg); } debug2("checking match for '%s' host %s originally %s", cp, host, original_host); while ((oattrib = attrib = strdelim(&cp)) && *attrib != '\0') { criteria = NULL; this_result = 1; if ((negate = attrib[0] == '!')) attrib++; /* criteria "all" and "canonical" have no argument */ if (strcasecmp(attrib, "all") == 0) { if (attributes > 1 || ((arg = strdelim(&cp)) != NULL && *arg != '\0')) { error("%.200s line %d: '%s' cannot be combined " "with other Match attributes", filename, linenum, oattrib); result = -1; goto out; } if (result) result = negate ? 0 : 1; goto out; } attributes++; if (strcasecmp(attrib, "canonical") == 0) { r = !!post_canon; /* force bitmask member to boolean */ if (r == (negate ? 1 : 0)) this_result = result = 0; debug3("%.200s line %d: %smatched '%s'", filename, linenum, this_result ? "" : "not ", oattrib); continue; } /* All other criteria require an argument */ if ((arg = strdelim(&cp)) == NULL || *arg == '\0') { error("Missing Match criteria for %s", attrib); result = -1; goto out; } if (strcasecmp(attrib, "host") == 0) { criteria = xstrdup(host); r = match_hostname(host, arg) == 1; if (r == (negate ? 1 : 0)) this_result = result = 0; } else if (strcasecmp(attrib, "originalhost") == 0) { criteria = xstrdup(original_host); r = match_hostname(original_host, arg) == 1; if (r == (negate ? 1 : 0)) this_result = result = 0; } else if (strcasecmp(attrib, "user") == 0) { criteria = xstrdup(ruser); r = match_pattern_list(ruser, arg, 0) == 1; if (r == (negate ? 1 : 0)) this_result = result = 0; } else if (strcasecmp(attrib, "localuser") == 0) { criteria = xstrdup(pw->pw_name); r = match_pattern_list(pw->pw_name, arg, 0) == 1; if (r == (negate ? 1 : 0)) this_result = result = 0; } else if (strcasecmp(attrib, "exec") == 0) { if (gethostname(thishost, sizeof(thishost)) == -1) fatal("gethostname: %s", strerror(errno)); strlcpy(shorthost, thishost, sizeof(shorthost)); shorthost[strcspn(thishost, ".")] = '\0'; snprintf(portstr, sizeof(portstr), "%d", port); cmd = percent_expand(arg, "L", shorthost, "d", pw->pw_dir, "h", host, "l", thishost, "n", original_host, "p", portstr, "r", ruser, "u", pw->pw_name, (char *)NULL); if (result != 1) { /* skip execution if prior predicate failed */ debug3("%.200s line %d: skipped exec " "\"%.100s\"", filename, linenum, cmd); free(cmd); continue; } r = execute_in_shell(cmd); if (r == -1) { fatal("%.200s line %d: match exec " "'%.100s' error", filename, linenum, cmd); } criteria = xstrdup(cmd); free(cmd); /* Force exit status to boolean */ r = r == 0; if (r == (negate ? 1 : 0)) this_result = result = 0; } else { error("Unsupported Match attribute %s", attrib); result = -1; goto out; } debug3("%.200s line %d: %smatched '%s \"%.100s\"' ", filename, linenum, this_result ? "": "not ", oattrib, criteria); free(criteria); } if (attributes == 0) { error("One or more attributes required for Match"); result = -1; goto out; } out: if (result != -1) debug2("match %sfound", result ? "" : "not "); *condition = cp; free(host); return result; } /* Check and prepare a domain name: removes trailing '.' and lowercases */ static void valid_domain(char *name, const char *filename, int linenum) { size_t i, l = strlen(name); u_char c, last = '\0'; if (l == 0) fatal("%s line %d: empty hostname suffix", filename, linenum); if (!isalpha((u_char)name[0]) && !isdigit((u_char)name[0])) fatal("%s line %d: hostname suffix \"%.100s\" " "starts with invalid character", filename, linenum, name); for (i = 0; i < l; i++) { c = tolower((u_char)name[i]); name[i] = (char)c; if (last == '.' && c == '.') fatal("%s line %d: hostname suffix \"%.100s\" contains " "consecutive separators", filename, linenum, name); if (c != '.' && c != '-' && !isalnum(c) && c != '_') /* technically invalid, but common */ fatal("%s line %d: hostname suffix \"%.100s\" contains " "invalid characters", filename, linenum, name); last = c; } if (name[l - 1] == '.') name[l - 1] = '\0'; } /* * Returns the number of the token pointed to by cp or oBadOption. */ static OpCodes parse_token(const char *cp, const char *filename, int linenum, const char *ignored_unknown) { int i; for (i = 0; keywords[i].name; i++) if (strcmp(cp, keywords[i].name) == 0) return keywords[i].opcode; if (ignored_unknown != NULL && match_pattern_list(cp, ignored_unknown, 1) == 1) return oIgnoredUnknownOption; error("%s: line %d: Bad configuration option: %s", filename, linenum, cp); return oBadOption; } /* Multistate option parsing */ struct multistate { char *key; int value; }; static const struct multistate multistate_flag[] = { { "true", 1 }, { "false", 0 }, { "yes", 1 }, { "no", 0 }, { NULL, -1 } }; static const struct multistate multistate_yesnoask[] = { { "true", 1 }, { "false", 0 }, { "yes", 1 }, { "no", 0 }, { "ask", 2 }, { NULL, -1 } }; static const struct multistate multistate_yesnoaskconfirm[] = { { "true", 1 }, { "false", 0 }, { "yes", 1 }, { "no", 0 }, { "ask", 2 }, { "confirm", 3 }, { NULL, -1 } }; static const struct multistate multistate_addressfamily[] = { { "inet", AF_INET }, { "inet6", AF_INET6 }, { "any", AF_UNSPEC }, { NULL, -1 } }; static const struct multistate multistate_controlmaster[] = { { "true", SSHCTL_MASTER_YES }, { "yes", SSHCTL_MASTER_YES }, { "false", SSHCTL_MASTER_NO }, { "no", SSHCTL_MASTER_NO }, { "auto", SSHCTL_MASTER_AUTO }, { "ask", SSHCTL_MASTER_ASK }, { "autoask", SSHCTL_MASTER_AUTO_ASK }, { NULL, -1 } }; static const struct multistate multistate_tunnel[] = { { "ethernet", SSH_TUNMODE_ETHERNET }, { "point-to-point", SSH_TUNMODE_POINTOPOINT }, { "true", SSH_TUNMODE_DEFAULT }, { "yes", SSH_TUNMODE_DEFAULT }, { "false", SSH_TUNMODE_NO }, { "no", SSH_TUNMODE_NO }, { NULL, -1 } }; static const struct multistate multistate_requesttty[] = { { "true", REQUEST_TTY_YES }, { "yes", REQUEST_TTY_YES }, { "false", REQUEST_TTY_NO }, { "no", REQUEST_TTY_NO }, { "force", REQUEST_TTY_FORCE }, { "auto", REQUEST_TTY_AUTO }, { NULL, -1 } }; static const struct multistate multistate_canonicalizehostname[] = { { "true", SSH_CANONICALISE_YES }, { "false", SSH_CANONICALISE_NO }, { "yes", SSH_CANONICALISE_YES }, { "no", SSH_CANONICALISE_NO }, { "always", SSH_CANONICALISE_ALWAYS }, { NULL, -1 } }; /* * Processes a single option line as used in the configuration files. This * only sets those values that have not already been set. */ int process_config_line(Options *options, struct passwd *pw, const char *host, const char *original_host, char *line, const char *filename, int linenum, int *activep, int flags) { return process_config_line_depth(options, pw, host, original_host, line, filename, linenum, activep, flags, 0); } #define WHITESPACE " \t\r\n" static int process_config_line_depth(Options *options, struct passwd *pw, const char *host, const char *original_host, char *line, const char *filename, int linenum, int *activep, int flags, int depth) { char *s, **charptr, *endofnumber, *keyword, *arg, *arg2; char **cpptr, fwdarg[256]; u_int i, *uintptr, max_entries = 0; int r, oactive, negated, opcode, *intptr, value, value2, cmdline = 0; LogLevel *log_level_ptr; long long val64; size_t len; struct Forward fwd; const struct multistate *multistate_ptr; struct allowed_cname *cname; glob_t gl; if (activep == NULL) { /* We are processing a command line directive */ cmdline = 1; activep = &cmdline; } - /* Strip trailing whitespace */ + /* Strip trailing whitespace. Allow \f (form feed) at EOL only */ if ((len = strlen(line)) == 0) return 0; for (len--; len > 0; len--) { - if (strchr(WHITESPACE, line[len]) == NULL) + if (strchr(WHITESPACE "\f", line[len]) == NULL) break; line[len] = '\0'; } s = line; /* Get the keyword. (Each line is supposed to begin with a keyword). */ if ((keyword = strdelim(&s)) == NULL) return 0; /* Ignore leading whitespace. */ if (*keyword == '\0') keyword = strdelim(&s); if (keyword == NULL || !*keyword || *keyword == '\n' || *keyword == '#') return 0; /* Match lowercase keyword */ lowercase(keyword); opcode = parse_token(keyword, filename, linenum, options->ignored_unknown); switch (opcode) { case oBadOption: /* don't panic, but count bad options */ return -1; case oIgnoredUnknownOption: debug("%s line %d: Ignored unknown option \"%s\"", filename, linenum, keyword); return 0; case oConnectTimeout: intptr = &options->connection_timeout; parse_time: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%s line %d: missing time value.", filename, linenum); if (strcmp(arg, "none") == 0) value = -1; else if ((value = convtime(arg)) == -1) fatal("%s line %d: invalid time value.", filename, linenum); if (*activep && *intptr == -1) *intptr = value; break; case oForwardAgent: intptr = &options->forward_agent; parse_flag: multistate_ptr = multistate_flag; parse_multistate: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%s line %d: missing argument.", filename, linenum); value = -1; for (i = 0; multistate_ptr[i].key != NULL; i++) { if (strcasecmp(arg, multistate_ptr[i].key) == 0) { value = multistate_ptr[i].value; break; } } if (value == -1) fatal("%s line %d: unsupported option \"%s\".", filename, linenum, arg); if (*activep && *intptr == -1) *intptr = value; break; case oForwardX11: intptr = &options->forward_x11; goto parse_flag; case oForwardX11Trusted: intptr = &options->forward_x11_trusted; goto parse_flag; case oForwardX11Timeout: intptr = &options->forward_x11_timeout; goto parse_time; case oGatewayPorts: intptr = &options->fwd_opts.gateway_ports; goto parse_flag; case oExitOnForwardFailure: intptr = &options->exit_on_forward_failure; goto parse_flag; case oUsePrivilegedPort: intptr = &options->use_privileged_port; goto parse_flag; case oPasswordAuthentication: intptr = &options->password_authentication; goto parse_flag; case oKbdInteractiveAuthentication: intptr = &options->kbd_interactive_authentication; goto parse_flag; case oKbdInteractiveDevices: charptr = &options->kbd_interactive_devices; goto parse_string; case oPubkeyAuthentication: intptr = &options->pubkey_authentication; goto parse_flag; case oRSAAuthentication: intptr = &options->rsa_authentication; goto parse_flag; case oRhostsRSAAuthentication: intptr = &options->rhosts_rsa_authentication; goto parse_flag; case oHostbasedAuthentication: intptr = &options->hostbased_authentication; goto parse_flag; case oChallengeResponseAuthentication: intptr = &options->challenge_response_authentication; goto parse_flag; case oGssAuthentication: intptr = &options->gss_authentication; goto parse_flag; case oGssDelegateCreds: intptr = &options->gss_deleg_creds; goto parse_flag; case oBatchMode: intptr = &options->batch_mode; goto parse_flag; case oCheckHostIP: intptr = &options->check_host_ip; goto parse_flag; case oVerifyHostKeyDNS: intptr = &options->verify_host_key_dns; multistate_ptr = multistate_yesnoask; goto parse_multistate; case oStrictHostKeyChecking: intptr = &options->strict_host_key_checking; multistate_ptr = multistate_yesnoask; goto parse_multistate; case oCompression: intptr = &options->compression; goto parse_flag; case oTCPKeepAlive: intptr = &options->tcp_keep_alive; goto parse_flag; case oNoHostAuthenticationForLocalhost: intptr = &options->no_host_authentication_for_localhost; goto parse_flag; case oNumberOfPasswordPrompts: intptr = &options->number_of_password_prompts; goto parse_int; case oCompressionLevel: intptr = &options->compression_level; goto parse_int; case oRekeyLimit: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); if (strcmp(arg, "default") == 0) { val64 = 0; } else { if (scan_scaled(arg, &val64) == -1) fatal("%.200s line %d: Bad number '%s': %s", filename, linenum, arg, strerror(errno)); if (val64 != 0 && val64 < 16) fatal("%.200s line %d: RekeyLimit too small", filename, linenum); } if (*activep && options->rekey_limit == -1) options->rekey_limit = val64; if (s != NULL) { /* optional rekey interval present */ if (strcmp(s, "none") == 0) { (void)strdelim(&s); /* discard */ break; } intptr = &options->rekey_interval; goto parse_time; } break; case oIdentityFile: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); if (*activep) { intptr = &options->num_identity_files; if (*intptr >= SSH_MAX_IDENTITY_FILES) fatal("%.200s line %d: Too many identity files specified (max %d).", filename, linenum, SSH_MAX_IDENTITY_FILES); add_identity_file(options, NULL, arg, flags & SSHCONF_USERCONF); } break; case oCertificateFile: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); if (*activep) { intptr = &options->num_certificate_files; if (*intptr >= SSH_MAX_CERTIFICATE_FILES) { fatal("%.200s line %d: Too many certificate " "files specified (max %d).", filename, linenum, SSH_MAX_CERTIFICATE_FILES); } add_certificate_file(options, arg, flags & SSHCONF_USERCONF); } break; case oXAuthLocation: charptr=&options->xauth_location; goto parse_string; case oUser: charptr = &options->user; parse_string: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); if (*activep && *charptr == NULL) *charptr = xstrdup(arg); break; case oGlobalKnownHostsFile: cpptr = (char **)&options->system_hostfiles; uintptr = &options->num_system_hostfiles; max_entries = SSH_MAX_HOSTS_FILES; parse_char_array: if (*activep && *uintptr == 0) { while ((arg = strdelim(&s)) != NULL && *arg != '\0') { if ((*uintptr) >= max_entries) fatal("%s line %d: " "too many authorized keys files.", filename, linenum); cpptr[(*uintptr)++] = xstrdup(arg); } } return 0; case oUserKnownHostsFile: cpptr = (char **)&options->user_hostfiles; uintptr = &options->num_user_hostfiles; max_entries = SSH_MAX_HOSTS_FILES; goto parse_char_array; case oHostName: charptr = &options->hostname; goto parse_string; case oHostKeyAlias: charptr = &options->host_key_alias; goto parse_string; case oPreferredAuthentications: charptr = &options->preferred_authentications; goto parse_string; case oBindAddress: charptr = &options->bind_address; goto parse_string; case oPKCS11Provider: charptr = &options->pkcs11_provider; goto parse_string; case oProxyCommand: charptr = &options->proxy_command; /* Ignore ProxyCommand if ProxyJump already specified */ if (options->jump_host != NULL) charptr = &options->jump_host; /* Skip below */ parse_command: if (s == NULL) fatal("%.200s line %d: Missing argument.", filename, linenum); len = strspn(s, WHITESPACE "="); if (*activep && *charptr == NULL) *charptr = xstrdup(s + len); return 0; case oProxyJump: if (s == NULL) { fatal("%.200s line %d: Missing argument.", filename, linenum); } len = strspn(s, WHITESPACE "="); if (parse_jump(s + len, options, *activep) == -1) { fatal("%.200s line %d: Invalid ProxyJump \"%s\"", filename, linenum, s + len); } return 0; case oPort: intptr = &options->port; parse_int: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); if (arg[0] < '0' || arg[0] > '9') fatal("%.200s line %d: Bad number.", filename, linenum); /* Octal, decimal, or hex format? */ value = strtol(arg, &endofnumber, 0); if (arg == endofnumber) fatal("%.200s line %d: Bad number.", filename, linenum); if (*activep && *intptr == -1) *intptr = value; break; case oConnectionAttempts: intptr = &options->connection_attempts; goto parse_int; case oCipher: intptr = &options->cipher; arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); value = cipher_number(arg); if (value == -1) fatal("%.200s line %d: Bad cipher '%s'.", filename, linenum, arg ? arg : ""); if (*activep && *intptr == -1) *intptr = value; break; case oCiphers: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); - if (!ciphers_valid(*arg == '+' ? arg + 1 : arg)) + if (*arg != '-' && !ciphers_valid(*arg == '+' ? arg + 1 : arg)) fatal("%.200s line %d: Bad SSH2 cipher spec '%s'.", filename, linenum, arg ? arg : ""); if (*activep && options->ciphers == NULL) options->ciphers = xstrdup(arg); break; case oMacs: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); - if (!mac_valid(*arg == '+' ? arg + 1 : arg)) + if (*arg != '-' && !mac_valid(*arg == '+' ? arg + 1 : arg)) fatal("%.200s line %d: Bad SSH2 Mac spec '%s'.", filename, linenum, arg ? arg : ""); if (*activep && options->macs == NULL) options->macs = xstrdup(arg); break; case oKexAlgorithms: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); - if (!kex_names_valid(*arg == '+' ? arg + 1 : arg)) + if (*arg != '-' && + !kex_names_valid(*arg == '+' ? arg + 1 : arg)) fatal("%.200s line %d: Bad SSH2 KexAlgorithms '%s'.", filename, linenum, arg ? arg : ""); if (*activep && options->kex_algorithms == NULL) options->kex_algorithms = xstrdup(arg); break; case oHostKeyAlgorithms: charptr = &options->hostkeyalgorithms; parse_keytypes: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); - if (!sshkey_names_valid2(*arg == '+' ? arg + 1 : arg, 1)) + if (*arg != '-' && + !sshkey_names_valid2(*arg == '+' ? arg + 1 : arg, 1)) fatal("%s line %d: Bad key types '%s'.", filename, linenum, arg ? arg : ""); if (*activep && *charptr == NULL) *charptr = xstrdup(arg); break; case oProtocol: intptr = &options->protocol; arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); value = proto_spec(arg); if (value == SSH_PROTO_UNKNOWN) fatal("%.200s line %d: Bad protocol spec '%s'.", filename, linenum, arg ? arg : ""); if (*activep && *intptr == SSH_PROTO_UNKNOWN) *intptr = value; break; case oLogLevel: log_level_ptr = &options->log_level; arg = strdelim(&s); value = log_level_number(arg); if (value == SYSLOG_LEVEL_NOT_SET) fatal("%.200s line %d: unsupported log level '%s'", filename, linenum, arg ? arg : ""); if (*activep && *log_level_ptr == SYSLOG_LEVEL_NOT_SET) *log_level_ptr = (LogLevel) value; break; case oLocalForward: case oRemoteForward: case oDynamicForward: arg = strdelim(&s); if (arg == NULL || *arg == '\0') fatal("%.200s line %d: Missing port argument.", filename, linenum); if (opcode == oLocalForward || opcode == oRemoteForward) { arg2 = strdelim(&s); if (arg2 == NULL || *arg2 == '\0') fatal("%.200s line %d: Missing target argument.", filename, linenum); /* construct a string for parse_forward */ snprintf(fwdarg, sizeof(fwdarg), "%s:%s", arg, arg2); } else if (opcode == oDynamicForward) { strlcpy(fwdarg, arg, sizeof(fwdarg)); } if (parse_forward(&fwd, fwdarg, opcode == oDynamicForward ? 1 : 0, opcode == oRemoteForward ? 1 : 0) == 0) fatal("%.200s line %d: Bad forwarding specification.", filename, linenum); if (*activep) { if (opcode == oLocalForward || opcode == oDynamicForward) add_local_forward(options, &fwd); else if (opcode == oRemoteForward) add_remote_forward(options, &fwd); } break; case oClearAllForwardings: intptr = &options->clear_forwardings; goto parse_flag; case oHost: if (cmdline) fatal("Host directive not supported as a command-line " "option"); *activep = 0; arg2 = NULL; while ((arg = strdelim(&s)) != NULL && *arg != '\0') { if ((flags & SSHCONF_NEVERMATCH) != 0) break; negated = *arg == '!'; if (negated) arg++; if (match_pattern(host, arg)) { if (negated) { debug("%.200s line %d: Skipping Host " "block because of negated match " "for %.100s", filename, linenum, arg); *activep = 0; break; } if (!*activep) arg2 = arg; /* logged below */ *activep = 1; } } if (*activep) debug("%.200s line %d: Applying options for %.100s", filename, linenum, arg2); /* Avoid garbage check below, as strdelim is done. */ return 0; case oMatch: if (cmdline) fatal("Host directive not supported as a command-line " "option"); value = match_cfg_line(options, &s, pw, host, original_host, flags & SSHCONF_POSTCANON, filename, linenum); if (value < 0) fatal("%.200s line %d: Bad Match condition", filename, linenum); *activep = (flags & SSHCONF_NEVERMATCH) ? 0 : value; break; case oEscapeChar: intptr = &options->escape_char; arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); if (strcmp(arg, "none") == 0) value = SSH_ESCAPECHAR_NONE; else if (arg[1] == '\0') value = (u_char) arg[0]; else if (arg[0] == '^' && arg[2] == 0 && (u_char) arg[1] >= 64 && (u_char) arg[1] < 128) value = (u_char) arg[1] & 31; else { fatal("%.200s line %d: Bad escape character.", filename, linenum); /* NOTREACHED */ value = 0; /* Avoid compiler warning. */ } if (*activep && *intptr == -1) *intptr = value; break; case oAddressFamily: intptr = &options->address_family; multistate_ptr = multistate_addressfamily; goto parse_multistate; case oEnableSSHKeysign: intptr = &options->enable_ssh_keysign; goto parse_flag; case oIdentitiesOnly: intptr = &options->identities_only; goto parse_flag; case oServerAliveInterval: intptr = &options->server_alive_interval; goto parse_time; case oServerAliveCountMax: intptr = &options->server_alive_count_max; goto parse_int; case oSendEnv: while ((arg = strdelim(&s)) != NULL && *arg != '\0') { if (strchr(arg, '=') != NULL) fatal("%s line %d: Invalid environment name.", filename, linenum); if (!*activep) continue; if (options->num_send_env >= MAX_SEND_ENV) fatal("%s line %d: too many send env.", filename, linenum); options->send_env[options->num_send_env++] = xstrdup(arg); } break; case oControlPath: charptr = &options->control_path; goto parse_string; case oControlMaster: intptr = &options->control_master; multistate_ptr = multistate_controlmaster; goto parse_multistate; case oControlPersist: /* no/false/yes/true, or a time spec */ intptr = &options->control_persist; arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing ControlPersist" " argument.", filename, linenum); value = 0; value2 = 0; /* timeout */ if (strcmp(arg, "no") == 0 || strcmp(arg, "false") == 0) value = 0; else if (strcmp(arg, "yes") == 0 || strcmp(arg, "true") == 0) value = 1; else if ((value2 = convtime(arg)) >= 0) value = 1; else fatal("%.200s line %d: Bad ControlPersist argument.", filename, linenum); if (*activep && *intptr == -1) { *intptr = value; options->control_persist_timeout = value2; } break; case oHashKnownHosts: intptr = &options->hash_known_hosts; goto parse_flag; case oTunnel: intptr = &options->tun_open; multistate_ptr = multistate_tunnel; goto parse_multistate; case oTunnelDevice: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); value = a2tun(arg, &value2); if (value == SSH_TUNID_ERR) fatal("%.200s line %d: Bad tun device.", filename, linenum); if (*activep) { options->tun_local = value; options->tun_remote = value2; } break; case oLocalCommand: charptr = &options->local_command; goto parse_command; case oPermitLocalCommand: intptr = &options->permit_local_command; goto parse_flag; case oVisualHostKey: intptr = &options->visual_host_key; goto parse_flag; case oInclude: if (cmdline) fatal("Include directive not supported as a " "command-line option"); value = 0; while ((arg = strdelim(&s)) != NULL && *arg != '\0') { /* * Ensure all paths are anchored. User configuration * files may begin with '~/' but system configurations * must not. If the path is relative, then treat it * as living in ~/.ssh for user configurations or * /etc/ssh for system ones. */ if (*arg == '~' && (flags & SSHCONF_USERCONF) == 0) fatal("%.200s line %d: bad include path %s.", filename, linenum, arg); if (*arg != '/' && *arg != '~') { xasprintf(&arg2, "%s/%s", (flags & SSHCONF_USERCONF) ? "~/" _PATH_SSH_USER_DIR : SSHDIR, arg); } else arg2 = xstrdup(arg); memset(&gl, 0, sizeof(gl)); r = glob(arg2, GLOB_TILDE, NULL, &gl); if (r == GLOB_NOMATCH) { debug("%.200s line %d: include %s matched no " "files",filename, linenum, arg2); + free(arg2); continue; } else if (r != 0 || gl.gl_pathc < 0) fatal("%.200s line %d: glob failed for %s.", filename, linenum, arg2); free(arg2); oactive = *activep; for (i = 0; i < (u_int)gl.gl_pathc; i++) { debug3("%.200s line %d: Including file %s " "depth %d%s", filename, linenum, gl.gl_pathv[i], depth, oactive ? "" : " (parse only)"); r = read_config_file_depth(gl.gl_pathv[i], pw, host, original_host, options, flags | SSHCONF_CHECKPERM | (oactive ? 0 : SSHCONF_NEVERMATCH), activep, depth + 1); + if (r != 1 && errno != ENOENT) { + fatal("Can't open user config file " + "%.100s: %.100s", gl.gl_pathv[i], + strerror(errno)); + } /* * don't let Match in includes clobber the * containing file's Match state. */ *activep = oactive; if (r != 1) value = -1; } globfree(&gl); } if (value != 0) return value; break; case oIPQoS: arg = strdelim(&s); if ((value = parse_ipqos(arg)) == -1) fatal("%s line %d: Bad IPQoS value: %s", filename, linenum, arg); arg = strdelim(&s); if (arg == NULL) value2 = value; else if ((value2 = parse_ipqos(arg)) == -1) fatal("%s line %d: Bad IPQoS value: %s", filename, linenum, arg); if (*activep) { options->ip_qos_interactive = value; options->ip_qos_bulk = value2; } break; case oRequestTTY: intptr = &options->request_tty; multistate_ptr = multistate_requesttty; goto parse_multistate; case oVersionAddendum: if (s == NULL) fatal("%.200s line %d: Missing argument.", filename, linenum); len = strspn(s, WHITESPACE); if (*activep && options->version_addendum == NULL) { if (strcasecmp(s + len, "none") == 0) options->version_addendum = xstrdup(""); else if (strchr(s + len, '\r') != NULL) fatal("%.200s line %d: Invalid argument", filename, linenum); else options->version_addendum = xstrdup(s + len); } return 0; case oIgnoreUnknown: charptr = &options->ignored_unknown; goto parse_string; case oProxyUseFdpass: intptr = &options->proxy_use_fdpass; goto parse_flag; case oCanonicalDomains: value = options->num_canonical_domains != 0; while ((arg = strdelim(&s)) != NULL && *arg != '\0') { valid_domain(arg, filename, linenum); if (!*activep || value) continue; if (options->num_canonical_domains >= MAX_CANON_DOMAINS) fatal("%s line %d: too many hostname suffixes.", filename, linenum); options->canonical_domains[ options->num_canonical_domains++] = xstrdup(arg); } break; case oCanonicalizePermittedCNAMEs: value = options->num_permitted_cnames != 0; while ((arg = strdelim(&s)) != NULL && *arg != '\0') { /* Either '*' for everything or 'list:list' */ if (strcmp(arg, "*") == 0) arg2 = arg; else { lowercase(arg); if ((arg2 = strchr(arg, ':')) == NULL || arg2[1] == '\0') { fatal("%s line %d: " "Invalid permitted CNAME \"%s\"", filename, linenum, arg); } *arg2 = '\0'; arg2++; } if (!*activep || value) continue; if (options->num_permitted_cnames >= MAX_CANON_DOMAINS) fatal("%s line %d: too many permitted CNAMEs.", filename, linenum); cname = options->permitted_cnames + options->num_permitted_cnames++; cname->source_list = xstrdup(arg); cname->target_list = xstrdup(arg2); } break; case oCanonicalizeHostname: intptr = &options->canonicalize_hostname; multistate_ptr = multistate_canonicalizehostname; goto parse_multistate; case oCanonicalizeMaxDots: intptr = &options->canonicalize_max_dots; goto parse_int; case oCanonicalizeFallbackLocal: intptr = &options->canonicalize_fallback_local; goto parse_flag; case oStreamLocalBindMask: arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing StreamLocalBindMask argument.", filename, linenum); /* Parse mode in octal format */ value = strtol(arg, &endofnumber, 8); if (arg == endofnumber || value < 0 || value > 0777) fatal("%.200s line %d: Bad mask.", filename, linenum); options->fwd_opts.streamlocal_bind_mask = (mode_t)value; break; case oStreamLocalBindUnlink: intptr = &options->fwd_opts.streamlocal_bind_unlink; goto parse_flag; case oRevokedHostKeys: charptr = &options->revoked_host_keys; goto parse_string; case oFingerprintHash: intptr = &options->fingerprint_hash; arg = strdelim(&s); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); if ((value = ssh_digest_alg_by_name(arg)) == -1) fatal("%.200s line %d: Invalid hash algorithm \"%s\".", filename, linenum, arg); if (*activep && *intptr == -1) *intptr = value; break; case oUpdateHostkeys: intptr = &options->update_hostkeys; multistate_ptr = multistate_yesnoask; goto parse_multistate; case oHostbasedKeyTypes: charptr = &options->hostbased_key_types; goto parse_keytypes; case oPubkeyAcceptedKeyTypes: charptr = &options->pubkey_key_types; goto parse_keytypes; case oAddKeysToAgent: intptr = &options->add_keys_to_agent; multistate_ptr = multistate_yesnoaskconfirm; goto parse_multistate; case oIdentityAgent: charptr = &options->identity_agent; goto parse_string; case oDeprecated: debug("%s line %d: Deprecated option \"%s\"", filename, linenum, keyword); return 0; case oUnsupported: error("%s line %d: Unsupported option \"%s\"", filename, linenum, keyword); return 0; default: fatal("%s: Unimplemented opcode %d", __func__, opcode); } /* Check that there is no garbage at end of line. */ if ((arg = strdelim(&s)) != NULL && *arg != '\0') { fatal("%.200s line %d: garbage at end of line; \"%.200s\".", filename, linenum, arg); } return 0; } /* * Reads the config file and modifies the options accordingly. Options * should already be initialized before this call. This never returns if * there is an error. If the file does not exist, this returns 0. */ int read_config_file(const char *filename, struct passwd *pw, const char *host, const char *original_host, Options *options, int flags) { int active = 1; return read_config_file_depth(filename, pw, host, original_host, options, flags, &active, 0); } #define READCONF_MAX_DEPTH 16 static int read_config_file_depth(const char *filename, struct passwd *pw, const char *host, const char *original_host, Options *options, int flags, int *activep, int depth) { FILE *f; - char line[1024]; + char line[4096]; int linenum; int bad_options = 0; if (depth < 0 || depth > READCONF_MAX_DEPTH) fatal("Too many recursive configuration includes"); if ((f = fopen(filename, "r")) == NULL) return 0; if (flags & SSHCONF_CHECKPERM) { struct stat sb; if (fstat(fileno(f), &sb) == -1) fatal("fstat %s: %s", filename, strerror(errno)); if (((sb.st_uid != 0 && sb.st_uid != getuid()) || (sb.st_mode & 022) != 0)) fatal("Bad owner or permissions on %s", filename); } debug("Reading configuration data %.200s", filename); /* * Mark that we are now processing the options. This flag is turned * on/off by Host specifications. */ linenum = 0; while (fgets(line, sizeof(line), f)) { /* Update line number counter. */ linenum++; + if (strlen(line) == sizeof(line) - 1) + fatal("%s line %d too long", filename, linenum); if (process_config_line_depth(options, pw, host, original_host, line, filename, linenum, activep, flags, depth) != 0) bad_options++; } fclose(f); if (bad_options > 0) fatal("%s: terminating, %d bad configuration options", filename, bad_options); return 1; } /* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */ int option_clear_or_none(const char *o) { return o == NULL || strcasecmp(o, "none") == 0; } /* * Initializes options to special values that indicate that they have not yet * been set. Read_config_file will only set options with this value. Options * are processed in the following order: command line, user config file, * system config file. Last, fill_default_options is called. */ void initialize_options(Options * options) { memset(options, 'X', sizeof(*options)); options->version_addendum = NULL; options->forward_agent = -1; options->forward_x11 = -1; options->forward_x11_trusted = -1; options->forward_x11_timeout = -1; options->stdio_forward_host = NULL; options->stdio_forward_port = 0; options->clear_forwardings = -1; options->exit_on_forward_failure = -1; options->xauth_location = NULL; options->fwd_opts.gateway_ports = -1; options->fwd_opts.streamlocal_bind_mask = (mode_t)-1; options->fwd_opts.streamlocal_bind_unlink = -1; options->use_privileged_port = -1; options->rsa_authentication = -1; options->pubkey_authentication = -1; options->challenge_response_authentication = -1; options->gss_authentication = -1; options->gss_deleg_creds = -1; options->password_authentication = -1; options->kbd_interactive_authentication = -1; options->kbd_interactive_devices = NULL; options->rhosts_rsa_authentication = -1; options->hostbased_authentication = -1; options->batch_mode = -1; options->check_host_ip = -1; options->strict_host_key_checking = -1; options->compression = -1; options->tcp_keep_alive = -1; options->compression_level = -1; options->port = -1; options->address_family = -1; options->connection_attempts = -1; options->connection_timeout = -1; options->number_of_password_prompts = -1; options->cipher = -1; options->ciphers = NULL; options->macs = NULL; options->kex_algorithms = NULL; options->hostkeyalgorithms = NULL; options->protocol = SSH_PROTO_UNKNOWN; options->num_identity_files = 0; options->num_certificate_files = 0; options->hostname = NULL; options->host_key_alias = NULL; options->proxy_command = NULL; options->jump_user = NULL; options->jump_host = NULL; options->jump_port = -1; options->jump_extra = NULL; options->user = NULL; options->escape_char = -1; options->num_system_hostfiles = 0; options->num_user_hostfiles = 0; options->local_forwards = NULL; options->num_local_forwards = 0; options->remote_forwards = NULL; options->num_remote_forwards = 0; options->log_level = SYSLOG_LEVEL_NOT_SET; options->preferred_authentications = NULL; options->bind_address = NULL; options->pkcs11_provider = NULL; options->enable_ssh_keysign = - 1; options->no_host_authentication_for_localhost = - 1; options->identities_only = - 1; options->rekey_limit = - 1; options->rekey_interval = -1; options->verify_host_key_dns = -1; options->server_alive_interval = -1; options->server_alive_count_max = -1; options->num_send_env = 0; options->control_path = NULL; options->control_master = -1; options->control_persist = -1; options->control_persist_timeout = 0; options->hash_known_hosts = -1; options->tun_open = -1; options->tun_local = -1; options->tun_remote = -1; options->local_command = NULL; options->permit_local_command = -1; options->add_keys_to_agent = -1; options->identity_agent = NULL; options->visual_host_key = -1; options->ip_qos_interactive = -1; options->ip_qos_bulk = -1; options->request_tty = -1; options->proxy_use_fdpass = -1; options->ignored_unknown = NULL; options->num_canonical_domains = 0; options->num_permitted_cnames = 0; options->canonicalize_max_dots = -1; options->canonicalize_fallback_local = -1; options->canonicalize_hostname = -1; options->revoked_host_keys = NULL; options->fingerprint_hash = -1; options->update_hostkeys = -1; options->hostbased_key_types = NULL; options->pubkey_key_types = NULL; } /* * A petite version of fill_default_options() that just fills the options * needed for hostname canonicalization to proceed. */ void fill_default_options_for_canonicalization(Options *options) { if (options->canonicalize_max_dots == -1) options->canonicalize_max_dots = 1; if (options->canonicalize_fallback_local == -1) options->canonicalize_fallback_local = 1; if (options->canonicalize_hostname == -1) options->canonicalize_hostname = SSH_CANONICALISE_NO; } /* * Called after processing other sources of option data, this fills those * options for which no value has been specified with their default values. */ void fill_default_options(Options * options) { if (options->forward_agent == -1) options->forward_agent = 0; if (options->forward_x11 == -1) options->forward_x11 = 0; if (options->forward_x11_trusted == -1) options->forward_x11_trusted = 0; if (options->forward_x11_timeout == -1) options->forward_x11_timeout = 1200; /* * stdio forwarding (-W) changes the default for these but we defer * setting the values so they can be overridden. */ if (options->exit_on_forward_failure == -1) options->exit_on_forward_failure = options->stdio_forward_host != NULL ? 1 : 0; if (options->clear_forwardings == -1) options->clear_forwardings = options->stdio_forward_host != NULL ? 1 : 0; if (options->clear_forwardings == 1) clear_forwardings(options); if (options->xauth_location == NULL) options->xauth_location = _PATH_XAUTH; if (options->fwd_opts.gateway_ports == -1) options->fwd_opts.gateway_ports = 0; if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1) options->fwd_opts.streamlocal_bind_mask = 0177; if (options->fwd_opts.streamlocal_bind_unlink == -1) options->fwd_opts.streamlocal_bind_unlink = 0; if (options->use_privileged_port == -1) options->use_privileged_port = 0; if (options->rsa_authentication == -1) options->rsa_authentication = 1; if (options->pubkey_authentication == -1) options->pubkey_authentication = 1; if (options->challenge_response_authentication == -1) options->challenge_response_authentication = 1; if (options->gss_authentication == -1) options->gss_authentication = 0; if (options->gss_deleg_creds == -1) options->gss_deleg_creds = 0; if (options->password_authentication == -1) options->password_authentication = 1; if (options->kbd_interactive_authentication == -1) options->kbd_interactive_authentication = 1; if (options->rhosts_rsa_authentication == -1) options->rhosts_rsa_authentication = 0; if (options->hostbased_authentication == -1) options->hostbased_authentication = 0; if (options->batch_mode == -1) options->batch_mode = 0; if (options->check_host_ip == -1) options->check_host_ip = 0; if (options->strict_host_key_checking == -1) options->strict_host_key_checking = 2; /* 2 is default */ if (options->compression == -1) options->compression = 0; if (options->tcp_keep_alive == -1) options->tcp_keep_alive = 1; if (options->compression_level == -1) options->compression_level = 6; if (options->port == -1) options->port = 0; /* Filled in ssh_connect. */ if (options->address_family == -1) options->address_family = AF_UNSPEC; if (options->connection_attempts == -1) options->connection_attempts = 1; if (options->number_of_password_prompts == -1) options->number_of_password_prompts = 3; /* Selected in ssh_login(). */ if (options->cipher == -1) options->cipher = SSH_CIPHER_NOT_SET; /* options->hostkeyalgorithms, default set in myproposals.h */ if (options->protocol == SSH_PROTO_UNKNOWN) options->protocol = SSH_PROTO_2; if (options->add_keys_to_agent == -1) options->add_keys_to_agent = 0; if (options->num_identity_files == 0) { if (options->protocol & SSH_PROTO_1) { add_identity_file(options, "~/", _PATH_SSH_CLIENT_IDENTITY, 0); } if (options->protocol & SSH_PROTO_2) { add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_RSA, 0); add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_DSA, 0); #ifdef OPENSSL_HAS_ECC add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_ECDSA, 0); #endif add_identity_file(options, "~/", _PATH_SSH_CLIENT_ID_ED25519, 0); } } if (options->escape_char == -1) options->escape_char = '~'; if (options->num_system_hostfiles == 0) { options->system_hostfiles[options->num_system_hostfiles++] = xstrdup(_PATH_SSH_SYSTEM_HOSTFILE); options->system_hostfiles[options->num_system_hostfiles++] = xstrdup(_PATH_SSH_SYSTEM_HOSTFILE2); } if (options->num_user_hostfiles == 0) { options->user_hostfiles[options->num_user_hostfiles++] = xstrdup(_PATH_SSH_USER_HOSTFILE); options->user_hostfiles[options->num_user_hostfiles++] = xstrdup(_PATH_SSH_USER_HOSTFILE2); } if (options->log_level == SYSLOG_LEVEL_NOT_SET) options->log_level = SYSLOG_LEVEL_INFO; if (options->no_host_authentication_for_localhost == - 1) options->no_host_authentication_for_localhost = 0; if (options->identities_only == -1) options->identities_only = 0; if (options->enable_ssh_keysign == -1) options->enable_ssh_keysign = 0; if (options->rekey_limit == -1) options->rekey_limit = 0; if (options->rekey_interval == -1) options->rekey_interval = 0; #if HAVE_LDNS if (options->verify_host_key_dns == -1) /* automatically trust a verified SSHFP record */ options->verify_host_key_dns = 1; #else if (options->verify_host_key_dns == -1) options->verify_host_key_dns = 0; #endif if (options->server_alive_interval == -1) options->server_alive_interval = 0; if (options->server_alive_count_max == -1) options->server_alive_count_max = 3; if (options->control_master == -1) options->control_master = 0; if (options->control_persist == -1) { options->control_persist = 0; options->control_persist_timeout = 0; } if (options->hash_known_hosts == -1) options->hash_known_hosts = 0; if (options->tun_open == -1) options->tun_open = SSH_TUNMODE_NO; if (options->tun_local == -1) options->tun_local = SSH_TUNID_ANY; if (options->tun_remote == -1) options->tun_remote = SSH_TUNID_ANY; if (options->permit_local_command == -1) options->permit_local_command = 0; if (options->visual_host_key == -1) options->visual_host_key = 0; if (options->ip_qos_interactive == -1) options->ip_qos_interactive = IPTOS_LOWDELAY; if (options->ip_qos_bulk == -1) options->ip_qos_bulk = IPTOS_THROUGHPUT; if (options->request_tty == -1) options->request_tty = REQUEST_TTY_AUTO; if (options->proxy_use_fdpass == -1) options->proxy_use_fdpass = 0; if (options->canonicalize_max_dots == -1) options->canonicalize_max_dots = 1; if (options->canonicalize_fallback_local == -1) options->canonicalize_fallback_local = 1; if (options->canonicalize_hostname == -1) options->canonicalize_hostname = SSH_CANONICALISE_NO; if (options->fingerprint_hash == -1) options->fingerprint_hash = SSH_FP_HASH_DEFAULT; if (options->update_hostkeys == -1) options->update_hostkeys = 0; if (kex_assemble_names(KEX_CLIENT_ENCRYPT, &options->ciphers) != 0 || kex_assemble_names(KEX_CLIENT_MAC, &options->macs) != 0 || kex_assemble_names(KEX_CLIENT_KEX, &options->kex_algorithms) != 0 || kex_assemble_names(KEX_DEFAULT_PK_ALG, &options->hostbased_key_types) != 0 || kex_assemble_names(KEX_DEFAULT_PK_ALG, &options->pubkey_key_types) != 0) fatal("%s: kex_assemble_names failed", __func__); #define CLEAR_ON_NONE(v) \ do { \ if (option_clear_or_none(v)) { \ free(v); \ v = NULL; \ } \ } while(0) CLEAR_ON_NONE(options->local_command); CLEAR_ON_NONE(options->proxy_command); CLEAR_ON_NONE(options->control_path); CLEAR_ON_NONE(options->revoked_host_keys); /* options->identity_agent distinguishes NULL from 'none' */ /* options->user will be set in the main program if appropriate */ /* options->hostname will be set in the main program if appropriate */ /* options->host_key_alias should not be set by default */ /* options->preferred_authentications will be set in ssh */ if (options->version_addendum == NULL) options->version_addendum = xstrdup(SSH_VERSION_FREEBSD); } struct fwdarg { char *arg; int ispath; }; /* * parse_fwd_field * parses the next field in a port forwarding specification. * sets fwd to the parsed field and advances p past the colon * or sets it to NULL at end of string. * returns 0 on success, else non-zero. */ static int parse_fwd_field(char **p, struct fwdarg *fwd) { char *ep, *cp = *p; int ispath = 0; if (*cp == '\0') { *p = NULL; return -1; /* end of string */ } /* * A field escaped with square brackets is used literally. * XXX - allow ']' to be escaped via backslash? */ if (*cp == '[') { /* find matching ']' */ for (ep = cp + 1; *ep != ']' && *ep != '\0'; ep++) { if (*ep == '/') ispath = 1; } /* no matching ']' or not at end of field. */ if (ep[0] != ']' || (ep[1] != ':' && ep[1] != '\0')) return -1; /* NUL terminate the field and advance p past the colon */ *ep++ = '\0'; if (*ep != '\0') *ep++ = '\0'; fwd->arg = cp + 1; fwd->ispath = ispath; *p = ep; return 0; } for (cp = *p; *cp != '\0'; cp++) { switch (*cp) { case '\\': memmove(cp, cp + 1, strlen(cp + 1) + 1); if (*cp == '\0') return -1; break; case '/': ispath = 1; break; case ':': *cp++ = '\0'; goto done; } } done: fwd->arg = *p; fwd->ispath = ispath; *p = cp; return 0; } /* * parse_forward * parses a string containing a port forwarding specification of the form: * dynamicfwd == 0 * [listenhost:]listenport|listenpath:connecthost:connectport|connectpath * listenpath:connectpath * dynamicfwd == 1 * [listenhost:]listenport * returns number of arguments parsed or zero on error */ int parse_forward(struct Forward *fwd, const char *fwdspec, int dynamicfwd, int remotefwd) { struct fwdarg fwdargs[4]; char *p, *cp; int i; memset(fwd, 0, sizeof(*fwd)); memset(fwdargs, 0, sizeof(fwdargs)); cp = p = xstrdup(fwdspec); /* skip leading spaces */ while (isspace((u_char)*cp)) cp++; for (i = 0; i < 4; ++i) { if (parse_fwd_field(&cp, &fwdargs[i]) != 0) break; } /* Check for trailing garbage */ if (cp != NULL && *cp != '\0') { i = 0; /* failure */ } switch (i) { case 1: if (fwdargs[0].ispath) { fwd->listen_path = xstrdup(fwdargs[0].arg); fwd->listen_port = PORT_STREAMLOCAL; } else { fwd->listen_host = NULL; fwd->listen_port = a2port(fwdargs[0].arg); } fwd->connect_host = xstrdup("socks"); break; case 2: if (fwdargs[0].ispath && fwdargs[1].ispath) { fwd->listen_path = xstrdup(fwdargs[0].arg); fwd->listen_port = PORT_STREAMLOCAL; fwd->connect_path = xstrdup(fwdargs[1].arg); fwd->connect_port = PORT_STREAMLOCAL; } else if (fwdargs[1].ispath) { fwd->listen_host = NULL; fwd->listen_port = a2port(fwdargs[0].arg); fwd->connect_path = xstrdup(fwdargs[1].arg); fwd->connect_port = PORT_STREAMLOCAL; } else { fwd->listen_host = xstrdup(fwdargs[0].arg); fwd->listen_port = a2port(fwdargs[1].arg); fwd->connect_host = xstrdup("socks"); } break; case 3: if (fwdargs[0].ispath) { fwd->listen_path = xstrdup(fwdargs[0].arg); fwd->listen_port = PORT_STREAMLOCAL; fwd->connect_host = xstrdup(fwdargs[1].arg); fwd->connect_port = a2port(fwdargs[2].arg); } else if (fwdargs[2].ispath) { fwd->listen_host = xstrdup(fwdargs[0].arg); fwd->listen_port = a2port(fwdargs[1].arg); fwd->connect_path = xstrdup(fwdargs[2].arg); fwd->connect_port = PORT_STREAMLOCAL; } else { fwd->listen_host = NULL; fwd->listen_port = a2port(fwdargs[0].arg); fwd->connect_host = xstrdup(fwdargs[1].arg); fwd->connect_port = a2port(fwdargs[2].arg); } break; case 4: fwd->listen_host = xstrdup(fwdargs[0].arg); fwd->listen_port = a2port(fwdargs[1].arg); fwd->connect_host = xstrdup(fwdargs[2].arg); fwd->connect_port = a2port(fwdargs[3].arg); break; default: i = 0; /* failure */ } free(p); if (dynamicfwd) { if (!(i == 1 || i == 2)) goto fail_free; } else { if (!(i == 3 || i == 4)) { if (fwd->connect_path == NULL && fwd->listen_path == NULL) goto fail_free; } if (fwd->connect_port <= 0 && fwd->connect_path == NULL) goto fail_free; } if ((fwd->listen_port < 0 && fwd->listen_path == NULL) || (!remotefwd && fwd->listen_port == 0)) goto fail_free; if (fwd->connect_host != NULL && strlen(fwd->connect_host) >= NI_MAXHOST) goto fail_free; /* XXX - if connecting to a remote socket, max sun len may not match this host */ if (fwd->connect_path != NULL && strlen(fwd->connect_path) >= PATH_MAX_SUN) goto fail_free; if (fwd->listen_host != NULL && strlen(fwd->listen_host) >= NI_MAXHOST) goto fail_free; if (fwd->listen_path != NULL && strlen(fwd->listen_path) >= PATH_MAX_SUN) goto fail_free; return (i); fail_free: free(fwd->connect_host); fwd->connect_host = NULL; free(fwd->connect_path); fwd->connect_path = NULL; free(fwd->listen_host); fwd->listen_host = NULL; free(fwd->listen_path); fwd->listen_path = NULL; return (0); } int parse_jump(const char *s, Options *o, int active) { char *orig, *sdup, *cp; char *host = NULL, *user = NULL; int ret = -1, port = -1, first; active &= o->proxy_command == NULL && o->jump_host == NULL; orig = sdup = xstrdup(s); first = active; do { if ((cp = strrchr(sdup, ',')) == NULL) cp = sdup; /* last */ else *cp++ = '\0'; if (first) { /* First argument and configuration is active */ if (parse_user_host_port(cp, &user, &host, &port) != 0) goto out; } else { /* Subsequent argument or inactive configuration */ if (parse_user_host_port(cp, NULL, NULL, NULL) != 0) goto out; } first = 0; /* only check syntax for subsequent hosts */ } while (cp != sdup); /* success */ if (active) { o->jump_user = user; o->jump_host = host; o->jump_port = port; o->proxy_command = xstrdup("none"); user = host = NULL; if ((cp = strrchr(s, ',')) != NULL && cp != s) { o->jump_extra = xstrdup(s); o->jump_extra[cp - s] = '\0'; } } ret = 0; out: free(orig); free(user); free(host); return ret; } /* XXX the following is a near-vebatim copy from servconf.c; refactor */ static const char * fmt_multistate_int(int val, const struct multistate *m) { u_int i; for (i = 0; m[i].key != NULL; i++) { if (m[i].value == val) return m[i].key; } return "UNKNOWN"; } static const char * fmt_intarg(OpCodes code, int val) { if (val == -1) return "unset"; switch (code) { case oAddressFamily: return fmt_multistate_int(val, multistate_addressfamily); case oVerifyHostKeyDNS: case oStrictHostKeyChecking: case oUpdateHostkeys: return fmt_multistate_int(val, multistate_yesnoask); case oControlMaster: return fmt_multistate_int(val, multistate_controlmaster); case oTunnel: return fmt_multistate_int(val, multistate_tunnel); case oRequestTTY: return fmt_multistate_int(val, multistate_requesttty); case oCanonicalizeHostname: return fmt_multistate_int(val, multistate_canonicalizehostname); case oFingerprintHash: return ssh_digest_alg_name(val); case oProtocol: switch (val) { case SSH_PROTO_1: return "1"; case SSH_PROTO_2: return "2"; case (SSH_PROTO_1|SSH_PROTO_2): return "2,1"; default: return "UNKNOWN"; } default: switch (val) { case 0: return "no"; case 1: return "yes"; default: return "UNKNOWN"; } } } static const char * lookup_opcode_name(OpCodes code) { u_int i; for (i = 0; keywords[i].name != NULL; i++) if (keywords[i].opcode == code) return(keywords[i].name); return "UNKNOWN"; } static void dump_cfg_int(OpCodes code, int val) { printf("%s %d\n", lookup_opcode_name(code), val); } static void dump_cfg_fmtint(OpCodes code, int val) { printf("%s %s\n", lookup_opcode_name(code), fmt_intarg(code, val)); } static void dump_cfg_string(OpCodes code, const char *val) { if (val == NULL) return; printf("%s %s\n", lookup_opcode_name(code), val); } static void dump_cfg_strarray(OpCodes code, u_int count, char **vals) { u_int i; for (i = 0; i < count; i++) printf("%s %s\n", lookup_opcode_name(code), vals[i]); } static void dump_cfg_strarray_oneline(OpCodes code, u_int count, char **vals) { u_int i; printf("%s", lookup_opcode_name(code)); for (i = 0; i < count; i++) printf(" %s", vals[i]); printf("\n"); } static void dump_cfg_forwards(OpCodes code, u_int count, const struct Forward *fwds) { const struct Forward *fwd; u_int i; /* oDynamicForward */ for (i = 0; i < count; i++) { fwd = &fwds[i]; - if (code == oDynamicForward && + if (code == oDynamicForward && fwd->connect_host != NULL && strcmp(fwd->connect_host, "socks") != 0) continue; - if (code == oLocalForward && + if (code == oLocalForward && fwd->connect_host != NULL && strcmp(fwd->connect_host, "socks") == 0) continue; printf("%s", lookup_opcode_name(code)); if (fwd->listen_port == PORT_STREAMLOCAL) printf(" %s", fwd->listen_path); else if (fwd->listen_host == NULL) printf(" %d", fwd->listen_port); else { printf(" [%s]:%d", fwd->listen_host, fwd->listen_port); } if (code != oDynamicForward) { if (fwd->connect_port == PORT_STREAMLOCAL) printf(" %s", fwd->connect_path); else if (fwd->connect_host == NULL) printf(" %d", fwd->connect_port); else { printf(" [%s]:%d", fwd->connect_host, fwd->connect_port); } } printf("\n"); } } void dump_client_config(Options *o, const char *host) { int i; char buf[8]; /* This is normally prepared in ssh_kex2 */ if (kex_assemble_names(KEX_DEFAULT_PK_ALG, &o->hostkeyalgorithms) != 0) fatal("%s: kex_assemble_names failed", __func__); /* Most interesting options first: user, host, port */ dump_cfg_string(oUser, o->user); dump_cfg_string(oHostName, host); dump_cfg_int(oPort, o->port); /* Flag options */ dump_cfg_fmtint(oAddressFamily, o->address_family); dump_cfg_fmtint(oBatchMode, o->batch_mode); dump_cfg_fmtint(oCanonicalizeFallbackLocal, o->canonicalize_fallback_local); dump_cfg_fmtint(oCanonicalizeHostname, o->canonicalize_hostname); dump_cfg_fmtint(oChallengeResponseAuthentication, o->challenge_response_authentication); dump_cfg_fmtint(oCheckHostIP, o->check_host_ip); dump_cfg_fmtint(oCompression, o->compression); dump_cfg_fmtint(oControlMaster, o->control_master); dump_cfg_fmtint(oEnableSSHKeysign, o->enable_ssh_keysign); dump_cfg_fmtint(oClearAllForwardings, o->clear_forwardings); dump_cfg_fmtint(oExitOnForwardFailure, o->exit_on_forward_failure); dump_cfg_fmtint(oFingerprintHash, o->fingerprint_hash); dump_cfg_fmtint(oForwardAgent, o->forward_agent); dump_cfg_fmtint(oForwardX11, o->forward_x11); dump_cfg_fmtint(oForwardX11Trusted, o->forward_x11_trusted); dump_cfg_fmtint(oGatewayPorts, o->fwd_opts.gateway_ports); #ifdef GSSAPI dump_cfg_fmtint(oGssAuthentication, o->gss_authentication); dump_cfg_fmtint(oGssDelegateCreds, o->gss_deleg_creds); #endif /* GSSAPI */ dump_cfg_fmtint(oHashKnownHosts, o->hash_known_hosts); dump_cfg_fmtint(oHostbasedAuthentication, o->hostbased_authentication); dump_cfg_fmtint(oIdentitiesOnly, o->identities_only); dump_cfg_fmtint(oKbdInteractiveAuthentication, o->kbd_interactive_authentication); dump_cfg_fmtint(oNoHostAuthenticationForLocalhost, o->no_host_authentication_for_localhost); dump_cfg_fmtint(oPasswordAuthentication, o->password_authentication); dump_cfg_fmtint(oPermitLocalCommand, o->permit_local_command); dump_cfg_fmtint(oProtocol, o->protocol); dump_cfg_fmtint(oProxyUseFdpass, o->proxy_use_fdpass); dump_cfg_fmtint(oPubkeyAuthentication, o->pubkey_authentication); dump_cfg_fmtint(oRequestTTY, o->request_tty); +#ifdef WITH_RSA1 dump_cfg_fmtint(oRhostsRSAAuthentication, o->rhosts_rsa_authentication); dump_cfg_fmtint(oRSAAuthentication, o->rsa_authentication); +#endif dump_cfg_fmtint(oStreamLocalBindUnlink, o->fwd_opts.streamlocal_bind_unlink); dump_cfg_fmtint(oStrictHostKeyChecking, o->strict_host_key_checking); dump_cfg_fmtint(oTCPKeepAlive, o->tcp_keep_alive); dump_cfg_fmtint(oTunnel, o->tun_open); dump_cfg_fmtint(oUsePrivilegedPort, o->use_privileged_port); dump_cfg_fmtint(oVerifyHostKeyDNS, o->verify_host_key_dns); dump_cfg_fmtint(oVisualHostKey, o->visual_host_key); dump_cfg_fmtint(oUpdateHostkeys, o->update_hostkeys); /* Integer options */ dump_cfg_int(oCanonicalizeMaxDots, o->canonicalize_max_dots); +#ifdef WITH_SSH1 dump_cfg_int(oCompressionLevel, o->compression_level); +#endif dump_cfg_int(oConnectionAttempts, o->connection_attempts); dump_cfg_int(oForwardX11Timeout, o->forward_x11_timeout); dump_cfg_int(oNumberOfPasswordPrompts, o->number_of_password_prompts); dump_cfg_int(oServerAliveCountMax, o->server_alive_count_max); dump_cfg_int(oServerAliveInterval, o->server_alive_interval); /* String options */ dump_cfg_string(oBindAddress, o->bind_address); dump_cfg_string(oCiphers, o->ciphers ? o->ciphers : KEX_CLIENT_ENCRYPT); dump_cfg_string(oControlPath, o->control_path); dump_cfg_string(oHostKeyAlgorithms, o->hostkeyalgorithms); dump_cfg_string(oHostKeyAlias, o->host_key_alias); dump_cfg_string(oHostbasedKeyTypes, o->hostbased_key_types); dump_cfg_string(oIdentityAgent, o->identity_agent); dump_cfg_string(oKbdInteractiveDevices, o->kbd_interactive_devices); dump_cfg_string(oKexAlgorithms, o->kex_algorithms ? o->kex_algorithms : KEX_CLIENT_KEX); dump_cfg_string(oLocalCommand, o->local_command); dump_cfg_string(oLogLevel, log_level_name(o->log_level)); dump_cfg_string(oMacs, o->macs ? o->macs : KEX_CLIENT_MAC); +#ifdef ENABLE_PKCS11 dump_cfg_string(oPKCS11Provider, o->pkcs11_provider); +#endif dump_cfg_string(oPreferredAuthentications, o->preferred_authentications); dump_cfg_string(oPubkeyAcceptedKeyTypes, o->pubkey_key_types); dump_cfg_string(oRevokedHostKeys, o->revoked_host_keys); dump_cfg_string(oXAuthLocation, o->xauth_location); /* Forwards */ dump_cfg_forwards(oDynamicForward, o->num_local_forwards, o->local_forwards); dump_cfg_forwards(oLocalForward, o->num_local_forwards, o->local_forwards); dump_cfg_forwards(oRemoteForward, o->num_remote_forwards, o->remote_forwards); /* String array options */ dump_cfg_strarray(oIdentityFile, o->num_identity_files, o->identity_files); dump_cfg_strarray_oneline(oCanonicalDomains, o->num_canonical_domains, o->canonical_domains); dump_cfg_strarray_oneline(oGlobalKnownHostsFile, o->num_system_hostfiles, o->system_hostfiles); dump_cfg_strarray_oneline(oUserKnownHostsFile, o->num_user_hostfiles, o->user_hostfiles); dump_cfg_strarray(oSendEnv, o->num_send_env, o->send_env); /* Special cases */ /* oConnectTimeout */ if (o->connection_timeout == -1) printf("connecttimeout none\n"); else dump_cfg_int(oConnectTimeout, o->connection_timeout); /* oTunnelDevice */ printf("tunneldevice"); if (o->tun_local == SSH_TUNID_ANY) printf(" any"); else printf(" %d", o->tun_local); if (o->tun_remote == SSH_TUNID_ANY) printf(":any"); else printf(":%d", o->tun_remote); printf("\n"); /* oCanonicalizePermittedCNAMEs */ if ( o->num_permitted_cnames > 0) { printf("canonicalizePermittedcnames"); for (i = 0; i < o->num_permitted_cnames; i++) { printf(" %s:%s", o->permitted_cnames[i].source_list, o->permitted_cnames[i].target_list); } printf("\n"); } /* oCipher */ if (o->cipher != SSH_CIPHER_NOT_SET) printf("Cipher %s\n", cipher_name(o->cipher)); /* oControlPersist */ if (o->control_persist == 0 || o->control_persist_timeout == 0) dump_cfg_fmtint(oControlPersist, o->control_persist); else dump_cfg_int(oControlPersist, o->control_persist_timeout); /* oEscapeChar */ if (o->escape_char == SSH_ESCAPECHAR_NONE) printf("escapechar none\n"); else { vis(buf, o->escape_char, VIS_WHITE, 0); printf("escapechar %s\n", buf); } /* oIPQoS */ printf("ipqos %s ", iptos2str(o->ip_qos_interactive)); printf("%s\n", iptos2str(o->ip_qos_bulk)); /* oRekeyLimit */ printf("rekeylimit %llu %d\n", (unsigned long long)o->rekey_limit, o->rekey_interval); /* oStreamLocalBindMask */ printf("streamlocalbindmask 0%o\n", o->fwd_opts.streamlocal_bind_mask); /* oProxyCommand / oProxyJump */ if (o->jump_host == NULL) dump_cfg_string(oProxyCommand, o->proxy_command); else { /* Check for numeric addresses */ i = strchr(o->jump_host, ':') != NULL || strspn(o->jump_host, "1234567890.") == strlen(o->jump_host); snprintf(buf, sizeof(buf), "%d", o->jump_port); printf("proxyjump %s%s%s%s%s%s%s%s%s\n", /* optional additional jump spec */ o->jump_extra == NULL ? "" : o->jump_extra, o->jump_extra == NULL ? "" : ",", /* optional user */ o->jump_user == NULL ? "" : o->jump_user, o->jump_user == NULL ? "" : "@", /* opening [ if hostname is numeric */ i ? "[" : "", /* mandatory hostname */ o->jump_host, /* closing ] if hostname is numeric */ i ? "]" : "", /* optional port number */ o->jump_port <= 0 ? "" : ":", o->jump_port <= 0 ? "" : buf); } } diff --git a/crypto/openssh/regress/Makefile b/crypto/openssh/regress/Makefile index c2dba4fdf5ea..b23496b98417 100644 --- a/crypto/openssh/regress/Makefile +++ b/crypto/openssh/regress/Makefile @@ -1,232 +1,233 @@ # $OpenBSD: Makefile,v 1.94 2016/12/16 03:51:19 dtucker Exp $ REGRESS_TARGETS= unit t1 t2 t3 t4 t5 t6 t7 t8 t9 t10 t11 t12 t-exec tests: prep $(REGRESS_TARGETS) # Interop tests are not run by default interop interop-tests: t-exec-interop prep: test "x${USE_VALGRIND}" = "x" || mkdir -p $(OBJ)/valgrind-out clean: for F in $(CLEANFILES); do rm -f $(OBJ)$$F; done test -z "${SUDO}" || ${SUDO} rm -f ${SUDO_CLEAN} rm -rf $(OBJ).putty distclean: clean LTESTS= connect \ proxy-connect \ connect-privsep \ proto-version \ proto-mismatch \ exit-status \ envpass \ transfer \ banner \ rekey \ stderr-data \ stderr-after-eof \ broken-pipe \ try-ciphers \ yes-head \ login-timeout \ agent \ agent-getpeereid \ agent-timeout \ agent-ptrace \ keyscan \ keygen-change \ keygen-convert \ keygen-moduli \ key-options \ scp \ sftp \ sftp-chroot \ sftp-cmds \ sftp-badcmds \ sftp-batch \ sftp-glob \ sftp-perm \ reconfigure \ dynamic-forward \ forwarding \ multiplex \ reexec \ brokenkeys \ sshcfgparse \ cfgparse \ cfgmatch \ addrmatch \ localcommand \ forcecommand \ portnum \ keytype \ kextype \ cert-hostkey \ cert-userkey \ host-expand \ keys-command \ forward-control \ integrity \ krl \ multipubkey \ limit-keytype \ hostkey-agent \ keygen-knownhosts \ hostkey-rotate \ principals-command \ cert-file \ cfginclude \ allow-deny-users # dhgex \ INTEROP_TESTS= putty-transfer putty-ciphers putty-kex conch-ciphers #INTEROP_TESTS+=ssh-com ssh-com-client ssh-com-keygen ssh-com-sftp #LTESTS= cipher-speed USERNAME!= id -un CLEANFILES= *.core actual agent-key.* authorized_keys_${USERNAME} \ authorized_keys_${USERNAME}.* \ authorized_principals_${USERNAME} \ banner.in banner.out cert_host_key* cert_user_key* \ copy.1 copy.2 data ed25519-agent ed25519-agent* \ ed25519-agent.pub empty.in expect failed-regress.log \ failed-ssh.log failed-sshd.log hkr.* host.rsa host.rsa1 \ host_* host_ca_key* host_krl_* host_revoked_* key.* \ key.dsa-* key.ecdsa-* key.ed25519-512 key.ed25519-512.pub \ key.rsa-* keys-command-args kh.* known_hosts \ known_hosts-cert known_hosts.* krl-* ls.copy modpipe \ netcat pidfile putty.rsa2 ready regress.log remote_pid \ revoked-* rsa rsa-agent rsa-agent.pub rsa.pub rsa1 \ rsa1-agent rsa1-agent.pub rsa1.pub rsa_ssh2_cr.prv \ rsa_ssh2_crnl.prv scp-ssh-wrapper.exe \ scp-ssh-wrapper.scp setuid-allowed sftp-server.log \ sftp-server.sh sftp.log ssh-log-wrapper.sh ssh.log \ ssh_config ssh_config.* ssh_proxy ssh_proxy_bak \ ssh_proxy_envpass sshd.log sshd_config sshd_config.orig \ sshd_proxy sshd_proxy.* sshd_proxy_bak sshd_proxy_orig \ t10.out t10.out.pub t12.out t12.out.pub t2.out t3.out \ t6.out1 t6.out2 t7.out t7.out.pub t8.out t8.out.pub \ t9.out t9.out.pub testdata user_*key* user_ca* user_key* SUDO_CLEAN+= /var/run/testdata_${USERNAME} /var/run/keycommand_${USERNAME} # Enable all malloc(3) randomisations and checks TEST_ENV= "MALLOC_OPTIONS=CFGJRSUX" TEST_SSH_SSHKEYGEN?=ssh-keygen CPPFLAGS=-I.. t1: ${TEST_SSH_SSHKEYGEN} -if ${.CURDIR}/rsa_ssh2.prv | diff - ${.CURDIR}/rsa_openssh.prv tr '\n' '\r' <${.CURDIR}/rsa_ssh2.prv > ${.OBJDIR}/rsa_ssh2_cr.prv ${TEST_SSH_SSHKEYGEN} -if ${.OBJDIR}/rsa_ssh2_cr.prv | diff - ${.CURDIR}/rsa_openssh.prv awk '{print $$0 "\r"}' ${.CURDIR}/rsa_ssh2.prv > ${.OBJDIR}/rsa_ssh2_crnl.prv ${TEST_SSH_SSHKEYGEN} -if ${.OBJDIR}/rsa_ssh2_crnl.prv | diff - ${.CURDIR}/rsa_openssh.prv t2: cat ${.CURDIR}/rsa_openssh.prv > $(OBJ)/t2.out chmod 600 $(OBJ)/t2.out ${TEST_SSH_SSHKEYGEN} -yf $(OBJ)/t2.out | diff - ${.CURDIR}/rsa_openssh.pub t3: ${TEST_SSH_SSHKEYGEN} -ef ${.CURDIR}/rsa_openssh.pub >$(OBJ)/t3.out ${TEST_SSH_SSHKEYGEN} -if $(OBJ)/t3.out | diff - ${.CURDIR}/rsa_openssh.pub t4: ${TEST_SSH_SSHKEYGEN} -E md5 -lf ${.CURDIR}/rsa_openssh.pub |\ awk '{print $$2}' | diff - ${.CURDIR}/t4.ok t5: ${TEST_SSH_SSHKEYGEN} -Bf ${.CURDIR}/rsa_openssh.pub |\ awk '{print $$2}' | diff - ${.CURDIR}/t5.ok t6: ${TEST_SSH_SSHKEYGEN} -if ${.CURDIR}/dsa_ssh2.prv > $(OBJ)/t6.out1 ${TEST_SSH_SSHKEYGEN} -if ${.CURDIR}/dsa_ssh2.pub > $(OBJ)/t6.out2 chmod 600 $(OBJ)/t6.out1 ${TEST_SSH_SSHKEYGEN} -yf $(OBJ)/t6.out1 | diff - $(OBJ)/t6.out2 $(OBJ)/t7.out: ${TEST_SSH_SSHKEYGEN} -q -t rsa -N '' -f $@ t7: $(OBJ)/t7.out ${TEST_SSH_SSHKEYGEN} -lf $(OBJ)/t7.out > /dev/null ${TEST_SSH_SSHKEYGEN} -Bf $(OBJ)/t7.out > /dev/null $(OBJ)/t8.out: ${TEST_SSH_SSHKEYGEN} -q -t dsa -N '' -f $@ t8: $(OBJ)/t8.out ${TEST_SSH_SSHKEYGEN} -lf $(OBJ)/t8.out > /dev/null ${TEST_SSH_SSHKEYGEN} -Bf $(OBJ)/t8.out > /dev/null $(OBJ)/t9.out: test "${TEST_SSH_ECC}" != yes || \ ${TEST_SSH_SSHKEYGEN} -q -t ecdsa -N '' -f $@ t9: $(OBJ)/t9.out test "${TEST_SSH_ECC}" != yes || \ ${TEST_SSH_SSHKEYGEN} -lf $(OBJ)/t9.out > /dev/null test "${TEST_SSH_ECC}" != yes || \ ${TEST_SSH_SSHKEYGEN} -Bf $(OBJ)/t9.out > /dev/null $(OBJ)/t10.out: ${TEST_SSH_SSHKEYGEN} -q -t ed25519 -N '' -f $@ t10: $(OBJ)/t10.out ${TEST_SSH_SSHKEYGEN} -lf $(OBJ)/t10.out > /dev/null ${TEST_SSH_SSHKEYGEN} -Bf $(OBJ)/t10.out > /dev/null t11: ${TEST_SSH_SSHKEYGEN} -E sha256 -lf ${.CURDIR}/rsa_openssh.pub |\ awk '{print $$2}' | diff - ${.CURDIR}/t11.ok $(OBJ)/t12.out: ${TEST_SSH_SSHKEYGEN} -q -t ed25519 -N '' -C 'test-comment-1234' -f $@ t12: $(OBJ)/t12.out ${TEST_SSH_SSHKEYGEN} -lf $(OBJ)/t12.out.pub | grep test-comment-1234 >/dev/null t-exec: ${LTESTS:=.sh} @if [ "x$?" = "x" ]; then exit 0; fi; \ for TEST in ""$?; do \ echo "run test $${TEST}" ... 1>&2; \ (env SUDO="${SUDO}" TEST_ENV=${TEST_ENV} ${TEST_SHELL} ${.CURDIR}/test-exec.sh ${.OBJDIR} ${.CURDIR}/$${TEST}) || exit $$?; \ done t-exec-interop: ${INTEROP_TESTS:=.sh} @if [ "x$?" = "x" ]; then exit 0; fi; \ for TEST in ""$?; do \ echo "run test $${TEST}" ... 1>&2; \ (env SUDO="${SUDO}" TEST_ENV=${TEST_ENV} ${TEST_SHELL} ${.CURDIR}/test-exec.sh ${.OBJDIR} ${.CURDIR}/$${TEST}) || exit $$?; \ done # Not run by default interop: ${INTEROP_TARGETS} # Unit tests, built by top-level Makefile unit: set -e ; if test -z "${SKIP_UNIT}" ; then \ V="" ; \ test "x${USE_VALGRIND}" = "x" || \ V=${.CURDIR}/valgrind-unit.sh ; \ $$V ${.OBJDIR}/unittests/sshbuf/test_sshbuf ; \ $$V ${.OBJDIR}/unittests/sshkey/test_sshkey \ -d ${.CURDIR}/unittests/sshkey/testdata ; \ $$V ${.OBJDIR}/unittests/bitmap/test_bitmap ; \ + $$V ${.OBJDIR}/unittests/conversion/test_conversion ; \ $$V ${.OBJDIR}/unittests/kex/test_kex ; \ $$V ${.OBJDIR}/unittests/hostkeys/test_hostkeys \ -d ${.CURDIR}/unittests/hostkeys/testdata ; \ $$V ${.OBJDIR}/unittests/match/test_match ; \ if test "x${TEST_SSH_UTF8}" = "xyes" ; then \ $$V ${.OBJDIR}/unittests/utf8/test_utf8 ; \ fi \ fi diff --git a/crypto/openssh/regress/agent-getpeereid.sh b/crypto/openssh/regress/agent-getpeereid.sh index 91621a59ca19..34bced154f72 100644 --- a/crypto/openssh/regress/agent-getpeereid.sh +++ b/crypto/openssh/regress/agent-getpeereid.sh @@ -1,56 +1,56 @@ -# $OpenBSD: agent-getpeereid.sh,v 1.7 2016/09/26 21:34:38 bluhm Exp $ +# $OpenBSD: agent-getpeereid.sh,v 1.8 2017/01/06 02:51:16 djm Exp $ # Placed in the Public Domain. tid="disallow agent attach from other uid" UNPRIV=nobody ASOCK=${OBJ}/agent SSH_AUTH_SOCK=/nonexistent if config_defined HAVE_GETPEEREID HAVE_GETPEERUCRED HAVE_SO_PEERCRED ; then : else echo "skipped (not supported on this platform)" exit 0 fi case "x$SUDO" in xsudo) sudo=1;; xdoas) ;; x) echo "need SUDO to switch to uid $UNPRIV" exit 0 ;; *) echo "unsupported $SUDO - "doas" and "sudo" are allowed" exit 0 ;; esac trace "start agent" eval `${SSHAGENT} -s -a ${ASOCK}` > /dev/null r=$? if [ $r -ne 0 ]; then fail "could not start ssh-agent: exit code $r" else chmod 644 ${SSH_AUTH_SOCK} - ssh-add -l > /dev/null 2>&1 + ${SSHADD} -l > /dev/null 2>&1 r=$? if [ $r -ne 1 ]; then fail "ssh-add failed with $r != 1" fi if test -z "$sudo" ; then # doas - ${SUDO} -n -u ${UNPRIV} ssh-add -l 2>/dev/null + ${SUDO} -n -u ${UNPRIV} ${SSHADD} -l 2>/dev/null else # sudo - < /dev/null ${SUDO} -S -u ${UNPRIV} ssh-add -l 2>/dev/null + < /dev/null ${SUDO} -S -u ${UNPRIV} ${SSHADD} -l 2>/dev/null fi r=$? if [ $r -lt 2 ]; then fail "ssh-add did not fail for ${UNPRIV}: $r < 2" fi trace "kill agent" ${SSHAGENT} -k > /dev/null fi rm -f ${OBJ}/agent diff --git a/crypto/openssh/regress/allow-deny-users.sh b/crypto/openssh/regress/allow-deny-users.sh index 32a269afa97c..86805e19322b 100644 --- a/crypto/openssh/regress/allow-deny-users.sh +++ b/crypto/openssh/regress/allow-deny-users.sh @@ -1,40 +1,40 @@ # Public Domain # Zev Weiss, 2016 tid="AllowUsers/DenyUsers" me="$LOGNAME" -if [ "x$me" == "x" ]; then +if [ "x$me" = "x" ]; then me=`whoami` fi other="nobody" test_auth() { deny="$1" allow="$2" should_succeed="$3" failmsg="$4" start_sshd -oDenyUsers="$deny" -oAllowUsers="$allow" ${SSH} -F $OBJ/ssh_config "$me@somehost" true status=$? if (test $status -eq 0 && ! $should_succeed) \ || (test $status -ne 0 && $should_succeed); then fail "$failmsg" fi stop_sshd } # DenyUsers AllowUsers should_succeed failure_message test_auth "" "" true "user in neither DenyUsers nor AllowUsers denied" test_auth "$other $me" "" false "user in DenyUsers allowed" test_auth "$me $other" "" false "user in DenyUsers allowed" test_auth "" "$other" false "user not in AllowUsers allowed" test_auth "" "$other $me" true "user in AllowUsers denied" test_auth "" "$me $other" true "user in AllowUsers denied" test_auth "$me $other" "$me $other" false "user in both DenyUsers and AllowUsers allowed" test_auth "$other $me" "$other $me" false "user in both DenyUsers and AllowUsers allowed" diff --git a/crypto/openssh/regress/cert-file.sh b/crypto/openssh/regress/cert-file.sh index b184e7feabce..43b8e02014ce 100755 --- a/crypto/openssh/regress/cert-file.sh +++ b/crypto/openssh/regress/cert-file.sh @@ -1,138 +1,169 @@ -# $OpenBSD: cert-file.sh,v 1.4 2016/12/16 02:48:55 djm Exp $ +# $OpenBSD: cert-file.sh,v 1.5 2017/03/11 23:44:16 djm Exp $ # Placed in the Public Domain. tid="ssh with certificates" rm -f $OBJ/user_ca_key* $OBJ/user_key* rm -f $OBJ/cert_user_key* # Create a CA key ${SSHKEYGEN} -q -N '' -t ed25519 -f $OBJ/user_ca_key1 ||\ fatal "ssh-keygen failed" ${SSHKEYGEN} -q -N '' -t ed25519 -f $OBJ/user_ca_key2 ||\ fatal "ssh-keygen failed" # Make some keys and certificates. ${SSHKEYGEN} -q -N '' -t ed25519 -f $OBJ/user_key1 || \ fatal "ssh-keygen failed" ${SSHKEYGEN} -q -N '' -t ed25519 -f $OBJ/user_key2 || \ fatal "ssh-keygen failed" +${SSHKEYGEN} -q -N '' -t ed25519 -f $OBJ/user_key3 || \ + fatal "ssh-keygen failed" +${SSHKEYGEN} -q -N '' -t ed25519 -f $OBJ/user_key4 || \ + fatal "ssh-keygen failed" +${SSHKEYGEN} -q -N '' -t ed25519 -f $OBJ/user_key5 || \ + fatal "ssh-keygen failed" + # Move the certificate to a different address to better control # when it is offered. ${SSHKEYGEN} -q -s $OBJ/user_ca_key1 -I "regress user key for $USER" \ -z $$ -n ${USER} $OBJ/user_key1 || - fail "couldn't sign user_key1 with user_ca_key1" + fatal "couldn't sign user_key1 with user_ca_key1" mv $OBJ/user_key1-cert.pub $OBJ/cert_user_key1_1.pub ${SSHKEYGEN} -q -s $OBJ/user_ca_key2 -I "regress user key for $USER" \ -z $$ -n ${USER} $OBJ/user_key1 || - fail "couldn't sign user_key1 with user_ca_key2" + fatal "couldn't sign user_key1 with user_ca_key2" mv $OBJ/user_key1-cert.pub $OBJ/cert_user_key1_2.pub +${SSHKEYGEN} -q -s $OBJ/user_ca_key1 -I "regress user key for $USER" \ + -z $$ -n ${USER} $OBJ/user_key3 || + fatal "couldn't sign user_key3 with user_ca_key1" +rm $OBJ/user_key3.pub # to test use of private key w/o public half. +${SSHKEYGEN} -q -s $OBJ/user_ca_key1 -I "regress user key for $USER" \ + -z $$ -n ${USER} $OBJ/user_key4 || + fatal "couldn't sign user_key4 with user_ca_key1" +rm $OBJ/user_key4 $OBJ/user_key4.pub # to test no matching pub/private key case. trace 'try with identity files' opts="-F $OBJ/ssh_proxy -oIdentitiesOnly=yes" opts2="$opts -i $OBJ/user_key1 -i $OBJ/user_key2" echo "cert-authority $(cat $OBJ/user_ca_key1.pub)" > $OBJ/authorized_keys_$USER +# Make a clean config that doesn't have any pre-added identities. +cat $OBJ/ssh_proxy | grep -v IdentityFile > $OBJ/no_identity_config + +# XXX: verify that certificate used was what we expect. Needs exposure of +# keys via enviornment variable or similar. + for p in ${SSH_PROTOCOLS}; do + # Key with no .pub should work - finding the equivalent *-cert.pub. + verbose "protocol $p: identity cert with no plain public file" + ${SSH} -F $OBJ/no_identity_config -oIdentitiesOnly=yes \ + -i $OBJ/user_key3 somehost exit 5$p + [ $? -ne 5$p ] && fail "ssh failed" + + # CertificateFile matching private key with no .pub file should work. + verbose "protocol $p: CertificateFile with no plain public file" + ${SSH} -F $OBJ/no_identity_config -oIdentitiesOnly=yes \ + -oCertificateFile=$OBJ/user_key3-cert.pub \ + -i $OBJ/user_key3 somehost exit 5$p + [ $? -ne 5$p ] && fail "ssh failed" + # Just keys should fail + verbose "protocol $p: plain keys" ${SSH} $opts2 somehost exit 5$p r=$? if [ $r -eq 5$p ]; then fail "ssh succeeded with no certs in protocol $p" fi # Keys with untrusted cert should fail. + verbose "protocol $p: untrusted cert" opts3="$opts2 -oCertificateFile=$OBJ/cert_user_key1_2.pub" ${SSH} $opts3 somehost exit 5$p r=$? if [ $r -eq 5$p ]; then fail "ssh succeeded with bad cert in protocol $p" fi # Good cert with bad key should fail. + verbose "protocol $p: good cert, bad key" opts3="$opts -i $OBJ/user_key2" opts3="$opts3 -oCertificateFile=$OBJ/cert_user_key1_1.pub" ${SSH} $opts3 somehost exit 5$p r=$? if [ $r -eq 5$p ]; then fail "ssh succeeded with no matching key in protocol $p" fi # Keys with one trusted cert, should succeed. + verbose "protocol $p: single trusted" opts3="$opts2 -oCertificateFile=$OBJ/cert_user_key1_1.pub" ${SSH} $opts3 somehost exit 5$p r=$? if [ $r -ne 5$p ]; then fail "ssh failed with trusted cert and key in protocol $p" fi # Multiple certs and keys, with one trusted cert, should succeed. + verbose "protocol $p: multiple trusted" opts3="$opts2 -oCertificateFile=$OBJ/cert_user_key1_2.pub" opts3="$opts3 -oCertificateFile=$OBJ/cert_user_key1_1.pub" ${SSH} $opts3 somehost exit 5$p r=$? if [ $r -ne 5$p ]; then fail "ssh failed with multiple certs in protocol $p" fi - - #Keys with trusted certificate specified in config options, should succeed. - opts3="$opts2 -oCertificateFile=$OBJ/cert_user_key1_1.pub" - ${SSH} $opts3 somehost exit 5$p - r=$? - if [ $r -ne 5$p ]; then - fail "ssh failed with trusted cert in config in protocol $p" - fi done #next, using an agent in combination with the keys SSH_AUTH_SOCK=/nonexistent ${SSHADD} -l > /dev/null 2>&1 if [ $? -ne 2 ]; then fatal "ssh-add -l did not fail with exit code 2" fi trace "start agent" eval `${SSHAGENT} -s` > /dev/null r=$? if [ $r -ne 0 ]; then fatal "could not start ssh-agent: exit code $r" fi # add private keys to agent ${SSHADD} -k $OBJ/user_key2 > /dev/null 2>&1 if [ $? -ne 0 ]; then fatal "ssh-add did not succeed with exit code 0" fi ${SSHADD} -k $OBJ/user_key1 > /dev/null 2>&1 if [ $? -ne 0 ]; then fatal "ssh-add did not succeed with exit code 0" fi # try ssh with the agent and certificates # note: ssh agent only uses certificates in protocol 2 opts="-F $OBJ/ssh_proxy" # with no certificates, shoud fail ${SSH} -2 $opts somehost exit 52 if [ $? -eq 52 ]; then fail "ssh connect with agent in protocol 2 succeeded with no cert" fi #with an untrusted certificate, should fail opts="$opts -oCertificateFile=$OBJ/cert_user_key1_2.pub" ${SSH} -2 $opts somehost exit 52 if [ $? -eq 52 ]; then fail "ssh connect with agent in protocol 2 succeeded with bad cert" fi #with an additional trusted certificate, should succeed opts="$opts -oCertificateFile=$OBJ/cert_user_key1_1.pub" ${SSH} -2 $opts somehost exit 52 if [ $? -ne 52 ]; then fail "ssh connect with agent in protocol 2 failed with good cert" fi trace "kill agent" ${SSHAGENT} -k > /dev/null #cleanup rm -f $OBJ/user_ca_key* $OBJ/user_key* rm -f $OBJ/cert_user_key* diff --git a/crypto/openssh/regress/forwarding.sh b/crypto/openssh/regress/forwarding.sh index 2539db9b754b..45c596d7db99 100644 --- a/crypto/openssh/regress/forwarding.sh +++ b/crypto/openssh/regress/forwarding.sh @@ -1,143 +1,149 @@ -# $OpenBSD: forwarding.sh,v 1.16 2016/04/14 23:57:17 djm Exp $ +# $OpenBSD: forwarding.sh,v 1.19 2017/01/30 05:22:14 djm Exp $ # Placed in the Public Domain. tid="local and remote forwarding" DATA=/bin/ls${EXEEXT} start_sshd base=33 last=$PORT fwd="" -CTL=$OBJ/ctl-sock -rm -f $CTL +CTL=/tmp/openssh.regress.ctl-sock.$$ for j in 0 1 2; do for i in 0 1 2; do a=$base$j$i b=`expr $a + 50` c=$last # fwd chain: $a -> $b -> $c fwd="$fwd -L$a:127.0.0.1:$b -R$b:127.0.0.1:$c" last=$a done done for p in ${SSH_PROTOCOLS}; do q=`expr 3 - $p` if ! ssh_version $q; then q=$p fi trace "start forwarding, fork to background" - ${SSH} -$p -F $OBJ/ssh_config -f $fwd somehost sleep 10 + rm -f $CTL + ${SSH} -S $CTL -M -$p -F $OBJ/ssh_config -f $fwd somehost sleep 10 trace "transfer over forwarded channels and check result" ${SSH} -$q -F $OBJ/ssh_config -p$last -o 'ConnectionAttempts=4' \ somehost cat ${DATA} > ${COPY} test -s ${COPY} || fail "failed copy of ${DATA}" cmp ${DATA} ${COPY} || fail "corrupted copy of ${DATA}" - sleep 10 + ${SSH} -F $OBJ/ssh_config -S $CTL -O exit somehost done for p in ${SSH_PROTOCOLS}; do for d in L R; do trace "exit on -$d forward failure, proto $p" # this one should succeed ${SSH} -$p -F $OBJ/ssh_config \ -$d ${base}01:127.0.0.1:$PORT \ -$d ${base}02:127.0.0.1:$PORT \ -$d ${base}03:127.0.0.1:$PORT \ -$d ${base}04:127.0.0.1:$PORT \ -oExitOnForwardFailure=yes somehost true if [ $? != 0 ]; then - fail "connection failed, should not" + fatal "connection failed, should not" else # this one should fail ${SSH} -q -$p -F $OBJ/ssh_config \ -$d ${base}01:127.0.0.1:$PORT \ -$d ${base}02:127.0.0.1:$PORT \ -$d ${base}03:127.0.0.1:$PORT \ -$d ${base}01:localhost:$PORT \ -$d ${base}04:127.0.0.1:$PORT \ -oExitOnForwardFailure=yes somehost true r=$? if [ $r != 255 ]; then fail "connection not termintated, but should ($r)" fi fi done done for p in ${SSH_PROTOCOLS}; do trace "simple clear forwarding proto $p" ${SSH} -$p -F $OBJ/ssh_config -oClearAllForwardings=yes somehost true trace "clear local forward proto $p" - ${SSH} -$p -f -F $OBJ/ssh_config -L ${base}01:127.0.0.1:$PORT \ + rm -f $CTL + ${SSH} -S $CTL -M -$p -f -F $OBJ/ssh_config -L ${base}01:127.0.0.1:$PORT \ -oClearAllForwardings=yes somehost sleep 10 if [ $? != 0 ]; then fail "connection failed with cleared local forwarding" else # this one should fail - ${SSH} -$p -F $OBJ/ssh_config -p ${base}01 true \ + ${SSH} -$p -F $OBJ/ssh_config -p ${base}01 somehost true \ >>$TEST_REGRESS_LOGFILE 2>&1 && \ fail "local forwarding not cleared" fi - sleep 10 + ${SSH} -F $OBJ/ssh_config -S $CTL -O exit somehost trace "clear remote forward proto $p" - ${SSH} -$p -f -F $OBJ/ssh_config -R ${base}01:127.0.0.1:$PORT \ + rm -f $CTL + ${SSH} -S $CTL -M -$p -f -F $OBJ/ssh_config -R ${base}01:127.0.0.1:$PORT \ -oClearAllForwardings=yes somehost sleep 10 if [ $? != 0 ]; then fail "connection failed with cleared remote forwarding" else # this one should fail - ${SSH} -$p -F $OBJ/ssh_config -p ${base}01 true \ + ${SSH} -$p -F $OBJ/ssh_config -p ${base}01 somehost true \ >>$TEST_REGRESS_LOGFILE 2>&1 && \ fail "remote forwarding not cleared" fi - sleep 10 + ${SSH} -F $OBJ/ssh_config -S $CTL -O exit somehost done for p in 2; do trace "stdio forwarding proto $p" cmd="${SSH} -$p -F $OBJ/ssh_config" $cmd -o "ProxyCommand $cmd -q -W localhost:$PORT somehost" \ somehost true if [ $? != 0 ]; then fail "stdio forwarding proto $p" fi done echo "LocalForward ${base}01 127.0.0.1:$PORT" >> $OBJ/ssh_config echo "RemoteForward ${base}02 127.0.0.1:${base}01" >> $OBJ/ssh_config for p in ${SSH_PROTOCOLS}; do trace "config file: start forwarding, fork to background" + rm -f $CTL ${SSH} -S $CTL -M -$p -F $OBJ/ssh_config -f somehost sleep 10 trace "config file: transfer over forwarded channels and check result" ${SSH} -F $OBJ/ssh_config -p${base}02 -o 'ConnectionAttempts=4' \ somehost cat ${DATA} > ${COPY} test -s ${COPY} || fail "failed copy of ${DATA}" cmp ${DATA} ${COPY} || fail "corrupted copy of ${DATA}" - ${SSH} -S $CTL -O exit somehost + ${SSH} -F $OBJ/ssh_config -S $CTL -O exit somehost done for p in 2; do trace "transfer over chained unix domain socket forwards and check result" rm -f $OBJ/unix-[123].fwd - ${SSH} -f -F $OBJ/ssh_config -R${base}01:[$OBJ/unix-1.fwd] somehost sleep 10 - ${SSH} -f -F $OBJ/ssh_config -L[$OBJ/unix-1.fwd]:[$OBJ/unix-2.fwd] somehost sleep 10 - ${SSH} -f -F $OBJ/ssh_config -R[$OBJ/unix-2.fwd]:[$OBJ/unix-3.fwd] somehost sleep 10 - ${SSH} -f -F $OBJ/ssh_config -L[$OBJ/unix-3.fwd]:127.0.0.1:$PORT somehost sleep 10 + rm -f $CTL $CTL.[123] + ${SSH} -S $CTL -M -f -F $OBJ/ssh_config -R${base}01:[$OBJ/unix-1.fwd] somehost sleep 10 + ${SSH} -S $CTL.1 -M -f -F $OBJ/ssh_config -L[$OBJ/unix-1.fwd]:[$OBJ/unix-2.fwd] somehost sleep 10 + ${SSH} -S $CTL.2 -M -f -F $OBJ/ssh_config -R[$OBJ/unix-2.fwd]:[$OBJ/unix-3.fwd] somehost sleep 10 + ${SSH} -S $CTL.3 -M -f -F $OBJ/ssh_config -L[$OBJ/unix-3.fwd]:127.0.0.1:$PORT somehost sleep 10 ${SSH} -F $OBJ/ssh_config -p${base}01 -o 'ConnectionAttempts=4' \ somehost cat ${DATA} > ${COPY} test -s ${COPY} || fail "failed copy ${DATA}" cmp ${DATA} ${COPY} || fail "corrupted copy of ${DATA}" - #wait - sleep 10 + ${SSH} -F $OBJ/ssh_config -S $CTL -O exit somehost + ${SSH} -F $OBJ/ssh_config -S $CTL.1 -O exit somehost + ${SSH} -F $OBJ/ssh_config -S $CTL.2 -O exit somehost + ${SSH} -F $OBJ/ssh_config -S $CTL.3 -O exit somehost done diff --git a/crypto/openssh/regress/integrity.sh b/crypto/openssh/regress/integrity.sh index 39d310deb09b..1df2924f5f09 100755 --- a/crypto/openssh/regress/integrity.sh +++ b/crypto/openssh/regress/integrity.sh @@ -1,75 +1,74 @@ -# $OpenBSD: integrity.sh,v 1.19 2016/11/25 02:56:49 dtucker Exp $ +# $OpenBSD: integrity.sh,v 1.20 2017/01/06 02:26:10 dtucker Exp $ # Placed in the Public Domain. tid="integrity" cp $OBJ/sshd_proxy $OBJ/sshd_proxy_bak # start at byte 2900 (i.e. after kex) and corrupt at different offsets -# XXX the test hangs if we modify the low bytes of the packet length -# XXX and ssh tries to read... tries=10 startoffset=2900 macs=`${SSH} -Q mac` # The following are not MACs, but ciphers with integrated integrity. They are # handled specially below. macs="$macs `${SSH} -Q cipher-auth`" # avoid DH group exchange as the extra traffic makes it harder to get the # offset into the stream right. echo "KexAlgorithms diffie-hellman-group14-sha1,diffie-hellman-group1-sha1" \ >> $OBJ/ssh_proxy # sshd-command for proxy (see test-exec.sh) cmd="$SUDO sh ${SRC}/sshd-log-wrapper.sh ${TEST_SSHD_LOGFILE} ${SSHD} -i -f $OBJ/sshd_proxy" for m in $macs; do trace "test $tid: mac $m" elen=0 epad=0 emac=0 + etmo=0 ecnt=0 skip=0 for off in `jot $tries $startoffset`; do skip=`expr $skip - 1` if [ $skip -gt 0 ]; then # avoid modifying the high bytes of the length continue fi cp $OBJ/sshd_proxy_bak $OBJ/sshd_proxy # modify output from sshd at offset $off pxy="proxycommand=$cmd | $OBJ/modpipe -wm xor:$off:1" if ${SSH} -Q cipher-auth | grep "^${m}\$" >/dev/null 2>&1 ; then echo "Ciphers=$m" >> $OBJ/sshd_proxy macopt="-c $m" else echo "Ciphers=aes128-ctr" >> $OBJ/sshd_proxy echo "MACs=$m" >> $OBJ/sshd_proxy macopt="-m $m -c aes128-ctr" fi verbose "test $tid: $m @$off" ${SSH} $macopt -2F $OBJ/ssh_proxy -o "$pxy" \ -oServerAliveInterval=1 -oServerAliveCountMax=30 \ 999.999.999.999 'printf "%4096s" " "' >/dev/null if [ $? -eq 0 ]; then fail "ssh -m $m succeeds with bit-flip at $off" fi ecnt=`expr $ecnt + 1` out=$(egrep -v "^debug" $TEST_SSH_LOGFILE | tail -2 | \ tr -s '\r\n' '.') case "$out" in Bad?packet*) elen=`expr $elen + 1`; skip=3;; Corrupted?MAC* | *message?authentication?code?incorrect*) emac=`expr $emac + 1`; skip=0;; padding*) epad=`expr $epad + 1`; skip=0;; *) fail "unexpected error mac $m at $off: $out";; esac done verbose "test $tid: $ecnt errors: mac $emac padding $epad length $elen" if [ $emac -eq 0 ]; then fail "$m: no mac errors" fi expect=`expr $ecnt - $epad - $elen` if [ $emac -ne $expect ]; then fail "$m: expected $expect mac errors, got $emac" fi done diff --git a/crypto/openssh/regress/test-exec.sh b/crypto/openssh/regress/test-exec.sh index bfa48803b561..dc033cd96203 100644 --- a/crypto/openssh/regress/test-exec.sh +++ b/crypto/openssh/regress/test-exec.sh @@ -1,582 +1,580 @@ -# $OpenBSD: test-exec.sh,v 1.58 2016/12/16 01:06:27 dtucker Exp $ +# $OpenBSD: test-exec.sh,v 1.59 2017/02/07 23:03:11 dtucker Exp $ # Placed in the Public Domain. #SUDO=sudo # Unbreak GNU head(1) _POSIX2_VERSION=199209 export _POSIX2_VERSION case `uname -s 2>/dev/null` in OSF1*) BIN_SH=xpg4 export BIN_SH ;; CYGWIN_NT-5.0) os=cygwin TEST_SSH_IPV6=no ;; CYGWIN*) os=cygwin ;; esac if [ ! -z "$TEST_SSH_PORT" ]; then PORT="$TEST_SSH_PORT" else PORT=4242 fi if [ -x /usr/ucb/whoami ]; then USER=`/usr/ucb/whoami` elif whoami >/dev/null 2>&1; then USER=`whoami` elif logname >/dev/null 2>&1; then USER=`logname` else USER=`id -un` fi OBJ=$1 if [ "x$OBJ" = "x" ]; then echo '$OBJ not defined' exit 2 fi if [ ! -d $OBJ ]; then echo "not a directory: $OBJ" exit 2 fi SCRIPT=$2 if [ "x$SCRIPT" = "x" ]; then echo '$SCRIPT not defined' exit 2 fi if [ ! -f $SCRIPT ]; then echo "not a file: $SCRIPT" exit 2 fi if $TEST_SHELL -n $SCRIPT; then true else echo "syntax error in $SCRIPT" exit 2 fi unset SSH_AUTH_SOCK SRC=`dirname ${SCRIPT}` # defaults SSH=ssh SSHD=sshd SSHAGENT=ssh-agent SSHADD=ssh-add SSHKEYGEN=ssh-keygen SSHKEYSCAN=ssh-keyscan SFTP=sftp SFTPSERVER=/usr/libexec/openssh/sftp-server SCP=scp # Interop testing PLINK=plink PUTTYGEN=puttygen CONCH=conch if [ "x$TEST_SSH_SSH" != "x" ]; then SSH="${TEST_SSH_SSH}" fi if [ "x$TEST_SSH_SSHD" != "x" ]; then SSHD="${TEST_SSH_SSHD}" fi if [ "x$TEST_SSH_SSHAGENT" != "x" ]; then SSHAGENT="${TEST_SSH_SSHAGENT}" fi if [ "x$TEST_SSH_SSHADD" != "x" ]; then SSHADD="${TEST_SSH_SSHADD}" fi if [ "x$TEST_SSH_SSHKEYGEN" != "x" ]; then SSHKEYGEN="${TEST_SSH_SSHKEYGEN}" fi if [ "x$TEST_SSH_SSHKEYSCAN" != "x" ]; then SSHKEYSCAN="${TEST_SSH_SSHKEYSCAN}" fi if [ "x$TEST_SSH_SFTP" != "x" ]; then SFTP="${TEST_SSH_SFTP}" fi if [ "x$TEST_SSH_SFTPSERVER" != "x" ]; then SFTPSERVER="${TEST_SSH_SFTPSERVER}" fi if [ "x$TEST_SSH_SCP" != "x" ]; then SCP="${TEST_SSH_SCP}" fi if [ "x$TEST_SSH_PLINK" != "x" ]; then # Find real binary, if it exists case "${TEST_SSH_PLINK}" in /*) PLINK="${TEST_SSH_PLINK}" ;; *) PLINK=`which ${TEST_SSH_PLINK} 2>/dev/null` ;; esac fi if [ "x$TEST_SSH_PUTTYGEN" != "x" ]; then # Find real binary, if it exists case "${TEST_SSH_PUTTYGEN}" in /*) PUTTYGEN="${TEST_SSH_PUTTYGEN}" ;; *) PUTTYGEN=`which ${TEST_SSH_PUTTYGEN} 2>/dev/null` ;; esac fi if [ "x$TEST_SSH_CONCH" != "x" ]; then # Find real binary, if it exists case "${TEST_SSH_CONCH}" in /*) CONCH="${TEST_SSH_CONCH}" ;; *) CONCH=`which ${TEST_SSH_CONCH} 2>/dev/null` ;; esac fi SSH_PROTOCOLS=2 #SSH_PROTOCOLS=`$SSH -Q protocol-version` if [ "x$TEST_SSH_PROTOCOLS" != "x" ]; then SSH_PROTOCOLS="${TEST_SSH_PROTOCOLS}" fi # Path to sshd must be absolute for rexec case "$SSHD" in /*) ;; *) SSHD=`which $SSHD` ;; esac case "$SSHAGENT" in /*) ;; *) SSHAGENT=`which $SSHAGENT` ;; esac # Record the actual binaries used. SSH_BIN=${SSH} SSHD_BIN=${SSHD} SSHAGENT_BIN=${SSHAGENT} SSHADD_BIN=${SSHADD} SSHKEYGEN_BIN=${SSHKEYGEN} SSHKEYSCAN_BIN=${SSHKEYSCAN} SFTP_BIN=${SFTP} SFTPSERVER_BIN=${SFTPSERVER} SCP_BIN=${SCP} if [ "x$USE_VALGRIND" != "x" ]; then mkdir -p $OBJ/valgrind-out VG_TEST=`basename $SCRIPT .sh` # Some tests are difficult to fix. case "$VG_TEST" in connect-privsep|reexec) VG_SKIP=1 ;; esac if [ x"$VG_SKIP" = "x" ]; then VG_IGNORE="/bin/*,/sbin/*,/usr/*,/var/*" VG_LOG="$OBJ/valgrind-out/${VG_TEST}." VG_OPTS="--track-origins=yes --leak-check=full" VG_OPTS="$VG_OPTS --trace-children=yes" VG_OPTS="$VG_OPTS --trace-children-skip=${VG_IGNORE}" VG_PATH="valgrind" if [ "x$VALGRIND_PATH" != "x" ]; then VG_PATH="$VALGRIND_PATH" fi VG="$VG_PATH $VG_OPTS" SSH="$VG --log-file=${VG_LOG}ssh.%p $SSH" SSHD="$VG --log-file=${VG_LOG}sshd.%p $SSHD" SSHAGENT="$VG --log-file=${VG_LOG}ssh-agent.%p $SSHAGENT" SSHADD="$VG --log-file=${VG_LOG}ssh-add.%p $SSHADD" SSHKEYGEN="$VG --log-file=${VG_LOG}ssh-keygen.%p $SSHKEYGEN" SSHKEYSCAN="$VG --log-file=${VG_LOG}ssh-keyscan.%p $SSHKEYSCAN" SFTP="$VG --log-file=${VG_LOG}sftp.%p ${SFTP}" SCP="$VG --log-file=${VG_LOG}scp.%p $SCP" cat > $OBJ/valgrind-sftp-server.sh << EOF #!/bin/sh exec $VG --log-file=${VG_LOG}sftp-server.%p $SFTPSERVER "\$@" EOF chmod a+rx $OBJ/valgrind-sftp-server.sh SFTPSERVER="$OBJ/valgrind-sftp-server.sh" fi fi # Logfiles. # SSH_LOGFILE should be the debug output of ssh(1) only # SSHD_LOGFILE should be the debug output of sshd(8) only # REGRESS_LOGFILE is the output of the test itself stdout and stderr if [ "x$TEST_SSH_LOGFILE" = "x" ]; then TEST_SSH_LOGFILE=$OBJ/ssh.log fi if [ "x$TEST_SSHD_LOGFILE" = "x" ]; then TEST_SSHD_LOGFILE=$OBJ/sshd.log fi if [ "x$TEST_REGRESS_LOGFILE" = "x" ]; then TEST_REGRESS_LOGFILE=$OBJ/regress.log fi # truncate logfiles >$TEST_SSH_LOGFILE >$TEST_SSHD_LOGFILE >$TEST_REGRESS_LOGFILE # Create wrapper ssh with logging. We can't just specify "SSH=ssh -E..." # because sftp and scp don't handle spaces in arguments. SSHLOGWRAP=$OBJ/ssh-log-wrapper.sh echo "#!/bin/sh" > $SSHLOGWRAP echo "exec ${SSH} -E${TEST_SSH_LOGFILE} "'"$@"' >>$SSHLOGWRAP chmod a+rx $OBJ/ssh-log-wrapper.sh REAL_SSH="$SSH" SSH="$SSHLOGWRAP" # Some test data. We make a copy because some tests will overwrite it. # The tests may assume that $DATA exists and is writable and $COPY does # not exist. Tests requiring larger data files can call increase_datafile_size # [kbytes] to ensure the file is at least that large. DATANAME=data DATA=$OBJ/${DATANAME} cat ${SSHAGENT_BIN} >${DATA} chmod u+w ${DATA} COPY=$OBJ/copy rm -f ${COPY} increase_datafile_size() { while [ `du -k ${DATA} | cut -f1` -lt $1 ]; do cat ${SSHAGENT_BIN} >>${DATA} done } # these should be used in tests export SSH SSHD SSHAGENT SSHADD SSHKEYGEN SSHKEYSCAN SFTP SFTPSERVER SCP #echo $SSH $SSHD $SSHAGENT $SSHADD $SSHKEYGEN $SSHKEYSCAN $SFTP $SFTPSERVER $SCP # Portable specific functions have_prog() { saved_IFS="$IFS" IFS=":" for i in $PATH do if [ -x $i/$1 ]; then IFS="$saved_IFS" return 0 fi done IFS="$saved_IFS" return 1 } jot() { awk "BEGIN { for (i = $2; i < $2 + $1; i++) { printf \"%d\n\", i } exit }" } # Check whether preprocessor symbols are defined in config.h. config_defined () { str=$1 while test "x$2" != "x" ; do str="$str|$2" shift done egrep "^#define.*($str)" ${BUILDDIR}/config.h >/dev/null 2>&1 } md5 () { if have_prog md5sum; then md5sum elif have_prog openssl; then openssl md5 elif have_prog cksum; then cksum elif have_prog sum; then sum else wc -c fi } # End of portable specific functions stop_sshd () { if [ -f $PIDFILE ]; then pid=`$SUDO cat $PIDFILE` if [ "X$pid" = "X" ]; then echo no sshd running else if [ $pid -lt 2 ]; then echo bad pid for sshd: $pid else $SUDO kill $pid trace "wait for sshd to exit" i=0; while [ -f $PIDFILE -a $i -lt 5 ]; do i=`expr $i + 1` sleep $i done test -f $PIDFILE && \ fatal "sshd didn't exit port $PORT pid $pid" fi fi fi } # helper cleanup () { if [ "x$SSH_PID" != "x" ]; then if [ $SSH_PID -lt 2 ]; then echo bad pid for ssh: $SSH_PID else kill $SSH_PID fi fi stop_sshd } start_debug_log () { echo "trace: $@" >$TEST_REGRESS_LOGFILE echo "trace: $@" >$TEST_SSH_LOGFILE echo "trace: $@" >$TEST_SSHD_LOGFILE } save_debug_log () { echo $@ >>$TEST_REGRESS_LOGFILE echo $@ >>$TEST_SSH_LOGFILE echo $@ >>$TEST_SSHD_LOGFILE (cat $TEST_REGRESS_LOGFILE; echo) >>$OBJ/failed-regress.log (cat $TEST_SSH_LOGFILE; echo) >>$OBJ/failed-ssh.log (cat $TEST_SSHD_LOGFILE; echo) >>$OBJ/failed-sshd.log } trace () { start_debug_log $@ if [ "X$TEST_SSH_TRACE" = "Xyes" ]; then echo "$@" fi } verbose () { start_debug_log $@ if [ "X$TEST_SSH_QUIET" != "Xyes" ]; then echo "$@" fi } warn () { echo "WARNING: $@" >>$TEST_SSH_LOGFILE echo "WARNING: $@" } fail () { save_debug_log "FAIL: $@" RESULT=1 echo "$@" } fatal () { save_debug_log "FATAL: $@" printf "FATAL: " fail "$@" cleanup exit $RESULT } ssh_version () { echo ${SSH_PROTOCOLS} | grep "$1" >/dev/null } RESULT=0 PIDFILE=$OBJ/pidfile trap fatal 3 2 if ssh_version 1; then PROTO="2,1" else PROTO="2" fi # create server config cat << EOF > $OBJ/sshd_config StrictModes no Port $PORT AddressFamily inet ListenAddress 127.0.0.1 #ListenAddress ::1 PidFile $PIDFILE AuthorizedKeysFile $OBJ/authorized_keys_%u LogLevel DEBUG3 AcceptEnv _XXX_TEST_* AcceptEnv _XXX_TEST Subsystem sftp $SFTPSERVER EOF # This may be necessary if /usr/src and/or /usr/obj are group-writable, # but if you aren't careful with permissions then the unit tests could # be abused to locally escalate privileges. if [ ! -z "$TEST_SSH_UNSAFE_PERMISSIONS" ]; then echo "StrictModes no" >> $OBJ/sshd_config fi if [ ! -z "$TEST_SSH_SSHD_CONFOPTS" ]; then trace "adding sshd_config option $TEST_SSH_SSHD_CONFOPTS" echo "$TEST_SSH_SSHD_CONFOPTS" >> $OBJ/sshd_config fi # server config for proxy connects cp $OBJ/sshd_config $OBJ/sshd_proxy # allow group-writable directories in proxy-mode echo 'StrictModes no' >> $OBJ/sshd_proxy # create client config cat << EOF > $OBJ/ssh_config Host * Hostname 127.0.0.1 HostKeyAlias localhost-with-alias Port $PORT User $USER GlobalKnownHostsFile $OBJ/known_hosts UserKnownHostsFile $OBJ/known_hosts - RSAAuthentication yes PubkeyAuthentication yes ChallengeResponseAuthentication no HostbasedAuthentication no PasswordAuthentication no - RhostsRSAAuthentication no BatchMode yes StrictHostKeyChecking yes LogLevel DEBUG3 EOF if [ ! -z "$TEST_SSH_SSH_CONFOPTS" ]; then trace "adding ssh_config option $TEST_SSH_SSH_CONFOPTS" echo "$TEST_SSH_SSH_CONFOPTS" >> $OBJ/ssh_config fi rm -f $OBJ/known_hosts $OBJ/authorized_keys_$USER if ssh_version 1; then SSH_KEYTYPES="rsa rsa1" else SSH_KEYTYPES="rsa ed25519" fi trace "generate keys" for t in ${SSH_KEYTYPES}; do # generate user key if [ ! -f $OBJ/$t ] || [ ${SSHKEYGEN_BIN} -nt $OBJ/$t ]; then rm -f $OBJ/$t ${SSHKEYGEN} -q -N '' -t $t -f $OBJ/$t ||\ fail "ssh-keygen for $t failed" fi # known hosts file for client ( printf 'localhost-with-alias,127.0.0.1,::1 ' cat $OBJ/$t.pub ) >> $OBJ/known_hosts # setup authorized keys cat $OBJ/$t.pub >> $OBJ/authorized_keys_$USER echo IdentityFile $OBJ/$t >> $OBJ/ssh_config # use key as host key, too $SUDO cp $OBJ/$t $OBJ/host.$t echo HostKey $OBJ/host.$t >> $OBJ/sshd_config # don't use SUDO for proxy connect echo HostKey $OBJ/$t >> $OBJ/sshd_proxy done chmod 644 $OBJ/authorized_keys_$USER # Activate Twisted Conch tests if the binary is present REGRESS_INTEROP_CONCH=no if test -x "$CONCH" ; then REGRESS_INTEROP_CONCH=yes fi # If PuTTY is present and we are running a PuTTY test, prepare keys and # configuration REGRESS_INTEROP_PUTTY=no if test -x "$PUTTYGEN" -a -x "$PLINK" ; then REGRESS_INTEROP_PUTTY=yes fi case "$SCRIPT" in *putty*) ;; *) REGRESS_INTEROP_PUTTY=no ;; esac if test "$REGRESS_INTEROP_PUTTY" = "yes" ; then mkdir -p ${OBJ}/.putty # Add a PuTTY key to authorized_keys rm -f ${OBJ}/putty.rsa2 if ! puttygen -t rsa -o ${OBJ}/putty.rsa2 \ --new-passphrase /dev/null < /dev/null > /dev/null; then echo "Your installed version of PuTTY is too old to support --new-passphrase; trying without (may require manual interaction) ..." >&2 puttygen -t rsa -o ${OBJ}/putty.rsa2 < /dev/null > /dev/null fi puttygen -O public-openssh ${OBJ}/putty.rsa2 \ >> $OBJ/authorized_keys_$USER # Convert rsa2 host key to PuTTY format ${SRC}/ssh2putty.sh 127.0.0.1 $PORT $OBJ/rsa > \ ${OBJ}/.putty/sshhostkeys ${SRC}/ssh2putty.sh 127.0.0.1 22 $OBJ/rsa >> \ ${OBJ}/.putty/sshhostkeys # Setup proxied session mkdir -p ${OBJ}/.putty/sessions rm -f ${OBJ}/.putty/sessions/localhost_proxy echo "Protocol=ssh" >> ${OBJ}/.putty/sessions/localhost_proxy echo "HostName=127.0.0.1" >> ${OBJ}/.putty/sessions/localhost_proxy echo "PortNumber=$PORT" >> ${OBJ}/.putty/sessions/localhost_proxy echo "ProxyMethod=5" >> ${OBJ}/.putty/sessions/localhost_proxy echo "ProxyTelnetCommand=sh ${SRC}/sshd-log-wrapper.sh ${TEST_SSHD_LOGFILE} ${SSHD} -i -f $OBJ/sshd_proxy" >> ${OBJ}/.putty/sessions/localhost_proxy echo "ProxyLocalhost=1" >> ${OBJ}/.putty/sessions/localhost_proxy REGRESS_INTEROP_PUTTY=yes fi # create a proxy version of the client config ( cat $OBJ/ssh_config echo proxycommand ${SUDO} sh ${SRC}/sshd-log-wrapper.sh ${TEST_SSHD_LOGFILE} ${SSHD} -i -f $OBJ/sshd_proxy ) > $OBJ/ssh_proxy # check proxy config ${SSHD} -t -f $OBJ/sshd_proxy || fatal "sshd_proxy broken" start_sshd () { # start sshd $SUDO ${SSHD} -f $OBJ/sshd_config "$@" -t || fatal "sshd_config broken" $SUDO ${SSHD} -f $OBJ/sshd_config "$@" -E$TEST_SSHD_LOGFILE trace "wait for sshd" i=0; while [ ! -f $PIDFILE -a $i -lt 10 ]; do i=`expr $i + 1` sleep $i done test -f $PIDFILE || fatal "no sshd running on port $PORT" } # source test body . $SCRIPT # kill sshd cleanup if [ $RESULT -eq 0 ]; then verbose ok $tid else echo failed $tid fi exit $RESULT diff --git a/crypto/openssh/regress/unittests/Makefile b/crypto/openssh/regress/unittests/Makefile index e70b16644311..e975f6ca4160 100644 --- a/crypto/openssh/regress/unittests/Makefile +++ b/crypto/openssh/regress/unittests/Makefile @@ -1,5 +1,6 @@ -# $OpenBSD: Makefile,v 1.7 2016/08/19 06:44:13 djm Exp $ -REGRESS_FAIL_EARLY= yes -SUBDIR= test_helper sshbuf sshkey bitmap kex hostkeys utf8 match +# $OpenBSD: Makefile,v 1.9 2017/03/14 01:20:29 dtucker Exp $ + +REGRESS_FAIL_EARLY?= yes +SUBDIR= test_helper sshbuf sshkey bitmap kex hostkeys utf8 match conversion .include diff --git a/crypto/openssh/regress/unittests/conversion/Makefile b/crypto/openssh/regress/unittests/conversion/Makefile new file mode 100644 index 000000000000..cde97dc28a9a --- /dev/null +++ b/crypto/openssh/regress/unittests/conversion/Makefile @@ -0,0 +1,10 @@ +# $OpenBSD: Makefile,v 1.1 2017/03/14 01:20:29 dtucker Exp $ + +PROG=test_conversion +SRCS=tests.c +REGRESS_TARGETS=run-regress-${PROG} + +run-regress-${PROG}: ${PROG} + env ${TEST_ENV} ./${PROG} + +.include diff --git a/crypto/openssh/regress/unittests/conversion/tests.c b/crypto/openssh/regress/unittests/conversion/tests.c new file mode 100644 index 000000000000..6dd77ef42548 --- /dev/null +++ b/crypto/openssh/regress/unittests/conversion/tests.c @@ -0,0 +1,51 @@ +/* $OpenBSD: tests.c,v 1.1 2017/03/14 01:20:29 dtucker Exp $ */ +/* + * Regress test for conversions + * + * Placed in the public domain + */ + +#include "includes.h" + +#include +#include +#include +#ifdef HAVE_STDINT_H +#include +#endif +#include +#include + +#include "../test_helper/test_helper.h" + +#include "misc.h" + +void +tests(void) +{ + char buf[1024]; + + TEST_START("conversion_convtime"); + ASSERT_LONG_EQ(convtime("0"), 0); + ASSERT_LONG_EQ(convtime("1"), 1); + ASSERT_LONG_EQ(convtime("1S"), 1); + /* from the examples in the comment above the function */ + ASSERT_LONG_EQ(convtime("90m"), 5400); + ASSERT_LONG_EQ(convtime("1h30m"), 5400); + ASSERT_LONG_EQ(convtime("2d"), 172800); + ASSERT_LONG_EQ(convtime("1w"), 604800); + + /* negative time is not allowed */ + ASSERT_LONG_EQ(convtime("-7"), -1); + ASSERT_LONG_EQ(convtime("-9d"), -1); + + /* overflow */ + snprintf(buf, sizeof buf, "%llu", (unsigned long long)LONG_MAX + 1); + ASSERT_LONG_EQ(convtime(buf), -1); + + /* overflow with multiplier */ + snprintf(buf, sizeof buf, "%lluM", (unsigned long long)LONG_MAX/60 + 1); + ASSERT_LONG_EQ(convtime(buf), -1); + ASSERT_LONG_EQ(convtime("1000000000000000000000w"), -1); + TEST_DONE(); +} diff --git a/crypto/openssh/regress/unittests/match/tests.c b/crypto/openssh/regress/unittests/match/tests.c index 7ff319c162ad..e1593367bf3b 100644 --- a/crypto/openssh/regress/unittests/match/tests.c +++ b/crypto/openssh/regress/unittests/match/tests.c @@ -1,113 +1,132 @@ -/* $OpenBSD: tests.c,v 1.3 2016/09/21 17:03:54 djm Exp $ */ +/* $OpenBSD: tests.c,v 1.4 2017/02/03 23:01:42 djm Exp $ */ /* * Regress test for matching functions * * Placed in the public domain */ #include "includes.h" #include #include #include #ifdef HAVE_STDINT_H #include #endif #include #include #include "../test_helper/test_helper.h" #include "match.h" void tests(void) { TEST_START("match_pattern"); ASSERT_INT_EQ(match_pattern("", ""), 1); ASSERT_INT_EQ(match_pattern("", "aaa"), 0); ASSERT_INT_EQ(match_pattern("aaa", ""), 0); ASSERT_INT_EQ(match_pattern("aaa", "aaaa"), 0); ASSERT_INT_EQ(match_pattern("aaaa", "aaa"), 0); TEST_DONE(); TEST_START("match_pattern wildcard"); ASSERT_INT_EQ(match_pattern("", "*"), 1); ASSERT_INT_EQ(match_pattern("a", "?"), 1); ASSERT_INT_EQ(match_pattern("aa", "a?"), 1); ASSERT_INT_EQ(match_pattern("a", "*"), 1); ASSERT_INT_EQ(match_pattern("aa", "a*"), 1); ASSERT_INT_EQ(match_pattern("aa", "?*"), 1); ASSERT_INT_EQ(match_pattern("aa", "**"), 1); ASSERT_INT_EQ(match_pattern("aa", "?a"), 1); ASSERT_INT_EQ(match_pattern("aa", "*a"), 1); ASSERT_INT_EQ(match_pattern("ba", "a?"), 0); ASSERT_INT_EQ(match_pattern("ba", "a*"), 0); ASSERT_INT_EQ(match_pattern("ab", "?a"), 0); ASSERT_INT_EQ(match_pattern("ab", "*a"), 0); TEST_DONE(); TEST_START("match_pattern_list"); ASSERT_INT_EQ(match_pattern_list("", "", 0), 0); /* no patterns */ ASSERT_INT_EQ(match_pattern_list("", "*", 0), 1); ASSERT_INT_EQ(match_pattern_list("", "!*", 0), -1); ASSERT_INT_EQ(match_pattern_list("", "!a,*", 0), 1); ASSERT_INT_EQ(match_pattern_list("", "*,!a", 0), 1); ASSERT_INT_EQ(match_pattern_list("", "a,!*", 0), -1); ASSERT_INT_EQ(match_pattern_list("", "!*,a", 0), -1); ASSERT_INT_EQ(match_pattern_list("a", "", 0), 0); ASSERT_INT_EQ(match_pattern_list("a", "*", 0), 1); ASSERT_INT_EQ(match_pattern_list("a", "!*", 0), -1); ASSERT_INT_EQ(match_pattern_list("a", "!a", 0), -1); /* XXX negated ASSERT_INT_EQ(match_pattern_list("a", "!b", 0), 1); */ ASSERT_INT_EQ(match_pattern_list("a", "!a,*", 0), -1); ASSERT_INT_EQ(match_pattern_list("b", "!a,*", 0), 1); ASSERT_INT_EQ(match_pattern_list("a", "*,!a", 0), -1); ASSERT_INT_EQ(match_pattern_list("b", "*,!a", 0), 1); ASSERT_INT_EQ(match_pattern_list("a", "a,!*", 0), -1); ASSERT_INT_EQ(match_pattern_list("b", "a,!*", 0), -1); ASSERT_INT_EQ(match_pattern_list("a", "a,!a", 0), -1); /* XXX negated ASSERT_INT_EQ(match_pattern_list("b", "a,!a", 0), 1); */ ASSERT_INT_EQ(match_pattern_list("a", "!*,a", 0), -1); ASSERT_INT_EQ(match_pattern_list("b", "!*,a", 0), -1); TEST_DONE(); TEST_START("match_pattern_list lowercase"); ASSERT_INT_EQ(match_pattern_list("abc", "ABC", 0), 0); ASSERT_INT_EQ(match_pattern_list("ABC", "abc", 0), 0); ASSERT_INT_EQ(match_pattern_list("abc", "ABC", 1), 1); ASSERT_INT_EQ(match_pattern_list("ABC", "abc", 1), 0); TEST_DONE(); TEST_START("addr_match_list"); ASSERT_INT_EQ(addr_match_list("127.0.0.1", "127.0.0.1/44"), -2); ASSERT_INT_EQ(addr_match_list(NULL, "127.0.0.1/44"), -2); ASSERT_INT_EQ(addr_match_list("a", "*"), 0); ASSERT_INT_EQ(addr_match_list("127.0.0.1", "*"), 1); ASSERT_INT_EQ(addr_match_list(NULL, "*"), 0); ASSERT_INT_EQ(addr_match_list("127.0.0.1", "127.0.0.1"), 1); ASSERT_INT_EQ(addr_match_list("127.0.0.1", "127.0.0.2"), 0); ASSERT_INT_EQ(addr_match_list("127.0.0.1", "!127.0.0.1"), -1); /* XXX negated ASSERT_INT_EQ(addr_match_list("127.0.0.1", "!127.0.0.2"), 1); */ ASSERT_INT_EQ(addr_match_list("127.0.0.255", "127.0.0.0/24"), 1); ASSERT_INT_EQ(addr_match_list("127.0.1.1", "127.0.0.0/24"), 0); ASSERT_INT_EQ(addr_match_list("127.0.0.1", "127.0.0.0/24"), 1); ASSERT_INT_EQ(addr_match_list("127.0.0.1", "127.0.1.0/24"), 0); ASSERT_INT_EQ(addr_match_list("127.0.0.1", "!127.0.0.0/24"), -1); /* XXX negated ASSERT_INT_EQ(addr_match_list("127.0.0.1", "!127.0.1.0/24"), 1); */ ASSERT_INT_EQ(addr_match_list("127.0.0.1", "10.0.0.1,!127.0.0.1"), -1); ASSERT_INT_EQ(addr_match_list("127.0.0.1", "!127.0.0.1,10.0.0.1"), -1); ASSERT_INT_EQ(addr_match_list("127.0.0.1", "10.0.0.1,127.0.0.2"), 0); ASSERT_INT_EQ(addr_match_list("127.0.0.1", "127.0.0.2,10.0.0.1"), 0); /* XXX negated ASSERT_INT_EQ(addr_match_list("127.0.0.1", "10.0.0.1,!127.0.0.2"), 1); */ /* XXX negated ASSERT_INT_EQ(addr_match_list("127.0.0.1", "!127.0.0.2,10.0.0.1"), 1); */ TEST_DONE(); +#define CHECK_FILTER(string,filter,expected) \ + do { \ + char *result = match_filter_list((string), (filter)); \ + ASSERT_STRING_EQ(result, expected); \ + free(result); \ + } while (0) + + TEST_START("match_filter_list"); + CHECK_FILTER("a,b,c", "", "a,b,c"); + CHECK_FILTER("a,b,c", "a", "b,c"); + CHECK_FILTER("a,b,c", "b", "a,c"); + CHECK_FILTER("a,b,c", "c", "a,b"); + CHECK_FILTER("a,b,c", "a,b", "c"); + CHECK_FILTER("a,b,c", "a,c", "b"); + CHECK_FILTER("a,b,c", "b,c", "a"); + CHECK_FILTER("a,b,c", "a,b,c", ""); + CHECK_FILTER("a,b,c", "b,c", "a"); + CHECK_FILTER("", "a,b,c", ""); + TEST_DONE(); /* * XXX TODO * int match_host_and_ip(const char *, const char *, const char *); * int match_user(const char *, const char *, const char *, const char *); * char *match_list(const char *, const char *, u_int *); * int addr_match_cidr_list(const char *, const char *); */ } diff --git a/crypto/openssh/regress/unittests/test_helper/test_helper.c b/crypto/openssh/regress/unittests/test_helper/test_helper.c index 26ca26b5e3b7..f855137fb29f 100644 --- a/crypto/openssh/regress/unittests/test_helper/test_helper.c +++ b/crypto/openssh/regress/unittests/test_helper/test_helper.c @@ -1,526 +1,537 @@ -/* $OpenBSD: test_helper.c,v 1.6 2015/03/03 20:42:49 djm Exp $ */ +/* $OpenBSD: test_helper.c,v 1.7 2017/03/14 01:10:07 dtucker Exp $ */ /* * Copyright (c) 2011 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* Utility functions/framework for regress tests */ #include "includes.h" #include #include #include #include #include #ifdef HAVE_STDINT_H # include #endif #include #include #include #include #include #include #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) # include #endif #include "test_helper.h" #include "atomicio.h" #define TEST_CHECK_INT(r, pred) do { \ switch (pred) { \ case TEST_EQ: \ if (r == 0) \ return; \ break; \ case TEST_NE: \ if (r != 0) \ return; \ break; \ case TEST_LT: \ if (r < 0) \ return; \ break; \ case TEST_LE: \ if (r <= 0) \ return; \ break; \ case TEST_GT: \ if (r > 0) \ return; \ break; \ case TEST_GE: \ if (r >= 0) \ return; \ break; \ default: \ abort(); \ } \ } while (0) #define TEST_CHECK(x1, x2, pred) do { \ switch (pred) { \ case TEST_EQ: \ if (x1 == x2) \ return; \ break; \ case TEST_NE: \ if (x1 != x2) \ return; \ break; \ case TEST_LT: \ if (x1 < x2) \ return; \ break; \ case TEST_LE: \ if (x1 <= x2) \ return; \ break; \ case TEST_GT: \ if (x1 > x2) \ return; \ break; \ case TEST_GE: \ if (x1 >= x2) \ return; \ break; \ default: \ abort(); \ } \ } while (0) extern char *__progname; static int verbose_mode = 0; static int quiet_mode = 0; static char *active_test_name = NULL; static u_int test_number = 0; static test_onerror_func_t *test_onerror = NULL; static void *onerror_ctx = NULL; static const char *data_dir = NULL; static char subtest_info[512]; int main(int argc, char **argv) { int ch; /* Handle systems without __progname */ if (__progname == NULL) { __progname = strrchr(argv[0], '/'); if (__progname == NULL || __progname[1] == '\0') __progname = argv[0]; else __progname++; if ((__progname = strdup(__progname)) == NULL) { fprintf(stderr, "strdup failed\n"); exit(1); } } while ((ch = getopt(argc, argv, "vqd:")) != -1) { switch (ch) { case 'd': data_dir = optarg; break; case 'q': verbose_mode = 0; quiet_mode = 1; break; case 'v': verbose_mode = 1; quiet_mode = 0; break; default: fprintf(stderr, "Unrecognised command line option\n"); fprintf(stderr, "Usage: %s [-v]\n", __progname); exit(1); } } setvbuf(stdout, NULL, _IONBF, 0); if (!quiet_mode) printf("%s: ", __progname); if (verbose_mode) printf("\n"); tests(); if (!quiet_mode) printf(" %u tests ok\n", test_number); return 0; } const char * test_data_file(const char *name) { static char ret[PATH_MAX]; if (data_dir != NULL) snprintf(ret, sizeof(ret), "%s/%s", data_dir, name); else strlcpy(ret, name, sizeof(ret)); if (access(ret, F_OK) != 0) { fprintf(stderr, "Cannot access data file %s: %s\n", ret, strerror(errno)); exit(1); } return ret; } void test_info(char *s, size_t len) { snprintf(s, len, "In test %u: \"%s\"%s%s\n", test_number, active_test_name == NULL ? "" : active_test_name, *subtest_info != '\0' ? " - " : "", subtest_info); } #ifdef SIGINFO static void siginfo(int unused __attribute__((__unused__))) { char buf[256]; test_info(buf, sizeof(buf)); atomicio(vwrite, STDERR_FILENO, buf, strlen(buf)); } #endif void test_start(const char *n) { assert(active_test_name == NULL); assert((active_test_name = strdup(n)) != NULL); *subtest_info = '\0'; if (verbose_mode) printf("test %u - \"%s\": ", test_number, active_test_name); test_number++; #ifdef SIGINFO signal(SIGINFO, siginfo); #endif } void set_onerror_func(test_onerror_func_t *f, void *ctx) { test_onerror = f; onerror_ctx = ctx; } void test_done(void) { *subtest_info = '\0'; assert(active_test_name != NULL); free(active_test_name); active_test_name = NULL; if (verbose_mode) printf("OK\n"); else if (!quiet_mode) { printf("."); fflush(stdout); } } void test_subtest_info(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vsnprintf(subtest_info, sizeof(subtest_info), fmt, ap); va_end(ap); } void ssl_err_check(const char *file, int line) { long openssl_error = ERR_get_error(); if (openssl_error == 0) return; fprintf(stderr, "\n%s:%d: uncaught OpenSSL error: %s", file, line, ERR_error_string(openssl_error, NULL)); abort(); } static const char * pred_name(enum test_predicate p) { switch (p) { case TEST_EQ: return "EQ"; case TEST_NE: return "NE"; case TEST_LT: return "LT"; case TEST_LE: return "LE"; case TEST_GT: return "GT"; case TEST_GE: return "GE"; default: return "UNKNOWN"; } } static void test_die(void) { if (test_onerror != NULL) test_onerror(onerror_ctx); abort(); } static void test_header(const char *file, int line, const char *a1, const char *a2, const char *name, enum test_predicate pred) { fprintf(stderr, "\n%s:%d test #%u \"%s\"%s%s\n", file, line, test_number, active_test_name, *subtest_info != '\0' ? " - " : "", subtest_info); fprintf(stderr, "ASSERT_%s_%s(%s%s%s) failed:\n", name, pred_name(pred), a1, a2 != NULL ? ", " : "", a2 != NULL ? a2 : ""); } void assert_bignum(const char *file, int line, const char *a1, const char *a2, const BIGNUM *aa1, const BIGNUM *aa2, enum test_predicate pred) { int r = BN_cmp(aa1, aa2); TEST_CHECK_INT(r, pred); test_header(file, line, a1, a2, "BIGNUM", pred); fprintf(stderr, "%12s = 0x%s\n", a1, BN_bn2hex(aa1)); fprintf(stderr, "%12s = 0x%s\n", a2, BN_bn2hex(aa2)); test_die(); } void assert_string(const char *file, int line, const char *a1, const char *a2, const char *aa1, const char *aa2, enum test_predicate pred) { int r; /* Verify pointers are not NULL */ assert_ptr(file, line, a1, "NULL", aa1, NULL, TEST_NE); assert_ptr(file, line, a2, "NULL", aa2, NULL, TEST_NE); r = strcmp(aa1, aa2); TEST_CHECK_INT(r, pred); test_header(file, line, a1, a2, "STRING", pred); fprintf(stderr, "%12s = %s (len %zu)\n", a1, aa1, strlen(aa1)); fprintf(stderr, "%12s = %s (len %zu)\n", a2, aa2, strlen(aa2)); test_die(); } static char * tohex(const void *_s, size_t l) { u_int8_t *s = (u_int8_t *)_s; size_t i, j; const char *hex = "0123456789abcdef"; char *r = malloc((l * 2) + 1); assert(r != NULL); for (i = j = 0; i < l; i++) { r[j++] = hex[(s[i] >> 4) & 0xf]; r[j++] = hex[s[i] & 0xf]; } r[j] = '\0'; return r; } void assert_mem(const char *file, int line, const char *a1, const char *a2, const void *aa1, const void *aa2, size_t l, enum test_predicate pred) { int r; if (l == 0) return; /* If length is >0, then verify pointers are not NULL */ assert_ptr(file, line, a1, "NULL", aa1, NULL, TEST_NE); assert_ptr(file, line, a2, "NULL", aa2, NULL, TEST_NE); r = memcmp(aa1, aa2, l); TEST_CHECK_INT(r, pred); test_header(file, line, a1, a2, "STRING", pred); fprintf(stderr, "%12s = %s (len %zu)\n", a1, tohex(aa1, MIN(l, 256)), l); fprintf(stderr, "%12s = %s (len %zu)\n", a2, tohex(aa2, MIN(l, 256)), l); test_die(); } static int memvalcmp(const u_int8_t *s, u_char v, size_t l, size_t *where) { size_t i; for (i = 0; i < l; i++) { if (s[i] != v) { *where = i; return 1; } } return 0; } void assert_mem_filled(const char *file, int line, const char *a1, const void *aa1, u_char v, size_t l, enum test_predicate pred) { size_t where = -1; int r; char tmp[64]; if (l == 0) return; /* If length is >0, then verify the pointer is not NULL */ assert_ptr(file, line, a1, "NULL", aa1, NULL, TEST_NE); r = memvalcmp(aa1, v, l, &where); TEST_CHECK_INT(r, pred); test_header(file, line, a1, NULL, "MEM_ZERO", pred); fprintf(stderr, "%20s = %s%s (len %zu)\n", a1, tohex(aa1, MIN(l, 20)), l > 20 ? "..." : "", l); snprintf(tmp, sizeof(tmp), "(%s)[%zu]", a1, where); fprintf(stderr, "%20s = 0x%02x (expected 0x%02x)\n", tmp, ((u_char *)aa1)[where], v); test_die(); } void assert_int(const char *file, int line, const char *a1, const char *a2, int aa1, int aa2, enum test_predicate pred) { TEST_CHECK(aa1, aa2, pred); test_header(file, line, a1, a2, "INT", pred); fprintf(stderr, "%12s = %d\n", a1, aa1); fprintf(stderr, "%12s = %d\n", a2, aa2); test_die(); } void assert_size_t(const char *file, int line, const char *a1, const char *a2, size_t aa1, size_t aa2, enum test_predicate pred) { TEST_CHECK(aa1, aa2, pred); test_header(file, line, a1, a2, "SIZE_T", pred); fprintf(stderr, "%12s = %zu\n", a1, aa1); fprintf(stderr, "%12s = %zu\n", a2, aa2); test_die(); } void assert_u_int(const char *file, int line, const char *a1, const char *a2, u_int aa1, u_int aa2, enum test_predicate pred) { TEST_CHECK(aa1, aa2, pred); test_header(file, line, a1, a2, "U_INT", pred); fprintf(stderr, "%12s = %u / 0x%x\n", a1, aa1, aa1); fprintf(stderr, "%12s = %u / 0x%x\n", a2, aa2, aa2); test_die(); } +void +assert_long(const char *file, int line, const char *a1, const char *a2, + long aa1, long aa2, enum test_predicate pred) +{ + TEST_CHECK(aa1, aa2, pred); + test_header(file, line, a1, a2, "LONG", pred); + fprintf(stderr, "%12s = %ld / 0x%lx\n", a1, aa1, aa1); + fprintf(stderr, "%12s = %ld / 0x%lx\n", a2, aa2, aa2); + test_die(); +} + void assert_long_long(const char *file, int line, const char *a1, const char *a2, long long aa1, long long aa2, enum test_predicate pred) { TEST_CHECK(aa1, aa2, pred); test_header(file, line, a1, a2, "LONG LONG", pred); fprintf(stderr, "%12s = %lld / 0x%llx\n", a1, aa1, aa1); fprintf(stderr, "%12s = %lld / 0x%llx\n", a2, aa2, aa2); test_die(); } void assert_char(const char *file, int line, const char *a1, const char *a2, char aa1, char aa2, enum test_predicate pred) { char buf[8]; TEST_CHECK(aa1, aa2, pred); test_header(file, line, a1, a2, "CHAR", pred); fprintf(stderr, "%12s = '%s' / 0x02%x\n", a1, vis(buf, aa1, VIS_SAFE|VIS_NL|VIS_TAB|VIS_OCTAL, 0), aa1); fprintf(stderr, "%12s = '%s' / 0x02%x\n", a1, vis(buf, aa2, VIS_SAFE|VIS_NL|VIS_TAB|VIS_OCTAL, 0), aa2); test_die(); } void assert_u8(const char *file, int line, const char *a1, const char *a2, u_int8_t aa1, u_int8_t aa2, enum test_predicate pred) { TEST_CHECK(aa1, aa2, pred); test_header(file, line, a1, a2, "U8", pred); fprintf(stderr, "%12s = 0x%02x %u\n", a1, aa1, aa1); fprintf(stderr, "%12s = 0x%02x %u\n", a2, aa2, aa2); test_die(); } void assert_u16(const char *file, int line, const char *a1, const char *a2, u_int16_t aa1, u_int16_t aa2, enum test_predicate pred) { TEST_CHECK(aa1, aa2, pred); test_header(file, line, a1, a2, "U16", pred); fprintf(stderr, "%12s = 0x%04x %u\n", a1, aa1, aa1); fprintf(stderr, "%12s = 0x%04x %u\n", a2, aa2, aa2); test_die(); } void assert_u32(const char *file, int line, const char *a1, const char *a2, u_int32_t aa1, u_int32_t aa2, enum test_predicate pred) { TEST_CHECK(aa1, aa2, pred); test_header(file, line, a1, a2, "U32", pred); fprintf(stderr, "%12s = 0x%08x %u\n", a1, aa1, aa1); fprintf(stderr, "%12s = 0x%08x %u\n", a2, aa2, aa2); test_die(); } void assert_u64(const char *file, int line, const char *a1, const char *a2, u_int64_t aa1, u_int64_t aa2, enum test_predicate pred) { TEST_CHECK(aa1, aa2, pred); test_header(file, line, a1, a2, "U64", pred); fprintf(stderr, "%12s = 0x%016llx %llu\n", a1, (unsigned long long)aa1, (unsigned long long)aa1); fprintf(stderr, "%12s = 0x%016llx %llu\n", a2, (unsigned long long)aa2, (unsigned long long)aa2); test_die(); } void assert_ptr(const char *file, int line, const char *a1, const char *a2, const void *aa1, const void *aa2, enum test_predicate pred) { TEST_CHECK(aa1, aa2, pred); test_header(file, line, a1, a2, "PTR", pred); fprintf(stderr, "%12s = %p\n", a1, aa1); fprintf(stderr, "%12s = %p\n", a2, aa2); test_die(); } diff --git a/crypto/openssh/regress/unittests/test_helper/test_helper.h b/crypto/openssh/regress/unittests/test_helper/test_helper.h index 1d9c66986d5d..615b7832b4dc 100644 --- a/crypto/openssh/regress/unittests/test_helper/test_helper.h +++ b/crypto/openssh/regress/unittests/test_helper/test_helper.h @@ -1,303 +1,318 @@ -/* $OpenBSD: test_helper.h,v 1.6 2015/01/18 19:52:44 djm Exp $ */ +/* $OpenBSD: test_helper.h,v 1.7 2017/03/14 01:10:07 dtucker Exp $ */ /* * Copyright (c) 2011 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* Utility functions/framework for regress tests */ #ifndef _TEST_HELPER_H #define _TEST_HELPER_H #include "includes.h" #include #ifdef HAVE_STDINT_H # include #endif #include #include enum test_predicate { TEST_EQ, TEST_NE, TEST_LT, TEST_LE, TEST_GT, TEST_GE }; typedef void (test_onerror_func_t)(void *); /* Supplied by test suite */ void tests(void); const char *test_data_file(const char *name); void test_start(const char *n); void test_info(char *s, size_t len); void set_onerror_func(test_onerror_func_t *f, void *ctx); void test_done(void); void test_subtest_info(const char *fmt, ...) __attribute__((format(printf, 1, 2))); void ssl_err_check(const char *file, int line); void assert_bignum(const char *file, int line, const char *a1, const char *a2, const BIGNUM *aa1, const BIGNUM *aa2, enum test_predicate pred); void assert_string(const char *file, int line, const char *a1, const char *a2, const char *aa1, const char *aa2, enum test_predicate pred); void assert_mem(const char *file, int line, const char *a1, const char *a2, const void *aa1, const void *aa2, size_t l, enum test_predicate pred); void assert_mem_filled(const char *file, int line, const char *a1, const void *aa1, u_char v, size_t l, enum test_predicate pred); void assert_int(const char *file, int line, const char *a1, const char *a2, int aa1, int aa2, enum test_predicate pred); void assert_size_t(const char *file, int line, const char *a1, const char *a2, size_t aa1, size_t aa2, enum test_predicate pred); void assert_u_int(const char *file, int line, const char *a1, const char *a2, u_int aa1, u_int aa2, enum test_predicate pred); +void assert_long(const char *file, int line, + const char *a1, const char *a2, + long aa1, long aa2, enum test_predicate pred); void assert_long_long(const char *file, int line, const char *a1, const char *a2, long long aa1, long long aa2, enum test_predicate pred); void assert_char(const char *file, int line, const char *a1, const char *a2, char aa1, char aa2, enum test_predicate pred); void assert_ptr(const char *file, int line, const char *a1, const char *a2, const void *aa1, const void *aa2, enum test_predicate pred); void assert_u8(const char *file, int line, const char *a1, const char *a2, u_int8_t aa1, u_int8_t aa2, enum test_predicate pred); void assert_u16(const char *file, int line, const char *a1, const char *a2, u_int16_t aa1, u_int16_t aa2, enum test_predicate pred); void assert_u32(const char *file, int line, const char *a1, const char *a2, u_int32_t aa1, u_int32_t aa2, enum test_predicate pred); void assert_u64(const char *file, int line, const char *a1, const char *a2, u_int64_t aa1, u_int64_t aa2, enum test_predicate pred); #define TEST_START(n) test_start(n) #define TEST_DONE() test_done() #define TEST_ONERROR(f, c) set_onerror_func(f, c) #define SSL_ERR_CHECK() ssl_err_check(__FILE__, __LINE__) #define ASSERT_BIGNUM_EQ(a1, a2) \ assert_bignum(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) #define ASSERT_STRING_EQ(a1, a2) \ assert_string(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) #define ASSERT_MEM_EQ(a1, a2, l) \ assert_mem(__FILE__, __LINE__, #a1, #a2, a1, a2, l, TEST_EQ) #define ASSERT_MEM_FILLED_EQ(a1, c, l) \ assert_mem_filled(__FILE__, __LINE__, #a1, a1, c, l, TEST_EQ) #define ASSERT_MEM_ZERO_EQ(a1, l) \ assert_mem_filled(__FILE__, __LINE__, #a1, a1, '\0', l, TEST_EQ) #define ASSERT_INT_EQ(a1, a2) \ assert_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) #define ASSERT_SIZE_T_EQ(a1, a2) \ assert_size_t(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) #define ASSERT_U_INT_EQ(a1, a2) \ assert_u_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) +#define ASSERT_LONG_EQ(a1, a2) \ + assert_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) #define ASSERT_LONG_LONG_EQ(a1, a2) \ assert_long_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) #define ASSERT_CHAR_EQ(a1, a2) \ assert_char(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) #define ASSERT_PTR_EQ(a1, a2) \ assert_ptr(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) #define ASSERT_U8_EQ(a1, a2) \ assert_u8(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) #define ASSERT_U16_EQ(a1, a2) \ assert_u16(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) #define ASSERT_U32_EQ(a1, a2) \ assert_u32(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) #define ASSERT_U64_EQ(a1, a2) \ assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_EQ) #define ASSERT_BIGNUM_NE(a1, a2) \ assert_bignum(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) #define ASSERT_STRING_NE(a1, a2) \ assert_string(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) #define ASSERT_MEM_NE(a1, a2, l) \ assert_mem(__FILE__, __LINE__, #a1, #a2, a1, a2, l, TEST_NE) #define ASSERT_MEM_ZERO_NE(a1, l) \ assert_mem_filled(__FILE__, __LINE__, #a1, a1, '\0', l, TEST_NE) #define ASSERT_INT_NE(a1, a2) \ assert_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) #define ASSERT_SIZE_T_NE(a1, a2) \ assert_size_t(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) #define ASSERT_U_INT_NE(a1, a2) \ assert_u_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) +#define ASSERT_LONG_NE(a1, a2) \ + assert_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) #define ASSERT_LONG_LONG_NE(a1, a2) \ assert_long_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) #define ASSERT_CHAR_NE(a1, a2) \ assert_char(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) #define ASSERT_PTR_NE(a1, a2) \ assert_ptr(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) #define ASSERT_U8_NE(a1, a2) \ assert_u8(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) #define ASSERT_U16_NE(a1, a2) \ assert_u16(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) #define ASSERT_U32_NE(a1, a2) \ assert_u32(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) #define ASSERT_U64_NE(a1, a2) \ assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_NE) #define ASSERT_BIGNUM_LT(a1, a2) \ assert_bignum(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) #define ASSERT_STRING_LT(a1, a2) \ assert_string(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) #define ASSERT_MEM_LT(a1, a2, l) \ assert_mem(__FILE__, __LINE__, #a1, #a2, a1, a2, l, TEST_LT) #define ASSERT_INT_LT(a1, a2) \ assert_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) #define ASSERT_SIZE_T_LT(a1, a2) \ assert_size_t(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) #define ASSERT_U_INT_LT(a1, a2) \ assert_u_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) +#define ASSERT_LONG_LT(a1, a2) \ + assert_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) #define ASSERT_LONG_LONG_LT(a1, a2) \ assert_long_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) #define ASSERT_CHAR_LT(a1, a2) \ assert_char(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) #define ASSERT_PTR_LT(a1, a2) \ assert_ptr(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) #define ASSERT_U8_LT(a1, a2) \ assert_u8(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) #define ASSERT_U16_LT(a1, a2) \ assert_u16(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) #define ASSERT_U32_LT(a1, a2) \ assert_u32(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) #define ASSERT_U64_LT(a1, a2) \ assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LT) #define ASSERT_BIGNUM_LE(a1, a2) \ assert_bignum(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) #define ASSERT_STRING_LE(a1, a2) \ assert_string(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) #define ASSERT_MEM_LE(a1, a2, l) \ assert_mem(__FILE__, __LINE__, #a1, #a2, a1, a2, l, TEST_LE) #define ASSERT_INT_LE(a1, a2) \ assert_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) #define ASSERT_SIZE_T_LE(a1, a2) \ assert_size_t(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) #define ASSERT_U_INT_LE(a1, a2) \ assert_u_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) +#define ASSERT_LONG_LE(a1, a2) \ + assert_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) #define ASSERT_LONG_LONG_LE(a1, a2) \ assert_long_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) #define ASSERT_CHAR_LE(a1, a2) \ assert_char(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) #define ASSERT_PTR_LE(a1, a2) \ assert_ptr(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) #define ASSERT_U8_LE(a1, a2) \ assert_u8(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) #define ASSERT_U16_LE(a1, a2) \ assert_u16(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) #define ASSERT_U32_LE(a1, a2) \ assert_u32(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) #define ASSERT_U64_LE(a1, a2) \ assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_LE) #define ASSERT_BIGNUM_GT(a1, a2) \ assert_bignum(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) #define ASSERT_STRING_GT(a1, a2) \ assert_string(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) #define ASSERT_MEM_GT(a1, a2, l) \ assert_mem(__FILE__, __LINE__, #a1, #a2, a1, a2, l, TEST_GT) #define ASSERT_INT_GT(a1, a2) \ assert_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) #define ASSERT_SIZE_T_GT(a1, a2) \ assert_size_t(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) #define ASSERT_U_INT_GT(a1, a2) \ assert_u_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) +#define ASSERT_LONG_GT(a1, a2) \ + assert_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) #define ASSERT_LONG_LONG_GT(a1, a2) \ assert_long_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) #define ASSERT_CHAR_GT(a1, a2) \ assert_char(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) #define ASSERT_PTR_GT(a1, a2) \ assert_ptr(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) #define ASSERT_U8_GT(a1, a2) \ assert_u8(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) #define ASSERT_U16_GT(a1, a2) \ assert_u16(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) #define ASSERT_U32_GT(a1, a2) \ assert_u32(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) #define ASSERT_U64_GT(a1, a2) \ assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GT) #define ASSERT_BIGNUM_GE(a1, a2) \ assert_bignum(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) #define ASSERT_STRING_GE(a1, a2) \ assert_string(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) #define ASSERT_MEM_GE(a1, a2, l) \ assert_mem(__FILE__, __LINE__, #a1, #a2, a1, a2, l, TEST_GE) #define ASSERT_INT_GE(a1, a2) \ assert_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) #define ASSERT_SIZE_T_GE(a1, a2) \ assert_size_t(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) #define ASSERT_U_INT_GE(a1, a2) \ assert_u_int(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) +#define ASSERT_LONG_GE(a1, a2) \ + assert_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) #define ASSERT_LONG_LONG_GE(a1, a2) \ assert_long_long(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) #define ASSERT_CHAR_GE(a1, a2) \ assert_char(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) #define ASSERT_PTR_GE(a1, a2) \ assert_ptr(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) #define ASSERT_U8_GE(a1, a2) \ assert_u8(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) #define ASSERT_U16_GE(a1, a2) \ assert_u16(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) #define ASSERT_U32_GE(a1, a2) \ assert_u32(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) #define ASSERT_U64_GE(a1, a2) \ assert_u64(__FILE__, __LINE__, #a1, #a2, a1, a2, TEST_GE) /* Fuzzing support */ struct fuzz; #define FUZZ_1_BIT_FLIP 0x00000001 /* Flip one bit at a time */ #define FUZZ_2_BIT_FLIP 0x00000002 /* Flip two bits at a time */ #define FUZZ_1_BYTE_FLIP 0x00000004 /* Flip one byte at a time */ #define FUZZ_2_BYTE_FLIP 0x00000008 /* Flip two bytes at a time */ #define FUZZ_TRUNCATE_START 0x00000010 /* Truncate from beginning */ #define FUZZ_TRUNCATE_END 0x00000020 /* Truncate from end */ #define FUZZ_BASE64 0x00000040 /* Try all base64 chars */ #define FUZZ_MAX FUZZ_BASE64 /* Start fuzzing a blob of data with selected strategies (bitmask) */ struct fuzz *fuzz_begin(u_int strategies, const void *p, size_t l); /* Free a fuzz context */ void fuzz_cleanup(struct fuzz *fuzz); /* Prepare the next fuzz case in the series */ void fuzz_next(struct fuzz *fuzz); /* * Check whether this fuzz case is identical to the original * This is slow, but useful if the caller needs to ensure that all tests * generated change the input (e.g. when fuzzing signatures). */ int fuzz_matches_original(struct fuzz *fuzz); /* Determine whether the current fuzz sequence is exhausted (nonzero = yes) */ int fuzz_done(struct fuzz *fuzz); /* Return the length and a pointer to the current fuzzed case */ size_t fuzz_len(struct fuzz *fuzz); u_char *fuzz_ptr(struct fuzz *fuzz); /* Dump the current fuzz case to stderr */ void fuzz_dump(struct fuzz *fuzz); #endif /* _TEST_HELPER_H */ diff --git a/crypto/openssh/regress/unittests/utf8/tests.c b/crypto/openssh/regress/unittests/utf8/tests.c index 31f9fe9c3b0c..f0bbca5096f0 100644 --- a/crypto/openssh/regress/unittests/utf8/tests.c +++ b/crypto/openssh/regress/unittests/utf8/tests.c @@ -1,83 +1,102 @@ -/* $OpenBSD: tests.c,v 1.3 2016/12/19 04:55:18 djm Exp $ */ +/* $OpenBSD: tests.c,v 1.4 2017/02/19 00:11:29 djm Exp $ */ /* * Regress test for the utf8.h *mprintf() API * * Written by Ingo Schwarze in 2016 * and placed in the public domain. */ #include "includes.h" #include #include #include "../test_helper/test_helper.h" #include "utf8.h" -void badarg(void); -void one(const char *, const char *, int, int, int, const char *); - -void +static void badarg(void) { char buf[16]; int len, width; width = 1; TEST_START("utf8_badarg"); len = snmprintf(buf, sizeof(buf), &width, "\377"); ASSERT_INT_EQ(len, -1); ASSERT_STRING_EQ(buf, ""); ASSERT_INT_EQ(width, 0); TEST_DONE(); } -void -one(const char *name, const char *mbs, int width, +static void +one(int utf8, const char *name, const char *mbs, int width, int wantwidth, int wantlen, const char *wants) { char buf[16]; int *wp; int len; if (wantlen == -2) wantlen = strlen(wants); - (void)strlcpy(buf, "utf8_", sizeof(buf)); + (void)strlcpy(buf, utf8 ? "utf8_" : "c_", sizeof(buf)); (void)strlcat(buf, name, sizeof(buf)); TEST_START(buf); wp = wantwidth == -2 ? NULL : &width; len = snmprintf(buf, sizeof(buf), wp, "%s", mbs); ASSERT_INT_EQ(len, wantlen); ASSERT_STRING_EQ(buf, wants); ASSERT_INT_EQ(width, wantwidth); TEST_DONE(); } void tests(void) { char *loc; TEST_START("utf8_setlocale"); loc = setlocale(LC_CTYPE, "en_US.UTF-8"); ASSERT_PTR_NE(loc, NULL); TEST_DONE(); badarg(); - one("empty", "", 2, 0, 0, ""); - one("ascii", "x", -2, -2, -2, "x"); - one("newline", "a\nb", -2, -2, -2, "a\nb"); - one("cr", "a\rb", -2, -2, -2, "a\rb"); - one("tab", "a\tb", -2, -2, -2, "a\tb"); - one("esc", "\033x", -2, -2, -2, "\\033x"); - one("inv_badbyte", "\377x", -2, -2, -2, "\\377x"); - one("inv_nocont", "\341x", -2, -2, -2, "\\341x"); - one("inv_nolead", "a\200b", -2, -2, -2, "a\\200b"); - one("sz_ascii", "1234567890123456", -2, -2, 16, "123456789012345"); - one("sz_esc", "123456789012\033", -2, -2, 16, "123456789012"); - one("width_ascii", "123", 2, 2, -1, "12"); - one("width_double", "a\343\201\201", 2, 1, -1, "a"); - one("double_fit", "a\343\201\201", 3, 3, 4, "a\343\201\201"); - one("double_spc", "a\343\201\201", 4, 3, 4, "a\343\201\201"); + one(1, "empty", "", 2, 0, 0, ""); + one(1, "ascii", "x", -2, -2, -2, "x"); + one(1, "newline", "a\nb", -2, -2, -2, "a\nb"); + one(1, "cr", "a\rb", -2, -2, -2, "a\rb"); + one(1, "tab", "a\tb", -2, -2, -2, "a\tb"); + one(1, "esc", "\033x", -2, -2, -2, "\\033x"); + one(1, "inv_badbyte", "\377x", -2, -2, -2, "\\377x"); + one(1, "inv_nocont", "\341x", -2, -2, -2, "\\341x"); + one(1, "inv_nolead", "a\200b", -2, -2, -2, "a\\200b"); + one(1, "sz_ascii", "1234567890123456", -2, -2, 16, "123456789012345"); + one(1, "sz_esc", "123456789012\033", -2, -2, 16, "123456789012"); + one(1, "width_ascii", "123", 2, 2, -1, "12"); + one(1, "width_double", "a\343\201\201", 2, 1, -1, "a"); + one(1, "double_fit", "a\343\201\201", 3, 3, 4, "a\343\201\201"); + one(1, "double_spc", "a\343\201\201", 4, 3, 4, "a\343\201\201"); + + TEST_START("C_setlocale"); + loc = setlocale(LC_CTYPE, "C"); + ASSERT_PTR_NE(loc, NULL); + TEST_DONE(); + + badarg(); + one(0, "empty", "", 2, 0, 0, ""); + one(0, "ascii", "x", -2, -2, -2, "x"); + one(0, "newline", "a\nb", -2, -2, -2, "a\nb"); + one(0, "cr", "a\rb", -2, -2, -2, "a\rb"); + one(0, "tab", "a\tb", -2, -2, -2, "a\tb"); + one(0, "esc", "\033x", -2, -2, -2, "\\033x"); + one(0, "inv_badbyte", "\377x", -2, -2, -2, "\\377x"); + one(0, "inv_nocont", "\341x", -2, -2, -2, "\\341x"); + one(0, "inv_nolead", "a\200b", -2, -2, -2, "a\\200b"); + one(0, "sz_ascii", "1234567890123456", -2, -2, 16, "123456789012345"); + one(0, "sz_esc", "123456789012\033", -2, -2, 16, "123456789012"); + one(0, "width_ascii", "123", 2, 2, -1, "12"); + one(0, "width_double", "a\343\201\201", 2, 1, -1, "a"); + one(0, "double_fit", "a\343\201\201", 7, 5, -1, "a\\343"); + one(0, "double_spc", "a\343\201\201", 13, 13, 13, "a\\343\\201\\201"); } diff --git a/crypto/openssh/sandbox-seccomp-filter.c b/crypto/openssh/sandbox-seccomp-filter.c index 2e1ed2c52727..3a1aedce72c2 100644 --- a/crypto/openssh/sandbox-seccomp-filter.c +++ b/crypto/openssh/sandbox-seccomp-filter.c @@ -1,327 +1,357 @@ /* * Copyright (c) 2012 Will Drewry * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Uncomment the SANDBOX_SECCOMP_FILTER_DEBUG macro below to help diagnose * filter breakage during development. *Do not* use this in production, * as it relies on making library calls that are unsafe in signal context. * * Instead, live systems the auditctl(8) may be used to monitor failures. * E.g. * auditctl -a task,always -F uid= */ /* #define SANDBOX_SECCOMP_FILTER_DEBUG 1 */ /* XXX it should be possible to do logging via the log socket safely */ #ifdef SANDBOX_SECCOMP_FILTER_DEBUG /* Use the kernel headers in case of an older toolchain. */ # include # define __have_siginfo_t 1 # define __have_sigval_t 1 # define __have_sigevent_t 1 #endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ #include "includes.h" #ifdef SANDBOX_SECCOMP_FILTER #include #include #include #include #include #include #include #include #include #include #include #include #include /* for offsetof */ #include #include #include #include #include "log.h" #include "ssh-sandbox.h" #include "xmalloc.h" /* Linux seccomp_filter sandbox */ #define SECCOMP_FILTER_FAIL SECCOMP_RET_KILL /* Use a signal handler to emit violations when debugging */ #ifdef SANDBOX_SECCOMP_FILTER_DEBUG # undef SECCOMP_FILTER_FAIL # define SECCOMP_FILTER_FAIL SECCOMP_RET_TRAP #endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ +#if __BYTE_ORDER == __LITTLE_ENDIAN +# define ARG_LO_OFFSET 0 +# define ARG_HI_OFFSET sizeof(uint32_t) +#elif __BYTE_ORDER == __BIG_ENDIAN +# define ARG_LO_OFFSET sizeof(uint32_t) +# define ARG_HI_OFFSET 0 +#else +#error "Unknown endianness" +#endif + /* Simple helpers to avoid manual errors (but larger BPF programs). */ #define SC_DENY(_nr, _errno) \ - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_ ## _nr, 0, 1), \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (_nr), 0, 1), \ BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ERRNO|(_errno)) #define SC_ALLOW(_nr) \ - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_ ## _nr, 0, 1), \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (_nr), 0, 1), \ BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW) #define SC_ALLOW_ARG(_nr, _arg_nr, _arg_val) \ - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_ ## _nr, 0, 4), \ - /* load first syscall argument */ \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (_nr), 0, 6), \ + /* load and test first syscall argument, low word */ \ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \ - offsetof(struct seccomp_data, args[(_arg_nr)])), \ - BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, (_arg_val), 0, 1), \ + offsetof(struct seccomp_data, args[(_arg_nr)]) + ARG_LO_OFFSET), \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, \ + ((_arg_val) & 0xFFFFFFFF), 0, 3), \ + /* load and test first syscall argument, high word */ \ + BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \ + offsetof(struct seccomp_data, args[(_arg_nr)]) + ARG_HI_OFFSET), \ + BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, \ + (((uint32_t)((uint64_t)(_arg_val) >> 32)) & 0xFFFFFFFF), 0, 1), \ BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW), \ /* reload syscall number; all rules expect it in accumulator */ \ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \ offsetof(struct seccomp_data, nr)) /* Syscall filtering set for preauth. */ static const struct sock_filter preauth_insns[] = { /* Ensure the syscall arch convention is as expected. */ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct seccomp_data, arch)), BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, SECCOMP_AUDIT_ARCH, 1, 0), BPF_STMT(BPF_RET+BPF_K, SECCOMP_FILTER_FAIL), /* Load the syscall number for checking. */ BPF_STMT(BPF_LD+BPF_W+BPF_ABS, offsetof(struct seccomp_data, nr)), /* Syscalls to non-fatally deny */ #ifdef __NR_lstat - SC_DENY(lstat, EACCES), + SC_DENY(__NR_lstat, EACCES), #endif #ifdef __NR_lstat64 - SC_DENY(lstat64, EACCES), + SC_DENY(__NR_lstat64, EACCES), #endif #ifdef __NR_fstat - SC_DENY(fstat, EACCES), + SC_DENY(__NR_fstat, EACCES), #endif #ifdef __NR_fstat64 - SC_DENY(fstat64, EACCES), + SC_DENY(__NR_fstat64, EACCES), #endif #ifdef __NR_open - SC_DENY(open, EACCES), + SC_DENY(__NR_open, EACCES), #endif #ifdef __NR_openat - SC_DENY(openat, EACCES), + SC_DENY(__NR_openat, EACCES), #endif #ifdef __NR_newfstatat - SC_DENY(newfstatat, EACCES), + SC_DENY(__NR_newfstatat, EACCES), #endif #ifdef __NR_stat - SC_DENY(stat, EACCES), + SC_DENY(__NR_stat, EACCES), #endif #ifdef __NR_stat64 - SC_DENY(stat64, EACCES), + SC_DENY(__NR_stat64, EACCES), #endif /* Syscalls to permit */ #ifdef __NR_brk - SC_ALLOW(brk), + SC_ALLOW(__NR_brk), #endif #ifdef __NR_clock_gettime - SC_ALLOW(clock_gettime), + SC_ALLOW(__NR_clock_gettime), #endif #ifdef __NR_close - SC_ALLOW(close), + SC_ALLOW(__NR_close), #endif #ifdef __NR_exit - SC_ALLOW(exit), + SC_ALLOW(__NR_exit), #endif #ifdef __NR_exit_group - SC_ALLOW(exit_group), + SC_ALLOW(__NR_exit_group), #endif #ifdef __NR_getpgid - SC_ALLOW(getpgid), + SC_ALLOW(__NR_getpgid), #endif #ifdef __NR_getpid - SC_ALLOW(getpid), + SC_ALLOW(__NR_getpid), #endif #ifdef __NR_getrandom - SC_ALLOW(getrandom), + SC_ALLOW(__NR_getrandom), #endif #ifdef __NR_gettimeofday - SC_ALLOW(gettimeofday), + SC_ALLOW(__NR_gettimeofday), #endif #ifdef __NR_madvise - SC_ALLOW(madvise), + SC_ALLOW(__NR_madvise), #endif #ifdef __NR_mmap - SC_ALLOW(mmap), + SC_ALLOW(__NR_mmap), #endif #ifdef __NR_mmap2 - SC_ALLOW(mmap2), + SC_ALLOW(__NR_mmap2), #endif #ifdef __NR_mremap - SC_ALLOW(mremap), + SC_ALLOW(__NR_mremap), #endif #ifdef __NR_munmap - SC_ALLOW(munmap), + SC_ALLOW(__NR_munmap), #endif #ifdef __NR__newselect - SC_ALLOW(_newselect), + SC_ALLOW(__NR__newselect), #endif #ifdef __NR_poll - SC_ALLOW(poll), + SC_ALLOW(__NR_poll), #endif #ifdef __NR_pselect6 - SC_ALLOW(pselect6), + SC_ALLOW(__NR_pselect6), #endif #ifdef __NR_read - SC_ALLOW(read), + SC_ALLOW(__NR_read), #endif #ifdef __NR_rt_sigprocmask - SC_ALLOW(rt_sigprocmask), + SC_ALLOW(__NR_rt_sigprocmask), #endif #ifdef __NR_select - SC_ALLOW(select), + SC_ALLOW(__NR_select), #endif #ifdef __NR_shutdown - SC_ALLOW(shutdown), + SC_ALLOW(__NR_shutdown), #endif #ifdef __NR_sigprocmask - SC_ALLOW(sigprocmask), + SC_ALLOW(__NR_sigprocmask), #endif #ifdef __NR_time - SC_ALLOW(time), + SC_ALLOW(__NR_time), #endif #ifdef __NR_write - SC_ALLOW(write), + SC_ALLOW(__NR_write), #endif #ifdef __NR_socketcall - SC_ALLOW_ARG(socketcall, 0, SYS_SHUTDOWN), + SC_ALLOW_ARG(__NR_socketcall, 0, SYS_SHUTDOWN), +#endif +#if defined(__NR_ioctl) && defined(__s390__) + /* Allow ioctls for ICA crypto card on s390 */ + SC_ALLOW_ARG(__NR_ioctl, 1, Z90STAT_STATUS_MASK), + SC_ALLOW_ARG(__NR_ioctl, 1, ICARSAMODEXPO), + SC_ALLOW_ARG(__NR_ioctl, 1, ICARSACRT), +#endif +#if defined(__x86_64__) && defined(__ILP32__) && defined(__X32_SYSCALL_BIT) + /* + * On Linux x32, the clock_gettime VDSO falls back to the + * x86-64 syscall under some circumstances, e.g. + * https://bugs.debian.org/849923 + */ + SC_ALLOW(__NR_clock_gettime & ~__X32_SYSCALL_BIT); #endif /* Default deny */ BPF_STMT(BPF_RET+BPF_K, SECCOMP_FILTER_FAIL), }; static const struct sock_fprog preauth_program = { .len = (unsigned short)(sizeof(preauth_insns)/sizeof(preauth_insns[0])), .filter = (struct sock_filter *)preauth_insns, }; struct ssh_sandbox { pid_t child_pid; }; struct ssh_sandbox * ssh_sandbox_init(struct monitor *monitor) { struct ssh_sandbox *box; /* * Strictly, we don't need to maintain any state here but we need * to return non-NULL to satisfy the API. */ debug3("%s: preparing seccomp filter sandbox", __func__); box = xcalloc(1, sizeof(*box)); box->child_pid = 0; return box; } #ifdef SANDBOX_SECCOMP_FILTER_DEBUG extern struct monitor *pmonitor; void mm_log_handler(LogLevel level, const char *msg, void *ctx); static void ssh_sandbox_violation(int signum, siginfo_t *info, void *void_context) { char msg[256]; snprintf(msg, sizeof(msg), "%s: unexpected system call (arch:0x%x,syscall:%d @ %p)", __func__, info->si_arch, info->si_syscall, info->si_call_addr); mm_log_handler(SYSLOG_LEVEL_FATAL, msg, pmonitor); _exit(1); } static void ssh_sandbox_child_debugging(void) { struct sigaction act; sigset_t mask; debug3("%s: installing SIGSYS handler", __func__); memset(&act, 0, sizeof(act)); sigemptyset(&mask); sigaddset(&mask, SIGSYS); act.sa_sigaction = &ssh_sandbox_violation; act.sa_flags = SA_SIGINFO; if (sigaction(SIGSYS, &act, NULL) == -1) fatal("%s: sigaction(SIGSYS): %s", __func__, strerror(errno)); if (sigprocmask(SIG_UNBLOCK, &mask, NULL) == -1) fatal("%s: sigprocmask(SIGSYS): %s", __func__, strerror(errno)); } #endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ void ssh_sandbox_child(struct ssh_sandbox *box) { struct rlimit rl_zero; int nnp_failed = 0; /* Set rlimits for completeness if possible. */ rl_zero.rlim_cur = rl_zero.rlim_max = 0; if (setrlimit(RLIMIT_FSIZE, &rl_zero) == -1) fatal("%s: setrlimit(RLIMIT_FSIZE, { 0, 0 }): %s", __func__, strerror(errno)); if (setrlimit(RLIMIT_NOFILE, &rl_zero) == -1) fatal("%s: setrlimit(RLIMIT_NOFILE, { 0, 0 }): %s", __func__, strerror(errno)); if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1) fatal("%s: setrlimit(RLIMIT_NPROC, { 0, 0 }): %s", __func__, strerror(errno)); #ifdef SANDBOX_SECCOMP_FILTER_DEBUG ssh_sandbox_child_debugging(); #endif /* SANDBOX_SECCOMP_FILTER_DEBUG */ debug3("%s: setting PR_SET_NO_NEW_PRIVS", __func__); if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) { debug("%s: prctl(PR_SET_NO_NEW_PRIVS): %s", __func__, strerror(errno)); nnp_failed = 1; } debug3("%s: attaching seccomp filter program", __func__); if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &preauth_program) == -1) debug("%s: prctl(PR_SET_SECCOMP): %s", __func__, strerror(errno)); else if (nnp_failed) fatal("%s: SECCOMP_MODE_FILTER activated but " "PR_SET_NO_NEW_PRIVS failed", __func__); } void ssh_sandbox_parent_finish(struct ssh_sandbox *box) { free(box); debug3("%s: finished", __func__); } void ssh_sandbox_parent_preauth(struct ssh_sandbox *box, pid_t child_pid) { box->child_pid = child_pid; } #endif /* SANDBOX_SECCOMP_FILTER */ diff --git a/crypto/openssh/servconf.c b/crypto/openssh/servconf.c index c3fe9f8502db..bc935fcaead0 100644 --- a/crypto/openssh/servconf.c +++ b/crypto/openssh/servconf.c @@ -1,2372 +1,2373 @@ -/* $OpenBSD: servconf.c,v 1.301 2016/11/30 03:00:05 djm Exp $ */ +/* $OpenBSD: servconf.c,v 1.306 2017/03/14 07:19:07 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #include "includes.h" __RCSID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_UTIL_H #include #endif #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "ssh.h" #include "log.h" #include "buffer.h" #include "misc.h" #include "servconf.h" #include "compat.h" #include "pathnames.h" #include "cipher.h" #include "key.h" #include "kex.h" #include "mac.h" #include "match.h" #include "channels.h" #include "groupaccess.h" #include "canohost.h" #include "packet.h" #include "hostfile.h" #include "auth.h" #include "myproposal.h" #include "digest.h" #include "version.h" static void add_listen_addr(ServerOptions *, char *, int); static void add_one_listen_addr(ServerOptions *, char *, int); /* Use of privilege separation or not */ extern int use_privsep; extern Buffer cfg; /* Initializes the server options to their default values. */ void initialize_server_options(ServerOptions *options) { memset(options, 0, sizeof(*options)); /* Portable-specific options */ options->use_pam = -1; /* Standard Options */ options->num_ports = 0; options->ports_from_cmdline = 0; options->queued_listen_addrs = NULL; options->num_queued_listens = 0; options->listen_addrs = NULL; options->address_family = -1; options->num_host_key_files = 0; options->num_host_cert_files = 0; options->host_key_agent = NULL; options->pid_file = NULL; options->login_grace_time = -1; options->permit_root_login = PERMIT_NOT_SET; options->ignore_rhosts = -1; options->ignore_user_known_hosts = -1; options->print_motd = -1; options->print_lastlog = -1; options->x11_forwarding = -1; options->x11_display_offset = -1; options->x11_use_localhost = -1; options->permit_tty = -1; options->permit_user_rc = -1; options->xauth_location = NULL; options->strict_modes = -1; options->tcp_keep_alive = -1; options->log_facility = SYSLOG_FACILITY_NOT_SET; options->log_level = SYSLOG_LEVEL_NOT_SET; options->hostbased_authentication = -1; options->hostbased_uses_name_from_packet_only = -1; options->hostbased_key_types = NULL; options->hostkeyalgorithms = NULL; options->pubkey_authentication = -1; options->pubkey_key_types = NULL; options->kerberos_authentication = -1; options->kerberos_or_local_passwd = -1; options->kerberos_ticket_cleanup = -1; options->kerberos_get_afs_token = -1; options->gss_authentication=-1; options->gss_cleanup_creds = -1; options->gss_strict_acceptor = -1; options->password_authentication = -1; options->kbd_interactive_authentication = -1; options->challenge_response_authentication = -1; options->permit_empty_passwd = -1; options->permit_user_env = -1; options->compression = -1; options->rekey_limit = -1; options->rekey_interval = -1; options->allow_tcp_forwarding = -1; options->allow_streamlocal_forwarding = -1; options->allow_agent_forwarding = -1; options->num_allow_users = 0; options->num_deny_users = 0; options->num_allow_groups = 0; options->num_deny_groups = 0; options->ciphers = NULL; options->macs = NULL; options->kex_algorithms = NULL; options->fwd_opts.gateway_ports = -1; options->fwd_opts.streamlocal_bind_mask = (mode_t)-1; options->fwd_opts.streamlocal_bind_unlink = -1; options->num_subsystems = 0; options->max_startups_begin = -1; options->max_startups_rate = -1; options->max_startups = -1; options->max_authtries = -1; options->max_sessions = -1; options->banner = NULL; options->use_dns = -1; options->client_alive_interval = -1; options->client_alive_count_max = -1; options->num_authkeys_files = 0; options->num_accept_env = 0; options->permit_tun = -1; options->num_permitted_opens = -1; options->adm_forced_command = NULL; options->chroot_directory = NULL; options->authorized_keys_command = NULL; options->authorized_keys_command_user = NULL; options->revoked_keys_file = NULL; options->trusted_user_ca_keys = NULL; options->authorized_principals_file = NULL; options->authorized_principals_command = NULL; options->authorized_principals_command_user = NULL; options->ip_qos_interactive = -1; options->ip_qos_bulk = -1; options->version_addendum = NULL; options->fingerprint_hash = -1; options->disable_forwarding = -1; options->use_blacklist = -1; } /* Returns 1 if a string option is unset or set to "none" or 0 otherwise. */ static int option_clear_or_none(const char *o) { return o == NULL || strcasecmp(o, "none") == 0; } static void assemble_algorithms(ServerOptions *o) { if (kex_assemble_names(KEX_SERVER_ENCRYPT, &o->ciphers) != 0 || kex_assemble_names(KEX_SERVER_MAC, &o->macs) != 0 || kex_assemble_names(KEX_SERVER_KEX, &o->kex_algorithms) != 0 || kex_assemble_names(KEX_DEFAULT_PK_ALG, &o->hostkeyalgorithms) != 0 || kex_assemble_names(KEX_DEFAULT_PK_ALG, &o->hostbased_key_types) != 0 || kex_assemble_names(KEX_DEFAULT_PK_ALG, &o->pubkey_key_types) != 0) fatal("kex_assemble_names failed"); } void fill_default_server_options(ServerOptions *options) { int i; /* Portable-specific options */ if (options->use_pam == -1) options->use_pam = 1; /* Standard Options */ #define add_host_key_file(path) \ do { \ if (access((path), O_RDONLY) == 0) \ options->host_key_files \ [options->num_host_key_files++] = (path); \ } while (0) if (options->num_host_key_files == 0) { /* fill default hostkeys for protocols */ add_host_key_file(_PATH_HOST_RSA_KEY_FILE); add_host_key_file(_PATH_HOST_DSA_KEY_FILE); #ifdef OPENSSL_HAS_ECC add_host_key_file(_PATH_HOST_ECDSA_KEY_FILE); #endif add_host_key_file(_PATH_HOST_ED25519_KEY_FILE); } #undef add_host_key_file if (options->num_host_key_files == 0) fatal("No host key files found"); /* No certificates by default */ if (options->num_ports == 0) options->ports[options->num_ports++] = SSH_DEFAULT_PORT; if (options->address_family == -1) options->address_family = AF_UNSPEC; if (options->listen_addrs == NULL) add_listen_addr(options, NULL, 0); if (options->pid_file == NULL) options->pid_file = xstrdup(_PATH_SSH_DAEMON_PID_FILE); if (options->login_grace_time == -1) options->login_grace_time = 120; if (options->permit_root_login == PERMIT_NOT_SET) options->permit_root_login = PERMIT_NO; if (options->ignore_rhosts == -1) options->ignore_rhosts = 1; if (options->ignore_user_known_hosts == -1) options->ignore_user_known_hosts = 0; if (options->print_motd == -1) options->print_motd = 1; if (options->print_lastlog == -1) options->print_lastlog = 1; if (options->x11_forwarding == -1) options->x11_forwarding = 1; if (options->x11_display_offset == -1) options->x11_display_offset = 10; if (options->x11_use_localhost == -1) options->x11_use_localhost = 1; if (options->xauth_location == NULL) options->xauth_location = xstrdup(_PATH_XAUTH); if (options->permit_tty == -1) options->permit_tty = 1; if (options->permit_user_rc == -1) options->permit_user_rc = 1; if (options->strict_modes == -1) options->strict_modes = 1; if (options->tcp_keep_alive == -1) options->tcp_keep_alive = 1; if (options->log_facility == SYSLOG_FACILITY_NOT_SET) options->log_facility = SYSLOG_FACILITY_AUTH; if (options->log_level == SYSLOG_LEVEL_NOT_SET) options->log_level = SYSLOG_LEVEL_INFO; if (options->hostbased_authentication == -1) options->hostbased_authentication = 0; if (options->hostbased_uses_name_from_packet_only == -1) options->hostbased_uses_name_from_packet_only = 0; if (options->pubkey_authentication == -1) options->pubkey_authentication = 1; if (options->kerberos_authentication == -1) options->kerberos_authentication = 0; if (options->kerberos_or_local_passwd == -1) options->kerberos_or_local_passwd = 1; if (options->kerberos_ticket_cleanup == -1) options->kerberos_ticket_cleanup = 1; if (options->kerberos_get_afs_token == -1) options->kerberos_get_afs_token = 0; if (options->gss_authentication == -1) options->gss_authentication = 0; if (options->gss_cleanup_creds == -1) options->gss_cleanup_creds = 1; if (options->gss_strict_acceptor == -1) - options->gss_strict_acceptor = 0; + options->gss_strict_acceptor = 1; if (options->password_authentication == -1) options->password_authentication = 0; if (options->kbd_interactive_authentication == -1) options->kbd_interactive_authentication = 0; if (options->challenge_response_authentication == -1) options->challenge_response_authentication = 1; if (options->permit_empty_passwd == -1) options->permit_empty_passwd = 0; if (options->permit_user_env == -1) options->permit_user_env = 0; if (options->compression == -1) options->compression = COMP_DELAYED; if (options->rekey_limit == -1) options->rekey_limit = 0; if (options->rekey_interval == -1) options->rekey_interval = 0; if (options->allow_tcp_forwarding == -1) options->allow_tcp_forwarding = FORWARD_ALLOW; if (options->allow_streamlocal_forwarding == -1) options->allow_streamlocal_forwarding = FORWARD_ALLOW; if (options->allow_agent_forwarding == -1) options->allow_agent_forwarding = 1; if (options->fwd_opts.gateway_ports == -1) options->fwd_opts.gateway_ports = 0; if (options->max_startups == -1) options->max_startups = 100; if (options->max_startups_rate == -1) options->max_startups_rate = 30; /* 30% */ if (options->max_startups_begin == -1) options->max_startups_begin = 10; if (options->max_authtries == -1) options->max_authtries = DEFAULT_AUTH_FAIL_MAX; if (options->max_sessions == -1) options->max_sessions = DEFAULT_SESSIONS_MAX; if (options->use_dns == -1) options->use_dns = 1; if (options->client_alive_interval == -1) options->client_alive_interval = 0; if (options->client_alive_count_max == -1) options->client_alive_count_max = 3; if (options->num_authkeys_files == 0) { options->authorized_keys_files[options->num_authkeys_files++] = xstrdup(_PATH_SSH_USER_PERMITTED_KEYS); options->authorized_keys_files[options->num_authkeys_files++] = xstrdup(_PATH_SSH_USER_PERMITTED_KEYS2); } if (options->permit_tun == -1) options->permit_tun = SSH_TUNMODE_NO; if (options->ip_qos_interactive == -1) options->ip_qos_interactive = IPTOS_LOWDELAY; if (options->ip_qos_bulk == -1) options->ip_qos_bulk = IPTOS_THROUGHPUT; if (options->version_addendum == NULL) options->version_addendum = xstrdup(SSH_VERSION_FREEBSD); if (options->fwd_opts.streamlocal_bind_mask == (mode_t)-1) options->fwd_opts.streamlocal_bind_mask = 0177; if (options->fwd_opts.streamlocal_bind_unlink == -1) options->fwd_opts.streamlocal_bind_unlink = 0; if (options->fingerprint_hash == -1) options->fingerprint_hash = SSH_FP_HASH_DEFAULT; if (options->disable_forwarding == -1) options->disable_forwarding = 0; if (options->use_blacklist == -1) options->use_blacklist = 0; assemble_algorithms(options); /* Turn privilege separation and sandboxing on by default */ if (use_privsep == -1) use_privsep = PRIVSEP_ON; #define CLEAR_ON_NONE(v) \ do { \ if (option_clear_or_none(v)) { \ free(v); \ v = NULL; \ } \ } while(0) CLEAR_ON_NONE(options->pid_file); CLEAR_ON_NONE(options->xauth_location); CLEAR_ON_NONE(options->banner); CLEAR_ON_NONE(options->trusted_user_ca_keys); CLEAR_ON_NONE(options->revoked_keys_file); CLEAR_ON_NONE(options->authorized_principals_file); CLEAR_ON_NONE(options->adm_forced_command); CLEAR_ON_NONE(options->chroot_directory); for (i = 0; i < options->num_host_key_files; i++) CLEAR_ON_NONE(options->host_key_files[i]); for (i = 0; i < options->num_host_cert_files; i++) CLEAR_ON_NONE(options->host_cert_files[i]); #undef CLEAR_ON_NONE /* Similar handling for AuthenticationMethods=any */ if (options->num_auth_methods == 1 && strcmp(options->auth_methods[0], "any") == 0) { free(options->auth_methods[0]); options->auth_methods[0] = NULL; options->num_auth_methods = 0; } #ifndef HAVE_MMAP if (use_privsep && options->compression == 1) { error("This platform does not support both privilege " "separation and compression"); error("Compression disabled"); options->compression = 0; } #endif } /* Keyword tokens. */ typedef enum { sBadOption, /* == unknown option */ /* Portable-specific options */ sUsePAM, /* Standard Options */ sPort, sHostKeyFile, sLoginGraceTime, sPermitRootLogin, sLogFacility, sLogLevel, sRhostsRSAAuthentication, sRSAAuthentication, sKerberosAuthentication, sKerberosOrLocalPasswd, sKerberosTicketCleanup, sKerberosGetAFSToken, sKerberosTgtPassing, sChallengeResponseAuthentication, sPasswordAuthentication, sKbdInteractiveAuthentication, sListenAddress, sAddressFamily, sPrintMotd, sPrintLastLog, sIgnoreRhosts, sX11Forwarding, sX11DisplayOffset, sX11UseLocalhost, sPermitTTY, sStrictModes, sEmptyPasswd, sTCPKeepAlive, sPermitUserEnvironment, sAllowTcpForwarding, sCompression, sRekeyLimit, sAllowUsers, sDenyUsers, sAllowGroups, sDenyGroups, sIgnoreUserKnownHosts, sCiphers, sMacs, sPidFile, sGatewayPorts, sPubkeyAuthentication, sPubkeyAcceptedKeyTypes, sXAuthLocation, sSubsystem, sMaxStartups, sMaxAuthTries, sMaxSessions, sBanner, sUseDNS, sHostbasedAuthentication, sHostbasedUsesNameFromPacketOnly, sHostbasedAcceptedKeyTypes, sHostKeyAlgorithms, sClientAliveInterval, sClientAliveCountMax, sAuthorizedKeysFile, sGssAuthentication, sGssCleanupCreds, sGssStrictAcceptor, sAcceptEnv, sPermitTunnel, sMatch, sPermitOpen, sForceCommand, sChrootDirectory, sUsePrivilegeSeparation, sAllowAgentForwarding, sHostCertificate, sRevokedKeys, sTrustedUserCAKeys, sAuthorizedPrincipalsFile, sAuthorizedPrincipalsCommand, sAuthorizedPrincipalsCommandUser, sKexAlgorithms, sIPQoS, sVersionAddendum, sAuthorizedKeysCommand, sAuthorizedKeysCommandUser, sAuthenticationMethods, sHostKeyAgent, sPermitUserRC, sStreamLocalBindMask, sStreamLocalBindUnlink, sAllowStreamLocalForwarding, sFingerprintHash, sDisableForwarding, sUseBlacklist, sDeprecated, sIgnore, sUnsupported } ServerOpCodes; #define SSHCFG_GLOBAL 0x01 /* allowed in main section of sshd_config */ #define SSHCFG_MATCH 0x02 /* allowed inside a Match section */ #define SSHCFG_ALL (SSHCFG_GLOBAL|SSHCFG_MATCH) /* Textual representation of the tokens. */ static struct { const char *name; ServerOpCodes opcode; u_int flags; } keywords[] = { /* Portable-specific options */ #ifdef USE_PAM { "usepam", sUsePAM, SSHCFG_GLOBAL }, #else { "usepam", sUnsupported, SSHCFG_GLOBAL }, #endif { "pamauthenticationviakbdint", sDeprecated, SSHCFG_GLOBAL }, /* Standard Options */ { "port", sPort, SSHCFG_GLOBAL }, { "hostkey", sHostKeyFile, SSHCFG_GLOBAL }, { "hostdsakey", sHostKeyFile, SSHCFG_GLOBAL }, /* alias */ { "hostkeyagent", sHostKeyAgent, SSHCFG_GLOBAL }, { "pidfile", sPidFile, SSHCFG_GLOBAL }, { "serverkeybits", sDeprecated, SSHCFG_GLOBAL }, { "logingracetime", sLoginGraceTime, SSHCFG_GLOBAL }, { "keyregenerationinterval", sDeprecated, SSHCFG_GLOBAL }, { "permitrootlogin", sPermitRootLogin, SSHCFG_ALL }, { "syslogfacility", sLogFacility, SSHCFG_GLOBAL }, { "loglevel", sLogLevel, SSHCFG_GLOBAL }, { "rhostsauthentication", sDeprecated, SSHCFG_GLOBAL }, { "rhostsrsaauthentication", sDeprecated, SSHCFG_ALL }, { "hostbasedauthentication", sHostbasedAuthentication, SSHCFG_ALL }, { "hostbasedusesnamefrompacketonly", sHostbasedUsesNameFromPacketOnly, SSHCFG_ALL }, { "hostbasedacceptedkeytypes", sHostbasedAcceptedKeyTypes, SSHCFG_ALL }, { "hostkeyalgorithms", sHostKeyAlgorithms, SSHCFG_GLOBAL }, { "rsaauthentication", sDeprecated, SSHCFG_ALL }, { "pubkeyauthentication", sPubkeyAuthentication, SSHCFG_ALL }, { "pubkeyacceptedkeytypes", sPubkeyAcceptedKeyTypes, SSHCFG_ALL }, { "dsaauthentication", sPubkeyAuthentication, SSHCFG_GLOBAL }, /* alias */ #ifdef KRB5 { "kerberosauthentication", sKerberosAuthentication, SSHCFG_ALL }, { "kerberosorlocalpasswd", sKerberosOrLocalPasswd, SSHCFG_GLOBAL }, { "kerberosticketcleanup", sKerberosTicketCleanup, SSHCFG_GLOBAL }, #ifdef USE_AFS { "kerberosgetafstoken", sKerberosGetAFSToken, SSHCFG_GLOBAL }, #else { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, #endif #else { "kerberosauthentication", sUnsupported, SSHCFG_ALL }, { "kerberosorlocalpasswd", sUnsupported, SSHCFG_GLOBAL }, { "kerberosticketcleanup", sUnsupported, SSHCFG_GLOBAL }, { "kerberosgetafstoken", sUnsupported, SSHCFG_GLOBAL }, #endif { "kerberostgtpassing", sUnsupported, SSHCFG_GLOBAL }, { "afstokenpassing", sUnsupported, SSHCFG_GLOBAL }, #ifdef GSSAPI { "gssapiauthentication", sGssAuthentication, SSHCFG_ALL }, { "gssapicleanupcredentials", sGssCleanupCreds, SSHCFG_GLOBAL }, { "gssapistrictacceptorcheck", sGssStrictAcceptor, SSHCFG_GLOBAL }, #else { "gssapiauthentication", sUnsupported, SSHCFG_ALL }, { "gssapicleanupcredentials", sUnsupported, SSHCFG_GLOBAL }, { "gssapistrictacceptorcheck", sUnsupported, SSHCFG_GLOBAL }, #endif { "passwordauthentication", sPasswordAuthentication, SSHCFG_ALL }, { "kbdinteractiveauthentication", sKbdInteractiveAuthentication, SSHCFG_ALL }, { "challengeresponseauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, { "skeyauthentication", sChallengeResponseAuthentication, SSHCFG_GLOBAL }, /* alias */ { "checkmail", sDeprecated, SSHCFG_GLOBAL }, { "listenaddress", sListenAddress, SSHCFG_GLOBAL }, { "addressfamily", sAddressFamily, SSHCFG_GLOBAL }, { "printmotd", sPrintMotd, SSHCFG_GLOBAL }, #ifdef DISABLE_LASTLOG { "printlastlog", sUnsupported, SSHCFG_GLOBAL }, #else { "printlastlog", sPrintLastLog, SSHCFG_GLOBAL }, #endif { "ignorerhosts", sIgnoreRhosts, SSHCFG_GLOBAL }, { "ignoreuserknownhosts", sIgnoreUserKnownHosts, SSHCFG_GLOBAL }, { "x11forwarding", sX11Forwarding, SSHCFG_ALL }, { "x11displayoffset", sX11DisplayOffset, SSHCFG_ALL }, { "x11uselocalhost", sX11UseLocalhost, SSHCFG_ALL }, { "xauthlocation", sXAuthLocation, SSHCFG_GLOBAL }, { "strictmodes", sStrictModes, SSHCFG_GLOBAL }, { "permitemptypasswords", sEmptyPasswd, SSHCFG_ALL }, { "permituserenvironment", sPermitUserEnvironment, SSHCFG_GLOBAL }, { "uselogin", sDeprecated, SSHCFG_GLOBAL }, { "compression", sCompression, SSHCFG_GLOBAL }, { "rekeylimit", sRekeyLimit, SSHCFG_ALL }, { "tcpkeepalive", sTCPKeepAlive, SSHCFG_GLOBAL }, { "keepalive", sTCPKeepAlive, SSHCFG_GLOBAL }, /* obsolete alias */ { "allowtcpforwarding", sAllowTcpForwarding, SSHCFG_ALL }, { "allowagentforwarding", sAllowAgentForwarding, SSHCFG_ALL }, { "allowusers", sAllowUsers, SSHCFG_ALL }, { "denyusers", sDenyUsers, SSHCFG_ALL }, { "allowgroups", sAllowGroups, SSHCFG_ALL }, { "denygroups", sDenyGroups, SSHCFG_ALL }, { "ciphers", sCiphers, SSHCFG_GLOBAL }, { "macs", sMacs, SSHCFG_GLOBAL }, { "protocol", sIgnore, SSHCFG_GLOBAL }, { "gatewayports", sGatewayPorts, SSHCFG_ALL }, { "subsystem", sSubsystem, SSHCFG_GLOBAL }, { "maxstartups", sMaxStartups, SSHCFG_GLOBAL }, { "maxauthtries", sMaxAuthTries, SSHCFG_ALL }, { "maxsessions", sMaxSessions, SSHCFG_ALL }, { "banner", sBanner, SSHCFG_ALL }, { "usedns", sUseDNS, SSHCFG_GLOBAL }, { "verifyreversemapping", sDeprecated, SSHCFG_GLOBAL }, { "reversemappingcheck", sDeprecated, SSHCFG_GLOBAL }, { "clientaliveinterval", sClientAliveInterval, SSHCFG_ALL }, { "clientalivecountmax", sClientAliveCountMax, SSHCFG_ALL }, { "authorizedkeysfile", sAuthorizedKeysFile, SSHCFG_ALL }, { "authorizedkeysfile2", sDeprecated, SSHCFG_ALL }, - { "useprivilegeseparation", sUsePrivilegeSeparation, SSHCFG_GLOBAL}, + { "useprivilegeseparation", sDeprecated, SSHCFG_GLOBAL}, { "acceptenv", sAcceptEnv, SSHCFG_ALL }, { "permittunnel", sPermitTunnel, SSHCFG_ALL }, { "permittty", sPermitTTY, SSHCFG_ALL }, { "permituserrc", sPermitUserRC, SSHCFG_ALL }, { "match", sMatch, SSHCFG_ALL }, { "permitopen", sPermitOpen, SSHCFG_ALL }, { "forcecommand", sForceCommand, SSHCFG_ALL }, { "chrootdirectory", sChrootDirectory, SSHCFG_ALL }, { "hostcertificate", sHostCertificate, SSHCFG_GLOBAL }, { "revokedkeys", sRevokedKeys, SSHCFG_ALL }, { "trustedusercakeys", sTrustedUserCAKeys, SSHCFG_ALL }, { "authorizedprincipalsfile", sAuthorizedPrincipalsFile, SSHCFG_ALL }, { "kexalgorithms", sKexAlgorithms, SSHCFG_GLOBAL }, { "ipqos", sIPQoS, SSHCFG_ALL }, { "authorizedkeyscommand", sAuthorizedKeysCommand, SSHCFG_ALL }, { "authorizedkeyscommanduser", sAuthorizedKeysCommandUser, SSHCFG_ALL }, { "authorizedprincipalscommand", sAuthorizedPrincipalsCommand, SSHCFG_ALL }, { "authorizedprincipalscommanduser", sAuthorizedPrincipalsCommandUser, SSHCFG_ALL }, { "versionaddendum", sVersionAddendum, SSHCFG_GLOBAL }, { "authenticationmethods", sAuthenticationMethods, SSHCFG_ALL }, { "streamlocalbindmask", sStreamLocalBindMask, SSHCFG_ALL }, { "streamlocalbindunlink", sStreamLocalBindUnlink, SSHCFG_ALL }, { "allowstreamlocalforwarding", sAllowStreamLocalForwarding, SSHCFG_ALL }, { "fingerprinthash", sFingerprintHash, SSHCFG_GLOBAL }, { "disableforwarding", sDisableForwarding, SSHCFG_ALL }, { "useblacklist", sUseBlacklist, SSHCFG_GLOBAL }, { "noneenabled", sUnsupported, SSHCFG_ALL }, { "hpndisabled", sDeprecated, SSHCFG_ALL }, { "hpnbuffersize", sDeprecated, SSHCFG_ALL }, { "tcprcvbufpoll", sDeprecated, SSHCFG_ALL }, { NULL, sBadOption, 0 } }; static struct { int val; char *text; } tunmode_desc[] = { { SSH_TUNMODE_NO, "no" }, { SSH_TUNMODE_POINTOPOINT, "point-to-point" }, { SSH_TUNMODE_ETHERNET, "ethernet" }, { SSH_TUNMODE_YES, "yes" }, { -1, NULL } }; /* * Returns the number of the token pointed to by cp or sBadOption. */ static ServerOpCodes parse_token(const char *cp, const char *filename, int linenum, u_int *flags) { u_int i; for (i = 0; keywords[i].name; i++) if (strcasecmp(cp, keywords[i].name) == 0) { *flags = keywords[i].flags; return keywords[i].opcode; } error("%s: line %d: Bad configuration option: %s", filename, linenum, cp); return sBadOption; } char * derelativise_path(const char *path) { char *expanded, *ret, cwd[PATH_MAX]; if (strcasecmp(path, "none") == 0) return xstrdup("none"); expanded = tilde_expand_filename(path, getuid()); if (*expanded == '/') return expanded; if (getcwd(cwd, sizeof(cwd)) == NULL) fatal("%s: getcwd: %s", __func__, strerror(errno)); xasprintf(&ret, "%s/%s", cwd, expanded); free(expanded); return ret; } static void add_listen_addr(ServerOptions *options, char *addr, int port) { u_int i; if (port == 0) for (i = 0; i < options->num_ports; i++) add_one_listen_addr(options, addr, options->ports[i]); else add_one_listen_addr(options, addr, port); } static void add_one_listen_addr(ServerOptions *options, char *addr, int port) { struct addrinfo hints, *ai, *aitop; char strport[NI_MAXSERV]; int gaierr; memset(&hints, 0, sizeof(hints)); hints.ai_family = options->address_family; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = (addr == NULL) ? AI_PASSIVE : 0; snprintf(strport, sizeof strport, "%d", port); if ((gaierr = getaddrinfo(addr, strport, &hints, &aitop)) != 0) fatal("bad addr or host: %s (%s)", addr ? addr : "", ssh_gai_strerror(gaierr)); for (ai = aitop; ai->ai_next; ai = ai->ai_next) ; ai->ai_next = options->listen_addrs; options->listen_addrs = aitop; } /* * Queue a ListenAddress to be processed once we have all of the Ports * and AddressFamily options. */ static void queue_listen_addr(ServerOptions *options, char *addr, int port) { options->queued_listen_addrs = xreallocarray( options->queued_listen_addrs, options->num_queued_listens + 1, sizeof(addr)); options->queued_listen_ports = xreallocarray( options->queued_listen_ports, options->num_queued_listens + 1, sizeof(port)); options->queued_listen_addrs[options->num_queued_listens] = xstrdup(addr); options->queued_listen_ports[options->num_queued_listens] = port; options->num_queued_listens++; } /* * Process queued (text) ListenAddress entries. */ static void process_queued_listen_addrs(ServerOptions *options) { u_int i; if (options->num_ports == 0) options->ports[options->num_ports++] = SSH_DEFAULT_PORT; if (options->address_family == -1) options->address_family = AF_UNSPEC; for (i = 0; i < options->num_queued_listens; i++) { add_listen_addr(options, options->queued_listen_addrs[i], options->queued_listen_ports[i]); free(options->queued_listen_addrs[i]); options->queued_listen_addrs[i] = NULL; } free(options->queued_listen_addrs); options->queued_listen_addrs = NULL; free(options->queued_listen_ports); options->queued_listen_ports = NULL; options->num_queued_listens = 0; } struct connection_info * get_connection_info(int populate, int use_dns) { struct ssh *ssh = active_state; /* XXX */ static struct connection_info ci; if (!populate) return &ci; ci.host = auth_get_canonical_hostname(ssh, use_dns); ci.address = ssh_remote_ipaddr(ssh); ci.laddress = ssh_local_ipaddr(ssh); ci.lport = ssh_local_port(ssh); return &ci; } /* * The strategy for the Match blocks is that the config file is parsed twice. * * The first time is at startup. activep is initialized to 1 and the * directives in the global context are processed and acted on. Hitting a * Match directive unsets activep and the directives inside the block are * checked for syntax only. * * The second time is after a connection has been established but before * authentication. activep is initialized to 2 and global config directives * are ignored since they have already been processed. If the criteria in a * Match block is met, activep is set and the subsequent directives * processed and actioned until EOF or another Match block unsets it. Any * options set are copied into the main server config. * * Potential additions/improvements: * - Add Match support for pre-kex directives, eg. Ciphers. * * - Add a Tag directive (idea from David Leonard) ala pf, eg: * Match Address 192.168.0.* * Tag trusted * Match Group wheel * Tag trusted * Match Tag trusted * AllowTcpForwarding yes * GatewayPorts clientspecified * [...] * * - Add a PermittedChannelRequests directive * Match Group shell * PermittedChannelRequests session,forwarded-tcpip */ static int match_cfg_line_group(const char *grps, int line, const char *user) { int result = 0; struct passwd *pw; if (user == NULL) goto out; if ((pw = getpwnam(user)) == NULL) { debug("Can't match group at line %d because user %.100s does " "not exist", line, user); } else if (ga_init(pw->pw_name, pw->pw_gid) == 0) { debug("Can't Match group because user %.100s not in any group " "at line %d", user, line); } else if (ga_match_pattern_list(grps) != 1) { debug("user %.100s does not match group list %.100s at line %d", user, grps, line); } else { debug("user %.100s matched group list %.100s at line %d", user, grps, line); result = 1; } out: ga_free(); return result; } /* * All of the attributes on a single Match line are ANDed together, so we need * to check every attribute and set the result to zero if any attribute does * not match. */ static int match_cfg_line(char **condition, int line, struct connection_info *ci) { int result = 1, attributes = 0, port; char *arg, *attrib, *cp = *condition; if (ci == NULL) debug3("checking syntax for 'Match %s'", cp); else debug3("checking match for '%s' user %s host %s addr %s " "laddr %s lport %d", cp, ci->user ? ci->user : "(null)", ci->host ? ci->host : "(null)", ci->address ? ci->address : "(null)", ci->laddress ? ci->laddress : "(null)", ci->lport); while ((attrib = strdelim(&cp)) && *attrib != '\0') { attributes++; if (strcasecmp(attrib, "all") == 0) { if (attributes != 1 || ((arg = strdelim(&cp)) != NULL && *arg != '\0')) { error("'all' cannot be combined with other " "Match attributes"); return -1; } *condition = cp; return 1; } if ((arg = strdelim(&cp)) == NULL || *arg == '\0') { error("Missing Match criteria for %s", attrib); return -1; } if (strcasecmp(attrib, "user") == 0) { if (ci == NULL || ci->user == NULL) { result = 0; continue; } if (match_pattern_list(ci->user, arg, 0) != 1) result = 0; else debug("user %.100s matched 'User %.100s' at " "line %d", ci->user, arg, line); } else if (strcasecmp(attrib, "group") == 0) { if (ci == NULL || ci->user == NULL) { result = 0; continue; } switch (match_cfg_line_group(arg, line, ci->user)) { case -1: return -1; case 0: result = 0; } } else if (strcasecmp(attrib, "host") == 0) { if (ci == NULL || ci->host == NULL) { result = 0; continue; } if (match_hostname(ci->host, arg) != 1) result = 0; else debug("connection from %.100s matched 'Host " "%.100s' at line %d", ci->host, arg, line); } else if (strcasecmp(attrib, "address") == 0) { if (ci == NULL || ci->address == NULL) { result = 0; continue; } switch (addr_match_list(ci->address, arg)) { case 1: debug("connection from %.100s matched 'Address " "%.100s' at line %d", ci->address, arg, line); break; case 0: case -1: result = 0; break; case -2: return -1; } } else if (strcasecmp(attrib, "localaddress") == 0){ if (ci == NULL || ci->laddress == NULL) { result = 0; continue; } switch (addr_match_list(ci->laddress, arg)) { case 1: debug("connection from %.100s matched " "'LocalAddress %.100s' at line %d", ci->laddress, arg, line); break; case 0: case -1: result = 0; break; case -2: return -1; } } else if (strcasecmp(attrib, "localport") == 0) { if ((port = a2port(arg)) == -1) { error("Invalid LocalPort '%s' on Match line", arg); return -1; } if (ci == NULL || ci->lport == 0) { result = 0; continue; } /* TODO support port lists */ if (port == ci->lport) debug("connection from %.100s matched " "'LocalPort %d' at line %d", ci->laddress, port, line); else result = 0; } else { error("Unsupported Match attribute %s", attrib); return -1; } } if (attributes == 0) { error("One or more attributes required for Match"); return -1; } if (ci != NULL) debug3("match %sfound", result ? "" : "not "); *condition = cp; return result; } #define WHITESPACE " \t\r\n" /* Multistate option parsing */ struct multistate { char *key; int value; }; static const struct multistate multistate_addressfamily[] = { { "inet", AF_INET }, { "inet6", AF_INET6 }, { "any", AF_UNSPEC }, { NULL, -1 } }; static const struct multistate multistate_permitrootlogin[] = { { "without-password", PERMIT_NO_PASSWD }, { "prohibit-password", PERMIT_NO_PASSWD }, { "forced-commands-only", PERMIT_FORCED_ONLY }, { "yes", PERMIT_YES }, { "no", PERMIT_NO }, { NULL, -1 } }; static const struct multistate multistate_compression[] = { { "yes", COMP_DELAYED }, { "delayed", COMP_DELAYED }, { "no", COMP_NONE }, { NULL, -1 } }; static const struct multistate multistate_gatewayports[] = { { "clientspecified", 2 }, { "yes", 1 }, { "no", 0 }, { NULL, -1 } }; static const struct multistate multistate_privsep[] = { { "yes", PRIVSEP_NOSANDBOX }, { "sandbox", PRIVSEP_ON }, { "nosandbox", PRIVSEP_NOSANDBOX }, { "no", PRIVSEP_OFF }, { NULL, -1 } }; static const struct multistate multistate_tcpfwd[] = { { "yes", FORWARD_ALLOW }, { "all", FORWARD_ALLOW }, { "no", FORWARD_DENY }, { "remote", FORWARD_REMOTE }, { "local", FORWARD_LOCAL }, { NULL, -1 } }; int process_server_config_line(ServerOptions *options, char *line, const char *filename, int linenum, int *activep, struct connection_info *connectinfo) { char *cp, **charptr, *arg, *p; int cmdline = 0, *intptr, value, value2, n, port; SyslogFacility *log_facility_ptr; LogLevel *log_level_ptr; ServerOpCodes opcode; u_int i, flags = 0; size_t len; long long val64; const struct multistate *multistate_ptr; + /* Strip trailing whitespace. Allow \f (form feed) at EOL only */ + if ((len = strlen(line)) == 0) + return 0; + for (len--; len > 0; len--) { + if (strchr(WHITESPACE "\f", line[len]) == NULL) + break; + line[len] = '\0'; + } + cp = line; if ((arg = strdelim(&cp)) == NULL) return 0; /* Ignore leading whitespace */ if (*arg == '\0') arg = strdelim(&cp); if (!arg || !*arg || *arg == '#') return 0; intptr = NULL; charptr = NULL; opcode = parse_token(arg, filename, linenum, &flags); if (activep == NULL) { /* We are processing a command line directive */ cmdline = 1; activep = &cmdline; } if (*activep && opcode != sMatch) debug3("%s:%d setting %s %s", filename, linenum, arg, cp); if (*activep == 0 && !(flags & SSHCFG_MATCH)) { if (connectinfo == NULL) { fatal("%s line %d: Directive '%s' is not allowed " "within a Match block", filename, linenum, arg); } else { /* this is a directive we have already processed */ while (arg) arg = strdelim(&cp); return 0; } } switch (opcode) { /* Portable-specific options */ case sUsePAM: intptr = &options->use_pam; goto parse_flag; /* Standard Options */ case sBadOption: return -1; case sPort: /* ignore ports from configfile if cmdline specifies ports */ if (options->ports_from_cmdline) return 0; if (options->num_ports >= MAX_PORTS) fatal("%s line %d: too many ports.", filename, linenum); arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing port number.", filename, linenum); options->ports[options->num_ports++] = a2port(arg); if (options->ports[options->num_ports-1] <= 0) fatal("%s line %d: Badly formatted port number.", filename, linenum); break; case sLoginGraceTime: intptr = &options->login_grace_time; parse_time: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing time value.", filename, linenum); if ((value = convtime(arg)) == -1) fatal("%s line %d: invalid time value.", filename, linenum); if (*activep && *intptr == -1) *intptr = value; break; case sListenAddress: arg = strdelim(&cp); if (arg == NULL || *arg == '\0') fatal("%s line %d: missing address", filename, linenum); /* check for bare IPv6 address: no "[]" and 2 or more ":" */ if (strchr(arg, '[') == NULL && (p = strchr(arg, ':')) != NULL && strchr(p+1, ':') != NULL) { queue_listen_addr(options, arg, 0); break; } p = hpdelim(&arg); if (p == NULL) fatal("%s line %d: bad address:port usage", filename, linenum); p = cleanhostname(p); if (arg == NULL) port = 0; else if ((port = a2port(arg)) <= 0) fatal("%s line %d: bad port number", filename, linenum); queue_listen_addr(options, p, port); break; case sAddressFamily: intptr = &options->address_family; multistate_ptr = multistate_addressfamily; parse_multistate: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing argument.", filename, linenum); value = -1; for (i = 0; multistate_ptr[i].key != NULL; i++) { if (strcasecmp(arg, multistate_ptr[i].key) == 0) { value = multistate_ptr[i].value; break; } } if (value == -1) fatal("%s line %d: unsupported option \"%s\".", filename, linenum, arg); if (*activep && *intptr == -1) *intptr = value; break; case sHostKeyFile: intptr = &options->num_host_key_files; if (*intptr >= MAX_HOSTKEYS) fatal("%s line %d: too many host keys specified (max %d).", filename, linenum, MAX_HOSTKEYS); charptr = &options->host_key_files[*intptr]; parse_filename: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing file name.", filename, linenum); if (*activep && *charptr == NULL) { *charptr = derelativise_path(arg); /* increase optional counter */ if (intptr != NULL) *intptr = *intptr + 1; } break; case sHostKeyAgent: charptr = &options->host_key_agent; arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing socket name.", filename, linenum); if (*activep && *charptr == NULL) *charptr = !strcmp(arg, SSH_AUTHSOCKET_ENV_NAME) ? xstrdup(arg) : derelativise_path(arg); break; case sHostCertificate: intptr = &options->num_host_cert_files; if (*intptr >= MAX_HOSTKEYS) fatal("%s line %d: too many host certificates " "specified (max %d).", filename, linenum, MAX_HOSTCERTS); charptr = &options->host_cert_files[*intptr]; goto parse_filename; case sPidFile: charptr = &options->pid_file; goto parse_filename; case sPermitRootLogin: intptr = &options->permit_root_login; multistate_ptr = multistate_permitrootlogin; goto parse_multistate; case sIgnoreRhosts: intptr = &options->ignore_rhosts; parse_flag: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing yes/no argument.", filename, linenum); value = 0; /* silence compiler */ if (strcmp(arg, "yes") == 0) value = 1; else if (strcmp(arg, "no") == 0) value = 0; else fatal("%s line %d: Bad yes/no argument: %s", filename, linenum, arg); if (*activep && *intptr == -1) *intptr = value; break; case sIgnoreUserKnownHosts: intptr = &options->ignore_user_known_hosts; goto parse_flag; case sHostbasedAuthentication: intptr = &options->hostbased_authentication; goto parse_flag; case sHostbasedUsesNameFromPacketOnly: intptr = &options->hostbased_uses_name_from_packet_only; goto parse_flag; case sHostbasedAcceptedKeyTypes: charptr = &options->hostbased_key_types; parse_keytypes: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: Missing argument.", filename, linenum); - if (!sshkey_names_valid2(*arg == '+' ? arg + 1 : arg, 1)) + if (*arg != '-' && + !sshkey_names_valid2(*arg == '+' ? arg + 1 : arg, 1)) fatal("%s line %d: Bad key types '%s'.", filename, linenum, arg ? arg : ""); if (*activep && *charptr == NULL) *charptr = xstrdup(arg); break; case sHostKeyAlgorithms: charptr = &options->hostkeyalgorithms; goto parse_keytypes; case sPubkeyAuthentication: intptr = &options->pubkey_authentication; goto parse_flag; case sPubkeyAcceptedKeyTypes: charptr = &options->pubkey_key_types; goto parse_keytypes; case sKerberosAuthentication: intptr = &options->kerberos_authentication; goto parse_flag; case sKerberosOrLocalPasswd: intptr = &options->kerberos_or_local_passwd; goto parse_flag; case sKerberosTicketCleanup: intptr = &options->kerberos_ticket_cleanup; goto parse_flag; case sKerberosGetAFSToken: intptr = &options->kerberos_get_afs_token; goto parse_flag; case sGssAuthentication: intptr = &options->gss_authentication; goto parse_flag; case sGssCleanupCreds: intptr = &options->gss_cleanup_creds; goto parse_flag; case sGssStrictAcceptor: intptr = &options->gss_strict_acceptor; goto parse_flag; case sPasswordAuthentication: intptr = &options->password_authentication; goto parse_flag; case sKbdInteractiveAuthentication: intptr = &options->kbd_interactive_authentication; goto parse_flag; case sChallengeResponseAuthentication: intptr = &options->challenge_response_authentication; goto parse_flag; case sPrintMotd: intptr = &options->print_motd; goto parse_flag; case sPrintLastLog: intptr = &options->print_lastlog; goto parse_flag; case sX11Forwarding: intptr = &options->x11_forwarding; goto parse_flag; case sX11DisplayOffset: intptr = &options->x11_display_offset; parse_int: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing integer value.", filename, linenum); value = atoi(arg); if (*activep && *intptr == -1) *intptr = value; break; case sX11UseLocalhost: intptr = &options->x11_use_localhost; goto parse_flag; case sXAuthLocation: charptr = &options->xauth_location; goto parse_filename; case sPermitTTY: intptr = &options->permit_tty; goto parse_flag; case sPermitUserRC: intptr = &options->permit_user_rc; goto parse_flag; case sStrictModes: intptr = &options->strict_modes; goto parse_flag; case sTCPKeepAlive: intptr = &options->tcp_keep_alive; goto parse_flag; case sEmptyPasswd: intptr = &options->permit_empty_passwd; goto parse_flag; case sPermitUserEnvironment: intptr = &options->permit_user_env; goto parse_flag; case sCompression: intptr = &options->compression; multistate_ptr = multistate_compression; goto parse_multistate; case sRekeyLimit: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); if (strcmp(arg, "default") == 0) { val64 = 0; } else { if (scan_scaled(arg, &val64) == -1) fatal("%.200s line %d: Bad number '%s': %s", filename, linenum, arg, strerror(errno)); if (val64 != 0 && val64 < 16) fatal("%.200s line %d: RekeyLimit too small", filename, linenum); } if (*activep && options->rekey_limit == -1) options->rekey_limit = val64; if (cp != NULL) { /* optional rekey interval present */ if (strcmp(cp, "none") == 0) { (void)strdelim(&cp); /* discard */ break; } intptr = &options->rekey_interval; goto parse_time; } break; case sGatewayPorts: intptr = &options->fwd_opts.gateway_ports; multistate_ptr = multistate_gatewayports; goto parse_multistate; case sUseDNS: intptr = &options->use_dns; goto parse_flag; case sLogFacility: log_facility_ptr = &options->log_facility; arg = strdelim(&cp); value = log_facility_number(arg); if (value == SYSLOG_FACILITY_NOT_SET) fatal("%.200s line %d: unsupported log facility '%s'", filename, linenum, arg ? arg : ""); if (*log_facility_ptr == -1) *log_facility_ptr = (SyslogFacility) value; break; case sLogLevel: log_level_ptr = &options->log_level; arg = strdelim(&cp); value = log_level_number(arg); if (value == SYSLOG_LEVEL_NOT_SET) fatal("%.200s line %d: unsupported log level '%s'", filename, linenum, arg ? arg : ""); if (*log_level_ptr == -1) *log_level_ptr = (LogLevel) value; break; case sAllowTcpForwarding: intptr = &options->allow_tcp_forwarding; multistate_ptr = multistate_tcpfwd; goto parse_multistate; case sAllowStreamLocalForwarding: intptr = &options->allow_streamlocal_forwarding; multistate_ptr = multistate_tcpfwd; goto parse_multistate; case sAllowAgentForwarding: intptr = &options->allow_agent_forwarding; goto parse_flag; case sDisableForwarding: intptr = &options->disable_forwarding; goto parse_flag; - case sUsePrivilegeSeparation: - intptr = &use_privsep; - multistate_ptr = multistate_privsep; - goto parse_multistate; - case sAllowUsers: while ((arg = strdelim(&cp)) && *arg != '\0') { if (options->num_allow_users >= MAX_ALLOW_USERS) fatal("%s line %d: too many allow users.", filename, linenum); if (match_user(NULL, NULL, NULL, arg) == -1) fatal("%s line %d: invalid AllowUsers pattern: " "\"%.100s\"", filename, linenum, arg); if (!*activep) continue; options->allow_users[options->num_allow_users++] = xstrdup(arg); } break; case sDenyUsers: while ((arg = strdelim(&cp)) && *arg != '\0') { if (options->num_deny_users >= MAX_DENY_USERS) fatal("%s line %d: too many deny users.", filename, linenum); if (match_user(NULL, NULL, NULL, arg) == -1) fatal("%s line %d: invalid DenyUsers pattern: " "\"%.100s\"", filename, linenum, arg); if (!*activep) continue; options->deny_users[options->num_deny_users++] = xstrdup(arg); } break; case sAllowGroups: while ((arg = strdelim(&cp)) && *arg != '\0') { if (options->num_allow_groups >= MAX_ALLOW_GROUPS) fatal("%s line %d: too many allow groups.", filename, linenum); if (!*activep) continue; options->allow_groups[options->num_allow_groups++] = xstrdup(arg); } break; case sDenyGroups: while ((arg = strdelim(&cp)) && *arg != '\0') { if (options->num_deny_groups >= MAX_DENY_GROUPS) fatal("%s line %d: too many deny groups.", filename, linenum); if (!*activep) continue; options->deny_groups[options->num_deny_groups++] = xstrdup(arg); } break; case sCiphers: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: Missing argument.", filename, linenum); - if (!ciphers_valid(*arg == '+' ? arg + 1 : arg)) + if (*arg != '-' && !ciphers_valid(*arg == '+' ? arg + 1 : arg)) fatal("%s line %d: Bad SSH2 cipher spec '%s'.", filename, linenum, arg ? arg : ""); if (options->ciphers == NULL) options->ciphers = xstrdup(arg); break; case sMacs: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: Missing argument.", filename, linenum); - if (!mac_valid(*arg == '+' ? arg + 1 : arg)) + if (*arg != '-' && !mac_valid(*arg == '+' ? arg + 1 : arg)) fatal("%s line %d: Bad SSH2 mac spec '%s'.", filename, linenum, arg ? arg : ""); if (options->macs == NULL) options->macs = xstrdup(arg); break; case sKexAlgorithms: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: Missing argument.", filename, linenum); - if (!kex_names_valid(*arg == '+' ? arg + 1 : arg)) + if (*arg != '-' && + !kex_names_valid(*arg == '+' ? arg + 1 : arg)) fatal("%s line %d: Bad SSH2 KexAlgorithms '%s'.", filename, linenum, arg ? arg : ""); if (options->kex_algorithms == NULL) options->kex_algorithms = xstrdup(arg); break; case sSubsystem: if (options->num_subsystems >= MAX_SUBSYSTEMS) { fatal("%s line %d: too many subsystems defined.", filename, linenum); } arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: Missing subsystem name.", filename, linenum); if (!*activep) { arg = strdelim(&cp); break; } for (i = 0; i < options->num_subsystems; i++) if (strcmp(arg, options->subsystem_name[i]) == 0) fatal("%s line %d: Subsystem '%s' already defined.", filename, linenum, arg); options->subsystem_name[options->num_subsystems] = xstrdup(arg); arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: Missing subsystem command.", filename, linenum); options->subsystem_command[options->num_subsystems] = xstrdup(arg); /* Collect arguments (separate to executable) */ p = xstrdup(arg); len = strlen(p) + 1; while ((arg = strdelim(&cp)) != NULL && *arg != '\0') { len += 1 + strlen(arg); p = xreallocarray(p, 1, len); strlcat(p, " ", len); strlcat(p, arg, len); } options->subsystem_args[options->num_subsystems] = p; options->num_subsystems++; break; case sMaxStartups: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: Missing MaxStartups spec.", filename, linenum); if ((n = sscanf(arg, "%d:%d:%d", &options->max_startups_begin, &options->max_startups_rate, &options->max_startups)) == 3) { if (options->max_startups_begin > options->max_startups || options->max_startups_rate > 100 || options->max_startups_rate < 1) fatal("%s line %d: Illegal MaxStartups spec.", filename, linenum); } else if (n != 1) fatal("%s line %d: Illegal MaxStartups spec.", filename, linenum); else options->max_startups = options->max_startups_begin; break; case sMaxAuthTries: intptr = &options->max_authtries; goto parse_int; case sMaxSessions: intptr = &options->max_sessions; goto parse_int; case sBanner: charptr = &options->banner; goto parse_filename; /* * These options can contain %X options expanded at * connect time, so that you can specify paths like: * * AuthorizedKeysFile /etc/ssh_keys/%u */ case sAuthorizedKeysFile: if (*activep && options->num_authkeys_files == 0) { while ((arg = strdelim(&cp)) && *arg != '\0') { if (options->num_authkeys_files >= MAX_AUTHKEYS_FILES) fatal("%s line %d: " "too many authorized keys files.", filename, linenum); options->authorized_keys_files[ options->num_authkeys_files++] = tilde_expand_filename(arg, getuid()); } } return 0; case sAuthorizedPrincipalsFile: charptr = &options->authorized_principals_file; arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing file name.", filename, linenum); if (*activep && *charptr == NULL) { *charptr = tilde_expand_filename(arg, getuid()); /* increase optional counter */ if (intptr != NULL) *intptr = *intptr + 1; } break; case sClientAliveInterval: intptr = &options->client_alive_interval; goto parse_time; case sClientAliveCountMax: intptr = &options->client_alive_count_max; goto parse_int; case sAcceptEnv: while ((arg = strdelim(&cp)) && *arg != '\0') { if (strchr(arg, '=') != NULL) fatal("%s line %d: Invalid environment name.", filename, linenum); if (options->num_accept_env >= MAX_ACCEPT_ENV) fatal("%s line %d: too many allow env.", filename, linenum); if (!*activep) continue; options->accept_env[options->num_accept_env++] = xstrdup(arg); } break; case sPermitTunnel: intptr = &options->permit_tun; arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: Missing yes/point-to-point/" "ethernet/no argument.", filename, linenum); value = -1; for (i = 0; tunmode_desc[i].val != -1; i++) if (strcmp(tunmode_desc[i].text, arg) == 0) { value = tunmode_desc[i].val; break; } if (value == -1) fatal("%s line %d: Bad yes/point-to-point/ethernet/" "no argument: %s", filename, linenum, arg); if (*activep && *intptr == -1) *intptr = value; break; case sMatch: if (cmdline) fatal("Match directive not supported as a command-line " "option"); value = match_cfg_line(&cp, linenum, connectinfo); if (value < 0) fatal("%s line %d: Bad Match condition", filename, linenum); *activep = value; break; case sPermitOpen: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing PermitOpen specification", filename, linenum); n = options->num_permitted_opens; /* modified later */ if (strcmp(arg, "any") == 0) { if (*activep && n == -1) { channel_clear_adm_permitted_opens(); options->num_permitted_opens = 0; } break; } if (strcmp(arg, "none") == 0) { if (*activep && n == -1) { options->num_permitted_opens = 1; channel_disable_adm_local_opens(); } break; } if (*activep && n == -1) channel_clear_adm_permitted_opens(); for (; arg != NULL && *arg != '\0'; arg = strdelim(&cp)) { p = hpdelim(&arg); if (p == NULL) fatal("%s line %d: missing host in PermitOpen", filename, linenum); p = cleanhostname(p); if (arg == NULL || ((port = permitopen_port(arg)) < 0)) fatal("%s line %d: bad port number in " "PermitOpen", filename, linenum); if (*activep && n == -1) options->num_permitted_opens = channel_add_adm_permitted_opens(p, port); } break; case sForceCommand: if (cp == NULL || *cp == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); len = strspn(cp, WHITESPACE); if (*activep && options->adm_forced_command == NULL) options->adm_forced_command = xstrdup(cp + len); return 0; case sChrootDirectory: charptr = &options->chroot_directory; arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing file name.", filename, linenum); if (*activep && *charptr == NULL) *charptr = xstrdup(arg); break; case sTrustedUserCAKeys: charptr = &options->trusted_user_ca_keys; goto parse_filename; case sRevokedKeys: charptr = &options->revoked_keys_file; goto parse_filename; case sIPQoS: arg = strdelim(&cp); if ((value = parse_ipqos(arg)) == -1) fatal("%s line %d: Bad IPQoS value: %s", filename, linenum, arg); arg = strdelim(&cp); if (arg == NULL) value2 = value; else if ((value2 = parse_ipqos(arg)) == -1) fatal("%s line %d: Bad IPQoS value: %s", filename, linenum, arg); if (*activep) { options->ip_qos_interactive = value; options->ip_qos_bulk = value2; } break; case sVersionAddendum: if (cp == NULL || *cp == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); len = strspn(cp, WHITESPACE); if (*activep && options->version_addendum == NULL) { if (strcasecmp(cp + len, "none") == 0) options->version_addendum = xstrdup(""); else if (strchr(cp + len, '\r') != NULL) fatal("%.200s line %d: Invalid argument", filename, linenum); else options->version_addendum = xstrdup(cp + len); } return 0; case sAuthorizedKeysCommand: if (cp == NULL) fatal("%.200s line %d: Missing argument.", filename, linenum); len = strspn(cp, WHITESPACE); if (*activep && options->authorized_keys_command == NULL) { if (cp[len] != '/' && strcasecmp(cp + len, "none") != 0) fatal("%.200s line %d: AuthorizedKeysCommand " "must be an absolute path", filename, linenum); options->authorized_keys_command = xstrdup(cp + len); } return 0; case sAuthorizedKeysCommandUser: charptr = &options->authorized_keys_command_user; arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing AuthorizedKeysCommandUser " "argument.", filename, linenum); if (*activep && *charptr == NULL) *charptr = xstrdup(arg); break; case sAuthorizedPrincipalsCommand: if (cp == NULL) fatal("%.200s line %d: Missing argument.", filename, linenum); len = strspn(cp, WHITESPACE); if (*activep && options->authorized_principals_command == NULL) { if (cp[len] != '/' && strcasecmp(cp + len, "none") != 0) fatal("%.200s line %d: " "AuthorizedPrincipalsCommand must be " "an absolute path", filename, linenum); options->authorized_principals_command = xstrdup(cp + len); } return 0; case sAuthorizedPrincipalsCommandUser: charptr = &options->authorized_principals_command_user; arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing " "AuthorizedPrincipalsCommandUser argument.", filename, linenum); if (*activep && *charptr == NULL) *charptr = xstrdup(arg); break; case sAuthenticationMethods: if (options->num_auth_methods == 0) { value = 0; /* seen "any" pseudo-method */ value2 = 0; /* sucessfully parsed any method */ while ((arg = strdelim(&cp)) && *arg != '\0') { if (options->num_auth_methods >= MAX_AUTH_METHODS) fatal("%s line %d: " "too many authentication methods.", filename, linenum); if (strcmp(arg, "any") == 0) { if (options->num_auth_methods > 0) { fatal("%s line %d: \"any\" " "must appear alone in " "AuthenticationMethods", filename, linenum); } value = 1; } else if (value) { fatal("%s line %d: \"any\" must appear " "alone in AuthenticationMethods", filename, linenum); } else if (auth2_methods_valid(arg, 0) != 0) { fatal("%s line %d: invalid " "authentication method list.", filename, linenum); } value2 = 1; if (!*activep) continue; options->auth_methods[ options->num_auth_methods++] = xstrdup(arg); } if (value2 == 0) { fatal("%s line %d: no AuthenticationMethods " "specified", filename, linenum); } } return 0; case sStreamLocalBindMask: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%s line %d: missing StreamLocalBindMask " "argument.", filename, linenum); /* Parse mode in octal format */ value = strtol(arg, &p, 8); if (arg == p || value < 0 || value > 0777) fatal("%s line %d: Bad mask.", filename, linenum); if (*activep) options->fwd_opts.streamlocal_bind_mask = (mode_t)value; break; case sStreamLocalBindUnlink: intptr = &options->fwd_opts.streamlocal_bind_unlink; goto parse_flag; case sFingerprintHash: arg = strdelim(&cp); if (!arg || *arg == '\0') fatal("%.200s line %d: Missing argument.", filename, linenum); if ((value = ssh_digest_alg_by_name(arg)) == -1) fatal("%.200s line %d: Invalid hash algorithm \"%s\".", filename, linenum, arg); if (*activep) options->fingerprint_hash = value; break; case sUseBlacklist: intptr = &options->use_blacklist; goto parse_flag; case sDeprecated: case sIgnore: case sUnsupported: do_log2(opcode == sIgnore ? SYSLOG_LEVEL_DEBUG2 : SYSLOG_LEVEL_INFO, "%s line %d: %s option %s", filename, linenum, opcode == sUnsupported ? "Unsupported" : "Deprecated", arg); while (arg) arg = strdelim(&cp); break; default: fatal("%s line %d: Missing handler for opcode %s (%d)", filename, linenum, arg, opcode); } if ((arg = strdelim(&cp)) != NULL && *arg != '\0') fatal("%s line %d: garbage at end of line; \"%.200s\".", filename, linenum, arg); return 0; } /* Reads the server configuration file. */ void load_server_config(const char *filename, Buffer *conf) { char line[4096], *cp; FILE *f; int lineno = 0; debug2("%s: filename %s", __func__, filename); if ((f = fopen(filename, "r")) == NULL) { perror(filename); exit(1); } buffer_clear(conf); while (fgets(line, sizeof(line), f)) { lineno++; if (strlen(line) == sizeof(line) - 1) fatal("%s line %d too long", filename, lineno); /* * Trim out comments and strip whitespace * NB - preserve newlines, they are needed to reproduce * line numbers later for error messages */ if ((cp = strchr(line, '#')) != NULL) memcpy(cp, "\n", 2); cp = line + strspn(line, " \t\r"); buffer_append(conf, cp, strlen(cp)); } buffer_append(conf, "\0", 1); fclose(f); debug2("%s: done config len = %d", __func__, buffer_len(conf)); } void parse_server_match_config(ServerOptions *options, struct connection_info *connectinfo) { ServerOptions mo; initialize_server_options(&mo); parse_server_config(&mo, "reprocess config", &cfg, connectinfo); copy_set_server_options(options, &mo, 0); } int parse_server_match_testspec(struct connection_info *ci, char *spec) { char *p; while ((p = strsep(&spec, ",")) && *p != '\0') { if (strncmp(p, "addr=", 5) == 0) { ci->address = xstrdup(p + 5); } else if (strncmp(p, "host=", 5) == 0) { ci->host = xstrdup(p + 5); } else if (strncmp(p, "user=", 5) == 0) { ci->user = xstrdup(p + 5); } else if (strncmp(p, "laddr=", 6) == 0) { ci->laddress = xstrdup(p + 6); } else if (strncmp(p, "lport=", 6) == 0) { ci->lport = a2port(p + 6); if (ci->lport == -1) { fprintf(stderr, "Invalid port '%s' in test mode" " specification %s\n", p+6, p); return -1; } } else { fprintf(stderr, "Invalid test mode specification %s\n", p); return -1; } } return 0; } /* * returns 1 for a complete spec, 0 for partial spec and -1 for an * empty spec. */ int server_match_spec_complete(struct connection_info *ci) { if (ci->user && ci->host && ci->address) return 1; /* complete */ if (!ci->user && !ci->host && !ci->address) return -1; /* empty */ return 0; /* partial */ } /* * Copy any supported values that are set. * * If the preauth flag is set, we do not bother copying the string or * array values that are not used pre-authentication, because any that we * do use must be explictly sent in mm_getpwnamallow(). */ void copy_set_server_options(ServerOptions *dst, ServerOptions *src, int preauth) { #define M_CP_INTOPT(n) do {\ if (src->n != -1) \ dst->n = src->n; \ } while (0) M_CP_INTOPT(password_authentication); M_CP_INTOPT(gss_authentication); M_CP_INTOPT(pubkey_authentication); M_CP_INTOPT(kerberos_authentication); M_CP_INTOPT(hostbased_authentication); M_CP_INTOPT(hostbased_uses_name_from_packet_only); M_CP_INTOPT(kbd_interactive_authentication); M_CP_INTOPT(permit_root_login); M_CP_INTOPT(permit_empty_passwd); M_CP_INTOPT(allow_tcp_forwarding); M_CP_INTOPT(allow_streamlocal_forwarding); M_CP_INTOPT(allow_agent_forwarding); M_CP_INTOPT(disable_forwarding); M_CP_INTOPT(permit_tun); M_CP_INTOPT(fwd_opts.gateway_ports); M_CP_INTOPT(fwd_opts.streamlocal_bind_unlink); M_CP_INTOPT(x11_display_offset); M_CP_INTOPT(x11_forwarding); M_CP_INTOPT(x11_use_localhost); M_CP_INTOPT(permit_tty); M_CP_INTOPT(permit_user_rc); M_CP_INTOPT(max_sessions); M_CP_INTOPT(max_authtries); M_CP_INTOPT(client_alive_count_max); M_CP_INTOPT(client_alive_interval); M_CP_INTOPT(ip_qos_interactive); M_CP_INTOPT(ip_qos_bulk); M_CP_INTOPT(rekey_limit); M_CP_INTOPT(rekey_interval); /* * The bind_mask is a mode_t that may be unsigned, so we can't use * M_CP_INTOPT - it does a signed comparison that causes compiler * warnings. */ if (src->fwd_opts.streamlocal_bind_mask != (mode_t)-1) { dst->fwd_opts.streamlocal_bind_mask = src->fwd_opts.streamlocal_bind_mask; } /* M_CP_STROPT and M_CP_STRARRAYOPT should not appear before here */ #define M_CP_STROPT(n) do {\ if (src->n != NULL && dst->n != src->n) { \ free(dst->n); \ dst->n = src->n; \ } \ } while(0) #define M_CP_STRARRAYOPT(n, num_n) do {\ if (src->num_n != 0) { \ for (dst->num_n = 0; dst->num_n < src->num_n; dst->num_n++) \ dst->n[dst->num_n] = xstrdup(src->n[dst->num_n]); \ } \ } while(0) /* See comment in servconf.h */ COPY_MATCH_STRING_OPTS(); /* Arguments that accept '+...' need to be expanded */ assemble_algorithms(dst); /* * The only things that should be below this point are string options * which are only used after authentication. */ if (preauth) return; /* These options may be "none" to clear a global setting */ M_CP_STROPT(adm_forced_command); if (option_clear_or_none(dst->adm_forced_command)) { free(dst->adm_forced_command); dst->adm_forced_command = NULL; } M_CP_STROPT(chroot_directory); if (option_clear_or_none(dst->chroot_directory)) { free(dst->chroot_directory); dst->chroot_directory = NULL; } } #undef M_CP_INTOPT #undef M_CP_STROPT #undef M_CP_STRARRAYOPT void parse_server_config(ServerOptions *options, const char *filename, Buffer *conf, struct connection_info *connectinfo) { int active, linenum, bad_options = 0; char *cp, *obuf, *cbuf; debug2("%s: config %s len %d", __func__, filename, buffer_len(conf)); if ((obuf = cbuf = sshbuf_dup_string(conf)) == NULL) fatal("%s: sshbuf_dup_string failed", __func__); active = connectinfo ? 0 : 1; linenum = 1; while ((cp = strsep(&cbuf, "\n")) != NULL) { if (process_server_config_line(options, cp, filename, linenum++, &active, connectinfo) != 0) bad_options++; } free(obuf); if (bad_options > 0) fatal("%s: terminating, %d bad configuration options", filename, bad_options); process_queued_listen_addrs(options); } static const char * fmt_multistate_int(int val, const struct multistate *m) { u_int i; for (i = 0; m[i].key != NULL; i++) { if (m[i].value == val) return m[i].key; } return "UNKNOWN"; } static const char * fmt_intarg(ServerOpCodes code, int val) { if (val == -1) return "unset"; switch (code) { case sAddressFamily: return fmt_multistate_int(val, multistate_addressfamily); case sPermitRootLogin: return fmt_multistate_int(val, multistate_permitrootlogin); case sGatewayPorts: return fmt_multistate_int(val, multistate_gatewayports); case sCompression: return fmt_multistate_int(val, multistate_compression); - case sUsePrivilegeSeparation: - return fmt_multistate_int(val, multistate_privsep); case sAllowTcpForwarding: return fmt_multistate_int(val, multistate_tcpfwd); case sAllowStreamLocalForwarding: return fmt_multistate_int(val, multistate_tcpfwd); case sFingerprintHash: return ssh_digest_alg_name(val); default: switch (val) { case 0: return "no"; case 1: return "yes"; default: return "UNKNOWN"; } } } static const char * lookup_opcode_name(ServerOpCodes code) { u_int i; for (i = 0; keywords[i].name != NULL; i++) if (keywords[i].opcode == code) return(keywords[i].name); return "UNKNOWN"; } static void dump_cfg_int(ServerOpCodes code, int val) { printf("%s %d\n", lookup_opcode_name(code), val); } static void dump_cfg_oct(ServerOpCodes code, int val) { printf("%s 0%o\n", lookup_opcode_name(code), val); } static void dump_cfg_fmtint(ServerOpCodes code, int val) { printf("%s %s\n", lookup_opcode_name(code), fmt_intarg(code, val)); } static void dump_cfg_string(ServerOpCodes code, const char *val) { - if (val == NULL) - return; printf("%s %s\n", lookup_opcode_name(code), val == NULL ? "none" : val); } static void dump_cfg_strarray(ServerOpCodes code, u_int count, char **vals) { u_int i; for (i = 0; i < count; i++) printf("%s %s\n", lookup_opcode_name(code), vals[i]); } static void dump_cfg_strarray_oneline(ServerOpCodes code, u_int count, char **vals) { u_int i; if (count <= 0 && code != sAuthenticationMethods) return; printf("%s", lookup_opcode_name(code)); for (i = 0; i < count; i++) printf(" %s", vals[i]); if (code == sAuthenticationMethods && count == 0) printf(" any"); printf("\n"); } void dump_config(ServerOptions *o) { u_int i; int ret; struct addrinfo *ai; char addr[NI_MAXHOST], port[NI_MAXSERV], *s = NULL; char *laddr1 = xstrdup(""), *laddr2 = NULL; /* these are usually at the top of the config */ for (i = 0; i < o->num_ports; i++) printf("port %d\n", o->ports[i]); dump_cfg_fmtint(sAddressFamily, o->address_family); /* * ListenAddress must be after Port. add_one_listen_addr pushes * addresses onto a stack, so to maintain ordering we need to * print these in reverse order. */ for (ai = o->listen_addrs; ai; ai = ai->ai_next) { if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, addr, sizeof(addr), port, sizeof(port), NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { error("getnameinfo failed: %.100s", (ret != EAI_SYSTEM) ? gai_strerror(ret) : strerror(errno)); } else { laddr2 = laddr1; if (ai->ai_family == AF_INET6) xasprintf(&laddr1, "listenaddress [%s]:%s\n%s", addr, port, laddr2); else xasprintf(&laddr1, "listenaddress %s:%s\n%s", addr, port, laddr2); free(laddr2); } } printf("%s", laddr1); free(laddr1); /* integer arguments */ #ifdef USE_PAM dump_cfg_fmtint(sUsePAM, o->use_pam); #endif dump_cfg_int(sLoginGraceTime, o->login_grace_time); dump_cfg_int(sX11DisplayOffset, o->x11_display_offset); dump_cfg_int(sMaxAuthTries, o->max_authtries); dump_cfg_int(sMaxSessions, o->max_sessions); dump_cfg_int(sClientAliveInterval, o->client_alive_interval); dump_cfg_int(sClientAliveCountMax, o->client_alive_count_max); dump_cfg_oct(sStreamLocalBindMask, o->fwd_opts.streamlocal_bind_mask); /* formatted integer arguments */ dump_cfg_fmtint(sPermitRootLogin, o->permit_root_login); dump_cfg_fmtint(sIgnoreRhosts, o->ignore_rhosts); dump_cfg_fmtint(sIgnoreUserKnownHosts, o->ignore_user_known_hosts); dump_cfg_fmtint(sHostbasedAuthentication, o->hostbased_authentication); dump_cfg_fmtint(sHostbasedUsesNameFromPacketOnly, o->hostbased_uses_name_from_packet_only); dump_cfg_fmtint(sPubkeyAuthentication, o->pubkey_authentication); #ifdef KRB5 dump_cfg_fmtint(sKerberosAuthentication, o->kerberos_authentication); dump_cfg_fmtint(sKerberosOrLocalPasswd, o->kerberos_or_local_passwd); dump_cfg_fmtint(sKerberosTicketCleanup, o->kerberos_ticket_cleanup); # ifdef USE_AFS dump_cfg_fmtint(sKerberosGetAFSToken, o->kerberos_get_afs_token); # endif #endif #ifdef GSSAPI dump_cfg_fmtint(sGssAuthentication, o->gss_authentication); dump_cfg_fmtint(sGssCleanupCreds, o->gss_cleanup_creds); #endif dump_cfg_fmtint(sPasswordAuthentication, o->password_authentication); dump_cfg_fmtint(sKbdInteractiveAuthentication, o->kbd_interactive_authentication); dump_cfg_fmtint(sChallengeResponseAuthentication, o->challenge_response_authentication); dump_cfg_fmtint(sPrintMotd, o->print_motd); #ifndef DISABLE_LASTLOG dump_cfg_fmtint(sPrintLastLog, o->print_lastlog); #endif dump_cfg_fmtint(sX11Forwarding, o->x11_forwarding); dump_cfg_fmtint(sX11UseLocalhost, o->x11_use_localhost); dump_cfg_fmtint(sPermitTTY, o->permit_tty); dump_cfg_fmtint(sPermitUserRC, o->permit_user_rc); dump_cfg_fmtint(sStrictModes, o->strict_modes); dump_cfg_fmtint(sTCPKeepAlive, o->tcp_keep_alive); dump_cfg_fmtint(sEmptyPasswd, o->permit_empty_passwd); dump_cfg_fmtint(sPermitUserEnvironment, o->permit_user_env); dump_cfg_fmtint(sCompression, o->compression); dump_cfg_fmtint(sGatewayPorts, o->fwd_opts.gateway_ports); dump_cfg_fmtint(sUseDNS, o->use_dns); dump_cfg_fmtint(sAllowTcpForwarding, o->allow_tcp_forwarding); dump_cfg_fmtint(sAllowAgentForwarding, o->allow_agent_forwarding); dump_cfg_fmtint(sDisableForwarding, o->disable_forwarding); dump_cfg_fmtint(sAllowStreamLocalForwarding, o->allow_streamlocal_forwarding); dump_cfg_fmtint(sStreamLocalBindUnlink, o->fwd_opts.streamlocal_bind_unlink); - dump_cfg_fmtint(sUsePrivilegeSeparation, use_privsep); dump_cfg_fmtint(sFingerprintHash, o->fingerprint_hash); dump_cfg_fmtint(sUseBlacklist, o->use_blacklist); /* string arguments */ dump_cfg_string(sPidFile, o->pid_file); dump_cfg_string(sXAuthLocation, o->xauth_location); dump_cfg_string(sCiphers, o->ciphers ? o->ciphers : KEX_SERVER_ENCRYPT); dump_cfg_string(sMacs, o->macs ? o->macs : KEX_SERVER_MAC); dump_cfg_string(sBanner, o->banner); dump_cfg_string(sForceCommand, o->adm_forced_command); dump_cfg_string(sChrootDirectory, o->chroot_directory); dump_cfg_string(sTrustedUserCAKeys, o->trusted_user_ca_keys); dump_cfg_string(sRevokedKeys, o->revoked_keys_file); dump_cfg_string(sAuthorizedPrincipalsFile, o->authorized_principals_file); dump_cfg_string(sVersionAddendum, *o->version_addendum == '\0' ? "none" : o->version_addendum); dump_cfg_string(sAuthorizedKeysCommand, o->authorized_keys_command); dump_cfg_string(sAuthorizedKeysCommandUser, o->authorized_keys_command_user); dump_cfg_string(sAuthorizedPrincipalsCommand, o->authorized_principals_command); dump_cfg_string(sAuthorizedPrincipalsCommandUser, o->authorized_principals_command_user); dump_cfg_string(sHostKeyAgent, o->host_key_agent); dump_cfg_string(sKexAlgorithms, o->kex_algorithms ? o->kex_algorithms : KEX_SERVER_KEX); dump_cfg_string(sHostbasedAcceptedKeyTypes, o->hostbased_key_types ? o->hostbased_key_types : KEX_DEFAULT_PK_ALG); dump_cfg_string(sHostKeyAlgorithms, o->hostkeyalgorithms ? o->hostkeyalgorithms : KEX_DEFAULT_PK_ALG); dump_cfg_string(sPubkeyAcceptedKeyTypes, o->pubkey_key_types ? o->pubkey_key_types : KEX_DEFAULT_PK_ALG); /* string arguments requiring a lookup */ dump_cfg_string(sLogLevel, log_level_name(o->log_level)); dump_cfg_string(sLogFacility, log_facility_name(o->log_facility)); /* string array arguments */ dump_cfg_strarray_oneline(sAuthorizedKeysFile, o->num_authkeys_files, o->authorized_keys_files); dump_cfg_strarray(sHostKeyFile, o->num_host_key_files, o->host_key_files); dump_cfg_strarray(sHostCertificate, o->num_host_cert_files, o->host_cert_files); dump_cfg_strarray(sAllowUsers, o->num_allow_users, o->allow_users); dump_cfg_strarray(sDenyUsers, o->num_deny_users, o->deny_users); dump_cfg_strarray(sAllowGroups, o->num_allow_groups, o->allow_groups); dump_cfg_strarray(sDenyGroups, o->num_deny_groups, o->deny_groups); dump_cfg_strarray(sAcceptEnv, o->num_accept_env, o->accept_env); dump_cfg_strarray_oneline(sAuthenticationMethods, o->num_auth_methods, o->auth_methods); /* other arguments */ for (i = 0; i < o->num_subsystems; i++) printf("subsystem %s %s\n", o->subsystem_name[i], o->subsystem_args[i]); printf("maxstartups %d:%d:%d\n", o->max_startups_begin, o->max_startups_rate, o->max_startups); for (i = 0; tunmode_desc[i].val != -1; i++) if (tunmode_desc[i].val == o->permit_tun) { s = tunmode_desc[i].text; break; } dump_cfg_string(sPermitTunnel, s); printf("ipqos %s ", iptos2str(o->ip_qos_interactive)); printf("%s\n", iptos2str(o->ip_qos_bulk)); printf("rekeylimit %llu %d\n", (unsigned long long)o->rekey_limit, o->rekey_interval); channel_print_adm_permitted_opens(); } diff --git a/crypto/openssh/serverloop.c b/crypto/openssh/serverloop.c index c4e4699da68c..2976f55943b4 100644 --- a/crypto/openssh/serverloop.c +++ b/crypto/openssh/serverloop.c @@ -1,859 +1,867 @@ -/* $OpenBSD: serverloop.c,v 1.189 2016/12/14 00:36:34 djm Exp $ */ +/* $OpenBSD: serverloop.c,v 1.191 2017/02/01 02:59:09 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Server main loop for handling the interactive session. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * SSH2 support by Markus Friedl. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include #include #include #include #include #include #include #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "packet.h" #include "buffer.h" #include "log.h" #include "misc.h" #include "servconf.h" #include "canohost.h" #include "sshpty.h" #include "channels.h" #include "compat.h" #include "ssh2.h" #include "key.h" #include "cipher.h" #include "kex.h" #include "hostfile.h" #include "auth.h" #include "session.h" #include "dispatch.h" #include "auth-options.h" #include "serverloop.h" #include "ssherr.h" extern ServerOptions options; /* XXX */ extern Authctxt *the_authctxt; extern int use_privsep; static int no_more_sessions = 0; /* Disallow further sessions. */ /* * This SIGCHLD kludge is used to detect when the child exits. The server * will exit after that, as soon as forwarded connections have terminated. */ static volatile sig_atomic_t child_terminated = 0; /* The child has terminated. */ /* Cleanup on signals (!use_privsep case only) */ static volatile sig_atomic_t received_sigterm = 0; /* prototypes */ static void server_init_dispatch(void); /* * we write to this pipe if a SIGCHLD is caught in order to avoid * the race between select() and child_terminated */ static int notify_pipe[2]; static void notify_setup(void) { if (pipe(notify_pipe) < 0) { error("pipe(notify_pipe) failed %s", strerror(errno)); } else if ((fcntl(notify_pipe[0], F_SETFD, FD_CLOEXEC) == -1) || (fcntl(notify_pipe[1], F_SETFD, FD_CLOEXEC) == -1)) { error("fcntl(notify_pipe, F_SETFD) failed %s", strerror(errno)); close(notify_pipe[0]); close(notify_pipe[1]); } else { set_nonblock(notify_pipe[0]); set_nonblock(notify_pipe[1]); return; } notify_pipe[0] = -1; /* read end */ notify_pipe[1] = -1; /* write end */ } static void notify_parent(void) { if (notify_pipe[1] != -1) (void)write(notify_pipe[1], "", 1); } static void notify_prepare(fd_set *readset) { if (notify_pipe[0] != -1) FD_SET(notify_pipe[0], readset); } static void notify_done(fd_set *readset) { char c; if (notify_pipe[0] != -1 && FD_ISSET(notify_pipe[0], readset)) while (read(notify_pipe[0], &c, 1) != -1) debug2("notify_done: reading"); } /*ARGSUSED*/ static void sigchld_handler(int sig) { int save_errno = errno; child_terminated = 1; #ifndef _UNICOS mysignal(SIGCHLD, sigchld_handler); #endif notify_parent(); errno = save_errno; } /*ARGSUSED*/ static void sigterm_handler(int sig) { received_sigterm = sig; } static void client_alive_check(void) { int channel_id; /* timeout, check to see how many we have had */ if (packet_inc_alive_timeouts() > options.client_alive_count_max) { logit("Timeout, client not responding."); cleanup_exit(255); } /* * send a bogus global/channel request with "wantreply", * we should get back a failure */ if ((channel_id = channel_find_open()) == -1) { packet_start(SSH2_MSG_GLOBAL_REQUEST); packet_put_cstring("keepalive@openssh.com"); packet_put_char(1); /* boolean: want reply */ } else { channel_request_start(channel_id, "keepalive@openssh.com", 1); } packet_send(); } /* * Sleep in select() until we can do something. This will initialize the * select masks. Upon return, the masks will indicate which descriptors * have data or can accept data. Optionally, a maximum time can be specified * for the duration of the wait (0 = infinite). */ static void wait_until_can_do_something(int connection_in, int connection_out, fd_set **readsetp, fd_set **writesetp, int *maxfdp, u_int *nallocp, u_int64_t max_time_ms) { struct timeval tv, *tvp; int ret; time_t minwait_secs = 0; int client_alive_scheduled = 0; /* Allocate and update select() masks for channel descriptors. */ channel_prepare_select(readsetp, writesetp, maxfdp, nallocp, &minwait_secs, 0); /* XXX need proper deadline system for rekey/client alive */ if (minwait_secs != 0) max_time_ms = MINIMUM(max_time_ms, (u_int)minwait_secs * 1000); /* * if using client_alive, set the max timeout accordingly, * and indicate that this particular timeout was for client * alive by setting the client_alive_scheduled flag. * * this could be randomized somewhat to make traffic * analysis more difficult, but we're not doing it yet. */ if (options.client_alive_interval) { uint64_t keepalive_ms = (uint64_t)options.client_alive_interval * 1000; client_alive_scheduled = 1; if (max_time_ms == 0 || max_time_ms > keepalive_ms) max_time_ms = keepalive_ms; } #if 0 /* wrong: bad condition XXX */ if (channel_not_very_much_buffered_data()) #endif FD_SET(connection_in, *readsetp); notify_prepare(*readsetp); /* * If we have buffered packet data going to the client, mark that * descriptor. */ if (packet_have_data_to_write()) FD_SET(connection_out, *writesetp); /* * If child has terminated and there is enough buffer space to read * from it, then read as much as is available and exit. */ if (child_terminated && packet_not_very_much_data_to_write()) if (max_time_ms == 0 || client_alive_scheduled) max_time_ms = 100; if (max_time_ms == 0) tvp = NULL; else { tv.tv_sec = max_time_ms / 1000; tv.tv_usec = 1000 * (max_time_ms % 1000); tvp = &tv; } /* Wait for something to happen, or the timeout to expire. */ ret = select((*maxfdp)+1, *readsetp, *writesetp, NULL, tvp); if (ret == -1) { memset(*readsetp, 0, *nallocp); memset(*writesetp, 0, *nallocp); if (errno != EINTR) error("select: %.100s", strerror(errno)); } else if (ret == 0 && client_alive_scheduled) client_alive_check(); notify_done(*readsetp); } /* * Processes input from the client and the program. Input data is stored * in buffers and processed later. */ static int process_input(fd_set *readset, int connection_in) { struct ssh *ssh = active_state; /* XXX */ int len; char buf[16384]; /* Read and buffer any input data from the client. */ if (FD_ISSET(connection_in, readset)) { len = read(connection_in, buf, sizeof(buf)); if (len == 0) { verbose("Connection closed by %.100s port %d", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); return -1; } else if (len < 0) { if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) { verbose("Read error from remote host " "%.100s port %d: %.100s", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), strerror(errno)); cleanup_exit(255); } } else { /* Buffer any received data. */ packet_process_incoming(buf, len); } } return 0; } /* * Sends data from internal buffers to client program stdin. */ static void process_output(fd_set *writeset, int connection_out) { /* Send any buffered packet data to the client. */ if (FD_ISSET(connection_out, writeset)) packet_write_poll(); } static void process_buffered_input_packets(void) { dispatch_run(DISPATCH_NONBLOCK, NULL, active_state); } static void collect_children(void) { pid_t pid; sigset_t oset, nset; int status; /* block SIGCHLD while we check for dead children */ sigemptyset(&nset); sigaddset(&nset, SIGCHLD); sigprocmask(SIG_BLOCK, &nset, &oset); if (child_terminated) { debug("Received SIGCHLD."); while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || (pid < 0 && errno == EINTR)) if (pid > 0) session_close_by_pid(pid, status); child_terminated = 0; } sigprocmask(SIG_SETMASK, &oset, NULL); } void server_loop2(Authctxt *authctxt) { fd_set *readset = NULL, *writeset = NULL; int max_fd; u_int nalloc = 0, connection_in, connection_out; u_int64_t rekey_timeout_ms = 0; debug("Entering interactive session for SSH2."); mysignal(SIGCHLD, sigchld_handler); child_terminated = 0; connection_in = packet_get_connection_in(); connection_out = packet_get_connection_out(); if (!use_privsep) { signal(SIGTERM, sigterm_handler); signal(SIGINT, sigterm_handler); signal(SIGQUIT, sigterm_handler); } notify_setup(); max_fd = MAXIMUM(connection_in, connection_out); max_fd = MAXIMUM(max_fd, notify_pipe[0]); server_init_dispatch(); for (;;) { process_buffered_input_packets(); if (!ssh_packet_is_rekeying(active_state) && packet_not_very_much_data_to_write()) channel_output_poll(); if (options.rekey_interval > 0 && !ssh_packet_is_rekeying(active_state)) rekey_timeout_ms = packet_get_rekey_timeout() * 1000; else rekey_timeout_ms = 0; wait_until_can_do_something(connection_in, connection_out, &readset, &writeset, &max_fd, &nalloc, rekey_timeout_ms); if (received_sigterm) { logit("Exiting on signal %d", (int)received_sigterm); /* Clean up sessions, utmp, etc. */ cleanup_exit(255); } collect_children(); if (!ssh_packet_is_rekeying(active_state)) channel_after_select(readset, writeset); if (process_input(readset, connection_in) < 0) break; process_output(writeset, connection_out); } collect_children(); free(readset); free(writeset); /* free all channels, no more reads and writes */ channel_free_all(); /* free remaining sessions, e.g. remove wtmp entries */ session_destroy_all(NULL); } static int server_input_keep_alive(int type, u_int32_t seq, void *ctxt) { debug("Got %d/%u for keepalive", type, seq); /* * reset timeout, since we got a sane answer from the client. * even if this was generated by something other than * the bogus CHANNEL_REQUEST we send for keepalives. */ packet_set_alive_timeouts(0); return 0; } static Channel * -server_request_direct_tcpip(void) +server_request_direct_tcpip(int *reason, const char **errmsg) { Channel *c = NULL; char *target, *originator; u_short target_port, originator_port; target = packet_get_string(NULL); target_port = packet_get_int(); originator = packet_get_string(NULL); originator_port = packet_get_int(); packet_check_eom(); debug("server_request_direct_tcpip: originator %s port %d, target %s " "port %d", originator, originator_port, target, target_port); /* XXX fine grained permissions */ if ((options.allow_tcp_forwarding & FORWARD_LOCAL) != 0 && !no_port_forwarding_flag && !options.disable_forwarding) { c = channel_connect_to_port(target, target_port, - "direct-tcpip", "direct-tcpip"); + "direct-tcpip", "direct-tcpip", reason, errmsg); } else { logit("refused local port forward: " "originator %s port %d, target %s port %d", originator, originator_port, target, target_port); + if (reason != NULL) + *reason = SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED; } free(originator); free(target); return c; } static Channel * server_request_direct_streamlocal(void) { Channel *c = NULL; char *target, *originator; u_short originator_port; + struct passwd *pw = the_authctxt->pw; + + if (pw == NULL || !the_authctxt->valid) + fatal("server_input_global_request: no/invalid user"); target = packet_get_string(NULL); originator = packet_get_string(NULL); originator_port = packet_get_int(); packet_check_eom(); debug("server_request_direct_streamlocal: originator %s port %d, target %s", originator, originator_port, target); /* XXX fine grained permissions */ if ((options.allow_streamlocal_forwarding & FORWARD_LOCAL) != 0 && !no_port_forwarding_flag && !options.disable_forwarding && - use_privsep) { + (pw->pw_uid == 0 || use_privsep)) { c = channel_connect_to_path(target, "direct-streamlocal@openssh.com", "direct-streamlocal"); } else { logit("refused streamlocal port forward: " "originator %s port %d, target %s", originator, originator_port, target); } free(originator); free(target); return c; } static Channel * server_request_tun(void) { Channel *c = NULL; int mode, tun; int sock; mode = packet_get_int(); switch (mode) { case SSH_TUNMODE_POINTOPOINT: case SSH_TUNMODE_ETHERNET: break; default: packet_send_debug("Unsupported tunnel device mode."); return NULL; } if ((options.permit_tun & mode) == 0) { packet_send_debug("Server has rejected tunnel device " "forwarding"); return NULL; } tun = packet_get_int(); if (forced_tun_device != -1) { if (tun != SSH_TUNID_ANY && forced_tun_device != tun) goto done; tun = forced_tun_device; } sock = tun_open(tun, mode); if (sock < 0) goto done; c = channel_new("tun", SSH_CHANNEL_OPEN, sock, sock, -1, CHAN_TCP_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, "tun", 1); c->datagram = 1; #if defined(SSH_TUN_FILTER) if (mode == SSH_TUNMODE_POINTOPOINT) channel_register_filter(c->self, sys_tun_infilter, sys_tun_outfilter, NULL, NULL); #endif done: if (c == NULL) packet_send_debug("Failed to open the tunnel device."); return c; } static Channel * server_request_session(void) { Channel *c; debug("input_session_request"); packet_check_eom(); if (no_more_sessions) { packet_disconnect("Possible attack: attempt to open a session " "after additional sessions disabled"); } /* * A server session has no fd to read or write until a * CHANNEL_REQUEST for a shell is made, so we set the type to * SSH_CHANNEL_LARVAL. Additionally, a callback for handling all * CHANNEL_REQUEST messages is registered. */ c = channel_new("session", SSH_CHANNEL_LARVAL, -1, -1, -1, /*window size*/0, CHAN_SES_PACKET_DEFAULT, 0, "server-session", 1); if (session_open(the_authctxt, c->self) != 1) { debug("session open failed, free channel %d", c->self); channel_free(c); return NULL; } channel_register_cleanup(c->self, session_close_by_channel, 0); return c; } static int server_input_channel_open(int type, u_int32_t seq, void *ctxt) { Channel *c = NULL; char *ctype; - int rchan; + const char *errmsg = NULL; + int rchan, reason = SSH2_OPEN_CONNECT_FAILED; u_int rmaxpack, rwindow, len; ctype = packet_get_string(&len); rchan = packet_get_int(); rwindow = packet_get_int(); rmaxpack = packet_get_int(); debug("server_input_channel_open: ctype %s rchan %d win %d max %d", ctype, rchan, rwindow, rmaxpack); if (strcmp(ctype, "session") == 0) { c = server_request_session(); } else if (strcmp(ctype, "direct-tcpip") == 0) { - c = server_request_direct_tcpip(); + c = server_request_direct_tcpip(&reason, &errmsg); } else if (strcmp(ctype, "direct-streamlocal@openssh.com") == 0) { c = server_request_direct_streamlocal(); } else if (strcmp(ctype, "tun@openssh.com") == 0) { c = server_request_tun(); } if (c != NULL) { debug("server_input_channel_open: confirm %s", ctype); c->remote_id = rchan; c->remote_window = rwindow; c->remote_maxpacket = rmaxpack; if (c->type != SSH_CHANNEL_CONNECTING) { packet_start(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION); packet_put_int(c->remote_id); packet_put_int(c->self); packet_put_int(c->local_window); packet_put_int(c->local_maxpacket); packet_send(); } } else { debug("server_input_channel_open: failure %s", ctype); packet_start(SSH2_MSG_CHANNEL_OPEN_FAILURE); packet_put_int(rchan); - packet_put_int(SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED); + packet_put_int(reason); if (!(datafellows & SSH_BUG_OPENFAILURE)) { - packet_put_cstring("open failed"); + packet_put_cstring(errmsg ? errmsg : "open failed"); packet_put_cstring(""); } packet_send(); } free(ctype); return 0; } static int server_input_hostkeys_prove(struct sshbuf **respp) { struct ssh *ssh = active_state; /* XXX */ struct sshbuf *resp = NULL; struct sshbuf *sigbuf = NULL; struct sshkey *key = NULL, *key_pub = NULL, *key_prv = NULL; int r, ndx, success = 0; const u_char *blob; u_char *sig = 0; size_t blen, slen; if ((resp = sshbuf_new()) == NULL || (sigbuf = sshbuf_new()) == NULL) fatal("%s: sshbuf_new", __func__); while (ssh_packet_remaining(ssh) > 0) { sshkey_free(key); key = NULL; if ((r = sshpkt_get_string_direct(ssh, &blob, &blen)) != 0 || (r = sshkey_from_blob(blob, blen, &key)) != 0) { error("%s: couldn't parse key: %s", __func__, ssh_err(r)); goto out; } /* * Better check that this is actually one of our hostkeys * before attempting to sign anything with it. */ if ((ndx = ssh->kex->host_key_index(key, 1, ssh)) == -1) { error("%s: unknown host %s key", __func__, sshkey_type(key)); goto out; } /* * XXX refactor: make kex->sign just use an index rather * than passing in public and private keys */ if ((key_prv = get_hostkey_by_index(ndx)) == NULL && (key_pub = get_hostkey_public_by_index(ndx, ssh)) == NULL) { error("%s: can't retrieve hostkey %d", __func__, ndx); goto out; } sshbuf_reset(sigbuf); free(sig); sig = NULL; if ((r = sshbuf_put_cstring(sigbuf, "hostkeys-prove-00@openssh.com")) != 0 || (r = sshbuf_put_string(sigbuf, ssh->kex->session_id, ssh->kex->session_id_len)) != 0 || (r = sshkey_puts(key, sigbuf)) != 0 || (r = ssh->kex->sign(key_prv, key_pub, &sig, &slen, sshbuf_ptr(sigbuf), sshbuf_len(sigbuf), NULL, 0)) != 0 || (r = sshbuf_put_string(resp, sig, slen)) != 0) { error("%s: couldn't prepare signature: %s", __func__, ssh_err(r)); goto out; } } /* Success */ *respp = resp; resp = NULL; /* don't free it */ success = 1; out: free(sig); sshbuf_free(resp); sshbuf_free(sigbuf); sshkey_free(key); return success; } static int server_input_global_request(int type, u_int32_t seq, void *ctxt) { char *rtype; int want_reply; int r, success = 0, allocated_listen_port = 0; struct sshbuf *resp = NULL; + struct passwd *pw = the_authctxt->pw; + + if (pw == NULL || !the_authctxt->valid) + fatal("server_input_global_request: no/invalid user"); rtype = packet_get_string(NULL); want_reply = packet_get_char(); debug("server_input_global_request: rtype %s want_reply %d", rtype, want_reply); /* -R style forwarding */ if (strcmp(rtype, "tcpip-forward") == 0) { - struct passwd *pw; struct Forward fwd; - pw = the_authctxt->pw; - if (pw == NULL || !the_authctxt->valid) - fatal("server_input_global_request: no/invalid user"); memset(&fwd, 0, sizeof(fwd)); fwd.listen_host = packet_get_string(NULL); fwd.listen_port = (u_short)packet_get_int(); debug("server_input_global_request: tcpip-forward listen %s port %d", fwd.listen_host, fwd.listen_port); /* check permissions */ if ((options.allow_tcp_forwarding & FORWARD_REMOTE) == 0 || no_port_forwarding_flag || options.disable_forwarding || (!want_reply && fwd.listen_port == 0) || (fwd.listen_port != 0 && !bind_permitted(fwd.listen_port, pw->pw_uid))) { success = 0; packet_send_debug("Server has disabled port forwarding."); } else { /* Start listening on the port */ success = channel_setup_remote_fwd_listener(&fwd, &allocated_listen_port, &options.fwd_opts); } free(fwd.listen_host); if ((resp = sshbuf_new()) == NULL) fatal("%s: sshbuf_new", __func__); if (allocated_listen_port != 0 && (r = sshbuf_put_u32(resp, allocated_listen_port)) != 0) fatal("%s: sshbuf_put_u32: %s", __func__, ssh_err(r)); } else if (strcmp(rtype, "cancel-tcpip-forward") == 0) { struct Forward fwd; memset(&fwd, 0, sizeof(fwd)); fwd.listen_host = packet_get_string(NULL); fwd.listen_port = (u_short)packet_get_int(); debug("%s: cancel-tcpip-forward addr %s port %d", __func__, fwd.listen_host, fwd.listen_port); success = channel_cancel_rport_listener(&fwd); free(fwd.listen_host); } else if (strcmp(rtype, "streamlocal-forward@openssh.com") == 0) { struct Forward fwd; memset(&fwd, 0, sizeof(fwd)); fwd.listen_path = packet_get_string(NULL); debug("server_input_global_request: streamlocal-forward listen path %s", fwd.listen_path); /* check permissions */ if ((options.allow_streamlocal_forwarding & FORWARD_REMOTE) == 0 || no_port_forwarding_flag || options.disable_forwarding || - !use_privsep) { + (pw->pw_uid != 0 && !use_privsep)) { success = 0; - packet_send_debug("Server has disabled port forwarding."); + packet_send_debug("Server has disabled " + "streamlocal forwarding."); } else { /* Start listening on the socket */ success = channel_setup_remote_fwd_listener( &fwd, NULL, &options.fwd_opts); } free(fwd.listen_path); } else if (strcmp(rtype, "cancel-streamlocal-forward@openssh.com") == 0) { struct Forward fwd; memset(&fwd, 0, sizeof(fwd)); fwd.listen_path = packet_get_string(NULL); debug("%s: cancel-streamlocal-forward path %s", __func__, fwd.listen_path); success = channel_cancel_rport_listener(&fwd); free(fwd.listen_path); } else if (strcmp(rtype, "no-more-sessions@openssh.com") == 0) { no_more_sessions = 1; success = 1; } else if (strcmp(rtype, "hostkeys-prove-00@openssh.com") == 0) { success = server_input_hostkeys_prove(&resp); } if (want_reply) { packet_start(success ? SSH2_MSG_REQUEST_SUCCESS : SSH2_MSG_REQUEST_FAILURE); if (success && resp != NULL) ssh_packet_put_raw(active_state, sshbuf_ptr(resp), sshbuf_len(resp)); packet_send(); packet_write_wait(); } free(rtype); sshbuf_free(resp); return 0; } static int server_input_channel_req(int type, u_int32_t seq, void *ctxt) { Channel *c; int id, reply, success = 0; char *rtype; id = packet_get_int(); rtype = packet_get_string(NULL); reply = packet_get_char(); debug("server_input_channel_req: channel %d request %s reply %d", id, rtype, reply); if ((c = channel_lookup(id)) == NULL) packet_disconnect("server_input_channel_req: " "unknown channel %d", id); if (!strcmp(rtype, "eow@openssh.com")) { packet_check_eom(); chan_rcvd_eow(c); } else if ((c->type == SSH_CHANNEL_LARVAL || c->type == SSH_CHANNEL_OPEN) && strcmp(c->ctype, "session") == 0) success = session_input_channel_req(c, rtype); if (reply && !(c->flags & CHAN_CLOSE_SENT)) { packet_start(success ? SSH2_MSG_CHANNEL_SUCCESS : SSH2_MSG_CHANNEL_FAILURE); packet_put_int(c->remote_id); packet_send(); } free(rtype); return 0; } static void server_init_dispatch(void) { debug("server_init_dispatch"); dispatch_init(&dispatch_protocol_error); dispatch_set(SSH2_MSG_CHANNEL_CLOSE, &channel_input_oclose); dispatch_set(SSH2_MSG_CHANNEL_DATA, &channel_input_data); dispatch_set(SSH2_MSG_CHANNEL_EOF, &channel_input_ieof); dispatch_set(SSH2_MSG_CHANNEL_EXTENDED_DATA, &channel_input_extended_data); dispatch_set(SSH2_MSG_CHANNEL_OPEN, &server_input_channel_open); dispatch_set(SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation); dispatch_set(SSH2_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure); dispatch_set(SSH2_MSG_CHANNEL_REQUEST, &server_input_channel_req); dispatch_set(SSH2_MSG_CHANNEL_WINDOW_ADJUST, &channel_input_window_adjust); dispatch_set(SSH2_MSG_GLOBAL_REQUEST, &server_input_global_request); /* client_alive */ dispatch_set(SSH2_MSG_CHANNEL_SUCCESS, &server_input_keep_alive); dispatch_set(SSH2_MSG_CHANNEL_FAILURE, &server_input_keep_alive); dispatch_set(SSH2_MSG_REQUEST_SUCCESS, &server_input_keep_alive); dispatch_set(SSH2_MSG_REQUEST_FAILURE, &server_input_keep_alive); /* rekeying */ dispatch_set(SSH2_MSG_KEXINIT, &kex_input_kexinit); } diff --git a/crypto/openssh/session.c b/crypto/openssh/session.c index 3a5fc441ba44..1217ad4cbc3c 100644 --- a/crypto/openssh/session.c +++ b/crypto/openssh/session.c @@ -1,2601 +1,2599 @@ /* $OpenBSD: session.c,v 1.286 2016/11/30 03:00:05 djm Exp $ */ /* * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * SSH2 support by Markus Friedl. * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" __RCSID("$FreeBSD$"); #include #include #ifdef HAVE_SYS_STAT_H # include #endif #include #include #include #include #include #include #include #include #include #ifdef HAVE_PATHS_H #include #endif #include #include #include #include #include #include #include #include #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "ssh.h" #include "ssh2.h" #include "sshpty.h" #include "packet.h" #include "buffer.h" #include "match.h" #include "uidswap.h" #include "compat.h" #include "channels.h" #include "key.h" #include "cipher.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "hostfile.h" #include "auth.h" #include "auth-options.h" #include "authfd.h" #include "pathnames.h" #include "log.h" #include "misc.h" #include "servconf.h" #include "sshlogin.h" #include "serverloop.h" #include "canohost.h" #include "session.h" #include "kex.h" #include "monitor_wrap.h" #include "sftp.h" #if defined(KRB5) && defined(USE_AFS) #include #endif #ifdef WITH_SELINUX #include #endif #define IS_INTERNAL_SFTP(c) \ (!strncmp(c, INTERNAL_SFTP_NAME, sizeof(INTERNAL_SFTP_NAME) - 1) && \ (c[sizeof(INTERNAL_SFTP_NAME) - 1] == '\0' || \ c[sizeof(INTERNAL_SFTP_NAME) - 1] == ' ' || \ c[sizeof(INTERNAL_SFTP_NAME) - 1] == '\t')) /* func */ Session *session_new(void); void session_set_fds(Session *, int, int, int, int, int); void session_pty_cleanup(Session *); void session_proctitle(Session *); int session_setup_x11fwd(Session *); int do_exec_pty(Session *, const char *); int do_exec_no_pty(Session *, const char *); int do_exec(Session *, const char *); void do_login(Session *, const char *); #ifdef LOGIN_NEEDS_UTMPX static void do_pre_login(Session *s); #endif void do_child(Session *, const char *); void do_motd(void); int check_quietlogin(Session *, const char *); static void do_authenticated2(Authctxt *); static int session_pty_req(Session *); /* import */ extern ServerOptions options; extern char *__progname; extern int log_stderr; extern int debug_flag; extern u_int utmp_len; extern int startup_pipe; extern void destroy_sensitive_data(void); extern Buffer loginmsg; /* original command from peer. */ const char *original_command = NULL; /* data */ static int sessions_first_unused = -1; static int sessions_nalloc = 0; static Session *sessions = NULL; #define SUBSYSTEM_NONE 0 #define SUBSYSTEM_EXT 1 #define SUBSYSTEM_INT_SFTP 2 #define SUBSYSTEM_INT_SFTP_ERROR 3 #ifdef HAVE_LOGIN_CAP login_cap_t *lc; #endif static int is_child = 0; static int in_chroot = 0; /* Name and directory of socket for authentication agent forwarding. */ static char *auth_sock_name = NULL; static char *auth_sock_dir = NULL; /* removes the agent forwarding socket */ static void auth_sock_cleanup_proc(struct passwd *pw) { if (auth_sock_name != NULL) { temporarily_use_uid(pw); unlink(auth_sock_name); rmdir(auth_sock_dir); auth_sock_name = NULL; restore_uid(); } } static int auth_input_request_forwarding(struct passwd * pw) { Channel *nc; int sock = -1; if (auth_sock_name != NULL) { error("authentication forwarding requested twice."); return 0; } /* Temporarily drop privileged uid for mkdir/bind. */ temporarily_use_uid(pw); /* Allocate a buffer for the socket name, and format the name. */ auth_sock_dir = xstrdup("/tmp/ssh-XXXXXXXXXX"); /* Create private directory for socket */ if (mkdtemp(auth_sock_dir) == NULL) { packet_send_debug("Agent forwarding disabled: " "mkdtemp() failed: %.100s", strerror(errno)); restore_uid(); free(auth_sock_dir); auth_sock_dir = NULL; goto authsock_err; } xasprintf(&auth_sock_name, "%s/agent.%ld", auth_sock_dir, (long) getpid()); /* Start a Unix listener on auth_sock_name. */ sock = unix_listener(auth_sock_name, SSH_LISTEN_BACKLOG, 0); /* Restore the privileged uid. */ restore_uid(); /* Check for socket/bind/listen failure. */ if (sock < 0) goto authsock_err; /* Allocate a channel for the authentication agent socket. */ nc = channel_new("auth socket", SSH_CHANNEL_AUTH_SOCKET, sock, sock, -1, CHAN_X11_WINDOW_DEFAULT, CHAN_X11_PACKET_DEFAULT, 0, "auth socket", 1); nc->path = xstrdup(auth_sock_name); return 1; authsock_err: free(auth_sock_name); if (auth_sock_dir != NULL) { rmdir(auth_sock_dir); free(auth_sock_dir); } if (sock != -1) close(sock); auth_sock_name = NULL; auth_sock_dir = NULL; return 0; } static void display_loginmsg(void) { if (buffer_len(&loginmsg) > 0) { buffer_append(&loginmsg, "\0", 1); printf("%s", (char *)buffer_ptr(&loginmsg)); buffer_clear(&loginmsg); } } void do_authenticated(Authctxt *authctxt) { setproctitle("%s", authctxt->pw->pw_name); /* setup the channel layer */ /* XXX - streamlocal? */ if (no_port_forwarding_flag || options.disable_forwarding || (options.allow_tcp_forwarding & FORWARD_LOCAL) == 0) channel_disable_adm_local_opens(); else channel_permit_all_opens(); auth_debug_send(); do_authenticated2(authctxt); do_cleanup(authctxt); } /* Check untrusted xauth strings for metacharacters */ static int xauth_valid_string(const char *s) { size_t i; for (i = 0; s[i] != '\0'; i++) { if (!isalnum((u_char)s[i]) && s[i] != '.' && s[i] != ':' && s[i] != '/' && s[i] != '-' && s[i] != '_') return 0; } return 1; } #define USE_PIPES 1 /* * This is called to fork and execute a command when we have no tty. This * will call do_child from the child, and server_loop from the parent after * setting up file descriptors and such. */ int do_exec_no_pty(Session *s, const char *command) { pid_t pid; #ifdef USE_PIPES int pin[2], pout[2], perr[2]; if (s == NULL) fatal("do_exec_no_pty: no session"); /* Allocate pipes for communicating with the program. */ if (pipe(pin) < 0) { error("%s: pipe in: %.100s", __func__, strerror(errno)); return -1; } if (pipe(pout) < 0) { error("%s: pipe out: %.100s", __func__, strerror(errno)); close(pin[0]); close(pin[1]); return -1; } if (pipe(perr) < 0) { error("%s: pipe err: %.100s", __func__, strerror(errno)); close(pin[0]); close(pin[1]); close(pout[0]); close(pout[1]); return -1; } #else int inout[2], err[2]; if (s == NULL) fatal("do_exec_no_pty: no session"); /* Uses socket pairs to communicate with the program. */ if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0) { error("%s: socketpair #1: %.100s", __func__, strerror(errno)); return -1; } if (socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0) { error("%s: socketpair #2: %.100s", __func__, strerror(errno)); close(inout[0]); close(inout[1]); return -1; } #endif session_proctitle(s); /* Fork the child. */ switch ((pid = fork())) { case -1: error("%s: fork: %.100s", __func__, strerror(errno)); #ifdef USE_PIPES close(pin[0]); close(pin[1]); close(pout[0]); close(pout[1]); close(perr[0]); close(perr[1]); #else close(inout[0]); close(inout[1]); close(err[0]); close(err[1]); #endif return -1; case 0: is_child = 1; /* Child. Reinitialize the log since the pid has changed. */ log_init(__progname, options.log_level, options.log_facility, log_stderr); /* * Create a new session and process group since the 4.4BSD * setlogin() affects the entire process group. */ if (setsid() < 0) error("setsid failed: %.100s", strerror(errno)); #ifdef USE_PIPES /* * Redirect stdin. We close the parent side of the socket * pair, and make the child side the standard input. */ close(pin[1]); if (dup2(pin[0], 0) < 0) perror("dup2 stdin"); close(pin[0]); /* Redirect stdout. */ close(pout[0]); if (dup2(pout[1], 1) < 0) perror("dup2 stdout"); close(pout[1]); /* Redirect stderr. */ close(perr[0]); if (dup2(perr[1], 2) < 0) perror("dup2 stderr"); close(perr[1]); #else /* * Redirect stdin, stdout, and stderr. Stdin and stdout will * use the same socket, as some programs (particularly rdist) * seem to depend on it. */ close(inout[1]); close(err[1]); if (dup2(inout[0], 0) < 0) /* stdin */ perror("dup2 stdin"); if (dup2(inout[0], 1) < 0) /* stdout (same as stdin) */ perror("dup2 stdout"); close(inout[0]); if (dup2(err[0], 2) < 0) /* stderr */ perror("dup2 stderr"); close(err[0]); #endif #ifdef _UNICOS cray_init_job(s->pw); /* set up cray jid and tmpdir */ #endif /* Do processing for the child (exec command etc). */ do_child(s, command); /* NOTREACHED */ default: break; } #ifdef _UNICOS signal(WJSIGNAL, cray_job_termination_handler); #endif /* _UNICOS */ #ifdef HAVE_CYGWIN cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); #endif s->pid = pid; /* Set interactive/non-interactive mode. */ packet_set_interactive(s->display != NULL, options.ip_qos_interactive, options.ip_qos_bulk); /* * Clear loginmsg, since it's the child's responsibility to display * it to the user, otherwise multiple sessions may accumulate * multiple copies of the login messages. */ buffer_clear(&loginmsg); #ifdef USE_PIPES /* We are the parent. Close the child sides of the pipes. */ close(pin[0]); close(pout[1]); close(perr[1]); session_set_fds(s, pin[1], pout[0], perr[0], s->is_subsystem, 0); #else /* We are the parent. Close the child sides of the socket pairs. */ close(inout[0]); close(err[0]); /* * Enter the interactive session. Note: server_loop must be able to * handle the case that fdin and fdout are the same. */ session_set_fds(s, inout[1], inout[1], err[1], s->is_subsystem, 0); #endif return 0; } /* * This is called to fork and execute a command when we have a tty. This * will call do_child from the child, and server_loop from the parent after * setting up file descriptors, controlling tty, updating wtmp, utmp, * lastlog, and other such operations. */ int do_exec_pty(Session *s, const char *command) { int fdout, ptyfd, ttyfd, ptymaster; pid_t pid; if (s == NULL) fatal("do_exec_pty: no session"); ptyfd = s->ptyfd; ttyfd = s->ttyfd; /* * Create another descriptor of the pty master side for use as the * standard input. We could use the original descriptor, but this * simplifies code in server_loop. The descriptor is bidirectional. * Do this before forking (and cleanup in the child) so as to * detect and gracefully fail out-of-fd conditions. */ if ((fdout = dup(ptyfd)) < 0) { error("%s: dup #1: %s", __func__, strerror(errno)); close(ttyfd); close(ptyfd); return -1; } /* we keep a reference to the pty master */ if ((ptymaster = dup(ptyfd)) < 0) { error("%s: dup #2: %s", __func__, strerror(errno)); close(ttyfd); close(ptyfd); close(fdout); return -1; } /* Fork the child. */ switch ((pid = fork())) { case -1: error("%s: fork: %.100s", __func__, strerror(errno)); close(fdout); close(ptymaster); close(ttyfd); close(ptyfd); return -1; case 0: is_child = 1; close(fdout); close(ptymaster); /* Child. Reinitialize the log because the pid has changed. */ log_init(__progname, options.log_level, options.log_facility, log_stderr); /* Close the master side of the pseudo tty. */ close(ptyfd); /* Make the pseudo tty our controlling tty. */ pty_make_controlling_tty(&ttyfd, s->tty); /* Redirect stdin/stdout/stderr from the pseudo tty. */ if (dup2(ttyfd, 0) < 0) error("dup2 stdin: %s", strerror(errno)); if (dup2(ttyfd, 1) < 0) error("dup2 stdout: %s", strerror(errno)); if (dup2(ttyfd, 2) < 0) error("dup2 stderr: %s", strerror(errno)); /* Close the extra descriptor for the pseudo tty. */ close(ttyfd); /* record login, etc. similar to login(1) */ #ifdef _UNICOS cray_init_job(s->pw); /* set up cray jid and tmpdir */ #endif /* _UNICOS */ #ifndef HAVE_OSF_SIA do_login(s, command); #endif /* * Do common processing for the child, such as execing * the command. */ do_child(s, command); /* NOTREACHED */ default: break; } #ifdef _UNICOS signal(WJSIGNAL, cray_job_termination_handler); #endif /* _UNICOS */ #ifdef HAVE_CYGWIN cygwin_set_impersonation_token(INVALID_HANDLE_VALUE); #endif s->pid = pid; /* Parent. Close the slave side of the pseudo tty. */ close(ttyfd); /* Enter interactive session. */ s->ptymaster = ptymaster; packet_set_interactive(1, options.ip_qos_interactive, options.ip_qos_bulk); session_set_fds(s, ptyfd, fdout, -1, 1, 1); return 0; } #ifdef LOGIN_NEEDS_UTMPX static void do_pre_login(Session *s) { struct ssh *ssh = active_state; /* XXX */ socklen_t fromlen; struct sockaddr_storage from; pid_t pid = getpid(); /* * Get IP address of client. If the connection is not a socket, let * the address be 0.0.0.0. */ memset(&from, 0, sizeof(from)); fromlen = sizeof(from); if (packet_connection_is_on_socket()) { if (getpeername(packet_get_connection_in(), (struct sockaddr *)&from, &fromlen) < 0) { debug("getpeername: %.100s", strerror(errno)); cleanup_exit(255); } } record_utmp_only(pid, s->tty, s->pw->pw_name, session_get_remote_name_or_ip(ssh, utmp_len, options.use_dns), (struct sockaddr *)&from, fromlen); } #endif /* * This is called to fork and execute a command. If another command is * to be forced, execute that instead. */ int do_exec(Session *s, const char *command) { struct ssh *ssh = active_state; /* XXX */ int ret; const char *forced = NULL, *tty = NULL; char session_type[1024]; if (options.adm_forced_command) { original_command = command; command = options.adm_forced_command; forced = "(config)"; } else if (forced_command) { original_command = command; command = forced_command; forced = "(key-option)"; } if (forced != NULL) { if (IS_INTERNAL_SFTP(command)) { s->is_subsystem = s->is_subsystem ? SUBSYSTEM_INT_SFTP : SUBSYSTEM_INT_SFTP_ERROR; } else if (s->is_subsystem) s->is_subsystem = SUBSYSTEM_EXT; snprintf(session_type, sizeof(session_type), "forced-command %s '%.900s'", forced, command); } else if (s->is_subsystem) { snprintf(session_type, sizeof(session_type), "subsystem '%.900s'", s->subsys); } else if (command == NULL) { snprintf(session_type, sizeof(session_type), "shell"); } else { /* NB. we don't log unforced commands to preserve privacy */ snprintf(session_type, sizeof(session_type), "command"); } if (s->ttyfd != -1) { tty = s->tty; if (strncmp(tty, "/dev/", 5) == 0) tty += 5; } verbose("Starting session: %s%s%s for %s from %.200s port %d id %d", session_type, tty == NULL ? "" : " on ", tty == NULL ? "" : tty, s->pw->pw_name, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), s->self); #ifdef SSH_AUDIT_EVENTS if (command != NULL) PRIVSEP(audit_run_command(command)); else if (s->ttyfd == -1) { char *shell = s->pw->pw_shell; if (shell[0] == '\0') /* empty shell means /bin/sh */ shell =_PATH_BSHELL; PRIVSEP(audit_run_command(shell)); } #endif if (s->ttyfd != -1) ret = do_exec_pty(s, command); else ret = do_exec_no_pty(s, command); original_command = NULL; /* * Clear loginmsg: it's the child's responsibility to display * it to the user, otherwise multiple sessions may accumulate * multiple copies of the login messages. */ buffer_clear(&loginmsg); return ret; } /* administrative, login(1)-like work */ void do_login(Session *s, const char *command) { struct ssh *ssh = active_state; /* XXX */ socklen_t fromlen; struct sockaddr_storage from; struct passwd * pw = s->pw; pid_t pid = getpid(); /* * Get IP address of client. If the connection is not a socket, let * the address be 0.0.0.0. */ memset(&from, 0, sizeof(from)); fromlen = sizeof(from); if (packet_connection_is_on_socket()) { if (getpeername(packet_get_connection_in(), (struct sockaddr *)&from, &fromlen) < 0) { debug("getpeername: %.100s", strerror(errno)); cleanup_exit(255); } } /* Record that there was a login on that tty from the remote host. */ if (!use_privsep) record_login(pid, s->tty, pw->pw_name, pw->pw_uid, session_get_remote_name_or_ip(ssh, utmp_len, options.use_dns), (struct sockaddr *)&from, fromlen); #ifdef USE_PAM /* * If password change is needed, do it now. * This needs to occur before the ~/.hushlogin check. */ if (options.use_pam && !use_privsep && s->authctxt->force_pwchange) { display_loginmsg(); do_pam_chauthtok(); s->authctxt->force_pwchange = 0; /* XXX - signal [net] parent to enable forwardings */ } #endif if (check_quietlogin(s, command)) return; display_loginmsg(); do_motd(); } /* * Display the message of the day. */ void do_motd(void) { FILE *f; char buf[256]; if (options.print_motd) { #ifdef HAVE_LOGIN_CAP f = fopen(login_getcapstr(lc, "welcome", "/etc/motd", "/etc/motd"), "r"); #else f = fopen("/etc/motd", "r"); #endif if (f) { while (fgets(buf, sizeof(buf), f)) fputs(buf, stdout); fclose(f); } } } /* * Check for quiet login, either .hushlogin or command given. */ int check_quietlogin(Session *s, const char *command) { char buf[256]; struct passwd *pw = s->pw; struct stat st; /* Return 1 if .hushlogin exists or a command given. */ if (command != NULL) return 1; snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir); #ifdef HAVE_LOGIN_CAP if (login_getcapbool(lc, "hushlogin", 0) || stat(buf, &st) >= 0) return 1; #else if (stat(buf, &st) >= 0) return 1; #endif return 0; } /* * Sets the value of the given variable in the environment. If the variable * already exists, its value is overridden. */ void child_set_env(char ***envp, u_int *envsizep, const char *name, const char *value) { char **env; u_int envsize; u_int i, namelen; if (strchr(name, '=') != NULL) { error("Invalid environment variable \"%.100s\"", name); return; } /* * If we're passed an uninitialized list, allocate a single null * entry before continuing. */ if (*envp == NULL && *envsizep == 0) { *envp = xmalloc(sizeof(char *)); *envp[0] = NULL; *envsizep = 1; } /* * Find the slot where the value should be stored. If the variable * already exists, we reuse the slot; otherwise we append a new slot * at the end of the array, expanding if necessary. */ env = *envp; namelen = strlen(name); for (i = 0; env[i]; i++) if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=') break; if (env[i]) { /* Reuse the slot. */ free(env[i]); } else { /* New variable. Expand if necessary. */ envsize = *envsizep; if (i >= envsize - 1) { if (envsize >= 1000) fatal("child_set_env: too many env vars"); envsize += 50; env = (*envp) = xreallocarray(env, envsize, sizeof(char *)); *envsizep = envsize; } /* Need to set the NULL pointer at end of array beyond the new slot. */ env[i + 1] = NULL; } /* Allocate space and format the variable in the appropriate slot. */ env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1); snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value); } /* * Reads environment variables from the given file and adds/overrides them * into the environment. If the file does not exist, this does nothing. * Otherwise, it must consist of empty lines, comments (line starts with '#') * and assignments of the form name=value. No other forms are allowed. */ static void read_environment_file(char ***env, u_int *envsize, const char *filename) { FILE *f; char buf[4096]; char *cp, *value; u_int lineno = 0; f = fopen(filename, "r"); if (!f) return; while (fgets(buf, sizeof(buf), f)) { if (++lineno > 1000) fatal("Too many lines in environment file %s", filename); for (cp = buf; *cp == ' ' || *cp == '\t'; cp++) ; if (!*cp || *cp == '#' || *cp == '\n') continue; cp[strcspn(cp, "\n")] = '\0'; value = strchr(cp, '='); if (value == NULL) { fprintf(stderr, "Bad line %u in %.100s\n", lineno, filename); continue; } /* * Replace the equals sign by nul, and advance value to * the value string. */ *value = '\0'; value++; child_set_env(env, envsize, cp, value); } fclose(f); } #ifdef HAVE_ETC_DEFAULT_LOGIN /* * Return named variable from specified environment, or NULL if not present. */ static char * child_get_env(char **env, const char *name) { int i; size_t len; len = strlen(name); for (i=0; env[i] != NULL; i++) if (strncmp(name, env[i], len) == 0 && env[i][len] == '=') return(env[i] + len + 1); return NULL; } /* * Read /etc/default/login. * We pick up the PATH (or SUPATH for root) and UMASK. */ static void read_etc_default_login(char ***env, u_int *envsize, uid_t uid) { char **tmpenv = NULL, *var; u_int i, tmpenvsize = 0; u_long mask; /* * We don't want to copy the whole file to the child's environment, * so we use a temporary environment and copy the variables we're * interested in. */ read_environment_file(&tmpenv, &tmpenvsize, "/etc/default/login"); if (tmpenv == NULL) return; if (uid == 0) var = child_get_env(tmpenv, "SUPATH"); else var = child_get_env(tmpenv, "PATH"); if (var != NULL) child_set_env(env, envsize, "PATH", var); if ((var = child_get_env(tmpenv, "UMASK")) != NULL) if (sscanf(var, "%5lo", &mask) == 1) umask((mode_t)mask); for (i = 0; tmpenv[i] != NULL; i++) free(tmpenv[i]); free(tmpenv); } #endif /* HAVE_ETC_DEFAULT_LOGIN */ void copy_environment(char **source, char ***env, u_int *envsize) { char *var_name, *var_val; int i; if (source == NULL) return; for(i = 0; source[i] != NULL; i++) { var_name = xstrdup(source[i]); if ((var_val = strstr(var_name, "=")) == NULL) { free(var_name); continue; } *var_val++ = '\0'; debug3("Copy environment: %s=%s", var_name, var_val); child_set_env(env, envsize, var_name, var_val); free(var_name); } } static char ** do_setup_env(Session *s, const char *shell) { struct ssh *ssh = active_state; /* XXX */ char buf[256]; u_int i, envsize; char **env, *laddr; struct passwd *pw = s->pw; #if !defined (HAVE_LOGIN_CAP) && !defined (HAVE_CYGWIN) char *path = NULL; #else extern char **environ; char **senv, **var, *val; #endif /* Initialize the environment. */ envsize = 100; env = xcalloc(envsize, sizeof(char *)); env[0] = NULL; #ifdef HAVE_CYGWIN /* * The Windows environment contains some setting which are * important for a running system. They must not be dropped. */ { char **p; p = fetch_windows_environment(); copy_environment(p, &env, &envsize); free_windows_environment(p); } #endif if (getenv("TZ")) child_set_env(&env, &envsize, "TZ", getenv("TZ")); #ifdef GSSAPI /* Allow any GSSAPI methods that we've used to alter * the childs environment as they see fit */ ssh_gssapi_do_child(&env, &envsize); #endif /* Set basic environment. */ for (i = 0; i < s->num_env; i++) child_set_env(&env, &envsize, s->env[i].name, s->env[i].val); child_set_env(&env, &envsize, "USER", pw->pw_name); child_set_env(&env, &envsize, "LOGNAME", pw->pw_name); #ifdef _AIX child_set_env(&env, &envsize, "LOGIN", pw->pw_name); #endif child_set_env(&env, &envsize, "HOME", pw->pw_dir); snprintf(buf, sizeof buf, "%.200s/%.50s", _PATH_MAILDIR, pw->pw_name); child_set_env(&env, &envsize, "MAIL", buf); #ifdef HAVE_LOGIN_CAP child_set_env(&env, &envsize, "PATH", _PATH_STDPATH); child_set_env(&env, &envsize, "TERM", "su"); /* * Temporarily swap out our real environment with an empty one, * let setusercontext() apply any environment variables defined * for the user's login class, copy those variables to the child, * free the temporary environment, and restore the original. */ senv = environ; environ = xmalloc(sizeof(*environ)); *environ = NULL; (void)setusercontext(lc, pw, pw->pw_uid, LOGIN_SETENV|LOGIN_SETPATH); for (var = environ; *var != NULL; ++var) { if ((val = strchr(*var, '=')) != NULL) { *val++ = '\0'; child_set_env(&env, &envsize, *var, val); } free(*var); } free(environ); environ = senv; #else /* HAVE_LOGIN_CAP */ # ifndef HAVE_CYGWIN /* * There's no standard path on Windows. The path contains * important components pointing to the system directories, * needed for loading shared libraries. So the path better * remains intact here. */ # ifdef HAVE_ETC_DEFAULT_LOGIN read_etc_default_login(&env, &envsize, pw->pw_uid); path = child_get_env(env, "PATH"); # endif /* HAVE_ETC_DEFAULT_LOGIN */ if (path == NULL || *path == '\0') { child_set_env(&env, &envsize, "PATH", s->pw->pw_uid == 0 ? SUPERUSER_PATH : _PATH_STDPATH); } # endif /* HAVE_CYGWIN */ #endif /* HAVE_LOGIN_CAP */ /* Normal systems set SHELL by default. */ child_set_env(&env, &envsize, "SHELL", shell); /* Set custom environment options from RSA authentication. */ while (custom_environment) { struct envstring *ce = custom_environment; char *str = ce->s; for (i = 0; str[i] != '=' && str[i]; i++) ; if (str[i] == '=') { str[i] = 0; child_set_env(&env, &envsize, str, str + i + 1); } custom_environment = ce->next; free(ce->s); free(ce); } /* SSH_CLIENT deprecated */ snprintf(buf, sizeof buf, "%.50s %d %d", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), ssh_local_port(ssh)); child_set_env(&env, &envsize, "SSH_CLIENT", buf); laddr = get_local_ipaddr(packet_get_connection_in()); snprintf(buf, sizeof buf, "%.50s %d %.50s %d", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), laddr, ssh_local_port(ssh)); free(laddr); child_set_env(&env, &envsize, "SSH_CONNECTION", buf); if (s->ttyfd != -1) child_set_env(&env, &envsize, "SSH_TTY", s->tty); if (s->term) child_set_env(&env, &envsize, "TERM", s->term); if (s->display) child_set_env(&env, &envsize, "DISPLAY", s->display); if (original_command) child_set_env(&env, &envsize, "SSH_ORIGINAL_COMMAND", original_command); #ifdef _UNICOS if (cray_tmpdir[0] != '\0') child_set_env(&env, &envsize, "TMPDIR", cray_tmpdir); #endif /* _UNICOS */ /* * Since we clear KRB5CCNAME at startup, if it's set now then it * must have been set by a native authentication method (eg AIX or * SIA), so copy it to the child. */ { char *cp; if ((cp = getenv("KRB5CCNAME")) != NULL) child_set_env(&env, &envsize, "KRB5CCNAME", cp); } #ifdef _AIX { char *cp; if ((cp = getenv("AUTHSTATE")) != NULL) child_set_env(&env, &envsize, "AUTHSTATE", cp); read_environment_file(&env, &envsize, "/etc/environment"); } #endif #ifdef KRB5 if (s->authctxt->krb5_ccname) child_set_env(&env, &envsize, "KRB5CCNAME", s->authctxt->krb5_ccname); #endif #ifdef USE_PAM /* * Pull in any environment variables that may have * been set by PAM. */ if (options.use_pam) { char **p; p = fetch_pam_child_environment(); copy_environment(p, &env, &envsize); free_pam_environment(p); p = fetch_pam_environment(); copy_environment(p, &env, &envsize); free_pam_environment(p); } #endif /* USE_PAM */ if (auth_sock_name != NULL) child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME, auth_sock_name); /* read $HOME/.ssh/environment. */ if (options.permit_user_env) { snprintf(buf, sizeof buf, "%.200s/.ssh/environment", strcmp(pw->pw_dir, "/") ? pw->pw_dir : ""); read_environment_file(&env, &envsize, buf); } if (debug_flag) { /* dump the environment */ fprintf(stderr, "Environment:\n"); for (i = 0; env[i]; i++) fprintf(stderr, " %.200s\n", env[i]); } return env; } /* * Run $HOME/.ssh/rc, /etc/ssh/sshrc, or xauth (whichever is found * first in this order). */ static void do_rc_files(Session *s, const char *shell) { FILE *f = NULL; char cmd[1024]; int do_xauth; struct stat st; do_xauth = s->display != NULL && s->auth_proto != NULL && s->auth_data != NULL; /* ignore _PATH_SSH_USER_RC for subsystems and admin forced commands */ if (!s->is_subsystem && options.adm_forced_command == NULL && !no_user_rc && options.permit_user_rc && stat(_PATH_SSH_USER_RC, &st) >= 0) { snprintf(cmd, sizeof cmd, "%s -c '%s %s'", shell, _PATH_BSHELL, _PATH_SSH_USER_RC); if (debug_flag) fprintf(stderr, "Running %s\n", cmd); f = popen(cmd, "w"); if (f) { if (do_xauth) fprintf(f, "%s %s\n", s->auth_proto, s->auth_data); pclose(f); } else fprintf(stderr, "Could not run %s\n", _PATH_SSH_USER_RC); } else if (stat(_PATH_SSH_SYSTEM_RC, &st) >= 0) { if (debug_flag) fprintf(stderr, "Running %s %s\n", _PATH_BSHELL, _PATH_SSH_SYSTEM_RC); f = popen(_PATH_BSHELL " " _PATH_SSH_SYSTEM_RC, "w"); if (f) { if (do_xauth) fprintf(f, "%s %s\n", s->auth_proto, s->auth_data); pclose(f); } else fprintf(stderr, "Could not run %s\n", _PATH_SSH_SYSTEM_RC); } else if (do_xauth && options.xauth_location != NULL) { /* Add authority data to .Xauthority if appropriate. */ if (debug_flag) { fprintf(stderr, "Running %.500s remove %.100s\n", options.xauth_location, s->auth_display); fprintf(stderr, "%.500s add %.100s %.100s %.100s\n", options.xauth_location, s->auth_display, s->auth_proto, s->auth_data); } snprintf(cmd, sizeof cmd, "%s -q -", options.xauth_location); f = popen(cmd, "w"); if (f) { fprintf(f, "remove %s\n", s->auth_display); fprintf(f, "add %s %s %s\n", s->auth_display, s->auth_proto, s->auth_data); pclose(f); } else { fprintf(stderr, "Could not run %s\n", cmd); } } } static void do_nologin(struct passwd *pw) { FILE *f = NULL; - char buf[1024], *nl, *def_nl = _PATH_NOLOGIN; + const char *nl; + char buf[1024], *def_nl = _PATH_NOLOGIN; struct stat sb; #ifdef HAVE_LOGIN_CAP if (login_getcapbool(lc, "ignorenologin", 0) || pw->pw_uid == 0) return; nl = login_getcapstr(lc, "nologin", def_nl, def_nl); #else if (pw->pw_uid == 0) return; nl = def_nl; #endif - if (stat(nl, &sb) == -1) { - if (nl != def_nl) - free(nl); + if (stat(nl, &sb) == -1) return; - } /* /etc/nologin exists. Print its contents if we can and exit. */ logit("User %.100s not allowed because %s exists", pw->pw_name, nl); if ((f = fopen(nl, "r")) != NULL) { while (fgets(buf, sizeof(buf), f)) fputs(buf, stderr); fclose(f); } exit(254); } /* * Chroot into a directory after checking it for safety: all path components * must be root-owned directories with strict permissions. */ static void safely_chroot(const char *path, uid_t uid) { const char *cp; char component[PATH_MAX]; struct stat st; if (*path != '/') fatal("chroot path does not begin at root"); if (strlen(path) >= sizeof(component)) fatal("chroot path too long"); /* * Descend the path, checking that each component is a * root-owned directory with strict permissions. */ for (cp = path; cp != NULL;) { if ((cp = strchr(cp, '/')) == NULL) strlcpy(component, path, sizeof(component)); else { cp++; memcpy(component, path, cp - path); component[cp - path] = '\0'; } debug3("%s: checking '%s'", __func__, component); if (stat(component, &st) != 0) fatal("%s: stat(\"%s\"): %s", __func__, component, strerror(errno)); if (st.st_uid != 0 || (st.st_mode & 022) != 0) fatal("bad ownership or modes for chroot " "directory %s\"%s\"", cp == NULL ? "" : "component ", component); if (!S_ISDIR(st.st_mode)) fatal("chroot path %s\"%s\" is not a directory", cp == NULL ? "" : "component ", component); } if (chdir(path) == -1) fatal("Unable to chdir to chroot path \"%s\": " "%s", path, strerror(errno)); if (chroot(path) == -1) fatal("chroot(\"%s\"): %s", path, strerror(errno)); if (chdir("/") == -1) fatal("%s: chdir(/) after chroot: %s", __func__, strerror(errno)); verbose("Changed root directory to \"%s\"", path); } /* Set login name, uid, gid, and groups. */ void do_setusercontext(struct passwd *pw) { char *chroot_path, *tmp; platform_setusercontext(pw); if (platform_privileged_uidswap()) { #ifdef HAVE_LOGIN_CAP if (setusercontext(lc, pw, pw->pw_uid, (LOGIN_SETALL & ~(LOGIN_SETENV|LOGIN_SETPATH|LOGIN_SETUSER))) < 0) { perror("unable to set user context"); exit(1); } #else if (setlogin(pw->pw_name) < 0) error("setlogin failed: %s", strerror(errno)); if (setgid(pw->pw_gid) < 0) { perror("setgid"); exit(1); } /* Initialize the group list. */ if (initgroups(pw->pw_name, pw->pw_gid) < 0) { perror("initgroups"); exit(1); } endgrent(); #endif platform_setusercontext_post_groups(pw); if (!in_chroot && options.chroot_directory != NULL && strcasecmp(options.chroot_directory, "none") != 0) { tmp = tilde_expand_filename(options.chroot_directory, pw->pw_uid); chroot_path = percent_expand(tmp, "h", pw->pw_dir, "u", pw->pw_name, (char *)NULL); safely_chroot(chroot_path, pw->pw_uid); free(tmp); free(chroot_path); /* Make sure we don't attempt to chroot again */ free(options.chroot_directory); options.chroot_directory = NULL; in_chroot = 1; } #ifdef HAVE_LOGIN_CAP if (setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUSER) < 0) { perror("unable to set user context (setuser)"); exit(1); } /* * FreeBSD's setusercontext() will not apply the user's * own umask setting unless running with the user's UID. */ (void) setusercontext(lc, pw, pw->pw_uid, LOGIN_SETUMASK); #else # ifdef USE_LIBIAF /* * In a chroot environment, the set_id() will always fail; * typically because of the lack of necessary authentication * services and runtime such as ./usr/lib/libiaf.so, * ./usr/lib/libpam.so.1, and ./etc/passwd We skip it in the * internal sftp chroot case. We'll lose auditing and ACLs but * permanently_set_uid will take care of the rest. */ if (!in_chroot && set_id(pw->pw_name) != 0) fatal("set_id(%s) Failed", pw->pw_name); # endif /* USE_LIBIAF */ /* Permanently switch to the desired uid. */ permanently_set_uid(pw); #endif } else if (options.chroot_directory != NULL && strcasecmp(options.chroot_directory, "none") != 0) { fatal("server lacks privileges to chroot to ChrootDirectory"); } if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid) fatal("Failed to set uids to %u.", (u_int) pw->pw_uid); } static void do_pwchange(Session *s) { fflush(NULL); fprintf(stderr, "WARNING: Your password has expired.\n"); if (s->ttyfd != -1) { fprintf(stderr, "You must change your password now and login again!\n"); #ifdef WITH_SELINUX setexeccon(NULL); #endif #ifdef PASSWD_NEEDS_USERNAME execl(_PATH_PASSWD_PROG, "passwd", s->pw->pw_name, (char *)NULL); #else execl(_PATH_PASSWD_PROG, "passwd", (char *)NULL); #endif perror("passwd"); } else { fprintf(stderr, "Password change required but no TTY available.\n"); } exit(1); } static void child_close_fds(void) { extern int auth_sock; if (auth_sock != -1) { close(auth_sock); auth_sock = -1; } if (packet_get_connection_in() == packet_get_connection_out()) close(packet_get_connection_in()); else { close(packet_get_connection_in()); close(packet_get_connection_out()); } /* * Close all descriptors related to channels. They will still remain * open in the parent. */ /* XXX better use close-on-exec? -markus */ channel_close_all(); /* * Close any extra file descriptors. Note that there may still be * descriptors left by system functions. They will be closed later. */ endpwent(); /* * Close any extra open file descriptors so that we don't have them * hanging around in clients. Note that we want to do this after * initgroups, because at least on Solaris 2.3 it leaves file * descriptors open. */ closefrom(STDERR_FILENO + 1); } /* * Performs common processing for the child, such as setting up the * environment, closing extra file descriptors, setting the user and group * ids, and executing the command or shell. */ #define ARGV_MAX 10 void do_child(Session *s, const char *command) { extern char **environ; char **env; char *argv[ARGV_MAX]; const char *shell, *shell0; struct passwd *pw = s->pw; int r = 0; /* remove hostkey from the child's memory */ destroy_sensitive_data(); /* Force a password change */ if (s->authctxt->force_pwchange) { do_setusercontext(pw); child_close_fds(); do_pwchange(s); exit(1); } #ifdef _UNICOS cray_setup(pw->pw_uid, pw->pw_name, command); #endif /* _UNICOS */ /* * Login(1) does this as well, and it needs uid 0 for the "-h" * switch, so we let login(1) to this for us. */ #ifdef HAVE_OSF_SIA session_setup_sia(pw, s->ttyfd == -1 ? NULL : s->tty); if (!check_quietlogin(s, command)) do_motd(); #else /* HAVE_OSF_SIA */ /* When PAM is enabled we rely on it to do the nologin check */ if (!options.use_pam) do_nologin(pw); do_setusercontext(pw); /* * PAM session modules in do_setusercontext may have * generated messages, so if this in an interactive * login then display them too. */ if (!check_quietlogin(s, command)) display_loginmsg(); #endif /* HAVE_OSF_SIA */ #ifdef USE_PAM if (options.use_pam && !is_pam_session_open()) { debug3("PAM session not opened, exiting"); display_loginmsg(); exit(254); } #endif /* * Get the shell from the password data. An empty shell field is * legal, and means /bin/sh. */ shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell; /* * Make sure $SHELL points to the shell from the password file, * even if shell is overridden from login.conf */ env = do_setup_env(s, shell); #ifdef HAVE_LOGIN_CAP shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell); #endif /* * Close the connection descriptors; note that this is the child, and * the server will still have the socket open, and it is important * that we do not shutdown it. Note that the descriptors cannot be * closed before building the environment, as we call * ssh_remote_ipaddr there. */ child_close_fds(); /* * Must take new environment into use so that .ssh/rc, * /etc/ssh/sshrc and xauth are run in the proper environment. */ environ = env; #if defined(KRB5) && defined(USE_AFS) /* * At this point, we check to see if AFS is active and if we have * a valid Kerberos 5 TGT. If so, it seems like a good idea to see * if we can (and need to) extend the ticket into an AFS token. If * we don't do this, we run into potential problems if the user's * home directory is in AFS and it's not world-readable. */ if (options.kerberos_get_afs_token && k_hasafs() && (s->authctxt->krb5_ctx != NULL)) { char cell[64]; debug("Getting AFS token"); k_setpag(); if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0) krb5_afslog(s->authctxt->krb5_ctx, s->authctxt->krb5_fwd_ccache, cell, NULL); krb5_afslog_home(s->authctxt->krb5_ctx, s->authctxt->krb5_fwd_ccache, NULL, NULL, pw->pw_dir); } #endif /* Change current directory to the user's home directory. */ if (chdir(pw->pw_dir) < 0) { /* Suppress missing homedir warning for chroot case */ #ifdef HAVE_LOGIN_CAP r = login_getcapbool(lc, "requirehome", 0); #endif if (r || !in_chroot) { fprintf(stderr, "Could not chdir to home " "directory %s: %s\n", pw->pw_dir, strerror(errno)); } if (r) exit(1); } closefrom(STDERR_FILENO + 1); do_rc_files(s, shell); /* restore SIGPIPE for child */ signal(SIGPIPE, SIG_DFL); if (s->is_subsystem == SUBSYSTEM_INT_SFTP_ERROR) { printf("This service allows sftp connections only.\n"); fflush(NULL); exit(1); } else if (s->is_subsystem == SUBSYSTEM_INT_SFTP) { extern int optind, optreset; int i; char *p, *args; setproctitle("%s@%s", s->pw->pw_name, INTERNAL_SFTP_NAME); args = xstrdup(command ? command : "sftp-server"); for (i = 0, (p = strtok(args, " ")); p; (p = strtok(NULL, " "))) if (i < ARGV_MAX - 1) argv[i++] = p; argv[i] = NULL; optind = optreset = 1; __progname = argv[0]; #ifdef WITH_SELINUX ssh_selinux_change_context("sftpd_t"); #endif exit(sftp_server_main(i, argv, s->pw)); } fflush(NULL); /* Get the last component of the shell name. */ if ((shell0 = strrchr(shell, '/')) != NULL) shell0++; else shell0 = shell; /* * If we have no command, execute the shell. In this case, the shell * name to be passed in argv[0] is preceded by '-' to indicate that * this is a login shell. */ if (!command) { char argv0[256]; /* Start the shell. Set initial character to '-'. */ argv0[0] = '-'; if (strlcpy(argv0 + 1, shell0, sizeof(argv0) - 1) >= sizeof(argv0) - 1) { errno = EINVAL; perror(shell); exit(1); } /* Execute the shell. */ argv[0] = argv0; argv[1] = NULL; execve(shell, argv, env); /* Executing the shell failed. */ perror(shell); exit(1); } /* * Execute the command using the user's shell. This uses the -c * option to execute the command. */ argv[0] = (char *) shell0; argv[1] = "-c"; argv[2] = (char *) command; argv[3] = NULL; execve(shell, argv, env); perror(shell); exit(1); } void session_unused(int id) { debug3("%s: session id %d unused", __func__, id); if (id >= options.max_sessions || id >= sessions_nalloc) { fatal("%s: insane session id %d (max %d nalloc %d)", __func__, id, options.max_sessions, sessions_nalloc); } memset(&sessions[id], 0, sizeof(*sessions)); sessions[id].self = id; sessions[id].used = 0; sessions[id].chanid = -1; sessions[id].ptyfd = -1; sessions[id].ttyfd = -1; sessions[id].ptymaster = -1; sessions[id].x11_chanids = NULL; sessions[id].next_unused = sessions_first_unused; sessions_first_unused = id; } Session * session_new(void) { Session *s, *tmp; if (sessions_first_unused == -1) { if (sessions_nalloc >= options.max_sessions) return NULL; debug2("%s: allocate (allocated %d max %d)", __func__, sessions_nalloc, options.max_sessions); tmp = xreallocarray(sessions, sessions_nalloc + 1, sizeof(*sessions)); if (tmp == NULL) { error("%s: cannot allocate %d sessions", __func__, sessions_nalloc + 1); return NULL; } sessions = tmp; session_unused(sessions_nalloc++); } if (sessions_first_unused >= sessions_nalloc || sessions_first_unused < 0) { fatal("%s: insane first_unused %d max %d nalloc %d", __func__, sessions_first_unused, options.max_sessions, sessions_nalloc); } s = &sessions[sessions_first_unused]; if (s->used) { fatal("%s: session %d already used", __func__, sessions_first_unused); } sessions_first_unused = s->next_unused; s->used = 1; s->next_unused = -1; debug("session_new: session %d", s->self); return s; } static void session_dump(void) { int i; for (i = 0; i < sessions_nalloc; i++) { Session *s = &sessions[i]; debug("dump: used %d next_unused %d session %d %p " "channel %d pid %ld", s->used, s->next_unused, s->self, s, s->chanid, (long)s->pid); } } int session_open(Authctxt *authctxt, int chanid) { Session *s = session_new(); debug("session_open: channel %d", chanid); if (s == NULL) { error("no more sessions"); return 0; } s->authctxt = authctxt; s->pw = authctxt->pw; if (s->pw == NULL || !authctxt->valid) fatal("no user for session %d", s->self); debug("session_open: session %d: link with channel %d", s->self, chanid); s->chanid = chanid; return 1; } Session * session_by_tty(char *tty) { int i; for (i = 0; i < sessions_nalloc; i++) { Session *s = &sessions[i]; if (s->used && s->ttyfd != -1 && strcmp(s->tty, tty) == 0) { debug("session_by_tty: session %d tty %s", i, tty); return s; } } debug("session_by_tty: unknown tty %.100s", tty); session_dump(); return NULL; } static Session * session_by_channel(int id) { int i; for (i = 0; i < sessions_nalloc; i++) { Session *s = &sessions[i]; if (s->used && s->chanid == id) { debug("session_by_channel: session %d channel %d", i, id); return s; } } debug("session_by_channel: unknown channel %d", id); session_dump(); return NULL; } static Session * session_by_x11_channel(int id) { int i, j; for (i = 0; i < sessions_nalloc; i++) { Session *s = &sessions[i]; if (s->x11_chanids == NULL || !s->used) continue; for (j = 0; s->x11_chanids[j] != -1; j++) { if (s->x11_chanids[j] == id) { debug("session_by_x11_channel: session %d " "channel %d", s->self, id); return s; } } } debug("session_by_x11_channel: unknown channel %d", id); session_dump(); return NULL; } static Session * session_by_pid(pid_t pid) { int i; debug("session_by_pid: pid %ld", (long)pid); for (i = 0; i < sessions_nalloc; i++) { Session *s = &sessions[i]; if (s->used && s->pid == pid) return s; } error("session_by_pid: unknown pid %ld", (long)pid); session_dump(); return NULL; } static int session_window_change_req(Session *s) { s->col = packet_get_int(); s->row = packet_get_int(); s->xpixel = packet_get_int(); s->ypixel = packet_get_int(); packet_check_eom(); pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); return 1; } static int session_pty_req(Session *s) { u_int len; int n_bytes; if (no_pty_flag || !options.permit_tty) { debug("Allocating a pty not permitted for this authentication."); return 0; } if (s->ttyfd != -1) { packet_disconnect("Protocol error: you already have a pty."); return 0; } s->term = packet_get_string(&len); s->col = packet_get_int(); s->row = packet_get_int(); s->xpixel = packet_get_int(); s->ypixel = packet_get_int(); if (strcmp(s->term, "") == 0) { free(s->term); s->term = NULL; } /* Allocate a pty and open it. */ debug("Allocating pty."); if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, sizeof(s->tty)))) { free(s->term); s->term = NULL; s->ptyfd = -1; s->ttyfd = -1; error("session_pty_req: session %d alloc failed", s->self); return 0; } debug("session_pty_req: session %d alloc %s", s->self, s->tty); n_bytes = packet_remaining(); tty_parse_modes(s->ttyfd, &n_bytes); if (!use_privsep) pty_setowner(s->pw, s->tty); /* Set window size from the packet. */ pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel); packet_check_eom(); session_proctitle(s); return 1; } static int session_subsystem_req(Session *s) { struct stat st; u_int len; int success = 0; char *prog, *cmd; u_int i; s->subsys = packet_get_string(&len); packet_check_eom(); debug2("subsystem request for %.100s by user %s", s->subsys, s->pw->pw_name); for (i = 0; i < options.num_subsystems; i++) { if (strcmp(s->subsys, options.subsystem_name[i]) == 0) { prog = options.subsystem_command[i]; cmd = options.subsystem_args[i]; if (strcmp(INTERNAL_SFTP_NAME, prog) == 0) { s->is_subsystem = SUBSYSTEM_INT_SFTP; debug("subsystem: %s", prog); } else { if (stat(prog, &st) < 0) debug("subsystem: cannot stat %s: %s", prog, strerror(errno)); s->is_subsystem = SUBSYSTEM_EXT; debug("subsystem: exec() %s", cmd); } success = do_exec(s, cmd) == 0; break; } } if (!success) logit("subsystem request for %.100s by user %s failed, " "subsystem not found", s->subsys, s->pw->pw_name); return success; } static int session_x11_req(Session *s) { int success; if (s->auth_proto != NULL || s->auth_data != NULL) { error("session_x11_req: session %d: " "x11 forwarding already active", s->self); return 0; } s->single_connection = packet_get_char(); s->auth_proto = packet_get_string(NULL); s->auth_data = packet_get_string(NULL); s->screen = packet_get_int(); packet_check_eom(); if (xauth_valid_string(s->auth_proto) && xauth_valid_string(s->auth_data)) success = session_setup_x11fwd(s); else { success = 0; error("Invalid X11 forwarding data"); } if (!success) { free(s->auth_proto); free(s->auth_data); s->auth_proto = NULL; s->auth_data = NULL; } return success; } static int session_shell_req(Session *s) { packet_check_eom(); return do_exec(s, NULL) == 0; } static int session_exec_req(Session *s) { u_int len, success; char *command = packet_get_string(&len); packet_check_eom(); success = do_exec(s, command) == 0; free(command); return success; } static int session_break_req(Session *s) { packet_get_int(); /* ignored */ packet_check_eom(); if (s->ptymaster == -1 || tcsendbreak(s->ptymaster, 0) < 0) return 0; return 1; } static int session_env_req(Session *s) { char *name, *val; u_int name_len, val_len, i; name = packet_get_cstring(&name_len); val = packet_get_cstring(&val_len); packet_check_eom(); /* Don't set too many environment variables */ if (s->num_env > 128) { debug2("Ignoring env request %s: too many env vars", name); goto fail; } for (i = 0; i < options.num_accept_env; i++) { if (match_pattern(name, options.accept_env[i])) { debug2("Setting env %d: %s=%s", s->num_env, name, val); s->env = xreallocarray(s->env, s->num_env + 1, sizeof(*s->env)); s->env[s->num_env].name = name; s->env[s->num_env].val = val; s->num_env++; return (1); } } debug2("Ignoring env request %s: disallowed name", name); fail: free(name); free(val); return (0); } static int session_auth_agent_req(Session *s) { static int called = 0; packet_check_eom(); if (no_agent_forwarding_flag || !options.allow_agent_forwarding) { debug("session_auth_agent_req: no_agent_forwarding_flag"); return 0; } if (called) { return 0; } else { called = 1; return auth_input_request_forwarding(s->pw); } } int session_input_channel_req(Channel *c, const char *rtype) { int success = 0; Session *s; if ((s = session_by_channel(c->self)) == NULL) { logit("session_input_channel_req: no session %d req %.100s", c->self, rtype); return 0; } debug("session_input_channel_req: session %d req %s", s->self, rtype); /* * a session is in LARVAL state until a shell, a command * or a subsystem is executed */ if (c->type == SSH_CHANNEL_LARVAL) { if (strcmp(rtype, "shell") == 0) { success = session_shell_req(s); } else if (strcmp(rtype, "exec") == 0) { success = session_exec_req(s); } else if (strcmp(rtype, "pty-req") == 0) { success = session_pty_req(s); } else if (strcmp(rtype, "x11-req") == 0) { success = session_x11_req(s); } else if (strcmp(rtype, "auth-agent-req@openssh.com") == 0) { success = session_auth_agent_req(s); } else if (strcmp(rtype, "subsystem") == 0) { success = session_subsystem_req(s); } else if (strcmp(rtype, "env") == 0) { success = session_env_req(s); } } if (strcmp(rtype, "window-change") == 0) { success = session_window_change_req(s); } else if (strcmp(rtype, "break") == 0) { success = session_break_req(s); } return success; } void session_set_fds(Session *s, int fdin, int fdout, int fderr, int ignore_fderr, int is_tty) { /* * now that have a child and a pipe to the child, * we can activate our channel and register the fd's */ if (s->chanid == -1) fatal("no channel for session %d", s->self); channel_set_fds(s->chanid, fdout, fdin, fderr, ignore_fderr ? CHAN_EXTENDED_IGNORE : CHAN_EXTENDED_READ, 1, is_tty, CHAN_SES_WINDOW_DEFAULT); } /* * Function to perform pty cleanup. Also called if we get aborted abnormally * (e.g., due to a dropped connection). */ void session_pty_cleanup2(Session *s) { if (s == NULL) { error("session_pty_cleanup: no session"); return; } if (s->ttyfd == -1) return; debug("session_pty_cleanup: session %d release %s", s->self, s->tty); /* Record that the user has logged out. */ if (s->pid != 0) record_logout(s->pid, s->tty, s->pw->pw_name); /* Release the pseudo-tty. */ if (getuid() == 0) pty_release(s->tty); /* * Close the server side of the socket pairs. We must do this after * the pty cleanup, so that another process doesn't get this pty * while we're still cleaning up. */ if (s->ptymaster != -1 && close(s->ptymaster) < 0) error("close(s->ptymaster/%d): %s", s->ptymaster, strerror(errno)); /* unlink pty from session */ s->ttyfd = -1; } void session_pty_cleanup(Session *s) { PRIVSEP(session_pty_cleanup2(s)); } static char * sig2name(int sig) { #define SSH_SIG(x) if (sig == SIG ## x) return #x SSH_SIG(ABRT); SSH_SIG(ALRM); SSH_SIG(FPE); SSH_SIG(HUP); SSH_SIG(ILL); SSH_SIG(INT); SSH_SIG(KILL); SSH_SIG(PIPE); SSH_SIG(QUIT); SSH_SIG(SEGV); SSH_SIG(TERM); SSH_SIG(USR1); SSH_SIG(USR2); #undef SSH_SIG return "SIG@openssh.com"; } static void session_close_x11(int id) { Channel *c; if ((c = channel_by_id(id)) == NULL) { debug("session_close_x11: x11 channel %d missing", id); } else { /* Detach X11 listener */ debug("session_close_x11: detach x11 channel %d", id); channel_cancel_cleanup(id); if (c->ostate != CHAN_OUTPUT_CLOSED) chan_mark_dead(c); } } static void session_close_single_x11(int id, void *arg) { Session *s; u_int i; debug3("session_close_single_x11: channel %d", id); channel_cancel_cleanup(id); if ((s = session_by_x11_channel(id)) == NULL) fatal("session_close_single_x11: no x11 channel %d", id); for (i = 0; s->x11_chanids[i] != -1; i++) { debug("session_close_single_x11: session %d: " "closing channel %d", s->self, s->x11_chanids[i]); /* * The channel "id" is already closing, but make sure we * close all of its siblings. */ if (s->x11_chanids[i] != id) session_close_x11(s->x11_chanids[i]); } free(s->x11_chanids); s->x11_chanids = NULL; free(s->display); s->display = NULL; free(s->auth_proto); s->auth_proto = NULL; free(s->auth_data); s->auth_data = NULL; free(s->auth_display); s->auth_display = NULL; } static void session_exit_message(Session *s, int status) { Channel *c; if ((c = channel_lookup(s->chanid)) == NULL) fatal("session_exit_message: session %d: no channel %d", s->self, s->chanid); debug("session_exit_message: session %d channel %d pid %ld", s->self, s->chanid, (long)s->pid); if (WIFEXITED(status)) { channel_request_start(s->chanid, "exit-status", 0); packet_put_int(WEXITSTATUS(status)); packet_send(); } else if (WIFSIGNALED(status)) { channel_request_start(s->chanid, "exit-signal", 0); packet_put_cstring(sig2name(WTERMSIG(status))); #ifdef WCOREDUMP packet_put_char(WCOREDUMP(status)? 1 : 0); #else /* WCOREDUMP */ packet_put_char(0); #endif /* WCOREDUMP */ packet_put_cstring(""); packet_put_cstring(""); packet_send(); } else { /* Some weird exit cause. Just exit. */ packet_disconnect("wait returned status %04x.", status); } /* disconnect channel */ debug("session_exit_message: release channel %d", s->chanid); /* * Adjust cleanup callback attachment to send close messages when * the channel gets EOF. The session will be then be closed * by session_close_by_channel when the childs close their fds. */ channel_register_cleanup(c->self, session_close_by_channel, 1); /* * emulate a write failure with 'chan_write_failed', nobody will be * interested in data we write. * Note that we must not call 'chan_read_failed', since there could * be some more data waiting in the pipe. */ if (c->ostate != CHAN_OUTPUT_CLOSED) chan_write_failed(c); } void session_close(Session *s) { struct ssh *ssh = active_state; /* XXX */ u_int i; verbose("Close session: user %s from %.200s port %d id %d", s->pw->pw_name, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), s->self); if (s->ttyfd != -1) session_pty_cleanup(s); free(s->term); free(s->display); free(s->x11_chanids); free(s->auth_display); free(s->auth_data); free(s->auth_proto); free(s->subsys); if (s->env != NULL) { for (i = 0; i < s->num_env; i++) { free(s->env[i].name); free(s->env[i].val); } free(s->env); } session_proctitle(s); session_unused(s->self); } void session_close_by_pid(pid_t pid, int status) { Session *s = session_by_pid(pid); if (s == NULL) { debug("session_close_by_pid: no session for pid %ld", (long)pid); return; } if (s->chanid != -1) session_exit_message(s, status); if (s->ttyfd != -1) session_pty_cleanup(s); s->pid = 0; } /* * this is called when a channel dies before * the session 'child' itself dies */ void session_close_by_channel(int id, void *arg) { Session *s = session_by_channel(id); u_int i; if (s == NULL) { debug("session_close_by_channel: no session for id %d", id); return; } debug("session_close_by_channel: channel %d child %ld", id, (long)s->pid); if (s->pid != 0) { debug("session_close_by_channel: channel %d: has child", id); /* * delay detach of session, but release pty, since * the fd's to the child are already closed */ if (s->ttyfd != -1) session_pty_cleanup(s); return; } /* detach by removing callback */ channel_cancel_cleanup(s->chanid); /* Close any X11 listeners associated with this session */ if (s->x11_chanids != NULL) { for (i = 0; s->x11_chanids[i] != -1; i++) { session_close_x11(s->x11_chanids[i]); s->x11_chanids[i] = -1; } } s->chanid = -1; session_close(s); } void session_destroy_all(void (*closefunc)(Session *)) { int i; for (i = 0; i < sessions_nalloc; i++) { Session *s = &sessions[i]; if (s->used) { if (closefunc != NULL) closefunc(s); else session_close(s); } } } static char * session_tty_list(void) { static char buf[1024]; int i; char *cp; buf[0] = '\0'; for (i = 0; i < sessions_nalloc; i++) { Session *s = &sessions[i]; if (s->used && s->ttyfd != -1) { if (strncmp(s->tty, "/dev/", 5) != 0) { cp = strrchr(s->tty, '/'); cp = (cp == NULL) ? s->tty : cp + 1; } else cp = s->tty + 5; if (buf[0] != '\0') strlcat(buf, ",", sizeof buf); strlcat(buf, cp, sizeof buf); } } if (buf[0] == '\0') strlcpy(buf, "notty", sizeof buf); return buf; } void session_proctitle(Session *s) { if (s->pw == NULL) error("no user for session %d", s->self); else setproctitle("%s@%s", s->pw->pw_name, session_tty_list()); } int session_setup_x11fwd(Session *s) { struct stat st; char display[512], auth_display[512]; char hostname[NI_MAXHOST]; u_int i; if (no_x11_forwarding_flag) { packet_send_debug("X11 forwarding disabled in user configuration file."); return 0; } if (!options.x11_forwarding) { debug("X11 forwarding disabled in server configuration file."); return 0; } if (options.xauth_location == NULL || (stat(options.xauth_location, &st) == -1)) { packet_send_debug("No xauth program; cannot forward with spoofing."); return 0; } if (s->display != NULL) { debug("X11 display already set."); return 0; } if (x11_create_display_inet(options.x11_display_offset, options.x11_use_localhost, s->single_connection, &s->display_number, &s->x11_chanids) == -1) { debug("x11_create_display_inet failed."); return 0; } for (i = 0; s->x11_chanids[i] != -1; i++) { channel_register_cleanup(s->x11_chanids[i], session_close_single_x11, 0); } /* Set up a suitable value for the DISPLAY variable. */ if (gethostname(hostname, sizeof(hostname)) < 0) fatal("gethostname: %.100s", strerror(errno)); /* * auth_display must be used as the displayname when the * authorization entry is added with xauth(1). This will be * different than the DISPLAY string for localhost displays. */ if (options.x11_use_localhost) { snprintf(display, sizeof display, "localhost:%u.%u", s->display_number, s->screen); snprintf(auth_display, sizeof auth_display, "unix:%u.%u", s->display_number, s->screen); s->display = xstrdup(display); s->auth_display = xstrdup(auth_display); } else { #ifdef IPADDR_IN_DISPLAY struct hostent *he; struct in_addr my_addr; he = gethostbyname(hostname); if (he == NULL) { error("Can't get IP address for X11 DISPLAY."); packet_send_debug("Can't get IP address for X11 DISPLAY."); return 0; } memcpy(&my_addr, he->h_addr_list[0], sizeof(struct in_addr)); snprintf(display, sizeof display, "%.50s:%u.%u", inet_ntoa(my_addr), s->display_number, s->screen); #else snprintf(display, sizeof display, "%.400s:%u.%u", hostname, s->display_number, s->screen); #endif s->display = xstrdup(display); s->auth_display = xstrdup(display); } return 1; } static void do_authenticated2(Authctxt *authctxt) { server_loop2(authctxt); } void do_cleanup(Authctxt *authctxt) { static int called = 0; debug("do_cleanup"); /* no cleanup if we're in the child for login shell */ if (is_child) return; /* avoid double cleanup */ if (called) return; called = 1; if (authctxt == NULL) return; #ifdef USE_PAM if (options.use_pam) { sshpam_cleanup(); sshpam_thread_cleanup(); } #endif if (!authctxt->authenticated) return; #ifdef KRB5 if (options.kerberos_ticket_cleanup && authctxt->krb5_ctx) krb5_cleanup_proc(authctxt); #endif #ifdef GSSAPI if (options.gss_cleanup_creds) ssh_gssapi_cleanup_creds(); #endif /* remove agent socket */ auth_sock_cleanup_proc(authctxt->pw); /* * Cleanup ptys/utmp only if privsep is disabled, * or if running in monitor. */ if (!use_privsep || mm_is_monitor()) session_destroy_all(session_pty_cleanup2); } /* Return a name for the remote host that fits inside utmp_size */ const char * session_get_remote_name_or_ip(struct ssh *ssh, u_int utmp_size, int use_dns) { const char *remote = ""; if (utmp_size > 0) remote = auth_get_canonical_hostname(ssh, use_dns); if (utmp_size == 0 || strlen(remote) > utmp_size) remote = ssh_remote_ipaddr(ssh); return remote; } diff --git a/crypto/openssh/sftp-client.c b/crypto/openssh/sftp-client.c index e65c15c8f728..a6e832270410 100644 --- a/crypto/openssh/sftp-client.c +++ b/crypto/openssh/sftp-client.c @@ -1,1897 +1,1906 @@ -/* $OpenBSD: sftp-client.c,v 1.125 2016/09/12 01:22:38 deraadt Exp $ */ +/* $OpenBSD: sftp-client.c,v 1.126 2017/01/03 05:46:51 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* XXX: memleaks */ /* XXX: signed vs unsigned */ /* XXX: remove all logging, only return status codes */ /* XXX: copy between two remote sites */ #include "includes.h" #include #ifdef HAVE_SYS_STATVFS_H #include #endif #include "openbsd-compat/sys-queue.h" #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include #include #include #include #include #include #include #include #include "xmalloc.h" #include "ssherr.h" #include "sshbuf.h" #include "log.h" #include "atomicio.h" #include "progressmeter.h" #include "misc.h" #include "utf8.h" #include "sftp.h" #include "sftp-common.h" #include "sftp-client.h" extern volatile sig_atomic_t interrupted; extern int showprogress; /* Minimum amount of data to read at a time */ #define MIN_READ_SIZE 512 /* Maximum depth to descend in directory trees */ #define MAX_DIR_DEPTH 64 +/* Directory separator characters */ +#ifdef HAVE_CYGWIN +# define SFTP_DIRECTORY_CHARS "/\\" +#else /* HAVE_CYGWIN */ +# define SFTP_DIRECTORY_CHARS "/" +#endif /* HAVE_CYGWIN */ + struct sftp_conn { int fd_in; int fd_out; u_int transfer_buflen; u_int num_requests; u_int version; u_int msg_id; #define SFTP_EXT_POSIX_RENAME 0x00000001 #define SFTP_EXT_STATVFS 0x00000002 #define SFTP_EXT_FSTATVFS 0x00000004 #define SFTP_EXT_HARDLINK 0x00000008 #define SFTP_EXT_FSYNC 0x00000010 u_int exts; u_int64_t limit_kbps; struct bwlimit bwlimit_in, bwlimit_out; }; static u_char * get_handle(struct sftp_conn *conn, u_int expected_id, size_t *len, const char *errfmt, ...) __attribute__((format(printf, 4, 5))); /* ARGSUSED */ static int sftpio(void *_bwlimit, size_t amount) { struct bwlimit *bwlimit = (struct bwlimit *)_bwlimit; bandwidth_limit(bwlimit, amount); return 0; } static void send_msg(struct sftp_conn *conn, struct sshbuf *m) { u_char mlen[4]; struct iovec iov[2]; if (sshbuf_len(m) > SFTP_MAX_MSG_LENGTH) fatal("Outbound message too long %zu", sshbuf_len(m)); /* Send length first */ put_u32(mlen, sshbuf_len(m)); iov[0].iov_base = mlen; iov[0].iov_len = sizeof(mlen); iov[1].iov_base = (u_char *)sshbuf_ptr(m); iov[1].iov_len = sshbuf_len(m); if (atomiciov6(writev, conn->fd_out, iov, 2, conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_out) != sshbuf_len(m) + sizeof(mlen)) fatal("Couldn't send packet: %s", strerror(errno)); sshbuf_reset(m); } static void get_msg(struct sftp_conn *conn, struct sshbuf *m) { u_int msg_len; u_char *p; int r; if ((r = sshbuf_reserve(m, 4, &p)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (atomicio6(read, conn->fd_in, p, 4, conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in) != 4) { if (errno == EPIPE) fatal("Connection closed"); else fatal("Couldn't read packet: %s", strerror(errno)); } if ((r = sshbuf_get_u32(m, &msg_len)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (msg_len > SFTP_MAX_MSG_LENGTH) fatal("Received message too long %u", msg_len); if ((r = sshbuf_reserve(m, msg_len, &p)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (atomicio6(read, conn->fd_in, p, msg_len, conn->limit_kbps > 0 ? sftpio : NULL, &conn->bwlimit_in) != msg_len) { if (errno == EPIPE) fatal("Connection closed"); else fatal("Read packet: %s", strerror(errno)); } } static void send_string_request(struct sftp_conn *conn, u_int id, u_int code, const char *s, u_int len) { struct sshbuf *msg; int r; if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); if ((r = sshbuf_put_u8(msg, code)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_string(msg, s, len)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); send_msg(conn, msg); debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id); sshbuf_free(msg); } static void send_string_attrs_request(struct sftp_conn *conn, u_int id, u_int code, const void *s, u_int len, Attrib *a) { struct sshbuf *msg; int r; if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); if ((r = sshbuf_put_u8(msg, code)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_string(msg, s, len)) != 0 || (r = encode_attrib(msg, a)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); send_msg(conn, msg); debug3("Sent message fd %d T:%u I:%u", conn->fd_out, code, id); sshbuf_free(msg); } static u_int get_status(struct sftp_conn *conn, u_int expected_id) { struct sshbuf *msg; u_char type; u_int id, status; int r; if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); get_msg(conn, msg); if ((r = sshbuf_get_u8(msg, &type)) != 0 || (r = sshbuf_get_u32(msg, &id)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (id != expected_id) fatal("ID mismatch (%u != %u)", id, expected_id); if (type != SSH2_FXP_STATUS) fatal("Expected SSH2_FXP_STATUS(%u) packet, got %u", SSH2_FXP_STATUS, type); if ((r = sshbuf_get_u32(msg, &status)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); sshbuf_free(msg); debug3("SSH2_FXP_STATUS %u", status); return status; } static u_char * get_handle(struct sftp_conn *conn, u_int expected_id, size_t *len, const char *errfmt, ...) { struct sshbuf *msg; u_int id, status; u_char type; u_char *handle; char errmsg[256]; va_list args; int r; va_start(args, errfmt); if (errfmt != NULL) vsnprintf(errmsg, sizeof(errmsg), errfmt, args); va_end(args); if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); get_msg(conn, msg); if ((r = sshbuf_get_u8(msg, &type)) != 0 || (r = sshbuf_get_u32(msg, &id)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (id != expected_id) fatal("%s: ID mismatch (%u != %u)", errfmt == NULL ? __func__ : errmsg, id, expected_id); if (type == SSH2_FXP_STATUS) { if ((r = sshbuf_get_u32(msg, &status)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (errfmt != NULL) error("%s: %s", errmsg, fx2txt(status)); sshbuf_free(msg); return(NULL); } else if (type != SSH2_FXP_HANDLE) fatal("%s: Expected SSH2_FXP_HANDLE(%u) packet, got %u", errfmt == NULL ? __func__ : errmsg, SSH2_FXP_HANDLE, type); if ((r = sshbuf_get_string(msg, &handle, len)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); sshbuf_free(msg); return handle; } static Attrib * get_decode_stat(struct sftp_conn *conn, u_int expected_id, int quiet) { struct sshbuf *msg; u_int id; u_char type; int r; static Attrib a; if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); get_msg(conn, msg); if ((r = sshbuf_get_u8(msg, &type)) != 0 || (r = sshbuf_get_u32(msg, &id)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); debug3("Received stat reply T:%u I:%u", type, id); if (id != expected_id) fatal("ID mismatch (%u != %u)", id, expected_id); if (type == SSH2_FXP_STATUS) { u_int status; if ((r = sshbuf_get_u32(msg, &status)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (quiet) debug("Couldn't stat remote file: %s", fx2txt(status)); else error("Couldn't stat remote file: %s", fx2txt(status)); sshbuf_free(msg); return(NULL); } else if (type != SSH2_FXP_ATTRS) { fatal("Expected SSH2_FXP_ATTRS(%u) packet, got %u", SSH2_FXP_ATTRS, type); } if ((r = decode_attrib(msg, &a)) != 0) { error("%s: couldn't decode attrib: %s", __func__, ssh_err(r)); sshbuf_free(msg); return NULL; } sshbuf_free(msg); return &a; } static int get_decode_statvfs(struct sftp_conn *conn, struct sftp_statvfs *st, u_int expected_id, int quiet) { struct sshbuf *msg; u_char type; u_int id; u_int64_t flag; int r; if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); get_msg(conn, msg); if ((r = sshbuf_get_u8(msg, &type)) != 0 || (r = sshbuf_get_u32(msg, &id)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); debug3("Received statvfs reply T:%u I:%u", type, id); if (id != expected_id) fatal("ID mismatch (%u != %u)", id, expected_id); if (type == SSH2_FXP_STATUS) { u_int status; if ((r = sshbuf_get_u32(msg, &status)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (quiet) debug("Couldn't statvfs: %s", fx2txt(status)); else error("Couldn't statvfs: %s", fx2txt(status)); sshbuf_free(msg); return -1; } else if (type != SSH2_FXP_EXTENDED_REPLY) { fatal("Expected SSH2_FXP_EXTENDED_REPLY(%u) packet, got %u", SSH2_FXP_EXTENDED_REPLY, type); } memset(st, 0, sizeof(*st)); if ((r = sshbuf_get_u64(msg, &st->f_bsize)) != 0 || (r = sshbuf_get_u64(msg, &st->f_frsize)) != 0 || (r = sshbuf_get_u64(msg, &st->f_blocks)) != 0 || (r = sshbuf_get_u64(msg, &st->f_bfree)) != 0 || (r = sshbuf_get_u64(msg, &st->f_bavail)) != 0 || (r = sshbuf_get_u64(msg, &st->f_files)) != 0 || (r = sshbuf_get_u64(msg, &st->f_ffree)) != 0 || (r = sshbuf_get_u64(msg, &st->f_favail)) != 0 || (r = sshbuf_get_u64(msg, &st->f_fsid)) != 0 || (r = sshbuf_get_u64(msg, &flag)) != 0 || (r = sshbuf_get_u64(msg, &st->f_namemax)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); st->f_flag = (flag & SSH2_FXE_STATVFS_ST_RDONLY) ? ST_RDONLY : 0; st->f_flag |= (flag & SSH2_FXE_STATVFS_ST_NOSUID) ? ST_NOSUID : 0; sshbuf_free(msg); return 0; } struct sftp_conn * do_init(int fd_in, int fd_out, u_int transfer_buflen, u_int num_requests, u_int64_t limit_kbps) { u_char type; struct sshbuf *msg; struct sftp_conn *ret; int r; ret = xcalloc(1, sizeof(*ret)); ret->msg_id = 1; ret->fd_in = fd_in; ret->fd_out = fd_out; ret->transfer_buflen = transfer_buflen; ret->num_requests = num_requests; ret->exts = 0; ret->limit_kbps = 0; if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); if ((r = sshbuf_put_u8(msg, SSH2_FXP_INIT)) != 0 || (r = sshbuf_put_u32(msg, SSH2_FILEXFER_VERSION)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); send_msg(ret, msg); sshbuf_reset(msg); get_msg(ret, msg); /* Expecting a VERSION reply */ if ((r = sshbuf_get_u8(msg, &type)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (type != SSH2_FXP_VERSION) { error("Invalid packet back from SSH2_FXP_INIT (type %u)", type); sshbuf_free(msg); free(ret); return(NULL); } if ((r = sshbuf_get_u32(msg, &ret->version)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); debug2("Remote version: %u", ret->version); /* Check for extensions */ while (sshbuf_len(msg) > 0) { char *name; u_char *value; size_t vlen; int known = 0; if ((r = sshbuf_get_cstring(msg, &name, NULL)) != 0 || (r = sshbuf_get_string(msg, &value, &vlen)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (strcmp(name, "posix-rename@openssh.com") == 0 && strcmp((char *)value, "1") == 0) { ret->exts |= SFTP_EXT_POSIX_RENAME; known = 1; } else if (strcmp(name, "statvfs@openssh.com") == 0 && strcmp((char *)value, "2") == 0) { ret->exts |= SFTP_EXT_STATVFS; known = 1; } else if (strcmp(name, "fstatvfs@openssh.com") == 0 && strcmp((char *)value, "2") == 0) { ret->exts |= SFTP_EXT_FSTATVFS; known = 1; } else if (strcmp(name, "hardlink@openssh.com") == 0 && strcmp((char *)value, "1") == 0) { ret->exts |= SFTP_EXT_HARDLINK; known = 1; } else if (strcmp(name, "fsync@openssh.com") == 0 && strcmp((char *)value, "1") == 0) { ret->exts |= SFTP_EXT_FSYNC; known = 1; } if (known) { debug2("Server supports extension \"%s\" revision %s", name, value); } else { debug2("Unrecognised server extension \"%s\"", name); } free(name); free(value); } sshbuf_free(msg); /* Some filexfer v.0 servers don't support large packets */ if (ret->version == 0) ret->transfer_buflen = MINIMUM(ret->transfer_buflen, 20480); ret->limit_kbps = limit_kbps; if (ret->limit_kbps > 0) { bandwidth_limit_init(&ret->bwlimit_in, ret->limit_kbps, ret->transfer_buflen); bandwidth_limit_init(&ret->bwlimit_out, ret->limit_kbps, ret->transfer_buflen); } return ret; } u_int sftp_proto_version(struct sftp_conn *conn) { return conn->version; } int do_close(struct sftp_conn *conn, const u_char *handle, u_int handle_len) { u_int id, status; struct sshbuf *msg; int r; if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); id = conn->msg_id++; if ((r = sshbuf_put_u8(msg, SSH2_FXP_CLOSE)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_string(msg, handle, handle_len)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); send_msg(conn, msg); debug3("Sent message SSH2_FXP_CLOSE I:%u", id); status = get_status(conn, id); if (status != SSH2_FX_OK) error("Couldn't close file: %s", fx2txt(status)); sshbuf_free(msg); return status == SSH2_FX_OK ? 0 : -1; } static int do_lsreaddir(struct sftp_conn *conn, const char *path, int print_flag, SFTP_DIRENT ***dir) { struct sshbuf *msg; u_int count, id, i, expected_id, ents = 0; size_t handle_len; u_char type, *handle; int status = SSH2_FX_FAILURE; int r; if (dir) *dir = NULL; id = conn->msg_id++; if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPENDIR)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_cstring(msg, path)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); send_msg(conn, msg); handle = get_handle(conn, id, &handle_len, "remote readdir(\"%s\")", path); if (handle == NULL) { sshbuf_free(msg); return -1; } if (dir) { ents = 0; *dir = xcalloc(1, sizeof(**dir)); (*dir)[0] = NULL; } for (; !interrupted;) { id = expected_id = conn->msg_id++; debug3("Sending SSH2_FXP_READDIR I:%u", id); sshbuf_reset(msg); if ((r = sshbuf_put_u8(msg, SSH2_FXP_READDIR)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_string(msg, handle, handle_len)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); send_msg(conn, msg); sshbuf_reset(msg); get_msg(conn, msg); if ((r = sshbuf_get_u8(msg, &type)) != 0 || (r = sshbuf_get_u32(msg, &id)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); debug3("Received reply T:%u I:%u", type, id); if (id != expected_id) fatal("ID mismatch (%u != %u)", id, expected_id); if (type == SSH2_FXP_STATUS) { u_int rstatus; if ((r = sshbuf_get_u32(msg, &rstatus)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); debug3("Received SSH2_FXP_STATUS %d", rstatus); if (rstatus == SSH2_FX_EOF) break; error("Couldn't read directory: %s", fx2txt(rstatus)); goto out; } else if (type != SSH2_FXP_NAME) fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", SSH2_FXP_NAME, type); if ((r = sshbuf_get_u32(msg, &count)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); + if (count > SSHBUF_SIZE_MAX) + fatal("%s: nonsensical number of entries", __func__); if (count == 0) break; debug3("Received %d SSH2_FXP_NAME responses", count); for (i = 0; i < count; i++) { char *filename, *longname; Attrib a; if ((r = sshbuf_get_cstring(msg, &filename, NULL)) != 0 || (r = sshbuf_get_cstring(msg, &longname, NULL)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if ((r = decode_attrib(msg, &a)) != 0) { error("%s: couldn't decode attrib: %s", __func__, ssh_err(r)); free(filename); free(longname); sshbuf_free(msg); return -1; } if (print_flag) mprintf("%s\n", longname); /* * Directory entries should never contain '/' * These can be used to attack recursive ops * (e.g. send '../../../../etc/passwd') */ - if (strchr(filename, '/') != NULL) { + if (strpbrk(filename, SFTP_DIRECTORY_CHARS) != NULL) { error("Server sent suspect path \"%s\" " "during readdir of \"%s\"", filename, path); } else if (dir) { *dir = xreallocarray(*dir, ents + 2, sizeof(**dir)); (*dir)[ents] = xcalloc(1, sizeof(***dir)); (*dir)[ents]->filename = xstrdup(filename); (*dir)[ents]->longname = xstrdup(longname); memcpy(&(*dir)[ents]->a, &a, sizeof(a)); (*dir)[++ents] = NULL; } free(filename); free(longname); } } status = 0; out: sshbuf_free(msg); do_close(conn, handle, handle_len); free(handle); if (status != 0 && dir != NULL) { /* Don't return results on error */ free_sftp_dirents(*dir); *dir = NULL; } else if (interrupted && dir != NULL && *dir != NULL) { /* Don't return partial matches on interrupt */ free_sftp_dirents(*dir); *dir = xcalloc(1, sizeof(**dir)); **dir = NULL; } return status; } int do_readdir(struct sftp_conn *conn, const char *path, SFTP_DIRENT ***dir) { return(do_lsreaddir(conn, path, 0, dir)); } void free_sftp_dirents(SFTP_DIRENT **s) { int i; if (s == NULL) return; for (i = 0; s[i]; i++) { free(s[i]->filename); free(s[i]->longname); free(s[i]); } free(s); } int do_rm(struct sftp_conn *conn, const char *path) { u_int status, id; debug2("Sending SSH2_FXP_REMOVE \"%s\"", path); id = conn->msg_id++; send_string_request(conn, id, SSH2_FXP_REMOVE, path, strlen(path)); status = get_status(conn, id); if (status != SSH2_FX_OK) error("Couldn't delete file: %s", fx2txt(status)); return status == SSH2_FX_OK ? 0 : -1; } int do_mkdir(struct sftp_conn *conn, const char *path, Attrib *a, int print_flag) { u_int status, id; id = conn->msg_id++; send_string_attrs_request(conn, id, SSH2_FXP_MKDIR, path, strlen(path), a); status = get_status(conn, id); if (status != SSH2_FX_OK && print_flag) error("Couldn't create directory: %s", fx2txt(status)); return status == SSH2_FX_OK ? 0 : -1; } int do_rmdir(struct sftp_conn *conn, const char *path) { u_int status, id; id = conn->msg_id++; send_string_request(conn, id, SSH2_FXP_RMDIR, path, strlen(path)); status = get_status(conn, id); if (status != SSH2_FX_OK) error("Couldn't remove directory: %s", fx2txt(status)); return status == SSH2_FX_OK ? 0 : -1; } Attrib * do_stat(struct sftp_conn *conn, const char *path, int quiet) { u_int id; id = conn->msg_id++; send_string_request(conn, id, conn->version == 0 ? SSH2_FXP_STAT_VERSION_0 : SSH2_FXP_STAT, path, strlen(path)); return(get_decode_stat(conn, id, quiet)); } Attrib * do_lstat(struct sftp_conn *conn, const char *path, int quiet) { u_int id; if (conn->version == 0) { if (quiet) debug("Server version does not support lstat operation"); else logit("Server version does not support lstat operation"); return(do_stat(conn, path, quiet)); } id = conn->msg_id++; send_string_request(conn, id, SSH2_FXP_LSTAT, path, strlen(path)); return(get_decode_stat(conn, id, quiet)); } #ifdef notyet Attrib * do_fstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len, int quiet) { u_int id; id = conn->msg_id++; send_string_request(conn, id, SSH2_FXP_FSTAT, handle, handle_len); return(get_decode_stat(conn, id, quiet)); } #endif int do_setstat(struct sftp_conn *conn, const char *path, Attrib *a) { u_int status, id; id = conn->msg_id++; send_string_attrs_request(conn, id, SSH2_FXP_SETSTAT, path, strlen(path), a); status = get_status(conn, id); if (status != SSH2_FX_OK) error("Couldn't setstat on \"%s\": %s", path, fx2txt(status)); return status == SSH2_FX_OK ? 0 : -1; } int do_fsetstat(struct sftp_conn *conn, const u_char *handle, u_int handle_len, Attrib *a) { u_int status, id; id = conn->msg_id++; send_string_attrs_request(conn, id, SSH2_FXP_FSETSTAT, handle, handle_len, a); status = get_status(conn, id); if (status != SSH2_FX_OK) error("Couldn't fsetstat: %s", fx2txt(status)); return status == SSH2_FX_OK ? 0 : -1; } char * do_realpath(struct sftp_conn *conn, const char *path) { struct sshbuf *msg; u_int expected_id, count, id; char *filename, *longname; Attrib a; u_char type; int r; expected_id = id = conn->msg_id++; send_string_request(conn, id, SSH2_FXP_REALPATH, path, strlen(path)); if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); get_msg(conn, msg); if ((r = sshbuf_get_u8(msg, &type)) != 0 || (r = sshbuf_get_u32(msg, &id)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (id != expected_id) fatal("ID mismatch (%u != %u)", id, expected_id); if (type == SSH2_FXP_STATUS) { u_int status; if ((r = sshbuf_get_u32(msg, &status)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); error("Couldn't canonicalize: %s", fx2txt(status)); sshbuf_free(msg); return NULL; } else if (type != SSH2_FXP_NAME) fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", SSH2_FXP_NAME, type); if ((r = sshbuf_get_u32(msg, &count)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (count != 1) fatal("Got multiple names (%d) from SSH_FXP_REALPATH", count); if ((r = sshbuf_get_cstring(msg, &filename, NULL)) != 0 || (r = sshbuf_get_cstring(msg, &longname, NULL)) != 0 || (r = decode_attrib(msg, &a)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); debug3("SSH_FXP_REALPATH %s -> %s size %lu", path, filename, (unsigned long)a.size); free(longname); sshbuf_free(msg); return(filename); } int do_rename(struct sftp_conn *conn, const char *oldpath, const char *newpath, int force_legacy) { struct sshbuf *msg; u_int status, id; int r, use_ext = (conn->exts & SFTP_EXT_POSIX_RENAME) && !force_legacy; if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); /* Send rename request */ id = conn->msg_id++; if (use_ext) { if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_cstring(msg, "posix-rename@openssh.com")) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); } else { if ((r = sshbuf_put_u8(msg, SSH2_FXP_RENAME)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); } if ((r = sshbuf_put_cstring(msg, oldpath)) != 0 || (r = sshbuf_put_cstring(msg, newpath)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); send_msg(conn, msg); debug3("Sent message %s \"%s\" -> \"%s\"", use_ext ? "posix-rename@openssh.com" : "SSH2_FXP_RENAME", oldpath, newpath); sshbuf_free(msg); status = get_status(conn, id); if (status != SSH2_FX_OK) error("Couldn't rename file \"%s\" to \"%s\": %s", oldpath, newpath, fx2txt(status)); return status == SSH2_FX_OK ? 0 : -1; } int do_hardlink(struct sftp_conn *conn, const char *oldpath, const char *newpath) { struct sshbuf *msg; u_int status, id; int r; if ((conn->exts & SFTP_EXT_HARDLINK) == 0) { error("Server does not support hardlink@openssh.com extension"); return -1; } if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); /* Send link request */ id = conn->msg_id++; if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_cstring(msg, "hardlink@openssh.com")) != 0 || (r = sshbuf_put_cstring(msg, oldpath)) != 0 || (r = sshbuf_put_cstring(msg, newpath)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); send_msg(conn, msg); debug3("Sent message hardlink@openssh.com \"%s\" -> \"%s\"", oldpath, newpath); sshbuf_free(msg); status = get_status(conn, id); if (status != SSH2_FX_OK) error("Couldn't link file \"%s\" to \"%s\": %s", oldpath, newpath, fx2txt(status)); return status == SSH2_FX_OK ? 0 : -1; } int do_symlink(struct sftp_conn *conn, const char *oldpath, const char *newpath) { struct sshbuf *msg; u_int status, id; int r; if (conn->version < 3) { error("This server does not support the symlink operation"); return(SSH2_FX_OP_UNSUPPORTED); } if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); /* Send symlink request */ id = conn->msg_id++; if ((r = sshbuf_put_u8(msg, SSH2_FXP_SYMLINK)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_cstring(msg, oldpath)) != 0 || (r = sshbuf_put_cstring(msg, newpath)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); send_msg(conn, msg); debug3("Sent message SSH2_FXP_SYMLINK \"%s\" -> \"%s\"", oldpath, newpath); sshbuf_free(msg); status = get_status(conn, id); if (status != SSH2_FX_OK) error("Couldn't symlink file \"%s\" to \"%s\": %s", oldpath, newpath, fx2txt(status)); return status == SSH2_FX_OK ? 0 : -1; } int do_fsync(struct sftp_conn *conn, u_char *handle, u_int handle_len) { struct sshbuf *msg; u_int status, id; int r; /* Silently return if the extension is not supported */ if ((conn->exts & SFTP_EXT_FSYNC) == 0) return -1; /* Send fsync request */ if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); id = conn->msg_id++; if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_cstring(msg, "fsync@openssh.com")) != 0 || (r = sshbuf_put_string(msg, handle, handle_len)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); send_msg(conn, msg); debug3("Sent message fsync@openssh.com I:%u", id); sshbuf_free(msg); status = get_status(conn, id); if (status != SSH2_FX_OK) error("Couldn't sync file: %s", fx2txt(status)); return status; } #ifdef notyet char * do_readlink(struct sftp_conn *conn, const char *path) { struct sshbuf *msg; u_int expected_id, count, id; char *filename, *longname; Attrib a; u_char type; int r; expected_id = id = conn->msg_id++; send_string_request(conn, id, SSH2_FXP_READLINK, path, strlen(path)); if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); get_msg(conn, msg); if ((r = sshbuf_get_u8(msg, &type)) != 0 || (r = sshbuf_get_u32(msg, &id)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (id != expected_id) fatal("ID mismatch (%u != %u)", id, expected_id); if (type == SSH2_FXP_STATUS) { u_int status; if ((r = sshbuf_get_u32(msg, &status)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); error("Couldn't readlink: %s", fx2txt(status)); sshbuf_free(msg); return(NULL); } else if (type != SSH2_FXP_NAME) fatal("Expected SSH2_FXP_NAME(%u) packet, got %u", SSH2_FXP_NAME, type); if ((r = sshbuf_get_u32(msg, &count)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (count != 1) fatal("Got multiple names (%d) from SSH_FXP_READLINK", count); if ((r = sshbuf_get_cstring(msg, &filename, NULL)) != 0 || (r = sshbuf_get_cstring(msg, &longname, NULL)) != 0 || (r = decode_attrib(msg, &a)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); debug3("SSH_FXP_READLINK %s -> %s", path, filename); free(longname); sshbuf_free(msg); return filename; } #endif int do_statvfs(struct sftp_conn *conn, const char *path, struct sftp_statvfs *st, int quiet) { struct sshbuf *msg; u_int id; int r; if ((conn->exts & SFTP_EXT_STATVFS) == 0) { error("Server does not support statvfs@openssh.com extension"); return -1; } id = conn->msg_id++; if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); sshbuf_reset(msg); if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_cstring(msg, "statvfs@openssh.com")) != 0 || (r = sshbuf_put_cstring(msg, path)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); send_msg(conn, msg); sshbuf_free(msg); return get_decode_statvfs(conn, st, id, quiet); } #ifdef notyet int do_fstatvfs(struct sftp_conn *conn, const u_char *handle, u_int handle_len, struct sftp_statvfs *st, int quiet) { struct sshbuf *msg; u_int id; if ((conn->exts & SFTP_EXT_FSTATVFS) == 0) { error("Server does not support fstatvfs@openssh.com extension"); return -1; } id = conn->msg_id++; if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); sshbuf_reset(msg); if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_cstring(msg, "fstatvfs@openssh.com")) != 0 || (r = sshbuf_put_string(msg, handle, handle_len)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); send_msg(conn, msg); sshbuf_free(msg); return get_decode_statvfs(conn, st, id, quiet); } #endif static void send_read_request(struct sftp_conn *conn, u_int id, u_int64_t offset, u_int len, const u_char *handle, u_int handle_len) { struct sshbuf *msg; int r; if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); sshbuf_reset(msg); if ((r = sshbuf_put_u8(msg, SSH2_FXP_READ)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_string(msg, handle, handle_len)) != 0 || (r = sshbuf_put_u64(msg, offset)) != 0 || (r = sshbuf_put_u32(msg, len)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); send_msg(conn, msg); sshbuf_free(msg); } int do_download(struct sftp_conn *conn, const char *remote_path, const char *local_path, Attrib *a, int preserve_flag, int resume_flag, int fsync_flag) { Attrib junk; struct sshbuf *msg; u_char *handle; int local_fd = -1, write_error; int read_error, write_errno, reordered = 0, r; u_int64_t offset = 0, size, highwater; u_int mode, id, buflen, num_req, max_req, status = SSH2_FX_OK; off_t progress_counter; size_t handle_len; struct stat st; struct request { u_int id; size_t len; u_int64_t offset; TAILQ_ENTRY(request) tq; }; TAILQ_HEAD(reqhead, request) requests; struct request *req; u_char type; TAILQ_INIT(&requests); if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL) return -1; /* Do not preserve set[ug]id here, as we do not preserve ownership */ if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) mode = a->perm & 0777; else mode = 0666; if ((a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) && (!S_ISREG(a->perm))) { error("Cannot download non-regular file: %s", remote_path); return(-1); } if (a->flags & SSH2_FILEXFER_ATTR_SIZE) size = a->size; else size = 0; buflen = conn->transfer_buflen; if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); attrib_clear(&junk); /* Send empty attributes */ /* Send open request */ id = conn->msg_id++; if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPEN)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_cstring(msg, remote_path)) != 0 || (r = sshbuf_put_u32(msg, SSH2_FXF_READ)) != 0 || (r = encode_attrib(msg, &junk)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); send_msg(conn, msg); debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path); handle = get_handle(conn, id, &handle_len, "remote open(\"%s\")", remote_path); if (handle == NULL) { sshbuf_free(msg); return(-1); } local_fd = open(local_path, O_WRONLY | O_CREAT | (resume_flag ? 0 : O_TRUNC), mode | S_IWUSR); if (local_fd == -1) { error("Couldn't open local file \"%s\" for writing: %s", local_path, strerror(errno)); goto fail; } offset = highwater = 0; if (resume_flag) { if (fstat(local_fd, &st) == -1) { error("Unable to stat local file \"%s\": %s", local_path, strerror(errno)); goto fail; } if (st.st_size < 0) { error("\"%s\" has negative size", local_path); goto fail; } if ((u_int64_t)st.st_size > size) { error("Unable to resume download of \"%s\": " "local file is larger than remote", local_path); fail: do_close(conn, handle, handle_len); sshbuf_free(msg); free(handle); if (local_fd != -1) close(local_fd); return -1; } offset = highwater = st.st_size; } /* Read from remote and write to local */ write_error = read_error = write_errno = num_req = 0; max_req = 1; progress_counter = offset; if (showprogress && size != 0) start_progress_meter(remote_path, size, &progress_counter); while (num_req > 0 || max_req > 0) { u_char *data; size_t len; /* * Simulate EOF on interrupt: stop sending new requests and * allow outstanding requests to drain gracefully */ if (interrupted) { if (num_req == 0) /* If we haven't started yet... */ break; max_req = 0; } /* Send some more requests */ while (num_req < max_req) { debug3("Request range %llu -> %llu (%d/%d)", (unsigned long long)offset, (unsigned long long)offset + buflen - 1, num_req, max_req); req = xcalloc(1, sizeof(*req)); req->id = conn->msg_id++; req->len = buflen; req->offset = offset; offset += buflen; num_req++; TAILQ_INSERT_TAIL(&requests, req, tq); send_read_request(conn, req->id, req->offset, req->len, handle, handle_len); } sshbuf_reset(msg); get_msg(conn, msg); if ((r = sshbuf_get_u8(msg, &type)) != 0 || (r = sshbuf_get_u32(msg, &id)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); debug3("Received reply T:%u I:%u R:%d", type, id, max_req); /* Find the request in our queue */ for (req = TAILQ_FIRST(&requests); req != NULL && req->id != id; req = TAILQ_NEXT(req, tq)) ; if (req == NULL) fatal("Unexpected reply %u", id); switch (type) { case SSH2_FXP_STATUS: if ((r = sshbuf_get_u32(msg, &status)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (status != SSH2_FX_EOF) read_error = 1; max_req = 0; TAILQ_REMOVE(&requests, req, tq); free(req); num_req--; break; case SSH2_FXP_DATA: if ((r = sshbuf_get_string(msg, &data, &len)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); debug3("Received data %llu -> %llu", (unsigned long long)req->offset, (unsigned long long)req->offset + len - 1); if (len > req->len) fatal("Received more data than asked for " "%zu > %zu", len, req->len); if ((lseek(local_fd, req->offset, SEEK_SET) == -1 || atomicio(vwrite, local_fd, data, len) != len) && !write_error) { write_errno = errno; write_error = 1; max_req = 0; } else if (!reordered && req->offset <= highwater) highwater = req->offset + len; else if (!reordered && req->offset > highwater) reordered = 1; progress_counter += len; free(data); if (len == req->len) { TAILQ_REMOVE(&requests, req, tq); free(req); num_req--; } else { /* Resend the request for the missing data */ debug3("Short data block, re-requesting " "%llu -> %llu (%2d)", (unsigned long long)req->offset + len, (unsigned long long)req->offset + req->len - 1, num_req); req->id = conn->msg_id++; req->len -= len; req->offset += len; send_read_request(conn, req->id, req->offset, req->len, handle, handle_len); /* Reduce the request size */ if (len < buflen) buflen = MAXIMUM(MIN_READ_SIZE, len); } if (max_req > 0) { /* max_req = 0 iff EOF received */ if (size > 0 && offset > size) { /* Only one request at a time * after the expected EOF */ debug3("Finish at %llu (%2d)", (unsigned long long)offset, num_req); max_req = 1; } else if (max_req <= conn->num_requests) { ++max_req; } } break; default: fatal("Expected SSH2_FXP_DATA(%u) packet, got %u", SSH2_FXP_DATA, type); } } if (showprogress && size) stop_progress_meter(); /* Sanity check */ if (TAILQ_FIRST(&requests) != NULL) fatal("Transfer complete, but requests still in queue"); /* Truncate at highest contiguous point to avoid holes on interrupt */ if (read_error || write_error || interrupted) { if (reordered && resume_flag) { error("Unable to resume download of \"%s\": " "server reordered requests", local_path); } debug("truncating at %llu", (unsigned long long)highwater); if (ftruncate(local_fd, highwater) == -1) error("ftruncate \"%s\": %s", local_path, strerror(errno)); } if (read_error) { error("Couldn't read from remote file \"%s\" : %s", remote_path, fx2txt(status)); status = -1; do_close(conn, handle, handle_len); } else if (write_error) { error("Couldn't write to \"%s\": %s", local_path, strerror(write_errno)); status = SSH2_FX_FAILURE; do_close(conn, handle, handle_len); } else { if (do_close(conn, handle, handle_len) != 0 || interrupted) status = SSH2_FX_FAILURE; else status = SSH2_FX_OK; /* Override umask and utimes if asked */ #ifdef HAVE_FCHMOD if (preserve_flag && fchmod(local_fd, mode) == -1) #else if (preserve_flag && chmod(local_path, mode) == -1) #endif /* HAVE_FCHMOD */ error("Couldn't set mode on \"%s\": %s", local_path, strerror(errno)); if (preserve_flag && (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME)) { struct timeval tv[2]; tv[0].tv_sec = a->atime; tv[1].tv_sec = a->mtime; tv[0].tv_usec = tv[1].tv_usec = 0; if (utimes(local_path, tv) == -1) error("Can't set times on \"%s\": %s", local_path, strerror(errno)); } if (fsync_flag) { debug("syncing \"%s\"", local_path); if (fsync(local_fd) == -1) error("Couldn't sync file \"%s\": %s", local_path, strerror(errno)); } } close(local_fd); sshbuf_free(msg); free(handle); return(status); } static int download_dir_internal(struct sftp_conn *conn, const char *src, const char *dst, int depth, Attrib *dirattrib, int preserve_flag, int print_flag, int resume_flag, int fsync_flag) { int i, ret = 0; SFTP_DIRENT **dir_entries; char *filename, *new_src, *new_dst; mode_t mode = 0777; if (depth >= MAX_DIR_DEPTH) { error("Maximum directory depth exceeded: %d levels", depth); return -1; } if (dirattrib == NULL && (dirattrib = do_stat(conn, src, 1)) == NULL) { error("Unable to stat remote directory \"%s\"", src); return -1; } if (!S_ISDIR(dirattrib->perm)) { error("\"%s\" is not a directory", src); return -1; } if (print_flag) mprintf("Retrieving %s\n", src); if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) mode = dirattrib->perm & 01777; else { debug("Server did not send permissions for " "directory \"%s\"", dst); } if (mkdir(dst, mode) == -1 && errno != EEXIST) { error("mkdir %s: %s", dst, strerror(errno)); return -1; } if (do_readdir(conn, src, &dir_entries) == -1) { error("%s: Failed to get directory contents", src); return -1; } for (i = 0; dir_entries[i] != NULL && !interrupted; i++) { filename = dir_entries[i]->filename; new_dst = path_append(dst, filename); new_src = path_append(src, filename); if (S_ISDIR(dir_entries[i]->a.perm)) { if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0) continue; if (download_dir_internal(conn, new_src, new_dst, depth + 1, &(dir_entries[i]->a), preserve_flag, print_flag, resume_flag, fsync_flag) == -1) ret = -1; } else if (S_ISREG(dir_entries[i]->a.perm) ) { if (do_download(conn, new_src, new_dst, &(dir_entries[i]->a), preserve_flag, resume_flag, fsync_flag) == -1) { error("Download of file %s to %s failed", new_src, new_dst); ret = -1; } } else logit("%s: not a regular file\n", new_src); free(new_dst); free(new_src); } if (preserve_flag) { if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { struct timeval tv[2]; tv[0].tv_sec = dirattrib->atime; tv[1].tv_sec = dirattrib->mtime; tv[0].tv_usec = tv[1].tv_usec = 0; if (utimes(dst, tv) == -1) error("Can't set times on \"%s\": %s", dst, strerror(errno)); } else debug("Server did not send times for directory " "\"%s\"", dst); } free_sftp_dirents(dir_entries); return ret; } int download_dir(struct sftp_conn *conn, const char *src, const char *dst, Attrib *dirattrib, int preserve_flag, int print_flag, int resume_flag, int fsync_flag) { char *src_canon; int ret; if ((src_canon = do_realpath(conn, src)) == NULL) { error("Unable to canonicalize path \"%s\"", src); return -1; } ret = download_dir_internal(conn, src_canon, dst, 0, dirattrib, preserve_flag, print_flag, resume_flag, fsync_flag); free(src_canon); return ret; } int do_upload(struct sftp_conn *conn, const char *local_path, const char *remote_path, int preserve_flag, int resume, int fsync_flag) { int r, local_fd; u_int status = SSH2_FX_OK; u_int id; u_char type; off_t offset, progress_counter; u_char *handle, *data; struct sshbuf *msg; struct stat sb; Attrib a, *c = NULL; u_int32_t startid; u_int32_t ackid; struct outstanding_ack { u_int id; u_int len; off_t offset; TAILQ_ENTRY(outstanding_ack) tq; }; TAILQ_HEAD(ackhead, outstanding_ack) acks; struct outstanding_ack *ack = NULL; size_t handle_len; TAILQ_INIT(&acks); if ((local_fd = open(local_path, O_RDONLY, 0)) == -1) { error("Couldn't open local file \"%s\" for reading: %s", local_path, strerror(errno)); return(-1); } if (fstat(local_fd, &sb) == -1) { error("Couldn't fstat local file \"%s\": %s", local_path, strerror(errno)); close(local_fd); return(-1); } if (!S_ISREG(sb.st_mode)) { error("%s is not a regular file", local_path); close(local_fd); return(-1); } stat_to_attrib(&sb, &a); a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; a.perm &= 0777; if (!preserve_flag) a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; if (resume) { /* Get remote file size if it exists */ if ((c = do_stat(conn, remote_path, 0)) == NULL) { close(local_fd); return -1; } if ((off_t)c->size >= sb.st_size) { error("destination file bigger or same size as " "source file"); close(local_fd); return -1; } if (lseek(local_fd, (off_t)c->size, SEEK_SET) == -1) { close(local_fd); return -1; } } if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); /* Send open request */ id = conn->msg_id++; if ((r = sshbuf_put_u8(msg, SSH2_FXP_OPEN)) != 0 || (r = sshbuf_put_u32(msg, id)) != 0 || (r = sshbuf_put_cstring(msg, remote_path)) != 0 || (r = sshbuf_put_u32(msg, SSH2_FXF_WRITE|SSH2_FXF_CREAT| (resume ? SSH2_FXF_APPEND : SSH2_FXF_TRUNC))) != 0 || (r = encode_attrib(msg, &a)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); send_msg(conn, msg); debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path); sshbuf_reset(msg); handle = get_handle(conn, id, &handle_len, "remote open(\"%s\")", remote_path); if (handle == NULL) { close(local_fd); sshbuf_free(msg); return -1; } startid = ackid = id + 1; data = xmalloc(conn->transfer_buflen); /* Read from local and write to remote */ offset = progress_counter = (resume ? c->size : 0); if (showprogress) start_progress_meter(local_path, sb.st_size, &progress_counter); for (;;) { int len; /* * Can't use atomicio here because it returns 0 on EOF, * thus losing the last block of the file. * Simulate an EOF on interrupt, allowing ACKs from the * server to drain. */ if (interrupted || status != SSH2_FX_OK) len = 0; else do len = read(local_fd, data, conn->transfer_buflen); while ((len == -1) && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)); if (len == -1) fatal("Couldn't read from \"%s\": %s", local_path, strerror(errno)); if (len != 0) { ack = xcalloc(1, sizeof(*ack)); ack->id = ++id; ack->offset = offset; ack->len = len; TAILQ_INSERT_TAIL(&acks, ack, tq); sshbuf_reset(msg); if ((r = sshbuf_put_u8(msg, SSH2_FXP_WRITE)) != 0 || (r = sshbuf_put_u32(msg, ack->id)) != 0 || (r = sshbuf_put_string(msg, handle, handle_len)) != 0 || (r = sshbuf_put_u64(msg, offset)) != 0 || (r = sshbuf_put_string(msg, data, len)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); send_msg(conn, msg); debug3("Sent message SSH2_FXP_WRITE I:%u O:%llu S:%u", id, (unsigned long long)offset, len); } else if (TAILQ_FIRST(&acks) == NULL) break; if (ack == NULL) fatal("Unexpected ACK %u", id); if (id == startid || len == 0 || id - ackid >= conn->num_requests) { u_int rid; sshbuf_reset(msg); get_msg(conn, msg); if ((r = sshbuf_get_u8(msg, &type)) != 0 || (r = sshbuf_get_u32(msg, &rid)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (type != SSH2_FXP_STATUS) fatal("Expected SSH2_FXP_STATUS(%d) packet, " "got %d", SSH2_FXP_STATUS, type); if ((r = sshbuf_get_u32(msg, &status)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); debug3("SSH2_FXP_STATUS %u", status); /* Find the request in our queue */ for (ack = TAILQ_FIRST(&acks); ack != NULL && ack->id != rid; ack = TAILQ_NEXT(ack, tq)) ; if (ack == NULL) fatal("Can't find request for ID %u", rid); TAILQ_REMOVE(&acks, ack, tq); debug3("In write loop, ack for %u %u bytes at %lld", ack->id, ack->len, (long long)ack->offset); ++ackid; progress_counter += ack->len; free(ack); } offset += len; if (offset < 0) fatal("%s: offset < 0", __func__); } sshbuf_free(msg); if (showprogress) stop_progress_meter(); free(data); if (status != SSH2_FX_OK) { error("Couldn't write to remote file \"%s\": %s", remote_path, fx2txt(status)); status = SSH2_FX_FAILURE; } if (close(local_fd) == -1) { error("Couldn't close local file \"%s\": %s", local_path, strerror(errno)); status = SSH2_FX_FAILURE; } /* Override umask and utimes if asked */ if (preserve_flag) do_fsetstat(conn, handle, handle_len, &a); if (fsync_flag) (void)do_fsync(conn, handle, handle_len); if (do_close(conn, handle, handle_len) != 0) status = SSH2_FX_FAILURE; free(handle); return status == SSH2_FX_OK ? 0 : -1; } static int upload_dir_internal(struct sftp_conn *conn, const char *src, const char *dst, int depth, int preserve_flag, int print_flag, int resume, int fsync_flag) { int ret = 0; DIR *dirp; struct dirent *dp; char *filename, *new_src, *new_dst; struct stat sb; Attrib a, *dirattrib; if (depth >= MAX_DIR_DEPTH) { error("Maximum directory depth exceeded: %d levels", depth); return -1; } if (stat(src, &sb) == -1) { error("Couldn't stat directory \"%s\": %s", src, strerror(errno)); return -1; } if (!S_ISDIR(sb.st_mode)) { error("\"%s\" is not a directory", src); return -1; } if (print_flag) mprintf("Entering %s\n", src); attrib_clear(&a); stat_to_attrib(&sb, &a); a.flags &= ~SSH2_FILEXFER_ATTR_SIZE; a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID; a.perm &= 01777; if (!preserve_flag) a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME; /* * sftp lacks a portable status value to match errno EEXIST, * so if we get a failure back then we must check whether * the path already existed and is a directory. */ if (do_mkdir(conn, dst, &a, 0) != 0) { if ((dirattrib = do_stat(conn, dst, 0)) == NULL) return -1; if (!S_ISDIR(dirattrib->perm)) { error("\"%s\" exists but is not a directory", dst); return -1; } } if ((dirp = opendir(src)) == NULL) { error("Failed to open dir \"%s\": %s", src, strerror(errno)); return -1; } while (((dp = readdir(dirp)) != NULL) && !interrupted) { if (dp->d_ino == 0) continue; filename = dp->d_name; new_dst = path_append(dst, filename); new_src = path_append(src, filename); if (lstat(new_src, &sb) == -1) { logit("%s: lstat failed: %s", filename, strerror(errno)); ret = -1; } else if (S_ISDIR(sb.st_mode)) { if (strcmp(filename, ".") == 0 || strcmp(filename, "..") == 0) continue; if (upload_dir_internal(conn, new_src, new_dst, depth + 1, preserve_flag, print_flag, resume, fsync_flag) == -1) ret = -1; } else if (S_ISREG(sb.st_mode)) { if (do_upload(conn, new_src, new_dst, preserve_flag, resume, fsync_flag) == -1) { error("Uploading of file %s to %s failed!", new_src, new_dst); ret = -1; } } else logit("%s: not a regular file\n", filename); free(new_dst); free(new_src); } do_setstat(conn, dst, &a); (void) closedir(dirp); return ret; } int upload_dir(struct sftp_conn *conn, const char *src, const char *dst, int preserve_flag, int print_flag, int resume, int fsync_flag) { char *dst_canon; int ret; if ((dst_canon = do_realpath(conn, dst)) == NULL) { error("Unable to canonicalize path \"%s\"", dst); return -1; } ret = upload_dir_internal(conn, src, dst_canon, 0, preserve_flag, print_flag, resume, fsync_flag); free(dst_canon); return ret; } char * path_append(const char *p1, const char *p2) { char *ret; size_t len = strlen(p1) + strlen(p2) + 2; ret = xmalloc(len); strlcpy(ret, p1, len); if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/') strlcat(ret, "/", len); strlcat(ret, p2, len); return(ret); } diff --git a/crypto/openssh/sftp.c b/crypto/openssh/sftp.c index 2b8fdabfb6df..76add3908ca7 100644 --- a/crypto/openssh/sftp.c +++ b/crypto/openssh/sftp.c @@ -1,2472 +1,2480 @@ -/* $OpenBSD: sftp.c,v 1.177 2016/10/18 12:41:22 millert Exp $ */ +/* $OpenBSD: sftp.c,v 1.178 2017/02/15 01:46:47 djm Exp $ */ /* * Copyright (c) 2001-2004 Damien Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include "includes.h" #include #include #ifdef HAVE_SYS_STAT_H # include #endif #include #include #include #ifdef HAVE_SYS_STATVFS_H #include #endif #include #include #ifdef HAVE_PATHS_H # include #endif #ifdef HAVE_LIBGEN_H #include #endif #ifdef HAVE_LOCALE_H # include #endif #ifdef USE_LIBEDIT #include #else typedef void EditLine; #endif #include #include #include #include #include #include #include #include #ifdef HAVE_UTIL_H # include #endif #include "xmalloc.h" #include "log.h" #include "pathnames.h" #include "misc.h" #include "utf8.h" #include "sftp.h" #include "ssherr.h" #include "sshbuf.h" #include "sftp-common.h" #include "sftp-client.h" #define DEFAULT_COPY_BUFLEN 32768 /* Size of buffer for up/download */ #define DEFAULT_NUM_REQUESTS 64 /* # concurrent outstanding requests */ /* File to read commands from */ FILE* infile; /* Are we in batchfile mode? */ int batchmode = 0; /* PID of ssh transport process */ static pid_t sshpid = -1; /* Suppress diagnositic messages */ int quiet = 0; /* This is set to 0 if the progressmeter is not desired. */ int showprogress = 1; /* When this option is set, we always recursively download/upload directories */ int global_rflag = 0; /* When this option is set, we resume download or upload if possible */ int global_aflag = 0; /* When this option is set, the file transfers will always preserve times */ int global_pflag = 0; /* When this option is set, transfers will have fsync() called on each file */ int global_fflag = 0; /* SIGINT received during command processing */ volatile sig_atomic_t interrupted = 0; /* I wish qsort() took a separate ctx for the comparison function...*/ int sort_flag; /* Context used for commandline completion */ struct complete_ctx { struct sftp_conn *conn; char **remote_pathp; }; int remote_glob(struct sftp_conn *, const char *, int, int (*)(const char *, int), glob_t *); /* proto for sftp-glob.c */ extern char *__progname; /* Separators for interactive commands */ #define WHITESPACE " \t\r\n" /* ls flags */ #define LS_LONG_VIEW 0x0001 /* Full view ala ls -l */ #define LS_SHORT_VIEW 0x0002 /* Single row view ala ls -1 */ #define LS_NUMERIC_VIEW 0x0004 /* Long view with numeric uid/gid */ #define LS_NAME_SORT 0x0008 /* Sort by name (default) */ #define LS_TIME_SORT 0x0010 /* Sort by mtime */ #define LS_SIZE_SORT 0x0020 /* Sort by file size */ #define LS_REVERSE_SORT 0x0040 /* Reverse sort order */ #define LS_SHOW_ALL 0x0080 /* Don't skip filenames starting with '.' */ #define LS_SI_UNITS 0x0100 /* Display sizes as K, M, G, etc. */ #define VIEW_FLAGS (LS_LONG_VIEW|LS_SHORT_VIEW|LS_NUMERIC_VIEW|LS_SI_UNITS) #define SORT_FLAGS (LS_NAME_SORT|LS_TIME_SORT|LS_SIZE_SORT) /* Commands for interactive mode */ enum sftp_command { I_CHDIR = 1, I_CHGRP, I_CHMOD, I_CHOWN, I_DF, I_GET, I_HELP, I_LCHDIR, I_LINK, I_LLS, I_LMKDIR, I_LPWD, I_LS, I_LUMASK, I_MKDIR, I_PUT, I_PWD, I_QUIT, I_REGET, I_RENAME, I_REPUT, I_RM, I_RMDIR, I_SHELL, I_SYMLINK, I_VERSION, I_PROGRESS, }; struct CMD { const char *c; const int n; const int t; }; /* Type of completion */ #define NOARGS 0 #define REMOTE 1 #define LOCAL 2 static const struct CMD cmds[] = { { "bye", I_QUIT, NOARGS }, { "cd", I_CHDIR, REMOTE }, { "chdir", I_CHDIR, REMOTE }, { "chgrp", I_CHGRP, REMOTE }, { "chmod", I_CHMOD, REMOTE }, { "chown", I_CHOWN, REMOTE }, { "df", I_DF, REMOTE }, { "dir", I_LS, REMOTE }, { "exit", I_QUIT, NOARGS }, { "get", I_GET, REMOTE }, { "help", I_HELP, NOARGS }, { "lcd", I_LCHDIR, LOCAL }, { "lchdir", I_LCHDIR, LOCAL }, { "lls", I_LLS, LOCAL }, { "lmkdir", I_LMKDIR, LOCAL }, { "ln", I_LINK, REMOTE }, { "lpwd", I_LPWD, LOCAL }, { "ls", I_LS, REMOTE }, { "lumask", I_LUMASK, NOARGS }, { "mkdir", I_MKDIR, REMOTE }, { "mget", I_GET, REMOTE }, { "mput", I_PUT, LOCAL }, { "progress", I_PROGRESS, NOARGS }, { "put", I_PUT, LOCAL }, { "pwd", I_PWD, REMOTE }, { "quit", I_QUIT, NOARGS }, { "reget", I_REGET, REMOTE }, { "rename", I_RENAME, REMOTE }, { "reput", I_REPUT, LOCAL }, { "rm", I_RM, REMOTE }, { "rmdir", I_RMDIR, REMOTE }, { "symlink", I_SYMLINK, REMOTE }, { "version", I_VERSION, NOARGS }, { "!", I_SHELL, NOARGS }, { "?", I_HELP, NOARGS }, { NULL, -1, -1 } }; int interactive_loop(struct sftp_conn *, char *file1, char *file2); /* ARGSUSED */ static void killchild(int signo) { if (sshpid > 1) { kill(sshpid, SIGTERM); waitpid(sshpid, NULL, 0); } _exit(1); } /* ARGSUSED */ static void suspchild(int signo) { if (sshpid > 1) { kill(sshpid, signo); while (waitpid(sshpid, NULL, WUNTRACED) == -1 && errno == EINTR) continue; } kill(getpid(), SIGSTOP); } /* ARGSUSED */ static void cmd_interrupt(int signo) { const char msg[] = "\rInterrupt \n"; int olderrno = errno; (void)write(STDERR_FILENO, msg, sizeof(msg) - 1); interrupted = 1; errno = olderrno; } static void help(void) { printf("Available commands:\n" "bye Quit sftp\n" "cd path Change remote directory to 'path'\n" "chgrp grp path Change group of file 'path' to 'grp'\n" "chmod mode path Change permissions of file 'path' to 'mode'\n" "chown own path Change owner of file 'path' to 'own'\n" "df [-hi] [path] Display statistics for current directory or\n" " filesystem containing 'path'\n" "exit Quit sftp\n" "get [-afPpRr] remote [local] Download file\n" "reget [-fPpRr] remote [local] Resume download file\n" "reput [-fPpRr] [local] remote Resume upload file\n" "help Display this help text\n" "lcd path Change local directory to 'path'\n" "lls [ls-options [path]] Display local directory listing\n" "lmkdir path Create local directory\n" "ln [-s] oldpath newpath Link remote file (-s for symlink)\n" "lpwd Print local working directory\n" "ls [-1afhlnrSt] [path] Display remote directory listing\n" "lumask umask Set local umask to 'umask'\n" "mkdir path Create remote directory\n" "progress Toggle display of progress meter\n" "put [-afPpRr] local [remote] Upload file\n" "pwd Display remote working directory\n" "quit Quit sftp\n" "rename oldpath newpath Rename remote file\n" "rm path Delete remote file\n" "rmdir path Remove remote directory\n" "symlink oldpath newpath Symlink remote file\n" "version Show SFTP version\n" "!command Execute 'command' in local shell\n" "! Escape to local shell\n" "? Synonym for help\n"); } static void local_do_shell(const char *args) { int status; char *shell; pid_t pid; if (!*args) args = NULL; if ((shell = getenv("SHELL")) == NULL || *shell == '\0') shell = _PATH_BSHELL; if ((pid = fork()) == -1) fatal("Couldn't fork: %s", strerror(errno)); if (pid == 0) { /* XXX: child has pipe fds to ssh subproc open - issue? */ if (args) { debug3("Executing %s -c \"%s\"", shell, args); execl(shell, shell, "-c", args, (char *)NULL); } else { debug3("Executing %s", shell); execl(shell, shell, (char *)NULL); } fprintf(stderr, "Couldn't execute \"%s\": %s\n", shell, strerror(errno)); _exit(1); } while (waitpid(pid, &status, 0) == -1) if (errno != EINTR) fatal("Couldn't wait for child: %s", strerror(errno)); if (!WIFEXITED(status)) error("Shell exited abnormally"); else if (WEXITSTATUS(status)) error("Shell exited with status %d", WEXITSTATUS(status)); } static void local_do_ls(const char *args) { if (!args || !*args) local_do_shell(_PATH_LS); else { int len = strlen(_PATH_LS " ") + strlen(args) + 1; char *buf = xmalloc(len); /* XXX: quoting - rip quoting code from ftp? */ snprintf(buf, len, _PATH_LS " %s", args); local_do_shell(buf); free(buf); } } /* Strip one path (usually the pwd) from the start of another */ static char * path_strip(const char *path, const char *strip) { size_t len; if (strip == NULL) return (xstrdup(path)); len = strlen(strip); if (strncmp(path, strip, len) == 0) { if (strip[len - 1] != '/' && path[len] == '/') len++; return (xstrdup(path + len)); } return (xstrdup(path)); } static char * make_absolute(char *p, const char *pwd) { char *abs_str; /* Derelativise */ if (p && p[0] != '/') { abs_str = path_append(pwd, p); free(p); return(abs_str); } else return(p); } static int parse_getput_flags(const char *cmd, char **argv, int argc, int *aflag, int *fflag, int *pflag, int *rflag) { extern int opterr, optind, optopt, optreset; int ch; optind = optreset = 1; opterr = 0; *aflag = *fflag = *rflag = *pflag = 0; while ((ch = getopt(argc, argv, "afPpRr")) != -1) { switch (ch) { case 'a': *aflag = 1; break; case 'f': *fflag = 1; break; case 'p': case 'P': *pflag = 1; break; case 'r': case 'R': *rflag = 1; break; default: error("%s: Invalid flag -%c", cmd, optopt); return -1; } } return optind; } static int parse_link_flags(const char *cmd, char **argv, int argc, int *sflag) { extern int opterr, optind, optopt, optreset; int ch; optind = optreset = 1; opterr = 0; *sflag = 0; while ((ch = getopt(argc, argv, "s")) != -1) { switch (ch) { case 's': *sflag = 1; break; default: error("%s: Invalid flag -%c", cmd, optopt); return -1; } } return optind; } static int parse_rename_flags(const char *cmd, char **argv, int argc, int *lflag) { extern int opterr, optind, optopt, optreset; int ch; optind = optreset = 1; opterr = 0; *lflag = 0; while ((ch = getopt(argc, argv, "l")) != -1) { switch (ch) { case 'l': *lflag = 1; break; default: error("%s: Invalid flag -%c", cmd, optopt); return -1; } } return optind; } static int parse_ls_flags(char **argv, int argc, int *lflag) { extern int opterr, optind, optopt, optreset; int ch; optind = optreset = 1; opterr = 0; *lflag = LS_NAME_SORT; while ((ch = getopt(argc, argv, "1Safhlnrt")) != -1) { switch (ch) { case '1': *lflag &= ~VIEW_FLAGS; *lflag |= LS_SHORT_VIEW; break; case 'S': *lflag &= ~SORT_FLAGS; *lflag |= LS_SIZE_SORT; break; case 'a': *lflag |= LS_SHOW_ALL; break; case 'f': *lflag &= ~SORT_FLAGS; break; case 'h': *lflag |= LS_SI_UNITS; break; case 'l': *lflag &= ~LS_SHORT_VIEW; *lflag |= LS_LONG_VIEW; break; case 'n': *lflag &= ~LS_SHORT_VIEW; *lflag |= LS_NUMERIC_VIEW|LS_LONG_VIEW; break; case 'r': *lflag |= LS_REVERSE_SORT; break; case 't': *lflag &= ~SORT_FLAGS; *lflag |= LS_TIME_SORT; break; default: error("ls: Invalid flag -%c", optopt); return -1; } } return optind; } static int parse_df_flags(const char *cmd, char **argv, int argc, int *hflag, int *iflag) { extern int opterr, optind, optopt, optreset; int ch; optind = optreset = 1; opterr = 0; *hflag = *iflag = 0; while ((ch = getopt(argc, argv, "hi")) != -1) { switch (ch) { case 'h': *hflag = 1; break; case 'i': *iflag = 1; break; default: error("%s: Invalid flag -%c", cmd, optopt); return -1; } } return optind; } static int parse_no_flags(const char *cmd, char **argv, int argc) { extern int opterr, optind, optopt, optreset; int ch; optind = optreset = 1; opterr = 0; while ((ch = getopt(argc, argv, "")) != -1) { switch (ch) { default: error("%s: Invalid flag -%c", cmd, optopt); return -1; } } return optind; } static int is_dir(const char *path) { struct stat sb; /* XXX: report errors? */ if (stat(path, &sb) == -1) return(0); return(S_ISDIR(sb.st_mode)); } static int remote_is_dir(struct sftp_conn *conn, const char *path) { Attrib *a; /* XXX: report errors? */ if ((a = do_stat(conn, path, 1)) == NULL) return(0); if (!(a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) return(0); return(S_ISDIR(a->perm)); } /* Check whether path returned from glob(..., GLOB_MARK, ...) is a directory */ static int pathname_is_dir(const char *pathname) { size_t l = strlen(pathname); return l > 0 && pathname[l - 1] == '/'; } static int process_get(struct sftp_conn *conn, const char *src, const char *dst, const char *pwd, int pflag, int rflag, int resume, int fflag) { char *abs_src = NULL; char *abs_dst = NULL; glob_t g; char *filename, *tmp=NULL; int i, r, err = 0; abs_src = xstrdup(src); abs_src = make_absolute(abs_src, pwd); memset(&g, 0, sizeof(g)); debug3("Looking up %s", abs_src); if ((r = remote_glob(conn, abs_src, GLOB_MARK, NULL, &g)) != 0) { if (r == GLOB_NOSPACE) { error("Too many matches for \"%s\".", abs_src); } else { error("File \"%s\" not found.", abs_src); } err = -1; goto out; } /* * If multiple matches then dst must be a directory or * unspecified. */ if (g.gl_matchc > 1 && dst != NULL && !is_dir(dst)) { error("Multiple source paths, but destination " "\"%s\" is not a directory", dst); err = -1; goto out; } for (i = 0; g.gl_pathv[i] && !interrupted; i++) { tmp = xstrdup(g.gl_pathv[i]); if ((filename = basename(tmp)) == NULL) { error("basename %s: %s", tmp, strerror(errno)); free(tmp); err = -1; goto out; } if (g.gl_matchc == 1 && dst) { if (is_dir(dst)) { abs_dst = path_append(dst, filename); } else { abs_dst = xstrdup(dst); } } else if (dst) { abs_dst = path_append(dst, filename); } else { abs_dst = xstrdup(filename); } free(tmp); resume |= global_aflag; if (!quiet && resume) mprintf("Resuming %s to %s\n", g.gl_pathv[i], abs_dst); else if (!quiet && !resume) mprintf("Fetching %s to %s\n", g.gl_pathv[i], abs_dst); if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { if (download_dir(conn, g.gl_pathv[i], abs_dst, NULL, pflag || global_pflag, 1, resume, fflag || global_fflag) == -1) err = -1; } else { if (do_download(conn, g.gl_pathv[i], abs_dst, NULL, pflag || global_pflag, resume, fflag || global_fflag) == -1) err = -1; } free(abs_dst); abs_dst = NULL; } out: free(abs_src); globfree(&g); return(err); } static int process_put(struct sftp_conn *conn, const char *src, const char *dst, const char *pwd, int pflag, int rflag, int resume, int fflag) { char *tmp_dst = NULL; char *abs_dst = NULL; char *tmp = NULL, *filename = NULL; glob_t g; int err = 0; int i, dst_is_dir = 1; struct stat sb; if (dst) { tmp_dst = xstrdup(dst); tmp_dst = make_absolute(tmp_dst, pwd); } memset(&g, 0, sizeof(g)); debug3("Looking up %s", src); if (glob(src, GLOB_NOCHECK | GLOB_MARK, NULL, &g)) { error("File \"%s\" not found.", src); err = -1; goto out; } /* If we aren't fetching to pwd then stash this status for later */ if (tmp_dst != NULL) dst_is_dir = remote_is_dir(conn, tmp_dst); /* If multiple matches, dst may be directory or unspecified */ if (g.gl_matchc > 1 && tmp_dst && !dst_is_dir) { error("Multiple paths match, but destination " "\"%s\" is not a directory", tmp_dst); err = -1; goto out; } for (i = 0; g.gl_pathv[i] && !interrupted; i++) { if (stat(g.gl_pathv[i], &sb) == -1) { err = -1; error("stat %s: %s", g.gl_pathv[i], strerror(errno)); continue; } tmp = xstrdup(g.gl_pathv[i]); if ((filename = basename(tmp)) == NULL) { error("basename %s: %s", tmp, strerror(errno)); free(tmp); err = -1; goto out; } if (g.gl_matchc == 1 && tmp_dst) { /* If directory specified, append filename */ if (dst_is_dir) abs_dst = path_append(tmp_dst, filename); else abs_dst = xstrdup(tmp_dst); } else if (tmp_dst) { abs_dst = path_append(tmp_dst, filename); } else { abs_dst = make_absolute(xstrdup(filename), pwd); } free(tmp); resume |= global_aflag; if (!quiet && resume) mprintf("Resuming upload of %s to %s\n", g.gl_pathv[i], abs_dst); else if (!quiet && !resume) mprintf("Uploading %s to %s\n", g.gl_pathv[i], abs_dst); if (pathname_is_dir(g.gl_pathv[i]) && (rflag || global_rflag)) { if (upload_dir(conn, g.gl_pathv[i], abs_dst, pflag || global_pflag, 1, resume, fflag || global_fflag) == -1) err = -1; } else { if (do_upload(conn, g.gl_pathv[i], abs_dst, pflag || global_pflag, resume, fflag || global_fflag) == -1) err = -1; } } out: free(abs_dst); free(tmp_dst); globfree(&g); return(err); } static int sdirent_comp(const void *aa, const void *bb) { SFTP_DIRENT *a = *(SFTP_DIRENT **)aa; SFTP_DIRENT *b = *(SFTP_DIRENT **)bb; int rmul = sort_flag & LS_REVERSE_SORT ? -1 : 1; #define NCMP(a,b) (a == b ? 0 : (a < b ? 1 : -1)) if (sort_flag & LS_NAME_SORT) return (rmul * strcmp(a->filename, b->filename)); else if (sort_flag & LS_TIME_SORT) return (rmul * NCMP(a->a.mtime, b->a.mtime)); else if (sort_flag & LS_SIZE_SORT) return (rmul * NCMP(a->a.size, b->a.size)); fatal("Unknown ls sort type"); } /* sftp ls.1 replacement for directories */ static int do_ls_dir(struct sftp_conn *conn, const char *path, const char *strip_path, int lflag) { int n; u_int c = 1, colspace = 0, columns = 1; SFTP_DIRENT **d; if ((n = do_readdir(conn, path, &d)) != 0) return (n); if (!(lflag & LS_SHORT_VIEW)) { u_int m = 0, width = 80; struct winsize ws; char *tmp; /* Count entries for sort and find longest filename */ for (n = 0; d[n] != NULL; n++) { if (d[n]->filename[0] != '.' || (lflag & LS_SHOW_ALL)) m = MAXIMUM(m, strlen(d[n]->filename)); } /* Add any subpath that also needs to be counted */ tmp = path_strip(path, strip_path); m += strlen(tmp); free(tmp); if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) width = ws.ws_col; columns = width / (m + 2); columns = MAXIMUM(columns, 1); colspace = width / columns; colspace = MINIMUM(colspace, width); } if (lflag & SORT_FLAGS) { for (n = 0; d[n] != NULL; n++) ; /* count entries */ sort_flag = lflag & (SORT_FLAGS|LS_REVERSE_SORT); qsort(d, n, sizeof(*d), sdirent_comp); } for (n = 0; d[n] != NULL && !interrupted; n++) { char *tmp, *fname; if (d[n]->filename[0] == '.' && !(lflag & LS_SHOW_ALL)) continue; tmp = path_append(path, d[n]->filename); fname = path_strip(tmp, strip_path); free(tmp); if (lflag & LS_LONG_VIEW) { if (lflag & (LS_NUMERIC_VIEW|LS_SI_UNITS)) { char *lname; struct stat sb; memset(&sb, 0, sizeof(sb)); attrib_to_stat(&d[n]->a, &sb); lname = ls_file(fname, &sb, 1, (lflag & LS_SI_UNITS)); mprintf("%s\n", lname); free(lname); } else mprintf("%s\n", d[n]->longname); } else { mprintf("%-*s", colspace, fname); if (c >= columns) { printf("\n"); c = 1; } else c++; } free(fname); } if (!(lflag & LS_LONG_VIEW) && (c != 1)) printf("\n"); free_sftp_dirents(d); return (0); } /* sftp ls.1 replacement which handles path globs */ static int do_globbed_ls(struct sftp_conn *conn, const char *path, const char *strip_path, int lflag) { char *fname, *lname; glob_t g; int err, r; struct winsize ws; u_int i, c = 1, colspace = 0, columns = 1, m = 0, width = 80; memset(&g, 0, sizeof(g)); if ((r = remote_glob(conn, path, GLOB_MARK|GLOB_NOCHECK|GLOB_BRACE|GLOB_KEEPSTAT|GLOB_NOSORT, NULL, &g)) != 0 || (g.gl_pathc && !g.gl_matchc)) { if (g.gl_pathc) globfree(&g); if (r == GLOB_NOSPACE) { error("Can't ls: Too many matches for \"%s\"", path); } else { error("Can't ls: \"%s\" not found", path); } return -1; } if (interrupted) goto out; /* * If the glob returns a single match and it is a directory, * then just list its contents. */ if (g.gl_matchc == 1 && g.gl_statv[0] != NULL && S_ISDIR(g.gl_statv[0]->st_mode)) { err = do_ls_dir(conn, g.gl_pathv[0], strip_path, lflag); globfree(&g); return err; } if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) width = ws.ws_col; if (!(lflag & LS_SHORT_VIEW)) { /* Count entries for sort and find longest filename */ for (i = 0; g.gl_pathv[i]; i++) m = MAXIMUM(m, strlen(g.gl_pathv[i])); columns = width / (m + 2); columns = MAXIMUM(columns, 1); colspace = width / columns; } for (i = 0; g.gl_pathv[i] && !interrupted; i++) { fname = path_strip(g.gl_pathv[i], strip_path); if (lflag & LS_LONG_VIEW) { if (g.gl_statv[i] == NULL) { error("no stat information for %s", fname); continue; } lname = ls_file(fname, g.gl_statv[i], 1, (lflag & LS_SI_UNITS)); mprintf("%s\n", lname); free(lname); } else { mprintf("%-*s", colspace, fname); if (c >= columns) { printf("\n"); c = 1; } else c++; } free(fname); } if (!(lflag & LS_LONG_VIEW) && (c != 1)) printf("\n"); out: if (g.gl_pathc) globfree(&g); return 0; } static int do_df(struct sftp_conn *conn, const char *path, int hflag, int iflag) { struct sftp_statvfs st; - char s_used[FMT_SCALED_STRSIZE]; - char s_avail[FMT_SCALED_STRSIZE]; - char s_root[FMT_SCALED_STRSIZE]; - char s_total[FMT_SCALED_STRSIZE]; - unsigned long long ffree; + char s_used[FMT_SCALED_STRSIZE], s_avail[FMT_SCALED_STRSIZE]; + char s_root[FMT_SCALED_STRSIZE], s_total[FMT_SCALED_STRSIZE]; + char s_icapacity[16], s_dcapacity[16]; if (do_statvfs(conn, path, &st, 1) == -1) return -1; + if (st.f_files == 0) + strlcpy(s_icapacity, "ERR", sizeof(s_icapacity)); + else { + snprintf(s_icapacity, sizeof(s_icapacity), "%3llu%%", + (unsigned long long)(100 * (st.f_files - st.f_ffree) / + st.f_files)); + } + if (st.f_blocks == 0) + strlcpy(s_dcapacity, "ERR", sizeof(s_dcapacity)); + else { + snprintf(s_dcapacity, sizeof(s_dcapacity), "%3llu%%", + (unsigned long long)(100 * (st.f_blocks - st.f_bfree) / + st.f_blocks)); + } if (iflag) { - ffree = st.f_files ? (100 * (st.f_files - st.f_ffree) / st.f_files) : 0; printf(" Inodes Used Avail " "(root) %%Capacity\n"); - printf("%11llu %11llu %11llu %11llu %3llu%%\n", + printf("%11llu %11llu %11llu %11llu %s\n", (unsigned long long)st.f_files, (unsigned long long)(st.f_files - st.f_ffree), (unsigned long long)st.f_favail, - (unsigned long long)st.f_ffree, ffree); + (unsigned long long)st.f_ffree, s_icapacity); } else if (hflag) { strlcpy(s_used, "error", sizeof(s_used)); strlcpy(s_avail, "error", sizeof(s_avail)); strlcpy(s_root, "error", sizeof(s_root)); strlcpy(s_total, "error", sizeof(s_total)); fmt_scaled((st.f_blocks - st.f_bfree) * st.f_frsize, s_used); fmt_scaled(st.f_bavail * st.f_frsize, s_avail); fmt_scaled(st.f_bfree * st.f_frsize, s_root); fmt_scaled(st.f_blocks * st.f_frsize, s_total); printf(" Size Used Avail (root) %%Capacity\n"); - printf("%7sB %7sB %7sB %7sB %3llu%%\n", - s_total, s_used, s_avail, s_root, - (unsigned long long)(100 * (st.f_blocks - st.f_bfree) / - st.f_blocks)); + printf("%7sB %7sB %7sB %7sB %s\n", + s_total, s_used, s_avail, s_root, s_dcapacity); } else { printf(" Size Used Avail " "(root) %%Capacity\n"); - printf("%12llu %12llu %12llu %12llu %3llu%%\n", + printf("%12llu %12llu %12llu %12llu %s\n", (unsigned long long)(st.f_frsize * st.f_blocks / 1024), (unsigned long long)(st.f_frsize * (st.f_blocks - st.f_bfree) / 1024), (unsigned long long)(st.f_frsize * st.f_bavail / 1024), (unsigned long long)(st.f_frsize * st.f_bfree / 1024), - (unsigned long long)(100 * (st.f_blocks - st.f_bfree) / - st.f_blocks)); + s_dcapacity); } return 0; } /* * Undo escaping of glob sequences in place. Used to undo extra escaping * applied in makeargv() when the string is destined for a function that * does not glob it. */ static void undo_glob_escape(char *s) { size_t i, j; for (i = j = 0;;) { if (s[i] == '\0') { s[j] = '\0'; return; } if (s[i] != '\\') { s[j++] = s[i++]; continue; } /* s[i] == '\\' */ ++i; switch (s[i]) { case '?': case '[': case '*': case '\\': s[j++] = s[i++]; break; case '\0': s[j++] = '\\'; s[j] = '\0'; return; default: s[j++] = '\\'; s[j++] = s[i++]; break; } } } /* * Split a string into an argument vector using sh(1)-style quoting, * comment and escaping rules, but with some tweaks to handle glob(3) * wildcards. * The "sloppy" flag allows for recovery from missing terminating quote, for * use in parsing incomplete commandlines during tab autocompletion. * * Returns NULL on error or a NULL-terminated array of arguments. * * If "lastquote" is not NULL, the quoting character used for the last * argument is placed in *lastquote ("\0", "'" or "\""). * * If "terminated" is not NULL, *terminated will be set to 1 when the * last argument's quote has been properly terminated or 0 otherwise. * This parameter is only of use if "sloppy" is set. */ #define MAXARGS 128 #define MAXARGLEN 8192 static char ** makeargv(const char *arg, int *argcp, int sloppy, char *lastquote, u_int *terminated) { int argc, quot; size_t i, j; static char argvs[MAXARGLEN]; static char *argv[MAXARGS + 1]; enum { MA_START, MA_SQUOTE, MA_DQUOTE, MA_UNQUOTED } state, q; *argcp = argc = 0; if (strlen(arg) > sizeof(argvs) - 1) { args_too_longs: error("string too long"); return NULL; } if (terminated != NULL) *terminated = 1; if (lastquote != NULL) *lastquote = '\0'; state = MA_START; i = j = 0; for (;;) { if ((size_t)argc >= sizeof(argv) / sizeof(*argv)){ error("Too many arguments."); return NULL; } if (isspace((unsigned char)arg[i])) { if (state == MA_UNQUOTED) { /* Terminate current argument */ argvs[j++] = '\0'; argc++; state = MA_START; } else if (state != MA_START) argvs[j++] = arg[i]; } else if (arg[i] == '"' || arg[i] == '\'') { q = arg[i] == '"' ? MA_DQUOTE : MA_SQUOTE; if (state == MA_START) { argv[argc] = argvs + j; state = q; if (lastquote != NULL) *lastquote = arg[i]; } else if (state == MA_UNQUOTED) state = q; else if (state == q) state = MA_UNQUOTED; else argvs[j++] = arg[i]; } else if (arg[i] == '\\') { if (state == MA_SQUOTE || state == MA_DQUOTE) { quot = state == MA_SQUOTE ? '\'' : '"'; /* Unescape quote we are in */ /* XXX support \n and friends? */ if (arg[i + 1] == quot) { i++; argvs[j++] = arg[i]; } else if (arg[i + 1] == '?' || arg[i + 1] == '[' || arg[i + 1] == '*') { /* * Special case for sftp: append * double-escaped glob sequence - * glob will undo one level of * escaping. NB. string can grow here. */ if (j >= sizeof(argvs) - 5) goto args_too_longs; argvs[j++] = '\\'; argvs[j++] = arg[i++]; argvs[j++] = '\\'; argvs[j++] = arg[i]; } else { argvs[j++] = arg[i++]; argvs[j++] = arg[i]; } } else { if (state == MA_START) { argv[argc] = argvs + j; state = MA_UNQUOTED; if (lastquote != NULL) *lastquote = '\0'; } if (arg[i + 1] == '?' || arg[i + 1] == '[' || arg[i + 1] == '*' || arg[i + 1] == '\\') { /* * Special case for sftp: append * escaped glob sequence - * glob will undo one level of * escaping. */ argvs[j++] = arg[i++]; argvs[j++] = arg[i]; } else { /* Unescape everything */ /* XXX support \n and friends? */ i++; argvs[j++] = arg[i]; } } } else if (arg[i] == '#') { if (state == MA_SQUOTE || state == MA_DQUOTE) argvs[j++] = arg[i]; else goto string_done; } else if (arg[i] == '\0') { if (state == MA_SQUOTE || state == MA_DQUOTE) { if (sloppy) { state = MA_UNQUOTED; if (terminated != NULL) *terminated = 0; goto string_done; } error("Unterminated quoted argument"); return NULL; } string_done: if (state == MA_UNQUOTED) { argvs[j++] = '\0'; argc++; } break; } else { if (state == MA_START) { argv[argc] = argvs + j; state = MA_UNQUOTED; if (lastquote != NULL) *lastquote = '\0'; } if ((state == MA_SQUOTE || state == MA_DQUOTE) && (arg[i] == '?' || arg[i] == '[' || arg[i] == '*')) { /* * Special case for sftp: escape quoted * glob(3) wildcards. NB. string can grow * here. */ if (j >= sizeof(argvs) - 3) goto args_too_longs; argvs[j++] = '\\'; argvs[j++] = arg[i]; } else argvs[j++] = arg[i]; } i++; } *argcp = argc; return argv; } static int parse_args(const char **cpp, int *ignore_errors, int *aflag, int *fflag, int *hflag, int *iflag, int *lflag, int *pflag, int *rflag, int *sflag, unsigned long *n_arg, char **path1, char **path2) { const char *cmd, *cp = *cpp; char *cp2, **argv; int base = 0; long l; int i, cmdnum, optidx, argc; /* Skip leading whitespace */ cp = cp + strspn(cp, WHITESPACE); /* Check for leading '-' (disable error processing) */ *ignore_errors = 0; if (*cp == '-') { *ignore_errors = 1; cp++; cp = cp + strspn(cp, WHITESPACE); } /* Ignore blank lines and lines which begin with comment '#' char */ if (*cp == '\0' || *cp == '#') return (0); if ((argv = makeargv(cp, &argc, 0, NULL, NULL)) == NULL) return -1; /* Figure out which command we have */ for (i = 0; cmds[i].c != NULL; i++) { if (argv[0] != NULL && strcasecmp(cmds[i].c, argv[0]) == 0) break; } cmdnum = cmds[i].n; cmd = cmds[i].c; /* Special case */ if (*cp == '!') { cp++; cmdnum = I_SHELL; } else if (cmdnum == -1) { error("Invalid command."); return -1; } /* Get arguments and parse flags */ *aflag = *fflag = *hflag = *iflag = *lflag = *pflag = 0; *rflag = *sflag = 0; *path1 = *path2 = NULL; optidx = 1; switch (cmdnum) { case I_GET: case I_REGET: case I_REPUT: case I_PUT: if ((optidx = parse_getput_flags(cmd, argv, argc, aflag, fflag, pflag, rflag)) == -1) return -1; /* Get first pathname (mandatory) */ if (argc - optidx < 1) { error("You must specify at least one path after a " "%s command.", cmd); return -1; } *path1 = xstrdup(argv[optidx]); /* Get second pathname (optional) */ if (argc - optidx > 1) { *path2 = xstrdup(argv[optidx + 1]); /* Destination is not globbed */ undo_glob_escape(*path2); } break; case I_LINK: if ((optidx = parse_link_flags(cmd, argv, argc, sflag)) == -1) return -1; goto parse_two_paths; case I_RENAME: if ((optidx = parse_rename_flags(cmd, argv, argc, lflag)) == -1) return -1; goto parse_two_paths; case I_SYMLINK: if ((optidx = parse_no_flags(cmd, argv, argc)) == -1) return -1; parse_two_paths: if (argc - optidx < 2) { error("You must specify two paths after a %s " "command.", cmd); return -1; } *path1 = xstrdup(argv[optidx]); *path2 = xstrdup(argv[optidx + 1]); /* Paths are not globbed */ undo_glob_escape(*path1); undo_glob_escape(*path2); break; case I_RM: case I_MKDIR: case I_RMDIR: case I_CHDIR: case I_LCHDIR: case I_LMKDIR: if ((optidx = parse_no_flags(cmd, argv, argc)) == -1) return -1; /* Get pathname (mandatory) */ if (argc - optidx < 1) { error("You must specify a path after a %s command.", cmd); return -1; } *path1 = xstrdup(argv[optidx]); /* Only "rm" globs */ if (cmdnum != I_RM) undo_glob_escape(*path1); break; case I_DF: if ((optidx = parse_df_flags(cmd, argv, argc, hflag, iflag)) == -1) return -1; /* Default to current directory if no path specified */ if (argc - optidx < 1) *path1 = NULL; else { *path1 = xstrdup(argv[optidx]); undo_glob_escape(*path1); } break; case I_LS: if ((optidx = parse_ls_flags(argv, argc, lflag)) == -1) return(-1); /* Path is optional */ if (argc - optidx > 0) *path1 = xstrdup(argv[optidx]); break; case I_LLS: /* Skip ls command and following whitespace */ cp = cp + strlen(cmd) + strspn(cp, WHITESPACE); case I_SHELL: /* Uses the rest of the line */ break; case I_LUMASK: case I_CHMOD: base = 8; case I_CHOWN: case I_CHGRP: if ((optidx = parse_no_flags(cmd, argv, argc)) == -1) return -1; /* Get numeric arg (mandatory) */ if (argc - optidx < 1) goto need_num_arg; errno = 0; l = strtol(argv[optidx], &cp2, base); if (cp2 == argv[optidx] || *cp2 != '\0' || ((l == LONG_MIN || l == LONG_MAX) && errno == ERANGE) || l < 0) { need_num_arg: error("You must supply a numeric argument " "to the %s command.", cmd); return -1; } *n_arg = l; if (cmdnum == I_LUMASK) break; /* Get pathname (mandatory) */ if (argc - optidx < 2) { error("You must specify a path after a %s command.", cmd); return -1; } *path1 = xstrdup(argv[optidx + 1]); break; case I_QUIT: case I_PWD: case I_LPWD: case I_HELP: case I_VERSION: case I_PROGRESS: if ((optidx = parse_no_flags(cmd, argv, argc)) == -1) return -1; break; default: fatal("Command not implemented"); } *cpp = cp; return(cmdnum); } static int parse_dispatch_command(struct sftp_conn *conn, const char *cmd, char **pwd, int err_abort) { char *path1, *path2, *tmp; int ignore_errors = 0, aflag = 0, fflag = 0, hflag = 0, iflag = 0; int lflag = 0, pflag = 0, rflag = 0, sflag = 0; int cmdnum, i; unsigned long n_arg = 0; Attrib a, *aa; char path_buf[PATH_MAX]; int err = 0; glob_t g; path1 = path2 = NULL; cmdnum = parse_args(&cmd, &ignore_errors, &aflag, &fflag, &hflag, &iflag, &lflag, &pflag, &rflag, &sflag, &n_arg, &path1, &path2); if (ignore_errors != 0) err_abort = 0; memset(&g, 0, sizeof(g)); /* Perform command */ switch (cmdnum) { case 0: /* Blank line */ break; case -1: /* Unrecognized command */ err = -1; break; case I_REGET: aflag = 1; /* FALLTHROUGH */ case I_GET: err = process_get(conn, path1, path2, *pwd, pflag, rflag, aflag, fflag); break; case I_REPUT: aflag = 1; /* FALLTHROUGH */ case I_PUT: err = process_put(conn, path1, path2, *pwd, pflag, rflag, aflag, fflag); break; case I_RENAME: path1 = make_absolute(path1, *pwd); path2 = make_absolute(path2, *pwd); err = do_rename(conn, path1, path2, lflag); break; case I_SYMLINK: sflag = 1; case I_LINK: if (!sflag) path1 = make_absolute(path1, *pwd); path2 = make_absolute(path2, *pwd); err = (sflag ? do_symlink : do_hardlink)(conn, path1, path2); break; case I_RM: path1 = make_absolute(path1, *pwd); remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); for (i = 0; g.gl_pathv[i] && !interrupted; i++) { if (!quiet) mprintf("Removing %s\n", g.gl_pathv[i]); err = do_rm(conn, g.gl_pathv[i]); if (err != 0 && err_abort) break; } break; case I_MKDIR: path1 = make_absolute(path1, *pwd); attrib_clear(&a); a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; a.perm = 0777; err = do_mkdir(conn, path1, &a, 1); break; case I_RMDIR: path1 = make_absolute(path1, *pwd); err = do_rmdir(conn, path1); break; case I_CHDIR: path1 = make_absolute(path1, *pwd); if ((tmp = do_realpath(conn, path1)) == NULL) { err = 1; break; } if ((aa = do_stat(conn, tmp, 0)) == NULL) { free(tmp); err = 1; break; } if (!(aa->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)) { error("Can't change directory: Can't check target"); free(tmp); err = 1; break; } if (!S_ISDIR(aa->perm)) { error("Can't change directory: \"%s\" is not " "a directory", tmp); free(tmp); err = 1; break; } free(*pwd); *pwd = tmp; break; case I_LS: if (!path1) { do_ls_dir(conn, *pwd, *pwd, lflag); break; } /* Strip pwd off beginning of non-absolute paths */ tmp = NULL; if (*path1 != '/') tmp = *pwd; path1 = make_absolute(path1, *pwd); err = do_globbed_ls(conn, path1, tmp, lflag); break; case I_DF: /* Default to current directory if no path specified */ if (path1 == NULL) path1 = xstrdup(*pwd); path1 = make_absolute(path1, *pwd); err = do_df(conn, path1, hflag, iflag); break; case I_LCHDIR: tmp = tilde_expand_filename(path1, getuid()); free(path1); path1 = tmp; if (chdir(path1) == -1) { error("Couldn't change local directory to " "\"%s\": %s", path1, strerror(errno)); err = 1; } break; case I_LMKDIR: if (mkdir(path1, 0777) == -1) { error("Couldn't create local directory " "\"%s\": %s", path1, strerror(errno)); err = 1; } break; case I_LLS: local_do_ls(cmd); break; case I_SHELL: local_do_shell(cmd); break; case I_LUMASK: umask(n_arg); printf("Local umask: %03lo\n", n_arg); break; case I_CHMOD: path1 = make_absolute(path1, *pwd); attrib_clear(&a); a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS; a.perm = n_arg; remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); for (i = 0; g.gl_pathv[i] && !interrupted; i++) { if (!quiet) mprintf("Changing mode on %s\n", g.gl_pathv[i]); err = do_setstat(conn, g.gl_pathv[i], &a); if (err != 0 && err_abort) break; } break; case I_CHOWN: case I_CHGRP: path1 = make_absolute(path1, *pwd); remote_glob(conn, path1, GLOB_NOCHECK, NULL, &g); for (i = 0; g.gl_pathv[i] && !interrupted; i++) { if (!(aa = do_stat(conn, g.gl_pathv[i], 0))) { if (err_abort) { err = -1; break; } else continue; } if (!(aa->flags & SSH2_FILEXFER_ATTR_UIDGID)) { error("Can't get current ownership of " "remote file \"%s\"", g.gl_pathv[i]); if (err_abort) { err = -1; break; } else continue; } aa->flags &= SSH2_FILEXFER_ATTR_UIDGID; if (cmdnum == I_CHOWN) { if (!quiet) mprintf("Changing owner on %s\n", g.gl_pathv[i]); aa->uid = n_arg; } else { if (!quiet) mprintf("Changing group on %s\n", g.gl_pathv[i]); aa->gid = n_arg; } err = do_setstat(conn, g.gl_pathv[i], aa); if (err != 0 && err_abort) break; } break; case I_PWD: mprintf("Remote working directory: %s\n", *pwd); break; case I_LPWD: if (!getcwd(path_buf, sizeof(path_buf))) { error("Couldn't get local cwd: %s", strerror(errno)); err = -1; break; } mprintf("Local working directory: %s\n", path_buf); break; case I_QUIT: /* Processed below */ break; case I_HELP: help(); break; case I_VERSION: printf("SFTP protocol version %u\n", sftp_proto_version(conn)); break; case I_PROGRESS: showprogress = !showprogress; if (showprogress) printf("Progress meter enabled\n"); else printf("Progress meter disabled\n"); break; default: fatal("%d is not implemented", cmdnum); } if (g.gl_pathc) globfree(&g); free(path1); free(path2); /* If an unignored error occurs in batch mode we should abort. */ if (err_abort && err != 0) return (-1); else if (cmdnum == I_QUIT) return (1); return (0); } #ifdef USE_LIBEDIT static char * prompt(EditLine *el) { return ("sftp> "); } /* Display entries in 'list' after skipping the first 'len' chars */ static void complete_display(char **list, u_int len) { u_int y, m = 0, width = 80, columns = 1, colspace = 0, llen; struct winsize ws; char *tmp; /* Count entries for sort and find longest */ for (y = 0; list[y]; y++) m = MAXIMUM(m, strlen(list[y])); if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) != -1) width = ws.ws_col; m = m > len ? m - len : 0; columns = width / (m + 2); columns = MAXIMUM(columns, 1); colspace = width / columns; colspace = MINIMUM(colspace, width); printf("\n"); m = 1; for (y = 0; list[y]; y++) { llen = strlen(list[y]); tmp = llen > len ? list[y] + len : ""; mprintf("%-*s", colspace, tmp); if (m >= columns) { printf("\n"); m = 1; } else m++; } printf("\n"); } /* * Given a "list" of words that begin with a common prefix of "word", * attempt to find an autocompletion to extends "word" by the next * characters common to all entries in "list". */ static char * complete_ambiguous(const char *word, char **list, size_t count) { if (word == NULL) return NULL; if (count > 0) { u_int y, matchlen = strlen(list[0]); /* Find length of common stem */ for (y = 1; list[y]; y++) { u_int x; for (x = 0; x < matchlen; x++) if (list[0][x] != list[y][x]) break; matchlen = x; } if (matchlen > strlen(word)) { char *tmp = xstrdup(list[0]); tmp[matchlen] = '\0'; return tmp; } } return xstrdup(word); } /* Autocomplete a sftp command */ static int complete_cmd_parse(EditLine *el, char *cmd, int lastarg, char quote, int terminated) { u_int y, count = 0, cmdlen, tmplen; char *tmp, **list, argterm[3]; const LineInfo *lf; list = xcalloc((sizeof(cmds) / sizeof(*cmds)) + 1, sizeof(char *)); /* No command specified: display all available commands */ if (cmd == NULL) { for (y = 0; cmds[y].c; y++) list[count++] = xstrdup(cmds[y].c); list[count] = NULL; complete_display(list, 0); for (y = 0; list[y] != NULL; y++) free(list[y]); free(list); return count; } /* Prepare subset of commands that start with "cmd" */ cmdlen = strlen(cmd); for (y = 0; cmds[y].c; y++) { if (!strncasecmp(cmd, cmds[y].c, cmdlen)) list[count++] = xstrdup(cmds[y].c); } list[count] = NULL; if (count == 0) { free(list); return 0; } /* Complete ambigious command */ tmp = complete_ambiguous(cmd, list, count); if (count > 1) complete_display(list, 0); for (y = 0; list[y]; y++) free(list[y]); free(list); if (tmp != NULL) { tmplen = strlen(tmp); cmdlen = strlen(cmd); /* If cmd may be extended then do so */ if (tmplen > cmdlen) if (el_insertstr(el, tmp + cmdlen) == -1) fatal("el_insertstr failed."); lf = el_line(el); /* Terminate argument cleanly */ if (count == 1) { y = 0; if (!terminated) argterm[y++] = quote; if (lastarg || *(lf->cursor) != ' ') argterm[y++] = ' '; argterm[y] = '\0'; if (y > 0 && el_insertstr(el, argterm) == -1) fatal("el_insertstr failed."); } free(tmp); } return count; } /* * Determine whether a particular sftp command's arguments (if any) * represent local or remote files. */ static int complete_is_remote(char *cmd) { int i; if (cmd == NULL) return -1; for (i = 0; cmds[i].c; i++) { if (!strncasecmp(cmd, cmds[i].c, strlen(cmds[i].c))) return cmds[i].t; } return -1; } /* Autocomplete a filename "file" */ static int complete_match(EditLine *el, struct sftp_conn *conn, char *remote_path, char *file, int remote, int lastarg, char quote, int terminated) { glob_t g; char *tmp, *tmp2, ins[8]; u_int i, hadglob, pwdlen, len, tmplen, filelen, cesc, isesc, isabs; int clen; const LineInfo *lf; /* Glob from "file" location */ if (file == NULL) tmp = xstrdup("*"); else xasprintf(&tmp, "%s*", file); /* Check if the path is absolute. */ isabs = tmp[0] == '/'; memset(&g, 0, sizeof(g)); if (remote != LOCAL) { tmp = make_absolute(tmp, remote_path); remote_glob(conn, tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); } else glob(tmp, GLOB_DOOFFS|GLOB_MARK, NULL, &g); /* Determine length of pwd so we can trim completion display */ for (hadglob = tmplen = pwdlen = 0; tmp[tmplen] != 0; tmplen++) { /* Terminate counting on first unescaped glob metacharacter */ if (tmp[tmplen] == '*' || tmp[tmplen] == '?') { if (tmp[tmplen] != '*' || tmp[tmplen + 1] != '\0') hadglob = 1; break; } if (tmp[tmplen] == '\\' && tmp[tmplen + 1] != '\0') tmplen++; if (tmp[tmplen] == '/') pwdlen = tmplen + 1; /* track last seen '/' */ } free(tmp); tmp = NULL; if (g.gl_matchc == 0) goto out; if (g.gl_matchc > 1) complete_display(g.gl_pathv, pwdlen); /* Don't try to extend globs */ if (file == NULL || hadglob) goto out; tmp2 = complete_ambiguous(file, g.gl_pathv, g.gl_matchc); tmp = path_strip(tmp2, isabs ? NULL : remote_path); free(tmp2); if (tmp == NULL) goto out; tmplen = strlen(tmp); filelen = strlen(file); /* Count the number of escaped characters in the input string. */ cesc = isesc = 0; for (i = 0; i < filelen; i++) { if (!isesc && file[i] == '\\' && i + 1 < filelen){ isesc = 1; cesc++; } else isesc = 0; } if (tmplen > (filelen - cesc)) { tmp2 = tmp + filelen - cesc; len = strlen(tmp2); /* quote argument on way out */ for (i = 0; i < len; i += clen) { if ((clen = mblen(tmp2 + i, len - i)) < 0 || (size_t)clen > sizeof(ins) - 2) fatal("invalid multibyte character"); ins[0] = '\\'; memcpy(ins + 1, tmp2 + i, clen); ins[clen + 1] = '\0'; switch (tmp2[i]) { case '\'': case '"': case '\\': case '\t': case '[': case ' ': case '#': case '*': if (quote == '\0' || tmp2[i] == quote) { if (el_insertstr(el, ins) == -1) fatal("el_insertstr " "failed."); break; } /* FALLTHROUGH */ default: if (el_insertstr(el, ins + 1) == -1) fatal("el_insertstr failed."); break; } } } lf = el_line(el); if (g.gl_matchc == 1) { i = 0; if (!terminated && quote != '\0') ins[i++] = quote; if (*(lf->cursor - 1) != '/' && (lastarg || *(lf->cursor) != ' ')) ins[i++] = ' '; ins[i] = '\0'; if (i > 0 && el_insertstr(el, ins) == -1) fatal("el_insertstr failed."); } free(tmp); out: globfree(&g); return g.gl_matchc; } /* tab-completion hook function, called via libedit */ static unsigned char complete(EditLine *el, int ch) { char **argv, *line, quote; int argc, carg; u_int cursor, len, terminated, ret = CC_ERROR; const LineInfo *lf; struct complete_ctx *complete_ctx; lf = el_line(el); if (el_get(el, EL_CLIENTDATA, (void**)&complete_ctx) != 0) fatal("%s: el_get failed", __func__); /* Figure out which argument the cursor points to */ cursor = lf->cursor - lf->buffer; line = xmalloc(cursor + 1); memcpy(line, lf->buffer, cursor); line[cursor] = '\0'; argv = makeargv(line, &carg, 1, "e, &terminated); free(line); /* Get all the arguments on the line */ len = lf->lastchar - lf->buffer; line = xmalloc(len + 1); memcpy(line, lf->buffer, len); line[len] = '\0'; argv = makeargv(line, &argc, 1, NULL, NULL); /* Ensure cursor is at EOL or a argument boundary */ if (line[cursor] != ' ' && line[cursor] != '\0' && line[cursor] != '\n') { free(line); return ret; } if (carg == 0) { /* Show all available commands */ complete_cmd_parse(el, NULL, argc == carg, '\0', 1); ret = CC_REDISPLAY; } else if (carg == 1 && cursor > 0 && line[cursor - 1] != ' ') { /* Handle the command parsing */ if (complete_cmd_parse(el, argv[0], argc == carg, quote, terminated) != 0) ret = CC_REDISPLAY; } else if (carg >= 1) { /* Handle file parsing */ int remote = complete_is_remote(argv[0]); char *filematch = NULL; if (carg > 1 && line[cursor-1] != ' ') filematch = argv[carg - 1]; if (remote != 0 && complete_match(el, complete_ctx->conn, *complete_ctx->remote_pathp, filematch, remote, carg == argc, quote, terminated) != 0) ret = CC_REDISPLAY; } free(line); return ret; } #endif /* USE_LIBEDIT */ int interactive_loop(struct sftp_conn *conn, char *file1, char *file2) { char *remote_path; char *dir = NULL; char cmd[2048]; int err, interactive; EditLine *el = NULL; #ifdef USE_LIBEDIT History *hl = NULL; HistEvent hev; extern char *__progname; struct complete_ctx complete_ctx; if (!batchmode && isatty(STDIN_FILENO)) { if ((el = el_init(__progname, stdin, stdout, stderr)) == NULL) fatal("Couldn't initialise editline"); if ((hl = history_init()) == NULL) fatal("Couldn't initialise editline history"); history(hl, &hev, H_SETSIZE, 100); el_set(el, EL_HIST, history, hl); el_set(el, EL_PROMPT, prompt); el_set(el, EL_EDITOR, "emacs"); el_set(el, EL_TERMINAL, NULL); el_set(el, EL_SIGNAL, 1); el_source(el, NULL); /* Tab Completion */ el_set(el, EL_ADDFN, "ftp-complete", "Context sensitive argument completion", complete); complete_ctx.conn = conn; complete_ctx.remote_pathp = &remote_path; el_set(el, EL_CLIENTDATA, (void*)&complete_ctx); el_set(el, EL_BIND, "^I", "ftp-complete", NULL); /* enable ctrl-left-arrow and ctrl-right-arrow */ el_set(el, EL_BIND, "\\e[1;5C", "em-next-word", NULL); el_set(el, EL_BIND, "\\e[5C", "em-next-word", NULL); el_set(el, EL_BIND, "\\e[1;5D", "ed-prev-word", NULL); el_set(el, EL_BIND, "\\e\\e[D", "ed-prev-word", NULL); /* make ^w match ksh behaviour */ el_set(el, EL_BIND, "^w", "ed-delete-prev-word", NULL); } #endif /* USE_LIBEDIT */ remote_path = do_realpath(conn, "."); if (remote_path == NULL) fatal("Need cwd"); if (file1 != NULL) { dir = xstrdup(file1); dir = make_absolute(dir, remote_path); if (remote_is_dir(conn, dir) && file2 == NULL) { if (!quiet) mprintf("Changing to: %s\n", dir); snprintf(cmd, sizeof cmd, "cd \"%s\"", dir); if (parse_dispatch_command(conn, cmd, &remote_path, 1) != 0) { free(dir); free(remote_path); free(conn); return (-1); } } else { /* XXX this is wrong wrt quoting */ snprintf(cmd, sizeof cmd, "get%s %s%s%s", global_aflag ? " -a" : "", dir, file2 == NULL ? "" : " ", file2 == NULL ? "" : file2); err = parse_dispatch_command(conn, cmd, &remote_path, 1); free(dir); free(remote_path); free(conn); return (err); } free(dir); } setvbuf(stdout, NULL, _IOLBF, 0); setvbuf(infile, NULL, _IOLBF, 0); interactive = !batchmode && isatty(STDIN_FILENO); err = 0; for (;;) { char *cp; signal(SIGINT, SIG_IGN); if (el == NULL) { if (interactive) printf("sftp> "); if (fgets(cmd, sizeof(cmd), infile) == NULL) { if (interactive) printf("\n"); break; } if (!interactive) { /* Echo command */ mprintf("sftp> %s", cmd); if (strlen(cmd) > 0 && cmd[strlen(cmd) - 1] != '\n') printf("\n"); } } else { #ifdef USE_LIBEDIT const char *line; int count = 0; if ((line = el_gets(el, &count)) == NULL || count <= 0) { printf("\n"); break; } history(hl, &hev, H_ENTER, line); if (strlcpy(cmd, line, sizeof(cmd)) >= sizeof(cmd)) { fprintf(stderr, "Error: input line too long\n"); continue; } #endif /* USE_LIBEDIT */ } cp = strrchr(cmd, '\n'); if (cp) *cp = '\0'; /* Handle user interrupts gracefully during commands */ interrupted = 0; signal(SIGINT, cmd_interrupt); err = parse_dispatch_command(conn, cmd, &remote_path, batchmode); if (err != 0) break; } free(remote_path); free(conn); #ifdef USE_LIBEDIT if (el != NULL) el_end(el); #endif /* USE_LIBEDIT */ /* err == 1 signifies normal "quit" exit */ return (err >= 0 ? 0 : -1); } static void connect_to_server(char *path, char **args, int *in, int *out) { int c_in, c_out; #ifdef USE_PIPES int pin[2], pout[2]; if ((pipe(pin) == -1) || (pipe(pout) == -1)) fatal("pipe: %s", strerror(errno)); *in = pin[0]; *out = pout[1]; c_in = pout[0]; c_out = pin[1]; #else /* USE_PIPES */ int inout[2]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) == -1) fatal("socketpair: %s", strerror(errno)); *in = *out = inout[0]; c_in = c_out = inout[1]; #endif /* USE_PIPES */ if ((sshpid = fork()) == -1) fatal("fork: %s", strerror(errno)); else if (sshpid == 0) { if ((dup2(c_in, STDIN_FILENO) == -1) || (dup2(c_out, STDOUT_FILENO) == -1)) { fprintf(stderr, "dup2: %s\n", strerror(errno)); _exit(1); } close(*in); close(*out); close(c_in); close(c_out); /* * The underlying ssh is in the same process group, so we must * ignore SIGINT if we want to gracefully abort commands, * otherwise the signal will make it to the ssh process and * kill it too. Contrawise, since sftp sends SIGTERMs to the * underlying ssh, it must *not* ignore that signal. */ signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_DFL); execvp(path, args); fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); _exit(1); } signal(SIGTERM, killchild); signal(SIGINT, killchild); signal(SIGHUP, killchild); signal(SIGTSTP, suspchild); signal(SIGTTIN, suspchild); signal(SIGTTOU, suspchild); close(c_in); close(c_out); } static void usage(void) { extern char *__progname; fprintf(stderr, "usage: %s [-1246aCfpqrv] [-B buffer_size] [-b batchfile] [-c cipher]\n" " [-D sftp_server_path] [-F ssh_config] " "[-i identity_file] [-l limit]\n" " [-o ssh_option] [-P port] [-R num_requests] " "[-S program]\n" " [-s subsystem | sftp_server] host\n" " %s [user@]host[:file ...]\n" " %s [user@]host[:dir[/]]\n" " %s -b batchfile [user@]host\n", __progname, __progname, __progname, __progname); exit(1); } int main(int argc, char **argv) { int in, out, ch, err; char *host = NULL, *userhost, *cp, *file2 = NULL; int debug_level = 0, sshver = 2; char *file1 = NULL, *sftp_server = NULL; char *ssh_program = _PATH_SSH_PROGRAM, *sftp_direct = NULL; const char *errstr; LogLevel ll = SYSLOG_LEVEL_INFO; arglist args; extern int optind; extern char *optarg; struct sftp_conn *conn; size_t copy_buffer_len = DEFAULT_COPY_BUFLEN; size_t num_requests = DEFAULT_NUM_REQUESTS; long long limit_kbps = 0; ssh_malloc_init(); /* must be called before any mallocs */ /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); msetlocale(); __progname = ssh_get_progname(argv[0]); memset(&args, '\0', sizeof(args)); args.list = NULL; addargs(&args, "%s", ssh_program); addargs(&args, "-oForwardX11 no"); addargs(&args, "-oForwardAgent no"); addargs(&args, "-oPermitLocalCommand no"); addargs(&args, "-oClearAllForwardings yes"); ll = SYSLOG_LEVEL_INFO; infile = stdin; while ((ch = getopt(argc, argv, "1246afhpqrvCc:D:i:l:o:s:S:b:B:F:P:R:")) != -1) { switch (ch) { /* Passed through to ssh(1) */ case '4': case '6': case 'C': addargs(&args, "-%c", ch); break; /* Passed through to ssh(1) with argument */ case 'F': case 'c': case 'i': case 'o': addargs(&args, "-%c", ch); addargs(&args, "%s", optarg); break; case 'q': ll = SYSLOG_LEVEL_ERROR; quiet = 1; showprogress = 0; addargs(&args, "-%c", ch); break; case 'P': addargs(&args, "-oPort %s", optarg); break; case 'v': if (debug_level < 3) { addargs(&args, "-v"); ll = SYSLOG_LEVEL_DEBUG1 + debug_level; } debug_level++; break; case '1': sshver = 1; if (sftp_server == NULL) sftp_server = _PATH_SFTP_SERVER; break; case '2': sshver = 2; break; case 'a': global_aflag = 1; break; case 'B': copy_buffer_len = strtol(optarg, &cp, 10); if (copy_buffer_len == 0 || *cp != '\0') fatal("Invalid buffer size \"%s\"", optarg); break; case 'b': if (batchmode) fatal("Batch file already specified."); /* Allow "-" as stdin */ if (strcmp(optarg, "-") != 0 && (infile = fopen(optarg, "r")) == NULL) fatal("%s (%s).", strerror(errno), optarg); showprogress = 0; quiet = batchmode = 1; addargs(&args, "-obatchmode yes"); break; case 'f': global_fflag = 1; break; case 'p': global_pflag = 1; break; case 'D': sftp_direct = optarg; break; case 'l': limit_kbps = strtonum(optarg, 1, 100 * 1024 * 1024, &errstr); if (errstr != NULL) usage(); limit_kbps *= 1024; /* kbps */ break; case 'r': global_rflag = 1; break; case 'R': num_requests = strtol(optarg, &cp, 10); if (num_requests == 0 || *cp != '\0') fatal("Invalid number of requests \"%s\"", optarg); break; case 's': sftp_server = optarg; break; case 'S': ssh_program = optarg; replacearg(&args, 0, "%s", ssh_program); break; case 'h': default: usage(); } } if (!isatty(STDERR_FILENO)) showprogress = 0; log_init(argv[0], ll, SYSLOG_FACILITY_USER, 1); if (sftp_direct == NULL) { if (optind == argc || argc > (optind + 2)) usage(); userhost = xstrdup(argv[optind]); file2 = argv[optind+1]; if ((host = strrchr(userhost, '@')) == NULL) host = userhost; else { *host++ = '\0'; if (!userhost[0]) { fprintf(stderr, "Missing username\n"); usage(); } addargs(&args, "-l"); addargs(&args, "%s", userhost); } if ((cp = colon(host)) != NULL) { *cp++ = '\0'; file1 = cp; } host = cleanhostname(host); if (!*host) { fprintf(stderr, "Missing hostname\n"); usage(); } addargs(&args, "-oProtocol %d", sshver); /* no subsystem if the server-spec contains a '/' */ if (sftp_server == NULL || strchr(sftp_server, '/') == NULL) addargs(&args, "-s"); addargs(&args, "--"); addargs(&args, "%s", host); addargs(&args, "%s", (sftp_server != NULL ? sftp_server : "sftp")); connect_to_server(ssh_program, args.list, &in, &out); } else { args.list = NULL; addargs(&args, "sftp-server"); connect_to_server(sftp_direct, args.list, &in, &out); } freeargs(&args); conn = do_init(in, out, copy_buffer_len, num_requests, limit_kbps); if (conn == NULL) fatal("Couldn't initialise connection to server"); if (!quiet) { if (sftp_direct == NULL) fprintf(stderr, "Connected to %s.\n", host); else fprintf(stderr, "Attached to %s.\n", sftp_direct); } err = interactive_loop(conn, file1, file2); #if !defined(USE_PIPES) shutdown(in, SHUT_RDWR); shutdown(out, SHUT_RDWR); #endif close(in); close(out); if (batchmode) fclose(infile); while (waitpid(sshpid, NULL, 0) == -1) if (errno != EINTR) fatal("Couldn't wait for ssh process: %s", strerror(errno)); exit(err == 0 ? 0 : 1); } diff --git a/crypto/openssh/ssh-agent.c b/crypto/openssh/ssh-agent.c index 0d2420480ee0..ef5c7f6e4855 100644 --- a/crypto/openssh/ssh-agent.c +++ b/crypto/openssh/ssh-agent.c @@ -1,1487 +1,1494 @@ -/* $OpenBSD: ssh-agent.c,v 1.215 2016/11/30 03:07:37 djm Exp $ */ +/* $OpenBSD: ssh-agent.c,v 1.218 2017/03/15 03:52:30 deraadt Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * The authentication agent program. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" __RCSID("$FreeBSD$"); #include #include #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #ifdef HAVE_SYS_UN_H # include #endif #include "openbsd-compat/sys-queue.h" #ifdef WITH_OPENSSL #include #include "openbsd-compat/openssl-compat.h" #endif #include #include #include #ifdef HAVE_PATHS_H # include #endif #include #include #include #include #include #include #include #ifdef HAVE_UTIL_H # include #endif #include "xmalloc.h" #include "ssh.h" #include "rsa.h" #include "sshbuf.h" #include "sshkey.h" #include "authfd.h" #include "compat.h" #include "log.h" #include "misc.h" #include "digest.h" #include "ssherr.h" #include "match.h" #ifdef ENABLE_PKCS11 #include "ssh-pkcs11.h" #endif #ifndef DEFAULT_PKCS11_WHITELIST -# define DEFAULT_PKCS11_WHITELIST "/usr/lib/*,/usr/local/lib/*" +# define DEFAULT_PKCS11_WHITELIST "/usr/lib*/*,/usr/local/lib*/*" #endif typedef enum { AUTH_UNUSED, AUTH_SOCKET, AUTH_CONNECTION } sock_type; typedef struct { int fd; sock_type type; struct sshbuf *input; struct sshbuf *output; struct sshbuf *request; } SocketEntry; u_int sockets_alloc = 0; SocketEntry *sockets = NULL; typedef struct identity { TAILQ_ENTRY(identity) next; struct sshkey *key; char *comment; char *provider; time_t death; u_int confirm; } Identity; typedef struct { int nentries; TAILQ_HEAD(idqueue, identity) idlist; } Idtab; /* private key table, one per protocol version */ Idtab idtable[3]; int max_fd = 0; /* pid of shell == parent of agent */ pid_t parent_pid = -1; time_t parent_alive_interval = 0; /* pid of process for which cleanup_socket is applicable */ pid_t cleanup_pid = 0; /* pathname and directory for AUTH_SOCKET */ char socket_name[PATH_MAX]; char socket_dir[PATH_MAX]; /* PKCS#11 path whitelist */ static char *pkcs11_whitelist; /* locking */ #define LOCK_SIZE 32 #define LOCK_SALT_SIZE 16 #define LOCK_ROUNDS 1 int locked = 0; u_char lock_pwhash[LOCK_SIZE]; u_char lock_salt[LOCK_SALT_SIZE]; extern char *__progname; /* Default lifetime in seconds (0 == forever) */ static long lifetime = 0; static int fingerprint_hash = SSH_FP_HASH_DEFAULT; /* * Client connection count; incremented in new_socket() and decremented in * close_socket(). When it reaches 0, ssh-agent will exit. Since it is * normally initialized to 1, it will never reach 0. However, if the -x * option is specified, it is initialized to 0 in main(); in that case, * ssh-agent will exit as soon as it has had at least one client but no * longer has any. */ static int xcount = 1; static void close_socket(SocketEntry *e) { int last = 0; if (e->type == AUTH_CONNECTION) { debug("xcount %d -> %d", xcount, xcount - 1); if (--xcount == 0) last = 1; } close(e->fd); e->fd = -1; e->type = AUTH_UNUSED; sshbuf_free(e->input); sshbuf_free(e->output); sshbuf_free(e->request); if (last) cleanup_exit(0); } static void idtab_init(void) { int i; for (i = 0; i <=2; i++) { TAILQ_INIT(&idtable[i].idlist); idtable[i].nentries = 0; } } /* return private key table for requested protocol version */ static Idtab * idtab_lookup(int version) { if (version < 1 || version > 2) fatal("internal error, bad protocol version %d", version); return &idtable[version]; } static void free_identity(Identity *id) { sshkey_free(id->key); free(id->provider); free(id->comment); free(id); } /* return matching private key for given public key */ static Identity * lookup_identity(struct sshkey *key, int version) { Identity *id; Idtab *tab = idtab_lookup(version); TAILQ_FOREACH(id, &tab->idlist, next) { if (sshkey_equal(key, id->key)) return (id); } return (NULL); } /* Check confirmation of keysign request */ static int confirm_key(Identity *id) { char *p; int ret = -1; p = sshkey_fingerprint(id->key, fingerprint_hash, SSH_FP_DEFAULT); if (p != NULL && ask_permission("Allow use of key %s?\nKey fingerprint %s.", id->comment, p)) ret = 0; free(p); return (ret); } static void send_status(SocketEntry *e, int success) { int r; if ((r = sshbuf_put_u32(e->output, 1)) != 0 || (r = sshbuf_put_u8(e->output, success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); } /* send list of supported public keys to 'client' */ static void process_request_identities(SocketEntry *e, int version) { Idtab *tab = idtab_lookup(version); Identity *id; struct sshbuf *msg; int r; if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); if ((r = sshbuf_put_u8(msg, (version == 1) ? SSH_AGENT_RSA_IDENTITIES_ANSWER : SSH2_AGENT_IDENTITIES_ANSWER)) != 0 || (r = sshbuf_put_u32(msg, tab->nentries)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); TAILQ_FOREACH(id, &tab->idlist, next) { if (id->key->type == KEY_RSA1) { #ifdef WITH_SSH1 if ((r = sshbuf_put_u32(msg, BN_num_bits(id->key->rsa->n))) != 0 || (r = sshbuf_put_bignum1(msg, id->key->rsa->e)) != 0 || (r = sshbuf_put_bignum1(msg, id->key->rsa->n)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); #endif } else { u_char *blob; size_t blen; if ((r = sshkey_to_blob(id->key, &blob, &blen)) != 0) { error("%s: sshkey_to_blob: %s", __func__, ssh_err(r)); continue; } if ((r = sshbuf_put_string(msg, blob, blen)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); free(blob); } if ((r = sshbuf_put_cstring(msg, id->comment)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); } if ((r = sshbuf_put_stringb(e->output, msg)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); sshbuf_free(msg); } #ifdef WITH_SSH1 /* ssh1 only */ static void process_authentication_challenge1(SocketEntry *e) { u_char buf[32], mdbuf[16], session_id[16]; u_int response_type; BIGNUM *challenge; Identity *id; int r, len; struct sshbuf *msg; struct ssh_digest_ctx *md; struct sshkey *key; if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); if ((key = sshkey_new(KEY_RSA1)) == NULL) fatal("%s: sshkey_new failed", __func__); if ((challenge = BN_new()) == NULL) fatal("%s: BN_new failed", __func__); if ((r = sshbuf_get_u32(e->request, NULL)) != 0 || /* ignored */ (r = sshbuf_get_bignum1(e->request, key->rsa->e)) != 0 || (r = sshbuf_get_bignum1(e->request, key->rsa->n)) != 0 || (r = sshbuf_get_bignum1(e->request, challenge))) fatal("%s: buffer error: %s", __func__, ssh_err(r)); /* Only protocol 1.1 is supported */ if (sshbuf_len(e->request) == 0) goto failure; if ((r = sshbuf_get(e->request, session_id, sizeof(session_id))) != 0 || (r = sshbuf_get_u32(e->request, &response_type)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (response_type != 1) goto failure; id = lookup_identity(key, 1); if (id != NULL && (!id->confirm || confirm_key(id) == 0)) { struct sshkey *private = id->key; /* Decrypt the challenge using the private key. */ if ((r = rsa_private_decrypt(challenge, challenge, private->rsa) != 0)) { fatal("%s: rsa_public_encrypt: %s", __func__, ssh_err(r)); goto failure; /* XXX ? */ } /* The response is MD5 of decrypted challenge plus session id */ len = BN_num_bytes(challenge); if (len <= 0 || len > 32) { logit("%s: bad challenge length %d", __func__, len); goto failure; } memset(buf, 0, 32); BN_bn2bin(challenge, buf + 32 - len); if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL || ssh_digest_update(md, buf, 32) < 0 || ssh_digest_update(md, session_id, 16) < 0 || ssh_digest_final(md, mdbuf, sizeof(mdbuf)) < 0) fatal("%s: md5 failed", __func__); ssh_digest_free(md); /* Send the response. */ if ((r = sshbuf_put_u8(msg, SSH_AGENT_RSA_RESPONSE)) != 0 || (r = sshbuf_put(msg, mdbuf, sizeof(mdbuf))) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); goto send; } failure: /* Unknown identity or protocol error. Send failure. */ if ((r = sshbuf_put_u8(msg, SSH_AGENT_FAILURE)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); send: if ((r = sshbuf_put_stringb(e->output, msg)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); sshkey_free(key); BN_clear_free(challenge); sshbuf_free(msg); } #endif static char * agent_decode_alg(struct sshkey *key, u_int flags) { if (key->type == KEY_RSA) { if (flags & SSH_AGENT_RSA_SHA2_256) return "rsa-sha2-256"; else if (flags & SSH_AGENT_RSA_SHA2_512) return "rsa-sha2-512"; } return NULL; } /* ssh2 only */ static void process_sign_request2(SocketEntry *e) { u_char *blob, *data, *signature = NULL; size_t blen, dlen, slen = 0; u_int compat = 0, flags; int r, ok = -1; struct sshbuf *msg; struct sshkey *key; struct identity *id; if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); if ((r = sshbuf_get_string(e->request, &blob, &blen)) != 0 || (r = sshbuf_get_string(e->request, &data, &dlen)) != 0 || (r = sshbuf_get_u32(e->request, &flags)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (flags & SSH_AGENT_OLD_SIGNATURE) compat = SSH_BUG_SIGBLOB; if ((r = sshkey_from_blob(blob, blen, &key)) != 0) { error("%s: cannot parse key blob: %s", __func__, ssh_err(r)); goto send; } if ((id = lookup_identity(key, 2)) == NULL) { verbose("%s: %s key not found", __func__, sshkey_type(key)); goto send; } if (id->confirm && confirm_key(id) != 0) { verbose("%s: user refused key", __func__); goto send; } if ((r = sshkey_sign(id->key, &signature, &slen, data, dlen, agent_decode_alg(key, flags), compat)) != 0) { error("%s: sshkey_sign: %s", __func__, ssh_err(r)); goto send; } /* Success */ ok = 0; send: sshkey_free(key); if (ok == 0) { if ((r = sshbuf_put_u8(msg, SSH2_AGENT_SIGN_RESPONSE)) != 0 || (r = sshbuf_put_string(msg, signature, slen)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); } else if ((r = sshbuf_put_u8(msg, SSH_AGENT_FAILURE)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if ((r = sshbuf_put_stringb(e->output, msg)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); sshbuf_free(msg); free(data); free(blob); free(signature); } /* shared */ static void process_remove_identity(SocketEntry *e, int version) { size_t blen; int r, success = 0; struct sshkey *key = NULL; u_char *blob; #ifdef WITH_SSH1 u_int bits; #endif /* WITH_SSH1 */ switch (version) { #ifdef WITH_SSH1 case 1: if ((key = sshkey_new(KEY_RSA1)) == NULL) { error("%s: sshkey_new failed", __func__); return; } if ((r = sshbuf_get_u32(e->request, &bits)) != 0 || (r = sshbuf_get_bignum1(e->request, key->rsa->e)) != 0 || (r = sshbuf_get_bignum1(e->request, key->rsa->n)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (bits != sshkey_size(key)) logit("Warning: identity keysize mismatch: " "actual %u, announced %u", sshkey_size(key), bits); break; #endif /* WITH_SSH1 */ case 2: if ((r = sshbuf_get_string(e->request, &blob, &blen)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if ((r = sshkey_from_blob(blob, blen, &key)) != 0) error("%s: sshkey_from_blob failed: %s", __func__, ssh_err(r)); free(blob); break; } if (key != NULL) { Identity *id = lookup_identity(key, version); if (id != NULL) { /* * We have this key. Free the old key. Since we * don't want to leave empty slots in the middle of * the array, we actually free the key there and move * all the entries between the empty slot and the end * of the array. */ Idtab *tab = idtab_lookup(version); if (tab->nentries < 1) fatal("process_remove_identity: " "internal error: tab->nentries %d", tab->nentries); TAILQ_REMOVE(&tab->idlist, id, next); free_identity(id); tab->nentries--; success = 1; } sshkey_free(key); } send_status(e, success); } static void process_remove_all_identities(SocketEntry *e, int version) { Idtab *tab = idtab_lookup(version); Identity *id; /* Loop over all identities and clear the keys. */ for (id = TAILQ_FIRST(&tab->idlist); id; id = TAILQ_FIRST(&tab->idlist)) { TAILQ_REMOVE(&tab->idlist, id, next); free_identity(id); } /* Mark that there are no identities. */ tab->nentries = 0; /* Send success. */ send_status(e, 1); } /* removes expired keys and returns number of seconds until the next expiry */ static time_t reaper(void) { time_t deadline = 0, now = monotime(); Identity *id, *nxt; int version; Idtab *tab; for (version = 1; version < 3; version++) { tab = idtab_lookup(version); for (id = TAILQ_FIRST(&tab->idlist); id; id = nxt) { nxt = TAILQ_NEXT(id, next); if (id->death == 0) continue; if (now >= id->death) { debug("expiring key '%s'", id->comment); TAILQ_REMOVE(&tab->idlist, id, next); free_identity(id); tab->nentries--; } else deadline = (deadline == 0) ? id->death : MINIMUM(deadline, id->death); } } if (deadline == 0 || deadline <= now) return 0; else return (deadline - now); } /* * XXX this and the corresponding serialisation function probably belongs * in key.c */ #ifdef WITH_SSH1 static int agent_decode_rsa1(struct sshbuf *m, struct sshkey **kp) { struct sshkey *k = NULL; int r = SSH_ERR_INTERNAL_ERROR; *kp = NULL; if ((k = sshkey_new_private(KEY_RSA1)) == NULL) return SSH_ERR_ALLOC_FAIL; if ((r = sshbuf_get_u32(m, NULL)) != 0 || /* ignored */ (r = sshbuf_get_bignum1(m, k->rsa->n)) != 0 || (r = sshbuf_get_bignum1(m, k->rsa->e)) != 0 || (r = sshbuf_get_bignum1(m, k->rsa->d)) != 0 || (r = sshbuf_get_bignum1(m, k->rsa->iqmp)) != 0 || /* SSH1 and SSL have p and q swapped */ (r = sshbuf_get_bignum1(m, k->rsa->q)) != 0 || /* p */ (r = sshbuf_get_bignum1(m, k->rsa->p)) != 0) /* q */ goto out; /* Generate additional parameters */ if ((r = rsa_generate_additional_parameters(k->rsa)) != 0) goto out; /* enable blinding */ if (RSA_blinding_on(k->rsa, NULL) != 1) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } r = 0; /* success */ out: if (r == 0) *kp = k; else sshkey_free(k); return r; } #endif /* WITH_SSH1 */ static void process_add_identity(SocketEntry *e, int version) { Idtab *tab = idtab_lookup(version); Identity *id; int success = 0, confirm = 0; u_int seconds; char *comment = NULL; time_t death = 0; struct sshkey *k = NULL; u_char ctype; int r = SSH_ERR_INTERNAL_ERROR; switch (version) { #ifdef WITH_SSH1 case 1: r = agent_decode_rsa1(e->request, &k); break; #endif /* WITH_SSH1 */ case 2: r = sshkey_private_deserialize(e->request, &k); break; } if (r != 0 || k == NULL || (r = sshbuf_get_cstring(e->request, &comment, NULL)) != 0) { error("%s: decode private key: %s", __func__, ssh_err(r)); goto err; } while (sshbuf_len(e->request)) { if ((r = sshbuf_get_u8(e->request, &ctype)) != 0) { error("%s: buffer error: %s", __func__, ssh_err(r)); goto err; } switch (ctype) { case SSH_AGENT_CONSTRAIN_LIFETIME: if ((r = sshbuf_get_u32(e->request, &seconds)) != 0) { error("%s: bad lifetime constraint: %s", __func__, ssh_err(r)); goto err; } death = monotime() + seconds; break; case SSH_AGENT_CONSTRAIN_CONFIRM: confirm = 1; break; default: error("%s: Unknown constraint %d", __func__, ctype); err: sshbuf_reset(e->request); free(comment); sshkey_free(k); goto send; } } success = 1; if (lifetime && !death) death = monotime() + lifetime; if ((id = lookup_identity(k, version)) == NULL) { id = xcalloc(1, sizeof(Identity)); id->key = k; TAILQ_INSERT_TAIL(&tab->idlist, id, next); /* Increment the number of identities. */ tab->nentries++; } else { sshkey_free(k); free(id->comment); } id->comment = comment; id->death = death; id->confirm = confirm; send: send_status(e, success); } /* XXX todo: encrypt sensitive data with passphrase */ static void process_lock_agent(SocketEntry *e, int lock) { int r, success = 0, delay; char *passwd; u_char passwdhash[LOCK_SIZE]; static u_int fail_count = 0; size_t pwlen; if ((r = sshbuf_get_cstring(e->request, &passwd, &pwlen)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (pwlen == 0) { debug("empty password not supported"); } else if (locked && !lock) { if (bcrypt_pbkdf(passwd, pwlen, lock_salt, sizeof(lock_salt), passwdhash, sizeof(passwdhash), LOCK_ROUNDS) < 0) fatal("bcrypt_pbkdf"); if (timingsafe_bcmp(passwdhash, lock_pwhash, LOCK_SIZE) == 0) { debug("agent unlocked"); locked = 0; fail_count = 0; explicit_bzero(lock_pwhash, sizeof(lock_pwhash)); success = 1; } else { /* delay in 0.1s increments up to 10s */ if (fail_count < 100) fail_count++; delay = 100000 * fail_count; debug("unlock failed, delaying %0.1lf seconds", (double)delay/1000000); usleep(delay); } explicit_bzero(passwdhash, sizeof(passwdhash)); } else if (!locked && lock) { debug("agent locked"); locked = 1; arc4random_buf(lock_salt, sizeof(lock_salt)); if (bcrypt_pbkdf(passwd, pwlen, lock_salt, sizeof(lock_salt), lock_pwhash, sizeof(lock_pwhash), LOCK_ROUNDS) < 0) fatal("bcrypt_pbkdf"); success = 1; } explicit_bzero(passwd, pwlen); free(passwd); send_status(e, success); } static void no_identities(SocketEntry *e, u_int type) { struct sshbuf *msg; int r; if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); if ((r = sshbuf_put_u8(msg, (type == SSH_AGENTC_REQUEST_RSA_IDENTITIES) ? SSH_AGENT_RSA_IDENTITIES_ANSWER : SSH2_AGENT_IDENTITIES_ANSWER)) != 0 || (r = sshbuf_put_u32(msg, 0)) != 0 || (r = sshbuf_put_stringb(e->output, msg)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); sshbuf_free(msg); } #ifdef ENABLE_PKCS11 static void process_add_smartcard_key(SocketEntry *e) { char *provider = NULL, *pin, canonical_provider[PATH_MAX]; int r, i, version, count = 0, success = 0, confirm = 0; u_int seconds; time_t death = 0; u_char type; struct sshkey **keys = NULL, *k; Identity *id; Idtab *tab; if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 || (r = sshbuf_get_cstring(e->request, &pin, NULL)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); while (sshbuf_len(e->request)) { if ((r = sshbuf_get_u8(e->request, &type)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); switch (type) { case SSH_AGENT_CONSTRAIN_LIFETIME: if ((r = sshbuf_get_u32(e->request, &seconds)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); death = monotime() + seconds; break; case SSH_AGENT_CONSTRAIN_CONFIRM: confirm = 1; break; default: error("process_add_smartcard_key: " "Unknown constraint type %d", type); goto send; } } if (realpath(provider, canonical_provider) == NULL) { verbose("failed PKCS#11 add of \"%.100s\": realpath: %s", provider, strerror(errno)); goto send; } if (match_pattern_list(canonical_provider, pkcs11_whitelist, 0) != 1) { verbose("refusing PKCS#11 add of \"%.100s\": " "provider not whitelisted", canonical_provider); goto send; } debug("%s: add %.100s", __func__, canonical_provider); if (lifetime && !death) death = monotime() + lifetime; count = pkcs11_add_provider(canonical_provider, pin, &keys); for (i = 0; i < count; i++) { k = keys[i]; version = k->type == KEY_RSA1 ? 1 : 2; tab = idtab_lookup(version); if (lookup_identity(k, version) == NULL) { id = xcalloc(1, sizeof(Identity)); id->key = k; id->provider = xstrdup(canonical_provider); id->comment = xstrdup(canonical_provider); /* XXX */ id->death = death; id->confirm = confirm; TAILQ_INSERT_TAIL(&tab->idlist, id, next); tab->nentries++; success = 1; } else { sshkey_free(k); } keys[i] = NULL; } send: free(pin); free(provider); free(keys); send_status(e, success); } static void process_remove_smartcard_key(SocketEntry *e) { - char *provider = NULL, *pin = NULL; + char *provider = NULL, *pin = NULL, canonical_provider[PATH_MAX]; int r, version, success = 0; Identity *id, *nxt; Idtab *tab; if ((r = sshbuf_get_cstring(e->request, &provider, NULL)) != 0 || (r = sshbuf_get_cstring(e->request, &pin, NULL)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); free(pin); + if (realpath(provider, canonical_provider) == NULL) { + verbose("failed PKCS#11 add of \"%.100s\": realpath: %s", + provider, strerror(errno)); + goto send; + } + + debug("%s: remove %.100s", __func__, canonical_provider); for (version = 1; version < 3; version++) { tab = idtab_lookup(version); for (id = TAILQ_FIRST(&tab->idlist); id; id = nxt) { nxt = TAILQ_NEXT(id, next); /* Skip file--based keys */ if (id->provider == NULL) continue; - if (!strcmp(provider, id->provider)) { + if (!strcmp(canonical_provider, id->provider)) { TAILQ_REMOVE(&tab->idlist, id, next); free_identity(id); tab->nentries--; } } } - if (pkcs11_del_provider(provider) == 0) + if (pkcs11_del_provider(canonical_provider) == 0) success = 1; else error("process_remove_smartcard_key:" " pkcs11_del_provider failed"); +send: free(provider); send_status(e, success); } #endif /* ENABLE_PKCS11 */ /* dispatch incoming messages */ static void process_message(SocketEntry *e) { u_int msg_len; u_char type; const u_char *cp; int r; if (sshbuf_len(e->input) < 5) return; /* Incomplete message. */ cp = sshbuf_ptr(e->input); msg_len = PEEK_U32(cp); if (msg_len > 256 * 1024) { close_socket(e); return; } if (sshbuf_len(e->input) < msg_len + 4) return; /* move the current input to e->request */ sshbuf_reset(e->request); if ((r = sshbuf_get_stringb(e->input, e->request)) != 0 || (r = sshbuf_get_u8(e->request, &type)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); /* check wheter agent is locked */ if (locked && type != SSH_AGENTC_UNLOCK) { sshbuf_reset(e->request); switch (type) { case SSH_AGENTC_REQUEST_RSA_IDENTITIES: case SSH2_AGENTC_REQUEST_IDENTITIES: /* send empty lists */ no_identities(e, type); break; default: /* send a fail message for all other request types */ send_status(e, 0); } return; } debug("type %d", type); switch (type) { case SSH_AGENTC_LOCK: case SSH_AGENTC_UNLOCK: process_lock_agent(e, type == SSH_AGENTC_LOCK); break; #ifdef WITH_SSH1 /* ssh1 */ case SSH_AGENTC_RSA_CHALLENGE: process_authentication_challenge1(e); break; case SSH_AGENTC_REQUEST_RSA_IDENTITIES: process_request_identities(e, 1); break; case SSH_AGENTC_ADD_RSA_IDENTITY: case SSH_AGENTC_ADD_RSA_ID_CONSTRAINED: process_add_identity(e, 1); break; case SSH_AGENTC_REMOVE_RSA_IDENTITY: process_remove_identity(e, 1); break; #endif case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES: process_remove_all_identities(e, 1); /* safe for !WITH_SSH1 */ break; /* ssh2 */ case SSH2_AGENTC_SIGN_REQUEST: process_sign_request2(e); break; case SSH2_AGENTC_REQUEST_IDENTITIES: process_request_identities(e, 2); break; case SSH2_AGENTC_ADD_IDENTITY: case SSH2_AGENTC_ADD_ID_CONSTRAINED: process_add_identity(e, 2); break; case SSH2_AGENTC_REMOVE_IDENTITY: process_remove_identity(e, 2); break; case SSH2_AGENTC_REMOVE_ALL_IDENTITIES: process_remove_all_identities(e, 2); break; #ifdef ENABLE_PKCS11 case SSH_AGENTC_ADD_SMARTCARD_KEY: case SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED: process_add_smartcard_key(e); break; case SSH_AGENTC_REMOVE_SMARTCARD_KEY: process_remove_smartcard_key(e); break; #endif /* ENABLE_PKCS11 */ default: /* Unknown message. Respond with failure. */ error("Unknown message %d", type); sshbuf_reset(e->request); send_status(e, 0); break; } } static void new_socket(sock_type type, int fd) { u_int i, old_alloc, new_alloc; if (type == AUTH_CONNECTION) { debug("xcount %d -> %d", xcount, xcount + 1); ++xcount; } set_nonblock(fd); if (fd > max_fd) max_fd = fd; for (i = 0; i < sockets_alloc; i++) if (sockets[i].type == AUTH_UNUSED) { sockets[i].fd = fd; if ((sockets[i].input = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); if ((sockets[i].output = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); if ((sockets[i].request = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); sockets[i].type = type; return; } old_alloc = sockets_alloc; new_alloc = sockets_alloc + 10; sockets = xreallocarray(sockets, new_alloc, sizeof(sockets[0])); for (i = old_alloc; i < new_alloc; i++) sockets[i].type = AUTH_UNUSED; sockets_alloc = new_alloc; sockets[old_alloc].fd = fd; if ((sockets[old_alloc].input = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); if ((sockets[old_alloc].output = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); if ((sockets[old_alloc].request = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); sockets[old_alloc].type = type; } static int prepare_select(fd_set **fdrp, fd_set **fdwp, int *fdl, u_int *nallocp, struct timeval **tvpp) { u_int i, sz; int n = 0; static struct timeval tv; time_t deadline; for (i = 0; i < sockets_alloc; i++) { switch (sockets[i].type) { case AUTH_SOCKET: case AUTH_CONNECTION: n = MAXIMUM(n, sockets[i].fd); break; case AUTH_UNUSED: break; default: fatal("Unknown socket type %d", sockets[i].type); break; } } sz = howmany(n+1, NFDBITS) * sizeof(fd_mask); if (*fdrp == NULL || sz > *nallocp) { free(*fdrp); free(*fdwp); *fdrp = xmalloc(sz); *fdwp = xmalloc(sz); *nallocp = sz; } if (n < *fdl) debug("XXX shrink: %d < %d", n, *fdl); *fdl = n; memset(*fdrp, 0, sz); memset(*fdwp, 0, sz); for (i = 0; i < sockets_alloc; i++) { switch (sockets[i].type) { case AUTH_SOCKET: case AUTH_CONNECTION: FD_SET(sockets[i].fd, *fdrp); if (sshbuf_len(sockets[i].output) > 0) FD_SET(sockets[i].fd, *fdwp); break; default: break; } } deadline = reaper(); if (parent_alive_interval != 0) deadline = (deadline == 0) ? parent_alive_interval : MINIMUM(deadline, parent_alive_interval); if (deadline == 0) { *tvpp = NULL; } else { tv.tv_sec = deadline; tv.tv_usec = 0; *tvpp = &tv; } return (1); } static void after_select(fd_set *readset, fd_set *writeset) { struct sockaddr_un sunaddr; socklen_t slen; char buf[1024]; int len, sock, r; u_int i, orig_alloc; uid_t euid; gid_t egid; for (i = 0, orig_alloc = sockets_alloc; i < orig_alloc; i++) switch (sockets[i].type) { case AUTH_UNUSED: break; case AUTH_SOCKET: if (FD_ISSET(sockets[i].fd, readset)) { slen = sizeof(sunaddr); sock = accept(sockets[i].fd, (struct sockaddr *)&sunaddr, &slen); if (sock < 0) { error("accept from AUTH_SOCKET: %s", strerror(errno)); break; } if (getpeereid(sock, &euid, &egid) < 0) { error("getpeereid %d failed: %s", sock, strerror(errno)); close(sock); break; } if ((euid != 0) && (getuid() != euid)) { error("uid mismatch: " "peer euid %u != uid %u", (u_int) euid, (u_int) getuid()); close(sock); break; } new_socket(AUTH_CONNECTION, sock); } break; case AUTH_CONNECTION: if (sshbuf_len(sockets[i].output) > 0 && FD_ISSET(sockets[i].fd, writeset)) { len = write(sockets[i].fd, sshbuf_ptr(sockets[i].output), sshbuf_len(sockets[i].output)); if (len == -1 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)) continue; if (len <= 0) { close_socket(&sockets[i]); break; } if ((r = sshbuf_consume(sockets[i].output, len)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); } if (FD_ISSET(sockets[i].fd, readset)) { len = read(sockets[i].fd, buf, sizeof(buf)); if (len == -1 && (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)) continue; if (len <= 0) { close_socket(&sockets[i]); break; } if ((r = sshbuf_put(sockets[i].input, buf, len)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); explicit_bzero(buf, sizeof(buf)); process_message(&sockets[i]); } break; default: fatal("Unknown type %d", sockets[i].type); } } static void cleanup_socket(void) { if (cleanup_pid != 0 && getpid() != cleanup_pid) return; debug("%s: cleanup", __func__); if (socket_name[0]) unlink(socket_name); if (socket_dir[0]) rmdir(socket_dir); } void cleanup_exit(int i) { cleanup_socket(); _exit(i); } /*ARGSUSED*/ static void cleanup_handler(int sig) { cleanup_socket(); #ifdef ENABLE_PKCS11 pkcs11_terminate(); #endif _exit(2); } static void check_parent_exists(void) { /* * If our parent has exited then getppid() will return (pid_t)1, * so testing for that should be safe. */ if (parent_pid != -1 && getppid() != parent_pid) { /* printf("Parent has died - Authentication agent exiting.\n"); */ cleanup_socket(); _exit(2); } } static void usage(void) { fprintf(stderr, - "usage: ssh-agent [-c | -s] [-Dd] [-a bind_address] [-E fingerprint_hash]\n" + "usage: ssh-agent [-c | -s] [-Ddx] [-a bind_address] [-E fingerprint_hash]\n" " [-P pkcs11_whitelist] [-t life] [command [arg ...]]\n" " ssh-agent [-c | -s] -k\n"); - fprintf(stderr, " -x Exit when the last client disconnects.\n"); exit(1); } int main(int ac, char **av) { int c_flag = 0, d_flag = 0, D_flag = 0, k_flag = 0, s_flag = 0; int sock, fd, ch, result, saved_errno; u_int nalloc; char *shell, *format, *pidstr, *agentsocket = NULL; fd_set *readsetp = NULL, *writesetp = NULL; #ifdef HAVE_SETRLIMIT struct rlimit rlim; #endif extern int optind; extern char *optarg; pid_t pid; char pidstrbuf[1 + 3 * sizeof pid]; struct timeval *tvp = NULL; size_t len; mode_t prev_mask; ssh_malloc_init(); /* must be called before any mallocs */ /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); /* drop */ setegid(getgid()); setgid(getgid()); setuid(geteuid()); platform_disable_tracing(0); /* strict=no */ #ifdef WITH_OPENSSL OpenSSL_add_all_algorithms(); #endif __progname = ssh_get_progname(av[0]); seed_rng(); while ((ch = getopt(ac, av, "cDdksE:a:P:t:x")) != -1) { switch (ch) { case 'E': fingerprint_hash = ssh_digest_alg_by_name(optarg); if (fingerprint_hash == -1) fatal("Invalid hash algorithm \"%s\"", optarg); break; case 'c': if (s_flag) usage(); c_flag++; break; case 'k': k_flag++; break; case 'P': if (pkcs11_whitelist != NULL) fatal("-P option already specified"); pkcs11_whitelist = xstrdup(optarg); break; case 's': if (c_flag) usage(); s_flag++; break; case 'd': if (d_flag || D_flag) usage(); d_flag++; break; case 'D': if (d_flag || D_flag) usage(); D_flag++; break; case 'a': agentsocket = optarg; break; case 't': if ((lifetime = convtime(optarg)) == -1) { fprintf(stderr, "Invalid lifetime\n"); usage(); } break; case 'x': xcount = 0; break; default: usage(); } } ac -= optind; av += optind; if (ac > 0 && (c_flag || k_flag || s_flag || d_flag || D_flag)) usage(); if (pkcs11_whitelist == NULL) pkcs11_whitelist = xstrdup(DEFAULT_PKCS11_WHITELIST); if (ac == 0 && !c_flag && !s_flag) { shell = getenv("SHELL"); if (shell != NULL && (len = strlen(shell)) > 2 && strncmp(shell + len - 3, "csh", 3) == 0) c_flag = 1; } if (k_flag) { const char *errstr = NULL; pidstr = getenv(SSH_AGENTPID_ENV_NAME); if (pidstr == NULL) { fprintf(stderr, "%s not set, cannot kill agent\n", SSH_AGENTPID_ENV_NAME); exit(1); } pid = (int)strtonum(pidstr, 2, INT_MAX, &errstr); if (errstr) { fprintf(stderr, "%s=\"%s\", which is not a good PID: %s\n", SSH_AGENTPID_ENV_NAME, pidstr, errstr); exit(1); } if (kill(pid, SIGTERM) == -1) { perror("kill"); exit(1); } format = c_flag ? "unsetenv %s;\n" : "unset %s;\n"; printf(format, SSH_AUTHSOCKET_ENV_NAME); printf(format, SSH_AGENTPID_ENV_NAME); printf("echo Agent pid %ld killed;\n", (long)pid); exit(0); } parent_pid = getpid(); if (agentsocket == NULL) { /* Create private directory for agent socket */ mktemp_proto(socket_dir, sizeof(socket_dir)); if (mkdtemp(socket_dir) == NULL) { perror("mkdtemp: private socket dir"); exit(1); } snprintf(socket_name, sizeof socket_name, "%s/agent.%ld", socket_dir, (long)parent_pid); } else { /* Try to use specified agent socket */ socket_dir[0] = '\0'; strlcpy(socket_name, agentsocket, sizeof socket_name); } /* * Create socket early so it will exist before command gets run from * the parent. */ prev_mask = umask(0177); sock = unix_listener(socket_name, SSH_LISTEN_BACKLOG, 0); if (sock < 0) { /* XXX - unix_listener() calls error() not perror() */ *socket_name = '\0'; /* Don't unlink any existing file */ cleanup_exit(1); } umask(prev_mask); /* * Fork, and have the parent execute the command, if any, or present * the socket data. The child continues as the authentication agent. */ if (D_flag || d_flag) { log_init(__progname, d_flag ? SYSLOG_LEVEL_DEBUG3 : SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_AUTH, 1); format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, SSH_AUTHSOCKET_ENV_NAME); printf("echo Agent pid %ld;\n", (long)parent_pid); fflush(stdout); goto skip; } pid = fork(); if (pid == -1) { perror("fork"); cleanup_exit(1); } if (pid != 0) { /* Parent - execute the given command. */ close(sock); snprintf(pidstrbuf, sizeof pidstrbuf, "%ld", (long)pid); if (ac == 0) { format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n"; printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name, SSH_AUTHSOCKET_ENV_NAME); printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf, SSH_AGENTPID_ENV_NAME); printf("echo Agent pid %ld;\n", (long)pid); exit(0); } if (setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1) == -1 || setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1) == -1) { perror("setenv"); exit(1); } execvp(av[0], av); perror(av[0]); exit(1); } /* child */ log_init(__progname, SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_AUTH, 0); if (setsid() == -1) { error("setsid: %s", strerror(errno)); cleanup_exit(1); } (void)chdir("/"); if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { /* XXX might close listen socket */ (void)dup2(fd, STDIN_FILENO); (void)dup2(fd, STDOUT_FILENO); (void)dup2(fd, STDERR_FILENO); if (fd > 2) close(fd); } #ifdef HAVE_SETRLIMIT /* deny core dumps, since memory contains unencrypted private keys */ rlim.rlim_cur = rlim.rlim_max = 0; if (setrlimit(RLIMIT_CORE, &rlim) < 0) { error("setrlimit RLIMIT_CORE: %s", strerror(errno)); cleanup_exit(1); } #endif skip: cleanup_pid = getpid(); #ifdef ENABLE_PKCS11 pkcs11_init(0); #endif new_socket(AUTH_SOCKET, sock); if (ac > 0) parent_alive_interval = 10; idtab_init(); signal(SIGPIPE, SIG_IGN); signal(SIGINT, (d_flag | D_flag) ? cleanup_handler : SIG_IGN); signal(SIGHUP, cleanup_handler); signal(SIGTERM, cleanup_handler); nalloc = 0; if (pledge("stdio rpath cpath unix id proc exec", NULL) == -1) fatal("%s: pledge: %s", __progname, strerror(errno)); platform_pledge_agent(); while (1) { prepare_select(&readsetp, &writesetp, &max_fd, &nalloc, &tvp); result = select(max_fd + 1, readsetp, writesetp, NULL, tvp); saved_errno = errno; if (parent_alive_interval != 0) check_parent_exists(); (void) reaper(); /* remove expired keys */ if (result < 0) { if (saved_errno == EINTR) continue; fatal("select: %s", strerror(saved_errno)); } else if (result > 0) after_select(readsetp, writesetp); } /* NOTREACHED */ } diff --git a/crypto/openssh/ssh-keygen.c b/crypto/openssh/ssh-keygen.c index 2a7939bfc6c0..f17af036bbfa 100644 --- a/crypto/openssh/ssh-keygen.c +++ b/crypto/openssh/ssh-keygen.c @@ -1,2755 +1,2769 @@ -/* $OpenBSD: ssh-keygen.c,v 1.292 2016/09/12 03:29:16 dtucker Exp $ */ +/* $OpenBSD: ssh-keygen.c,v 1.299 2017/03/10 04:26:06 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland * All rights reserved * Identity and host key generation and maintenance. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #include "includes.h" #include #include #include #ifdef WITH_OPENSSL #include #include #include "openbsd-compat/openssl-compat.h" #endif #include #include #include #ifdef HAVE_PATHS_H # include #endif #include #include #include #include #include #include #include +#include #include "xmalloc.h" #include "sshkey.h" #include "rsa.h" #include "authfile.h" #include "uuencode.h" #include "sshbuf.h" #include "pathnames.h" #include "log.h" #include "misc.h" #include "match.h" #include "hostfile.h" #include "dns.h" #include "ssh.h" #include "ssh2.h" #include "ssherr.h" #include "ssh-pkcs11.h" #include "atomicio.h" #include "krl.h" #include "digest.h" +#include "utf8.h" #ifdef WITH_OPENSSL # define DEFAULT_KEY_TYPE_NAME "rsa" #else # define DEFAULT_KEY_TYPE_NAME "ed25519" #endif /* Number of bits in the RSA/DSA key. This value can be set on the command line. */ #define DEFAULT_BITS 2048 #define DEFAULT_BITS_DSA 1024 #define DEFAULT_BITS_ECDSA 256 u_int32_t bits = 0; /* * Flag indicating that we just want to change the passphrase. This can be * set on the command line. */ int change_passphrase = 0; /* * Flag indicating that we just want to change the comment. This can be set * on the command line. */ int change_comment = 0; int quiet = 0; int log_level = SYSLOG_LEVEL_INFO; /* Flag indicating that we want to hash a known_hosts file */ int hash_hosts = 0; /* Flag indicating that we want lookup a host in known_hosts file */ int find_host = 0; /* Flag indicating that we want to delete a host from a known_hosts file */ int delete_host = 0; /* Flag indicating that we want to show the contents of a certificate */ int show_cert = 0; /* Flag indicating that we just want to see the key fingerprint */ int print_fingerprint = 0; int print_bubblebabble = 0; /* Hash algorithm to use for fingerprints. */ int fingerprint_hash = SSH_FP_HASH_DEFAULT; /* The identity file name, given on the command line or entered by the user. */ char identity_file[1024]; int have_identity = 0; /* This is set to the passphrase if given on the command line. */ char *identity_passphrase = NULL; /* This is set to the new passphrase if given on the command line. */ char *identity_new_passphrase = NULL; /* This is set to the new comment if given on the command line. */ char *identity_comment = NULL; /* Path to CA key when certifying keys. */ char *ca_key_path = NULL; /* Certificate serial number */ unsigned long long cert_serial = 0; /* Key type when certifying */ u_int cert_key_type = SSH2_CERT_TYPE_USER; /* "key ID" of signed key */ char *cert_key_id = NULL; /* Comma-separated list of principal names for certifying keys */ char *cert_principals = NULL; /* Validity period for certificates */ u_int64_t cert_valid_from = 0; u_int64_t cert_valid_to = ~0ULL; /* Certificate options */ #define CERTOPT_X_FWD (1) #define CERTOPT_AGENT_FWD (1<<1) #define CERTOPT_PORT_FWD (1<<2) #define CERTOPT_PTY (1<<3) #define CERTOPT_USER_RC (1<<4) #define CERTOPT_DEFAULT (CERTOPT_X_FWD|CERTOPT_AGENT_FWD| \ CERTOPT_PORT_FWD|CERTOPT_PTY|CERTOPT_USER_RC) u_int32_t certflags_flags = CERTOPT_DEFAULT; char *certflags_command = NULL; char *certflags_src_addr = NULL; /* Conversion to/from various formats */ int convert_to = 0; int convert_from = 0; enum { FMT_RFC4716, FMT_PKCS8, FMT_PEM } convert_format = FMT_RFC4716; int print_public = 0; int print_generic = 0; char *key_type_name = NULL; /* Load key from this PKCS#11 provider */ char *pkcs11provider = NULL; /* Use new OpenSSH private key format when writing SSH2 keys instead of PEM */ int use_new_format = 0; /* Cipher for new-format private keys */ char *new_format_cipher = NULL; /* * Number of KDF rounds to derive new format keys / * number of primality trials when screening moduli. */ int rounds = 0; /* argv0 */ extern char *__progname; char hostname[NI_MAXHOST]; #ifdef WITH_OPENSSL /* moduli.c */ int gen_candidates(FILE *, u_int32_t, u_int32_t, BIGNUM *); int prime_test(FILE *, FILE *, u_int32_t, u_int32_t, char *, unsigned long, unsigned long); #endif static void type_bits_valid(int type, const char *name, u_int32_t *bitsp) { #ifdef WITH_OPENSSL u_int maxbits, nid; #endif if (type == KEY_UNSPEC) fatal("unknown key type %s", key_type_name); if (*bitsp == 0) { #ifdef WITH_OPENSSL if (type == KEY_DSA) *bitsp = DEFAULT_BITS_DSA; else if (type == KEY_ECDSA) { if (name != NULL && (nid = sshkey_ecdsa_nid_from_name(name)) > 0) *bitsp = sshkey_curve_nid_to_bits(nid); if (*bitsp == 0) *bitsp = DEFAULT_BITS_ECDSA; } else #endif *bitsp = DEFAULT_BITS; } #ifdef WITH_OPENSSL maxbits = (type == KEY_DSA) ? OPENSSL_DSA_MAX_MODULUS_BITS : OPENSSL_RSA_MAX_MODULUS_BITS; if (*bitsp > maxbits) fatal("key bits exceeds maximum %d", maxbits); if (type == KEY_DSA && *bitsp != 1024) fatal("DSA keys must be 1024 bits"); else if (type != KEY_ECDSA && type != KEY_ED25519 && *bitsp < 1024) fatal("Key must at least be 1024 bits"); else if (type == KEY_ECDSA && sshkey_ecdsa_bits_to_nid(*bitsp) == -1) fatal("Invalid ECDSA key length - valid lengths are " "256, 384 or 521 bits"); #endif } static void ask_filename(struct passwd *pw, const char *prompt) { char buf[1024]; char *name = NULL; if (key_type_name == NULL) name = _PATH_SSH_CLIENT_ID_RSA; else { switch (sshkey_type_from_name(key_type_name)) { case KEY_RSA1: name = _PATH_SSH_CLIENT_IDENTITY; break; case KEY_DSA_CERT: case KEY_DSA: name = _PATH_SSH_CLIENT_ID_DSA; break; #ifdef OPENSSL_HAS_ECC case KEY_ECDSA_CERT: case KEY_ECDSA: name = _PATH_SSH_CLIENT_ID_ECDSA; break; #endif case KEY_RSA_CERT: case KEY_RSA: name = _PATH_SSH_CLIENT_ID_RSA; break; case KEY_ED25519: case KEY_ED25519_CERT: name = _PATH_SSH_CLIENT_ID_ED25519; break; default: fatal("bad key type"); } } snprintf(identity_file, sizeof(identity_file), "%s/%s", pw->pw_dir, name); printf("%s (%s): ", prompt, identity_file); fflush(stdout); if (fgets(buf, sizeof(buf), stdin) == NULL) exit(1); buf[strcspn(buf, "\n")] = '\0'; if (strcmp(buf, "") != 0) strlcpy(identity_file, buf, sizeof(identity_file)); have_identity = 1; } static struct sshkey * load_identity(char *filename) { char *pass; struct sshkey *prv; int r; if ((r = sshkey_load_private(filename, "", &prv, NULL)) == 0) return prv; if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) fatal("Load key \"%s\": %s", filename, ssh_err(r)); if (identity_passphrase) pass = xstrdup(identity_passphrase); else pass = read_passphrase("Enter passphrase: ", RP_ALLOW_STDIN); r = sshkey_load_private(filename, pass, &prv, NULL); explicit_bzero(pass, strlen(pass)); free(pass); if (r != 0) fatal("Load key \"%s\": %s", filename, ssh_err(r)); return prv; } #define SSH_COM_PUBLIC_BEGIN "---- BEGIN SSH2 PUBLIC KEY ----" #define SSH_COM_PUBLIC_END "---- END SSH2 PUBLIC KEY ----" #define SSH_COM_PRIVATE_BEGIN "---- BEGIN SSH2 ENCRYPTED PRIVATE KEY ----" #define SSH_COM_PRIVATE_KEY_MAGIC 0x3f6ff9eb #ifdef WITH_OPENSSL static void do_convert_to_ssh2(struct passwd *pw, struct sshkey *k) { size_t len; u_char *blob; char comment[61]; int r; if (k->type == KEY_RSA1) fatal("version 1 keys are not supported"); if ((r = sshkey_to_blob(k, &blob, &len)) != 0) fatal("key_to_blob failed: %s", ssh_err(r)); /* Comment + surrounds must fit into 72 chars (RFC 4716 sec 3.3) */ snprintf(comment, sizeof(comment), "%u-bit %s, converted by %s@%s from OpenSSH", sshkey_size(k), sshkey_type(k), pw->pw_name, hostname); fprintf(stdout, "%s\n", SSH_COM_PUBLIC_BEGIN); fprintf(stdout, "Comment: \"%s\"\n", comment); dump_base64(stdout, blob, len); fprintf(stdout, "%s\n", SSH_COM_PUBLIC_END); sshkey_free(k); free(blob); exit(0); } static void do_convert_to_pkcs8(struct sshkey *k) { switch (sshkey_type_plain(k->type)) { case KEY_RSA1: case KEY_RSA: if (!PEM_write_RSA_PUBKEY(stdout, k->rsa)) fatal("PEM_write_RSA_PUBKEY failed"); break; case KEY_DSA: if (!PEM_write_DSA_PUBKEY(stdout, k->dsa)) fatal("PEM_write_DSA_PUBKEY failed"); break; #ifdef OPENSSL_HAS_ECC case KEY_ECDSA: if (!PEM_write_EC_PUBKEY(stdout, k->ecdsa)) fatal("PEM_write_EC_PUBKEY failed"); break; #endif default: fatal("%s: unsupported key type %s", __func__, sshkey_type(k)); } exit(0); } static void do_convert_to_pem(struct sshkey *k) { switch (sshkey_type_plain(k->type)) { case KEY_RSA1: case KEY_RSA: if (!PEM_write_RSAPublicKey(stdout, k->rsa)) fatal("PEM_write_RSAPublicKey failed"); break; #if notyet /* OpenSSH 0.9.8 lacks this function */ case KEY_DSA: if (!PEM_write_DSAPublicKey(stdout, k->dsa)) fatal("PEM_write_DSAPublicKey failed"); break; #endif /* XXX ECDSA? */ default: fatal("%s: unsupported key type %s", __func__, sshkey_type(k)); } exit(0); } static void do_convert_to(struct passwd *pw) { struct sshkey *k; struct stat st; int r; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); if (stat(identity_file, &st) < 0) fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); if ((r = sshkey_load_public(identity_file, &k, NULL)) != 0) k = load_identity(identity_file); switch (convert_format) { case FMT_RFC4716: do_convert_to_ssh2(pw, k); break; case FMT_PKCS8: do_convert_to_pkcs8(k); break; case FMT_PEM: do_convert_to_pem(k); break; default: fatal("%s: unknown key format %d", __func__, convert_format); } exit(0); } /* * This is almost exactly the bignum1 encoding, but with 32 bit for length * instead of 16. */ static void buffer_get_bignum_bits(struct sshbuf *b, BIGNUM *value) { u_int bytes, bignum_bits; int r; if ((r = sshbuf_get_u32(b, &bignum_bits)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); bytes = (bignum_bits + 7) / 8; if (sshbuf_len(b) < bytes) fatal("%s: input buffer too small: need %d have %zu", __func__, bytes, sshbuf_len(b)); if (BN_bin2bn(sshbuf_ptr(b), bytes, value) == NULL) fatal("%s: BN_bin2bn failed", __func__); if ((r = sshbuf_consume(b, bytes)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); } static struct sshkey * do_convert_private_ssh2_from_blob(u_char *blob, u_int blen) { struct sshbuf *b; struct sshkey *key = NULL; char *type, *cipher; u_char e1, e2, e3, *sig = NULL, data[] = "abcde12345"; int r, rlen, ktype; u_int magic, i1, i2, i3, i4; size_t slen; u_long e; if ((b = sshbuf_from(blob, blen)) == NULL) fatal("%s: sshbuf_from failed", __func__); if ((r = sshbuf_get_u32(b, &magic)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (magic != SSH_COM_PRIVATE_KEY_MAGIC) { error("bad magic 0x%x != 0x%x", magic, SSH_COM_PRIVATE_KEY_MAGIC); sshbuf_free(b); return NULL; } if ((r = sshbuf_get_u32(b, &i1)) != 0 || (r = sshbuf_get_cstring(b, &type, NULL)) != 0 || (r = sshbuf_get_cstring(b, &cipher, NULL)) != 0 || (r = sshbuf_get_u32(b, &i2)) != 0 || (r = sshbuf_get_u32(b, &i3)) != 0 || (r = sshbuf_get_u32(b, &i4)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); debug("ignore (%d %d %d %d)", i1, i2, i3, i4); if (strcmp(cipher, "none") != 0) { error("unsupported cipher %s", cipher); free(cipher); sshbuf_free(b); free(type); return NULL; } free(cipher); if (strstr(type, "dsa")) { ktype = KEY_DSA; } else if (strstr(type, "rsa")) { ktype = KEY_RSA; } else { sshbuf_free(b); free(type); return NULL; } if ((key = sshkey_new_private(ktype)) == NULL) fatal("key_new_private failed"); free(type); switch (key->type) { case KEY_DSA: buffer_get_bignum_bits(b, key->dsa->p); buffer_get_bignum_bits(b, key->dsa->g); buffer_get_bignum_bits(b, key->dsa->q); buffer_get_bignum_bits(b, key->dsa->pub_key); buffer_get_bignum_bits(b, key->dsa->priv_key); break; case KEY_RSA: if ((r = sshbuf_get_u8(b, &e1)) != 0 || (e1 < 30 && (r = sshbuf_get_u8(b, &e2)) != 0) || (e1 < 30 && (r = sshbuf_get_u8(b, &e3)) != 0)) fatal("%s: buffer error: %s", __func__, ssh_err(r)); e = e1; debug("e %lx", e); if (e < 30) { e <<= 8; e += e2; debug("e %lx", e); e <<= 8; e += e3; debug("e %lx", e); } if (!BN_set_word(key->rsa->e, e)) { sshbuf_free(b); sshkey_free(key); return NULL; } buffer_get_bignum_bits(b, key->rsa->d); buffer_get_bignum_bits(b, key->rsa->n); buffer_get_bignum_bits(b, key->rsa->iqmp); buffer_get_bignum_bits(b, key->rsa->q); buffer_get_bignum_bits(b, key->rsa->p); if ((r = rsa_generate_additional_parameters(key->rsa)) != 0) fatal("generate RSA parameters failed: %s", ssh_err(r)); break; } rlen = sshbuf_len(b); if (rlen != 0) error("do_convert_private_ssh2_from_blob: " "remaining bytes in key blob %d", rlen); sshbuf_free(b); /* try the key */ if (sshkey_sign(key, &sig, &slen, data, sizeof(data), NULL, 0) != 0 || sshkey_verify(key, sig, slen, data, sizeof(data), 0) != 0) { sshkey_free(key); free(sig); return NULL; } free(sig); return key; } static int get_line(FILE *fp, char *line, size_t len) { int c; size_t pos = 0; line[0] = '\0'; while ((c = fgetc(fp)) != EOF) { if (pos >= len - 1) fatal("input line too long."); switch (c) { case '\r': c = fgetc(fp); if (c != EOF && c != '\n' && ungetc(c, fp) == EOF) fatal("unget: %s", strerror(errno)); return pos; case '\n': return pos; } line[pos++] = c; line[pos] = '\0'; } /* We reached EOF */ return -1; } static void do_convert_from_ssh2(struct passwd *pw, struct sshkey **k, int *private) { int r, blen, escaped = 0; u_int len; char line[1024]; u_char blob[8096]; char encoded[8096]; FILE *fp; if ((fp = fopen(identity_file, "r")) == NULL) fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); encoded[0] = '\0'; while ((blen = get_line(fp, line, sizeof(line))) != -1) { if (blen > 0 && line[blen - 1] == '\\') escaped++; if (strncmp(line, "----", 4) == 0 || strstr(line, ": ") != NULL) { if (strstr(line, SSH_COM_PRIVATE_BEGIN) != NULL) *private = 1; if (strstr(line, " END ") != NULL) { break; } /* fprintf(stderr, "ignore: %s", line); */ continue; } if (escaped) { escaped--; /* fprintf(stderr, "escaped: %s", line); */ continue; } strlcat(encoded, line, sizeof(encoded)); } len = strlen(encoded); if (((len % 4) == 3) && (encoded[len-1] == '=') && (encoded[len-2] == '=') && (encoded[len-3] == '=')) encoded[len-3] = '\0'; blen = uudecode(encoded, blob, sizeof(blob)); if (blen < 0) fatal("uudecode failed."); if (*private) *k = do_convert_private_ssh2_from_blob(blob, blen); else if ((r = sshkey_from_blob(blob, blen, k)) != 0) fatal("decode blob failed: %s", ssh_err(r)); fclose(fp); } static void do_convert_from_pkcs8(struct sshkey **k, int *private) { EVP_PKEY *pubkey; FILE *fp; if ((fp = fopen(identity_file, "r")) == NULL) fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); if ((pubkey = PEM_read_PUBKEY(fp, NULL, NULL, NULL)) == NULL) { fatal("%s: %s is not a recognised public key format", __func__, identity_file); } fclose(fp); switch (EVP_PKEY_type(pubkey->type)) { case EVP_PKEY_RSA: if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) fatal("sshkey_new failed"); (*k)->type = KEY_RSA; (*k)->rsa = EVP_PKEY_get1_RSA(pubkey); break; case EVP_PKEY_DSA: if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) fatal("sshkey_new failed"); (*k)->type = KEY_DSA; (*k)->dsa = EVP_PKEY_get1_DSA(pubkey); break; #ifdef OPENSSL_HAS_ECC case EVP_PKEY_EC: if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) fatal("sshkey_new failed"); (*k)->type = KEY_ECDSA; (*k)->ecdsa = EVP_PKEY_get1_EC_KEY(pubkey); (*k)->ecdsa_nid = sshkey_ecdsa_key_to_nid((*k)->ecdsa); break; #endif default: fatal("%s: unsupported pubkey type %d", __func__, EVP_PKEY_type(pubkey->type)); } EVP_PKEY_free(pubkey); return; } static void do_convert_from_pem(struct sshkey **k, int *private) { FILE *fp; RSA *rsa; #ifdef notyet DSA *dsa; #endif if ((fp = fopen(identity_file, "r")) == NULL) fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); if ((rsa = PEM_read_RSAPublicKey(fp, NULL, NULL, NULL)) != NULL) { if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) fatal("sshkey_new failed"); (*k)->type = KEY_RSA; (*k)->rsa = rsa; fclose(fp); return; } #if notyet /* OpenSSH 0.9.8 lacks this function */ rewind(fp); if ((dsa = PEM_read_DSAPublicKey(fp, NULL, NULL, NULL)) != NULL) { if ((*k = sshkey_new(KEY_UNSPEC)) == NULL) fatal("sshkey_new failed"); (*k)->type = KEY_DSA; (*k)->dsa = dsa; fclose(fp); return; } /* XXX ECDSA */ #endif fatal("%s: unrecognised raw private key format", __func__); } static void do_convert_from(struct passwd *pw) { struct sshkey *k = NULL; int r, private = 0, ok = 0; struct stat st; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); if (stat(identity_file, &st) < 0) fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); switch (convert_format) { case FMT_RFC4716: do_convert_from_ssh2(pw, &k, &private); break; case FMT_PKCS8: do_convert_from_pkcs8(&k, &private); break; case FMT_PEM: do_convert_from_pem(&k, &private); break; default: fatal("%s: unknown key format %d", __func__, convert_format); } if (!private) { if ((r = sshkey_write(k, stdout)) == 0) ok = 1; if (ok) fprintf(stdout, "\n"); } else { switch (k->type) { case KEY_DSA: ok = PEM_write_DSAPrivateKey(stdout, k->dsa, NULL, NULL, 0, NULL, NULL); break; #ifdef OPENSSL_HAS_ECC case KEY_ECDSA: ok = PEM_write_ECPrivateKey(stdout, k->ecdsa, NULL, NULL, 0, NULL, NULL); break; #endif case KEY_RSA: ok = PEM_write_RSAPrivateKey(stdout, k->rsa, NULL, NULL, 0, NULL, NULL); break; default: fatal("%s: unsupported key type %s", __func__, sshkey_type(k)); } } if (!ok) fatal("key write failed"); sshkey_free(k); exit(0); } #endif static void do_print_public(struct passwd *pw) { struct sshkey *prv; struct stat st; int r; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); if (stat(identity_file, &st) < 0) fatal("%s: %s", identity_file, strerror(errno)); prv = load_identity(identity_file); if ((r = sshkey_write(prv, stdout)) != 0) error("key_write failed: %s", ssh_err(r)); sshkey_free(prv); fprintf(stdout, "\n"); exit(0); } static void do_download(struct passwd *pw) { #ifdef ENABLE_PKCS11 struct sshkey **keys = NULL; int i, nkeys; enum sshkey_fp_rep rep; int fptype; char *fp, *ra; fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash; rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT; pkcs11_init(0); nkeys = pkcs11_add_provider(pkcs11provider, NULL, &keys); if (nkeys <= 0) fatal("cannot read public key from pkcs11"); for (i = 0; i < nkeys; i++) { if (print_fingerprint) { fp = sshkey_fingerprint(keys[i], fptype, rep); ra = sshkey_fingerprint(keys[i], fingerprint_hash, SSH_FP_RANDOMART); if (fp == NULL || ra == NULL) fatal("%s: sshkey_fingerprint fail", __func__); printf("%u %s %s (PKCS11 key)\n", sshkey_size(keys[i]), fp, sshkey_type(keys[i])); if (log_level >= SYSLOG_LEVEL_VERBOSE) printf("%s\n", ra); free(ra); free(fp); } else { (void) sshkey_write(keys[i], stdout); /* XXX check */ fprintf(stdout, "\n"); } sshkey_free(keys[i]); } free(keys); pkcs11_terminate(); exit(0); #else fatal("no pkcs11 support"); #endif /* ENABLE_PKCS11 */ } static struct sshkey * try_read_key(char **cpp) { struct sshkey *ret; int r; if ((ret = sshkey_new(KEY_RSA1)) == NULL) fatal("sshkey_new failed"); /* Try RSA1 */ if ((r = sshkey_read(ret, cpp)) == 0) return ret; /* Try modern */ sshkey_free(ret); if ((ret = sshkey_new(KEY_UNSPEC)) == NULL) fatal("sshkey_new failed"); if ((r = sshkey_read(ret, cpp)) == 0) return ret; /* Not a key */ sshkey_free(ret); return NULL; } static void fingerprint_one_key(const struct sshkey *public, const char *comment) { char *fp = NULL, *ra = NULL; enum sshkey_fp_rep rep; int fptype; fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash; rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT; fp = sshkey_fingerprint(public, fptype, rep); ra = sshkey_fingerprint(public, fingerprint_hash, SSH_FP_RANDOMART); if (fp == NULL || ra == NULL) fatal("%s: sshkey_fingerprint failed", __func__); - printf("%u %s %s (%s)\n", sshkey_size(public), fp, + mprintf("%u %s %s (%s)\n", sshkey_size(public), fp, comment ? comment : "no comment", sshkey_type(public)); if (log_level >= SYSLOG_LEVEL_VERBOSE) printf("%s\n", ra); free(ra); free(fp); } static void fingerprint_private(const char *path) { struct stat st; char *comment = NULL; struct sshkey *public = NULL; int r; if (stat(identity_file, &st) < 0) fatal("%s: %s", path, strerror(errno)); if ((r = sshkey_load_public(path, &public, &comment)) != 0) { debug("load public \"%s\": %s", path, ssh_err(r)); if ((r = sshkey_load_private(path, NULL, &public, &comment)) != 0) { debug("load private \"%s\": %s", path, ssh_err(r)); fatal("%s is not a key file.", path); } } fingerprint_one_key(public, comment); sshkey_free(public); free(comment); } static void do_fingerprint(struct passwd *pw) { FILE *f; struct sshkey *public = NULL; char *comment = NULL, *cp, *ep, line[SSH_MAX_PUBKEY_BYTES]; int i, invalid = 1; const char *path; u_long lnum = 0; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); path = identity_file; if (strcmp(identity_file, "-") == 0) { f = stdin; path = "(stdin)"; } else if ((f = fopen(path, "r")) == NULL) fatal("%s: %s: %s", __progname, path, strerror(errno)); while (read_keyfile_line(f, path, line, sizeof(line), &lnum) == 0) { cp = line; cp[strcspn(cp, "\n")] = '\0'; /* Trim leading space and comments */ cp = line + strspn(line, " \t"); if (*cp == '#' || *cp == '\0') continue; /* * Input may be plain keys, private keys, authorized_keys * or known_hosts. */ /* * Try private keys first. Assume a key is private if * "SSH PRIVATE KEY" appears on the first line and we're * not reading from stdin (XXX support private keys on stdin). */ if (lnum == 1 && strcmp(identity_file, "-") != 0 && strstr(cp, "PRIVATE KEY") != NULL) { fclose(f); fingerprint_private(path); exit(0); } /* * If it's not a private key, then this must be prepared to * accept a public key prefixed with a hostname or options. * Try a bare key first, otherwise skip the leading stuff. */ if ((public = try_read_key(&cp)) == NULL) { i = strtol(cp, &ep, 10); if (i == 0 || ep == NULL || (*ep != ' ' && *ep != '\t')) { int quoted = 0; comment = cp; for (; *cp && (quoted || (*cp != ' ' && *cp != '\t')); cp++) { if (*cp == '\\' && cp[1] == '"') cp++; /* Skip both */ else if (*cp == '"') quoted = !quoted; } if (!*cp) continue; *cp++ = '\0'; } } /* Retry after parsing leading hostname/key options */ if (public == NULL && (public = try_read_key(&cp)) == NULL) { debug("%s:%lu: not a public key", path, lnum); continue; } /* Find trailing comment, if any */ for (; *cp == ' ' || *cp == '\t'; cp++) ; if (*cp != '\0' && *cp != '#') comment = cp; fingerprint_one_key(public, comment); sshkey_free(public); invalid = 0; /* One good key in the file is sufficient */ } fclose(f); if (invalid) fatal("%s is not a public key file.", path); exit(0); } static void do_gen_all_hostkeys(struct passwd *pw) { struct { char *key_type; char *key_type_display; char *path; } key_types[] = { #ifdef WITH_OPENSSL #ifdef WITH_SSH1 { "rsa1", "RSA1", _PATH_HOST_KEY_FILE }, #endif /* WITH_SSH1 */ { "rsa", "RSA" ,_PATH_HOST_RSA_KEY_FILE }, { "dsa", "DSA", _PATH_HOST_DSA_KEY_FILE }, #ifdef OPENSSL_HAS_ECC { "ecdsa", "ECDSA",_PATH_HOST_ECDSA_KEY_FILE }, #endif /* OPENSSL_HAS_ECC */ #endif /* WITH_OPENSSL */ { "ed25519", "ED25519",_PATH_HOST_ED25519_KEY_FILE }, { NULL, NULL, NULL } }; int first = 0; struct stat st; struct sshkey *private, *public; char comment[1024]; int i, type, fd, r; FILE *f; for (i = 0; key_types[i].key_type; i++) { if (stat(key_types[i].path, &st) == 0) continue; if (errno != ENOENT) { error("Could not stat %s: %s", key_types[i].path, strerror(errno)); first = 0; continue; } if (first == 0) { first = 1; printf("%s: generating new host keys: ", __progname); } printf("%s ", key_types[i].key_type_display); fflush(stdout); type = sshkey_type_from_name(key_types[i].key_type); strlcpy(identity_file, key_types[i].path, sizeof(identity_file)); bits = 0; type_bits_valid(type, NULL, &bits); if ((r = sshkey_generate(type, bits, &private)) != 0) { error("key_generate failed: %s", ssh_err(r)); first = 0; continue; } if ((r = sshkey_from_private(private, &public)) != 0) fatal("sshkey_from_private failed: %s", ssh_err(r)); snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); if ((r = sshkey_save_private(private, identity_file, "", comment, use_new_format, new_format_cipher, rounds)) != 0) { error("Saving key \"%s\" failed: %s", identity_file, ssh_err(r)); sshkey_free(private); sshkey_free(public); first = 0; continue; } sshkey_free(private); strlcat(identity_file, ".pub", sizeof(identity_file)); fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd == -1) { error("Could not save your public key in %s", identity_file); sshkey_free(public); first = 0; continue; } f = fdopen(fd, "w"); if (f == NULL) { error("fdopen %s failed", identity_file); close(fd); sshkey_free(public); first = 0; continue; } if ((r = sshkey_write(public, f)) != 0) { error("write key failed: %s", ssh_err(r)); fclose(f); sshkey_free(public); first = 0; continue; } fprintf(f, " %s\n", comment); fclose(f); sshkey_free(public); } if (first != 0) printf("\n"); } struct known_hosts_ctx { const char *host; /* Hostname searched for in find/delete case */ FILE *out; /* Output file, stdout for find_hosts case */ int has_unhashed; /* When hashing, original had unhashed hosts */ int found_key; /* For find/delete, host was found */ int invalid; /* File contained invalid items; don't delete */ }; static int known_hosts_hash(struct hostkey_foreach_line *l, void *_ctx) { struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx; char *hashed, *cp, *hosts, *ohosts; int has_wild = l->hosts && strcspn(l->hosts, "*?!") != strlen(l->hosts); + int was_hashed = l->hosts && l->hosts[0] == HASH_DELIM; switch (l->status) { case HKF_STATUS_OK: case HKF_STATUS_MATCHED: /* * Don't hash hosts already already hashed, with wildcard * characters or a CA/revocation marker. */ - if ((l->match & HKF_MATCH_HOST_HASHED) != 0 || - has_wild || l->marker != MRK_NONE) { + if (was_hashed || has_wild || l->marker != MRK_NONE) { fprintf(ctx->out, "%s\n", l->line); if (has_wild && !find_host) { - logit("%s:%ld: ignoring host name " + logit("%s:%lu: ignoring host name " "with wildcard: %.64s", l->path, l->linenum, l->hosts); } return 0; } /* * Split any comma-separated hostnames from the host list, * hash and store separately. */ ohosts = hosts = xstrdup(l->hosts); while ((cp = strsep(&hosts, ",")) != NULL && *cp != '\0') { + lowercase(cp); if ((hashed = host_hash(cp, NULL, 0)) == NULL) fatal("hash_host failed"); fprintf(ctx->out, "%s %s\n", hashed, l->rawkey); ctx->has_unhashed = 1; } free(ohosts); return 0; case HKF_STATUS_INVALID: /* Retain invalid lines, but mark file as invalid. */ ctx->invalid = 1; - logit("%s:%ld: invalid line", l->path, l->linenum); + logit("%s:%lu: invalid line", l->path, l->linenum); /* FALLTHROUGH */ default: fprintf(ctx->out, "%s\n", l->line); return 0; } /* NOTREACHED */ return -1; } static int known_hosts_find_delete(struct hostkey_foreach_line *l, void *_ctx) { struct known_hosts_ctx *ctx = (struct known_hosts_ctx *)_ctx; enum sshkey_fp_rep rep; int fptype; char *fp; fptype = print_bubblebabble ? SSH_DIGEST_SHA1 : fingerprint_hash; rep = print_bubblebabble ? SSH_FP_BUBBLEBABBLE : SSH_FP_DEFAULT; if (l->status == HKF_STATUS_MATCHED) { if (delete_host) { if (l->marker != MRK_NONE) { /* Don't remove CA and revocation lines */ fprintf(ctx->out, "%s\n", l->line); } else { /* * Hostname matches and has no CA/revoke * marker, delete it by *not* writing the * line to ctx->out. */ ctx->found_key = 1; if (!quiet) - printf("# Host %s found: line %ld\n", + printf("# Host %s found: line %lu\n", ctx->host, l->linenum); } return 0; } else if (find_host) { ctx->found_key = 1; if (!quiet) { - printf("# Host %s found: line %ld %s\n", + printf("# Host %s found: line %lu %s\n", ctx->host, l->linenum, l->marker == MRK_CA ? "CA" : (l->marker == MRK_REVOKE ? "REVOKED" : "")); } if (hash_hosts) known_hosts_hash(l, ctx); else if (print_fingerprint) { fp = sshkey_fingerprint(l->key, fptype, rep); - printf("%s %s %s %s\n", ctx->host, + mprintf("%s %s %s %s\n", ctx->host, sshkey_type(l->key), fp, l->comment); free(fp); } else fprintf(ctx->out, "%s\n", l->line); return 0; } } else if (delete_host) { /* Retain non-matching hosts when deleting */ if (l->status == HKF_STATUS_INVALID) { ctx->invalid = 1; - logit("%s:%ld: invalid line", l->path, l->linenum); + logit("%s:%lu: invalid line", l->path, l->linenum); } fprintf(ctx->out, "%s\n", l->line); } return 0; } static void do_known_hosts(struct passwd *pw, const char *name) { char *cp, tmp[PATH_MAX], old[PATH_MAX]; int r, fd, oerrno, inplace = 0; struct known_hosts_ctx ctx; u_int foreach_options; if (!have_identity) { cp = tilde_expand_filename(_PATH_SSH_USER_HOSTFILE, pw->pw_uid); if (strlcpy(identity_file, cp, sizeof(identity_file)) >= sizeof(identity_file)) fatal("Specified known hosts path too long"); free(cp); have_identity = 1; } memset(&ctx, 0, sizeof(ctx)); ctx.out = stdout; ctx.host = name; /* * Find hosts goes to stdout, hash and deletions happen in-place * A corner case is ssh-keygen -HF foo, which should go to stdout */ if (!find_host && (hash_hosts || delete_host)) { if (strlcpy(tmp, identity_file, sizeof(tmp)) >= sizeof(tmp) || strlcat(tmp, ".XXXXXXXXXX", sizeof(tmp)) >= sizeof(tmp) || strlcpy(old, identity_file, sizeof(old)) >= sizeof(old) || strlcat(old, ".old", sizeof(old)) >= sizeof(old)) fatal("known_hosts path too long"); umask(077); if ((fd = mkstemp(tmp)) == -1) fatal("mkstemp: %s", strerror(errno)); if ((ctx.out = fdopen(fd, "w")) == NULL) { oerrno = errno; unlink(tmp); fatal("fdopen: %s", strerror(oerrno)); } inplace = 1; } /* XXX support identity_file == "-" for stdin */ foreach_options = find_host ? HKF_WANT_MATCH : 0; foreach_options |= print_fingerprint ? HKF_WANT_PARSE_KEY : 0; if ((r = hostkeys_foreach(identity_file, hash_hosts ? known_hosts_hash : known_hosts_find_delete, &ctx, name, NULL, foreach_options)) != 0) { if (inplace) unlink(tmp); fatal("%s: hostkeys_foreach failed: %s", __func__, ssh_err(r)); } if (inplace) fclose(ctx.out); if (ctx.invalid) { error("%s is not a valid known_hosts file.", identity_file); if (inplace) { error("Not replacing existing known_hosts " "file because of errors"); unlink(tmp); } exit(1); } else if (delete_host && !ctx.found_key) { logit("Host %s not found in %s", name, identity_file); if (inplace) unlink(tmp); } else if (inplace) { /* Backup existing file */ if (unlink(old) == -1 && errno != ENOENT) fatal("unlink %.100s: %s", old, strerror(errno)); if (link(identity_file, old) == -1) fatal("link %.100s to %.100s: %s", identity_file, old, strerror(errno)); /* Move new one into place */ if (rename(tmp, identity_file) == -1) { error("rename\"%s\" to \"%s\": %s", tmp, identity_file, strerror(errno)); unlink(tmp); unlink(old); exit(1); } printf("%s updated.\n", identity_file); printf("Original contents retained as %s\n", old); if (ctx.has_unhashed) { logit("WARNING: %s contains unhashed entries", old); logit("Delete this file to ensure privacy " "of hostnames"); } } exit (find_host && !ctx.found_key); } /* * Perform changing a passphrase. The argument is the passwd structure * for the current user. */ static void do_change_passphrase(struct passwd *pw) { char *comment; char *old_passphrase, *passphrase1, *passphrase2; struct stat st; struct sshkey *private; int r; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); if (stat(identity_file, &st) < 0) fatal("%s: %s", identity_file, strerror(errno)); /* Try to load the file with empty passphrase. */ r = sshkey_load_private(identity_file, "", &private, &comment); if (r == SSH_ERR_KEY_WRONG_PASSPHRASE) { if (identity_passphrase) old_passphrase = xstrdup(identity_passphrase); else old_passphrase = read_passphrase("Enter old passphrase: ", RP_ALLOW_STDIN); r = sshkey_load_private(identity_file, old_passphrase, &private, &comment); explicit_bzero(old_passphrase, strlen(old_passphrase)); free(old_passphrase); if (r != 0) goto badkey; } else if (r != 0) { badkey: fatal("Failed to load key %s: %s", identity_file, ssh_err(r)); } if (comment) - printf("Key has comment '%s'\n", comment); + mprintf("Key has comment '%s'\n", comment); /* Ask the new passphrase (twice). */ if (identity_new_passphrase) { passphrase1 = xstrdup(identity_new_passphrase); passphrase2 = NULL; } else { passphrase1 = read_passphrase("Enter new passphrase (empty for no " "passphrase): ", RP_ALLOW_STDIN); passphrase2 = read_passphrase("Enter same passphrase again: ", RP_ALLOW_STDIN); /* Verify that they are the same. */ if (strcmp(passphrase1, passphrase2) != 0) { explicit_bzero(passphrase1, strlen(passphrase1)); explicit_bzero(passphrase2, strlen(passphrase2)); free(passphrase1); free(passphrase2); printf("Pass phrases do not match. Try again.\n"); exit(1); } /* Destroy the other copy. */ explicit_bzero(passphrase2, strlen(passphrase2)); free(passphrase2); } /* Save the file using the new passphrase. */ if ((r = sshkey_save_private(private, identity_file, passphrase1, comment, use_new_format, new_format_cipher, rounds)) != 0) { error("Saving key \"%s\" failed: %s.", identity_file, ssh_err(r)); explicit_bzero(passphrase1, strlen(passphrase1)); free(passphrase1); sshkey_free(private); free(comment); exit(1); } /* Destroy the passphrase and the copy of the key in memory. */ explicit_bzero(passphrase1, strlen(passphrase1)); free(passphrase1); sshkey_free(private); /* Destroys contents */ free(comment); printf("Your identification has been saved with the new passphrase.\n"); exit(0); } /* * Print the SSHFP RR. */ static int do_print_resource_record(struct passwd *pw, char *fname, char *hname) { struct sshkey *public; char *comment = NULL; struct stat st; int r; if (fname == NULL) fatal("%s: no filename", __func__); if (stat(fname, &st) < 0) { if (errno == ENOENT) return 0; fatal("%s: %s", fname, strerror(errno)); } if ((r = sshkey_load_public(fname, &public, &comment)) != 0) fatal("Failed to read v2 public key from \"%s\": %s.", fname, ssh_err(r)); export_dns_rr(hname, public, stdout, print_generic); sshkey_free(public); free(comment); return 1; } /* * Change the comment of a private key file. */ static void do_change_comment(struct passwd *pw) { char new_comment[1024], *comment, *passphrase; struct sshkey *private; struct sshkey *public; struct stat st; FILE *f; int r, fd; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); if (stat(identity_file, &st) < 0) fatal("%s: %s", identity_file, strerror(errno)); if ((r = sshkey_load_private(identity_file, "", &private, &comment)) == 0) passphrase = xstrdup(""); else if (r != SSH_ERR_KEY_WRONG_PASSPHRASE) fatal("Cannot load private key \"%s\": %s.", identity_file, ssh_err(r)); else { if (identity_passphrase) passphrase = xstrdup(identity_passphrase); else if (identity_new_passphrase) passphrase = xstrdup(identity_new_passphrase); else passphrase = read_passphrase("Enter passphrase: ", RP_ALLOW_STDIN); /* Try to load using the passphrase. */ if ((r = sshkey_load_private(identity_file, passphrase, &private, &comment)) != 0) { explicit_bzero(passphrase, strlen(passphrase)); free(passphrase); fatal("Cannot load private key \"%s\": %s.", identity_file, ssh_err(r)); } } if (private->type != KEY_RSA1 && private->type != KEY_ED25519 && !use_new_format) { error("Comments are only supported for RSA1 or keys stored in " "the new format (-o)."); explicit_bzero(passphrase, strlen(passphrase)); sshkey_free(private); exit(1); } - printf("Key now has comment '%s'\n", comment); + if (comment) + printf("Key now has comment '%s'\n", comment); + else + printf("Key now has no comment\n"); if (identity_comment) { strlcpy(new_comment, identity_comment, sizeof(new_comment)); } else { printf("Enter new comment: "); fflush(stdout); if (!fgets(new_comment, sizeof(new_comment), stdin)) { explicit_bzero(passphrase, strlen(passphrase)); sshkey_free(private); exit(1); } new_comment[strcspn(new_comment, "\n")] = '\0'; } /* Save the file using the new passphrase. */ if ((r = sshkey_save_private(private, identity_file, passphrase, new_comment, use_new_format, new_format_cipher, rounds)) != 0) { error("Saving key \"%s\" failed: %s", identity_file, ssh_err(r)); explicit_bzero(passphrase, strlen(passphrase)); free(passphrase); sshkey_free(private); free(comment); exit(1); } explicit_bzero(passphrase, strlen(passphrase)); free(passphrase); if ((r = sshkey_from_private(private, &public)) != 0) fatal("key_from_private failed: %s", ssh_err(r)); sshkey_free(private); strlcat(identity_file, ".pub", sizeof(identity_file)); fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd == -1) fatal("Could not save your public key in %s", identity_file); f = fdopen(fd, "w"); if (f == NULL) fatal("fdopen %s failed: %s", identity_file, strerror(errno)); if ((r = sshkey_write(public, f)) != 0) fatal("write key failed: %s", ssh_err(r)); sshkey_free(public); fprintf(f, " %s\n", new_comment); fclose(f); free(comment); printf("The comment in your key file has been changed.\n"); exit(0); } static void add_flag_option(struct sshbuf *c, const char *name) { int r; debug3("%s: %s", __func__, name); if ((r = sshbuf_put_cstring(c, name)) != 0 || (r = sshbuf_put_string(c, NULL, 0)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); } static void add_string_option(struct sshbuf *c, const char *name, const char *value) { struct sshbuf *b; int r; debug3("%s: %s=%s", __func__, name, value); if ((b = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); if ((r = sshbuf_put_cstring(b, value)) != 0 || (r = sshbuf_put_cstring(c, name)) != 0 || (r = sshbuf_put_stringb(c, b)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); sshbuf_free(b); } #define OPTIONS_CRITICAL 1 #define OPTIONS_EXTENSIONS 2 static void prepare_options_buf(struct sshbuf *c, int which) { sshbuf_reset(c); if ((which & OPTIONS_CRITICAL) != 0 && certflags_command != NULL) add_string_option(c, "force-command", certflags_command); if ((which & OPTIONS_EXTENSIONS) != 0 && (certflags_flags & CERTOPT_X_FWD) != 0) add_flag_option(c, "permit-X11-forwarding"); if ((which & OPTIONS_EXTENSIONS) != 0 && (certflags_flags & CERTOPT_AGENT_FWD) != 0) add_flag_option(c, "permit-agent-forwarding"); if ((which & OPTIONS_EXTENSIONS) != 0 && (certflags_flags & CERTOPT_PORT_FWD) != 0) add_flag_option(c, "permit-port-forwarding"); if ((which & OPTIONS_EXTENSIONS) != 0 && (certflags_flags & CERTOPT_PTY) != 0) add_flag_option(c, "permit-pty"); if ((which & OPTIONS_EXTENSIONS) != 0 && (certflags_flags & CERTOPT_USER_RC) != 0) add_flag_option(c, "permit-user-rc"); if ((which & OPTIONS_CRITICAL) != 0 && certflags_src_addr != NULL) add_string_option(c, "source-address", certflags_src_addr); } static struct sshkey * load_pkcs11_key(char *path) { #ifdef ENABLE_PKCS11 struct sshkey **keys = NULL, *public, *private = NULL; int r, i, nkeys; if ((r = sshkey_load_public(path, &public, NULL)) != 0) fatal("Couldn't load CA public key \"%s\": %s", path, ssh_err(r)); nkeys = pkcs11_add_provider(pkcs11provider, identity_passphrase, &keys); debug3("%s: %d keys", __func__, nkeys); if (nkeys <= 0) fatal("cannot read public key from pkcs11"); for (i = 0; i < nkeys; i++) { if (sshkey_equal_public(public, keys[i])) { private = keys[i]; continue; } sshkey_free(keys[i]); } free(keys); sshkey_free(public); return private; #else fatal("no pkcs11 support"); #endif /* ENABLE_PKCS11 */ } static void do_ca_sign(struct passwd *pw, int argc, char **argv) { int r, i, fd; u_int n; struct sshkey *ca, *public; char valid[64], *otmp, *tmp, *cp, *out, *comment, **plist = NULL; FILE *f; #ifdef ENABLE_PKCS11 pkcs11_init(1); #endif tmp = tilde_expand_filename(ca_key_path, pw->pw_uid); if (pkcs11provider != NULL) { if ((ca = load_pkcs11_key(tmp)) == NULL) fatal("No PKCS#11 key matching %s found", ca_key_path); } else ca = load_identity(tmp); free(tmp); if (key_type_name != NULL && sshkey_type_from_name(key_type_name) != ca->type) { fatal("CA key type %s doesn't match specified %s", sshkey_ssh_name(ca), key_type_name); } for (i = 0; i < argc; i++) { /* Split list of principals */ n = 0; if (cert_principals != NULL) { otmp = tmp = xstrdup(cert_principals); plist = NULL; for (; (cp = strsep(&tmp, ",")) != NULL; n++) { plist = xreallocarray(plist, n + 1, sizeof(*plist)); if (*(plist[n] = xstrdup(cp)) == '\0') fatal("Empty principal name"); } free(otmp); } tmp = tilde_expand_filename(argv[i], pw->pw_uid); if ((r = sshkey_load_public(tmp, &public, &comment)) != 0) fatal("%s: unable to open \"%s\": %s", __func__, tmp, ssh_err(r)); if (public->type != KEY_RSA && public->type != KEY_DSA && public->type != KEY_ECDSA && public->type != KEY_ED25519) fatal("%s: key \"%s\" type %s cannot be certified", __func__, tmp, sshkey_type(public)); /* Prepare certificate to sign */ if ((r = sshkey_to_certified(public)) != 0) fatal("Could not upgrade key %s to certificate: %s", tmp, ssh_err(r)); public->cert->type = cert_key_type; public->cert->serial = (u_int64_t)cert_serial; public->cert->key_id = xstrdup(cert_key_id); public->cert->nprincipals = n; public->cert->principals = plist; public->cert->valid_after = cert_valid_from; public->cert->valid_before = cert_valid_to; prepare_options_buf(public->cert->critical, OPTIONS_CRITICAL); prepare_options_buf(public->cert->extensions, OPTIONS_EXTENSIONS); if ((r = sshkey_from_private(ca, &public->cert->signature_key)) != 0) fatal("key_from_private (ca key): %s", ssh_err(r)); if ((r = sshkey_certify(public, ca, key_type_name)) != 0) fatal("Couldn't certify key %s: %s", tmp, ssh_err(r)); if ((cp = strrchr(tmp, '.')) != NULL && strcmp(cp, ".pub") == 0) *cp = '\0'; xasprintf(&out, "%s-cert.pub", tmp); free(tmp); if ((fd = open(out, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) fatal("Could not open \"%s\" for writing: %s", out, strerror(errno)); if ((f = fdopen(fd, "w")) == NULL) fatal("%s: fdopen: %s", __func__, strerror(errno)); if ((r = sshkey_write(public, f)) != 0) fatal("Could not write certified key to %s: %s", out, ssh_err(r)); fprintf(f, " %s\n", comment); fclose(f); if (!quiet) { sshkey_format_cert_validity(public->cert, valid, sizeof(valid)); logit("Signed %s key %s: id \"%s\" serial %llu%s%s " "valid %s", sshkey_cert_type(public), out, public->cert->key_id, (unsigned long long)public->cert->serial, cert_principals != NULL ? " for " : "", cert_principals != NULL ? cert_principals : "", valid); } sshkey_free(public); free(out); } #ifdef ENABLE_PKCS11 pkcs11_terminate(); #endif exit(0); } static u_int64_t parse_relative_time(const char *s, time_t now) { int64_t mul, secs; mul = *s == '-' ? -1 : 1; if ((secs = convtime(s + 1)) == -1) fatal("Invalid relative certificate time %s", s); if (mul == -1 && secs > now) fatal("Certificate time %s cannot be represented", s); return now + (u_int64_t)(secs * mul); } static u_int64_t parse_absolute_time(const char *s) { struct tm tm; time_t tt; char buf[32], *fmt; /* * POSIX strptime says "The application shall ensure that there * is white-space or other non-alphanumeric characters between * any two conversion specifications" so arrange things this way. */ switch (strlen(s)) { case 8: fmt = "%Y-%m-%d"; snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2s", s, s + 4, s + 6); break; case 14: fmt = "%Y-%m-%dT%H:%M:%S"; snprintf(buf, sizeof(buf), "%.4s-%.2s-%.2sT%.2s:%.2s:%.2s", s, s + 4, s + 6, s + 8, s + 10, s + 12); break; default: fatal("Invalid certificate time format %s", s); } memset(&tm, 0, sizeof(tm)); if (strptime(buf, fmt, &tm) == NULL) fatal("Invalid certificate time %s", s); if ((tt = mktime(&tm)) < 0) fatal("Certificate time %s cannot be represented", s); return (u_int64_t)tt; } static void parse_cert_times(char *timespec) { char *from, *to; time_t now = time(NULL); int64_t secs; /* +timespec relative to now */ if (*timespec == '+' && strchr(timespec, ':') == NULL) { if ((secs = convtime(timespec + 1)) == -1) fatal("Invalid relative certificate life %s", timespec); cert_valid_to = now + secs; /* * Backdate certificate one minute to avoid problems on hosts * with poorly-synchronised clocks. */ cert_valid_from = ((now - 59)/ 60) * 60; return; } /* * from:to, where * from := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS * to := [+-]timespec | YYYYMMDD | YYYYMMDDHHMMSS */ from = xstrdup(timespec); to = strchr(from, ':'); if (to == NULL || from == to || *(to + 1) == '\0') fatal("Invalid certificate life specification %s", timespec); *to++ = '\0'; if (*from == '-' || *from == '+') cert_valid_from = parse_relative_time(from, now); else cert_valid_from = parse_absolute_time(from); if (*to == '-' || *to == '+') cert_valid_to = parse_relative_time(to, now); else cert_valid_to = parse_absolute_time(to); if (cert_valid_to <= cert_valid_from) fatal("Empty certificate validity interval"); free(from); } static void add_cert_option(char *opt) { char *val; if (strcasecmp(opt, "clear") == 0) certflags_flags = 0; else if (strcasecmp(opt, "no-x11-forwarding") == 0) certflags_flags &= ~CERTOPT_X_FWD; else if (strcasecmp(opt, "permit-x11-forwarding") == 0) certflags_flags |= CERTOPT_X_FWD; else if (strcasecmp(opt, "no-agent-forwarding") == 0) certflags_flags &= ~CERTOPT_AGENT_FWD; else if (strcasecmp(opt, "permit-agent-forwarding") == 0) certflags_flags |= CERTOPT_AGENT_FWD; else if (strcasecmp(opt, "no-port-forwarding") == 0) certflags_flags &= ~CERTOPT_PORT_FWD; else if (strcasecmp(opt, "permit-port-forwarding") == 0) certflags_flags |= CERTOPT_PORT_FWD; else if (strcasecmp(opt, "no-pty") == 0) certflags_flags &= ~CERTOPT_PTY; else if (strcasecmp(opt, "permit-pty") == 0) certflags_flags |= CERTOPT_PTY; else if (strcasecmp(opt, "no-user-rc") == 0) certflags_flags &= ~CERTOPT_USER_RC; else if (strcasecmp(opt, "permit-user-rc") == 0) certflags_flags |= CERTOPT_USER_RC; else if (strncasecmp(opt, "force-command=", 14) == 0) { val = opt + 14; if (*val == '\0') fatal("Empty force-command option"); if (certflags_command != NULL) fatal("force-command already specified"); certflags_command = xstrdup(val); } else if (strncasecmp(opt, "source-address=", 15) == 0) { val = opt + 15; if (*val == '\0') fatal("Empty source-address option"); if (certflags_src_addr != NULL) fatal("source-address already specified"); if (addr_match_cidr_list(NULL, val) != 0) fatal("Invalid source-address list"); certflags_src_addr = xstrdup(val); } else fatal("Unsupported certificate option \"%s\"", opt); } static void show_options(struct sshbuf *optbuf, int in_critical) { char *name, *arg; struct sshbuf *options, *option = NULL; int r; if ((options = sshbuf_fromb(optbuf)) == NULL) fatal("%s: sshbuf_fromb failed", __func__); while (sshbuf_len(options) != 0) { sshbuf_free(option); option = NULL; if ((r = sshbuf_get_cstring(options, &name, NULL)) != 0 || (r = sshbuf_froms(options, &option)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); printf(" %s", name); if (!in_critical && (strcmp(name, "permit-X11-forwarding") == 0 || strcmp(name, "permit-agent-forwarding") == 0 || strcmp(name, "permit-port-forwarding") == 0 || strcmp(name, "permit-pty") == 0 || strcmp(name, "permit-user-rc") == 0)) printf("\n"); else if (in_critical && (strcmp(name, "force-command") == 0 || strcmp(name, "source-address") == 0)) { if ((r = sshbuf_get_cstring(option, &arg, NULL)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); printf(" %s\n", arg); free(arg); } else { printf(" UNKNOWN OPTION (len %zu)\n", sshbuf_len(option)); sshbuf_reset(option); } free(name); if (sshbuf_len(option) != 0) fatal("Option corrupt: extra data at end"); } sshbuf_free(option); sshbuf_free(options); } static void print_cert(struct sshkey *key) { char valid[64], *key_fp, *ca_fp; u_int i; key_fp = sshkey_fingerprint(key, fingerprint_hash, SSH_FP_DEFAULT); ca_fp = sshkey_fingerprint(key->cert->signature_key, fingerprint_hash, SSH_FP_DEFAULT); if (key_fp == NULL || ca_fp == NULL) fatal("%s: sshkey_fingerprint fail", __func__); sshkey_format_cert_validity(key->cert, valid, sizeof(valid)); printf(" Type: %s %s certificate\n", sshkey_ssh_name(key), sshkey_cert_type(key)); printf(" Public key: %s %s\n", sshkey_type(key), key_fp); printf(" Signing CA: %s %s\n", sshkey_type(key->cert->signature_key), ca_fp); printf(" Key ID: \"%s\"\n", key->cert->key_id); printf(" Serial: %llu\n", (unsigned long long)key->cert->serial); printf(" Valid: %s\n", valid); printf(" Principals: "); if (key->cert->nprincipals == 0) printf("(none)\n"); else { for (i = 0; i < key->cert->nprincipals; i++) printf("\n %s", key->cert->principals[i]); printf("\n"); } printf(" Critical Options: "); if (sshbuf_len(key->cert->critical) == 0) printf("(none)\n"); else { printf("\n"); show_options(key->cert->critical, 1); } printf(" Extensions: "); if (sshbuf_len(key->cert->extensions) == 0) printf("(none)\n"); else { printf("\n"); show_options(key->cert->extensions, 0); } } static void do_show_cert(struct passwd *pw) { struct sshkey *key = NULL; struct stat st; int r, is_stdin = 0, ok = 0; FILE *f; char *cp, line[SSH_MAX_PUBKEY_BYTES]; const char *path; u_long lnum = 0; if (!have_identity) ask_filename(pw, "Enter file in which the key is"); if (strcmp(identity_file, "-") != 0 && stat(identity_file, &st) < 0) fatal("%s: %s: %s", __progname, identity_file, strerror(errno)); path = identity_file; if (strcmp(path, "-") == 0) { f = stdin; path = "(stdin)"; is_stdin = 1; } else if ((f = fopen(identity_file, "r")) == NULL) fatal("fopen %s: %s", identity_file, strerror(errno)); while (read_keyfile_line(f, path, line, sizeof(line), &lnum) == 0) { sshkey_free(key); key = NULL; /* Trim leading space and comments */ cp = line + strspn(line, " \t"); if (*cp == '#' || *cp == '\0') continue; if ((key = sshkey_new(KEY_UNSPEC)) == NULL) fatal("key_new"); if ((r = sshkey_read(key, &cp)) != 0) { error("%s:%lu: invalid key: %s", path, lnum, ssh_err(r)); continue; } if (!sshkey_is_cert(key)) { error("%s:%lu is not a certificate", path, lnum); continue; } ok = 1; if (!is_stdin && lnum == 1) printf("%s:\n", path); else printf("%s:%lu:\n", path, lnum); print_cert(key); } sshkey_free(key); fclose(f); exit(ok ? 0 : 1); } static void load_krl(const char *path, struct ssh_krl **krlp) { struct sshbuf *krlbuf; int r, fd; if ((krlbuf = sshbuf_new()) == NULL) fatal("sshbuf_new failed"); if ((fd = open(path, O_RDONLY)) == -1) fatal("open %s: %s", path, strerror(errno)); if ((r = sshkey_load_file(fd, krlbuf)) != 0) fatal("Unable to load KRL: %s", ssh_err(r)); close(fd); /* XXX check sigs */ if ((r = ssh_krl_from_blob(krlbuf, krlp, NULL, 0)) != 0 || *krlp == NULL) fatal("Invalid KRL file: %s", ssh_err(r)); sshbuf_free(krlbuf); } static void update_krl_from_file(struct passwd *pw, const char *file, int wild_ca, const struct sshkey *ca, struct ssh_krl *krl) { struct sshkey *key = NULL; u_long lnum = 0; char *path, *cp, *ep, line[SSH_MAX_PUBKEY_BYTES]; unsigned long long serial, serial2; int i, was_explicit_key, was_sha1, r; FILE *krl_spec; path = tilde_expand_filename(file, pw->pw_uid); if (strcmp(path, "-") == 0) { krl_spec = stdin; free(path); path = xstrdup("(standard input)"); } else if ((krl_spec = fopen(path, "r")) == NULL) fatal("fopen %s: %s", path, strerror(errno)); if (!quiet) printf("Revoking from %s\n", path); while (read_keyfile_line(krl_spec, path, line, sizeof(line), &lnum) == 0) { was_explicit_key = was_sha1 = 0; cp = line + strspn(line, " \t"); /* Trim trailing space, comments and strip \n */ for (i = 0, r = -1; cp[i] != '\0'; i++) { if (cp[i] == '#' || cp[i] == '\n') { cp[i] = '\0'; break; } if (cp[i] == ' ' || cp[i] == '\t') { /* Remember the start of a span of whitespace */ if (r == -1) r = i; } else r = -1; } if (r != -1) cp[r] = '\0'; if (*cp == '\0') continue; if (strncasecmp(cp, "serial:", 7) == 0) { if (ca == NULL && !wild_ca) { fatal("revoking certificates by serial number " "requires specification of a CA key"); } cp += 7; cp = cp + strspn(cp, " \t"); errno = 0; serial = strtoull(cp, &ep, 0); if (*cp == '\0' || (*ep != '\0' && *ep != '-')) fatal("%s:%lu: invalid serial \"%s\"", path, lnum, cp); if (errno == ERANGE && serial == ULLONG_MAX) fatal("%s:%lu: serial out of range", path, lnum); serial2 = serial; if (*ep == '-') { cp = ep + 1; errno = 0; serial2 = strtoull(cp, &ep, 0); if (*cp == '\0' || *ep != '\0') fatal("%s:%lu: invalid serial \"%s\"", path, lnum, cp); if (errno == ERANGE && serial2 == ULLONG_MAX) fatal("%s:%lu: serial out of range", path, lnum); if (serial2 <= serial) fatal("%s:%lu: invalid serial range " "%llu:%llu", path, lnum, (unsigned long long)serial, (unsigned long long)serial2); } if (ssh_krl_revoke_cert_by_serial_range(krl, ca, serial, serial2) != 0) { fatal("%s: revoke serial failed", __func__); } } else if (strncasecmp(cp, "id:", 3) == 0) { if (ca == NULL && !wild_ca) { fatal("revoking certificates by key ID " "requires specification of a CA key"); } cp += 3; cp = cp + strspn(cp, " \t"); if (ssh_krl_revoke_cert_by_key_id(krl, ca, cp) != 0) fatal("%s: revoke key ID failed", __func__); } else { if (strncasecmp(cp, "key:", 4) == 0) { cp += 4; cp = cp + strspn(cp, " \t"); was_explicit_key = 1; } else if (strncasecmp(cp, "sha1:", 5) == 0) { cp += 5; cp = cp + strspn(cp, " \t"); was_sha1 = 1; } else { /* * Just try to process the line as a key. * Parsing will fail if it isn't. */ } if ((key = sshkey_new(KEY_UNSPEC)) == NULL) fatal("key_new"); if ((r = sshkey_read(key, &cp)) != 0) fatal("%s:%lu: invalid key: %s", path, lnum, ssh_err(r)); if (was_explicit_key) r = ssh_krl_revoke_key_explicit(krl, key); else if (was_sha1) r = ssh_krl_revoke_key_sha1(krl, key); else r = ssh_krl_revoke_key(krl, key); if (r != 0) fatal("%s: revoke key failed: %s", __func__, ssh_err(r)); sshkey_free(key); } } if (strcmp(path, "-") != 0) fclose(krl_spec); free(path); } static void do_gen_krl(struct passwd *pw, int updating, int argc, char **argv) { struct ssh_krl *krl; struct stat sb; struct sshkey *ca = NULL; int fd, i, r, wild_ca = 0; char *tmp; struct sshbuf *kbuf; if (*identity_file == '\0') fatal("KRL generation requires an output file"); if (stat(identity_file, &sb) == -1) { if (errno != ENOENT) fatal("Cannot access KRL \"%s\": %s", identity_file, strerror(errno)); if (updating) fatal("KRL \"%s\" does not exist", identity_file); } if (ca_key_path != NULL) { if (strcasecmp(ca_key_path, "none") == 0) wild_ca = 1; else { tmp = tilde_expand_filename(ca_key_path, pw->pw_uid); if ((r = sshkey_load_public(tmp, &ca, NULL)) != 0) fatal("Cannot load CA public key %s: %s", tmp, ssh_err(r)); free(tmp); } } if (updating) load_krl(identity_file, &krl); else if ((krl = ssh_krl_init()) == NULL) fatal("couldn't create KRL"); if (cert_serial != 0) ssh_krl_set_version(krl, cert_serial); if (identity_comment != NULL) ssh_krl_set_comment(krl, identity_comment); for (i = 0; i < argc; i++) update_krl_from_file(pw, argv[i], wild_ca, ca, krl); if ((kbuf = sshbuf_new()) == NULL) fatal("sshbuf_new failed"); if (ssh_krl_to_blob(krl, kbuf, NULL, 0) != 0) fatal("Couldn't generate KRL"); if ((fd = open(identity_file, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) fatal("open %s: %s", identity_file, strerror(errno)); if (atomicio(vwrite, fd, (void *)sshbuf_ptr(kbuf), sshbuf_len(kbuf)) != sshbuf_len(kbuf)) fatal("write %s: %s", identity_file, strerror(errno)); close(fd); sshbuf_free(kbuf); ssh_krl_free(krl); sshkey_free(ca); } static void do_check_krl(struct passwd *pw, int argc, char **argv) { int i, r, ret = 0; char *comment; struct ssh_krl *krl; struct sshkey *k; if (*identity_file == '\0') fatal("KRL checking requires an input file"); load_krl(identity_file, &krl); for (i = 0; i < argc; i++) { if ((r = sshkey_load_public(argv[i], &k, &comment)) != 0) fatal("Cannot load public key %s: %s", argv[i], ssh_err(r)); r = ssh_krl_check_key(krl, k); printf("%s%s%s%s: %s\n", argv[i], *comment ? " (" : "", comment, *comment ? ")" : "", r == 0 ? "ok" : "REVOKED"); if (r != 0) ret = 1; sshkey_free(k); free(comment); } ssh_krl_free(krl); exit(ret); } +#ifdef WITH_SSH1 +# define RSA1_USAGE " | rsa1" +#else +# define RSA1_USAGE "" +#endif + static void usage(void) { fprintf(stderr, - "usage: ssh-keygen [-q] [-b bits] [-t dsa | ecdsa | ed25519 | rsa | rsa1]\n" + "usage: ssh-keygen [-q] [-b bits] [-t dsa | ecdsa | ed25519 | rsa%s]\n" " [-N new_passphrase] [-C comment] [-f output_keyfile]\n" " ssh-keygen -p [-P old_passphrase] [-N new_passphrase] [-f keyfile]\n" " ssh-keygen -i [-m key_format] [-f input_keyfile]\n" " ssh-keygen -e [-m key_format] [-f input_keyfile]\n" " ssh-keygen -y [-f input_keyfile]\n" " ssh-keygen -c [-P passphrase] [-C comment] [-f keyfile]\n" " ssh-keygen -l [-v] [-E fingerprint_hash] [-f input_keyfile]\n" - " ssh-keygen -B [-f input_keyfile]\n"); + " ssh-keygen -B [-f input_keyfile]\n", RSA1_USAGE); #ifdef ENABLE_PKCS11 fprintf(stderr, " ssh-keygen -D pkcs11\n"); #endif fprintf(stderr, " ssh-keygen -F hostname [-f known_hosts_file] [-l]\n" " ssh-keygen -H [-f known_hosts_file]\n" " ssh-keygen -R hostname [-f known_hosts_file]\n" " ssh-keygen -r hostname [-f input_keyfile] [-g]\n" #ifdef WITH_OPENSSL " ssh-keygen -G output_file [-v] [-b bits] [-M memory] [-S start_point]\n" " ssh-keygen -T output_file -f input_file [-v] [-a rounds] [-J num_lines]\n" " [-j start_line] [-K checkpt] [-W generator]\n" #endif " ssh-keygen -s ca_key -I certificate_identity [-h] [-n principals]\n" " [-O option] [-V validity_interval] [-z serial_number] file ...\n" " ssh-keygen -L [-f input_keyfile]\n" " ssh-keygen -A\n" " ssh-keygen -k -f krl_file [-u] [-s ca_public] [-z version_number]\n" " file ...\n" " ssh-keygen -Q -f krl_file file ...\n"); exit(1); } /* * Main program for key management. */ int main(int argc, char **argv) { char dotsshdir[PATH_MAX], comment[1024], *passphrase1, *passphrase2; char *rr_hostname = NULL, *ep, *fp, *ra; struct sshkey *private, *public; struct passwd *pw; struct stat st; int r, opt, type, fd; int gen_all_hostkeys = 0, gen_krl = 0, update_krl = 0, check_krl = 0; FILE *f; const char *errstr; #ifdef WITH_OPENSSL /* Moduli generation/screening */ char out_file[PATH_MAX], *checkpoint = NULL; u_int32_t memory = 0, generator_wanted = 0; int do_gen_candidates = 0, do_screen_candidates = 0; unsigned long start_lineno = 0, lines_to_process = 0; BIGNUM *start = NULL; #endif extern int optind; extern char *optarg; ssh_malloc_init(); /* must be called before any mallocs */ /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); __progname = ssh_get_progname(argv[0]); #ifdef WITH_OPENSSL OpenSSL_add_all_algorithms(); #endif log_init(argv[0], SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_USER, 1); seed_rng(); + msetlocale(); + /* we need this for the home * directory. */ pw = getpwuid(getuid()); if (!pw) fatal("No user exists for uid %lu", (u_long)getuid()); if (gethostname(hostname, sizeof(hostname)) < 0) fatal("gethostname: %s", strerror(errno)); /* Remaining characters: UYdw */ while ((opt = getopt(argc, argv, "ABHLQXceghiklopquvxy" "C:D:E:F:G:I:J:K:M:N:O:P:R:S:T:V:W:Z:" "a:b:f:g:j:m:n:r:s:t:z:")) != -1) { switch (opt) { case 'A': gen_all_hostkeys = 1; break; case 'b': bits = (u_int32_t)strtonum(optarg, 256, 32768, &errstr); if (errstr) fatal("Bits has bad value %s (%s)", optarg, errstr); break; case 'E': fingerprint_hash = ssh_digest_alg_by_name(optarg); if (fingerprint_hash == -1) fatal("Invalid hash algorithm \"%s\"", optarg); break; case 'F': find_host = 1; rr_hostname = optarg; break; case 'H': hash_hosts = 1; break; case 'I': cert_key_id = optarg; break; case 'R': delete_host = 1; rr_hostname = optarg; break; case 'L': show_cert = 1; break; case 'l': print_fingerprint = 1; break; case 'B': print_bubblebabble = 1; break; case 'm': if (strcasecmp(optarg, "RFC4716") == 0 || strcasecmp(optarg, "ssh2") == 0) { convert_format = FMT_RFC4716; break; } if (strcasecmp(optarg, "PKCS8") == 0) { convert_format = FMT_PKCS8; break; } if (strcasecmp(optarg, "PEM") == 0) { convert_format = FMT_PEM; break; } fatal("Unsupported conversion format \"%s\"", optarg); case 'n': cert_principals = optarg; break; case 'o': use_new_format = 1; break; case 'p': change_passphrase = 1; break; case 'c': change_comment = 1; break; case 'f': if (strlcpy(identity_file, optarg, sizeof(identity_file)) >= sizeof(identity_file)) fatal("Identity filename too long"); have_identity = 1; break; case 'g': print_generic = 1; break; case 'P': identity_passphrase = optarg; break; case 'N': identity_new_passphrase = optarg; break; case 'Q': check_krl = 1; break; case 'O': add_cert_option(optarg); break; case 'Z': new_format_cipher = optarg; break; case 'C': identity_comment = optarg; break; case 'q': quiet = 1; break; case 'e': case 'x': /* export key */ convert_to = 1; break; case 'h': cert_key_type = SSH2_CERT_TYPE_HOST; certflags_flags = 0; break; case 'k': gen_krl = 1; break; case 'i': case 'X': /* import key */ convert_from = 1; break; case 'y': print_public = 1; break; case 's': ca_key_path = optarg; break; case 't': key_type_name = optarg; break; case 'D': pkcs11provider = optarg; break; case 'u': update_krl = 1; break; case 'v': if (log_level == SYSLOG_LEVEL_INFO) log_level = SYSLOG_LEVEL_DEBUG1; else { if (log_level >= SYSLOG_LEVEL_DEBUG1 && log_level < SYSLOG_LEVEL_DEBUG3) log_level++; } break; case 'r': rr_hostname = optarg; break; case 'a': rounds = (int)strtonum(optarg, 1, INT_MAX, &errstr); if (errstr) fatal("Invalid number: %s (%s)", optarg, errstr); break; case 'V': parse_cert_times(optarg); break; case 'z': errno = 0; cert_serial = strtoull(optarg, &ep, 10); if (*optarg < '0' || *optarg > '9' || *ep != '\0' || (errno == ERANGE && cert_serial == ULLONG_MAX)) fatal("Invalid serial number \"%s\"", optarg); break; #ifdef WITH_OPENSSL /* Moduli generation/screening */ case 'G': do_gen_candidates = 1; if (strlcpy(out_file, optarg, sizeof(out_file)) >= sizeof(out_file)) fatal("Output filename too long"); break; case 'J': lines_to_process = strtoul(optarg, NULL, 10); break; case 'j': start_lineno = strtoul(optarg, NULL, 10); break; case 'K': if (strlen(optarg) >= PATH_MAX) fatal("Checkpoint filename too long"); checkpoint = xstrdup(optarg); break; case 'M': memory = (u_int32_t)strtonum(optarg, 1, UINT_MAX, &errstr); if (errstr) fatal("Memory limit is %s: %s", errstr, optarg); break; case 'S': /* XXX - also compare length against bits */ if (BN_hex2bn(&start, optarg) == 0) fatal("Invalid start point."); break; case 'T': do_screen_candidates = 1; if (strlcpy(out_file, optarg, sizeof(out_file)) >= sizeof(out_file)) fatal("Output filename too long"); break; case 'W': generator_wanted = (u_int32_t)strtonum(optarg, 1, UINT_MAX, &errstr); if (errstr != NULL) fatal("Desired generator invalid: %s (%s)", optarg, errstr); break; #endif /* WITH_OPENSSL */ case '?': default: usage(); } } /* reinit */ log_init(argv[0], log_level, SYSLOG_FACILITY_USER, 1); argv += optind; argc -= optind; if (ca_key_path != NULL) { if (argc < 1 && !gen_krl) { error("Too few arguments."); usage(); } } else if (argc > 0 && !gen_krl && !check_krl) { error("Too many arguments."); usage(); } if (change_passphrase && change_comment) { error("Can only have one of -p and -c."); usage(); } if (print_fingerprint && (delete_host || hash_hosts)) { error("Cannot use -l with -H or -R."); usage(); } if (gen_krl) { do_gen_krl(pw, update_krl, argc, argv); return (0); } if (check_krl) { do_check_krl(pw, argc, argv); return (0); } if (ca_key_path != NULL) { if (cert_key_id == NULL) fatal("Must specify key id (-I) when certifying"); do_ca_sign(pw, argc, argv); } if (show_cert) do_show_cert(pw); if (delete_host || hash_hosts || find_host) do_known_hosts(pw, rr_hostname); if (pkcs11provider != NULL) do_download(pw); if (print_fingerprint || print_bubblebabble) do_fingerprint(pw); if (change_passphrase) do_change_passphrase(pw); if (change_comment) do_change_comment(pw); #ifdef WITH_OPENSSL if (convert_to) do_convert_to(pw); if (convert_from) do_convert_from(pw); #endif if (print_public) do_print_public(pw); if (rr_hostname != NULL) { unsigned int n = 0; if (have_identity) { n = do_print_resource_record(pw, identity_file, rr_hostname); if (n == 0) fatal("%s: %s", identity_file, strerror(errno)); exit(0); } else { n += do_print_resource_record(pw, _PATH_HOST_RSA_KEY_FILE, rr_hostname); n += do_print_resource_record(pw, _PATH_HOST_DSA_KEY_FILE, rr_hostname); n += do_print_resource_record(pw, _PATH_HOST_ECDSA_KEY_FILE, rr_hostname); n += do_print_resource_record(pw, _PATH_HOST_ED25519_KEY_FILE, rr_hostname); if (n == 0) fatal("no keys found."); exit(0); } } #ifdef WITH_OPENSSL if (do_gen_candidates) { FILE *out = fopen(out_file, "w"); if (out == NULL) { error("Couldn't open modulus candidate file \"%s\": %s", out_file, strerror(errno)); return (1); } if (bits == 0) bits = DEFAULT_BITS; if (gen_candidates(out, memory, bits, start) != 0) fatal("modulus candidate generation failed"); return (0); } if (do_screen_candidates) { FILE *in; FILE *out = fopen(out_file, "a"); if (have_identity && strcmp(identity_file, "-") != 0) { if ((in = fopen(identity_file, "r")) == NULL) { fatal("Couldn't open modulus candidate " "file \"%s\": %s", identity_file, strerror(errno)); } } else in = stdin; if (out == NULL) { fatal("Couldn't open moduli file \"%s\": %s", out_file, strerror(errno)); } if (prime_test(in, out, rounds == 0 ? 100 : rounds, generator_wanted, checkpoint, start_lineno, lines_to_process) != 0) fatal("modulus screening failed"); return (0); } #endif if (gen_all_hostkeys) { do_gen_all_hostkeys(pw); return (0); } if (key_type_name == NULL) key_type_name = DEFAULT_KEY_TYPE_NAME; type = sshkey_type_from_name(key_type_name); type_bits_valid(type, key_type_name, &bits); if (!quiet) printf("Generating public/private %s key pair.\n", key_type_name); if ((r = sshkey_generate(type, bits, &private)) != 0) fatal("key_generate failed"); if ((r = sshkey_from_private(private, &public)) != 0) fatal("key_from_private failed: %s\n", ssh_err(r)); if (!have_identity) ask_filename(pw, "Enter file in which to save the key"); /* Create ~/.ssh directory if it doesn't already exist. */ snprintf(dotsshdir, sizeof dotsshdir, "%s/%s", pw->pw_dir, _PATH_SSH_USER_DIR); if (strstr(identity_file, dotsshdir) != NULL) { if (stat(dotsshdir, &st) < 0) { if (errno != ENOENT) { error("Could not stat %s: %s", dotsshdir, strerror(errno)); } else if (mkdir(dotsshdir, 0700) < 0) { error("Could not create directory '%s': %s", dotsshdir, strerror(errno)); } else if (!quiet) printf("Created directory '%s'.\n", dotsshdir); } } /* If the file already exists, ask the user to confirm. */ if (stat(identity_file, &st) >= 0) { char yesno[3]; printf("%s already exists.\n", identity_file); printf("Overwrite (y/n)? "); fflush(stdout); if (fgets(yesno, sizeof(yesno), stdin) == NULL) exit(1); if (yesno[0] != 'y' && yesno[0] != 'Y') exit(1); } /* Ask for a passphrase (twice). */ if (identity_passphrase) passphrase1 = xstrdup(identity_passphrase); else if (identity_new_passphrase) passphrase1 = xstrdup(identity_new_passphrase); else { passphrase_again: passphrase1 = read_passphrase("Enter passphrase (empty for no " "passphrase): ", RP_ALLOW_STDIN); passphrase2 = read_passphrase("Enter same passphrase again: ", RP_ALLOW_STDIN); if (strcmp(passphrase1, passphrase2) != 0) { /* * The passphrases do not match. Clear them and * retry. */ explicit_bzero(passphrase1, strlen(passphrase1)); explicit_bzero(passphrase2, strlen(passphrase2)); free(passphrase1); free(passphrase2); printf("Passphrases do not match. Try again.\n"); goto passphrase_again; } /* Clear the other copy of the passphrase. */ explicit_bzero(passphrase2, strlen(passphrase2)); free(passphrase2); } if (identity_comment) { strlcpy(comment, identity_comment, sizeof(comment)); } else { /* Create default comment field for the passphrase. */ snprintf(comment, sizeof comment, "%s@%s", pw->pw_name, hostname); } /* Save the key with the given passphrase and comment. */ if ((r = sshkey_save_private(private, identity_file, passphrase1, comment, use_new_format, new_format_cipher, rounds)) != 0) { error("Saving key \"%s\" failed: %s", identity_file, ssh_err(r)); explicit_bzero(passphrase1, strlen(passphrase1)); free(passphrase1); exit(1); } /* Clear the passphrase. */ explicit_bzero(passphrase1, strlen(passphrase1)); free(passphrase1); /* Clear the private key and the random number generator. */ sshkey_free(private); if (!quiet) printf("Your identification has been saved in %s.\n", identity_file); strlcat(identity_file, ".pub", sizeof(identity_file)); if ((fd = open(identity_file, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) fatal("Unable to save public key to %s: %s", identity_file, strerror(errno)); if ((f = fdopen(fd, "w")) == NULL) fatal("fdopen %s failed: %s", identity_file, strerror(errno)); if ((r = sshkey_write(public, f)) != 0) error("write key failed: %s", ssh_err(r)); fprintf(f, " %s\n", comment); fclose(f); if (!quiet) { fp = sshkey_fingerprint(public, fingerprint_hash, SSH_FP_DEFAULT); ra = sshkey_fingerprint(public, fingerprint_hash, SSH_FP_RANDOMART); if (fp == NULL || ra == NULL) fatal("sshkey_fingerprint failed"); printf("Your public key has been saved in %s.\n", identity_file); printf("The key fingerprint is:\n"); printf("%s %s\n", fp, comment); printf("The key's randomart image is:\n"); printf("%s\n", ra); free(ra); free(fp); } sshkey_free(public); exit(0); } diff --git a/crypto/openssh/ssh-keyscan.c b/crypto/openssh/ssh-keyscan.c index c30d54e628f5..1f95239a37c6 100644 --- a/crypto/openssh/ssh-keyscan.c +++ b/crypto/openssh/ssh-keyscan.c @@ -1,850 +1,856 @@ -/* $OpenBSD: ssh-keyscan.c,v 1.106 2016/05/02 10:26:04 djm Exp $ */ +/* $OpenBSD: ssh-keyscan.c,v 1.109 2017/03/10 04:26:06 djm Exp $ */ /* * Copyright 1995, 1996 by David Mazieres . * * Modification and redistribution in source and binary forms is * permitted provided that due credit is given to the author and the * OpenBSD project by leaving this copyright notice intact. */ #include "includes.h" #include #include "openbsd-compat/sys-queue.h" #include #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include #include #include #include #include #include #include #include #include #include "xmalloc.h" #include "ssh.h" #include "ssh1.h" #include "sshbuf.h" #include "sshkey.h" #include "cipher.h" #include "kex.h" #include "compat.h" #include "myproposal.h" #include "packet.h" #include "dispatch.h" #include "log.h" #include "atomicio.h" #include "misc.h" #include "hostfile.h" #include "ssherr.h" #include "ssh_api.h" /* Flag indicating whether IPv4 or IPv6. This can be set on the command line. Default value is AF_UNSPEC means both IPv4 and IPv6. */ int IPv4or6 = AF_UNSPEC; int ssh_port = SSH_DEFAULT_PORT; #define KT_RSA1 1 #define KT_DSA 2 #define KT_RSA 4 #define KT_ECDSA 8 #define KT_ED25519 16 int get_cert = 0; int get_keytypes = KT_RSA|KT_ECDSA|KT_ED25519; int hash_hosts = 0; /* Hash hostname on output */ #define MAXMAXFD 256 /* The number of seconds after which to give up on a TCP connection */ int timeout = 5; int maxfd; #define MAXCON (maxfd - 10) extern char *__progname; fd_set *read_wait; size_t read_wait_nfdset; int ncon; struct ssh *active_state = NULL; /* XXX needed for linking */ /* * Keep a connection structure for each file descriptor. The state * associated with file descriptor n is held in fdcon[n]. */ typedef struct Connection { u_char c_status; /* State of connection on this file desc. */ #define CS_UNUSED 0 /* File descriptor unused */ #define CS_CON 1 /* Waiting to connect/read greeting */ #define CS_SIZE 2 /* Waiting to read initial packet size */ #define CS_KEYS 3 /* Waiting to read public key packet */ int c_fd; /* Quick lookup: c->c_fd == c - fdcon */ int c_plen; /* Packet length field for ssh packet */ int c_len; /* Total bytes which must be read. */ int c_off; /* Length of data read so far. */ int c_keytype; /* Only one of KT_RSA1, KT_DSA, or KT_RSA */ sig_atomic_t c_done; /* SSH2 done */ char *c_namebase; /* Address to free for c_name and c_namelist */ char *c_name; /* Hostname of connection for errors */ char *c_namelist; /* Pointer to other possible addresses */ char *c_output_name; /* Hostname of connection for output */ char *c_data; /* Data read from this fd */ struct ssh *c_ssh; /* SSH-connection */ struct timeval c_tv; /* Time at which connection gets aborted */ TAILQ_ENTRY(Connection) c_link; /* List of connections in timeout order. */ } con; TAILQ_HEAD(conlist, Connection) tq; /* Timeout Queue */ con *fdcon; static void keyprint(con *c, struct sshkey *key); static int fdlim_get(int hard) { #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) struct rlimit rlfd; if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0) return (-1); if ((hard ? rlfd.rlim_max : rlfd.rlim_cur) == RLIM_INFINITY) return SSH_SYSFDMAX; else return hard ? rlfd.rlim_max : rlfd.rlim_cur; #else return SSH_SYSFDMAX; #endif } static int fdlim_set(int lim) { #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) struct rlimit rlfd; #endif if (lim <= 0) return (-1); #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) if (getrlimit(RLIMIT_NOFILE, &rlfd) < 0) return (-1); rlfd.rlim_cur = lim; if (setrlimit(RLIMIT_NOFILE, &rlfd) < 0) return (-1); #elif defined (HAVE_SETDTABLESIZE) setdtablesize(lim); #endif return (0); } /* * This is an strsep function that returns a null field for adjacent * separators. This is the same as the 4.4BSD strsep, but different from the * one in the GNU libc. */ static char * xstrsep(char **str, const char *delim) { char *s, *e; if (!**str) return (NULL); s = *str; e = s + strcspn(s, delim); if (*e != '\0') *e++ = '\0'; *str = e; return (s); } /* * Get the next non-null token (like GNU strsep). Strsep() will return a * null token for two adjacent separators, so we may have to loop. */ static char * strnnsep(char **stringp, char *delim) { char *tok; do { tok = xstrsep(stringp, delim); } while (tok && *tok == '\0'); return (tok); } #ifdef WITH_SSH1 static struct sshkey * keygrab_ssh1(con *c) { static struct sshkey *rsa; static struct sshbuf *msg; int r; u_char type; if (rsa == NULL) { if ((rsa = sshkey_new(KEY_RSA1)) == NULL) { error("%s: sshkey_new failed", __func__); return NULL; } if ((msg = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); } if ((r = sshbuf_put(msg, c->c_data, c->c_plen)) != 0 || (r = sshbuf_consume(msg, 8 - (c->c_plen & 7))) != 0 || /* padding */ (r = sshbuf_get_u8(msg, &type)) != 0) goto buf_err; if (type != (int) SSH_SMSG_PUBLIC_KEY) { error("%s: invalid packet type", c->c_name); sshbuf_reset(msg); return NULL; } if ((r = sshbuf_consume(msg, 8)) != 0 || /* cookie */ /* server key */ (r = sshbuf_get_u32(msg, NULL)) != 0 || (r = sshbuf_get_bignum1(msg, NULL)) != 0 || (r = sshbuf_get_bignum1(msg, NULL)) != 0 || /* host key */ (r = sshbuf_get_u32(msg, NULL)) != 0 || (r = sshbuf_get_bignum1(msg, rsa->rsa->e)) != 0 || (r = sshbuf_get_bignum1(msg, rsa->rsa->n)) != 0) { buf_err: error("%s: buffer error: %s", __func__, ssh_err(r)); sshbuf_reset(msg); return NULL; } sshbuf_reset(msg); return (rsa); } #endif static int key_print_wrapper(struct sshkey *hostkey, struct ssh *ssh) { con *c; if ((c = ssh_get_app_data(ssh)) != NULL) keyprint(c, hostkey); /* always abort key exchange */ return -1; } static int ssh2_capable(int remote_major, int remote_minor) { switch (remote_major) { case 1: if (remote_minor == 99) return 1; break; case 2: return 1; default: break; } return 0; } static void keygrab_ssh2(con *c) { char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; int r; enable_compat20(); switch (c->c_keytype) { case KT_DSA: myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? "ssh-dss-cert-v01@openssh.com" : "ssh-dss"; break; case KT_RSA: myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? "ssh-rsa-cert-v01@openssh.com" : "ssh-rsa"; break; case KT_ED25519: myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? "ssh-ed25519-cert-v01@openssh.com" : "ssh-ed25519"; break; case KT_ECDSA: myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = get_cert ? "ecdsa-sha2-nistp256-cert-v01@openssh.com," "ecdsa-sha2-nistp384-cert-v01@openssh.com," "ecdsa-sha2-nistp521-cert-v01@openssh.com" : "ecdsa-sha2-nistp256," "ecdsa-sha2-nistp384," "ecdsa-sha2-nistp521"; break; default: fatal("unknown key type %d", c->c_keytype); break; } if ((r = kex_setup(c->c_ssh, myproposal)) != 0) { free(c->c_ssh); fprintf(stderr, "kex_setup: %s\n", ssh_err(r)); exit(1); } #ifdef WITH_OPENSSL c->c_ssh->kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; c->c_ssh->kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; c->c_ssh->kex->kex[KEX_DH_GRP14_SHA256] = kexdh_client; c->c_ssh->kex->kex[KEX_DH_GRP16_SHA512] = kexdh_client; c->c_ssh->kex->kex[KEX_DH_GRP18_SHA512] = kexdh_client; c->c_ssh->kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; c->c_ssh->kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; # ifdef OPENSSL_HAS_ECC c->c_ssh->kex->kex[KEX_ECDH_SHA2] = kexecdh_client; # endif #endif c->c_ssh->kex->kex[KEX_C25519_SHA256] = kexc25519_client; ssh_set_verify_host_key_callback(c->c_ssh, key_print_wrapper); /* * do the key-exchange until an error occurs or until * the key_print_wrapper() callback sets c_done. */ ssh_dispatch_run(c->c_ssh, DISPATCH_BLOCK, &c->c_done, c->c_ssh); } static void -keyprint_one(char *host, struct sshkey *key) +keyprint_one(const char *host, struct sshkey *key) { char *hostport; - - if (hash_hosts && (host = host_hash(host, NULL, 0)) == NULL) - fatal("host_hash failed"); + const char *known_host, *hashed; hostport = put_host_port(host, ssh_port); + lowercase(hostport); + if (hash_hosts && (hashed = host_hash(host, NULL, 0)) == NULL) + fatal("host_hash failed"); + known_host = hash_hosts ? hashed : hostport; if (!get_cert) - fprintf(stdout, "%s ", hostport); + fprintf(stdout, "%s ", known_host); sshkey_write(key, stdout); fputs("\n", stdout); free(hostport); } static void keyprint(con *c, struct sshkey *key) { char *hosts = c->c_output_name ? c->c_output_name : c->c_name; char *host, *ohosts; if (key == NULL) return; if (get_cert || (!hash_hosts && ssh_port == SSH_DEFAULT_PORT)) { keyprint_one(hosts, key); return; } ohosts = hosts = xstrdup(hosts); while ((host = strsep(&hosts, ",")) != NULL) keyprint_one(host, key); free(ohosts); } static int tcpconnect(char *host) { struct addrinfo hints, *ai, *aitop; char strport[NI_MAXSERV]; int gaierr, s = -1; snprintf(strport, sizeof strport, "%d", ssh_port); memset(&hints, 0, sizeof(hints)); hints.ai_family = IPv4or6; hints.ai_socktype = SOCK_STREAM; if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) { error("getaddrinfo %s: %s", host, ssh_gai_strerror(gaierr)); return -1; } for (ai = aitop; ai; ai = ai->ai_next) { s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (s < 0) { error("socket: %s", strerror(errno)); continue; } if (set_nonblock(s) == -1) fatal("%s: set_nonblock(%d)", __func__, s); if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0 && errno != EINPROGRESS) error("connect (`%s'): %s", host, strerror(errno)); else break; close(s); s = -1; } freeaddrinfo(aitop); return s; } static int conalloc(char *iname, char *oname, int keytype) { char *namebase, *name, *namelist; int s; namebase = namelist = xstrdup(iname); do { name = xstrsep(&namelist, ","); if (!name) { free(namebase); return (-1); } } while ((s = tcpconnect(name)) < 0); if (s >= maxfd) fatal("conalloc: fdno %d too high", s); if (fdcon[s].c_status) fatal("conalloc: attempt to reuse fdno %d", s); debug3("%s: oname %s kt %d", __func__, oname, keytype); fdcon[s].c_fd = s; fdcon[s].c_status = CS_CON; fdcon[s].c_namebase = namebase; fdcon[s].c_name = name; fdcon[s].c_namelist = namelist; fdcon[s].c_output_name = xstrdup(oname); fdcon[s].c_data = (char *) &fdcon[s].c_plen; fdcon[s].c_len = 4; fdcon[s].c_off = 0; fdcon[s].c_keytype = keytype; gettimeofday(&fdcon[s].c_tv, NULL); fdcon[s].c_tv.tv_sec += timeout; TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); FD_SET(s, read_wait); ncon++; return (s); } static void confree(int s) { if (s >= maxfd || fdcon[s].c_status == CS_UNUSED) fatal("confree: attempt to free bad fdno %d", s); close(s); free(fdcon[s].c_namebase); free(fdcon[s].c_output_name); if (fdcon[s].c_status == CS_KEYS) free(fdcon[s].c_data); fdcon[s].c_status = CS_UNUSED; fdcon[s].c_keytype = 0; if (fdcon[s].c_ssh) { ssh_packet_close(fdcon[s].c_ssh); free(fdcon[s].c_ssh); fdcon[s].c_ssh = NULL; } TAILQ_REMOVE(&tq, &fdcon[s], c_link); FD_CLR(s, read_wait); ncon--; } static void contouch(int s) { TAILQ_REMOVE(&tq, &fdcon[s], c_link); gettimeofday(&fdcon[s].c_tv, NULL); fdcon[s].c_tv.tv_sec += timeout; TAILQ_INSERT_TAIL(&tq, &fdcon[s], c_link); } static int conrecycle(int s) { con *c = &fdcon[s]; int ret; ret = conalloc(c->c_namelist, c->c_output_name, c->c_keytype); confree(s); return (ret); } static void congreet(int s) { int n = 0, remote_major = 0, remote_minor = 0; char buf[256], *cp; char remote_version[sizeof buf]; size_t bufsiz; con *c = &fdcon[s]; for (;;) { memset(buf, '\0', sizeof(buf)); bufsiz = sizeof(buf); cp = buf; while (bufsiz-- && (n = atomicio(read, s, cp, 1)) == 1 && *cp != '\n') { if (*cp == '\r') *cp = '\n'; cp++; } if (n != 1 || strncmp(buf, "SSH-", 4) == 0) break; } if (n == 0) { switch (errno) { case EPIPE: error("%s: Connection closed by remote host", c->c_name); break; case ECONNREFUSED: break; default: error("read (%s): %s", c->c_name, strerror(errno)); break; } conrecycle(s); return; } if (*cp != '\n' && *cp != '\r') { error("%s: bad greeting", c->c_name); confree(s); return; } *cp = '\0'; if ((c->c_ssh = ssh_packet_set_connection(NULL, s, s)) == NULL) fatal("ssh_packet_set_connection failed"); ssh_packet_set_timeout(c->c_ssh, timeout, 1); ssh_set_app_data(c->c_ssh, c); /* back link */ if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, remote_version) == 3) c->c_ssh->compat = compat_datafellows(remote_version); else c->c_ssh->compat = 0; if (c->c_keytype != KT_RSA1) { if (!ssh2_capable(remote_major, remote_minor)) { debug("%s doesn't support ssh2", c->c_name); confree(s); return; } } else if (remote_major != 1) { debug("%s doesn't support ssh1", c->c_name); confree(s); return; } fprintf(stderr, "# %s:%d %s\n", c->c_name, ssh_port, chop(buf)); n = snprintf(buf, sizeof buf, "SSH-%d.%d-OpenSSH-keyscan\r\n", c->c_keytype == KT_RSA1? PROTOCOL_MAJOR_1 : PROTOCOL_MAJOR_2, c->c_keytype == KT_RSA1? PROTOCOL_MINOR_1 : PROTOCOL_MINOR_2); if (n < 0 || (size_t)n >= sizeof(buf)) { error("snprintf: buffer too small"); confree(s); return; } if (atomicio(vwrite, s, buf, n) != (size_t)n) { error("write (%s): %s", c->c_name, strerror(errno)); confree(s); return; } if (c->c_keytype != KT_RSA1) { keygrab_ssh2(c); confree(s); return; } c->c_status = CS_SIZE; contouch(s); } static void conread(int s) { con *c = &fdcon[s]; size_t n; if (c->c_status == CS_CON) { congreet(s); return; } n = atomicio(read, s, c->c_data + c->c_off, c->c_len - c->c_off); if (n == 0) { error("read (%s): %s", c->c_name, strerror(errno)); confree(s); return; } c->c_off += n; if (c->c_off == c->c_len) switch (c->c_status) { case CS_SIZE: c->c_plen = htonl(c->c_plen); c->c_len = c->c_plen + 8 - (c->c_plen & 7); c->c_off = 0; c->c_data = xmalloc(c->c_len); c->c_status = CS_KEYS; break; #ifdef WITH_SSH1 case CS_KEYS: keyprint(c, keygrab_ssh1(c)); confree(s); return; #endif default: fatal("conread: invalid status %d", c->c_status); break; } contouch(s); } static void conloop(void) { struct timeval seltime, now; fd_set *r, *e; con *c; int i; gettimeofday(&now, NULL); c = TAILQ_FIRST(&tq); if (c && (c->c_tv.tv_sec > now.tv_sec || (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec > now.tv_usec))) { seltime = c->c_tv; seltime.tv_sec -= now.tv_sec; seltime.tv_usec -= now.tv_usec; if (seltime.tv_usec < 0) { seltime.tv_usec += 1000000; seltime.tv_sec--; } } else timerclear(&seltime); r = xcalloc(read_wait_nfdset, sizeof(fd_mask)); e = xcalloc(read_wait_nfdset, sizeof(fd_mask)); memcpy(r, read_wait, read_wait_nfdset * sizeof(fd_mask)); memcpy(e, read_wait, read_wait_nfdset * sizeof(fd_mask)); while (select(maxfd, r, NULL, e, &seltime) == -1 && (errno == EAGAIN || errno == EINTR || errno == EWOULDBLOCK)) ; for (i = 0; i < maxfd; i++) { if (FD_ISSET(i, e)) { error("%s: exception!", fdcon[i].c_name); confree(i); } else if (FD_ISSET(i, r)) conread(i); } free(r); free(e); c = TAILQ_FIRST(&tq); while (c && (c->c_tv.tv_sec < now.tv_sec || (c->c_tv.tv_sec == now.tv_sec && c->c_tv.tv_usec < now.tv_usec))) { int s = c->c_fd; c = TAILQ_NEXT(c, c_link); conrecycle(s); } } static void do_host(char *host) { char *name = strnnsep(&host, " \t\n"); int j; if (name == NULL) return; for (j = KT_RSA1; j <= KT_ED25519; j *= 2) { if (get_keytypes & j) { while (ncon >= MAXCON) conloop(); conalloc(name, *host ? host : name, j); } } } void fatal(const char *fmt,...) { va_list args; va_start(args, fmt); do_log(SYSLOG_LEVEL_FATAL, fmt, args); va_end(args); exit(255); } static void usage(void) { fprintf(stderr, "usage: %s [-46cHv] [-f file] [-p port] [-T timeout] [-t type]\n" "\t\t [host | addrlist namelist] ...\n", __progname); exit(1); } int main(int argc, char **argv) { int debug_flag = 0, log_level = SYSLOG_LEVEL_INFO; int opt, fopt_count = 0, j; char *tname, *cp, line[NI_MAXHOST]; FILE *fp; u_long linenum; extern int optind; extern char *optarg; ssh_malloc_init(); /* must be called before any mallocs */ __progname = ssh_get_progname(argv[0]); seed_rng(); TAILQ_INIT(&tq); /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); if (argc <= 1) usage(); while ((opt = getopt(argc, argv, "cHv46p:T:t:f:")) != -1) { switch (opt) { case 'H': hash_hosts = 1; break; case 'c': get_cert = 1; break; case 'p': ssh_port = a2port(optarg); if (ssh_port <= 0) { fprintf(stderr, "Bad port '%s'\n", optarg); exit(1); } break; case 'T': timeout = convtime(optarg); if (timeout == -1 || timeout == 0) { fprintf(stderr, "Bad timeout '%s'\n", optarg); usage(); } break; case 'v': if (!debug_flag) { debug_flag = 1; log_level = SYSLOG_LEVEL_DEBUG1; } else if (log_level < SYSLOG_LEVEL_DEBUG3) log_level++; else fatal("Too high debugging level."); break; case 'f': if (strcmp(optarg, "-") == 0) optarg = NULL; argv[fopt_count++] = optarg; break; case 't': get_keytypes = 0; tname = strtok(optarg, ","); while (tname) { int type = sshkey_type_from_name(tname); + switch (type) { +#ifdef WITH_SSH1 case KEY_RSA1: get_keytypes |= KT_RSA1; break; +#endif case KEY_DSA: get_keytypes |= KT_DSA; break; case KEY_ECDSA: get_keytypes |= KT_ECDSA; break; case KEY_RSA: get_keytypes |= KT_RSA; break; case KEY_ED25519: get_keytypes |= KT_ED25519; break; case KEY_UNSPEC: - fatal("unknown key type %s", tname); + default: + fatal("Unknown key type \"%s\"", tname); } tname = strtok(NULL, ","); } break; case '4': IPv4or6 = AF_INET; break; case '6': IPv4or6 = AF_INET6; break; case '?': default: usage(); } } if (optind == argc && !fopt_count) usage(); log_init("ssh-keyscan", log_level, SYSLOG_FACILITY_USER, 1); maxfd = fdlim_get(1); if (maxfd < 0) fatal("%s: fdlim_get: bad value", __progname); if (maxfd > MAXMAXFD) maxfd = MAXMAXFD; if (MAXCON <= 0) fatal("%s: not enough file descriptors", __progname); if (maxfd > fdlim_get(0)) fdlim_set(maxfd); fdcon = xcalloc(maxfd, sizeof(con)); read_wait_nfdset = howmany(maxfd, NFDBITS); read_wait = xcalloc(read_wait_nfdset, sizeof(fd_mask)); for (j = 0; j < fopt_count; j++) { if (argv[j] == NULL) fp = stdin; else if ((fp = fopen(argv[j], "r")) == NULL) fatal("%s: %s: %s", __progname, argv[j], strerror(errno)); linenum = 0; while (read_keyfile_line(fp, argv[j] == NULL ? "(stdin)" : argv[j], line, sizeof(line), &linenum) != -1) { /* Chomp off trailing whitespace and comments */ if ((cp = strchr(line, '#')) == NULL) cp = line + strlen(line) - 1; while (cp >= line) { if (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '#') *cp-- = '\0'; else break; } /* Skip empty lines */ if (*line == '\0') continue; do_host(line); } if (ferror(fp)) fatal("%s: %s: %s", __progname, argv[j], strerror(errno)); fclose(fp); } while (optind < argc) do_host(argv[optind++]); while (ncon > 0) conloop(); return (0); } diff --git a/crypto/openssh/ssh.c b/crypto/openssh/ssh.c index 445c0d5d0ca3..05afc3cf9455 100644 --- a/crypto/openssh/ssh.c +++ b/crypto/openssh/ssh.c @@ -1,2209 +1,2209 @@ -/* $OpenBSD: ssh.c,v 1.448 2016/12/06 07:48:01 djm Exp $ */ +/* $OpenBSD: ssh.c,v 1.451 2017/03/10 04:07:20 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Ssh client program. This program can be used to log into a remote machine. * The software supports strong authentication, encryption, and forwarding * of X11, TCP/IP, and authentication connections. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * Copyright (c) 1999 Niels Provos. All rights reserved. * Copyright (c) 2000, 2001, 2002, 2003 Markus Friedl. All rights reserved. * * Modified to work with SSL by Niels Provos * in Canada (German citizen). * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" __RCSID("$FreeBSD$"); #include #ifdef HAVE_SYS_STAT_H # include #endif #include #include #include #include #include #include #include #include #ifdef HAVE_PATHS_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifdef WITH_OPENSSL #include #include #endif #include "openbsd-compat/openssl-compat.h" #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "ssh.h" #include "ssh1.h" #include "ssh2.h" #include "canohost.h" #include "compat.h" #include "cipher.h" #include "digest.h" #include "packet.h" #include "buffer.h" #include "channels.h" #include "key.h" #include "authfd.h" #include "authfile.h" #include "pathnames.h" #include "dispatch.h" #include "clientloop.h" #include "log.h" #include "misc.h" #include "readconf.h" #include "sshconnect.h" #include "kex.h" #include "mac.h" #include "sshpty.h" #include "match.h" #include "msg.h" #include "uidswap.h" #include "version.h" #include "ssherr.h" #include "myproposal.h" #include "utf8.h" #ifdef ENABLE_PKCS11 #include "ssh-pkcs11.h" #endif extern char *__progname; /* Saves a copy of argv for setproctitle emulation */ #ifndef HAVE_SETPROCTITLE static char **saved_av; #endif /* Flag indicating whether debug mode is on. May be set on the command line. */ int debug_flag = 0; /* Flag indicating whether a tty should be requested */ int tty_flag = 0; /* don't exec a shell */ int no_shell_flag = 0; /* * Flag indicating that nothing should be read from stdin. This can be set * on the command line. */ int stdin_null_flag = 0; /* * Flag indicating that the current process should be backgrounded and * a new slave launched in the foreground for ControlPersist. */ int need_controlpersist_detach = 0; /* Copies of flags for ControlPersist foreground slave */ int ostdin_null_flag, ono_shell_flag, otty_flag, orequest_tty; /* * Flag indicating that ssh should fork after authentication. This is useful * so that the passphrase can be entered manually, and then ssh goes to the * background. */ int fork_after_authentication_flag = 0; /* * General data structure for command line options and options configurable * in configuration files. See readconf.h. */ Options options; /* optional user configfile */ char *config = NULL; /* * Name of the host we are connecting to. This is the name given on the * command line, or the HostName specified for the user-supplied name in a * configuration file. */ char *host; /* socket address the host resolves to */ struct sockaddr_storage hostaddr; /* Private host keys. */ Sensitive sensitive_data; /* Original real UID. */ uid_t original_real_uid; uid_t original_effective_uid; /* command to be executed */ Buffer command; /* Should we execute a command or invoke a subsystem? */ int subsystem_flag = 0; /* # of replies received for global requests */ static int remote_forward_confirms_received = 0; /* mux.c */ extern int muxserver_sock; extern u_int muxclient_command; /* Prints a help message to the user. This function never returns. */ static void usage(void) { fprintf(stderr, "usage: ssh [-1246AaCfGgKkMNnqsTtVvXxYy] [-b bind_address] [-c cipher_spec]\n" " [-D [bind_address:]port] [-E log_file] [-e escape_char]\n" " [-F configfile] [-I pkcs11] [-i identity_file]\n" " [-J [user@]host[:port]] [-L address] [-l login_name] [-m mac_spec]\n" " [-O ctl_cmd] [-o option] [-p port] [-Q query_option] [-R address]\n" " [-S ctl_path] [-W host:port] [-w local_tun[:remote_tun]]\n" " [user@]hostname [command]\n" ); exit(255); } static int ssh_session(void); static int ssh_session2(void); static void load_public_identity_files(void); static void main_sigchld_handler(int); /* ~/ expand a list of paths. NB. assumes path[n] is heap-allocated. */ static void tilde_expand_paths(char **paths, u_int num_paths) { u_int i; char *cp; for (i = 0; i < num_paths; i++) { cp = tilde_expand_filename(paths[i], original_real_uid); free(paths[i]); paths[i] = cp; } } /* * Attempt to resolve a host name / port to a set of addresses and * optionally return any CNAMEs encountered along the way. * Returns NULL on failure. * NB. this function must operate with a options having undefined members. */ static struct addrinfo * resolve_host(const char *name, int port, int logerr, char *cname, size_t clen) { char strport[NI_MAXSERV]; struct addrinfo hints, *res; int gaierr, loglevel = SYSLOG_LEVEL_DEBUG1; if (port <= 0) port = default_ssh_port(); snprintf(strport, sizeof strport, "%d", port); memset(&hints, 0, sizeof(hints)); hints.ai_family = options.address_family == -1 ? AF_UNSPEC : options.address_family; hints.ai_socktype = SOCK_STREAM; if (cname != NULL) hints.ai_flags = AI_CANONNAME; if ((gaierr = getaddrinfo(name, strport, &hints, &res)) != 0) { if (logerr || (gaierr != EAI_NONAME && gaierr != EAI_NODATA)) loglevel = SYSLOG_LEVEL_ERROR; do_log2(loglevel, "%s: Could not resolve hostname %.100s: %s", __progname, name, ssh_gai_strerror(gaierr)); return NULL; } if (cname != NULL && res->ai_canonname != NULL) { if (strlcpy(cname, res->ai_canonname, clen) >= clen) { error("%s: host \"%s\" cname \"%s\" too long (max %lu)", __func__, name, res->ai_canonname, (u_long)clen); if (clen > 0) *cname = '\0'; } } return res; } /* * Attempt to resolve a numeric host address / port to a single address. * Returns a canonical address string. * Returns NULL on failure. * NB. this function must operate with a options having undefined members. */ static struct addrinfo * resolve_addr(const char *name, int port, char *caddr, size_t clen) { char addr[NI_MAXHOST], strport[NI_MAXSERV]; struct addrinfo hints, *res; int gaierr; if (port <= 0) port = default_ssh_port(); snprintf(strport, sizeof strport, "%u", port); memset(&hints, 0, sizeof(hints)); hints.ai_family = options.address_family == -1 ? AF_UNSPEC : options.address_family; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICHOST|AI_NUMERICSERV; if ((gaierr = getaddrinfo(name, strport, &hints, &res)) != 0) { debug2("%s: could not resolve name %.100s as address: %s", __func__, name, ssh_gai_strerror(gaierr)); return NULL; } if (res == NULL) { debug("%s: getaddrinfo %.100s returned no addresses", __func__, name); return NULL; } if (res->ai_next != NULL) { debug("%s: getaddrinfo %.100s returned multiple addresses", __func__, name); goto fail; } if ((gaierr = getnameinfo(res->ai_addr, res->ai_addrlen, addr, sizeof(addr), NULL, 0, NI_NUMERICHOST)) != 0) { debug("%s: Could not format address for name %.100s: %s", __func__, name, ssh_gai_strerror(gaierr)); goto fail; } if (strlcpy(caddr, addr, clen) >= clen) { error("%s: host \"%s\" addr \"%s\" too long (max %lu)", __func__, name, addr, (u_long)clen); if (clen > 0) *caddr = '\0'; fail: freeaddrinfo(res); return NULL; } return res; } /* * Check whether the cname is a permitted replacement for the hostname * and perform the replacement if it is. * NB. this function must operate with a options having undefined members. */ static int check_follow_cname(int direct, char **namep, const char *cname) { int i; struct allowed_cname *rule; if (*cname == '\0' || options.num_permitted_cnames == 0 || strcmp(*namep, cname) == 0) return 0; if (options.canonicalize_hostname == SSH_CANONICALISE_NO) return 0; /* * Don't attempt to canonicalize names that will be interpreted by * a proxy or jump host unless the user specifically requests so. */ if (!direct && options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS) return 0; debug3("%s: check \"%s\" CNAME \"%s\"", __func__, *namep, cname); for (i = 0; i < options.num_permitted_cnames; i++) { rule = options.permitted_cnames + i; if (match_pattern_list(*namep, rule->source_list, 1) != 1 || match_pattern_list(cname, rule->target_list, 1) != 1) continue; verbose("Canonicalized DNS aliased hostname " "\"%s\" => \"%s\"", *namep, cname); free(*namep); *namep = xstrdup(cname); return 1; } return 0; } /* * Attempt to resolve the supplied hostname after applying the user's * canonicalization rules. Returns the address list for the host or NULL * if no name was found after canonicalization. * NB. this function must operate with a options having undefined members. */ static struct addrinfo * resolve_canonicalize(char **hostp, int port) { int i, direct, ndots; char *cp, *fullhost, newname[NI_MAXHOST]; struct addrinfo *addrs; if (options.canonicalize_hostname == SSH_CANONICALISE_NO) return NULL; /* * Don't attempt to canonicalize names that will be interpreted by * a proxy unless the user specifically requests so. */ direct = option_clear_or_none(options.proxy_command) && options.jump_host == NULL; if (!direct && options.canonicalize_hostname != SSH_CANONICALISE_ALWAYS) return NULL; /* Try numeric hostnames first */ if ((addrs = resolve_addr(*hostp, port, newname, sizeof(newname))) != NULL) { debug2("%s: hostname %.100s is address", __func__, *hostp); if (strcasecmp(*hostp, newname) != 0) { debug2("%s: canonicalised address \"%s\" => \"%s\"", __func__, *hostp, newname); free(*hostp); *hostp = xstrdup(newname); } return addrs; } /* If domain name is anchored, then resolve it now */ if ((*hostp)[strlen(*hostp) - 1] == '.') { debug3("%s: name is fully qualified", __func__); fullhost = xstrdup(*hostp); if ((addrs = resolve_host(fullhost, port, 0, newname, sizeof(newname))) != NULL) goto found; free(fullhost); goto notfound; } /* Don't apply canonicalization to sufficiently-qualified hostnames */ ndots = 0; for (cp = *hostp; *cp != '\0'; cp++) { if (*cp == '.') ndots++; } if (ndots > options.canonicalize_max_dots) { debug3("%s: not canonicalizing hostname \"%s\" (max dots %d)", __func__, *hostp, options.canonicalize_max_dots); return NULL; } /* Attempt each supplied suffix */ for (i = 0; i < options.num_canonical_domains; i++) { *newname = '\0'; xasprintf(&fullhost, "%s.%s.", *hostp, options.canonical_domains[i]); debug3("%s: attempting \"%s\" => \"%s\"", __func__, *hostp, fullhost); if ((addrs = resolve_host(fullhost, port, 0, newname, sizeof(newname))) == NULL) { free(fullhost); continue; } found: /* Remove trailing '.' */ fullhost[strlen(fullhost) - 1] = '\0'; /* Follow CNAME if requested */ if (!check_follow_cname(direct, &fullhost, newname)) { debug("Canonicalized hostname \"%s\" => \"%s\"", *hostp, fullhost); } free(*hostp); *hostp = fullhost; return addrs; } notfound: if (!options.canonicalize_fallback_local) fatal("%s: Could not resolve host \"%s\"", __progname, *hostp); debug2("%s: host %s not found in any suffix", __func__, *hostp); return NULL; } /* * Read per-user configuration file. Ignore the system wide config * file if the user specifies a config file on the command line. */ static void process_config_files(const char *host_arg, struct passwd *pw, int post_canon) { char buf[PATH_MAX]; int r; if (config != NULL) { if (strcasecmp(config, "none") != 0 && !read_config_file(config, pw, host, host_arg, &options, SSHCONF_USERCONF | (post_canon ? SSHCONF_POSTCANON : 0))) fatal("Can't open user config file %.100s: " "%.100s", config, strerror(errno)); } else { r = snprintf(buf, sizeof buf, "%s/%s", pw->pw_dir, _PATH_SSH_USER_CONFFILE); if (r > 0 && (size_t)r < sizeof(buf)) (void)read_config_file(buf, pw, host, host_arg, &options, SSHCONF_CHECKPERM | SSHCONF_USERCONF | (post_canon ? SSHCONF_POSTCANON : 0)); /* Read systemwide configuration file after user config. */ (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, host, host_arg, &options, post_canon ? SSHCONF_POSTCANON : 0); } } /* Rewrite the port number in an addrinfo list of addresses */ static void set_addrinfo_port(struct addrinfo *addrs, int port) { struct addrinfo *addr; for (addr = addrs; addr != NULL; addr = addr->ai_next) { switch (addr->ai_family) { case AF_INET: ((struct sockaddr_in *)addr->ai_addr)-> sin_port = htons(port); break; case AF_INET6: ((struct sockaddr_in6 *)addr->ai_addr)-> sin6_port = htons(port); break; } } } /* * Main program for the ssh client. */ int main(int ac, char **av) { struct ssh *ssh = NULL; int i, r, opt, exit_status, use_syslog, direct, config_test = 0; char *p, *cp, *line, *argv0, buf[PATH_MAX], *host_arg, *logfile; char thishost[NI_MAXHOST], shorthost[NI_MAXHOST], portstr[NI_MAXSERV]; char cname[NI_MAXHOST], uidstr[32], *conn_hash_hex; struct stat st; struct passwd *pw; int timeout_ms; extern int optind, optreset; extern char *optarg; struct Forward fwd; struct addrinfo *addrs = NULL; struct ssh_digest_ctx *md; u_char conn_hash[SSH_DIGEST_MAX_LENGTH]; ssh_malloc_init(); /* must be called before any mallocs */ /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); __progname = ssh_get_progname(av[0]); #ifndef HAVE_SETPROCTITLE /* Prepare for later setproctitle emulation */ /* Save argv so it isn't clobbered by setproctitle() emulation */ saved_av = xcalloc(ac + 1, sizeof(*saved_av)); for (i = 0; i < ac; i++) saved_av[i] = xstrdup(av[i]); saved_av[i] = NULL; compat_init_setproctitle(ac, av); av = saved_av; #endif /* * Discard other fds that are hanging around. These can cause problem * with backgrounded ssh processes started by ControlPersist. */ closefrom(STDERR_FILENO + 1); /* * Save the original real uid. It will be needed later (uid-swapping * may clobber the real uid). */ original_real_uid = getuid(); original_effective_uid = geteuid(); /* * Use uid-swapping to give up root privileges for the duration of * option processing. We will re-instantiate the rights when we are * ready to create the privileged port, and will permanently drop * them when the port has been created (actually, when the connection * has been made, as we may need to create the port several times). */ PRIV_END; #ifdef HAVE_SETRLIMIT /* If we are installed setuid root be careful to not drop core. */ if (original_real_uid != original_effective_uid) { struct rlimit rlim; rlim.rlim_cur = rlim.rlim_max = 0; if (setrlimit(RLIMIT_CORE, &rlim) < 0) fatal("setrlimit failed: %.100s", strerror(errno)); } #endif /* Get user data. */ pw = getpwuid(original_real_uid); if (!pw) { logit("No user exists for uid %lu", (u_long)original_real_uid); exit(255); } /* Take a copy of the returned structure. */ pw = pwcopy(pw); /* * Set our umask to something reasonable, as some files are created * with the default umask. This will make them world-readable but * writable only by the owner, which is ok for all files for which we * don't set the modes explicitly. */ umask(022); msetlocale(); /* * Initialize option structure to indicate that no values have been * set. */ initialize_options(&options); /* Parse command-line arguments. */ host = NULL; use_syslog = 0; logfile = NULL; argv0 = av[0]; again: while ((opt = getopt(ac, av, "1246ab:c:e:fgi:kl:m:no:p:qstvx" "ACD:E:F:GI:J:KL:MNO:PQ:R:S:TVw:W:XYy")) != -1) { switch (opt) { case '1': options.protocol = SSH_PROTO_1; break; case '2': options.protocol = SSH_PROTO_2; break; case '4': options.address_family = AF_INET; break; case '6': options.address_family = AF_INET6; break; case 'n': stdin_null_flag = 1; break; case 'f': fork_after_authentication_flag = 1; stdin_null_flag = 1; break; case 'x': options.forward_x11 = 0; break; case 'X': options.forward_x11 = 1; break; case 'y': use_syslog = 1; break; case 'E': logfile = optarg; break; case 'G': config_test = 1; break; case 'Y': options.forward_x11 = 1; options.forward_x11_trusted = 1; break; case 'g': options.fwd_opts.gateway_ports = 1; break; case 'O': if (options.stdio_forward_host != NULL) fatal("Cannot specify multiplexing " "command with -W"); else if (muxclient_command != 0) fatal("Multiplexing command already specified"); if (strcmp(optarg, "check") == 0) muxclient_command = SSHMUX_COMMAND_ALIVE_CHECK; else if (strcmp(optarg, "forward") == 0) muxclient_command = SSHMUX_COMMAND_FORWARD; else if (strcmp(optarg, "exit") == 0) muxclient_command = SSHMUX_COMMAND_TERMINATE; else if (strcmp(optarg, "stop") == 0) muxclient_command = SSHMUX_COMMAND_STOP; else if (strcmp(optarg, "cancel") == 0) muxclient_command = SSHMUX_COMMAND_CANCEL_FWD; else if (strcmp(optarg, "proxy") == 0) muxclient_command = SSHMUX_COMMAND_PROXY; else fatal("Invalid multiplex command."); break; case 'P': /* deprecated */ options.use_privileged_port = 0; break; case 'Q': cp = NULL; if (strcmp(optarg, "cipher") == 0) cp = cipher_alg_list('\n', 0); else if (strcmp(optarg, "cipher-auth") == 0) cp = cipher_alg_list('\n', 1); else if (strcmp(optarg, "mac") == 0) cp = mac_alg_list('\n'); else if (strcmp(optarg, "kex") == 0) cp = kex_alg_list('\n'); else if (strcmp(optarg, "key") == 0) - cp = sshkey_alg_list(0, 0, '\n'); + cp = sshkey_alg_list(0, 0, 0, '\n'); else if (strcmp(optarg, "key-cert") == 0) - cp = sshkey_alg_list(1, 0, '\n'); + cp = sshkey_alg_list(1, 0, 0, '\n'); else if (strcmp(optarg, "key-plain") == 0) - cp = sshkey_alg_list(0, 1, '\n'); + cp = sshkey_alg_list(0, 1, 0, '\n'); else if (strcmp(optarg, "protocol-version") == 0) { #ifdef WITH_SSH1 cp = xstrdup("1\n2"); #else cp = xstrdup("2"); #endif } if (cp == NULL) fatal("Unsupported query \"%s\"", optarg); printf("%s\n", cp); free(cp); exit(0); break; case 'a': options.forward_agent = 0; break; case 'A': options.forward_agent = 1; break; case 'k': options.gss_deleg_creds = 0; break; case 'K': options.gss_authentication = 1; options.gss_deleg_creds = 1; break; case 'i': p = tilde_expand_filename(optarg, original_real_uid); if (stat(p, &st) < 0) fprintf(stderr, "Warning: Identity file %s " "not accessible: %s.\n", p, strerror(errno)); else add_identity_file(&options, NULL, p, 1); free(p); break; case 'I': #ifdef ENABLE_PKCS11 free(options.pkcs11_provider); options.pkcs11_provider = xstrdup(optarg); #else fprintf(stderr, "no support for PKCS#11.\n"); #endif break; case 'J': if (options.jump_host != NULL) fatal("Only a single -J option permitted"); if (options.proxy_command != NULL) fatal("Cannot specify -J with ProxyCommand"); if (parse_jump(optarg, &options, 1) == -1) fatal("Invalid -J argument"); options.proxy_command = xstrdup("none"); break; case 't': if (options.request_tty == REQUEST_TTY_YES) options.request_tty = REQUEST_TTY_FORCE; else options.request_tty = REQUEST_TTY_YES; break; case 'v': if (debug_flag == 0) { debug_flag = 1; options.log_level = SYSLOG_LEVEL_DEBUG1; } else { if (options.log_level < SYSLOG_LEVEL_DEBUG3) { debug_flag++; options.log_level++; } } break; case 'V': if (options.version_addendum && *options.version_addendum != '\0') fprintf(stderr, "%s %s, %s\n", SSH_RELEASE, options.version_addendum, OPENSSL_VERSION); else fprintf(stderr, "%s, %s\n", SSH_RELEASE, OPENSSL_VERSION); if (opt == 'V') exit(0); break; case 'w': if (options.tun_open == -1) options.tun_open = SSH_TUNMODE_DEFAULT; options.tun_local = a2tun(optarg, &options.tun_remote); if (options.tun_local == SSH_TUNID_ERR) { fprintf(stderr, "Bad tun device '%s'\n", optarg); exit(255); } break; case 'W': if (options.stdio_forward_host != NULL) fatal("stdio forward already specified"); if (muxclient_command != 0) fatal("Cannot specify stdio forward with -O"); if (parse_forward(&fwd, optarg, 1, 0)) { options.stdio_forward_host = fwd.listen_host; options.stdio_forward_port = fwd.listen_port; free(fwd.connect_host); } else { fprintf(stderr, "Bad stdio forwarding specification '%s'\n", optarg); exit(255); } options.request_tty = REQUEST_TTY_NO; no_shell_flag = 1; break; case 'q': options.log_level = SYSLOG_LEVEL_QUIET; break; case 'e': if (optarg[0] == '^' && optarg[2] == 0 && (u_char) optarg[1] >= 64 && (u_char) optarg[1] < 128) options.escape_char = (u_char) optarg[1] & 31; else if (strlen(optarg) == 1) options.escape_char = (u_char) optarg[0]; else if (strcmp(optarg, "none") == 0) options.escape_char = SSH_ESCAPECHAR_NONE; else { fprintf(stderr, "Bad escape character '%s'.\n", optarg); exit(255); } break; case 'c': if (ciphers_valid(*optarg == '+' ? optarg + 1 : optarg)) { /* SSH2 only */ free(options.ciphers); options.ciphers = xstrdup(optarg); options.cipher = SSH_CIPHER_INVALID; break; } /* SSH1 only */ options.cipher = cipher_number(optarg); if (options.cipher == -1) { fprintf(stderr, "Unknown cipher type '%s'\n", optarg); exit(255); } if (options.cipher == SSH_CIPHER_3DES) options.ciphers = xstrdup("3des-cbc"); else if (options.cipher == SSH_CIPHER_BLOWFISH) options.ciphers = xstrdup("blowfish-cbc"); else options.ciphers = xstrdup(KEX_CLIENT_ENCRYPT); break; case 'm': if (mac_valid(optarg)) { free(options.macs); options.macs = xstrdup(optarg); } else { fprintf(stderr, "Unknown mac type '%s'\n", optarg); exit(255); } break; case 'M': if (options.control_master == SSHCTL_MASTER_YES) options.control_master = SSHCTL_MASTER_ASK; else options.control_master = SSHCTL_MASTER_YES; break; case 'p': options.port = a2port(optarg); if (options.port <= 0) { fprintf(stderr, "Bad port '%s'\n", optarg); exit(255); } break; case 'l': options.user = optarg; break; case 'L': if (parse_forward(&fwd, optarg, 0, 0)) add_local_forward(&options, &fwd); else { fprintf(stderr, "Bad local forwarding specification '%s'\n", optarg); exit(255); } break; case 'R': if (parse_forward(&fwd, optarg, 0, 1)) { add_remote_forward(&options, &fwd); } else { fprintf(stderr, "Bad remote forwarding specification " "'%s'\n", optarg); exit(255); } break; case 'D': if (parse_forward(&fwd, optarg, 1, 0)) { add_local_forward(&options, &fwd); } else { fprintf(stderr, "Bad dynamic forwarding specification " "'%s'\n", optarg); exit(255); } break; case 'C': options.compression = 1; break; case 'N': no_shell_flag = 1; options.request_tty = REQUEST_TTY_NO; break; case 'T': options.request_tty = REQUEST_TTY_NO; break; case 'o': line = xstrdup(optarg); if (process_config_line(&options, pw, host ? host : "", host ? host : "", line, "command-line", 0, NULL, SSHCONF_USERCONF) != 0) exit(255); free(line); break; case 's': subsystem_flag = 1; break; case 'S': free(options.control_path); options.control_path = xstrdup(optarg); break; case 'b': options.bind_address = optarg; break; case 'F': config = optarg; break; default: usage(); } } ac -= optind; av += optind; if (ac > 0 && !host) { if (strrchr(*av, '@')) { p = xstrdup(*av); cp = strrchr(p, '@'); if (cp == NULL || cp == p) usage(); options.user = p; *cp = '\0'; host = xstrdup(++cp); } else host = xstrdup(*av); if (ac > 1) { optind = optreset = 1; goto again; } ac--, av++; } /* Check that we got a host name. */ if (!host) usage(); host_arg = xstrdup(host); #ifdef WITH_OPENSSL OpenSSL_add_all_algorithms(); ERR_load_crypto_strings(); #endif /* Initialize the command to execute on remote host. */ buffer_init(&command); /* * Save the command to execute on the remote host in a buffer. There * is no limit on the length of the command, except by the maximum * packet size. Also sets the tty flag if there is no command. */ if (!ac) { /* No command specified - execute shell on a tty. */ if (subsystem_flag) { fprintf(stderr, "You must specify a subsystem to invoke.\n"); usage(); } } else { /* A command has been specified. Store it into the buffer. */ for (i = 0; i < ac; i++) { if (i) buffer_append(&command, " ", 1); buffer_append(&command, av[i], strlen(av[i])); } } /* Cannot fork to background if no command. */ if (fork_after_authentication_flag && buffer_len(&command) == 0 && !no_shell_flag) fatal("Cannot fork into background without a command " "to execute."); /* * Initialize "log" output. Since we are the client all output * goes to stderr unless otherwise specified by -y or -E. */ if (use_syslog && logfile != NULL) fatal("Can't specify both -y and -E"); if (logfile != NULL) log_redirect_stderr_to(logfile); log_init(argv0, options.log_level == -1 ? SYSLOG_LEVEL_INFO : options.log_level, SYSLOG_FACILITY_USER, !use_syslog); if (debug_flag) /* version_addendum is always NULL at this point */ logit("%s, %s", SSH_RELEASE, OPENSSL_VERSION); /* Parse the configuration files */ process_config_files(host_arg, pw, 0); /* Hostname canonicalisation needs a few options filled. */ fill_default_options_for_canonicalization(&options); /* If the user has replaced the hostname then take it into use now */ if (options.hostname != NULL) { /* NB. Please keep in sync with readconf.c:match_cfg_line() */ cp = percent_expand(options.hostname, "h", host, (char *)NULL); free(host); host = cp; free(options.hostname); options.hostname = xstrdup(host); } /* If canonicalization requested then try to apply it */ lowercase(host); if (options.canonicalize_hostname != SSH_CANONICALISE_NO) addrs = resolve_canonicalize(&host, options.port); /* * If CanonicalizePermittedCNAMEs have been specified but * other canonicalization did not happen (by not being requested * or by failing with fallback) then the hostname may still be changed * as a result of CNAME following. * * Try to resolve the bare hostname name using the system resolver's * usual search rules and then apply the CNAME follow rules. * * Skip the lookup if a ProxyCommand is being used unless the user * has specifically requested canonicalisation for this case via * CanonicalizeHostname=always */ direct = option_clear_or_none(options.proxy_command) && options.jump_host == NULL; if (addrs == NULL && options.num_permitted_cnames != 0 && (direct || options.canonicalize_hostname == SSH_CANONICALISE_ALWAYS)) { if ((addrs = resolve_host(host, options.port, option_clear_or_none(options.proxy_command), cname, sizeof(cname))) == NULL) { /* Don't fatal proxied host names not in the DNS */ if (option_clear_or_none(options.proxy_command)) cleanup_exit(255); /* logged in resolve_host */ } else check_follow_cname(direct, &host, cname); } /* * If canonicalisation is enabled then re-parse the configuration * files as new stanzas may match. */ if (options.canonicalize_hostname != 0) { debug("Re-reading configuration after hostname " "canonicalisation"); free(options.hostname); options.hostname = xstrdup(host); process_config_files(host_arg, pw, 1); /* * Address resolution happens early with canonicalisation * enabled and the port number may have changed since, so * reset it in address list */ if (addrs != NULL && options.port > 0) set_addrinfo_port(addrs, options.port); } /* Fill configuration defaults. */ fill_default_options(&options); /* * If ProxyJump option specified, then construct a ProxyCommand now. */ if (options.jump_host != NULL) { char port_s[8]; /* Consistency check */ if (options.proxy_command != NULL) fatal("inconsistent options: ProxyCommand+ProxyJump"); /* Never use FD passing for ProxyJump */ options.proxy_use_fdpass = 0; snprintf(port_s, sizeof(port_s), "%d", options.jump_port); xasprintf(&options.proxy_command, - "ssh%s%s%s%s%s%s%s%s%s%.*s -W %%h:%%p %s", + "ssh%s%s%s%s%s%s%s%s%s%.*s -W '[%%h]:%%p' %s", /* Optional "-l user" argument if jump_user set */ options.jump_user == NULL ? "" : " -l ", options.jump_user == NULL ? "" : options.jump_user, /* Optional "-p port" argument if jump_port set */ options.jump_port <= 0 ? "" : " -p ", options.jump_port <= 0 ? "" : port_s, /* Optional additional jump hosts ",..." */ options.jump_extra == NULL ? "" : " -J ", options.jump_extra == NULL ? "" : options.jump_extra, /* Optional "-F" argumment if -F specified */ config == NULL ? "" : " -F ", config == NULL ? "" : config, /* Optional "-v" arguments if -v set */ debug_flag ? " -" : "", debug_flag, "vvv", /* Mandatory hostname */ options.jump_host); debug("Setting implicit ProxyCommand from ProxyJump: %s", options.proxy_command); } if (options.port == 0) options.port = default_ssh_port(); channel_set_af(options.address_family); /* Tidy and check options */ if (options.host_key_alias != NULL) lowercase(options.host_key_alias); if (options.proxy_command != NULL && strcmp(options.proxy_command, "-") == 0 && options.proxy_use_fdpass) fatal("ProxyCommand=- and ProxyUseFDPass are incompatible"); if (options.control_persist && options.update_hostkeys == SSH_UPDATE_HOSTKEYS_ASK) { debug("UpdateHostKeys=ask is incompatible with ControlPersist; " "disabling"); options.update_hostkeys = 0; } if (options.connection_attempts <= 0) fatal("Invalid number of ConnectionAttempts"); #ifndef HAVE_CYGWIN if (original_effective_uid != 0) options.use_privileged_port = 0; #endif /* reinit */ log_init(argv0, options.log_level, SYSLOG_FACILITY_USER, !use_syslog); if (options.request_tty == REQUEST_TTY_YES || options.request_tty == REQUEST_TTY_FORCE) tty_flag = 1; /* Allocate a tty by default if no command specified. */ if (buffer_len(&command) == 0) tty_flag = options.request_tty != REQUEST_TTY_NO; /* Force no tty */ if (options.request_tty == REQUEST_TTY_NO || (muxclient_command && muxclient_command != SSHMUX_COMMAND_PROXY)) tty_flag = 0; /* Do not allocate a tty if stdin is not a tty. */ if ((!isatty(fileno(stdin)) || stdin_null_flag) && options.request_tty != REQUEST_TTY_FORCE) { if (tty_flag) logit("Pseudo-terminal will not be allocated because " "stdin is not a terminal."); tty_flag = 0; } seed_rng(); if (options.user == NULL) options.user = xstrdup(pw->pw_name); if (gethostname(thishost, sizeof(thishost)) == -1) fatal("gethostname: %s", strerror(errno)); strlcpy(shorthost, thishost, sizeof(shorthost)); shorthost[strcspn(thishost, ".")] = '\0'; snprintf(portstr, sizeof(portstr), "%d", options.port); snprintf(uidstr, sizeof(uidstr), "%d", pw->pw_uid); /* Find canonic host name. */ if (strchr(host, '.') == 0) { struct addrinfo hints; struct addrinfo *ai = NULL; int errgai; memset(&hints, 0, sizeof(hints)); hints.ai_family = options.address_family; hints.ai_flags = AI_CANONNAME; hints.ai_socktype = SOCK_STREAM; errgai = getaddrinfo(host, NULL, &hints, &ai); if (errgai == 0) { if (ai->ai_canonname != NULL) host = xstrdup(ai->ai_canonname); freeaddrinfo(ai); } } if ((md = ssh_digest_start(SSH_DIGEST_SHA1)) == NULL || ssh_digest_update(md, thishost, strlen(thishost)) < 0 || ssh_digest_update(md, host, strlen(host)) < 0 || ssh_digest_update(md, portstr, strlen(portstr)) < 0 || ssh_digest_update(md, options.user, strlen(options.user)) < 0 || ssh_digest_final(md, conn_hash, sizeof(conn_hash)) < 0) fatal("%s: mux digest failed", __func__); ssh_digest_free(md); conn_hash_hex = tohex(conn_hash, ssh_digest_bytes(SSH_DIGEST_SHA1)); if (options.local_command != NULL) { debug3("expanding LocalCommand: %s", options.local_command); cp = options.local_command; options.local_command = percent_expand(cp, "C", conn_hash_hex, "L", shorthost, "d", pw->pw_dir, "h", host, "l", thishost, "n", host_arg, "p", portstr, "r", options.user, "u", pw->pw_name, (char *)NULL); debug3("expanded LocalCommand: %s", options.local_command); free(cp); } if (options.control_path != NULL) { cp = tilde_expand_filename(options.control_path, original_real_uid); free(options.control_path); options.control_path = percent_expand(cp, "C", conn_hash_hex, "L", shorthost, "h", host, "l", thishost, "n", host_arg, "p", portstr, "r", options.user, "u", pw->pw_name, "i", uidstr, (char *)NULL); free(cp); } free(conn_hash_hex); if (config_test) { dump_client_config(&options, host); exit(0); } if (muxclient_command != 0 && options.control_path == NULL) fatal("No ControlPath specified for \"-O\" command"); if (options.control_path != NULL) { int sock; if ((sock = muxclient(options.control_path)) >= 0) { packet_set_connection(sock, sock); ssh = active_state; /* XXX */ enable_compat20(); /* XXX */ packet_set_mux(); goto skip_connect; } } /* * If hostname canonicalisation was not enabled, then we may not * have yet resolved the hostname. Do so now. */ if (addrs == NULL && options.proxy_command == NULL) { debug2("resolving \"%s\" port %d", host, options.port); if ((addrs = resolve_host(host, options.port, 1, cname, sizeof(cname))) == NULL) cleanup_exit(255); /* resolve_host logs the error */ } timeout_ms = options.connection_timeout * 1000; /* Open a connection to the remote host. */ if (ssh_connect(host, addrs, &hostaddr, options.port, options.address_family, options.connection_attempts, &timeout_ms, options.tcp_keep_alive, options.use_privileged_port) != 0) exit(255); if (addrs != NULL) freeaddrinfo(addrs); packet_set_timeout(options.server_alive_interval, options.server_alive_count_max); ssh = active_state; /* XXX */ if (timeout_ms > 0) debug3("timeout: %d ms remain after connect", timeout_ms); /* * If we successfully made the connection, load the host private key * in case we will need it later for combined rsa-rhosts * authentication. This must be done before releasing extra * privileges, because the file is only readable by root. * If we cannot access the private keys, load the public keys * instead and try to execute the ssh-keysign helper instead. */ sensitive_data.nkeys = 0; sensitive_data.keys = NULL; sensitive_data.external_keysign = 0; if (options.rhosts_rsa_authentication || options.hostbased_authentication) { sensitive_data.nkeys = 9; sensitive_data.keys = xcalloc(sensitive_data.nkeys, sizeof(Key)); for (i = 0; i < sensitive_data.nkeys; i++) sensitive_data.keys[i] = NULL; PRIV_START; #if WITH_SSH1 sensitive_data.keys[0] = key_load_private_type(KEY_RSA1, _PATH_HOST_KEY_FILE, "", NULL, NULL); #endif #ifdef OPENSSL_HAS_ECC sensitive_data.keys[1] = key_load_private_cert(KEY_ECDSA, _PATH_HOST_ECDSA_KEY_FILE, "", NULL); #endif sensitive_data.keys[2] = key_load_private_cert(KEY_ED25519, _PATH_HOST_ED25519_KEY_FILE, "", NULL); sensitive_data.keys[3] = key_load_private_cert(KEY_RSA, _PATH_HOST_RSA_KEY_FILE, "", NULL); sensitive_data.keys[4] = key_load_private_cert(KEY_DSA, _PATH_HOST_DSA_KEY_FILE, "", NULL); #ifdef OPENSSL_HAS_ECC sensitive_data.keys[5] = key_load_private_type(KEY_ECDSA, _PATH_HOST_ECDSA_KEY_FILE, "", NULL, NULL); #endif sensitive_data.keys[6] = key_load_private_type(KEY_ED25519, _PATH_HOST_ED25519_KEY_FILE, "", NULL, NULL); sensitive_data.keys[7] = key_load_private_type(KEY_RSA, _PATH_HOST_RSA_KEY_FILE, "", NULL, NULL); sensitive_data.keys[8] = key_load_private_type(KEY_DSA, _PATH_HOST_DSA_KEY_FILE, "", NULL, NULL); PRIV_END; if (options.hostbased_authentication == 1 && sensitive_data.keys[0] == NULL && sensitive_data.keys[5] == NULL && sensitive_data.keys[6] == NULL && sensitive_data.keys[7] == NULL && sensitive_data.keys[8] == NULL) { #ifdef OPENSSL_HAS_ECC sensitive_data.keys[1] = key_load_cert( _PATH_HOST_ECDSA_KEY_FILE); #endif sensitive_data.keys[2] = key_load_cert( _PATH_HOST_ED25519_KEY_FILE); sensitive_data.keys[3] = key_load_cert( _PATH_HOST_RSA_KEY_FILE); sensitive_data.keys[4] = key_load_cert( _PATH_HOST_DSA_KEY_FILE); #ifdef OPENSSL_HAS_ECC sensitive_data.keys[5] = key_load_public( _PATH_HOST_ECDSA_KEY_FILE, NULL); #endif sensitive_data.keys[6] = key_load_public( _PATH_HOST_ED25519_KEY_FILE, NULL); sensitive_data.keys[7] = key_load_public( _PATH_HOST_RSA_KEY_FILE, NULL); sensitive_data.keys[8] = key_load_public( _PATH_HOST_DSA_KEY_FILE, NULL); sensitive_data.external_keysign = 1; } } /* * Get rid of any extra privileges that we may have. We will no * longer need them. Also, extra privileges could make it very hard * to read identity files and other non-world-readable files from the * user's home directory if it happens to be on a NFS volume where * root is mapped to nobody. */ if (original_effective_uid == 0) { PRIV_START; permanently_set_uid(pw); } /* * Now that we are back to our own permissions, create ~/.ssh * directory if it doesn't already exist. */ if (config == NULL) { r = snprintf(buf, sizeof buf, "%s%s%s", pw->pw_dir, strcmp(pw->pw_dir, "/") ? "/" : "", _PATH_SSH_USER_DIR); if (r > 0 && (size_t)r < sizeof(buf) && stat(buf, &st) < 0) { #ifdef WITH_SELINUX ssh_selinux_setfscreatecon(buf); #endif if (mkdir(buf, 0700) < 0) error("Could not create directory '%.200s'.", buf); #ifdef WITH_SELINUX ssh_selinux_setfscreatecon(NULL); #endif } } /* load options.identity_files */ load_public_identity_files(); /* optionally set the SSH_AUTHSOCKET_ENV_NAME varibale */ if (options.identity_agent && strcmp(options.identity_agent, SSH_AUTHSOCKET_ENV_NAME) != 0) { if (strcmp(options.identity_agent, "none") == 0) { unsetenv(SSH_AUTHSOCKET_ENV_NAME); } else { p = tilde_expand_filename(options.identity_agent, original_real_uid); cp = percent_expand(p, "d", pw->pw_dir, "u", pw->pw_name, "l", thishost, "h", host, "r", options.user, (char *)NULL); setenv(SSH_AUTHSOCKET_ENV_NAME, cp, 1); free(cp); free(p); } } /* Expand ~ in known host file names. */ tilde_expand_paths(options.system_hostfiles, options.num_system_hostfiles); tilde_expand_paths(options.user_hostfiles, options.num_user_hostfiles); signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE early */ signal(SIGCHLD, main_sigchld_handler); /* Log into the remote system. Never returns if the login fails. */ ssh_login(&sensitive_data, host, (struct sockaddr *)&hostaddr, options.port, pw, timeout_ms); if (packet_connection_is_on_socket()) { verbose("Authenticated to %s ([%s]:%d).", host, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); } else { verbose("Authenticated to %s (via proxy).", host); } /* We no longer need the private host keys. Clear them now. */ if (sensitive_data.nkeys != 0) { for (i = 0; i < sensitive_data.nkeys; i++) { if (sensitive_data.keys[i] != NULL) { /* Destroys contents safely */ debug3("clear hostkey %d", i); key_free(sensitive_data.keys[i]); sensitive_data.keys[i] = NULL; } } free(sensitive_data.keys); } for (i = 0; i < options.num_identity_files; i++) { free(options.identity_files[i]); options.identity_files[i] = NULL; if (options.identity_keys[i]) { key_free(options.identity_keys[i]); options.identity_keys[i] = NULL; } } for (i = 0; i < options.num_certificate_files; i++) { free(options.certificate_files[i]); options.certificate_files[i] = NULL; } skip_connect: exit_status = compat20 ? ssh_session2() : ssh_session(); packet_close(); if (options.control_path != NULL && muxserver_sock != -1) unlink(options.control_path); /* Kill ProxyCommand if it is running. */ ssh_kill_proxy_command(); return exit_status; } static void control_persist_detach(void) { pid_t pid; int devnull, keep_stderr; debug("%s: backgrounding master process", __func__); /* * master (current process) into the background, and make the * foreground process a client of the backgrounded master. */ switch ((pid = fork())) { case -1: fatal("%s: fork: %s", __func__, strerror(errno)); case 0: /* Child: master process continues mainloop */ break; default: /* Parent: set up mux slave to connect to backgrounded master */ debug2("%s: background process is %ld", __func__, (long)pid); stdin_null_flag = ostdin_null_flag; options.request_tty = orequest_tty; tty_flag = otty_flag; close(muxserver_sock); muxserver_sock = -1; options.control_master = SSHCTL_MASTER_NO; muxclient(options.control_path); /* muxclient() doesn't return on success. */ fatal("Failed to connect to new control master"); } if ((devnull = open(_PATH_DEVNULL, O_RDWR)) == -1) { error("%s: open(\"/dev/null\"): %s", __func__, strerror(errno)); } else { keep_stderr = log_is_on_stderr() && debug_flag; if (dup2(devnull, STDIN_FILENO) == -1 || dup2(devnull, STDOUT_FILENO) == -1 || (!keep_stderr && dup2(devnull, STDERR_FILENO) == -1)) error("%s: dup2: %s", __func__, strerror(errno)); if (devnull > STDERR_FILENO) close(devnull); } daemon(1, 1); setproctitle("%s [mux]", options.control_path); } /* Do fork() after authentication. Used by "ssh -f" */ static void fork_postauth(void) { if (need_controlpersist_detach) control_persist_detach(); debug("forking to background"); fork_after_authentication_flag = 0; if (daemon(1, 1) < 0) fatal("daemon() failed: %.200s", strerror(errno)); } /* Callback for remote forward global requests */ static void ssh_confirm_remote_forward(int type, u_int32_t seq, void *ctxt) { struct Forward *rfwd = (struct Forward *)ctxt; /* XXX verbose() on failure? */ debug("remote forward %s for: listen %s%s%d, connect %s:%d", type == SSH2_MSG_REQUEST_SUCCESS ? "success" : "failure", rfwd->listen_path ? rfwd->listen_path : rfwd->listen_host ? rfwd->listen_host : "", (rfwd->listen_path || rfwd->listen_host) ? ":" : "", rfwd->listen_port, rfwd->connect_path ? rfwd->connect_path : rfwd->connect_host, rfwd->connect_port); if (rfwd->listen_path == NULL && rfwd->listen_port == 0) { if (type == SSH2_MSG_REQUEST_SUCCESS) { rfwd->allocated_port = packet_get_int(); logit("Allocated port %u for remote forward to %s:%d", rfwd->allocated_port, rfwd->connect_host, rfwd->connect_port); channel_update_permitted_opens(rfwd->handle, rfwd->allocated_port); } else { channel_update_permitted_opens(rfwd->handle, -1); } } if (type == SSH2_MSG_REQUEST_FAILURE) { if (options.exit_on_forward_failure) { if (rfwd->listen_path != NULL) fatal("Error: remote port forwarding failed " "for listen path %s", rfwd->listen_path); else fatal("Error: remote port forwarding failed " "for listen port %d", rfwd->listen_port); } else { if (rfwd->listen_path != NULL) logit("Warning: remote port forwarding failed " "for listen path %s", rfwd->listen_path); else logit("Warning: remote port forwarding failed " "for listen port %d", rfwd->listen_port); } } if (++remote_forward_confirms_received == options.num_remote_forwards) { debug("All remote forwarding requests processed"); if (fork_after_authentication_flag) fork_postauth(); } } static void client_cleanup_stdio_fwd(int id, void *arg) { debug("stdio forwarding: done"); cleanup_exit(0); } static void ssh_stdio_confirm(int id, int success, void *arg) { if (!success) fatal("stdio forwarding failed"); } static void ssh_init_stdio_forwarding(void) { Channel *c; int in, out; if (options.stdio_forward_host == NULL) return; if (!compat20) fatal("stdio forwarding require Protocol 2"); debug3("%s: %s:%d", __func__, options.stdio_forward_host, options.stdio_forward_port); if ((in = dup(STDIN_FILENO)) < 0 || (out = dup(STDOUT_FILENO)) < 0) fatal("channel_connect_stdio_fwd: dup() in/out failed"); if ((c = channel_connect_stdio_fwd(options.stdio_forward_host, options.stdio_forward_port, in, out)) == NULL) fatal("%s: channel_connect_stdio_fwd failed", __func__); channel_register_cleanup(c->self, client_cleanup_stdio_fwd, 0); channel_register_open_confirm(c->self, ssh_stdio_confirm, NULL); } static void ssh_init_forwarding(void) { int success = 0; int i; /* Initiate local TCP/IP port forwardings. */ for (i = 0; i < options.num_local_forwards; i++) { debug("Local connections to %.200s:%d forwarded to remote " "address %.200s:%d", (options.local_forwards[i].listen_path != NULL) ? options.local_forwards[i].listen_path : (options.local_forwards[i].listen_host == NULL) ? (options.fwd_opts.gateway_ports ? "*" : "LOCALHOST") : options.local_forwards[i].listen_host, options.local_forwards[i].listen_port, (options.local_forwards[i].connect_path != NULL) ? options.local_forwards[i].connect_path : options.local_forwards[i].connect_host, options.local_forwards[i].connect_port); success += channel_setup_local_fwd_listener( &options.local_forwards[i], &options.fwd_opts); } if (i > 0 && success != i && options.exit_on_forward_failure) fatal("Could not request local forwarding."); if (i > 0 && success == 0) error("Could not request local forwarding."); /* Initiate remote TCP/IP port forwardings. */ for (i = 0; i < options.num_remote_forwards; i++) { debug("Remote connections from %.200s:%d forwarded to " "local address %.200s:%d", (options.remote_forwards[i].listen_path != NULL) ? options.remote_forwards[i].listen_path : (options.remote_forwards[i].listen_host == NULL) ? "LOCALHOST" : options.remote_forwards[i].listen_host, options.remote_forwards[i].listen_port, (options.remote_forwards[i].connect_path != NULL) ? options.remote_forwards[i].connect_path : options.remote_forwards[i].connect_host, options.remote_forwards[i].connect_port); options.remote_forwards[i].handle = channel_request_remote_forwarding( &options.remote_forwards[i]); if (options.remote_forwards[i].handle < 0) { if (options.exit_on_forward_failure) fatal("Could not request remote forwarding."); else logit("Warning: Could not request remote " "forwarding."); } else { client_register_global_confirm(ssh_confirm_remote_forward, &options.remote_forwards[i]); } } /* Initiate tunnel forwarding. */ if (options.tun_open != SSH_TUNMODE_NO) { if (client_request_tun_fwd(options.tun_open, options.tun_local, options.tun_remote) == -1) { if (options.exit_on_forward_failure) fatal("Could not request tunnel forwarding."); else error("Could not request tunnel forwarding."); } } } static void check_agent_present(void) { int r; if (options.forward_agent) { /* Clear agent forwarding if we don't have an agent. */ if ((r = ssh_get_authentication_socket(NULL)) != 0) { options.forward_agent = 0; if (r != SSH_ERR_AGENT_NOT_PRESENT) debug("ssh_get_authentication_socket: %s", ssh_err(r)); } } } static int ssh_session(void) { int type; int interactive = 0; int have_tty = 0; struct winsize ws; char *cp; const char *display; char *proto = NULL, *data = NULL; /* Enable compression if requested. */ if (options.compression) { debug("Requesting compression at level %d.", options.compression_level); if (options.compression_level < 1 || options.compression_level > 9) fatal("Compression level must be from 1 (fast) to " "9 (slow, best)."); /* Send the request. */ packet_start(SSH_CMSG_REQUEST_COMPRESSION); packet_put_int(options.compression_level); packet_send(); packet_write_wait(); type = packet_read(); if (type == SSH_SMSG_SUCCESS) packet_start_compression(options.compression_level); else if (type == SSH_SMSG_FAILURE) logit("Warning: Remote host refused compression."); else packet_disconnect("Protocol error waiting for " "compression response."); } /* Allocate a pseudo tty if appropriate. */ if (tty_flag) { debug("Requesting pty."); /* Start the packet. */ packet_start(SSH_CMSG_REQUEST_PTY); /* Store TERM in the packet. There is no limit on the length of the string. */ cp = getenv("TERM"); if (!cp) cp = ""; packet_put_cstring(cp); /* Store window size in the packet. */ if (ioctl(fileno(stdin), TIOCGWINSZ, &ws) < 0) memset(&ws, 0, sizeof(ws)); packet_put_int((u_int)ws.ws_row); packet_put_int((u_int)ws.ws_col); packet_put_int((u_int)ws.ws_xpixel); packet_put_int((u_int)ws.ws_ypixel); /* Store tty modes in the packet. */ tty_make_modes(fileno(stdin), NULL); /* Send the packet, and wait for it to leave. */ packet_send(); packet_write_wait(); /* Read response from the server. */ type = packet_read(); if (type == SSH_SMSG_SUCCESS) { interactive = 1; have_tty = 1; } else if (type == SSH_SMSG_FAILURE) logit("Warning: Remote host failed or refused to " "allocate a pseudo tty."); else packet_disconnect("Protocol error waiting for pty " "request response."); } /* Request X11 forwarding if enabled and DISPLAY is set. */ display = getenv("DISPLAY"); if (display == NULL && options.forward_x11) debug("X11 forwarding requested but DISPLAY not set"); if (options.forward_x11 && client_x11_get_proto(display, options.xauth_location, options.forward_x11_trusted, options.forward_x11_timeout, &proto, &data) == 0) { /* Request forwarding with authentication spoofing. */ debug("Requesting X11 forwarding with authentication " "spoofing."); x11_request_forwarding_with_spoofing(0, display, proto, data, 0); /* Read response from the server. */ type = packet_read(); if (type == SSH_SMSG_SUCCESS) { interactive = 1; } else if (type == SSH_SMSG_FAILURE) { logit("Warning: Remote host denied X11 forwarding."); } else { packet_disconnect("Protocol error waiting for X11 " "forwarding"); } } /* Tell the packet module whether this is an interactive session. */ packet_set_interactive(interactive, options.ip_qos_interactive, options.ip_qos_bulk); /* Request authentication agent forwarding if appropriate. */ check_agent_present(); if (options.forward_agent) { debug("Requesting authentication agent forwarding."); auth_request_forwarding(); /* Read response from the server. */ type = packet_read(); packet_check_eom(); if (type != SSH_SMSG_SUCCESS) logit("Warning: Remote host denied authentication agent forwarding."); } /* Initiate port forwardings. */ ssh_init_stdio_forwarding(); ssh_init_forwarding(); /* Execute a local command */ if (options.local_command != NULL && options.permit_local_command) ssh_local_cmd(options.local_command); /* * If requested and we are not interested in replies to remote * forwarding requests, then let ssh continue in the background. */ if (fork_after_authentication_flag) { if (options.exit_on_forward_failure && options.num_remote_forwards > 0) { debug("deferring postauth fork until remote forward " "confirmation received"); } else fork_postauth(); } /* * If a command was specified on the command line, execute the * command now. Otherwise request the server to start a shell. */ if (buffer_len(&command) > 0) { int len = buffer_len(&command); if (len > 900) len = 900; debug("Sending command: %.*s", len, (u_char *)buffer_ptr(&command)); packet_start(SSH_CMSG_EXEC_CMD); packet_put_string(buffer_ptr(&command), buffer_len(&command)); packet_send(); packet_write_wait(); } else { debug("Requesting shell."); packet_start(SSH_CMSG_EXEC_SHELL); packet_send(); packet_write_wait(); } /* Enter the interactive session. */ return client_loop(have_tty, tty_flag ? options.escape_char : SSH_ESCAPECHAR_NONE, 0); } /* request pty/x11/agent/tcpfwd/shell for channel */ static void ssh_session2_setup(int id, int success, void *arg) { extern char **environ; const char *display; int interactive = tty_flag; char *proto = NULL, *data = NULL; if (!success) return; /* No need for error message, channels code sens one */ display = getenv("DISPLAY"); if (display == NULL && options.forward_x11) debug("X11 forwarding requested but DISPLAY not set"); if (options.forward_x11 && client_x11_get_proto(display, options.xauth_location, options.forward_x11_trusted, options.forward_x11_timeout, &proto, &data) == 0) { /* Request forwarding with authentication spoofing. */ debug("Requesting X11 forwarding with authentication " "spoofing."); x11_request_forwarding_with_spoofing(id, display, proto, data, 1); client_expect_confirm(id, "X11 forwarding", CONFIRM_WARN); /* XXX exit_on_forward_failure */ interactive = 1; } check_agent_present(); if (options.forward_agent) { debug("Requesting authentication agent forwarding."); channel_request_start(id, "auth-agent-req@openssh.com", 0); packet_send(); } /* Tell the packet module whether this is an interactive session. */ packet_set_interactive(interactive, options.ip_qos_interactive, options.ip_qos_bulk); client_session2_setup(id, tty_flag, subsystem_flag, getenv("TERM"), NULL, fileno(stdin), &command, environ); } /* open new channel for a session */ static int ssh_session2_open(void) { Channel *c; int window, packetmax, in, out, err; if (stdin_null_flag) { in = open(_PATH_DEVNULL, O_RDONLY); } else { in = dup(STDIN_FILENO); } out = dup(STDOUT_FILENO); err = dup(STDERR_FILENO); if (in < 0 || out < 0 || err < 0) fatal("dup() in/out/err failed"); /* enable nonblocking unless tty */ if (!isatty(in)) set_nonblock(in); if (!isatty(out)) set_nonblock(out); if (!isatty(err)) set_nonblock(err); window = CHAN_SES_WINDOW_DEFAULT; packetmax = CHAN_SES_PACKET_DEFAULT; if (tty_flag) { window >>= 1; packetmax >>= 1; } c = channel_new( "session", SSH_CHANNEL_OPENING, in, out, err, window, packetmax, CHAN_EXTENDED_WRITE, "client-session", /*nonblock*/0); debug3("ssh_session2_open: channel_new: %d", c->self); channel_send_open(c->self); if (!no_shell_flag) channel_register_open_confirm(c->self, ssh_session2_setup, NULL); return c->self; } static int ssh_session2(void) { int id = -1; /* XXX should be pre-session */ if (!options.control_persist) ssh_init_stdio_forwarding(); ssh_init_forwarding(); /* Start listening for multiplex clients */ if (!packet_get_mux()) muxserver_listen(); /* * If we are in control persist mode and have a working mux listen * socket, then prepare to background ourselves and have a foreground * client attach as a control slave. * NB. we must save copies of the flags that we override for * the backgrounding, since we defer attachment of the slave until * after the connection is fully established (in particular, * async rfwd replies have been received for ExitOnForwardFailure). */ if (options.control_persist && muxserver_sock != -1) { ostdin_null_flag = stdin_null_flag; ono_shell_flag = no_shell_flag; orequest_tty = options.request_tty; otty_flag = tty_flag; stdin_null_flag = 1; no_shell_flag = 1; tty_flag = 0; if (!fork_after_authentication_flag) need_controlpersist_detach = 1; fork_after_authentication_flag = 1; } /* * ControlPersist mux listen socket setup failed, attempt the * stdio forward setup that we skipped earlier. */ if (options.control_persist && muxserver_sock == -1) ssh_init_stdio_forwarding(); if (!no_shell_flag || (datafellows & SSH_BUG_DUMMYCHAN)) id = ssh_session2_open(); else { packet_set_interactive( options.control_master == SSHCTL_MASTER_NO, options.ip_qos_interactive, options.ip_qos_bulk); } /* If we don't expect to open a new session, then disallow it */ if (options.control_master == SSHCTL_MASTER_NO && (datafellows & SSH_NEW_OPENSSH)) { debug("Requesting no-more-sessions@openssh.com"); packet_start(SSH2_MSG_GLOBAL_REQUEST); packet_put_cstring("no-more-sessions@openssh.com"); packet_put_char(0); packet_send(); } /* Execute a local command */ if (options.local_command != NULL && options.permit_local_command) ssh_local_cmd(options.local_command); /* * If requested and we are not interested in replies to remote * forwarding requests, then let ssh continue in the background. */ if (fork_after_authentication_flag) { if (options.exit_on_forward_failure && options.num_remote_forwards > 0) { debug("deferring postauth fork until remote forward " "confirmation received"); } else fork_postauth(); } return client_loop(tty_flag, tty_flag ? options.escape_char : SSH_ESCAPECHAR_NONE, id); } /* Loads all IdentityFile and CertificateFile keys */ static void load_public_identity_files(void) { char *filename, *cp, thishost[NI_MAXHOST]; char *pwdir = NULL, *pwname = NULL; Key *public; struct passwd *pw; int i; u_int n_ids, n_certs; char *identity_files[SSH_MAX_IDENTITY_FILES]; Key *identity_keys[SSH_MAX_IDENTITY_FILES]; char *certificate_files[SSH_MAX_CERTIFICATE_FILES]; struct sshkey *certificates[SSH_MAX_CERTIFICATE_FILES]; #ifdef ENABLE_PKCS11 Key **keys; int nkeys; #endif /* PKCS11 */ n_ids = n_certs = 0; memset(identity_files, 0, sizeof(identity_files)); memset(identity_keys, 0, sizeof(identity_keys)); memset(certificate_files, 0, sizeof(certificate_files)); memset(certificates, 0, sizeof(certificates)); #ifdef ENABLE_PKCS11 if (options.pkcs11_provider != NULL && options.num_identity_files < SSH_MAX_IDENTITY_FILES && (pkcs11_init(!options.batch_mode) == 0) && (nkeys = pkcs11_add_provider(options.pkcs11_provider, NULL, &keys)) > 0) { for (i = 0; i < nkeys; i++) { if (n_ids >= SSH_MAX_IDENTITY_FILES) { key_free(keys[i]); continue; } identity_keys[n_ids] = keys[i]; identity_files[n_ids] = xstrdup(options.pkcs11_provider); /* XXX */ n_ids++; } free(keys); } #endif /* ENABLE_PKCS11 */ if ((pw = getpwuid(original_real_uid)) == NULL) fatal("load_public_identity_files: getpwuid failed"); pwname = xstrdup(pw->pw_name); pwdir = xstrdup(pw->pw_dir); if (gethostname(thishost, sizeof(thishost)) == -1) fatal("load_public_identity_files: gethostname: %s", strerror(errno)); for (i = 0; i < options.num_identity_files; i++) { if (n_ids >= SSH_MAX_IDENTITY_FILES || strcasecmp(options.identity_files[i], "none") == 0) { free(options.identity_files[i]); options.identity_files[i] = NULL; continue; } cp = tilde_expand_filename(options.identity_files[i], original_real_uid); filename = percent_expand(cp, "d", pwdir, "u", pwname, "l", thishost, "h", host, "r", options.user, (char *)NULL); free(cp); public = key_load_public(filename, NULL); debug("identity file %s type %d", filename, public ? public->type : -1); free(options.identity_files[i]); identity_files[n_ids] = filename; identity_keys[n_ids] = public; if (++n_ids >= SSH_MAX_IDENTITY_FILES) continue; /* * If no certificates have been explicitly listed then try * to add the default certificate variant too. */ if (options.num_certificate_files != 0) continue; xasprintf(&cp, "%s-cert", filename); public = key_load_public(cp, NULL); debug("identity file %s type %d", cp, public ? public->type : -1); if (public == NULL) { free(cp); continue; } if (!key_is_cert(public)) { debug("%s: key %s type %s is not a certificate", __func__, cp, key_type(public)); key_free(public); free(cp); continue; } /* NB. leave filename pointing to private key */ identity_files[n_ids] = xstrdup(filename); identity_keys[n_ids] = public; n_ids++; } if (options.num_certificate_files > SSH_MAX_CERTIFICATE_FILES) fatal("%s: too many certificates", __func__); for (i = 0; i < options.num_certificate_files; i++) { cp = tilde_expand_filename(options.certificate_files[i], original_real_uid); filename = percent_expand(cp, "d", pwdir, "u", pwname, "l", thishost, "h", host, "r", options.user, (char *)NULL); free(cp); public = key_load_public(filename, NULL); debug("certificate file %s type %d", filename, public ? public->type : -1); free(options.certificate_files[i]); options.certificate_files[i] = NULL; if (public == NULL) { free(filename); continue; } if (!key_is_cert(public)) { debug("%s: key %s type %s is not a certificate", __func__, filename, key_type(public)); key_free(public); free(filename); continue; } certificate_files[n_certs] = filename; certificates[n_certs] = public; ++n_certs; } options.num_identity_files = n_ids; memcpy(options.identity_files, identity_files, sizeof(identity_files)); memcpy(options.identity_keys, identity_keys, sizeof(identity_keys)); options.num_certificate_files = n_certs; memcpy(options.certificate_files, certificate_files, sizeof(certificate_files)); memcpy(options.certificates, certificates, sizeof(certificates)); explicit_bzero(pwname, strlen(pwname)); free(pwname); explicit_bzero(pwdir, strlen(pwdir)); free(pwdir); } static void main_sigchld_handler(int sig) { int save_errno = errno; pid_t pid; int status; while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || (pid < 0 && errno == EINTR)) ; signal(sig, main_sigchld_handler); errno = save_errno; } diff --git a/crypto/openssh/ssh_config b/crypto/openssh/ssh_config index b803803ad8df..580c60e53f98 100644 --- a/crypto/openssh/ssh_config +++ b/crypto/openssh/ssh_config @@ -1,53 +1,53 @@ # $OpenBSD: ssh_config,v 1.30 2016/02/20 23:06:23 sobrado Exp $ # $FreeBSD$ # This is the ssh client system-wide configuration file. See # ssh_config(5) for more information. This file provides defaults for # users, and the values can be changed in per-user configuration files # or on the command line. # Configuration data is parsed as follows: # 1. command line options # 2. user-specific file # 3. system-wide file # Any configuration value is only changed the first time it is set. # Thus, host-specific definitions should be at the beginning of the # configuration file, and defaults at the end. # Site-wide defaults for some commonly used options. For a comprehensive # list of available options, their meanings and defaults, please see the # ssh_config(5) man page. # Host * # ForwardAgent no # ForwardX11 no # RhostsRSAAuthentication no # RSAAuthentication yes # PasswordAuthentication yes # HostbasedAuthentication no # GSSAPIAuthentication no # GSSAPIDelegateCredentials no # BatchMode no # CheckHostIP no # AddressFamily any # ConnectTimeout 0 # StrictHostKeyChecking ask # IdentityFile ~/.ssh/identity # IdentityFile ~/.ssh/id_rsa # IdentityFile ~/.ssh/id_dsa # IdentityFile ~/.ssh/id_ecdsa # IdentityFile ~/.ssh/id_ed25519 # Port 22 # Protocol 2 # Cipher 3des # Ciphers aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc # MACs hmac-md5,hmac-sha1,umac-64@openssh.com,hmac-ripemd160 # EscapeChar ~ # Tunnel no # TunnelDevice any:any # PermitLocalCommand no # VisualHostKey no # ProxyCommand ssh -q -W %h:%p gateway.example.com # RekeyLimit 1G 1h # VerifyHostKeyDNS yes -# VersionAddendum FreeBSD-20170305 +# VersionAddendum FreeBSD-20170804 diff --git a/crypto/openssh/ssh_config.5 b/crypto/openssh/ssh_config.5 index e0191bb79e78..d5553a84204f 100644 --- a/crypto/openssh/ssh_config.5 +++ b/crypto/openssh/ssh_config.5 @@ -1,1791 +1,1815 @@ .\" .\" Author: Tatu Ylonen .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland .\" All rights reserved .\" .\" As far as I am concerned, the code I have written for this software .\" can be used freely for any purpose. Any derived versions of this .\" software must be clearly marked as such, and if the derived work is .\" incompatible with the protocol description in the RFC file, it must be .\" called by a name other than "ssh" or "Secure Shell". .\" .\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. .\" Copyright (c) 1999 Aaron Campbell. All rights reserved. .\" Copyright (c) 1999 Theo de Raadt. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: ssh_config.5,v 1.240 2016/10/15 19:56:25 jmc Exp $ +.\" $OpenBSD: ssh_config.5,v 1.242 2017/02/27 14:30:33 jmc Exp $ .\" $FreeBSD$ -.Dd $Mdocdate: October 15 2016 $ +.Dd $Mdocdate: February 27 2017 $ .Dt SSH_CONFIG 5 .Os .Sh NAME .Nm ssh_config .Nd OpenSSH SSH client configuration files .Sh SYNOPSIS .Nm ~/.ssh/config .Nm /etc/ssh/ssh_config .Sh DESCRIPTION .Xr ssh 1 obtains configuration data from the following sources in the following order: .Pp .Bl -enum -offset indent -compact .It command-line options .It user's configuration file .Pq Pa ~/.ssh/config .It system-wide configuration file .Pq Pa /etc/ssh/ssh_config .El .Pp For each parameter, the first obtained value will be used. The configuration files contain sections separated by .Cm Host specifications, and that section is only applied for hosts that match one of the patterns given in the specification. The matched host name is usually the one given on the command line (see the .Cm CanonicalizeHostname option for exceptions). .Pp Since the first obtained value for each parameter is used, more host-specific declarations should be given near the beginning of the file, and general defaults at the end. .Pp The file contains keyword-argument pairs, one per line. Lines starting with .Ql # and empty lines are interpreted as comments. Arguments may optionally be enclosed in double quotes .Pq \&" in order to represent arguments containing spaces. Configuration options may be separated by whitespace or optional whitespace and exactly one .Ql = ; the latter format is useful to avoid the need to quote whitespace when specifying configuration options using the .Nm ssh , .Nm scp , and .Nm sftp .Fl o option. .Pp The possible keywords and their meanings are as follows (note that keywords are case-insensitive and arguments are case-sensitive): .Bl -tag -width Ds .It Cm Host Restricts the following declarations (up to the next .Cm Host or .Cm Match keyword) to be only for those hosts that match one of the patterns given after the keyword. If more than one pattern is provided, they should be separated by whitespace. A single .Ql * as a pattern can be used to provide global defaults for all hosts. The host is usually the .Ar hostname argument given on the command line (see the .Cm CanonicalizeHostname keyword for exceptions). .Pp A pattern entry may be negated by prefixing it with an exclamation mark .Pq Sq !\& . If a negated entry is matched, then the .Cm Host entry is ignored, regardless of whether any other patterns on the line match. Negated matches are therefore useful to provide exceptions for wildcard matches. .Pp See .Sx PATTERNS for more information on patterns. .It Cm Match Restricts the following declarations (up to the next .Cm Host or .Cm Match keyword) to be used only when the conditions following the .Cm Match keyword are satisfied. Match conditions are specified using one or more criteria or the single token .Cm all which always matches. The available criteria keywords are: .Cm canonical , .Cm exec , .Cm host , .Cm originalhost , .Cm user , and .Cm localuser . The .Cm all criteria must appear alone or immediately after .Cm canonical . Other criteria may be combined arbitrarily. All criteria but .Cm all and .Cm canonical require an argument. Criteria may be negated by prepending an exclamation mark .Pq Sq !\& . .Pp The .Cm canonical keyword matches only when the configuration file is being re-parsed after hostname canonicalization (see the .Cm CanonicalizeHostname option.) This may be useful to specify conditions that work with canonical host names only. The .Cm exec keyword executes the specified command under the user's shell. If the command returns a zero exit status then the condition is considered true. Commands containing whitespace characters must be quoted. Arguments to .Cm exec accept the tokens described in the .Sx TOKENS section. .Pp The other keywords' criteria must be single entries or comma-separated lists and may use the wildcard and negation operators described in the .Sx PATTERNS section. The criteria for the .Cm host keyword are matched against the target hostname, after any substitution by the .Cm Hostname or .Cm CanonicalizeHostname options. The .Cm originalhost keyword matches against the hostname as it was specified on the command-line. The .Cm user keyword matches against the target username on the remote host. The .Cm localuser keyword matches against the name of the local user running .Xr ssh 1 (this keyword may be useful in system-wide .Nm files). .It Cm AddKeysToAgent Specifies whether keys should be automatically added to a running .Xr ssh-agent 1 . If this option is set to .Cm yes and a key is loaded from a file, the key and its passphrase are added to the agent with the default lifetime, as if by .Xr ssh-add 1 . If this option is set to .Cm ask , .Xr ssh 1 will require confirmation using the .Ev SSH_ASKPASS program before adding a key (see .Xr ssh-add 1 for details). If this option is set to .Cm confirm , each use of the key must be confirmed, as if the .Fl c option was specified to .Xr ssh-add 1 . If this option is set to .Cm no , no keys are added to the agent. The argument must be .Cm yes , .Cm confirm , .Cm ask , or .Cm no (the default). .It Cm AddressFamily Specifies which address family to use when connecting. Valid arguments are .Cm any (the default), .Cm inet (use IPv4 only), or .Cm inet6 (use IPv6 only). .It Cm BatchMode If set to .Cm yes , passphrase/password querying will be disabled. This option is useful in scripts and other batch jobs where no user is present to supply the password. The argument must be .Cm yes or .Cm no (the default). .It Cm BindAddress Use the specified address on the local machine as the source address of the connection. Only useful on systems with more than one address. Note that this option does not work if .Cm UsePrivilegedPort is set to .Cm yes . .It Cm CanonicalDomains When .Cm CanonicalizeHostname is enabled, this option specifies the list of domain suffixes in which to search for the specified destination host. .It Cm CanonicalizeFallbackLocal Specifies whether to fail with an error when hostname canonicalization fails. The default, .Cm yes , will attempt to look up the unqualified hostname using the system resolver's search rules. A value of .Cm no will cause .Xr ssh 1 to fail instantly if .Cm CanonicalizeHostname is enabled and the target hostname cannot be found in any of the domains specified by .Cm CanonicalDomains . .It Cm CanonicalizeHostname Controls whether explicit hostname canonicalization is performed. The default, .Cm no , is not to perform any name rewriting and let the system resolver handle all hostname lookups. If set to .Cm yes then, for connections that do not use a .Cm ProxyCommand , .Xr ssh 1 will attempt to canonicalize the hostname specified on the command line using the .Cm CanonicalDomains suffixes and .Cm CanonicalizePermittedCNAMEs rules. If .Cm CanonicalizeHostname is set to .Cm always , then canonicalization is applied to proxied connections too. .Pp If this option is enabled, then the configuration files are processed again using the new target name to pick up any new configuration in matching .Cm Host and .Cm Match stanzas. .It Cm CanonicalizeMaxDots Specifies the maximum number of dot characters in a hostname before canonicalization is disabled. The default, 1, allows a single dot (i.e. hostname.subdomain). .It Cm CanonicalizePermittedCNAMEs Specifies rules to determine whether CNAMEs should be followed when canonicalizing hostnames. The rules consist of one or more arguments of .Ar source_domain_list : Ns Ar target_domain_list , where .Ar source_domain_list is a pattern-list of domains that may follow CNAMEs in canonicalization, and .Ar target_domain_list is a pattern-list of domains that they may resolve to. .Pp For example, .Qq *.a.example.com:*.b.example.com,*.c.example.com will allow hostnames matching .Qq *.a.example.com to be canonicalized to names in the .Qq *.b.example.com or .Qq *.c.example.com domains. .It Cm CertificateFile Specifies a file from which the user's certificate is read. A corresponding private key must be provided separately in order to use this certificate either from an .Cm IdentityFile directive or .Fl i flag to .Xr ssh 1 , via .Xr ssh-agent 1 , or via a .Cm PKCS11Provider . .Pp Arguments to .Cm CertificateFile may use the tilde syntax to refer to a user's home directory or the tokens described in the .Sx TOKENS section. .Pp It is possible to have multiple certificate files specified in configuration files; these certificates will be tried in sequence. Multiple .Cm CertificateFile directives will add to the list of certificates used for authentication. .It Cm ChallengeResponseAuthentication Specifies whether to use challenge-response authentication. The argument to this keyword must be .Cm yes (the default) or .Cm no . .It Cm CheckHostIP If set to .Cm yes , .Xr ssh 1 will additionally check the host IP address in the .Pa known_hosts file. This allows it to detect if a host key changed due to DNS spoofing and will add addresses of destination hosts to .Pa ~/.ssh/known_hosts in the process, regardless of the setting of .Cm StrictHostKeyChecking . If the option is set to .Cm no , the check will not be executed. The default is .Cm no . .It Cm Cipher Specifies the cipher to use for encrypting the session in protocol version 1. Currently, .Cm blowfish , .Cm 3des (the default), and .Cm des are supported, though .Cm des is only supported in the .Xr ssh 1 client for interoperability with legacy protocol 1 implementations; its use is strongly discouraged due to cryptographic weaknesses. .It Cm Ciphers Specifies the ciphers allowed for protocol version 2 in order of preference. Multiple ciphers must be comma-separated. If the specified value begins with a .Sq + character, then the specified ciphers will be appended to the default set instead of replacing them. +If the specified value begins with a +.Sq - +character, then the specified ciphers (including wildcards) will be removed +from the default set instead of replacing them. .Pp The supported ciphers are: .Bd -literal -offset indent 3des-cbc aes128-cbc aes192-cbc aes256-cbc aes128-ctr aes192-ctr aes256-ctr aes128-gcm@openssh.com aes256-gcm@openssh.com arcfour arcfour128 arcfour256 blowfish-cbc cast128-cbc chacha20-poly1305@openssh.com .Ed .Pp The default is: .Bd -literal -offset indent chacha20-poly1305@openssh.com, aes128-ctr,aes192-ctr,aes256-ctr, aes128-gcm@openssh.com,aes256-gcm@openssh.com, aes128-cbc,aes192-cbc,aes256-cbc .Ed .Pp The list of available ciphers may also be obtained using .Qq ssh -Q cipher . .It Cm ClearAllForwardings Specifies that all local, remote, and dynamic port forwardings specified in the configuration files or on the command line be cleared. This option is primarily useful when used from the .Xr ssh 1 command line to clear port forwardings set in configuration files, and is automatically set by .Xr scp 1 and .Xr sftp 1 . The argument must be .Cm yes or .Cm no (the default). .It Cm Compression Specifies whether to use compression. The argument must be .Cm yes or .Cm no (the default). .It Cm CompressionLevel Specifies the compression level to use if compression is enabled. The argument must be an integer from 1 (fast) to 9 (slow, best). The default level is 6, which is good for most applications. The meaning of the values is the same as in .Xr gzip 1 . Note that this option applies to protocol version 1 only. .It Cm ConnectionAttempts Specifies the number of tries (one per second) to make before exiting. The argument must be an integer. This may be useful in scripts if the connection sometimes fails. The default is 1. .It Cm ConnectTimeout Specifies the timeout (in seconds) used when connecting to the SSH server, instead of using the default system TCP timeout. This value is used only when the target is down or really unreachable, not when it refuses the connection. .It Cm ControlMaster Enables the sharing of multiple sessions over a single network connection. When set to .Cm yes , .Xr ssh 1 will listen for connections on a control socket specified using the .Cm ControlPath argument. Additional sessions can connect to this socket using the same .Cm ControlPath with .Cm ControlMaster set to .Cm no (the default). These sessions will try to reuse the master instance's network connection rather than initiating new ones, but will fall back to connecting normally if the control socket does not exist, or is not listening. .Pp Setting this to .Cm ask will cause .Xr ssh 1 to listen for control connections, but require confirmation using .Xr ssh-askpass 1 . If the .Cm ControlPath cannot be opened, .Xr ssh 1 will continue without connecting to a master instance. .Pp X11 and .Xr ssh-agent 1 forwarding is supported over these multiplexed connections, however the display and agent forwarded will be the one belonging to the master connection i.e. it is not possible to forward multiple displays or agents. .Pp Two additional options allow for opportunistic multiplexing: try to use a master connection but fall back to creating a new one if one does not already exist. These options are: .Cm auto and .Cm autoask . The latter requires confirmation like the .Cm ask option. .It Cm ControlPath Specify the path to the control socket used for connection sharing as described in the .Cm ControlMaster section above or the string .Cm none to disable connection sharing. Arguments to .Cm ControlPath may use the tilde syntax to refer to a user's home directory or the tokens described in the .Sx TOKENS section. It is recommended that any .Cm ControlPath used for opportunistic connection sharing include at least %h, %p, and %r (or alternatively %C) and be placed in a directory that is not writable by other users. This ensures that shared connections are uniquely identified. .It Cm ControlPersist When used in conjunction with .Cm ControlMaster , specifies that the master connection should remain open in the background (waiting for future client connections) after the initial client connection has been closed. If set to .Cm no , then the master connection will not be placed into the background, and will close as soon as the initial client connection is closed. If set to .Cm yes or 0, then the master connection will remain in the background indefinitely (until killed or closed via a mechanism such as the .Qq ssh -O exit ) . If set to a time in seconds, or a time in any of the formats documented in .Xr sshd_config 5 , then the backgrounded master connection will automatically terminate after it has remained idle (with no client connections) for the specified time. .It Cm DynamicForward Specifies that a TCP port on the local machine be forwarded over the secure channel, and the application protocol is then used to determine where to connect to from the remote machine. .Pp The argument must be .Sm off .Oo Ar bind_address : Oc Ar port . .Sm on IPv6 addresses can be specified by enclosing addresses in square brackets. By default, the local port is bound in accordance with the .Cm GatewayPorts setting. However, an explicit .Ar bind_address may be used to bind the connection to a specific address. The .Ar bind_address of .Cm localhost indicates that the listening port be bound for local use only, while an empty address or .Sq * indicates that the port should be available from all interfaces. .Pp Currently the SOCKS4 and SOCKS5 protocols are supported, and .Xr ssh 1 will act as a SOCKS server. Multiple forwardings may be specified, and additional forwardings can be given on the command line. Only the superuser can forward privileged ports. .It Cm EnableSSHKeysign Setting this option to .Cm yes in the global client configuration file .Pa /etc/ssh/ssh_config enables the use of the helper program .Xr ssh-keysign 8 during .Cm HostbasedAuthentication . The argument must be .Cm yes or .Cm no (the default). This option should be placed in the non-hostspecific section. See .Xr ssh-keysign 8 for more information. .It Cm EscapeChar Sets the escape character (default: .Ql ~ ) . The escape character can also be set on the command line. The argument should be a single character, .Ql ^ followed by a letter, or .Cm none to disable the escape character entirely (making the connection transparent for binary data). .It Cm ExitOnForwardFailure Specifies whether .Xr ssh 1 should terminate the connection if it cannot set up all requested dynamic, tunnel, local, and remote port forwardings, (e.g.\& if either end is unable to bind and listen on a specified port). Note that .Cm ExitOnForwardFailure does not apply to connections made over port forwardings and will not, for example, cause .Xr ssh 1 to exit if TCP connections to the ultimate forwarding destination fail. The argument must be .Cm yes or .Cm no (the default). .It Cm FingerprintHash Specifies the hash algorithm used when displaying key fingerprints. Valid options are: .Cm md5 and .Cm sha256 (the default). .It Cm ForwardAgent Specifies whether the connection to the authentication agent (if any) will be forwarded to the remote machine. The argument must be .Cm yes or .Cm no (the default). .Pp Agent forwarding should be enabled with caution. Users with the ability to bypass file permissions on the remote host (for the agent's Unix-domain socket) can access the local agent through the forwarded connection. An attacker cannot obtain key material from the agent, however they can perform operations on the keys that enable them to authenticate using the identities loaded into the agent. .It Cm ForwardX11 Specifies whether X11 connections will be automatically redirected over the secure channel and .Ev DISPLAY set. The argument must be .Cm yes or .Cm no (the default). .Pp X11 forwarding should be enabled with caution. Users with the ability to bypass file permissions on the remote host (for the user's X11 authorization database) can access the local X11 display through the forwarded connection. An attacker may then be able to perform activities such as keystroke monitoring if the .Cm ForwardX11Trusted option is also enabled. .It Cm ForwardX11Timeout Specify a timeout for untrusted X11 forwarding using the format described in the .Sx TIME FORMATS section of .Xr sshd_config 5 . X11 connections received by .Xr ssh 1 after this time will be refused. The default is to disable untrusted X11 forwarding after twenty minutes has elapsed. .It Cm ForwardX11Trusted If this option is set to .Cm yes , remote X11 clients will have full access to the original X11 display. .Pp If this option is set to .Cm no (the default), remote X11 clients will be considered untrusted and prevented from stealing or tampering with data belonging to trusted X11 clients. Furthermore, the .Xr xauth 1 token used for the session will be set to expire after 20 minutes. Remote clients will be refused access after this time. .Pp See the X11 SECURITY extension specification for full details on the restrictions imposed on untrusted clients. .It Cm GatewayPorts Specifies whether remote hosts are allowed to connect to local forwarded ports. By default, .Xr ssh 1 binds local port forwardings to the loopback address. This prevents other remote hosts from connecting to forwarded ports. .Cm GatewayPorts can be used to specify that ssh should bind local port forwardings to the wildcard address, thus allowing remote hosts to connect to forwarded ports. The argument must be .Cm yes or .Cm no (the default). .It Cm GlobalKnownHostsFile Specifies one or more files to use for the global host key database, separated by whitespace. The default is .Pa /etc/ssh/ssh_known_hosts , .Pa /etc/ssh/ssh_known_hosts2 . .It Cm GSSAPIAuthentication Specifies whether user authentication based on GSSAPI is allowed. The default is .Cm no . .It Cm GSSAPIDelegateCredentials Forward (delegate) credentials to the server. The default is .Cm no . .It Cm HashKnownHosts Indicates that .Xr ssh 1 should hash host names and addresses when they are added to .Pa ~/.ssh/known_hosts . These hashed names may be used normally by .Xr ssh 1 and .Xr sshd 8 , but they do not reveal identifying information should the file's contents be disclosed. The default is .Cm no . Note that existing names and addresses in known hosts files will not be converted automatically, but may be manually hashed using .Xr ssh-keygen 1 . .It Cm HostbasedAuthentication Specifies whether to try rhosts based authentication with public key authentication. The argument must be .Cm yes or .Cm no (the default). .It Cm HostbasedKeyTypes Specifies the key types that will be used for hostbased authentication as a comma-separated pattern list. Alternately if the specified value begins with a .Sq + character, then the specified key types will be appended to the default set instead of replacing them. +If the specified value begins with a +.Sq - +character, then the specified key types (including wildcards) will be removed +from the default set instead of replacing them. The default for this option is: .Bd -literal -offset 3n ecdsa-sha2-nistp256-cert-v01@openssh.com, ecdsa-sha2-nistp384-cert-v01@openssh.com, ecdsa-sha2-nistp521-cert-v01@openssh.com, ssh-ed25519-cert-v01@openssh.com, ssh-rsa-cert-v01@openssh.com, ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, ssh-ed25519,ssh-rsa .Ed .Pp The .Fl Q option of .Xr ssh 1 may be used to list supported key types. .It Cm HostKeyAlgorithms Specifies the host key algorithms that the client wants to use in order of preference. Alternately if the specified value begins with a .Sq + character, then the specified key types will be appended to the default set instead of replacing them. +If the specified value begins with a +.Sq - +character, then the specified key types (including wildcards) will be removed +from the default set instead of replacing them. The default for this option is: .Bd -literal -offset 3n ecdsa-sha2-nistp256-cert-v01@openssh.com, ecdsa-sha2-nistp384-cert-v01@openssh.com, ecdsa-sha2-nistp521-cert-v01@openssh.com, ssh-ed25519-cert-v01@openssh.com, ssh-rsa-cert-v01@openssh.com, ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, ssh-ed25519,ssh-rsa .Ed .Pp If hostkeys are known for the destination host then this default is modified to prefer their algorithms. .Pp The list of available key types may also be obtained using .Qq ssh -Q key . .It Cm HostKeyAlias Specifies an alias that should be used instead of the real host name when looking up or saving the host key in the host key database files. This option is useful for tunneling SSH connections or for multiple servers running on a single host. .It Cm HostName Specifies the real host name to log into. This can be used to specify nicknames or abbreviations for hosts. Arguments to .Cm HostName accept the tokens described in the .Sx TOKENS section. Numeric IP addresses are also permitted (both on the command line and in .Cm HostName specifications). The default is the name given on the command line. .It Cm IdentitiesOnly Specifies that .Xr ssh 1 should only use the authentication identity and certificate files explicitly configured in the .Nm files or passed on the .Xr ssh 1 command-line, even if .Xr ssh-agent 1 or a .Cm PKCS11Provider offers more identities. The argument to this keyword must be .Cm yes or .Cm no (the default). This option is intended for situations where ssh-agent offers many different identities. .It Cm IdentityAgent Specifies the .Ux Ns -domain socket used to communicate with the authentication agent. .Pp This option overrides the .Ev SSH_AUTH_SOCK environment variable and can be used to select a specific agent. Setting the socket name to .Cm none disables the use of an authentication agent. If the string .Qq SSH_AUTH_SOCK is specified, the location of the socket will be read from the .Ev SSH_AUTH_SOCK environment variable. .Pp Arguments to .Cm IdentityAgent may use the tilde syntax to refer to a user's home directory or the tokens described in the .Sx TOKENS section. .It Cm IdentityFile Specifies a file from which the user's DSA, ECDSA, Ed25519 or RSA authentication identity is read. The default is .Pa ~/.ssh/identity for protocol version 1, and .Pa ~/.ssh/id_dsa , .Pa ~/.ssh/id_ecdsa , .Pa ~/.ssh/id_ed25519 and .Pa ~/.ssh/id_rsa for protocol version 2. Additionally, any identities represented by the authentication agent will be used for authentication unless .Cm IdentitiesOnly is set. If no certificates have been explicitly specified by .Cm CertificateFile , .Xr ssh 1 will try to load certificate information from the filename obtained by appending .Pa -cert.pub to the path of a specified .Cm IdentityFile . .Pp Arguments to .Cm IdentityFile may use the tilde syntax to refer to a user's home directory or the tokens described in the .Sx TOKENS section. .Pp It is possible to have multiple identity files specified in configuration files; all these identities will be tried in sequence. Multiple .Cm IdentityFile directives will add to the list of identities tried (this behaviour differs from that of other configuration directives). .Pp .Cm IdentityFile may be used in conjunction with .Cm IdentitiesOnly to select which identities in an agent are offered during authentication. .Cm IdentityFile may also be used in conjunction with .Cm CertificateFile in order to provide any certificate also needed for authentication with the identity. .It Cm IgnoreUnknown Specifies a pattern-list of unknown options to be ignored if they are encountered in configuration parsing. This may be used to suppress errors if .Nm contains options that are unrecognised by .Xr ssh 1 . It is recommended that .Cm IgnoreUnknown be listed early in the configuration file as it will not be applied to unknown options that appear before it. .It Cm Include Include the specified configuration file(s). Multiple pathnames may be specified and each pathname may contain .Xr glob 3 wildcards and, for user configurations, shell-like .Sq ~ references to user home directories. Files without absolute paths are assumed to be in .Pa ~/.ssh if included in a user configuration file or .Pa /etc/ssh if included from the system configuration file. .Cm Include directive may appear inside a .Cm Match or .Cm Host block to perform conditional inclusion. .It Cm IPQoS Specifies the IPv4 type-of-service or DSCP class for connections. Accepted values are .Cm af11 , .Cm af12 , .Cm af13 , .Cm af21 , .Cm af22 , .Cm af23 , .Cm af31 , .Cm af32 , .Cm af33 , .Cm af41 , .Cm af42 , .Cm af43 , .Cm cs0 , .Cm cs1 , .Cm cs2 , .Cm cs3 , .Cm cs4 , .Cm cs5 , .Cm cs6 , .Cm cs7 , .Cm ef , .Cm lowdelay , .Cm throughput , .Cm reliability , or a numeric value. This option may take one or two arguments, separated by whitespace. If one argument is specified, it is used as the packet class unconditionally. If two values are specified, the first is automatically selected for interactive sessions and the second for non-interactive sessions. The default is .Cm lowdelay for interactive sessions and .Cm throughput for non-interactive sessions. .It Cm KbdInteractiveAuthentication Specifies whether to use keyboard-interactive authentication. The argument to this keyword must be .Cm yes (the default) or .Cm no . .It Cm KbdInteractiveDevices Specifies the list of methods to use in keyboard-interactive authentication. Multiple method names must be comma-separated. The default is to use the server specified list. The methods available vary depending on what the server supports. For an OpenSSH server, it may be zero or more of: .Cm bsdauth , .Cm pam , and .Cm skey . .It Cm KexAlgorithms Specifies the available KEX (Key Exchange) algorithms. Multiple algorithms must be comma-separated. Alternately if the specified value begins with a .Sq + character, then the specified methods will be appended to the default set instead of replacing them. +If the specified value begins with a +.Sq - +character, then the specified methods (including wildcards) will be removed +from the default set instead of replacing them. The default is: .Bd -literal -offset indent curve25519-sha256,curve25519-sha256@libssh.org, ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521, diffie-hellman-group-exchange-sha256, diffie-hellman-group-exchange-sha1, diffie-hellman-group14-sha1 .Ed .Pp The list of available key exchange algorithms may also be obtained using .Qq ssh -Q kex . .It Cm LocalCommand Specifies a command to execute on the local machine after successfully connecting to the server. The command string extends to the end of the line, and is executed with the user's shell. Arguments to .Cm LocalCommand accept the tokens described in the .Sx TOKENS section. .Pp The command is run synchronously and does not have access to the session of the .Xr ssh 1 that spawned it. It should not be used for interactive commands. .Pp This directive is ignored unless .Cm PermitLocalCommand has been enabled. .It Cm LocalForward Specifies that a TCP port on the local machine be forwarded over the secure channel to the specified host and port from the remote machine. The first argument must be .Sm off .Oo Ar bind_address : Oc Ar port .Sm on and the second argument must be .Ar host : Ns Ar hostport . IPv6 addresses can be specified by enclosing addresses in square brackets. Multiple forwardings may be specified, and additional forwardings can be given on the command line. Only the superuser can forward privileged ports. By default, the local port is bound in accordance with the .Cm GatewayPorts setting. However, an explicit .Ar bind_address may be used to bind the connection to a specific address. The .Ar bind_address of .Cm localhost indicates that the listening port be bound for local use only, while an empty address or .Sq * indicates that the port should be available from all interfaces. .It Cm LogLevel Gives the verbosity level that is used when logging messages from .Xr ssh 1 . The possible values are: QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3. The default is INFO. DEBUG and DEBUG1 are equivalent. DEBUG2 and DEBUG3 each specify higher levels of verbose output. .It Cm MACs Specifies the MAC (message authentication code) algorithms in order of preference. The MAC algorithm is used for data integrity protection. Multiple algorithms must be comma-separated. If the specified value begins with a .Sq + character, then the specified algorithms will be appended to the default set instead of replacing them. +If the specified value begins with a +.Sq - +character, then the specified algorithms (including wildcards) will be removed +from the default set instead of replacing them. .Pp The algorithms that contain .Qq -etm calculate the MAC after encryption (encrypt-then-mac). These are considered safer and their use recommended. .Pp The default is: .Bd -literal -offset indent umac-64-etm@openssh.com,umac-128-etm@openssh.com, hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com, hmac-sha1-etm@openssh.com, umac-64@openssh.com,umac-128@openssh.com, hmac-sha2-256,hmac-sha2-512,hmac-sha1 .Ed .Pp The list of available MAC algorithms may also be obtained using .Qq ssh -Q mac . .It Cm NoHostAuthenticationForLocalhost This option can be used if the home directory is shared across machines. In this case localhost will refer to a different machine on each of the machines and the user will get many warnings about changed host keys. However, this option disables host authentication for localhost. The argument to this keyword must be .Cm yes or -.Cm no . +.Cm no (the default). .It Cm NumberOfPasswordPrompts Specifies the number of password prompts before giving up. The argument to this keyword must be an integer. The default is 3. .It Cm PasswordAuthentication Specifies whether to use password authentication. The argument to this keyword must be .Cm yes (the default) or .Cm no . .It Cm PermitLocalCommand Allow local command execution via the .Ic LocalCommand option or using the .Ic !\& Ns Ar command escape sequence in .Xr ssh 1 . The argument must be .Cm yes or .Cm no (the default). .It Cm PKCS11Provider Specifies which PKCS#11 provider to use. The argument to this keyword is the PKCS#11 shared library .Xr ssh 1 should use to communicate with a PKCS#11 token providing the user's private RSA key. .It Cm Port Specifies the port number to connect on the remote host. The default is 22. .It Cm PreferredAuthentications Specifies the order in which the client should try authentication methods. This allows a client to prefer one method (e.g.\& .Cm keyboard-interactive ) over another method (e.g.\& .Cm password ) . The default is: .Bd -literal -offset indent gssapi-with-mic,hostbased,publickey, keyboard-interactive,password .Ed .It Cm Protocol Specifies the protocol versions .Xr ssh 1 should support in order of preference. The possible values are 1 and 2. Multiple versions must be comma-separated. When this option is set to .Cm 2,1 .Nm ssh will try version 2 and fall back to version 1 if version 2 is not available. The default is version 2. Protocol 1 suffers from a number of cryptographic weaknesses and should not be used. It is only offered to support legacy devices. .It Cm ProxyCommand Specifies the command to use to connect to the server. The command string extends to the end of the line, and is executed using the user's shell .Ql exec directive to avoid a lingering shell process. .Pp Arguments to .Cm ProxyCommand accept the tokens described in the .Sx TOKENS section. The command can be basically anything, and should read from its standard input and write to its standard output. It should eventually connect an .Xr sshd 8 server running on some machine, or execute .Ic sshd -i somewhere. Host key management will be done using the HostName of the host being connected (defaulting to the name typed by the user). Setting the command to .Cm none disables this option entirely. Note that .Cm CheckHostIP is not available for connects with a proxy command. .Pp This directive is useful in conjunction with .Xr nc 1 and its proxy support. For example, the following directive would connect via an HTTP proxy at 192.0.2.0: .Bd -literal -offset 3n ProxyCommand /usr/bin/nc -X connect -x 192.0.2.0:8080 %h %p .Ed .It Cm ProxyJump Specifies one or more jump proxies as .Xo .Sm off .Op Ar user No @ .Ar host .Op : Ns Ar port .Sm on .Xc . Multiple proxies may be separated by comma characters and will be visited sequentially. Setting this option will cause .Xr ssh 1 to connect to the target host by first making a .Xr ssh 1 connection to the specified .Cm ProxyJump host and then establishing a TCP forwarding to the ultimate target from there. .Pp Note that this option will compete with the .Cm ProxyCommand option - whichever is specified first will prevent later instances of the other from taking effect. .It Cm ProxyUseFdpass Specifies that .Cm ProxyCommand will pass a connected file descriptor back to .Xr ssh 1 instead of continuing to execute and pass data. The default is .Cm no . .It Cm PubkeyAcceptedKeyTypes Specifies the key types that will be used for public key authentication as a comma-separated pattern list. Alternately if the specified value begins with a .Sq + character, then the key types after it will be appended to the default instead of replacing it. +If the specified value begins with a +.Sq - +character, then the specified key types (including wildcards) will be removed +from the default set instead of replacing them. The default for this option is: .Bd -literal -offset 3n ecdsa-sha2-nistp256-cert-v01@openssh.com, ecdsa-sha2-nistp384-cert-v01@openssh.com, ecdsa-sha2-nistp521-cert-v01@openssh.com, ssh-ed25519-cert-v01@openssh.com, ssh-rsa-cert-v01@openssh.com, ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, ssh-ed25519,ssh-rsa .Ed .Pp The list of available key types may also be obtained using .Qq ssh -Q key . .It Cm PubkeyAuthentication Specifies whether to try public key authentication. The argument to this keyword must be .Cm yes (the default) or .Cm no . .It Cm RekeyLimit Specifies the maximum amount of data that may be transmitted before the session key is renegotiated, optionally followed a maximum amount of time that may pass before the session key is renegotiated. The first argument is specified in bytes and may have a suffix of .Sq K , .Sq M , or .Sq G to indicate Kilobytes, Megabytes, or Gigabytes, respectively. The default is between .Sq 1G and .Sq 4G , depending on the cipher. The optional second value is specified in seconds and may use any of the units documented in the .Sx TIME FORMATS section of .Xr sshd_config 5 . The default value for .Cm RekeyLimit is .Cm default none , which means that rekeying is performed after the cipher's default amount of data has been sent or received and no time based rekeying is done. .It Cm RemoteForward Specifies that a TCP port on the remote machine be forwarded over the secure channel to the specified host and port from the local machine. The first argument must be .Sm off .Oo Ar bind_address : Oc Ar port .Sm on and the second argument must be .Ar host : Ns Ar hostport . IPv6 addresses can be specified by enclosing addresses in square brackets. Multiple forwardings may be specified, and additional forwardings can be given on the command line. Privileged ports can be forwarded only when logging in as root on the remote machine. .Pp If the .Ar port argument is 0, the listen port will be dynamically allocated on the server and reported to the client at run time. .Pp If the .Ar bind_address is not specified, the default is to only bind to loopback addresses. If the .Ar bind_address is .Ql * or an empty string, then the forwarding is requested to listen on all interfaces. Specifying a remote .Ar bind_address will only succeed if the server's .Cm GatewayPorts option is enabled (see .Xr sshd_config 5 ) . .It Cm RequestTTY Specifies whether to request a pseudo-tty for the session. The argument may be one of: .Cm no (never request a TTY), .Cm yes (always request a TTY when standard input is a TTY), .Cm force (always request a TTY) or .Cm auto (request a TTY when opening a login session). This option mirrors the .Fl t and .Fl T flags for .Xr ssh 1 . .It Cm RevokedHostKeys Specifies revoked host public keys. Keys listed in this file will be refused for host authentication. Note that if this file does not exist or is not readable, then host authentication will be refused for all hosts. Keys may be specified as a text file, listing one public key per line, or as an OpenSSH Key Revocation List (KRL) as generated by .Xr ssh-keygen 1 . For more information on KRLs, see the KEY REVOCATION LISTS section in .Xr ssh-keygen 1 . .It Cm RhostsRSAAuthentication Specifies whether to try rhosts based authentication with RSA host authentication. The argument must be .Cm yes or .Cm no (the default). This option applies to protocol version 1 only and requires .Xr ssh 1 to be setuid root. .It Cm RSAAuthentication Specifies whether to try RSA authentication. The argument to this keyword must be .Cm yes (the default) or .Cm no . RSA authentication will only be attempted if the identity file exists, or an authentication agent is running. Note that this option applies to protocol version 1 only. .It Cm SendEnv Specifies what variables from the local .Xr environ 7 should be sent to the server. The server must also support it, and the server must be configured to accept these environment variables. Note that the .Ev TERM environment variable is always sent whenever a pseudo-terminal is requested as it is required by the protocol. Refer to .Cm AcceptEnv in .Xr sshd_config 5 for how to configure the server. Variables are specified by name, which may contain wildcard characters. Multiple environment variables may be separated by whitespace or spread across multiple .Cm SendEnv directives. The default is not to send any environment variables. .Pp See .Sx PATTERNS for more information on patterns. .It Cm ServerAliveCountMax Sets the number of server alive messages (see below) which may be sent without .Xr ssh 1 receiving any messages back from the server. If this threshold is reached while server alive messages are being sent, ssh will disconnect from the server, terminating the session. It is important to note that the use of server alive messages is very different from .Cm TCPKeepAlive (below). The server alive messages are sent through the encrypted channel and therefore will not be spoofable. The TCP keepalive option enabled by .Cm TCPKeepAlive is spoofable. The server alive mechanism is valuable when the client or server depend on knowing when a connection has become inactive. .Pp The default value is 3. If, for example, .Cm ServerAliveInterval (see below) is set to 15 and .Cm ServerAliveCountMax is left at the default, if the server becomes unresponsive, ssh will disconnect after approximately 45 seconds. .It Cm ServerAliveInterval Sets a timeout interval in seconds after which if no data has been received from the server, .Xr ssh 1 will send a message through the encrypted channel to request a response from the server. The default is 0, indicating that these messages will not be sent to the server. .It Cm StreamLocalBindMask Sets the octal file creation mode mask .Pq umask used when creating a Unix-domain socket file for local or remote port forwarding. This option is only used for port forwarding to a Unix-domain socket file. .Pp The default value is 0177, which creates a Unix-domain socket file that is readable and writable only by the owner. Note that not all operating systems honor the file mode on Unix-domain socket files. .It Cm StreamLocalBindUnlink Specifies whether to remove an existing Unix-domain socket file for local or remote port forwarding before creating a new one. If the socket file already exists and .Cm StreamLocalBindUnlink is not enabled, .Nm ssh will be unable to forward the port to the Unix-domain socket file. This option is only used for port forwarding to a Unix-domain socket file. .Pp The argument must be .Cm yes or .Cm no (the default). .It Cm StrictHostKeyChecking If this flag is set to .Cm yes , .Xr ssh 1 will never automatically add host keys to the .Pa ~/.ssh/known_hosts file, and refuses to connect to hosts whose host key has changed. This provides maximum protection against trojan horse attacks, though it can be annoying when the .Pa /etc/ssh/ssh_known_hosts file is poorly maintained or when connections to new hosts are frequently made. This option forces the user to manually add all new hosts. If this flag is set to .Cm no , ssh will automatically add new host keys to the user known hosts files. If this flag is set to .Cm ask (the default), new host keys will be added to the user known host files only after the user has confirmed that is what they really want to do, and ssh will refuse to connect to hosts whose host key has changed. The host keys of known hosts will be verified automatically in all cases. .It Cm TCPKeepAlive Specifies whether the system should send TCP keepalive messages to the other side. If they are sent, death of the connection or crash of one of the machines will be properly noticed. However, this means that connections will die if the route is down temporarily, and some people find it annoying. .Pp The default is .Cm yes (to send TCP keepalive messages), and the client will notice if the network goes down or the remote host dies. This is important in scripts, and many users want it too. .Pp To disable TCP keepalive messages, the value should be set to .Cm no . .It Cm Tunnel Request .Xr tun 4 device forwarding between the client and the server. The argument must be .Cm yes , .Cm point-to-point (layer 3), .Cm ethernet (layer 2), or .Cm no (the default). Specifying .Cm yes requests the default tunnel mode, which is .Cm point-to-point . .It Cm TunnelDevice Specifies the .Xr tun 4 devices to open on the client .Pq Ar local_tun and the server .Pq Ar remote_tun . .Pp The argument must be .Sm off .Ar local_tun Op : Ar remote_tun . .Sm on The devices may be specified by numerical ID or the keyword .Cm any , which uses the next available tunnel device. If .Ar remote_tun is not specified, it defaults to .Cm any . The default is .Cm any:any . .It Cm UpdateHostKeys Specifies whether .Xr ssh 1 should accept notifications of additional hostkeys from the server sent after authentication has completed and add them to .Cm UserKnownHostsFile . The argument must be .Cm yes , .Cm no (the default) or .Cm ask . Enabling this option allows learning alternate hostkeys for a server and supports graceful key rotation by allowing a server to send replacement public keys before old ones are removed. Additional hostkeys are only accepted if the key used to authenticate the host was already trusted or explicitly accepted by the user. If .Cm UpdateHostKeys is set to .Cm ask , then the user is asked to confirm the modifications to the known_hosts file. Confirmation is currently incompatible with .Cm ControlPersist , and will be disabled if it is enabled. .Pp Presently, only .Xr sshd 8 from OpenSSH 6.8 and greater support the .Qq hostkeys@openssh.com protocol extension used to inform the client of all the server's hostkeys. .It Cm UsePrivilegedPort Specifies whether to use a privileged port for outgoing connections. The argument must be .Cm yes or .Cm no (the default). If set to .Cm yes , .Xr ssh 1 must be setuid root. Note that this option must be set to .Cm yes for .Cm RhostsRSAAuthentication with older servers. .It Cm User Specifies the user to log in as. This can be useful when a different user name is used on different machines. This saves the trouble of having to remember to give the user name on the command line. .It Cm UserKnownHostsFile Specifies one or more files to use for the user host key database, separated by whitespace. The default is .Pa ~/.ssh/known_hosts , .Pa ~/.ssh/known_hosts2 . .It Cm VerifyHostKeyDNS Specifies whether to verify the remote key using DNS and SSHFP resource records. If this option is set to .Cm yes , the client will implicitly trust keys that match a secure fingerprint from DNS. Insecure fingerprints will be handled as if this option was set to .Cm ask . If this option is set to .Cm ask , information on fingerprint match will be displayed, but the user will still need to confirm new host keys according to the .Cm StrictHostKeyChecking option. The default is .Cm yes if compiled with LDNS and .Cm no otherwise. .Pp See also .Sx VERIFYING HOST KEYS in .Xr ssh 1 . .It Cm VersionAddendum Specifies a string to append to the regular version string to identify OS- or site-specific modifications. The default is -.Dq FreeBSD-20170305 . +.Dq FreeBSD-20170804 . The value .Cm none may be used to disable this. .It Cm VisualHostKey If this flag is set to .Cm yes , an ASCII art representation of the remote host key fingerprint is printed in addition to the fingerprint string at login and for unknown host keys. If this flag is set to .Cm no (the default), no fingerprint strings are printed at login and only the fingerprint string will be printed for unknown host keys. .It Cm XAuthLocation Specifies the full pathname of the .Xr xauth 1 program. The default is .Pa /usr/local/bin/xauth . .El .Sh PATTERNS A .Em pattern consists of zero or more non-whitespace characters, .Sq * (a wildcard that matches zero or more characters), or .Sq ?\& (a wildcard that matches exactly one character). For example, to specify a set of declarations for any host in the .Qq .co.uk set of domains, the following pattern could be used: .Pp .Dl Host *.co.uk .Pp The following pattern would match any host in the 192.168.0.[0-9] network range: .Pp .Dl Host 192.168.0.? .Pp A .Em pattern-list is a comma-separated list of patterns. Patterns within pattern-lists may be negated by preceding them with an exclamation mark .Pq Sq !\& . For example, to allow a key to be used from anywhere within an organization except from the .Qq dialup pool, the following entry (in authorized_keys) could be used: .Pp .Dl from=\&"!*.dialup.example.com,*.example.com\&" .Sh TOKENS Arguments to some keywords can make use of tokens, which are expanded at runtime: .Pp .Bl -tag -width XXXX -offset indent -compact .It %% A literal .Sq % . .It \&%C Shorthand for %l%h%p%r. .It %d Local user's home directory. .It %h The remote hostname. .It %i The local user ID. .It %L The local hostname. .It %l The local hostname, including the domain name. .It %n The original remote hostname, as given on the command line. .It %p The remote port. .It %r The remote username. .It %u The local username. .El .Pp .Cm Match exec accepts the tokens %%, %h, %L, %l, %n, %p, %r, and %u. .Pp .Cm CertificateFile accepts the tokens %%, %d, %h, %l, %r, and %u. .Pp .Cm ControlPath accepts the tokens %%, %C, %h, %i, %L, %l, %n, %p, %r, and %u. .Pp .Cm HostName accepts the tokens %% and %h. .Pp .Cm IdentityAgent and .Cm IdentityFile accept the tokens %%, %d, %h, %l, %r, and %u. .Pp .Cm LocalCommand accepts the tokens %%, %C, %d, %h, %l, %n, %p, %r, and %u. .Pp .Cm ProxyCommand accepts the tokens %%, %h, %p, and %r. .Sh FILES .Bl -tag -width Ds .It Pa ~/.ssh/config This is the per-user configuration file. The format of this file is described above. This file is used by the SSH client. Because of the potential for abuse, this file must have strict permissions: read/write for the user, and not accessible by others. .It Pa /etc/ssh/ssh_config Systemwide configuration file. This file provides defaults for those values that are not specified in the user's configuration file, and for those users who do not have a configuration file. This file must be world-readable. .El .Sh SEE ALSO .Xr ssh 1 .Sh AUTHORS .An -nosplit OpenSSH is a derivative of the original and free ssh 1.2.12 release by .An Tatu Ylonen . .An Aaron Campbell , Bob Beck , Markus Friedl , .An Niels Provos , Theo de Raadt and .An Dug Song removed many bugs, re-added newer features and created OpenSSH. .An Markus Friedl contributed the support for SSH protocol versions 1.5 and 2.0. diff --git a/crypto/openssh/ssh_namespace.h b/crypto/openssh/ssh_namespace.h index 875d26ba23f1..94c7d6fae1b8 100644 --- a/crypto/openssh/ssh_namespace.h +++ b/crypto/openssh/ssh_namespace.h @@ -1,930 +1,929 @@ /* * Namespace munging inspired by an equivalent hack in NetBSD's tree: add * the "Fssh_" prefix to every symbol in libssh which doesn't already have * it. This prevents collisions between symbols in libssh and symbols in * other libraries or applications which link with libssh, either directly * or indirectly (e.g. through PAM loading pam_ssh). * * A list of symbols which need munging is obtained as follows: * # nm libprivatessh.a | LC_ALL=C awk ' /^[0-9a-z]+ [Tt] [A-Za-z_][0-9A-Za-z_]*$/ && $3 !~ /^Fssh_/ { printf("#define %-39s Fssh_%s\n", $3, $3) }' | unexpand -a | LC_ALL=C sort -u * * $FreeBSD$ */ #define Blowfish_decipher Fssh_Blowfish_decipher #define Blowfish_encipher Fssh_Blowfish_encipher #define Blowfish_expand0state Fssh_Blowfish_expand0state #define Blowfish_expandstate Fssh_Blowfish_expandstate #define Blowfish_initstate Fssh_Blowfish_initstate #define Blowfish_stream2word Fssh_Blowfish_stream2word #define _ssh__compat_glob Fssh__ssh__compat_glob #define _ssh__compat_globfree Fssh__ssh__compat_globfree #define _ssh_compat_realpath Fssh__ssh_compat_realpath #define _ssh_exchange_banner Fssh__ssh_exchange_banner #define _ssh_host_key_sign Fssh__ssh_host_key_sign #define _ssh_host_private_key Fssh__ssh_host_private_key #define _ssh_host_public_key Fssh__ssh_host_public_key #define _ssh_order_hostkeyalgs Fssh__ssh_order_hostkeyalgs #define _ssh_read_banner Fssh__ssh_read_banner #define _ssh_send_banner Fssh__ssh_send_banner #define _ssh_verify_host_key Fssh__ssh_verify_host_key #define a2port Fssh_a2port #define a2tun Fssh_a2tun #define add_host_to_hostfile Fssh_add_host_to_hostfile #define add_p1p1 Fssh_add_p1p1 #define addargs Fssh_addargs #define addr_match_cidr_list Fssh_addr_match_cidr_list #define addr_match_list Fssh_addr_match_list #define addr_netmatch Fssh_addr_netmatch -#define addr_pton Fssh_addr_pton #define addr_pton_cidr Fssh_addr_pton_cidr #define ask_permission Fssh_ask_permission #define atomicio Fssh_atomicio #define atomicio6 Fssh_atomicio6 #define atomiciov Fssh_atomiciov #define atomiciov6 Fssh_atomiciov6 #define auth_request_forwarding Fssh_auth_request_forwarding #define bandwidth_limit Fssh_bandwidth_limit #define bandwidth_limit_init Fssh_bandwidth_limit_init #define barrett_reduce Fssh_barrett_reduce +#define baud_to_speed Fssh_baud_to_speed #define bcrypt_hash Fssh_bcrypt_hash #define bcrypt_pbkdf Fssh_bcrypt_pbkdf -#define bf_ssh1_cipher Fssh_bf_ssh1_cipher +#define bind_permitted Fssh_bind_permitted #define bitmap_clear_bit Fssh_bitmap_clear_bit #define bitmap_free Fssh_bitmap_free #define bitmap_from_string Fssh_bitmap_from_string #define bitmap_nbits Fssh_bitmap_nbits #define bitmap_nbytes Fssh_bitmap_nbytes #define bitmap_new Fssh_bitmap_new #define bitmap_set_bit Fssh_bitmap_set_bit #define bitmap_test_bit Fssh_bitmap_test_bit #define bitmap_to_string Fssh_bitmap_to_string #define bitmap_zero Fssh_bitmap_zero #define blf_cbc_decrypt Fssh_blf_cbc_decrypt #define blf_cbc_encrypt Fssh_blf_cbc_encrypt #define blf_dec Fssh_blf_dec #define blf_ecb_decrypt Fssh_blf_ecb_decrypt #define blf_ecb_encrypt Fssh_blf_ecb_encrypt #define blf_enc Fssh_blf_enc #define blf_key Fssh_blf_key #define buffer_append Fssh_buffer_append #define buffer_append_space Fssh_buffer_append_space #define buffer_check_alloc Fssh_buffer_check_alloc #define buffer_consume Fssh_buffer_consume #define buffer_consume_end Fssh_buffer_consume_end #define buffer_consume_end_ret Fssh_buffer_consume_end_ret #define buffer_consume_ret Fssh_buffer_consume_ret #define buffer_get Fssh_buffer_get #define buffer_get_bignum2 Fssh_buffer_get_bignum2 #define buffer_get_bignum2_ret Fssh_buffer_get_bignum2_ret #define buffer_get_char Fssh_buffer_get_char #define buffer_get_char_ret Fssh_buffer_get_char_ret #define buffer_get_cstring Fssh_buffer_get_cstring #define buffer_get_cstring_ret Fssh_buffer_get_cstring_ret #define buffer_get_ecpoint Fssh_buffer_get_ecpoint #define buffer_get_ecpoint_ret Fssh_buffer_get_ecpoint_ret #define buffer_get_int Fssh_buffer_get_int #define buffer_get_int64 Fssh_buffer_get_int64 #define buffer_get_int64_ret Fssh_buffer_get_int64_ret #define buffer_get_int_ret Fssh_buffer_get_int_ret #define buffer_get_ret Fssh_buffer_get_ret #define buffer_get_short Fssh_buffer_get_short #define buffer_get_short_ret Fssh_buffer_get_short_ret #define buffer_get_string Fssh_buffer_get_string #define buffer_get_string_ptr Fssh_buffer_get_string_ptr #define buffer_get_string_ptr_ret Fssh_buffer_get_string_ptr_ret #define buffer_get_string_ret Fssh_buffer_get_string_ret #define buffer_put_bignum2 Fssh_buffer_put_bignum2 #define buffer_put_bignum2_from_string Fssh_buffer_put_bignum2_from_string #define buffer_put_bignum2_ret Fssh_buffer_put_bignum2_ret #define buffer_put_char Fssh_buffer_put_char #define buffer_put_cstring Fssh_buffer_put_cstring #define buffer_put_ecpoint Fssh_buffer_put_ecpoint #define buffer_put_ecpoint_ret Fssh_buffer_put_ecpoint_ret #define buffer_put_int Fssh_buffer_put_int #define buffer_put_int64 Fssh_buffer_put_int64 #define buffer_put_short Fssh_buffer_put_short #define buffer_put_string Fssh_buffer_put_string #define cert_free Fssh_cert_free -#define cert_new Fssh_cert_new #define chacha_encrypt_bytes Fssh_chacha_encrypt_bytes #define chacha_ivsetup Fssh_chacha_ivsetup #define chacha_keysetup Fssh_chacha_keysetup #define chachapoly_crypt Fssh_chachapoly_crypt #define chachapoly_get_length Fssh_chachapoly_get_length #define chachapoly_init Fssh_chachapoly_init #define chan_ibuf_empty Fssh_chan_ibuf_empty #define chan_is_dead Fssh_chan_is_dead #define chan_mark_dead Fssh_chan_mark_dead #define chan_obuf_empty Fssh_chan_obuf_empty #define chan_rcvd_eow Fssh_chan_rcvd_eow #define chan_rcvd_ieof Fssh_chan_rcvd_ieof #define chan_rcvd_oclose Fssh_chan_rcvd_oclose #define chan_read_failed Fssh_chan_read_failed #define chan_send_eof2 Fssh_chan_send_eof2 -#define chan_send_ieof1 Fssh_chan_send_ieof1 #define chan_send_oclose1 Fssh_chan_send_oclose1 #define chan_shutdown_read Fssh_chan_shutdown_read #define chan_shutdown_write Fssh_chan_shutdown_write #define chan_write_failed Fssh_chan_write_failed #define channel_add_adm_permitted_opens Fssh_channel_add_adm_permitted_opens #define channel_add_permitted_opens Fssh_channel_add_permitted_opens #define channel_after_select Fssh_channel_after_select #define channel_by_id Fssh_channel_by_id +#define channel_by_remote_id Fssh_channel_by_remote_id #define channel_cancel_cleanup Fssh_channel_cancel_cleanup #define channel_cancel_lport_listener Fssh_channel_cancel_lport_listener #define channel_cancel_rport_listener Fssh_channel_cancel_rport_listener #define channel_clear_adm_permitted_opens Fssh_channel_clear_adm_permitted_opens #define channel_clear_permitted_opens Fssh_channel_clear_permitted_opens #define channel_close_all Fssh_channel_close_all #define channel_close_fd Fssh_channel_close_fd #define channel_close_fds Fssh_channel_close_fds #define channel_connect_by_listen_address Fssh_channel_connect_by_listen_address #define channel_connect_by_listen_path Fssh_channel_connect_by_listen_path #define channel_connect_stdio_fwd Fssh_channel_connect_stdio_fwd #define channel_connect_to_path Fssh_channel_connect_to_path #define channel_connect_to_port Fssh_channel_connect_to_port #define channel_disable_adm_local_opens Fssh_channel_disable_adm_local_opens #define channel_find_open Fssh_channel_find_open #define channel_free Fssh_channel_free #define channel_free_all Fssh_channel_free_all #define channel_fwd_bind_addr Fssh_channel_fwd_bind_addr #define channel_handler Fssh_channel_handler #define channel_input_close Fssh_channel_input_close #define channel_input_close_confirmation Fssh_channel_input_close_confirmation #define channel_input_data Fssh_channel_input_data #define channel_input_extended_data Fssh_channel_input_extended_data #define channel_input_ieof Fssh_channel_input_ieof #define channel_input_oclose Fssh_channel_input_oclose #define channel_input_open_confirmation Fssh_channel_input_open_confirmation #define channel_input_open_failure Fssh_channel_input_open_failure -#define channel_input_port_forward_request Fssh_channel_input_port_forward_request #define channel_input_port_open Fssh_channel_input_port_open #define channel_input_status_confirm Fssh_channel_input_status_confirm #define channel_input_window_adjust Fssh_channel_input_window_adjust #define channel_lookup Fssh_channel_lookup #define channel_new Fssh_channel_new #define channel_not_very_much_buffered_data Fssh_channel_not_very_much_buffered_data #define channel_open_message Fssh_channel_open_message #define channel_output_poll Fssh_channel_output_poll #define channel_permit_all_opens Fssh_channel_permit_all_opens #define channel_post_auth_listener Fssh_channel_post_auth_listener #define channel_post_connecting Fssh_channel_post_connecting #define channel_post_mux_client Fssh_channel_post_mux_client #define channel_post_mux_listener Fssh_channel_post_mux_listener #define channel_post_open Fssh_channel_post_open #define channel_post_output_drain_13 Fssh_channel_post_output_drain_13 #define channel_post_port_listener Fssh_channel_post_port_listener #define channel_post_x11_listener Fssh_channel_post_x11_listener #define channel_pre_connecting Fssh_channel_pre_connecting #define channel_pre_dynamic Fssh_channel_pre_dynamic #define channel_pre_input_draining Fssh_channel_pre_input_draining #define channel_pre_listener Fssh_channel_pre_listener #define channel_pre_mux_client Fssh_channel_pre_mux_client #define channel_pre_open Fssh_channel_pre_open #define channel_pre_open_13 Fssh_channel_pre_open_13 #define channel_pre_output_draining Fssh_channel_pre_output_draining #define channel_pre_x11_open Fssh_channel_pre_x11_open #define channel_pre_x11_open_13 Fssh_channel_pre_x11_open_13 #define channel_prepare_select Fssh_channel_prepare_select #define channel_print_adm_permitted_opens Fssh_channel_print_adm_permitted_opens +#define channel_proxy_downstream Fssh_channel_proxy_downstream +#define channel_proxy_upstream Fssh_channel_proxy_upstream #define channel_register_cleanup Fssh_channel_register_cleanup #define channel_register_fds Fssh_channel_register_fds #define channel_register_filter Fssh_channel_register_filter #define channel_register_open_confirm Fssh_channel_register_open_confirm #define channel_register_status_confirm Fssh_channel_register_status_confirm #define channel_request_remote_forwarding Fssh_channel_request_remote_forwarding #define channel_request_rforward_cancel Fssh_channel_request_rforward_cancel #define channel_request_start Fssh_channel_request_start #define channel_send_open Fssh_channel_send_open #define channel_send_window_changes Fssh_channel_send_window_changes #define channel_set_af Fssh_channel_set_af #define channel_set_fds Fssh_channel_set_fds #define channel_set_x11_refuse_time Fssh_channel_set_x11_refuse_time #define channel_setup_fwd_listener_streamlocal Fssh_channel_setup_fwd_listener_streamlocal #define channel_setup_fwd_listener_tcpip Fssh_channel_setup_fwd_listener_tcpip #define channel_setup_local_fwd_listener Fssh_channel_setup_local_fwd_listener #define channel_setup_remote_fwd_listener Fssh_channel_setup_remote_fwd_listener #define channel_still_open Fssh_channel_still_open #define channel_stop_listening Fssh_channel_stop_listening #define channel_update_permitted_opens Fssh_channel_update_permitted_opens #define check_crc Fssh_check_crc #define check_hostkeys_by_key_or_type Fssh_check_hostkeys_by_key_or_type #define check_key_in_hostkeys Fssh_check_key_in_hostkeys #define choose_dh Fssh_choose_dh #define choose_t Fssh_choose_t #define chop Fssh_chop #define cipher_alg_list Fssh_cipher_alg_list #define cipher_authlen Fssh_cipher_authlen #define cipher_blocksize Fssh_cipher_blocksize #define cipher_by_name Fssh_cipher_by_name #define cipher_by_number Fssh_cipher_by_number -#define cipher_cleanup Fssh_cipher_cleanup #define cipher_crypt Fssh_cipher_crypt +#define cipher_ctx_get_number Fssh_cipher_ctx_get_number +#define cipher_ctx_is_plaintext Fssh_cipher_ctx_is_plaintext +#define cipher_free Fssh_cipher_free #define cipher_get_keycontext Fssh_cipher_get_keycontext #define cipher_get_keyiv Fssh_cipher_get_keyiv #define cipher_get_keyiv_len Fssh_cipher_get_keyiv_len #define cipher_get_length Fssh_cipher_get_length #define cipher_get_number Fssh_cipher_get_number #define cipher_init Fssh_cipher_init #define cipher_is_cbc Fssh_cipher_is_cbc #define cipher_ivlen Fssh_cipher_ivlen #define cipher_keylen Fssh_cipher_keylen #define cipher_mask_ssh1 Fssh_cipher_mask_ssh1 #define cipher_name Fssh_cipher_name #define cipher_number Fssh_cipher_number #define cipher_seclen Fssh_cipher_seclen #define cipher_set_key_string Fssh_cipher_set_key_string #define cipher_set_keycontext Fssh_cipher_set_keycontext #define cipher_set_keyiv Fssh_cipher_set_keyiv #define cipher_warning_message Fssh_cipher_warning_message #define ciphers_valid Fssh_ciphers_valid #define cleanhostname Fssh_cleanhostname #define cleanup_exit Fssh_cleanup_exit #define colon Fssh_colon #define compare Fssh_compare #define compare_gps Fssh_compare_gps #define compat_cipher_proposal Fssh_compat_cipher_proposal #define compat_datafellows Fssh_compat_datafellows #define compat_kex_proposal Fssh_compat_kex_proposal #define compat_pkalg_proposal Fssh_compat_pkalg_proposal #define compress_buffer Fssh_compress_buffer #define connect_next Fssh_connect_next -#define connect_to Fssh_connect_to +#define connect_to_reason Fssh_connect_to_reason #define convtime Fssh_convtime #define crypto_hash_sha512 Fssh_crypto_hash_sha512 #define crypto_hashblocks_sha512 Fssh_crypto_hashblocks_sha512 #define crypto_scalarmult_curve25519 Fssh_crypto_scalarmult_curve25519 #define crypto_sign_ed25519 Fssh_crypto_sign_ed25519 #define crypto_sign_ed25519_keypair Fssh_crypto_sign_ed25519_keypair #define crypto_sign_ed25519_open Fssh_crypto_sign_ed25519_open #define crypto_sign_ed25519_ref_double_scalarmult_vartime Fssh_crypto_sign_ed25519_ref_double_scalarmult_vartime #define crypto_sign_ed25519_ref_fe25519_add Fssh_crypto_sign_ed25519_ref_fe25519_add #define crypto_sign_ed25519_ref_fe25519_cmov Fssh_crypto_sign_ed25519_ref_fe25519_cmov #define crypto_sign_ed25519_ref_fe25519_freeze Fssh_crypto_sign_ed25519_ref_fe25519_freeze #define crypto_sign_ed25519_ref_fe25519_getparity Fssh_crypto_sign_ed25519_ref_fe25519_getparity #define crypto_sign_ed25519_ref_fe25519_invert Fssh_crypto_sign_ed25519_ref_fe25519_invert #define crypto_sign_ed25519_ref_fe25519_iseq_vartime Fssh_crypto_sign_ed25519_ref_fe25519_iseq_vartime #define crypto_sign_ed25519_ref_fe25519_iszero Fssh_crypto_sign_ed25519_ref_fe25519_iszero #define crypto_sign_ed25519_ref_fe25519_mul Fssh_crypto_sign_ed25519_ref_fe25519_mul #define crypto_sign_ed25519_ref_fe25519_neg Fssh_crypto_sign_ed25519_ref_fe25519_neg #define crypto_sign_ed25519_ref_fe25519_pack Fssh_crypto_sign_ed25519_ref_fe25519_pack #define crypto_sign_ed25519_ref_fe25519_pow2523 Fssh_crypto_sign_ed25519_ref_fe25519_pow2523 #define crypto_sign_ed25519_ref_fe25519_setone Fssh_crypto_sign_ed25519_ref_fe25519_setone #define crypto_sign_ed25519_ref_fe25519_setzero Fssh_crypto_sign_ed25519_ref_fe25519_setzero #define crypto_sign_ed25519_ref_fe25519_square Fssh_crypto_sign_ed25519_ref_fe25519_square #define crypto_sign_ed25519_ref_fe25519_sub Fssh_crypto_sign_ed25519_ref_fe25519_sub #define crypto_sign_ed25519_ref_fe25519_unpack Fssh_crypto_sign_ed25519_ref_fe25519_unpack #define crypto_sign_ed25519_ref_isneutral_vartime Fssh_crypto_sign_ed25519_ref_isneutral_vartime #define crypto_sign_ed25519_ref_pack Fssh_crypto_sign_ed25519_ref_pack #define crypto_sign_ed25519_ref_sc25519_2interleave2 Fssh_crypto_sign_ed25519_ref_sc25519_2interleave2 #define crypto_sign_ed25519_ref_sc25519_add Fssh_crypto_sign_ed25519_ref_sc25519_add #define crypto_sign_ed25519_ref_sc25519_from32bytes Fssh_crypto_sign_ed25519_ref_sc25519_from32bytes #define crypto_sign_ed25519_ref_sc25519_from64bytes Fssh_crypto_sign_ed25519_ref_sc25519_from64bytes #define crypto_sign_ed25519_ref_sc25519_from_shortsc Fssh_crypto_sign_ed25519_ref_sc25519_from_shortsc #define crypto_sign_ed25519_ref_sc25519_isshort_vartime Fssh_crypto_sign_ed25519_ref_sc25519_isshort_vartime #define crypto_sign_ed25519_ref_sc25519_iszero_vartime Fssh_crypto_sign_ed25519_ref_sc25519_iszero_vartime #define crypto_sign_ed25519_ref_sc25519_lt_vartime Fssh_crypto_sign_ed25519_ref_sc25519_lt_vartime #define crypto_sign_ed25519_ref_sc25519_mul Fssh_crypto_sign_ed25519_ref_sc25519_mul #define crypto_sign_ed25519_ref_sc25519_mul_shortsc Fssh_crypto_sign_ed25519_ref_sc25519_mul_shortsc #define crypto_sign_ed25519_ref_sc25519_sub_nored Fssh_crypto_sign_ed25519_ref_sc25519_sub_nored #define crypto_sign_ed25519_ref_sc25519_to32bytes Fssh_crypto_sign_ed25519_ref_sc25519_to32bytes #define crypto_sign_ed25519_ref_sc25519_window3 Fssh_crypto_sign_ed25519_ref_sc25519_window3 #define crypto_sign_ed25519_ref_sc25519_window5 Fssh_crypto_sign_ed25519_ref_sc25519_window5 #define crypto_sign_ed25519_ref_scalarmult_base Fssh_crypto_sign_ed25519_ref_scalarmult_base #define crypto_sign_ed25519_ref_shortsc25519_from16bytes Fssh_crypto_sign_ed25519_ref_shortsc25519_from16bytes #define crypto_sign_ed25519_ref_unpackneg_vartime Fssh_crypto_sign_ed25519_ref_unpackneg_vartime #define crypto_verify_32 Fssh_crypto_verify_32 +#define daemonized Fssh_daemonized #define dbl_p1p1 Fssh_dbl_p1p1 #define deattack_init Fssh_deattack_init #define debug Fssh_debug #define debug2 Fssh_debug2 #define debug3 Fssh_debug3 #define deny_input_open Fssh_deny_input_open #define detect_attack Fssh_detect_attack #define dh_estimate Fssh_dh_estimate #define dh_gen_key Fssh_dh_gen_key #define dh_new_group Fssh_dh_new_group #define dh_new_group1 Fssh_dh_new_group1 #define dh_new_group14 Fssh_dh_new_group14 #define dh_new_group16 Fssh_dh_new_group16 #define dh_new_group18 Fssh_dh_new_group18 #define dh_new_group_asc Fssh_dh_new_group_asc #define dh_new_group_fallback Fssh_dh_new_group_fallback #define dh_pub_is_valid Fssh_dh_pub_is_valid #define dispatch_protocol_error Fssh_dispatch_protocol_error #define dispatch_protocol_ignore Fssh_dispatch_protocol_ignore #define dns_read_key Fssh_dns_read_key #define do_log Fssh_do_log #define do_log2 Fssh_do_log2 #define dump_base64 Fssh_dump_base64 #define enable_compat13 Fssh_enable_compat13 #define enable_compat20 Fssh_enable_compat20 #define error Fssh_error -#define evp_ssh1_3des Fssh_evp_ssh1_3des -#define evp_ssh1_bf Fssh_evp_ssh1_bf #define export_dns_rr Fssh_export_dns_rr #define fatal Fssh_fatal -#define filter_proposal Fssh_filter_proposal #define fingerprint_b64 Fssh_fingerprint_b64 #define fingerprint_hex Fssh_fingerprint_hex #define fmprintf Fssh_fmprintf #define fmt_scaled Fssh_fmt_scaled #define forward_equals Fssh_forward_equals #define free_hostkeys Fssh_free_hostkeys #define freeargs Fssh_freeargs #define freerrset Fssh_freerrset #define gen_candidates Fssh_gen_candidates #define get_hram Fssh_get_hram #define get_local_ipaddr Fssh_get_local_ipaddr #define get_local_name Fssh_get_local_name #define get_local_port Fssh_get_local_port #define get_peer_ipaddr Fssh_get_peer_ipaddr #define get_peer_port Fssh_get_peer_port #define get_sock_port Fssh_get_sock_port #define get_socket_address Fssh_get_socket_address #define get_u16 Fssh_get_u16 #define get_u32 Fssh_get_u32 #define get_u32_le Fssh_get_u32_le #define get_u64 Fssh_get_u64 #define getrrsetbyname Fssh_getrrsetbyname #define glob0 Fssh_glob0 #define glob2 Fssh_glob2 #define globexp1 Fssh_globexp1 #define globextend Fssh_globextend #define host_delete Fssh_host_delete #define host_hash Fssh_host_hash #define hostfile_read_key Fssh_hostfile_read_key #define hostfile_replace_entries Fssh_hostfile_replace_entries #define hostkeys_foreach Fssh_hostkeys_foreach #define hpdelim Fssh_hpdelim #define init_hostkeys Fssh_init_hostkeys #define input_kex_c25519_init Fssh_input_kex_c25519_init #define input_kex_c25519_reply Fssh_input_kex_c25519_reply #define input_kex_dh Fssh_input_kex_dh #define input_kex_dh_gex_group Fssh_input_kex_dh_gex_group #define input_kex_dh_gex_init Fssh_input_kex_dh_gex_init #define input_kex_dh_gex_reply Fssh_input_kex_dh_gex_reply #define input_kex_dh_gex_request Fssh_input_kex_dh_gex_request #define input_kex_dh_init Fssh_input_kex_dh_init #define input_kex_ecdh_init Fssh_input_kex_ecdh_init #define input_kex_ecdh_reply Fssh_input_kex_ecdh_reply #define iptos2str Fssh_iptos2str #define ipv64_normalise_mapped Fssh_ipv64_normalise_mapped #define is_key_revoked Fssh_is_key_revoked #define kex_alg_by_name Fssh_kex_alg_by_name #define kex_alg_list Fssh_kex_alg_list #define kex_assemble_names Fssh_kex_assemble_names #define kex_buf2prop Fssh_kex_buf2prop #define kex_c25519_hash Fssh_kex_c25519_hash #define kex_derive_keys Fssh_kex_derive_keys #define kex_derive_keys_bn Fssh_kex_derive_keys_bn #define kex_dh_hash Fssh_kex_dh_hash #define kex_ecdh_hash Fssh_kex_ecdh_hash #define kex_free Fssh_kex_free #define kex_free_newkeys Fssh_kex_free_newkeys #define kex_input_ext_info Fssh_kex_input_ext_info #define kex_input_kexinit Fssh_kex_input_kexinit #define kex_input_newkeys Fssh_kex_input_newkeys #define kex_names_cat Fssh_kex_names_cat #define kex_names_valid Fssh_kex_names_valid #define kex_new Fssh_kex_new #define kex_prop2buf Fssh_kex_prop2buf #define kex_prop_free Fssh_kex_prop_free #define kex_protocol_error Fssh_kex_protocol_error #define kex_send_kexinit Fssh_kex_send_kexinit #define kex_send_newkeys Fssh_kex_send_newkeys #define kex_setup Fssh_kex_setup #define kex_start_rekex Fssh_kex_start_rekex #define kexc25519_client Fssh_kexc25519_client #define kexc25519_keygen Fssh_kexc25519_keygen #define kexc25519_server Fssh_kexc25519_server #define kexc25519_shared_key Fssh_kexc25519_shared_key #define kexdh_client Fssh_kexdh_client #define kexdh_server Fssh_kexdh_server #define kexecdh_client Fssh_kexecdh_client #define kexecdh_server Fssh_kexecdh_server #define kexgex_client Fssh_kexgex_client #define kexgex_hash Fssh_kexgex_hash #define kexgex_server Fssh_kexgex_server #define key_add_private Fssh_key_add_private -#define key_alg_list Fssh_key_alg_list #define key_cert_check_authority Fssh_key_cert_check_authority #define key_cert_copy Fssh_key_cert_copy #define key_certify Fssh_key_certify #define key_demote Fssh_key_demote #define key_drop_cert Fssh_key_drop_cert #define key_ec_validate_private Fssh_key_ec_validate_private #define key_ec_validate_public Fssh_key_ec_validate_public #define key_from_blob Fssh_key_from_blob #define key_from_private Fssh_key_from_private #define key_generate Fssh_key_generate #define key_load_cert Fssh_key_load_cert #define key_load_file Fssh_key_load_file #define key_load_private Fssh_key_load_private #define key_load_private_cert Fssh_key_load_private_cert #define key_load_private_type Fssh_key_load_private_type #define key_load_public Fssh_key_load_public #define key_new_private Fssh_key_new_private #define key_perm_ok Fssh_key_perm_ok #define key_private_deserialize Fssh_key_private_deserialize #define key_private_serialize Fssh_key_private_serialize #define key_read Fssh_key_read #define key_save_private Fssh_key_save_private #define key_sign Fssh_key_sign #define key_to_blob Fssh_key_to_blob #define key_to_certified Fssh_key_to_certified #define key_verify Fssh_key_verify #define key_write Fssh_key_write #define load_hostkeys Fssh_load_hostkeys #define log_change_level Fssh_log_change_level #define log_facility_name Fssh_log_facility_name #define log_facility_number Fssh_log_facility_number #define log_init Fssh_log_init #define log_is_on_stderr Fssh_log_is_on_stderr #define log_level_name Fssh_log_level_name #define log_level_number Fssh_log_level_number #define log_redirect_stderr_to Fssh_log_redirect_stderr_to #define logdie Fssh_logdie #define logit Fssh_logit #define lookup_key_in_hostkeys_by_type Fssh_lookup_key_in_hostkeys_by_type #define lowercase Fssh_lowercase #define mac_alg_list Fssh_mac_alg_list #define mac_check Fssh_mac_check #define mac_clear Fssh_mac_clear #define mac_compute Fssh_mac_compute #define mac_init Fssh_mac_init #define mac_setup Fssh_mac_setup #define mac_valid Fssh_mac_valid #define match Fssh_match +#define match_filter_list Fssh_match_filter_list #define match_host_and_ip Fssh_match_host_and_ip #define match_hostname Fssh_match_hostname #define match_list Fssh_match_list -#define match_maybe_hashed Fssh_match_maybe_hashed #define match_pattern Fssh_match_pattern #define match_pattern_list Fssh_match_pattern_list #define match_user Fssh_match_user #define mktemp_proto Fssh_mktemp_proto #define mm_choose_dh Fssh_mm_choose_dh #define mm_receive_fd Fssh_mm_receive_fd #define mm_send_fd Fssh_mm_send_fd #define mm_sshkey_sign Fssh_mm_sshkey_sign #define monotime Fssh_monotime #define monotime_double Fssh_monotime_double #define mprintf Fssh_mprintf #define ms_subtract_diff Fssh_ms_subtract_diff #define ms_to_timeval Fssh_ms_to_timeval -#define mult Fssh_mult +#define msetlocale Fssh_msetlocale #define mysignal Fssh_mysignal #define newkeys_from_blob Fssh_newkeys_from_blob #define newkeys_to_blob Fssh_newkeys_to_blob #define nh_aux Fssh_nh_aux #define nh_final Fssh_nh_final #define packet_close Fssh_packet_close #define packet_disconnect Fssh_packet_disconnect #define packet_get_char Fssh_packet_get_char #define packet_get_int Fssh_packet_get_int #define packet_process_incoming Fssh_packet_process_incoming #define packet_read_expect Fssh_packet_read_expect #define packet_read_poll_seqnr Fssh_packet_read_poll_seqnr #define packet_read_seqnr Fssh_packet_read_seqnr #define packet_send_debug Fssh_packet_send_debug #define packet_set_connection Fssh_packet_set_connection #define packet_write_poll Fssh_packet_write_poll #define packet_write_wait Fssh_packet_write_wait #define parse_ipqos Fssh_parse_ipqos #define parse_prime Fssh_parse_prime #define parse_user_host_port Fssh_parse_user_host_port #define percent_expand Fssh_percent_expand #define permanently_drop_suid Fssh_permanently_drop_suid #define permanently_set_uid Fssh_permanently_set_uid #define permitopen_port Fssh_permitopen_port #define pkcs11_add_provider Fssh_pkcs11_add_provider #define pkcs11_del_provider Fssh_pkcs11_del_provider #define pkcs11_fetch_keys_filter Fssh_pkcs11_fetch_keys_filter #define pkcs11_find Fssh_pkcs11_find #define pkcs11_init Fssh_pkcs11_init #define pkcs11_provider_finalize Fssh_pkcs11_provider_finalize -#define pkcs11_provider_unref Fssh_pkcs11_provider_unref #define pkcs11_rsa_finish Fssh_pkcs11_rsa_finish #define pkcs11_rsa_private_decrypt Fssh_pkcs11_rsa_private_decrypt #define pkcs11_rsa_private_encrypt Fssh_pkcs11_rsa_private_encrypt #define pkcs11_terminate Fssh_pkcs11_terminate #define plain_key_blob Fssh_plain_key_blob #define platform_disable_tracing Fssh_platform_disable_tracing #define platform_pledge_agent Fssh_platform_pledge_agent #define platform_pledge_mux Fssh_platform_pledge_mux #define platform_pledge_sftp_server Fssh_platform_pledge_sftp_server #define pledge Fssh_pledge #define poly1305_auth Fssh_poly1305_auth #define poly64 Fssh_poly64 #define poly_hash Fssh_poly_hash #define port_open_helper Fssh_port_open_helper #define prime_test Fssh_prime_test #define proto_spec Fssh_proto_spec -#define put_bitmap Fssh_put_bitmap #define put_host_port Fssh_put_host_port #define put_u16 Fssh_put_u16 #define put_u32 Fssh_put_u32 #define put_u32_le Fssh_put_u32_le #define put_u64 Fssh_put_u64 #define pwcopy Fssh_pwcopy #define qfileout Fssh_qfileout #define read_keyfile_line Fssh_read_keyfile_line #define read_mux Fssh_read_mux #define read_passphrase Fssh_read_passphrase +#define recip Fssh_recip #define record_hostkey Fssh_record_hostkey #define reduce_add_sub Fssh_reduce_add_sub #define refresh_progress_meter Fssh_refresh_progress_meter #define replacearg Fssh_replacearg #define restore_uid Fssh_restore_uid #define revoke_blob Fssh_revoke_blob #define revoked_blob_tree_RB_REMOVE Fssh_revoked_blob_tree_RB_REMOVE #define revoked_certs_for_ca_key Fssh_revoked_certs_for_ca_key #define revoked_serial_tree_RB_REMOVE Fssh_revoked_serial_tree_RB_REMOVE #define rijndaelEncrypt Fssh_rijndaelEncrypt #define rijndaelKeySetupEnc Fssh_rijndaelKeySetupEnc #define rsa_generate_additional_parameters Fssh_rsa_generate_additional_parameters #define rsa_private_decrypt Fssh_rsa_private_decrypt #define rsa_public_encrypt Fssh_rsa_public_encrypt #define sanitise_stdfd Fssh_sanitise_stdfd #define scan_scaled Fssh_scan_scaled #define seed_rng Fssh_seed_rng #define set_log_handler Fssh_set_log_handler #define set_nodelay Fssh_set_nodelay #define set_nonblock Fssh_set_nonblock #define shadow_pw Fssh_shadow_pw #define sieve_large Fssh_sieve_large #define sig_winch Fssh_sig_winch #define sigdie Fssh_sigdie #define snmprintf Fssh_snmprintf #define sock_set_v6only Fssh_sock_set_v6only -#define square Fssh_square -#define ssh1_3des_cbc Fssh_ssh1_3des_cbc -#define ssh1_3des_cleanup Fssh_ssh1_3des_cleanup -#define ssh1_3des_init Fssh_ssh1_3des_init -#define ssh1_3des_iv Fssh_ssh1_3des_iv +#define speed_to_baud Fssh_speed_to_baud #define ssh_OpenSSL_add_all_algorithms Fssh_ssh_OpenSSL_add_all_algorithms #define ssh_add_hostkey Fssh_ssh_add_hostkey #define ssh_add_identity_constrained Fssh_ssh_add_identity_constrained #define ssh_agent_sign Fssh_ssh_agent_sign #define ssh_alloc_session_state Fssh_ssh_alloc_session_state #define ssh_close_authentication_socket Fssh_ssh_close_authentication_socket #define ssh_compatible_openssl Fssh_ssh_compatible_openssl #define ssh_crc32 Fssh_ssh_crc32 #define ssh_digest_alg_by_name Fssh_ssh_digest_alg_by_name #define ssh_digest_alg_name Fssh_ssh_digest_alg_name #define ssh_digest_blocksize Fssh_ssh_digest_blocksize #define ssh_digest_buffer Fssh_ssh_digest_buffer #define ssh_digest_bytes Fssh_ssh_digest_bytes #define ssh_digest_copy_state Fssh_ssh_digest_copy_state #define ssh_digest_final Fssh_ssh_digest_final #define ssh_digest_free Fssh_ssh_digest_free #define ssh_digest_memory Fssh_ssh_digest_memory #define ssh_digest_start Fssh_ssh_digest_start #define ssh_digest_update Fssh_ssh_digest_update #define ssh_digest_update_buffer Fssh_ssh_digest_update_buffer #define ssh_dispatch_init Fssh_ssh_dispatch_init #define ssh_dispatch_range Fssh_ssh_dispatch_range #define ssh_dispatch_run Fssh_ssh_dispatch_run #define ssh_dispatch_run_fatal Fssh_ssh_dispatch_run_fatal #define ssh_dispatch_set Fssh_ssh_dispatch_set #define ssh_dss_sign Fssh_ssh_dss_sign #define ssh_dss_verify Fssh_ssh_dss_verify #define ssh_ecdsa_sign Fssh_ssh_ecdsa_sign #define ssh_ecdsa_verify Fssh_ssh_ecdsa_verify #define ssh_ed25519_sign Fssh_ssh_ed25519_sign #define ssh_ed25519_verify Fssh_ssh_ed25519_verify #define ssh_err Fssh_ssh_err #define ssh_fetch_identitylist Fssh_ssh_fetch_identitylist #define ssh_free Fssh_ssh_free #define ssh_free_identitylist Fssh_ssh_free_identitylist #define ssh_gai_strerror Fssh_ssh_gai_strerror #define ssh_get_app_data Fssh_ssh_get_app_data #define ssh_get_authentication_socket Fssh_ssh_get_authentication_socket #define ssh_get_progname Fssh_ssh_get_progname #define ssh_hmac_bytes Fssh_ssh_hmac_bytes #define ssh_hmac_final Fssh_ssh_hmac_final #define ssh_hmac_free Fssh_ssh_hmac_free #define ssh_hmac_init Fssh_ssh_hmac_init #define ssh_hmac_start Fssh_ssh_hmac_start #define ssh_hmac_update Fssh_ssh_hmac_update #define ssh_hmac_update_buffer Fssh_ssh_hmac_update_buffer #define ssh_init Fssh_ssh_init #define ssh_input_append Fssh_ssh_input_append #define ssh_input_space Fssh_ssh_input_space #define ssh_krl_check_key Fssh_ssh_krl_check_key #define ssh_krl_file_contains_key Fssh_ssh_krl_file_contains_key #define ssh_krl_free Fssh_ssh_krl_free #define ssh_krl_from_blob Fssh_ssh_krl_from_blob #define ssh_krl_init Fssh_ssh_krl_init #define ssh_krl_revoke_cert_by_key_id Fssh_ssh_krl_revoke_cert_by_key_id #define ssh_krl_revoke_cert_by_serial Fssh_ssh_krl_revoke_cert_by_serial #define ssh_krl_revoke_cert_by_serial_range Fssh_ssh_krl_revoke_cert_by_serial_range #define ssh_krl_revoke_key Fssh_ssh_krl_revoke_key #define ssh_krl_revoke_key_explicit Fssh_ssh_krl_revoke_key_explicit #define ssh_krl_revoke_key_sha1 Fssh_ssh_krl_revoke_key_sha1 #define ssh_krl_set_comment Fssh_ssh_krl_set_comment #define ssh_krl_set_version Fssh_ssh_krl_set_version #define ssh_krl_to_blob Fssh_ssh_krl_to_blob #define ssh_local_ipaddr Fssh_ssh_local_ipaddr #define ssh_local_port Fssh_ssh_local_port #define ssh_lock_agent Fssh_ssh_lock_agent #define ssh_malloc_init Fssh_ssh_malloc_init #define ssh_msg_recv Fssh_ssh_msg_recv #define ssh_msg_send Fssh_ssh_msg_send #define ssh_output_consume Fssh_ssh_output_consume #define ssh_output_ptr Fssh_ssh_output_ptr #define ssh_output_space Fssh_ssh_output_space #define ssh_packet_close Fssh_ssh_packet_close #define ssh_packet_connection_af Fssh_ssh_packet_connection_af #define ssh_packet_connection_is_on_socket Fssh_ssh_packet_connection_is_on_socket #define ssh_packet_disconnect Fssh_ssh_packet_disconnect #define ssh_packet_enable_delayed_compress Fssh_ssh_packet_enable_delayed_compress #define ssh_packet_get_bignum2 Fssh_ssh_packet_get_bignum2 #define ssh_packet_get_bytes Fssh_ssh_packet_get_bytes #define ssh_packet_get_char Fssh_ssh_packet_get_char #define ssh_packet_get_connection_in Fssh_ssh_packet_get_connection_in #define ssh_packet_get_connection_out Fssh_ssh_packet_get_connection_out #define ssh_packet_get_cstring Fssh_ssh_packet_get_cstring #define ssh_packet_get_ecpoint Fssh_ssh_packet_get_ecpoint #define ssh_packet_get_input Fssh_ssh_packet_get_input #define ssh_packet_get_int Fssh_ssh_packet_get_int #define ssh_packet_get_int64 Fssh_ssh_packet_get_int64 #define ssh_packet_get_maxsize Fssh_ssh_packet_get_maxsize +#define ssh_packet_get_mux Fssh_ssh_packet_get_mux #define ssh_packet_get_output Fssh_ssh_packet_get_output #define ssh_packet_get_protocol_flags Fssh_ssh_packet_get_protocol_flags #define ssh_packet_get_rekey_timeout Fssh_ssh_packet_get_rekey_timeout #define ssh_packet_get_state Fssh_ssh_packet_get_state #define ssh_packet_get_string Fssh_ssh_packet_get_string #define ssh_packet_get_string_ptr Fssh_ssh_packet_get_string_ptr #define ssh_packet_have_data_to_write Fssh_ssh_packet_have_data_to_write #define ssh_packet_inc_alive_timeouts Fssh_ssh_packet_inc_alive_timeouts #define ssh_packet_is_interactive Fssh_ssh_packet_is_interactive #define ssh_packet_is_rekeying Fssh_ssh_packet_is_rekeying +#define ssh_packet_log_type Fssh_ssh_packet_log_type #define ssh_packet_need_rekeying Fssh_ssh_packet_need_rekeying #define ssh_packet_next Fssh_ssh_packet_next #define ssh_packet_not_very_much_data_to_write Fssh_ssh_packet_not_very_much_data_to_write #define ssh_packet_payload Fssh_ssh_packet_payload #define ssh_packet_process_incoming Fssh_ssh_packet_process_incoming #define ssh_packet_put Fssh_ssh_packet_put #define ssh_packet_put_bignum2 Fssh_ssh_packet_put_bignum2 #define ssh_packet_put_char Fssh_ssh_packet_put_char #define ssh_packet_put_cstring Fssh_ssh_packet_put_cstring #define ssh_packet_put_ecpoint Fssh_ssh_packet_put_ecpoint #define ssh_packet_put_int Fssh_ssh_packet_put_int #define ssh_packet_put_int64 Fssh_ssh_packet_put_int64 #define ssh_packet_put_raw Fssh_ssh_packet_put_raw #define ssh_packet_put_string Fssh_ssh_packet_put_string #define ssh_packet_read Fssh_ssh_packet_read #define ssh_packet_read_expect Fssh_ssh_packet_read_expect #define ssh_packet_read_poll1 Fssh_ssh_packet_read_poll1 #define ssh_packet_read_poll2 Fssh_ssh_packet_read_poll2 #define ssh_packet_read_poll_seqnr Fssh_ssh_packet_read_poll_seqnr #define ssh_packet_read_seqnr Fssh_ssh_packet_read_seqnr #define ssh_packet_remaining Fssh_ssh_packet_remaining #define ssh_packet_send Fssh_ssh_packet_send #define ssh_packet_send1 Fssh_ssh_packet_send1 #define ssh_packet_send2 Fssh_ssh_packet_send2 #define ssh_packet_send2_wrapped Fssh_ssh_packet_send2_wrapped #define ssh_packet_send_debug Fssh_ssh_packet_send_debug #define ssh_packet_send_ignore Fssh_ssh_packet_send_ignore #define ssh_packet_set_alive_timeouts Fssh_ssh_packet_set_alive_timeouts #define ssh_packet_set_authenticated Fssh_ssh_packet_set_authenticated -#define ssh_packet_set_compress_hooks Fssh_ssh_packet_set_compress_hooks #define ssh_packet_set_connection Fssh_ssh_packet_set_connection #define ssh_packet_set_encryption_key Fssh_ssh_packet_set_encryption_key +#define ssh_packet_set_input_hook Fssh_ssh_packet_set_input_hook #define ssh_packet_set_interactive Fssh_ssh_packet_set_interactive +#define ssh_packet_set_log_preamble Fssh_ssh_packet_set_log_preamble #define ssh_packet_set_maxsize Fssh_ssh_packet_set_maxsize +#define ssh_packet_set_mux Fssh_ssh_packet_set_mux #define ssh_packet_set_nonblocking Fssh_ssh_packet_set_nonblocking #define ssh_packet_set_protocol_flags Fssh_ssh_packet_set_protocol_flags #define ssh_packet_set_rekey_limits Fssh_ssh_packet_set_rekey_limits #define ssh_packet_set_server Fssh_ssh_packet_set_server #define ssh_packet_set_state Fssh_ssh_packet_set_state #define ssh_packet_set_timeout Fssh_ssh_packet_set_timeout #define ssh_packet_set_tos Fssh_ssh_packet_set_tos #define ssh_packet_start Fssh_ssh_packet_start #define ssh_packet_start_compression Fssh_ssh_packet_start_compression #define ssh_packet_start_discard Fssh_ssh_packet_start_discard #define ssh_packet_stop_discard Fssh_ssh_packet_stop_discard #define ssh_packet_write_poll Fssh_ssh_packet_write_poll #define ssh_packet_write_wait Fssh_ssh_packet_write_wait #define ssh_remote_ipaddr Fssh_ssh_remote_ipaddr #define ssh_remote_port Fssh_ssh_remote_port #define ssh_remove_all_identities Fssh_ssh_remove_all_identities #define ssh_remove_identity Fssh_ssh_remove_identity #define ssh_request_reply Fssh_ssh_request_reply #define ssh_rsa_sign Fssh_ssh_rsa_sign #define ssh_rsa_verify Fssh_ssh_rsa_verify #define ssh_set_app_data Fssh_ssh_set_app_data #define ssh_set_newkeys Fssh_ssh_set_newkeys #define ssh_set_verify_host_key_callback Fssh_ssh_set_verify_host_key_callback #define ssh_update_card Fssh_ssh_update_card #define sshbuf_alloc Fssh_sshbuf_alloc +#define sshbuf_allocate Fssh_sshbuf_allocate #define sshbuf_avail Fssh_sshbuf_avail #define sshbuf_b64tod Fssh_sshbuf_b64tod #define sshbuf_check_reserve Fssh_sshbuf_check_reserve #define sshbuf_consume Fssh_sshbuf_consume #define sshbuf_consume_end Fssh_sshbuf_consume_end #define sshbuf_dtob16 Fssh_sshbuf_dtob16 #define sshbuf_dtob64 Fssh_sshbuf_dtob64 #define sshbuf_dump Fssh_sshbuf_dump #define sshbuf_dump_data Fssh_sshbuf_dump_data #define sshbuf_dup_string Fssh_sshbuf_dup_string #define sshbuf_free Fssh_sshbuf_free #define sshbuf_from Fssh_sshbuf_from #define sshbuf_fromb Fssh_sshbuf_fromb #define sshbuf_froms Fssh_sshbuf_froms #define sshbuf_get Fssh_sshbuf_get #define sshbuf_get_bignum1 Fssh_sshbuf_get_bignum1 #define sshbuf_get_bignum2 Fssh_sshbuf_get_bignum2 #define sshbuf_get_bignum2_bytes_direct Fssh_sshbuf_get_bignum2_bytes_direct #define sshbuf_get_cstring Fssh_sshbuf_get_cstring #define sshbuf_get_ec Fssh_sshbuf_get_ec #define sshbuf_get_eckey Fssh_sshbuf_get_eckey #define sshbuf_get_string Fssh_sshbuf_get_string #define sshbuf_get_string_direct Fssh_sshbuf_get_string_direct #define sshbuf_get_stringb Fssh_sshbuf_get_stringb #define sshbuf_get_u16 Fssh_sshbuf_get_u16 #define sshbuf_get_u32 Fssh_sshbuf_get_u32 #define sshbuf_get_u64 Fssh_sshbuf_get_u64 #define sshbuf_get_u8 Fssh_sshbuf_get_u8 #define sshbuf_init Fssh_sshbuf_init #define sshbuf_len Fssh_sshbuf_len #define sshbuf_max_size Fssh_sshbuf_max_size #define sshbuf_mutable_ptr Fssh_sshbuf_mutable_ptr #define sshbuf_new Fssh_sshbuf_new #define sshbuf_parent Fssh_sshbuf_parent #define sshbuf_peek_string_direct Fssh_sshbuf_peek_string_direct #define sshbuf_ptr Fssh_sshbuf_ptr #define sshbuf_put Fssh_sshbuf_put #define sshbuf_put_bignum1 Fssh_sshbuf_put_bignum1 #define sshbuf_put_bignum2 Fssh_sshbuf_put_bignum2 #define sshbuf_put_bignum2_bytes Fssh_sshbuf_put_bignum2_bytes #define sshbuf_put_cstring Fssh_sshbuf_put_cstring #define sshbuf_put_ec Fssh_sshbuf_put_ec #define sshbuf_put_eckey Fssh_sshbuf_put_eckey #define sshbuf_put_string Fssh_sshbuf_put_string #define sshbuf_put_stringb Fssh_sshbuf_put_stringb #define sshbuf_put_u16 Fssh_sshbuf_put_u16 #define sshbuf_put_u32 Fssh_sshbuf_put_u32 #define sshbuf_put_u64 Fssh_sshbuf_put_u64 #define sshbuf_put_u8 Fssh_sshbuf_put_u8 #define sshbuf_putb Fssh_sshbuf_putb #define sshbuf_putf Fssh_sshbuf_putf #define sshbuf_putfv Fssh_sshbuf_putfv #define sshbuf_refcount Fssh_sshbuf_refcount #define sshbuf_reserve Fssh_sshbuf_reserve #define sshbuf_reset Fssh_sshbuf_reset #define sshbuf_set_max_size Fssh_sshbuf_set_max_size #define sshbuf_set_parent Fssh_sshbuf_set_parent #define sshkey_add_private Fssh_sshkey_add_private +#define sshkey_alg_list Fssh_sshkey_alg_list #define sshkey_cert_check_authority Fssh_sshkey_cert_check_authority #define sshkey_cert_copy Fssh_sshkey_cert_copy #define sshkey_cert_type Fssh_sshkey_cert_type #define sshkey_certify Fssh_sshkey_certify #define sshkey_check_revoked Fssh_sshkey_check_revoked #define sshkey_curve_name_to_nid Fssh_sshkey_curve_name_to_nid #define sshkey_curve_nid_to_bits Fssh_sshkey_curve_nid_to_bits #define sshkey_curve_nid_to_name Fssh_sshkey_curve_nid_to_name #define sshkey_demote Fssh_sshkey_demote #define sshkey_drop_cert Fssh_sshkey_drop_cert #define sshkey_dump_ec_key Fssh_sshkey_dump_ec_key #define sshkey_dump_ec_point Fssh_sshkey_dump_ec_point #define sshkey_ec_nid_to_hash_alg Fssh_sshkey_ec_nid_to_hash_alg #define sshkey_ec_validate_private Fssh_sshkey_ec_validate_private #define sshkey_ec_validate_public Fssh_sshkey_ec_validate_public #define sshkey_ecdsa_bits_to_nid Fssh_sshkey_ecdsa_bits_to_nid #define sshkey_ecdsa_key_to_nid Fssh_sshkey_ecdsa_key_to_nid #define sshkey_ecdsa_nid_from_name Fssh_sshkey_ecdsa_nid_from_name #define sshkey_equal Fssh_sshkey_equal #define sshkey_equal_public Fssh_sshkey_equal_public #define sshkey_fingerprint Fssh_sshkey_fingerprint #define sshkey_fingerprint_raw Fssh_sshkey_fingerprint_raw #define sshkey_format_cert_validity Fssh_sshkey_format_cert_validity #define sshkey_free Fssh_sshkey_free #define sshkey_from_blob Fssh_sshkey_from_blob #define sshkey_from_blob_internal Fssh_sshkey_from_blob_internal #define sshkey_from_private Fssh_sshkey_from_private #define sshkey_fromb Fssh_sshkey_fromb #define sshkey_froms Fssh_sshkey_froms #define sshkey_generate Fssh_sshkey_generate #define sshkey_in_file Fssh_sshkey_in_file #define sshkey_is_cert Fssh_sshkey_is_cert #define sshkey_load_cert Fssh_sshkey_load_cert #define sshkey_load_file Fssh_sshkey_load_file #define sshkey_load_private Fssh_sshkey_load_private #define sshkey_load_private_cert Fssh_sshkey_load_private_cert #define sshkey_load_private_type Fssh_sshkey_load_private_type #define sshkey_load_private_type_fd Fssh_sshkey_load_private_type_fd #define sshkey_load_public Fssh_sshkey_load_public #define sshkey_names_valid2 Fssh_sshkey_names_valid2 #define sshkey_new Fssh_sshkey_new #define sshkey_new_private Fssh_sshkey_new_private #define sshkey_parse_private2 Fssh_sshkey_parse_private2 #define sshkey_parse_private_fileblob Fssh_sshkey_parse_private_fileblob #define sshkey_parse_private_fileblob_type Fssh_sshkey_parse_private_fileblob_type #define sshkey_parse_private_pem_fileblob Fssh_sshkey_parse_private_pem_fileblob #define sshkey_perm_ok Fssh_sshkey_perm_ok #define sshkey_plain_to_blob Fssh_sshkey_plain_to_blob #define sshkey_private_deserialize Fssh_sshkey_private_deserialize #define sshkey_private_serialize Fssh_sshkey_private_serialize #define sshkey_private_to_blob2 Fssh_sshkey_private_to_blob2 #define sshkey_private_to_fileblob Fssh_sshkey_private_to_fileblob #define sshkey_putb Fssh_sshkey_putb #define sshkey_putb_plain Fssh_sshkey_putb_plain #define sshkey_puts Fssh_sshkey_puts #define sshkey_read Fssh_sshkey_read #define sshkey_save_private Fssh_sshkey_save_private #define sshkey_sign Fssh_sshkey_sign #define sshkey_size Fssh_sshkey_size #define sshkey_ssh_name Fssh_sshkey_ssh_name #define sshkey_ssh_name_plain Fssh_sshkey_ssh_name_plain #define sshkey_to_base64 Fssh_sshkey_to_base64 #define sshkey_to_blob Fssh_sshkey_to_blob #define sshkey_to_certified Fssh_sshkey_to_certified #define sshkey_try_load_public Fssh_sshkey_try_load_public #define sshkey_type Fssh_sshkey_type #define sshkey_type_from_name Fssh_sshkey_type_from_name #define sshkey_type_is_cert Fssh_sshkey_type_is_cert #define sshkey_type_plain Fssh_sshkey_type_plain #define sshkey_verify Fssh_sshkey_verify #define sshkey_write Fssh_sshkey_write #define sshpkt_add_padding Fssh_sshpkt_add_padding #define sshpkt_disconnect Fssh_sshpkt_disconnect #define sshpkt_fatal Fssh_sshpkt_fatal #define sshpkt_get Fssh_sshpkt_get #define sshpkt_get_bignum2 Fssh_sshpkt_get_bignum2 #define sshpkt_get_cstring Fssh_sshpkt_get_cstring #define sshpkt_get_ec Fssh_sshpkt_get_ec #define sshpkt_get_end Fssh_sshpkt_get_end #define sshpkt_get_string Fssh_sshpkt_get_string #define sshpkt_get_string_direct Fssh_sshpkt_get_string_direct #define sshpkt_get_u32 Fssh_sshpkt_get_u32 #define sshpkt_get_u64 Fssh_sshpkt_get_u64 #define sshpkt_get_u8 Fssh_sshpkt_get_u8 #define sshpkt_ptr Fssh_sshpkt_ptr #define sshpkt_put Fssh_sshpkt_put #define sshpkt_put_bignum2 Fssh_sshpkt_put_bignum2 #define sshpkt_put_cstring Fssh_sshpkt_put_cstring #define sshpkt_put_ec Fssh_sshpkt_put_ec #define sshpkt_put_string Fssh_sshpkt_put_string #define sshpkt_put_stringb Fssh_sshpkt_put_stringb #define sshpkt_put_u32 Fssh_sshpkt_put_u32 #define sshpkt_put_u64 Fssh_sshpkt_put_u64 #define sshpkt_put_u8 Fssh_sshpkt_put_u8 #define sshpkt_putb Fssh_sshpkt_putb #define sshpkt_send Fssh_sshpkt_send #define sshpkt_start Fssh_sshpkt_start #define start_progress_meter Fssh_start_progress_meter #define stop_progress_meter Fssh_stop_progress_meter #define stravis Fssh_stravis #define strdelim Fssh_strdelim #define strnvis Fssh_strnvis #define strvis Fssh_strvis #define strvisx Fssh_strvisx #define sys_tun_open Fssh_sys_tun_open #define temporarily_use_uid Fssh_temporarily_use_uid #define tilde_expand_filename Fssh_tilde_expand_filename #define to_blob Fssh_to_blob #define to_blob_buf Fssh_to_blob_buf #define tohex Fssh_tohex #define tty_make_modes Fssh_tty_make_modes #define tty_parse_modes Fssh_tty_parse_modes #define tun_open Fssh_tun_open #define umac128_delete Fssh_umac128_delete #define umac128_final Fssh_umac128_final #define umac128_new Fssh_umac128_new #define umac128_update Fssh_umac128_update #define umac_delete Fssh_umac_delete #define umac_final Fssh_umac_final #define umac_new Fssh_umac_new #define umac_update Fssh_umac_update #define uncompress_buffer Fssh_uncompress_buffer #define unix_listener Fssh_unix_listener #define unset_nonblock Fssh_unset_nonblock #define update_progress_meter Fssh_update_progress_meter #define uudecode Fssh_uudecode #define uuencode Fssh_uuencode #define vasnmprintf Fssh_vasnmprintf #define verbose Fssh_verbose #define verify_host_key_dns Fssh_verify_host_key_dns #define vfmprintf Fssh_vfmprintf #define vis Fssh_vis #define write_host_entry Fssh_write_host_entry #define x11_connect_display Fssh_x11_connect_display #define x11_create_display_inet Fssh_x11_create_display_inet #define x11_input_open Fssh_x11_input_open #define x11_open_helper Fssh_x11_open_helper #define x11_request_forwarding_with_spoofing Fssh_x11_request_forwarding_with_spoofing #define xasprintf Fssh_xasprintf #define xcalloc Fssh_xcalloc #define xcrypt Fssh_xcrypt #define xmalloc Fssh_xmalloc -#define xmmap Fssh_xmmap #define xreallocarray Fssh_xreallocarray #define xstrdup Fssh_xstrdup diff --git a/crypto/openssh/sshconnect.c b/crypto/openssh/sshconnect.c index 4ae0ed1e61aa..02aee69c5684 100644 --- a/crypto/openssh/sshconnect.c +++ b/crypto/openssh/sshconnect.c @@ -1,1544 +1,1546 @@ -/* $OpenBSD: sshconnect.c,v 1.272 2016/09/12 01:22:38 deraadt Exp $ */ +/* $OpenBSD: sshconnect.c,v 1.273 2017/03/10 03:22:40 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Code to connect to a remote host, and to perform the client side of the * login (authentication) dialog. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #include "includes.h" __RCSID("$FreeBSD$"); #include #include #include #include #ifdef HAVE_SYS_TIME_H # include #endif #include #include #include #include #include #include #include #ifdef HAVE_PATHS_H #include #endif #include #include #include #include #include #include #include #include "xmalloc.h" #include "key.h" #include "hostfile.h" #include "ssh.h" #include "rsa.h" #include "buffer.h" #include "packet.h" #include "uidswap.h" #include "compat.h" #include "key.h" #include "sshconnect.h" #include "hostfile.h" #include "log.h" #include "misc.h" #include "readconf.h" #include "atomicio.h" #include "dns.h" #include "monitor_fdpass.h" #include "ssh2.h" #include "version.h" #include "authfile.h" #include "ssherr.h" #include "authfd.h" char *client_version_string = NULL; char *server_version_string = NULL; Key *previous_host_key = NULL; static int matching_host_key_dns = 0; static pid_t proxy_command_pid = 0; /* import */ extern Options options; extern char *__progname; extern uid_t original_real_uid; extern uid_t original_effective_uid; static int show_other_keys(struct hostkeys *, Key *); static void warn_changed_key(Key *); /* Expand a proxy command */ static char * expand_proxy_command(const char *proxy_command, const char *user, const char *host, int port) { char *tmp, *ret, strport[NI_MAXSERV]; snprintf(strport, sizeof strport, "%d", port); xasprintf(&tmp, "exec %s", proxy_command); ret = percent_expand(tmp, "h", host, "p", strport, "r", options.user, (char *)NULL); free(tmp); return ret; } /* * Connect to the given ssh server using a proxy command that passes a * a connected fd back to us. */ static int ssh_proxy_fdpass_connect(const char *host, u_short port, const char *proxy_command) { char *command_string; int sp[2], sock; pid_t pid; char *shell; if ((shell = getenv("SHELL")) == NULL) shell = _PATH_BSHELL; if (socketpair(AF_UNIX, SOCK_STREAM, 0, sp) < 0) fatal("Could not create socketpair to communicate with " "proxy dialer: %.100s", strerror(errno)); command_string = expand_proxy_command(proxy_command, options.user, host, port); debug("Executing proxy dialer command: %.500s", command_string); /* Fork and execute the proxy command. */ if ((pid = fork()) == 0) { char *argv[10]; /* Child. Permanently give up superuser privileges. */ permanently_drop_suid(original_real_uid); close(sp[1]); /* Redirect stdin and stdout. */ if (sp[0] != 0) { if (dup2(sp[0], 0) < 0) perror("dup2 stdin"); } if (sp[0] != 1) { if (dup2(sp[0], 1) < 0) perror("dup2 stdout"); } if (sp[0] >= 2) close(sp[0]); /* * Stderr is left as it is so that error messages get * printed on the user's terminal. */ argv[0] = shell; argv[1] = "-c"; argv[2] = command_string; argv[3] = NULL; /* * Execute the proxy command. * Note that we gave up any extra privileges above. */ execv(argv[0], argv); perror(argv[0]); exit(1); } /* Parent. */ if (pid < 0) fatal("fork failed: %.100s", strerror(errno)); close(sp[0]); free(command_string); if ((sock = mm_receive_fd(sp[1])) == -1) fatal("proxy dialer did not pass back a connection"); close(sp[1]); while (waitpid(pid, NULL, 0) == -1) if (errno != EINTR) fatal("Couldn't wait for child: %s", strerror(errno)); /* Set the connection file descriptors. */ packet_set_connection(sock, sock); return 0; } /* * Connect to the given ssh server using a proxy command. */ static int ssh_proxy_connect(const char *host, u_short port, const char *proxy_command) { char *command_string; int pin[2], pout[2]; pid_t pid; char *shell; if ((shell = getenv("SHELL")) == NULL || *shell == '\0') shell = _PATH_BSHELL; /* Create pipes for communicating with the proxy. */ if (pipe(pin) < 0 || pipe(pout) < 0) fatal("Could not create pipes to communicate with the proxy: %.100s", strerror(errno)); command_string = expand_proxy_command(proxy_command, options.user, host, port); debug("Executing proxy command: %.500s", command_string); /* Fork and execute the proxy command. */ if ((pid = fork()) == 0) { char *argv[10]; /* Child. Permanently give up superuser privileges. */ permanently_drop_suid(original_real_uid); /* Redirect stdin and stdout. */ close(pin[1]); if (pin[0] != 0) { if (dup2(pin[0], 0) < 0) perror("dup2 stdin"); close(pin[0]); } close(pout[0]); if (dup2(pout[1], 1) < 0) perror("dup2 stdout"); /* Cannot be 1 because pin allocated two descriptors. */ close(pout[1]); /* Stderr is left as it is so that error messages get printed on the user's terminal. */ argv[0] = shell; argv[1] = "-c"; argv[2] = command_string; argv[3] = NULL; /* Execute the proxy command. Note that we gave up any extra privileges above. */ signal(SIGPIPE, SIG_DFL); execv(argv[0], argv); perror(argv[0]); exit(1); } /* Parent. */ if (pid < 0) fatal("fork failed: %.100s", strerror(errno)); else proxy_command_pid = pid; /* save pid to clean up later */ /* Close child side of the descriptors. */ close(pin[0]); close(pout[1]); /* Free the command name. */ free(command_string); /* Set the connection file descriptors. */ packet_set_connection(pout[0], pin[1]); /* Indicate OK return */ return 0; } void ssh_kill_proxy_command(void) { /* * Send SIGHUP to proxy command if used. We don't wait() in * case it hangs and instead rely on init to reap the child */ if (proxy_command_pid > 1) kill(proxy_command_pid, SIGHUP); } /* * Creates a (possibly privileged) socket for use as the ssh connection. */ static int ssh_create_socket(int privileged, struct addrinfo *ai) { int sock, r, gaierr; struct addrinfo hints, *res = NULL; sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (sock < 0) { error("socket: %s", strerror(errno)); return -1; } fcntl(sock, F_SETFD, FD_CLOEXEC); /* Bind the socket to an alternative local IP address */ if (options.bind_address == NULL && !privileged) return sock; if (options.bind_address) { memset(&hints, 0, sizeof(hints)); hints.ai_family = ai->ai_family; hints.ai_socktype = ai->ai_socktype; hints.ai_protocol = ai->ai_protocol; hints.ai_flags = AI_PASSIVE; gaierr = getaddrinfo(options.bind_address, NULL, &hints, &res); if (gaierr) { error("getaddrinfo: %s: %s", options.bind_address, ssh_gai_strerror(gaierr)); close(sock); return -1; } } /* * If we are running as root and want to connect to a privileged * port, bind our own socket to a privileged port. */ if (privileged) { PRIV_START; r = bindresvport_sa(sock, res ? res->ai_addr : NULL); PRIV_END; if (r < 0) { error("bindresvport_sa: af=%d %s", ai->ai_family, strerror(errno)); goto fail; } } else { if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) { error("bind: %s: %s", options.bind_address, strerror(errno)); fail: close(sock); freeaddrinfo(res); return -1; } } if (res != NULL) freeaddrinfo(res); return sock; } static int timeout_connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen, int *timeoutp) { fd_set *fdset; struct timeval tv, t_start; socklen_t optlen; int optval, rc, result = -1; gettimeofday(&t_start, NULL); if (*timeoutp <= 0) { result = connect(sockfd, serv_addr, addrlen); goto done; } set_nonblock(sockfd); rc = connect(sockfd, serv_addr, addrlen); if (rc == 0) { unset_nonblock(sockfd); result = 0; goto done; } if (errno != EINPROGRESS) { result = -1; goto done; } fdset = xcalloc(howmany(sockfd + 1, NFDBITS), sizeof(fd_mask)); FD_SET(sockfd, fdset); ms_to_timeval(&tv, *timeoutp); for (;;) { rc = select(sockfd + 1, NULL, fdset, NULL, &tv); if (rc != -1 || errno != EINTR) break; } switch (rc) { case 0: /* Timed out */ errno = ETIMEDOUT; break; case -1: /* Select error */ debug("select: %s", strerror(errno)); break; case 1: /* Completed or failed */ optval = 0; optlen = sizeof(optval); if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &optval, &optlen) == -1) { debug("getsockopt: %s", strerror(errno)); break; } if (optval != 0) { errno = optval; break; } result = 0; unset_nonblock(sockfd); break; default: /* Should not occur */ fatal("Bogus return (%d) from select()", rc); } free(fdset); done: if (result == 0 && *timeoutp > 0) { ms_subtract_diff(&t_start, timeoutp); if (*timeoutp <= 0) { errno = ETIMEDOUT; result = -1; } } return (result); } /* * Opens a TCP/IP connection to the remote server on the given host. * The address of the remote host will be returned in hostaddr. * If port is 0, the default port will be used. If needpriv is true, * a privileged port will be allocated to make the connection. * This requires super-user privileges if needpriv is true. * Connection_attempts specifies the maximum number of tries (one per * second). If proxy_command is non-NULL, it specifies the command (with %h * and %p substituted for host and port, respectively) to use to contact * the daemon. */ static int ssh_connect_direct(const char *host, struct addrinfo *aitop, struct sockaddr_storage *hostaddr, u_short port, int family, int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv) { int on = 1; int sock = -1, attempt; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; struct addrinfo *ai; debug2("%s: needpriv %d", __func__, needpriv); memset(ntop, 0, sizeof(ntop)); memset(strport, 0, sizeof(strport)); for (attempt = 0; attempt < connection_attempts; attempt++) { if (attempt > 0) { /* Sleep a moment before retrying. */ sleep(1); debug("Trying again..."); } /* * Loop through addresses for this host, and try each one in * sequence until the connection succeeds. */ for (ai = aitop; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue; if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) { error("%s: getnameinfo failed", __func__); continue; } debug("Connecting to %.200s [%.100s] port %s.", host, ntop, strport); /* Create a socket for connecting. */ sock = ssh_create_socket(needpriv, ai); if (sock < 0) /* Any error is already output */ continue; if (timeout_connect(sock, ai->ai_addr, ai->ai_addrlen, timeout_ms) >= 0) { /* Successful connection. */ memcpy(hostaddr, ai->ai_addr, ai->ai_addrlen); break; } else { debug("connect to address %s port %s: %s", ntop, strport, strerror(errno)); close(sock); sock = -1; } } if (sock != -1) break; /* Successful connection. */ } /* Return failure if we didn't get a successful connection. */ if (sock == -1) { error("ssh: connect to host %s port %s: %s", host, strport, strerror(errno)); return (-1); } debug("Connection established."); /* Set SO_KEEPALIVE if requested. */ if (want_keepalive && setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void *)&on, sizeof(on)) < 0) error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); /* Set the connection. */ packet_set_connection(sock, sock); return 0; } int ssh_connect(const char *host, struct addrinfo *addrs, struct sockaddr_storage *hostaddr, u_short port, int family, int connection_attempts, int *timeout_ms, int want_keepalive, int needpriv) { if (options.proxy_command == NULL) { return ssh_connect_direct(host, addrs, hostaddr, port, family, connection_attempts, timeout_ms, want_keepalive, needpriv); } else if (strcmp(options.proxy_command, "-") == 0) { packet_set_connection(STDIN_FILENO, STDOUT_FILENO); return 0; /* Always succeeds */ } else if (options.proxy_use_fdpass) { return ssh_proxy_fdpass_connect(host, port, options.proxy_command); } return ssh_proxy_connect(host, port, options.proxy_command); } static void send_client_banner(int connection_out, int minor1) { /* Send our own protocol version identification. */ xasprintf(&client_version_string, "SSH-%d.%d-%.100s%s%s%s", compat20 ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, compat20 ? PROTOCOL_MINOR_2 : minor1, SSH_VERSION, *options.version_addendum == '\0' ? "" : " ", options.version_addendum, compat20 ? "\r\n" : "\n"); if (atomicio(vwrite, connection_out, client_version_string, strlen(client_version_string)) != strlen(client_version_string)) fatal("write: %.100s", strerror(errno)); chop(client_version_string); debug("Local version string %.100s", client_version_string); } /* * Waits for the server identification string, and sends our own * identification string. */ void ssh_exchange_identification(int timeout_ms) { char buf[256], remote_version[256]; /* must be same size! */ int remote_major, remote_minor, mismatch; int connection_in = packet_get_connection_in(); int connection_out = packet_get_connection_out(); int minor1 = PROTOCOL_MINOR_1, client_banner_sent = 0; u_int i, n; size_t len; int fdsetsz, remaining, rc; struct timeval t_start, t_remaining; fd_set *fdset; fdsetsz = howmany(connection_in + 1, NFDBITS) * sizeof(fd_mask); fdset = xcalloc(1, fdsetsz); /* * If we are SSH2-only then we can send the banner immediately and * save a round-trip. */ if (options.protocol == SSH_PROTO_2) { enable_compat20(); send_client_banner(connection_out, 0); client_banner_sent = 1; } /* Read other side's version identification. */ remaining = timeout_ms; for (n = 0;;) { for (i = 0; i < sizeof(buf) - 1; i++) { if (timeout_ms > 0) { gettimeofday(&t_start, NULL); ms_to_timeval(&t_remaining, remaining); FD_SET(connection_in, fdset); rc = select(connection_in + 1, fdset, NULL, fdset, &t_remaining); ms_subtract_diff(&t_start, &remaining); if (rc == 0 || remaining <= 0) fatal("Connection timed out during " "banner exchange"); if (rc == -1) { if (errno == EINTR) continue; fatal("ssh_exchange_identification: " "select: %s", strerror(errno)); } } len = atomicio(read, connection_in, &buf[i], 1); if (len != 1 && errno == EPIPE) fatal("ssh_exchange_identification: " "Connection closed by remote host"); else if (len != 1) fatal("ssh_exchange_identification: " "read: %.100s", strerror(errno)); if (buf[i] == '\r') { buf[i] = '\n'; buf[i + 1] = 0; continue; /**XXX wait for \n */ } if (buf[i] == '\n') { buf[i + 1] = 0; break; } if (++n > 65536) fatal("ssh_exchange_identification: " "No banner received"); } buf[sizeof(buf) - 1] = 0; if (strncmp(buf, "SSH-", 4) == 0) break; debug("ssh_exchange_identification: %s", buf); } server_version_string = xstrdup(buf); free(fdset); /* * Check that the versions match. In future this might accept * several versions and set appropriate flags to handle them. */ if (sscanf(server_version_string, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, remote_version) != 3) fatal("Bad remote protocol version identification: '%.100s'", buf); debug("Remote protocol version %d.%d, remote software version %.100s", remote_major, remote_minor, remote_version); active_state->compat = compat_datafellows(remote_version); mismatch = 0; switch (remote_major) { case 1: if (remote_minor == 99 && (options.protocol & SSH_PROTO_2) && !(options.protocol & SSH_PROTO_1_PREFERRED)) { enable_compat20(); break; } if (!(options.protocol & SSH_PROTO_1)) { mismatch = 1; break; } if (remote_minor < 3) { fatal("Remote machine has too old SSH software version."); } else if (remote_minor == 3 || remote_minor == 4) { /* We speak 1.3, too. */ enable_compat13(); minor1 = 3; if (options.forward_agent) { logit("Agent forwarding disabled for protocol 1.3"); options.forward_agent = 0; } } break; case 2: if (options.protocol & SSH_PROTO_2) { enable_compat20(); break; } /* FALLTHROUGH */ default: mismatch = 1; break; } if (mismatch) fatal("Protocol major versions differ: %d vs. %d", (options.protocol & SSH_PROTO_2) ? PROTOCOL_MAJOR_2 : PROTOCOL_MAJOR_1, remote_major); if ((datafellows & SSH_BUG_DERIVEKEY) != 0) fatal("Server version \"%.100s\" uses unsafe key agreement; " "refusing connection", remote_version); if ((datafellows & SSH_BUG_RSASIGMD5) != 0) logit("Server version \"%.100s\" uses unsafe RSA signature " "scheme; disabling use of RSA keys", remote_version); if (!client_banner_sent) send_client_banner(connection_out, minor1); chop(server_version_string); } /* defaults to 'no' */ static int confirm(const char *prompt) { const char *msg, *again = "Please type 'yes' or 'no': "; char *p; int ret = -1; if (options.batch_mode) return 0; for (msg = prompt;;msg = again) { p = read_passphrase(msg, RP_ECHO); if (p == NULL || (p[0] == '\0') || (p[0] == '\n') || strncasecmp(p, "no", 2) == 0) ret = 0; if (p && strncasecmp(p, "yes", 3) == 0) ret = 1; free(p); if (ret != -1) return ret; } } static int check_host_cert(const char *host, const Key *host_key) { const char *reason; if (key_cert_check_authority(host_key, 1, 0, host, &reason) != 0) { error("%s", reason); return 0; } if (buffer_len(host_key->cert->critical) != 0) { error("Certificate for %s contains unsupported " "critical options(s)", host); return 0; } return 1; } static int sockaddr_is_local(struct sockaddr *hostaddr) { switch (hostaddr->sa_family) { case AF_INET: return (ntohl(((struct sockaddr_in *)hostaddr)-> sin_addr.s_addr) >> 24) == IN_LOOPBACKNET; case AF_INET6: return IN6_IS_ADDR_LOOPBACK( &(((struct sockaddr_in6 *)hostaddr)->sin6_addr)); default: return 0; } } /* * Prepare the hostname and ip address strings that are used to lookup * host keys in known_hosts files. These may have a port number appended. */ void get_hostfile_hostname_ipaddr(char *hostname, struct sockaddr *hostaddr, u_short port, char **hostfile_hostname, char **hostfile_ipaddr) { char ntop[NI_MAXHOST]; socklen_t addrlen; switch (hostaddr == NULL ? -1 : hostaddr->sa_family) { case -1: addrlen = 0; break; case AF_INET: addrlen = sizeof(struct sockaddr_in); break; case AF_INET6: addrlen = sizeof(struct sockaddr_in6); break; default: addrlen = sizeof(struct sockaddr); break; } /* * We don't have the remote ip-address for connections * using a proxy command */ if (hostfile_ipaddr != NULL) { if (options.proxy_command == NULL) { if (getnameinfo(hostaddr, addrlen, ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST) != 0) fatal("%s: getnameinfo failed", __func__); *hostfile_ipaddr = put_host_port(ntop, port); } else { *hostfile_ipaddr = xstrdup(""); } } /* * Allow the user to record the key under a different name or * differentiate a non-standard port. This is useful for ssh * tunneling over forwarded connections or if you run multiple * sshd's on different ports on the same machine. */ if (hostfile_hostname != NULL) { if (options.host_key_alias != NULL) { *hostfile_hostname = xstrdup(options.host_key_alias); debug("using hostkeyalias: %s", *hostfile_hostname); } else { *hostfile_hostname = put_host_port(hostname, port); } } } /* * check whether the supplied host key is valid, return -1 if the key * is not valid. user_hostfile[0] will not be updated if 'readonly' is true. */ #define RDRW 0 #define RDONLY 1 #define ROQUIET 2 static int check_host_key(char *hostname, struct sockaddr *hostaddr, u_short port, Key *host_key, int readonly, char **user_hostfiles, u_int num_user_hostfiles, char **system_hostfiles, u_int num_system_hostfiles) { HostStatus host_status; HostStatus ip_status; Key *raw_key = NULL; char *ip = NULL, *host = NULL; char hostline[1000], *hostp, *fp, *ra; char msg[1024]; const char *type; const struct hostkey_entry *host_found, *ip_found; int len, cancelled_forwarding = 0; int local = sockaddr_is_local(hostaddr); int r, want_cert = key_is_cert(host_key), host_ip_differ = 0; int hostkey_trusted = 0; /* Known or explicitly accepted by user */ struct hostkeys *host_hostkeys, *ip_hostkeys; u_int i; /* * Force accepting of the host key for loopback/localhost. The * problem is that if the home directory is NFS-mounted to multiple * machines, localhost will refer to a different machine in each of * them, and the user will get bogus HOST_CHANGED warnings. This * essentially disables host authentication for localhost; however, * this is probably not a real problem. */ if (options.no_host_authentication_for_localhost == 1 && local && options.host_key_alias == NULL) { debug("Forcing accepting of host key for " "loopback/localhost."); return 0; } /* * Prepare the hostname and address strings used for hostkey lookup. * In some cases, these will have a port number appended. */ get_hostfile_hostname_ipaddr(hostname, hostaddr, port, &host, &ip); /* * Turn off check_host_ip if the connection is to localhost, via proxy * command or if we don't have a hostname to compare with */ if (options.check_host_ip && (local || strcmp(hostname, ip) == 0 || options.proxy_command != NULL)) options.check_host_ip = 0; host_hostkeys = init_hostkeys(); for (i = 0; i < num_user_hostfiles; i++) load_hostkeys(host_hostkeys, host, user_hostfiles[i]); for (i = 0; i < num_system_hostfiles; i++) load_hostkeys(host_hostkeys, host, system_hostfiles[i]); ip_hostkeys = NULL; if (!want_cert && options.check_host_ip) { ip_hostkeys = init_hostkeys(); for (i = 0; i < num_user_hostfiles; i++) load_hostkeys(ip_hostkeys, ip, user_hostfiles[i]); for (i = 0; i < num_system_hostfiles; i++) load_hostkeys(ip_hostkeys, ip, system_hostfiles[i]); } retry: /* Reload these as they may have changed on cert->key downgrade */ want_cert = key_is_cert(host_key); type = key_type(host_key); /* * Check if the host key is present in the user's list of known * hosts or in the systemwide list. */ host_status = check_key_in_hostkeys(host_hostkeys, host_key, &host_found); /* * Also perform check for the ip address, skip the check if we are * localhost, looking for a certificate, or the hostname was an ip * address to begin with. */ if (!want_cert && ip_hostkeys != NULL) { ip_status = check_key_in_hostkeys(ip_hostkeys, host_key, &ip_found); if (host_status == HOST_CHANGED && (ip_status != HOST_CHANGED || (ip_found != NULL && !key_equal(ip_found->key, host_found->key)))) host_ip_differ = 1; } else ip_status = host_status; switch (host_status) { case HOST_OK: /* The host is known and the key matches. */ debug("Host '%.200s' is known and matches the %s host %s.", host, type, want_cert ? "certificate" : "key"); debug("Found %s in %s:%lu", want_cert ? "CA key" : "key", host_found->file, host_found->line); if (want_cert && !check_host_cert(hostname, host_key)) goto fail; if (options.check_host_ip && ip_status == HOST_NEW) { if (readonly || want_cert) logit("%s host key for IP address " "'%.128s' not in list of known hosts.", type, ip); else if (!add_host_to_hostfile(user_hostfiles[0], ip, host_key, options.hash_known_hosts)) logit("Failed to add the %s host key for IP " "address '%.128s' to the list of known " "hosts (%.500s).", type, ip, user_hostfiles[0]); else logit("Warning: Permanently added the %s host " "key for IP address '%.128s' to the list " "of known hosts.", type, ip); } else if (options.visual_host_key) { fp = sshkey_fingerprint(host_key, options.fingerprint_hash, SSH_FP_DEFAULT); ra = sshkey_fingerprint(host_key, options.fingerprint_hash, SSH_FP_RANDOMART); if (fp == NULL || ra == NULL) fatal("%s: sshkey_fingerprint fail", __func__); logit("Host key fingerprint is %s\n%s", fp, ra); free(ra); free(fp); } hostkey_trusted = 1; break; case HOST_NEW: if (options.host_key_alias == NULL && port != 0 && port != SSH_DEFAULT_PORT) { debug("checking without port identifier"); if (check_host_key(hostname, hostaddr, 0, host_key, ROQUIET, user_hostfiles, num_user_hostfiles, system_hostfiles, num_system_hostfiles) == 0) { debug("found matching key w/out port"); break; } } if (readonly || want_cert) goto fail; /* The host is new. */ if (options.strict_host_key_checking == 1) { /* * User has requested strict host key checking. We * will not add the host key automatically. The only * alternative left is to abort. */ error("No %s host key is known for %.200s and you " "have requested strict checking.", type, host); goto fail; } else if (options.strict_host_key_checking == 2) { char msg1[1024], msg2[1024]; if (show_other_keys(host_hostkeys, host_key)) snprintf(msg1, sizeof(msg1), "\nbut keys of different type are already" " known for this host."); else snprintf(msg1, sizeof(msg1), "."); /* The default */ fp = sshkey_fingerprint(host_key, options.fingerprint_hash, SSH_FP_DEFAULT); ra = sshkey_fingerprint(host_key, options.fingerprint_hash, SSH_FP_RANDOMART); if (fp == NULL || ra == NULL) fatal("%s: sshkey_fingerprint fail", __func__); msg2[0] = '\0'; if (options.verify_host_key_dns) { if (matching_host_key_dns) snprintf(msg2, sizeof(msg2), "Matching host key fingerprint" " found in DNS.\n"); else snprintf(msg2, sizeof(msg2), "No matching host key fingerprint" " found in DNS.\n"); } snprintf(msg, sizeof(msg), "The authenticity of host '%.200s (%s)' can't be " "established%s\n" "%s key fingerprint is %s.%s%s\n%s" "Are you sure you want to continue connecting " "(yes/no)? ", host, ip, msg1, type, fp, options.visual_host_key ? "\n" : "", options.visual_host_key ? ra : "", msg2); free(ra); free(fp); if (!confirm(msg)) goto fail; hostkey_trusted = 1; /* user explicitly confirmed */ } /* * If not in strict mode, add the key automatically to the * local known_hosts file. */ if (options.check_host_ip && ip_status == HOST_NEW) { snprintf(hostline, sizeof(hostline), "%s,%s", host, ip); hostp = hostline; if (options.hash_known_hosts) { /* Add hash of host and IP separately */ r = add_host_to_hostfile(user_hostfiles[0], host, host_key, options.hash_known_hosts) && add_host_to_hostfile(user_hostfiles[0], ip, host_key, options.hash_known_hosts); } else { /* Add unhashed "host,ip" */ r = add_host_to_hostfile(user_hostfiles[0], hostline, host_key, options.hash_known_hosts); } } else { r = add_host_to_hostfile(user_hostfiles[0], host, host_key, options.hash_known_hosts); hostp = host; } if (!r) logit("Failed to add the host to the list of known " "hosts (%.500s).", user_hostfiles[0]); else logit("Warning: Permanently added '%.200s' (%s) to the " "list of known hosts.", hostp, type); break; case HOST_REVOKED: error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("@ WARNING: REVOKED HOST KEY DETECTED! @"); error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("The %s host key for %s is marked as revoked.", type, host); error("This could mean that a stolen key is being used to"); error("impersonate this host."); /* * If strict host key checking is in use, the user will have * to edit the key manually and we can only abort. */ if (options.strict_host_key_checking) { error("%s host key for %.200s was revoked and you have " "requested strict checking.", type, host); goto fail; } goto continue_unsafe; case HOST_CHANGED: if (want_cert) { /* * This is only a debug() since it is valid to have * CAs with wildcard DNS matches that don't match * all hosts that one might visit. */ debug("Host certificate authority does not " "match %s in %s:%lu", CA_MARKER, host_found->file, host_found->line); goto fail; } if (readonly == ROQUIET) goto fail; if (options.check_host_ip && host_ip_differ) { char *key_msg; if (ip_status == HOST_NEW) key_msg = "is unknown"; else if (ip_status == HOST_OK) key_msg = "is unchanged"; else key_msg = "has a different value"; error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("@ WARNING: POSSIBLE DNS SPOOFING DETECTED! @"); error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("The %s host key for %s has changed,", type, host); error("and the key for the corresponding IP address %s", ip); error("%s. This could either mean that", key_msg); error("DNS SPOOFING is happening or the IP address for the host"); error("and its host key have changed at the same time."); if (ip_status != HOST_NEW) error("Offending key for IP in %s:%lu", ip_found->file, ip_found->line); } /* The host key has changed. */ warn_changed_key(host_key); error("Add correct host key in %.100s to get rid of this message.", user_hostfiles[0]); error("Offending %s key in %s:%lu", key_type(host_found->key), host_found->file, host_found->line); /* * If strict host key checking is in use, the user will have * to edit the key manually and we can only abort. */ if (options.strict_host_key_checking) { error("%s host key for %.200s has changed and you have " "requested strict checking.", type, host); goto fail; } continue_unsafe: /* * If strict host key checking has not been requested, allow * the connection but without MITM-able authentication or * forwarding. */ if (options.password_authentication) { error("Password authentication is disabled to avoid " "man-in-the-middle attacks."); options.password_authentication = 0; cancelled_forwarding = 1; } if (options.kbd_interactive_authentication) { error("Keyboard-interactive authentication is disabled" " to avoid man-in-the-middle attacks."); options.kbd_interactive_authentication = 0; options.challenge_response_authentication = 0; cancelled_forwarding = 1; } if (options.challenge_response_authentication) { error("Challenge/response authentication is disabled" " to avoid man-in-the-middle attacks."); options.challenge_response_authentication = 0; cancelled_forwarding = 1; } if (options.forward_agent) { error("Agent forwarding is disabled to avoid " "man-in-the-middle attacks."); options.forward_agent = 0; cancelled_forwarding = 1; } if (options.forward_x11) { error("X11 forwarding is disabled to avoid " "man-in-the-middle attacks."); options.forward_x11 = 0; cancelled_forwarding = 1; } if (options.num_local_forwards > 0 || options.num_remote_forwards > 0) { error("Port forwarding is disabled to avoid " "man-in-the-middle attacks."); options.num_local_forwards = options.num_remote_forwards = 0; cancelled_forwarding = 1; } if (options.tun_open != SSH_TUNMODE_NO) { error("Tunnel forwarding is disabled to avoid " "man-in-the-middle attacks."); options.tun_open = SSH_TUNMODE_NO; cancelled_forwarding = 1; } if (options.exit_on_forward_failure && cancelled_forwarding) fatal("Error: forwarding disabled due to host key " "check failure"); /* * XXX Should permit the user to change to use the new id. * This could be done by converting the host key to an * identifying sentence, tell that the host identifies itself * by that sentence, and ask the user if he/she wishes to * accept the authentication. */ break; case HOST_FOUND: fatal("internal error"); break; } if (options.check_host_ip && host_status != HOST_CHANGED && ip_status == HOST_CHANGED) { snprintf(msg, sizeof(msg), "Warning: the %s host key for '%.200s' " "differs from the key for the IP address '%.128s'" "\nOffending key for IP in %s:%lu", type, host, ip, ip_found->file, ip_found->line); if (host_status == HOST_OK) { len = strlen(msg); snprintf(msg + len, sizeof(msg) - len, "\nMatching host key in %s:%lu", host_found->file, host_found->line); } if (options.strict_host_key_checking == 1) { logit("%s", msg); error("Exiting, you have requested strict checking."); goto fail; } else if (options.strict_host_key_checking == 2) { strlcat(msg, "\nAre you sure you want " "to continue connecting (yes/no)? ", sizeof(msg)); if (!confirm(msg)) goto fail; } else { logit("%s", msg); } } if (!hostkey_trusted && options.update_hostkeys) { debug("%s: hostkey not known or explicitly trusted: " "disabling UpdateHostkeys", __func__); options.update_hostkeys = 0; } free(ip); free(host); if (host_hostkeys != NULL) free_hostkeys(host_hostkeys); if (ip_hostkeys != NULL) free_hostkeys(ip_hostkeys); return 0; fail: if (want_cert && host_status != HOST_REVOKED) { /* * No matching certificate. Downgrade cert to raw key and * search normally. */ debug("No matching CA found. Retry with plain key"); raw_key = key_from_private(host_key); if (key_drop_cert(raw_key) != 0) fatal("Couldn't drop certificate"); host_key = raw_key; goto retry; } if (raw_key != NULL) key_free(raw_key); free(ip); free(host); if (host_hostkeys != NULL) free_hostkeys(host_hostkeys); if (ip_hostkeys != NULL) free_hostkeys(ip_hostkeys); return -1; } /* returns 0 if key verifies or -1 if key does NOT verify */ int verify_host_key(char *host, struct sockaddr *hostaddr, Key *host_key) { u_int i; int r = -1, flags = 0; char valid[64], *fp = NULL, *cafp = NULL; struct sshkey *plain = NULL; if ((fp = sshkey_fingerprint(host_key, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { error("%s: fingerprint host key: %s", __func__, ssh_err(r)); r = -1; goto out; } if (sshkey_is_cert(host_key)) { if ((cafp = sshkey_fingerprint(host_key->cert->signature_key, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { error("%s: fingerprint CA key: %s", __func__, ssh_err(r)); r = -1; goto out; } sshkey_format_cert_validity(host_key->cert, valid, sizeof(valid)); debug("Server host certificate: %s %s, serial %llu " "ID \"%s\" CA %s %s valid %s", sshkey_ssh_name(host_key), fp, (unsigned long long)host_key->cert->serial, host_key->cert->key_id, sshkey_ssh_name(host_key->cert->signature_key), cafp, valid); for (i = 0; i < host_key->cert->nprincipals; i++) { debug2("Server host certificate hostname: %s", host_key->cert->principals[i]); } } else { debug("Server host key: %s %s", compat20 ? sshkey_ssh_name(host_key) : sshkey_type(host_key), fp); } if (sshkey_equal(previous_host_key, host_key)) { debug2("%s: server host key %s %s matches cached key", __func__, sshkey_type(host_key), fp); r = 0; goto out; } /* Check in RevokedHostKeys file if specified */ if (options.revoked_host_keys != NULL) { r = sshkey_check_revoked(host_key, options.revoked_host_keys); switch (r) { case 0: break; /* not revoked */ case SSH_ERR_KEY_REVOKED: error("Host key %s %s revoked by file %s", sshkey_type(host_key), fp, options.revoked_host_keys); r = -1; goto out; default: error("Error checking host key %s %s in " "revoked keys file %s: %s", sshkey_type(host_key), fp, options.revoked_host_keys, ssh_err(r)); r = -1; goto out; } } if (options.verify_host_key_dns) { /* * XXX certs are not yet supported for DNS, so downgrade * them and try the plain key. */ if ((r = sshkey_from_private(host_key, &plain)) != 0) goto out; if (sshkey_is_cert(plain)) sshkey_drop_cert(plain); if (verify_host_key_dns(host, hostaddr, plain, &flags) == 0) { if (flags & DNS_VERIFY_FOUND) { if (options.verify_host_key_dns == 1 && flags & DNS_VERIFY_MATCH && flags & DNS_VERIFY_SECURE) { r = 0; goto out; } if (flags & DNS_VERIFY_MATCH) { matching_host_key_dns = 1; } else { warn_changed_key(plain); error("Update the SSHFP RR in DNS " "with the new host key to get rid " "of this message."); } } } } r = check_host_key(host, hostaddr, options.port, host_key, RDRW, options.user_hostfiles, options.num_user_hostfiles, options.system_hostfiles, options.num_system_hostfiles); out: sshkey_free(plain); free(fp); free(cafp); if (r == 0 && host_key != NULL) { key_free(previous_host_key); previous_host_key = key_from_private(host_key); } return r; } /* * Starts a dialog with the server, and authenticates the current user on the * server. This does not need any extra privileges. The basic connection * to the server must already have been established before this is called. * If login fails, this function prints an error and never returns. * This function does not require super-user privileges. */ void ssh_login(Sensitive *sensitive, const char *orighost, struct sockaddr *hostaddr, u_short port, struct passwd *pw, int timeout_ms) { char *host; char *server_user, *local_user; local_user = xstrdup(pw->pw_name); server_user = options.user ? options.user : local_user; /* Convert the user-supplied hostname into all lowercase. */ host = xstrdup(orighost); lowercase(host); /* Exchange protocol version identification strings with the server. */ ssh_exchange_identification(timeout_ms); /* Put the connection into non-blocking mode. */ packet_set_nonblocking(); /* key exchange */ /* authenticate user */ debug("Authenticating to %s:%d as '%s'", host, port, server_user); if (compat20) { ssh_kex2(host, hostaddr, port); ssh_userauth2(local_user, server_user, host, sensitive); } else { #ifdef WITH_SSH1 ssh_kex(host, hostaddr); ssh_userauth1(local_user, server_user, host, sensitive); #else fatal("ssh1 is not supported"); #endif } free(local_user); } void ssh_put_password(char *password) { int size; char *padded; if (datafellows & SSH_BUG_PASSWORDPAD) { packet_put_cstring(password); return; } size = ROUNDUP(strlen(password) + 1, 32); padded = xcalloc(1, size); strlcpy(padded, password, size); packet_put_string(padded, size); explicit_bzero(padded, size); free(padded); } /* print all known host keys for a given host, but skip keys of given type */ static int show_other_keys(struct hostkeys *hostkeys, Key *key) { int type[] = { KEY_RSA1, KEY_RSA, KEY_DSA, KEY_ECDSA, KEY_ED25519, -1 }; int i, ret = 0; char *fp, *ra; const struct hostkey_entry *found; for (i = 0; type[i] != -1; i++) { if (type[i] == key->type) continue; if (!lookup_key_in_hostkeys_by_type(hostkeys, type[i], &found)) continue; fp = sshkey_fingerprint(found->key, options.fingerprint_hash, SSH_FP_DEFAULT); ra = sshkey_fingerprint(found->key, options.fingerprint_hash, SSH_FP_RANDOMART); if (fp == NULL || ra == NULL) fatal("%s: sshkey_fingerprint fail", __func__); logit("WARNING: %s key found for host %s\n" "in %s:%lu\n" "%s key fingerprint %s.", key_type(found->key), found->host, found->file, found->line, key_type(found->key), fp); if (options.visual_host_key) logit("%s", ra); free(ra); free(fp); ret = 1; } return ret; } static void warn_changed_key(Key *host_key) { char *fp; fp = sshkey_fingerprint(host_key, options.fingerprint_hash, SSH_FP_DEFAULT); if (fp == NULL) fatal("%s: sshkey_fingerprint fail", __func__); error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @"); error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@"); error("IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!"); error("Someone could be eavesdropping on you right now (man-in-the-middle attack)!"); error("It is also possible that a host key has just been changed."); error("The fingerprint for the %s key sent by the remote host is\n%s.", key_type(host_key), fp); error("Please contact your system administrator."); free(fp); } /* * Execute a local command */ int ssh_local_cmd(const char *args) { char *shell; pid_t pid; int status; void (*osighand)(int); if (!options.permit_local_command || args == NULL || !*args) return (1); if ((shell = getenv("SHELL")) == NULL || *shell == '\0') shell = _PATH_BSHELL; osighand = signal(SIGCHLD, SIG_DFL); pid = fork(); if (pid == 0) { signal(SIGPIPE, SIG_DFL); debug3("Executing %s -c \"%s\"", shell, args); execl(shell, shell, "-c", args, (char *)NULL); error("Couldn't execute %s -c \"%s\": %s", shell, args, strerror(errno)); _exit(1); } else if (pid == -1) fatal("fork failed: %.100s", strerror(errno)); while (waitpid(pid, &status, 0) == -1) if (errno != EINTR) fatal("Couldn't wait for child: %s", strerror(errno)); signal(SIGCHLD, osighand); if (!WIFEXITED(status)) return (1); return (WEXITSTATUS(status)); } void maybe_add_key_to_agent(char *authfile, Key *private, char *comment, char *passphrase) { int auth_sock = -1, r; if (options.add_keys_to_agent == 0) return; if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) { debug3("no authentication agent, not adding key"); return; } if (options.add_keys_to_agent == 2 && !ask_permission("Add key %s (%s) to agent?", authfile, comment)) { debug3("user denied adding this key"); + close(auth_sock); return; } if ((r = ssh_add_identity_constrained(auth_sock, private, comment, 0, (options.add_keys_to_agent == 3))) == 0) debug("identity added to agent: %s", authfile); else debug("could not add identity to agent: %s (%d)", authfile, r); + close(auth_sock); } diff --git a/crypto/openssh/sshconnect1.c b/crypto/openssh/sshconnect1.c index a0453618402b..dc00b4cd04d7 100644 --- a/crypto/openssh/sshconnect1.c +++ b/crypto/openssh/sshconnect1.c @@ -1,772 +1,774 @@ -/* $OpenBSD: sshconnect1.c,v 1.79 2016/09/19 07:52:42 natano Exp $ */ +/* $OpenBSD: sshconnect1.c,v 1.80 2017/03/10 03:53:11 dtucker Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * Code to connect to a remote host, and to perform the client side of the * login (authentication) dialog. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". */ #include "includes.h" #ifdef WITH_SSH1 #include #include #include #include #include #include #include #include #include #include #include "xmalloc.h" #include "ssh.h" #include "ssh1.h" #include "rsa.h" #include "buffer.h" #include "packet.h" #include "key.h" #include "cipher.h" #include "kex.h" #include "uidswap.h" #include "log.h" #include "misc.h" #include "readconf.h" #include "authfd.h" #include "sshconnect.h" #include "authfile.h" #include "canohost.h" #include "hostfile.h" #include "auth.h" #include "digest.h" #include "ssherr.h" /* Session id for the current session. */ u_char session_id[16]; u_int supported_authentications = 0; extern Options options; extern char *__progname; /* * Checks if the user has an authentication agent, and if so, tries to * authenticate using the agent. */ static int try_agent_authentication(void) { int r, type, agent_fd, ret = 0; u_char response[16]; size_t i; BIGNUM *challenge; struct ssh_identitylist *idlist = NULL; /* Get connection to the agent. */ if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) { if (r != SSH_ERR_AGENT_NOT_PRESENT) debug("%s: ssh_get_authentication_socket: %s", __func__, ssh_err(r)); return 0; } if ((challenge = BN_new()) == NULL) fatal("try_agent_authentication: BN_new failed"); /* Loop through identities served by the agent. */ if ((r = ssh_fetch_identitylist(agent_fd, 1, &idlist)) != 0) { if (r != SSH_ERR_AGENT_NO_IDENTITIES) debug("%s: ssh_fetch_identitylist: %s", __func__, ssh_err(r)); goto out; } for (i = 0; i < idlist->nkeys; i++) { /* Try this identity. */ debug("Trying RSA authentication via agent with '%.100s'", idlist->comments[i]); /* Tell the server that we are willing to authenticate using this key. */ packet_start(SSH_CMSG_AUTH_RSA); packet_put_bignum(idlist->keys[i]->rsa->n); packet_send(); packet_write_wait(); /* Wait for server's response. */ type = packet_read(); /* The server sends failure if it doesn't like our key or does not support RSA authentication. */ if (type == SSH_SMSG_FAILURE) { debug("Server refused our key."); continue; } /* Otherwise it should have sent a challenge. */ if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) packet_disconnect("Protocol error during RSA authentication: %d", type); packet_get_bignum(challenge); packet_check_eom(); debug("Received RSA challenge from server."); /* Ask the agent to decrypt the challenge. */ if ((r = ssh_decrypt_challenge(agent_fd, idlist->keys[i], challenge, session_id, response)) != 0) { /* * The agent failed to authenticate this identifier * although it advertised it supports this. Just * return a wrong value. */ logit("Authentication agent failed to decrypt " "challenge: %s", ssh_err(r)); explicit_bzero(response, sizeof(response)); } debug("Sending response to RSA challenge."); /* Send the decrypted challenge back to the server. */ packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); for (i = 0; i < 16; i++) packet_put_char(response[i]); packet_send(); packet_write_wait(); /* Wait for response from the server. */ type = packet_read(); /* * The server returns success if it accepted the * authentication. */ if (type == SSH_SMSG_SUCCESS) { debug("RSA authentication accepted by server."); ret = 1; break; } else if (type != SSH_SMSG_FAILURE) packet_disconnect("Protocol error waiting RSA auth " "response: %d", type); } if (ret != 1) debug("RSA authentication using agent refused."); out: ssh_free_identitylist(idlist); ssh_close_authentication_socket(agent_fd); BN_clear_free(challenge); return ret; } /* * Computes the proper response to a RSA challenge, and sends the response to * the server. */ static void respond_to_rsa_challenge(BIGNUM * challenge, RSA * prv) { u_char buf[32], response[16]; struct ssh_digest_ctx *md; int i, len; /* Decrypt the challenge using the private key. */ /* XXX think about Bleichenbacher, too */ if (rsa_private_decrypt(challenge, challenge, prv) != 0) packet_disconnect( "respond_to_rsa_challenge: rsa_private_decrypt failed"); /* Compute the response. */ /* The response is MD5 of decrypted challenge plus session id. */ len = BN_num_bytes(challenge); if (len <= 0 || (u_int)len > sizeof(buf)) packet_disconnect( "respond_to_rsa_challenge: bad challenge length %d", len); memset(buf, 0, sizeof(buf)); BN_bn2bin(challenge, buf + sizeof(buf) - len); if ((md = ssh_digest_start(SSH_DIGEST_MD5)) == NULL || ssh_digest_update(md, buf, 32) < 0 || ssh_digest_update(md, session_id, 16) < 0 || ssh_digest_final(md, response, sizeof(response)) < 0) fatal("%s: md5 failed", __func__); ssh_digest_free(md); debug("Sending response to host key RSA challenge."); /* Send the response back to the server. */ packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); for (i = 0; i < 16; i++) packet_put_char(response[i]); packet_send(); packet_write_wait(); explicit_bzero(buf, sizeof(buf)); explicit_bzero(response, sizeof(response)); explicit_bzero(&md, sizeof(md)); } /* * Checks if the user has authentication file, and if so, tries to authenticate * the user using it. */ static int try_rsa_authentication(int idx) { BIGNUM *challenge; Key *public, *private; char buf[300], *passphrase = NULL, *comment, *authfile; int i, perm_ok = 1, type, quit; public = options.identity_keys[idx]; authfile = options.identity_files[idx]; comment = xstrdup(authfile); debug("Trying RSA authentication with key '%.100s'", comment); /* Tell the server that we are willing to authenticate using this key. */ packet_start(SSH_CMSG_AUTH_RSA); packet_put_bignum(public->rsa->n); packet_send(); packet_write_wait(); /* Wait for server's response. */ type = packet_read(); /* * The server responds with failure if it doesn't like our key or * doesn't support RSA authentication. */ if (type == SSH_SMSG_FAILURE) { debug("Server refused our key."); free(comment); return 0; } /* Otherwise, the server should respond with a challenge. */ if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) packet_disconnect("Protocol error during RSA authentication: %d", type); /* Get the challenge from the packet. */ if ((challenge = BN_new()) == NULL) fatal("try_rsa_authentication: BN_new failed"); packet_get_bignum(challenge); packet_check_eom(); debug("Received RSA challenge from server."); /* * If the key is not stored in external hardware, we have to * load the private key. Try first with empty passphrase; if it * fails, ask for a passphrase. */ if (public->flags & SSHKEY_FLAG_EXT) private = public; else private = key_load_private_type(KEY_RSA1, authfile, "", NULL, &perm_ok); if (private == NULL && !options.batch_mode && perm_ok) { snprintf(buf, sizeof(buf), "Enter passphrase for RSA key '%.100s': ", comment); for (i = 0; i < options.number_of_password_prompts; i++) { passphrase = read_passphrase(buf, 0); if (strcmp(passphrase, "") != 0) { private = key_load_private_type(KEY_RSA1, authfile, passphrase, NULL, NULL); quit = 0; } else { debug2("no passphrase given, try next key"); quit = 1; } if (private != NULL || quit) break; debug2("bad passphrase given, try again..."); } } if (private != NULL) maybe_add_key_to_agent(authfile, private, comment, passphrase); if (passphrase != NULL) { explicit_bzero(passphrase, strlen(passphrase)); free(passphrase); } /* We no longer need the comment. */ free(comment); if (private == NULL) { if (!options.batch_mode && perm_ok) error("Bad passphrase."); /* Send a dummy response packet to avoid protocol error. */ packet_start(SSH_CMSG_AUTH_RSA_RESPONSE); for (i = 0; i < 16; i++) packet_put_char(0); packet_send(); packet_write_wait(); /* Expect the server to reject it... */ packet_read_expect(SSH_SMSG_FAILURE); BN_clear_free(challenge); return 0; } /* Compute and send a response to the challenge. */ respond_to_rsa_challenge(challenge, private->rsa); /* Destroy the private key unless it in external hardware. */ if (!(private->flags & SSHKEY_FLAG_EXT)) key_free(private); /* We no longer need the challenge. */ BN_clear_free(challenge); /* Wait for response from the server. */ type = packet_read(); if (type == SSH_SMSG_SUCCESS) { debug("RSA authentication accepted by server."); return 1; } if (type != SSH_SMSG_FAILURE) packet_disconnect("Protocol error waiting RSA auth response: %d", type); debug("RSA authentication refused."); return 0; } /* * Tries to authenticate the user using combined rhosts or /etc/hosts.equiv * authentication and RSA host authentication. */ static int try_rhosts_rsa_authentication(const char *local_user, Key * host_key) { int type; BIGNUM *challenge; debug("Trying rhosts or /etc/hosts.equiv with RSA host authentication."); /* Tell the server that we are willing to authenticate using this key. */ packet_start(SSH_CMSG_AUTH_RHOSTS_RSA); packet_put_cstring(local_user); packet_put_int(BN_num_bits(host_key->rsa->n)); packet_put_bignum(host_key->rsa->e); packet_put_bignum(host_key->rsa->n); packet_send(); packet_write_wait(); /* Wait for server's response. */ type = packet_read(); /* The server responds with failure if it doesn't admit our .rhosts authentication or doesn't know our host key. */ if (type == SSH_SMSG_FAILURE) { debug("Server refused our rhosts authentication or host key."); return 0; } /* Otherwise, the server should respond with a challenge. */ if (type != SSH_SMSG_AUTH_RSA_CHALLENGE) packet_disconnect("Protocol error during RSA authentication: %d", type); /* Get the challenge from the packet. */ if ((challenge = BN_new()) == NULL) fatal("try_rhosts_rsa_authentication: BN_new failed"); packet_get_bignum(challenge); packet_check_eom(); debug("Received RSA challenge for host key from server."); /* Compute a response to the challenge. */ respond_to_rsa_challenge(challenge, host_key->rsa); /* We no longer need the challenge. */ BN_clear_free(challenge); /* Wait for response from the server. */ type = packet_read(); if (type == SSH_SMSG_SUCCESS) { debug("Rhosts or /etc/hosts.equiv with RSA host authentication accepted by server."); return 1; } if (type != SSH_SMSG_FAILURE) packet_disconnect("Protocol error waiting RSA auth response: %d", type); debug("Rhosts or /etc/hosts.equiv with RSA host authentication refused."); return 0; } /* * Tries to authenticate with any string-based challenge/response system. * Note that the client code is not tied to s/key or TIS. */ static int try_challenge_response_authentication(void) { int type, i; u_int clen; char prompt[1024]; char *challenge, *response; debug("Doing challenge response authentication."); for (i = 0; i < options.number_of_password_prompts; i++) { /* request a challenge */ packet_start(SSH_CMSG_AUTH_TIS); packet_send(); packet_write_wait(); type = packet_read(); if (type != SSH_SMSG_FAILURE && type != SSH_SMSG_AUTH_TIS_CHALLENGE) { packet_disconnect("Protocol error: got %d in response " "to SSH_CMSG_AUTH_TIS", type); } if (type != SSH_SMSG_AUTH_TIS_CHALLENGE) { debug("No challenge."); return 0; } challenge = packet_get_string(&clen); packet_check_eom(); snprintf(prompt, sizeof prompt, "%s%s", challenge, strchr(challenge, '\n') ? "" : "\nResponse: "); free(challenge); if (i != 0) error("Permission denied, please try again."); if (options.cipher == SSH_CIPHER_NONE) logit("WARNING: Encryption is disabled! " "Response will be transmitted in clear text."); response = read_passphrase(prompt, 0); if (strcmp(response, "") == 0) { free(response); break; } packet_start(SSH_CMSG_AUTH_TIS_RESPONSE); ssh_put_password(response); explicit_bzero(response, strlen(response)); free(response); packet_send(); packet_write_wait(); type = packet_read(); if (type == SSH_SMSG_SUCCESS) return 1; if (type != SSH_SMSG_FAILURE) packet_disconnect("Protocol error: got %d in response " "to SSH_CMSG_AUTH_TIS_RESPONSE", type); } /* failure */ return 0; } /* * Tries to authenticate with plain passwd authentication. */ static int try_password_authentication(char *prompt) { int type, i; char *password; debug("Doing password authentication."); if (options.cipher == SSH_CIPHER_NONE) logit("WARNING: Encryption is disabled! Password will be transmitted in clear text."); for (i = 0; i < options.number_of_password_prompts; i++) { if (i != 0) error("Permission denied, please try again."); password = read_passphrase(prompt, 0); packet_start(SSH_CMSG_AUTH_PASSWORD); ssh_put_password(password); explicit_bzero(password, strlen(password)); free(password); packet_send(); packet_write_wait(); type = packet_read(); if (type == SSH_SMSG_SUCCESS) return 1; if (type != SSH_SMSG_FAILURE) packet_disconnect("Protocol error: got %d in response to passwd auth", type); } /* failure */ return 0; } /* * SSH1 key exchange */ void ssh_kex(char *host, struct sockaddr *hostaddr) { int i; BIGNUM *key; Key *host_key, *server_key; int bits, rbits; int ssh_cipher_default = SSH_CIPHER_3DES; u_char session_key[SSH_SESSION_KEY_LENGTH]; u_char cookie[8]; u_int supported_ciphers; u_int server_flags, client_flags; debug("Waiting for server public key."); /* Wait for a public key packet from the server. */ packet_read_expect(SSH_SMSG_PUBLIC_KEY); /* Get cookie from the packet. */ for (i = 0; i < 8; i++) cookie[i] = packet_get_char(); /* Get the public key. */ - server_key = key_new(KEY_RSA1); + if ((server_key = key_new(KEY_RSA1)) == NULL) + fatal("%s: key_new(KEY_RSA1) failed", __func__); bits = packet_get_int(); packet_get_bignum(server_key->rsa->e); packet_get_bignum(server_key->rsa->n); rbits = BN_num_bits(server_key->rsa->n); if (bits != rbits) { logit("Warning: Server lies about size of server public key: " "actual size is %d bits vs. announced %d.", rbits, bits); logit("Warning: This may be due to an old implementation of ssh."); } /* Get the host key. */ - host_key = key_new(KEY_RSA1); + if ((host_key = key_new(KEY_RSA1)) == NULL) + fatal("%s: key_new(KEY_RSA1) failed", __func__); bits = packet_get_int(); packet_get_bignum(host_key->rsa->e); packet_get_bignum(host_key->rsa->n); rbits = BN_num_bits(host_key->rsa->n); if (bits != rbits) { logit("Warning: Server lies about size of server host key: " "actual size is %d bits vs. announced %d.", rbits, bits); logit("Warning: This may be due to an old implementation of ssh."); } /* Get protocol flags. */ server_flags = packet_get_int(); packet_set_protocol_flags(server_flags); supported_ciphers = packet_get_int(); supported_authentications = packet_get_int(); packet_check_eom(); debug("Received server public key (%d bits) and host key (%d bits).", BN_num_bits(server_key->rsa->n), BN_num_bits(host_key->rsa->n)); if (verify_host_key(host, hostaddr, host_key) == -1) fatal("Host key verification failed."); client_flags = SSH_PROTOFLAG_SCREEN_NUMBER | SSH_PROTOFLAG_HOST_IN_FWD_OPEN; derive_ssh1_session_id(host_key->rsa->n, server_key->rsa->n, cookie, session_id); /* * Generate an encryption key for the session. The key is a 256 bit * random number, interpreted as a 32-byte key, with the least * significant 8 bits being the first byte of the key. */ arc4random_buf(session_key, sizeof(session_key)); /* * According to the protocol spec, the first byte of the session key * is the highest byte of the integer. The session key is xored with * the first 16 bytes of the session id. */ if ((key = BN_new()) == NULL) fatal("ssh_kex: BN_new failed"); if (BN_set_word(key, 0) == 0) fatal("ssh_kex: BN_set_word failed"); for (i = 0; i < SSH_SESSION_KEY_LENGTH; i++) { if (BN_lshift(key, key, 8) == 0) fatal("ssh_kex: BN_lshift failed"); if (i < 16) { if (BN_add_word(key, session_key[i] ^ session_id[i]) == 0) fatal("ssh_kex: BN_add_word failed"); } else { if (BN_add_word(key, session_key[i]) == 0) fatal("ssh_kex: BN_add_word failed"); } } /* * Encrypt the integer using the public key and host key of the * server (key with smaller modulus first). */ if (BN_cmp(server_key->rsa->n, host_key->rsa->n) < 0) { /* Public key has smaller modulus. */ if (BN_num_bits(host_key->rsa->n) < BN_num_bits(server_key->rsa->n) + SSH_KEY_BITS_RESERVED) { fatal("respond_to_rsa_challenge: host_key %d < server_key %d + " "SSH_KEY_BITS_RESERVED %d", BN_num_bits(host_key->rsa->n), BN_num_bits(server_key->rsa->n), SSH_KEY_BITS_RESERVED); } if (rsa_public_encrypt(key, key, server_key->rsa) != 0 || rsa_public_encrypt(key, key, host_key->rsa) != 0) fatal("%s: rsa_public_encrypt failed", __func__); } else { /* Host key has smaller modulus (or they are equal). */ if (BN_num_bits(server_key->rsa->n) < BN_num_bits(host_key->rsa->n) + SSH_KEY_BITS_RESERVED) { fatal("respond_to_rsa_challenge: server_key %d < host_key %d + " "SSH_KEY_BITS_RESERVED %d", BN_num_bits(server_key->rsa->n), BN_num_bits(host_key->rsa->n), SSH_KEY_BITS_RESERVED); } if (rsa_public_encrypt(key, key, host_key->rsa) != 0 || rsa_public_encrypt(key, key, server_key->rsa) != 0) fatal("%s: rsa_public_encrypt failed", __func__); } /* Destroy the public keys since we no longer need them. */ key_free(server_key); key_free(host_key); if (options.cipher == SSH_CIPHER_NOT_SET) { if (cipher_mask_ssh1(1) & supported_ciphers & (1 << ssh_cipher_default)) options.cipher = ssh_cipher_default; } else if (options.cipher == SSH_CIPHER_INVALID || !(cipher_mask_ssh1(1) & (1 << options.cipher))) { logit("No valid SSH1 cipher, using %.100s instead.", cipher_name(ssh_cipher_default)); options.cipher = ssh_cipher_default; } /* Check that the selected cipher is supported. */ if (!(supported_ciphers & (1 << options.cipher))) fatal("Selected cipher type %.100s not supported by server.", cipher_name(options.cipher)); debug("Encryption type: %.100s", cipher_name(options.cipher)); /* Send the encrypted session key to the server. */ packet_start(SSH_CMSG_SESSION_KEY); packet_put_char(options.cipher); /* Send the cookie back to the server. */ for (i = 0; i < 8; i++) packet_put_char(cookie[i]); /* Send and destroy the encrypted encryption key integer. */ packet_put_bignum(key); BN_clear_free(key); /* Send protocol flags. */ packet_put_int(client_flags); /* Send the packet now. */ packet_send(); packet_write_wait(); debug("Sent encrypted session key."); /* Set the encryption key. */ packet_set_encryption_key(session_key, SSH_SESSION_KEY_LENGTH, options.cipher); /* * We will no longer need the session key here. * Destroy any extra copies. */ explicit_bzero(session_key, sizeof(session_key)); /* * Expect a success message from the server. Note that this message * will be received in encrypted form. */ packet_read_expect(SSH_SMSG_SUCCESS); debug("Received encrypted confirmation."); } /* * Authenticate user */ void ssh_userauth1(const char *local_user, const char *server_user, char *host, Sensitive *sensitive) { int i, type; if (supported_authentications == 0) fatal("ssh_userauth1: server supports no auth methods"); /* Send the name of the user to log in as on the server. */ packet_start(SSH_CMSG_USER); packet_put_cstring(server_user); packet_send(); packet_write_wait(); /* * The server should respond with success if no authentication is * needed (the user has no password). Otherwise the server responds * with failure. */ type = packet_read(); /* check whether the connection was accepted without authentication. */ if (type == SSH_SMSG_SUCCESS) goto success; if (type != SSH_SMSG_FAILURE) packet_disconnect("Protocol error: got %d in response to SSH_CMSG_USER", type); /* * Try .rhosts or /etc/hosts.equiv authentication with RSA host * authentication. */ if ((supported_authentications & (1 << SSH_AUTH_RHOSTS_RSA)) && options.rhosts_rsa_authentication) { for (i = 0; i < sensitive->nkeys; i++) { if (sensitive->keys[i] != NULL && sensitive->keys[i]->type == KEY_RSA1 && try_rhosts_rsa_authentication(local_user, sensitive->keys[i])) goto success; } } /* Try RSA authentication if the server supports it. */ if ((supported_authentications & (1 << SSH_AUTH_RSA)) && options.rsa_authentication) { /* * Try RSA authentication using the authentication agent. The * agent is tried first because no passphrase is needed for * it, whereas identity files may require passphrases. */ if (try_agent_authentication()) goto success; /* Try RSA authentication for each identity. */ for (i = 0; i < options.num_identity_files; i++) if (options.identity_keys[i] != NULL && options.identity_keys[i]->type == KEY_RSA1 && try_rsa_authentication(i)) goto success; } /* Try challenge response authentication if the server supports it. */ if ((supported_authentications & (1 << SSH_AUTH_TIS)) && options.challenge_response_authentication && !options.batch_mode) { if (try_challenge_response_authentication()) goto success; } /* Try password authentication if the server supports it. */ if ((supported_authentications & (1 << SSH_AUTH_PASSWORD)) && options.password_authentication && !options.batch_mode) { char prompt[80]; snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ", server_user, host); if (try_password_authentication(prompt)) goto success; } /* All authentication methods have failed. Exit with an error message. */ fatal("Permission denied."); /* NOTREACHED */ success: return; /* need statement after label */ } #endif /* WITH_SSH1 */ diff --git a/crypto/openssh/sshconnect2.c b/crypto/openssh/sshconnect2.c index 103a2b36a7cf..f8a54beea949 100644 --- a/crypto/openssh/sshconnect2.c +++ b/crypto/openssh/sshconnect2.c @@ -1,1936 +1,1973 @@ -/* $OpenBSD: sshconnect2.c,v 1.251 2016/12/04 23:54:02 djm Exp $ */ +/* $OpenBSD: sshconnect2.c,v 1.255 2017/03/11 23:40:26 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2008 Damien Miller. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) #include #endif #include "openbsd-compat/sys-queue.h" #include "xmalloc.h" #include "ssh.h" #include "ssh2.h" #include "buffer.h" #include "packet.h" #include "compat.h" #include "cipher.h" #include "key.h" #include "kex.h" #include "myproposal.h" #include "sshconnect.h" #include "authfile.h" #include "dh.h" #include "authfd.h" #include "log.h" #include "misc.h" #include "readconf.h" #include "match.h" #include "dispatch.h" #include "canohost.h" #include "msg.h" #include "pathnames.h" #include "uidswap.h" #include "hostfile.h" #include "ssherr.h" #include "utf8.h" #ifdef GSSAPI #include "ssh-gss.h" #endif /* import */ extern char *client_version_string; extern char *server_version_string; extern Options options; /* * SSH2 key exchange */ u_char *session_id2 = NULL; u_int session_id2_len = 0; char *xxx_host; struct sockaddr *xxx_hostaddr; static int verify_host_key_callback(Key *hostkey, struct ssh *ssh) { if (verify_host_key(xxx_host, xxx_hostaddr, hostkey) == -1) fatal("Host key verification failed."); return 0; } static char * order_hostkeyalgs(char *host, struct sockaddr *hostaddr, u_short port) { char *oavail, *avail, *first, *last, *alg, *hostname, *ret; size_t maxlen; struct hostkeys *hostkeys; int ktype; u_int i; /* Find all hostkeys for this hostname */ get_hostfile_hostname_ipaddr(host, hostaddr, port, &hostname, NULL); hostkeys = init_hostkeys(); for (i = 0; i < options.num_user_hostfiles; i++) load_hostkeys(hostkeys, hostname, options.user_hostfiles[i]); for (i = 0; i < options.num_system_hostfiles; i++) load_hostkeys(hostkeys, hostname, options.system_hostfiles[i]); oavail = avail = xstrdup(KEX_DEFAULT_PK_ALG); maxlen = strlen(avail) + 1; first = xmalloc(maxlen); last = xmalloc(maxlen); *first = *last = '\0'; #define ALG_APPEND(to, from) \ do { \ if (*to != '\0') \ strlcat(to, ",", maxlen); \ strlcat(to, from, maxlen); \ } while (0) while ((alg = strsep(&avail, ",")) && *alg != '\0') { if ((ktype = sshkey_type_from_name(alg)) == KEY_UNSPEC) fatal("%s: unknown alg %s", __func__, alg); if (lookup_key_in_hostkeys_by_type(hostkeys, sshkey_type_plain(ktype), NULL)) ALG_APPEND(first, alg); else ALG_APPEND(last, alg); } #undef ALG_APPEND xasprintf(&ret, "%s%s%s", first, (*first == '\0' || *last == '\0') ? "" : ",", last); if (*first != '\0') debug3("%s: prefer hostkeyalgs: %s", __func__, first); free(first); free(last); free(hostname); free(oavail); free_hostkeys(hostkeys); return ret; } void ssh_kex2(char *host, struct sockaddr *hostaddr, u_short port) { char *myproposal[PROPOSAL_MAX] = { KEX_CLIENT }; char *s; struct kex *kex; int r; xxx_host = host; xxx_hostaddr = hostaddr; if ((s = kex_names_cat(options.kex_algorithms, "ext-info-c")) == NULL) fatal("%s: kex_names_cat", __func__); myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(s); myproposal[PROPOSAL_ENC_ALGS_CTOS] = compat_cipher_proposal(options.ciphers); myproposal[PROPOSAL_ENC_ALGS_STOC] = compat_cipher_proposal(options.ciphers); myproposal[PROPOSAL_COMP_ALGS_CTOS] = myproposal[PROPOSAL_COMP_ALGS_STOC] = options.compression ? "zlib@openssh.com,zlib,none" : "none,zlib@openssh.com,zlib"; myproposal[PROPOSAL_MAC_ALGS_CTOS] = myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; if (options.hostkeyalgorithms != NULL) { if (kex_assemble_names(KEX_DEFAULT_PK_ALG, &options.hostkeyalgorithms) != 0) fatal("%s: kex_assemble_namelist", __func__); myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal(options.hostkeyalgorithms); } else { /* Enforce default */ options.hostkeyalgorithms = xstrdup(KEX_DEFAULT_PK_ALG); /* Prefer algorithms that we already have keys for */ myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( order_hostkeyalgs(host, hostaddr, port)); } if (options.rekey_limit || options.rekey_interval) - packet_set_rekey_limits((u_int32_t)options.rekey_limit, - (time_t)options.rekey_interval); + packet_set_rekey_limits(options.rekey_limit, + options.rekey_interval); /* start key exchange */ if ((r = kex_setup(active_state, myproposal)) != 0) fatal("kex_setup: %s", ssh_err(r)); kex = active_state->kex; #ifdef WITH_OPENSSL kex->kex[KEX_DH_GRP1_SHA1] = kexdh_client; kex->kex[KEX_DH_GRP14_SHA1] = kexdh_client; kex->kex[KEX_DH_GRP14_SHA256] = kexdh_client; kex->kex[KEX_DH_GRP16_SHA512] = kexdh_client; kex->kex[KEX_DH_GRP18_SHA512] = kexdh_client; kex->kex[KEX_DH_GEX_SHA1] = kexgex_client; kex->kex[KEX_DH_GEX_SHA256] = kexgex_client; # ifdef OPENSSL_HAS_ECC kex->kex[KEX_ECDH_SHA2] = kexecdh_client; # endif #endif kex->kex[KEX_C25519_SHA256] = kexc25519_client; kex->client_version_string=client_version_string; kex->server_version_string=server_version_string; kex->verify_host_key=&verify_host_key_callback; dispatch_run(DISPATCH_BLOCK, &kex->done, active_state); /* remove ext-info from the KEX proposals for rekeying */ myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal(options.kex_algorithms); if ((r = kex_prop2buf(kex->my, myproposal)) != 0) fatal("kex_prop2buf: %s", ssh_err(r)); session_id2 = kex->session_id; session_id2_len = kex->session_id_len; #ifdef DEBUG_KEXDH /* send 1st encrypted/maced/compressed message */ packet_start(SSH2_MSG_IGNORE); packet_put_cstring("markus"); packet_send(); packet_write_wait(); #endif } /* * Authenticate user */ typedef struct cauthctxt Authctxt; typedef struct cauthmethod Authmethod; typedef struct identity Identity; typedef struct idlist Idlist; struct identity { TAILQ_ENTRY(identity) next; int agent_fd; /* >=0 if agent supports key */ struct sshkey *key; /* public/private key */ char *filename; /* comment for agent-only keys */ int tried; int isprivate; /* key points to the private key */ int userprovided; }; TAILQ_HEAD(idlist, identity); struct cauthctxt { const char *server_user; const char *local_user; const char *host; const char *service; struct cauthmethod *method; sig_atomic_t success; char *authlist; int attempt; /* pubkey */ struct idlist keys; int agent_fd; /* hostbased */ Sensitive *sensitive; char *oktypes, *ktypes; const char *active_ktype; /* kbd-interactive */ int info_req_seen; /* generic */ void *methoddata; }; struct cauthmethod { char *name; /* string to compare against server's list */ int (*userauth)(Authctxt *authctxt); void (*cleanup)(Authctxt *authctxt); int *enabled; /* flag in option struct that enables method */ int *batch_flag; /* flag in option struct that disables method */ }; int input_userauth_service_accept(int, u_int32_t, void *); int input_userauth_ext_info(int, u_int32_t, void *); int input_userauth_success(int, u_int32_t, void *); int input_userauth_success_unexpected(int, u_int32_t, void *); int input_userauth_failure(int, u_int32_t, void *); int input_userauth_banner(int, u_int32_t, void *); int input_userauth_error(int, u_int32_t, void *); int input_userauth_info_req(int, u_int32_t, void *); int input_userauth_pk_ok(int, u_int32_t, void *); int input_userauth_passwd_changereq(int, u_int32_t, void *); int userauth_none(Authctxt *); int userauth_pubkey(Authctxt *); int userauth_passwd(Authctxt *); int userauth_kbdint(Authctxt *); int userauth_hostbased(Authctxt *); #ifdef GSSAPI int userauth_gssapi(Authctxt *authctxt); int input_gssapi_response(int type, u_int32_t, void *); int input_gssapi_token(int type, u_int32_t, void *); int input_gssapi_hash(int type, u_int32_t, void *); int input_gssapi_error(int, u_int32_t, void *); int input_gssapi_errtok(int, u_int32_t, void *); #endif void userauth(Authctxt *, char *); static int sign_and_send_pubkey(Authctxt *, Identity *); static void pubkey_prepare(Authctxt *); static void pubkey_cleanup(Authctxt *); static void pubkey_reset(Authctxt *); static Key *load_identity_file(Identity *); static Authmethod *authmethod_get(char *authlist); static Authmethod *authmethod_lookup(const char *name); static char *authmethods_get(void); Authmethod authmethods[] = { #ifdef GSSAPI {"gssapi-with-mic", userauth_gssapi, NULL, &options.gss_authentication, NULL}, #endif {"hostbased", userauth_hostbased, NULL, &options.hostbased_authentication, NULL}, {"publickey", userauth_pubkey, NULL, &options.pubkey_authentication, NULL}, {"keyboard-interactive", userauth_kbdint, NULL, &options.kbd_interactive_authentication, &options.batch_mode}, {"password", userauth_passwd, NULL, &options.password_authentication, &options.batch_mode}, {"none", userauth_none, NULL, NULL, NULL}, {NULL, NULL, NULL, NULL, NULL} }; void ssh_userauth2(const char *local_user, const char *server_user, char *host, Sensitive *sensitive) { struct ssh *ssh = active_state; Authctxt authctxt; int r; if (options.challenge_response_authentication) options.kbd_interactive_authentication = 1; if (options.preferred_authentications == NULL) options.preferred_authentications = authmethods_get(); /* setup authentication context */ memset(&authctxt, 0, sizeof(authctxt)); pubkey_prepare(&authctxt); authctxt.server_user = server_user; authctxt.local_user = local_user; authctxt.host = host; authctxt.service = "ssh-connection"; /* service name */ authctxt.success = 0; authctxt.method = authmethod_lookup("none"); authctxt.authlist = NULL; authctxt.methoddata = NULL; authctxt.sensitive = sensitive; authctxt.active_ktype = authctxt.oktypes = authctxt.ktypes = NULL; authctxt.info_req_seen = 0; authctxt.agent_fd = -1; if (authctxt.method == NULL) fatal("ssh_userauth2: internal error: cannot send userauth none request"); if ((r = sshpkt_start(ssh, SSH2_MSG_SERVICE_REQUEST)) != 0 || (r = sshpkt_put_cstring(ssh, "ssh-userauth")) != 0 || (r = sshpkt_send(ssh)) != 0) fatal("%s: %s", __func__, ssh_err(r)); ssh_dispatch_init(ssh, &input_userauth_error); ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &input_userauth_ext_info); ssh_dispatch_set(ssh, SSH2_MSG_SERVICE_ACCEPT, &input_userauth_service_accept); ssh_dispatch_run(ssh, DISPATCH_BLOCK, &authctxt.success, &authctxt); /* loop until success */ pubkey_cleanup(&authctxt); ssh_dispatch_range(ssh, SSH2_MSG_USERAUTH_MIN, SSH2_MSG_USERAUTH_MAX, NULL); if (!authctxt.success) fatal("Authentication failed."); debug("Authentication succeeded (%s).", authctxt.method->name); } /* ARGSUSED */ int input_userauth_service_accept(int type, u_int32_t seqnr, void *ctxt) { Authctxt *authctxt = ctxt; struct ssh *ssh = active_state; int r; if (ssh_packet_remaining(ssh) > 0) { char *reply; if ((r = sshpkt_get_cstring(ssh, &reply, NULL)) != 0) goto out; debug2("service_accept: %s", reply); free(reply); } else { debug2("buggy server: service_accept w/o service"); } if ((r = sshpkt_get_end(ssh)) != 0) goto out; debug("SSH2_MSG_SERVICE_ACCEPT received"); /* initial userauth request */ userauth_none(authctxt); ssh_dispatch_set(ssh, SSH2_MSG_EXT_INFO, &input_userauth_error); ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_SUCCESS, &input_userauth_success); ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_FAILURE, &input_userauth_failure); ssh_dispatch_set(ssh, SSH2_MSG_USERAUTH_BANNER, &input_userauth_banner); r = 0; out: return r; } /* ARGSUSED */ int input_userauth_ext_info(int type, u_int32_t seqnr, void *ctxt) { return kex_input_ext_info(type, seqnr, active_state); } void userauth(Authctxt *authctxt, char *authlist) { if (authctxt->method != NULL && authctxt->method->cleanup != NULL) authctxt->method->cleanup(authctxt); free(authctxt->methoddata); authctxt->methoddata = NULL; if (authlist == NULL) { authlist = authctxt->authlist; } else { free(authctxt->authlist); authctxt->authlist = authlist; } for (;;) { Authmethod *method = authmethod_get(authlist); if (method == NULL) fatal("Permission denied (%s).", authlist); authctxt->method = method; /* reset the per method handler */ dispatch_range(SSH2_MSG_USERAUTH_PER_METHOD_MIN, SSH2_MSG_USERAUTH_PER_METHOD_MAX, NULL); /* and try new method */ if (method->userauth(authctxt) != 0) { debug2("we sent a %s packet, wait for reply", method->name); break; } else { debug2("we did not send a packet, disable method"); method->enabled = NULL; } } } /* ARGSUSED */ int input_userauth_error(int type, u_int32_t seq, void *ctxt) { fatal("input_userauth_error: bad message during authentication: " "type %d", type); return 0; } /* ARGSUSED */ int input_userauth_banner(int type, u_int32_t seq, void *ctxt) { char *msg, *lang; u_int len; debug3("%s", __func__); msg = packet_get_string(&len); lang = packet_get_string(NULL); if (len > 0 && options.log_level >= SYSLOG_LEVEL_INFO) fmprintf(stderr, "%s", msg); free(msg); free(lang); return 0; } /* ARGSUSED */ int input_userauth_success(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; if (authctxt == NULL) fatal("input_userauth_success: no authentication context"); free(authctxt->authlist); authctxt->authlist = NULL; if (authctxt->method != NULL && authctxt->method->cleanup != NULL) authctxt->method->cleanup(authctxt); free(authctxt->methoddata); authctxt->methoddata = NULL; authctxt->success = 1; /* break out */ return 0; } int input_userauth_success_unexpected(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; if (authctxt == NULL) fatal("%s: no authentication context", __func__); fatal("Unexpected authentication success during %s.", authctxt->method->name); return 0; } /* ARGSUSED */ int input_userauth_failure(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; char *authlist = NULL; int partial; if (authctxt == NULL) fatal("input_userauth_failure: no authentication context"); authlist = packet_get_string(NULL); partial = packet_get_char(); packet_check_eom(); if (partial != 0) { verbose("Authenticated with partial success."); /* reset state */ pubkey_reset(authctxt); } debug("Authentications that can continue: %s", authlist); userauth(authctxt, authlist); return 0; } /* ARGSUSED */ int input_userauth_pk_ok(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; Key *key = NULL; Identity *id = NULL; Buffer b; int pktype, sent = 0; u_int alen, blen; char *pkalg, *fp; u_char *pkblob; if (authctxt == NULL) fatal("input_userauth_pk_ok: no authentication context"); if (datafellows & SSH_BUG_PKOK) { /* this is similar to SSH_BUG_PKAUTH */ debug2("input_userauth_pk_ok: SSH_BUG_PKOK"); pkblob = packet_get_string(&blen); buffer_init(&b); buffer_append(&b, pkblob, blen); pkalg = buffer_get_string(&b, &alen); buffer_free(&b); } else { pkalg = packet_get_string(&alen); pkblob = packet_get_string(&blen); } packet_check_eom(); debug("Server accepts key: pkalg %s blen %u", pkalg, blen); if ((pktype = key_type_from_name(pkalg)) == KEY_UNSPEC) { debug("unknown pkalg %s", pkalg); goto done; } if ((key = key_from_blob(pkblob, blen)) == NULL) { debug("no key from blob. pkalg %s", pkalg); goto done; } if (key->type != pktype) { error("input_userauth_pk_ok: type mismatch " "for decoded key (received %d, expected %d)", key->type, pktype); goto done; } if ((fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) goto done; debug2("input_userauth_pk_ok: fp %s", fp); free(fp); /* * search keys in the reverse order, because last candidate has been * moved to the end of the queue. this also avoids confusion by * duplicate keys */ TAILQ_FOREACH_REVERSE(id, &authctxt->keys, idlist, next) { if (key_equal(key, id->key)) { sent = sign_and_send_pubkey(authctxt, id); break; } } done: if (key != NULL) key_free(key); free(pkalg); free(pkblob); /* try another method if we did not send a packet */ if (sent == 0) userauth(authctxt, NULL); return 0; } #ifdef GSSAPI int userauth_gssapi(Authctxt *authctxt) { Gssctxt *gssctxt = NULL; static gss_OID_set gss_supported = NULL; static u_int mech = 0; OM_uint32 min; int ok = 0; /* Try one GSSAPI method at a time, rather than sending them all at * once. */ if (gss_supported == NULL) gss_indicate_mechs(&min, &gss_supported); /* Check to see if the mechanism is usable before we offer it */ while (mech < gss_supported->count && !ok) { /* My DER encoding requires length<128 */ if (gss_supported->elements[mech].length < 128 && ssh_gssapi_check_mechanism(&gssctxt, &gss_supported->elements[mech], authctxt->host)) { ok = 1; /* Mechanism works */ } else { mech++; } } if (!ok) return 0; authctxt->methoddata=(void *)gssctxt; packet_start(SSH2_MSG_USERAUTH_REQUEST); packet_put_cstring(authctxt->server_user); packet_put_cstring(authctxt->service); packet_put_cstring(authctxt->method->name); packet_put_int(1); packet_put_int((gss_supported->elements[mech].length) + 2); packet_put_char(SSH_GSS_OIDTYPE); packet_put_char(gss_supported->elements[mech].length); packet_put_raw(gss_supported->elements[mech].elements, gss_supported->elements[mech].length); packet_send(); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE, &input_gssapi_response); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERROR, &input_gssapi_error); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok); mech++; /* Move along to next candidate */ return 1; } static OM_uint32 process_gssapi_token(void *ctxt, gss_buffer_t recv_tok) { Authctxt *authctxt = ctxt; Gssctxt *gssctxt = authctxt->methoddata; gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; gss_buffer_desc mic = GSS_C_EMPTY_BUFFER; gss_buffer_desc gssbuf; OM_uint32 status, ms, flags; Buffer b; status = ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds, recv_tok, &send_tok, &flags); if (send_tok.length > 0) { if (GSS_ERROR(status)) packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK); else packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN); packet_put_string(send_tok.value, send_tok.length); packet_send(); gss_release_buffer(&ms, &send_tok); } if (status == GSS_S_COMPLETE) { /* send either complete or MIC, depending on mechanism */ if (!(flags & GSS_C_INTEG_FLAG)) { packet_start(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE); packet_send(); } else { ssh_gssapi_buildmic(&b, authctxt->server_user, authctxt->service, "gssapi-with-mic"); gssbuf.value = buffer_ptr(&b); gssbuf.length = buffer_len(&b); status = ssh_gssapi_sign(gssctxt, &gssbuf, &mic); if (!GSS_ERROR(status)) { packet_start(SSH2_MSG_USERAUTH_GSSAPI_MIC); packet_put_string(mic.value, mic.length); packet_send(); } buffer_free(&b); gss_release_buffer(&ms, &mic); } } return status; } /* ARGSUSED */ int input_gssapi_response(int type, u_int32_t plen, void *ctxt) { Authctxt *authctxt = ctxt; Gssctxt *gssctxt; int oidlen; char *oidv; if (authctxt == NULL) fatal("input_gssapi_response: no authentication context"); gssctxt = authctxt->methoddata; /* Setup our OID */ oidv = packet_get_string(&oidlen); if (oidlen <= 2 || oidv[0] != SSH_GSS_OIDTYPE || oidv[1] != oidlen - 2) { free(oidv); debug("Badly encoded mechanism OID received"); userauth(authctxt, NULL); return 0; } if (!ssh_gssapi_check_oid(gssctxt, oidv + 2, oidlen - 2)) fatal("Server returned different OID than expected"); packet_check_eom(); free(oidv); if (GSS_ERROR(process_gssapi_token(ctxt, GSS_C_NO_BUFFER))) { /* Start again with next method on list */ debug("Trying to start again"); userauth(authctxt, NULL); return 0; } return 0; } /* ARGSUSED */ int input_gssapi_token(int type, u_int32_t plen, void *ctxt) { Authctxt *authctxt = ctxt; gss_buffer_desc recv_tok; OM_uint32 status; u_int slen; if (authctxt == NULL) fatal("input_gssapi_response: no authentication context"); recv_tok.value = packet_get_string(&slen); recv_tok.length = slen; /* safe typecast */ packet_check_eom(); status = process_gssapi_token(ctxt, &recv_tok); free(recv_tok.value); if (GSS_ERROR(status)) { /* Start again with the next method in the list */ userauth(authctxt, NULL); return 0; } return 0; } /* ARGSUSED */ int input_gssapi_errtok(int type, u_int32_t plen, void *ctxt) { Authctxt *authctxt = ctxt; Gssctxt *gssctxt; gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER; gss_buffer_desc recv_tok; OM_uint32 ms; u_int len; if (authctxt == NULL) fatal("input_gssapi_response: no authentication context"); gssctxt = authctxt->methoddata; recv_tok.value = packet_get_string(&len); recv_tok.length = len; packet_check_eom(); /* Stick it into GSSAPI and see what it says */ (void)ssh_gssapi_init_ctx(gssctxt, options.gss_deleg_creds, &recv_tok, &send_tok, NULL); free(recv_tok.value); gss_release_buffer(&ms, &send_tok); /* Server will be returning a failed packet after this one */ return 0; } /* ARGSUSED */ int input_gssapi_error(int type, u_int32_t plen, void *ctxt) { char *msg; char *lang; /* maj */(void)packet_get_int(); /* min */(void)packet_get_int(); msg=packet_get_string(NULL); lang=packet_get_string(NULL); packet_check_eom(); debug("Server GSSAPI Error:\n%s", msg); free(msg); free(lang); return 0; } #endif /* GSSAPI */ int userauth_none(Authctxt *authctxt) { /* initial userauth request */ packet_start(SSH2_MSG_USERAUTH_REQUEST); packet_put_cstring(authctxt->server_user); packet_put_cstring(authctxt->service); packet_put_cstring(authctxt->method->name); packet_send(); return 1; } int userauth_passwd(Authctxt *authctxt) { static int attempt = 0; char prompt[150]; char *password; const char *host = options.host_key_alias ? options.host_key_alias : authctxt->host; if (attempt++ >= options.number_of_password_prompts) return 0; if (attempt != 1) error("Permission denied, please try again."); snprintf(prompt, sizeof(prompt), "%.30s@%.128s's password: ", authctxt->server_user, host); password = read_passphrase(prompt, 0); packet_start(SSH2_MSG_USERAUTH_REQUEST); packet_put_cstring(authctxt->server_user); packet_put_cstring(authctxt->service); packet_put_cstring(authctxt->method->name); packet_put_char(0); packet_put_cstring(password); explicit_bzero(password, strlen(password)); free(password); packet_add_padding(64); packet_send(); dispatch_set(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, &input_userauth_passwd_changereq); return 1; } /* * parse PASSWD_CHANGEREQ, prompt user and send SSH2_MSG_USERAUTH_REQUEST */ /* ARGSUSED */ int input_userauth_passwd_changereq(int type, u_int32_t seqnr, void *ctxt) { Authctxt *authctxt = ctxt; char *info, *lang, *password = NULL, *retype = NULL; char prompt[150]; - const char *host = options.host_key_alias ? options.host_key_alias : - authctxt->host; + const char *host; debug2("input_userauth_passwd_changereq"); if (authctxt == NULL) fatal("input_userauth_passwd_changereq: " "no authentication context"); + host = options.host_key_alias ? options.host_key_alias : authctxt->host; info = packet_get_string(NULL); lang = packet_get_string(NULL); if (strlen(info) > 0) logit("%s", info); free(info); free(lang); packet_start(SSH2_MSG_USERAUTH_REQUEST); packet_put_cstring(authctxt->server_user); packet_put_cstring(authctxt->service); packet_put_cstring(authctxt->method->name); packet_put_char(1); /* additional info */ snprintf(prompt, sizeof(prompt), "Enter %.30s@%.128s's old password: ", authctxt->server_user, host); password = read_passphrase(prompt, 0); packet_put_cstring(password); explicit_bzero(password, strlen(password)); free(password); password = NULL; while (password == NULL) { snprintf(prompt, sizeof(prompt), "Enter %.30s@%.128s's new password: ", authctxt->server_user, host); password = read_passphrase(prompt, RP_ALLOW_EOF); if (password == NULL) { /* bail out */ return 0; } snprintf(prompt, sizeof(prompt), "Retype %.30s@%.128s's new password: ", authctxt->server_user, host); retype = read_passphrase(prompt, 0); if (strcmp(password, retype) != 0) { explicit_bzero(password, strlen(password)); free(password); logit("Mismatch; try again, EOF to quit."); password = NULL; } explicit_bzero(retype, strlen(retype)); free(retype); } packet_put_cstring(password); explicit_bzero(password, strlen(password)); free(password); packet_add_padding(64); packet_send(); dispatch_set(SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ, &input_userauth_passwd_changereq); return 0; } static const char * -identity_sign_encode(struct identity *id) +key_sign_encode(const struct sshkey *key) { struct ssh *ssh = active_state; - if (id->key->type == KEY_RSA) { + if (key->type == KEY_RSA) { switch (ssh->kex->rsa_sha2) { case 256: return "rsa-sha2-256"; case 512: return "rsa-sha2-512"; } } - return key_ssh_name(id->key); + return key_ssh_name(key); } static int identity_sign(struct identity *id, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, u_int compat) { Key *prv; int ret; - const char *alg; - - alg = identity_sign_encode(id); /* the agent supports this key */ - if (id->agent_fd != -1) + if (id->key != NULL && id->agent_fd != -1) return ssh_agent_sign(id->agent_fd, id->key, sigp, lenp, - data, datalen, alg, compat); + data, datalen, key_sign_encode(id->key), compat); /* * we have already loaded the private key or * the private key is stored in external hardware */ - if (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT)) - return (sshkey_sign(id->key, sigp, lenp, data, datalen, alg, - compat)); + if (id->key != NULL && + (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT))) + return (sshkey_sign(id->key, sigp, lenp, data, datalen, + key_sign_encode(id->key), compat)); + /* load the private key from the file */ if ((prv = load_identity_file(id)) == NULL) return SSH_ERR_KEY_NOT_FOUND; - ret = sshkey_sign(prv, sigp, lenp, data, datalen, alg, compat); + ret = sshkey_sign(prv, sigp, lenp, data, datalen, + key_sign_encode(prv), compat); sshkey_free(prv); return (ret); } +static int +id_filename_matches(Identity *id, Identity *private_id) +{ + const char *suffixes[] = { ".pub", "-cert.pub", NULL }; + size_t len = strlen(id->filename), plen = strlen(private_id->filename); + size_t i, slen; + + if (strcmp(id->filename, private_id->filename) == 0) + return 1; + for (i = 0; suffixes[i]; i++) { + slen = strlen(suffixes[i]); + if (len > slen && plen == len - slen && + strcmp(id->filename + (len - slen), suffixes[i]) == 0 && + memcmp(id->filename, private_id->filename, plen) == 0) + return 1; + } + return 0; +} + static int sign_and_send_pubkey(Authctxt *authctxt, Identity *id) { Buffer b; Identity *private_id; u_char *blob, *signature; size_t slen; u_int bloblen, skip = 0; int matched, ret = -1, have_sig = 1; char *fp; if ((fp = sshkey_fingerprint(id->key, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) return 0; debug3("%s: %s %s", __func__, key_type(id->key), fp); free(fp); if (key_to_blob(id->key, &blob, &bloblen) == 0) { /* we cannot handle this key */ debug3("sign_and_send_pubkey: cannot handle key"); return 0; } /* data to be signed */ buffer_init(&b); if (datafellows & SSH_OLD_SESSIONID) { buffer_append(&b, session_id2, session_id2_len); skip = session_id2_len; } else { buffer_put_string(&b, session_id2, session_id2_len); skip = buffer_len(&b); } buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); buffer_put_cstring(&b, authctxt->server_user); buffer_put_cstring(&b, datafellows & SSH_BUG_PKSERVICE ? "ssh-userauth" : authctxt->service); if (datafellows & SSH_BUG_PKAUTH) { buffer_put_char(&b, have_sig); } else { buffer_put_cstring(&b, authctxt->method->name); buffer_put_char(&b, have_sig); - buffer_put_cstring(&b, identity_sign_encode(id)); + buffer_put_cstring(&b, key_sign_encode(id->key)); } buffer_put_string(&b, blob, bloblen); /* * If the key is an certificate, try to find a matching private key * and use it to complete the signature. * If no such private key exists, fall back to trying the certificate * key itself in case it has a private half already loaded. */ if (key_is_cert(id->key)) { matched = 0; TAILQ_FOREACH(private_id, &authctxt->keys, next) { if (sshkey_equal_public(id->key, private_id->key) && id->key->type != private_id->key->type) { id = private_id; matched = 1; break; } } + /* + * Exact key matches are preferred, but also allow + * filename matches for non-PKCS#11/agent keys that + * didn't load public keys. This supports the case + * of keeping just a private key file and public + * certificate on disk. + */ + if (!matched && !id->isprivate && id->agent_fd == -1 && + (id->key->flags & SSHKEY_FLAG_EXT) == 0) { + TAILQ_FOREACH(private_id, &authctxt->keys, next) { + if (private_id->key == NULL && + id_filename_matches(id, private_id)) { + id = private_id; + matched = 1; + break; + } + } + } if (matched) { debug2("%s: using private key \"%s\"%s for " "certificate", __func__, id->filename, id->agent_fd != -1 ? " from agent" : ""); } else { debug("%s: no separate private key for certificate " "\"%s\"", __func__, id->filename); } } /* generate signature */ ret = identity_sign(id, &signature, &slen, buffer_ptr(&b), buffer_len(&b), datafellows); if (ret != 0) { if (ret != SSH_ERR_KEY_NOT_FOUND) error("%s: signing failed: %s", __func__, ssh_err(ret)); free(blob); buffer_free(&b); return 0; } #ifdef DEBUG_PK buffer_dump(&b); #endif if (datafellows & SSH_BUG_PKSERVICE) { buffer_clear(&b); buffer_append(&b, session_id2, session_id2_len); skip = session_id2_len; buffer_put_char(&b, SSH2_MSG_USERAUTH_REQUEST); buffer_put_cstring(&b, authctxt->server_user); buffer_put_cstring(&b, authctxt->service); buffer_put_cstring(&b, authctxt->method->name); buffer_put_char(&b, have_sig); if (!(datafellows & SSH_BUG_PKAUTH)) buffer_put_cstring(&b, key_ssh_name(id->key)); buffer_put_string(&b, blob, bloblen); } free(blob); /* append signature */ buffer_put_string(&b, signature, slen); free(signature); /* skip session id and packet type */ if (buffer_len(&b) < skip + 1) fatal("userauth_pubkey: internal error"); buffer_consume(&b, skip + 1); /* put remaining data from buffer into packet */ packet_start(SSH2_MSG_USERAUTH_REQUEST); packet_put_raw(buffer_ptr(&b), buffer_len(&b)); buffer_free(&b); packet_send(); return 1; } static int send_pubkey_test(Authctxt *authctxt, Identity *id) { u_char *blob; u_int bloblen, have_sig = 0; debug3("send_pubkey_test"); if (key_to_blob(id->key, &blob, &bloblen) == 0) { /* we cannot handle this key */ debug3("send_pubkey_test: cannot handle key"); return 0; } /* register callback for USERAUTH_PK_OK message */ dispatch_set(SSH2_MSG_USERAUTH_PK_OK, &input_userauth_pk_ok); packet_start(SSH2_MSG_USERAUTH_REQUEST); packet_put_cstring(authctxt->server_user); packet_put_cstring(authctxt->service); packet_put_cstring(authctxt->method->name); packet_put_char(have_sig); if (!(datafellows & SSH_BUG_PKAUTH)) - packet_put_cstring(identity_sign_encode(id)); + packet_put_cstring(key_sign_encode(id->key)); packet_put_string(blob, bloblen); free(blob); packet_send(); return 1; } static Key * load_identity_file(Identity *id) { Key *private = NULL; char prompt[300], *passphrase, *comment; int r, perm_ok = 0, quit = 0, i; struct stat st; if (stat(id->filename, &st) < 0) { (id->userprovided ? logit : debug3)("no such identity: %s: %s", id->filename, strerror(errno)); return NULL; } snprintf(prompt, sizeof prompt, "Enter passphrase for key '%.100s': ", id->filename); for (i = 0; i <= options.number_of_password_prompts; i++) { if (i == 0) passphrase = ""; else { passphrase = read_passphrase(prompt, 0); if (*passphrase == '\0') { debug2("no passphrase given, try next key"); free(passphrase); break; } } switch ((r = sshkey_load_private_type(KEY_UNSPEC, id->filename, passphrase, &private, &comment, &perm_ok))) { case 0: break; case SSH_ERR_KEY_WRONG_PASSPHRASE: if (options.batch_mode) { quit = 1; break; } if (i != 0) debug2("bad passphrase given, try again..."); break; case SSH_ERR_SYSTEM_ERROR: if (errno == ENOENT) { debug2("Load key \"%s\": %s", id->filename, ssh_err(r)); quit = 1; break; } /* FALLTHROUGH */ default: error("Load key \"%s\": %s", id->filename, ssh_err(r)); quit = 1; break; } if (!quit && private != NULL && id->agent_fd == -1 && !(id->key && id->isprivate)) maybe_add_key_to_agent(id->filename, private, comment, passphrase); if (i > 0) { explicit_bzero(passphrase, strlen(passphrase)); free(passphrase); } free(comment); if (private != NULL || quit) break; } return private; } /* * try keys in the following order: * 1. certificates listed in the config file * 2. other input certificates * 3. agent keys that are found in the config file * 4. other agent keys * 5. keys that are only listed in the config file */ static void pubkey_prepare(Authctxt *authctxt) { struct identity *id, *id2, *tmp; struct idlist agent, files, *preferred; struct sshkey *key; int agent_fd = -1, i, r, found; size_t j; struct ssh_identitylist *idlist; TAILQ_INIT(&agent); /* keys from the agent */ TAILQ_INIT(&files); /* keys from the config file */ preferred = &authctxt->keys; TAILQ_INIT(preferred); /* preferred order of keys */ /* list of keys stored in the filesystem and PKCS#11 */ for (i = 0; i < options.num_identity_files; i++) { key = options.identity_keys[i]; if (key && key->type == KEY_RSA1) continue; if (key && key->cert && key->cert->type != SSH2_CERT_TYPE_USER) continue; options.identity_keys[i] = NULL; id = xcalloc(1, sizeof(*id)); id->agent_fd = -1; id->key = key; id->filename = xstrdup(options.identity_files[i]); id->userprovided = options.identity_file_userprovided[i]; TAILQ_INSERT_TAIL(&files, id, next); } /* list of certificates specified by user */ for (i = 0; i < options.num_certificate_files; i++) { key = options.certificates[i]; if (!key_is_cert(key) || key->cert == NULL || key->cert->type != SSH2_CERT_TYPE_USER) continue; id = xcalloc(1, sizeof(*id)); id->agent_fd = -1; id->key = key; id->filename = xstrdup(options.certificate_files[i]); id->userprovided = options.certificate_file_userprovided[i]; TAILQ_INSERT_TAIL(preferred, id, next); } /* list of keys supported by the agent */ if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) { if (r != SSH_ERR_AGENT_NOT_PRESENT) debug("%s: ssh_get_authentication_socket: %s", __func__, ssh_err(r)); } else if ((r = ssh_fetch_identitylist(agent_fd, 2, &idlist)) != 0) { if (r != SSH_ERR_AGENT_NO_IDENTITIES) debug("%s: ssh_fetch_identitylist: %s", __func__, ssh_err(r)); close(agent_fd); } else { for (j = 0; j < idlist->nkeys; j++) { found = 0; TAILQ_FOREACH(id, &files, next) { /* * agent keys from the config file are * preferred */ if (sshkey_equal(idlist->keys[j], id->key)) { TAILQ_REMOVE(&files, id, next); TAILQ_INSERT_TAIL(preferred, id, next); id->agent_fd = agent_fd; found = 1; break; } } if (!found && !options.identities_only) { id = xcalloc(1, sizeof(*id)); /* XXX "steals" key/comment from idlist */ id->key = idlist->keys[j]; id->filename = idlist->comments[j]; idlist->keys[j] = NULL; idlist->comments[j] = NULL; id->agent_fd = agent_fd; TAILQ_INSERT_TAIL(&agent, id, next); } } ssh_free_identitylist(idlist); /* append remaining agent keys */ for (id = TAILQ_FIRST(&agent); id; id = TAILQ_FIRST(&agent)) { TAILQ_REMOVE(&agent, id, next); TAILQ_INSERT_TAIL(preferred, id, next); } authctxt->agent_fd = agent_fd; } /* Prefer PKCS11 keys that are explicitly listed */ TAILQ_FOREACH_SAFE(id, &files, next, tmp) { if (id->key == NULL || (id->key->flags & SSHKEY_FLAG_EXT) == 0) continue; found = 0; TAILQ_FOREACH(id2, &files, next) { if (id2->key == NULL || (id2->key->flags & SSHKEY_FLAG_EXT) == 0) continue; if (sshkey_equal(id->key, id2->key)) { TAILQ_REMOVE(&files, id, next); TAILQ_INSERT_TAIL(preferred, id, next); found = 1; break; } } /* If IdentitiesOnly set and key not found then don't use it */ if (!found && options.identities_only) { TAILQ_REMOVE(&files, id, next); explicit_bzero(id, sizeof(*id)); free(id); } } /* append remaining keys from the config file */ for (id = TAILQ_FIRST(&files); id; id = TAILQ_FIRST(&files)) { TAILQ_REMOVE(&files, id, next); TAILQ_INSERT_TAIL(preferred, id, next); } /* finally, filter by PubkeyAcceptedKeyTypes */ TAILQ_FOREACH_SAFE(id, preferred, next, id2) { if (id->key != NULL && match_pattern_list(sshkey_ssh_name(id->key), options.pubkey_key_types, 0) != 1) { debug("Skipping %s key %s - " "not in PubkeyAcceptedKeyTypes", sshkey_ssh_name(id->key), id->filename); TAILQ_REMOVE(preferred, id, next); sshkey_free(id->key); free(id->filename); memset(id, 0, sizeof(*id)); continue; } debug2("key: %s (%p)%s%s", id->filename, id->key, id->userprovided ? ", explicit" : "", id->agent_fd != -1 ? ", agent" : ""); } } static void pubkey_cleanup(Authctxt *authctxt) { Identity *id; if (authctxt->agent_fd != -1) ssh_close_authentication_socket(authctxt->agent_fd); for (id = TAILQ_FIRST(&authctxt->keys); id; id = TAILQ_FIRST(&authctxt->keys)) { TAILQ_REMOVE(&authctxt->keys, id, next); sshkey_free(id->key); free(id->filename); free(id); } } static void pubkey_reset(Authctxt *authctxt) { Identity *id; TAILQ_FOREACH(id, &authctxt->keys, next) id->tried = 0; } static int try_identity(Identity *id) { if (!id->key) return (0); if (key_type_plain(id->key->type) == KEY_RSA && (datafellows & SSH_BUG_RSASIGMD5) != 0) { debug("Skipped %s key %s for RSA/MD5 server", key_type(id->key), id->filename); return (0); } return (id->key->type != KEY_RSA1); } int userauth_pubkey(Authctxt *authctxt) { Identity *id; int sent = 0; while ((id = TAILQ_FIRST(&authctxt->keys))) { if (id->tried++) return (0); /* move key to the end of the queue */ TAILQ_REMOVE(&authctxt->keys, id, next); TAILQ_INSERT_TAIL(&authctxt->keys, id, next); /* * send a test message if we have the public key. for * encrypted keys we cannot do this and have to load the * private key instead */ if (id->key != NULL) { if (try_identity(id)) { debug("Offering %s public key: %s", key_type(id->key), id->filename); sent = send_pubkey_test(authctxt, id); } } else { debug("Trying private key: %s", id->filename); id->key = load_identity_file(id); if (id->key != NULL) { if (try_identity(id)) { id->isprivate = 1; sent = sign_and_send_pubkey( authctxt, id); } key_free(id->key); id->key = NULL; id->isprivate = 0; } } if (sent) return (sent); } return (0); } /* * Send userauth request message specifying keyboard-interactive method. */ int userauth_kbdint(Authctxt *authctxt) { static int attempt = 0; if (attempt++ >= options.number_of_password_prompts) return 0; /* disable if no SSH2_MSG_USERAUTH_INFO_REQUEST has been seen */ if (attempt > 1 && !authctxt->info_req_seen) { debug3("userauth_kbdint: disable: no info_req_seen"); dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, NULL); return 0; } debug2("userauth_kbdint"); packet_start(SSH2_MSG_USERAUTH_REQUEST); packet_put_cstring(authctxt->server_user); packet_put_cstring(authctxt->service); packet_put_cstring(authctxt->method->name); packet_put_cstring(""); /* lang */ packet_put_cstring(options.kbd_interactive_devices ? options.kbd_interactive_devices : ""); packet_send(); dispatch_set(SSH2_MSG_USERAUTH_INFO_REQUEST, &input_userauth_info_req); return 1; } /* * parse INFO_REQUEST, prompt user and send INFO_RESPONSE */ int input_userauth_info_req(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; char *name, *inst, *lang, *prompt, *response; u_int num_prompts, i; int echo = 0; debug2("input_userauth_info_req"); if (authctxt == NULL) fatal("input_userauth_info_req: no authentication context"); authctxt->info_req_seen = 1; name = packet_get_string(NULL); inst = packet_get_string(NULL); lang = packet_get_string(NULL); if (strlen(name) > 0) logit("%s", name); if (strlen(inst) > 0) logit("%s", inst); free(name); free(inst); free(lang); num_prompts = packet_get_int(); /* * Begin to build info response packet based on prompts requested. * We commit to providing the correct number of responses, so if * further on we run into a problem that prevents this, we have to * be sure and clean this up and send a correct error response. */ packet_start(SSH2_MSG_USERAUTH_INFO_RESPONSE); packet_put_int(num_prompts); debug2("input_userauth_info_req: num_prompts %d", num_prompts); for (i = 0; i < num_prompts; i++) { prompt = packet_get_string(NULL); echo = packet_get_char(); response = read_passphrase(prompt, echo ? RP_ECHO : 0); packet_put_cstring(response); explicit_bzero(response, strlen(response)); free(response); free(prompt); } packet_check_eom(); /* done with parsing incoming message. */ packet_add_padding(64); packet_send(); return 0; } static int ssh_keysign(struct sshkey *key, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen) { struct sshbuf *b; struct stat st; pid_t pid; int i, r, to[2], from[2], status, sock = packet_get_connection_in(); u_char rversion = 0, version = 2; void (*osigchld)(int); *sigp = NULL; *lenp = 0; if (stat(_PATH_SSH_KEY_SIGN, &st) < 0) { error("%s: not installed: %s", __func__, strerror(errno)); return -1; } if (fflush(stdout) != 0) { error("%s: fflush: %s", __func__, strerror(errno)); return -1; } if (pipe(to) < 0) { error("%s: pipe: %s", __func__, strerror(errno)); return -1; } if (pipe(from) < 0) { error("%s: pipe: %s", __func__, strerror(errno)); return -1; } if ((pid = fork()) < 0) { error("%s: fork: %s", __func__, strerror(errno)); return -1; } osigchld = signal(SIGCHLD, SIG_DFL); if (pid == 0) { /* keep the socket on exec */ fcntl(sock, F_SETFD, 0); permanently_drop_suid(getuid()); close(from[0]); if (dup2(from[1], STDOUT_FILENO) < 0) fatal("%s: dup2: %s", __func__, strerror(errno)); close(to[1]); if (dup2(to[0], STDIN_FILENO) < 0) fatal("%s: dup2: %s", __func__, strerror(errno)); close(from[1]); close(to[0]); /* Close everything but stdio and the socket */ for (i = STDERR_FILENO + 1; i < sock; i++) close(i); closefrom(sock + 1); debug3("%s: [child] pid=%ld, exec %s", __func__, (long)getpid(), _PATH_SSH_KEY_SIGN); execl(_PATH_SSH_KEY_SIGN, _PATH_SSH_KEY_SIGN, (char *)NULL); fatal("%s: exec(%s): %s", __func__, _PATH_SSH_KEY_SIGN, strerror(errno)); } close(from[1]); close(to[0]); if ((b = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); /* send # of sock, data to be signed */ - if ((r = sshbuf_put_u32(b, sock) != 0) || + if ((r = sshbuf_put_u32(b, sock)) != 0 || (r = sshbuf_put_string(b, data, datalen)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); if (ssh_msg_send(to[1], version, b) == -1) fatal("%s: couldn't send request", __func__); sshbuf_reset(b); r = ssh_msg_recv(from[0], b); close(from[0]); close(to[1]); if (r < 0) { error("%s: no reply", __func__); goto fail; } errno = 0; while (waitpid(pid, &status, 0) < 0) { if (errno != EINTR) { error("%s: waitpid %ld: %s", __func__, (long)pid, strerror(errno)); goto fail; } } if (!WIFEXITED(status)) { error("%s: exited abnormally", __func__); goto fail; } if (WEXITSTATUS(status) != 0) { error("%s: exited with status %d", __func__, WEXITSTATUS(status)); goto fail; } if ((r = sshbuf_get_u8(b, &rversion)) != 0) { error("%s: buffer error: %s", __func__, ssh_err(r)); goto fail; } if (rversion != version) { error("%s: bad version", __func__); goto fail; } if ((r = sshbuf_get_string(b, sigp, lenp)) != 0) { error("%s: buffer error: %s", __func__, ssh_err(r)); fail: signal(SIGCHLD, osigchld); sshbuf_free(b); return -1; } signal(SIGCHLD, osigchld); sshbuf_free(b); return 0; } int userauth_hostbased(Authctxt *authctxt) { struct ssh *ssh = active_state; struct sshkey *private = NULL; struct sshbuf *b = NULL; const char *service; u_char *sig = NULL, *keyblob = NULL; char *fp = NULL, *chost = NULL, *lname = NULL; size_t siglen = 0, keylen = 0; int i, r, success = 0; if (authctxt->ktypes == NULL) { authctxt->oktypes = xstrdup(options.hostbased_key_types); authctxt->ktypes = authctxt->oktypes; } /* * Work through each listed type pattern in HostbasedKeyTypes, * trying each hostkey that matches the type in turn. */ for (;;) { if (authctxt->active_ktype == NULL) authctxt->active_ktype = strsep(&authctxt->ktypes, ","); if (authctxt->active_ktype == NULL || *authctxt->active_ktype == '\0') break; debug3("%s: trying key type %s", __func__, authctxt->active_ktype); /* check for a useful key */ private = NULL; for (i = 0; i < authctxt->sensitive->nkeys; i++) { if (authctxt->sensitive->keys[i] == NULL || authctxt->sensitive->keys[i]->type == KEY_RSA1 || authctxt->sensitive->keys[i]->type == KEY_UNSPEC) continue; if (match_pattern_list( sshkey_ssh_name(authctxt->sensitive->keys[i]), authctxt->active_ktype, 0) != 1) continue; /* we take and free the key */ private = authctxt->sensitive->keys[i]; authctxt->sensitive->keys[i] = NULL; break; } /* Found one */ if (private != NULL) break; /* No more keys of this type; advance */ authctxt->active_ktype = NULL; } if (private == NULL) { free(authctxt->oktypes); authctxt->oktypes = authctxt->ktypes = NULL; authctxt->active_ktype = NULL; debug("No more client hostkeys for hostbased authentication."); goto out; } if ((fp = sshkey_fingerprint(private, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) { error("%s: sshkey_fingerprint failed", __func__); goto out; } debug("%s: trying hostkey %s %s", __func__, sshkey_ssh_name(private), fp); /* figure out a name for the client host */ if ((lname = get_local_name(packet_get_connection_in())) == NULL) { error("%s: cannot get local ipaddr/name", __func__); goto out; } /* XXX sshbuf_put_stringf? */ xasprintf(&chost, "%s.", lname); debug2("%s: chost %s", __func__, chost); service = datafellows & SSH_BUG_HBSERVICE ? "ssh-userauth" : authctxt->service; /* construct data */ if ((b = sshbuf_new()) == NULL) { error("%s: sshbuf_new failed", __func__); goto out; } if ((r = sshkey_to_blob(private, &keyblob, &keylen)) != 0) { error("%s: sshkey_to_blob: %s", __func__, ssh_err(r)); goto out; } if ((r = sshbuf_put_string(b, session_id2, session_id2_len)) != 0 || (r = sshbuf_put_u8(b, SSH2_MSG_USERAUTH_REQUEST)) != 0 || (r = sshbuf_put_cstring(b, authctxt->server_user)) != 0 || (r = sshbuf_put_cstring(b, service)) != 0 || (r = sshbuf_put_cstring(b, authctxt->method->name)) != 0 || (r = sshbuf_put_cstring(b, key_ssh_name(private))) != 0 || (r = sshbuf_put_string(b, keyblob, keylen)) != 0 || (r = sshbuf_put_cstring(b, chost)) != 0 || (r = sshbuf_put_cstring(b, authctxt->local_user)) != 0) { error("%s: buffer error: %s", __func__, ssh_err(r)); goto out; } #ifdef DEBUG_PK sshbuf_dump(b, stderr); #endif if (authctxt->sensitive->external_keysign) r = ssh_keysign(private, &sig, &siglen, sshbuf_ptr(b), sshbuf_len(b)); else if ((r = sshkey_sign(private, &sig, &siglen, sshbuf_ptr(b), sshbuf_len(b), NULL, datafellows)) != 0) debug("%s: sshkey_sign: %s", __func__, ssh_err(r)); if (r != 0) { error("sign using hostkey %s %s failed", sshkey_ssh_name(private), fp); goto out; } if ((r = sshpkt_start(ssh, SSH2_MSG_USERAUTH_REQUEST)) != 0 || (r = sshpkt_put_cstring(ssh, authctxt->server_user)) != 0 || (r = sshpkt_put_cstring(ssh, authctxt->service)) != 0 || (r = sshpkt_put_cstring(ssh, authctxt->method->name)) != 0 || (r = sshpkt_put_cstring(ssh, key_ssh_name(private))) != 0 || (r = sshpkt_put_string(ssh, keyblob, keylen)) != 0 || (r = sshpkt_put_cstring(ssh, chost)) != 0 || (r = sshpkt_put_cstring(ssh, authctxt->local_user)) != 0 || (r = sshpkt_put_string(ssh, sig, siglen)) != 0 || (r = sshpkt_send(ssh)) != 0) { error("%s: packet error: %s", __func__, ssh_err(r)); goto out; } success = 1; out: if (sig != NULL) { explicit_bzero(sig, siglen); free(sig); } free(keyblob); free(lname); free(fp); free(chost); sshkey_free(private); sshbuf_free(b); return success; } /* find auth method */ /* * given auth method name, if configurable options permit this method fill * in auth_ident field and return true, otherwise return false. */ static int authmethod_is_enabled(Authmethod *method) { if (method == NULL) return 0; /* return false if options indicate this method is disabled */ if (method->enabled == NULL || *method->enabled == 0) return 0; /* return false if batch mode is enabled but method needs interactive mode */ if (method->batch_flag != NULL && *method->batch_flag != 0) return 0; return 1; } static Authmethod * authmethod_lookup(const char *name) { Authmethod *method = NULL; if (name != NULL) for (method = authmethods; method->name != NULL; method++) if (strcmp(name, method->name) == 0) return method; debug2("Unrecognized authentication method name: %s", name ? name : "NULL"); return NULL; } /* XXX internal state */ static Authmethod *current = NULL; static char *supported = NULL; static char *preferred = NULL; /* * Given the authentication method list sent by the server, return the * next method we should try. If the server initially sends a nil list, * use a built-in default list. */ static Authmethod * authmethod_get(char *authlist) { char *name = NULL; u_int next; /* Use a suitable default if we're passed a nil list. */ if (authlist == NULL || strlen(authlist) == 0) authlist = options.preferred_authentications; if (supported == NULL || strcmp(authlist, supported) != 0) { debug3("start over, passed a different list %s", authlist); free(supported); supported = xstrdup(authlist); preferred = options.preferred_authentications; debug3("preferred %s", preferred); current = NULL; } else if (current != NULL && authmethod_is_enabled(current)) return current; for (;;) { if ((name = match_list(preferred, supported, &next)) == NULL) { debug("No more authentication methods to try."); current = NULL; return NULL; } preferred += next; debug3("authmethod_lookup %s", name); debug3("remaining preferred: %s", preferred); if ((current = authmethod_lookup(name)) != NULL && authmethod_is_enabled(current)) { debug3("authmethod_is_enabled %s", name); debug("Next authentication method: %s", name); free(name); return current; } free(name); } } static char * authmethods_get(void) { Authmethod *method = NULL; Buffer b; char *list; buffer_init(&b); for (method = authmethods; method->name != NULL; method++) { if (authmethod_is_enabled(method)) { if (buffer_len(&b) > 0) buffer_append(&b, ",", 1); buffer_append(&b, method->name, strlen(method->name)); } } if ((list = sshbuf_dup_string(&b)) == NULL) fatal("%s: sshbuf_dup_string failed", __func__); buffer_free(&b); return list; } diff --git a/crypto/openssh/sshd.8 b/crypto/openssh/sshd.8 index 8608ca26959a..a427f52ff4b0 100644 --- a/crypto/openssh/sshd.8 +++ b/crypto/openssh/sshd.8 @@ -1,957 +1,957 @@ .\" .\" Author: Tatu Ylonen .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland .\" All rights reserved .\" .\" As far as I am concerned, the code I have written for this software .\" can be used freely for any purpose. Any derived versions of this .\" software must be clearly marked as such, and if the derived work is .\" incompatible with the protocol description in the RFC file, it must be .\" called by a name other than "ssh" or "Secure Shell". .\" .\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. .\" Copyright (c) 1999 Aaron Campbell. All rights reserved. .\" Copyright (c) 1999 Theo de Raadt. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd.8,v 1.287 2016/11/30 02:57:40 djm Exp $ +.\" $OpenBSD: sshd.8,v 1.288 2017/01/30 23:27:39 dtucker Exp $ .\" $FreeBSD$ -.Dd $Mdocdate: November 30 2016 $ +.Dd $Mdocdate: January 30 2017 $ .Dt SSHD 8 .Os .Sh NAME .Nm sshd .Nd OpenSSH SSH daemon .Sh SYNOPSIS .Nm sshd .Bk -words .Op Fl 46DdeiqTt .Op Fl C Ar connection_spec .Op Fl c Ar host_certificate_file .Op Fl E Ar log_file .Op Fl f Ar config_file .Op Fl g Ar login_grace_time .Op Fl h Ar host_key_file .Op Fl o Ar option .Op Fl p Ar port .Op Fl u Ar len .Ek .Sh DESCRIPTION .Nm (OpenSSH Daemon) is the daemon program for .Xr ssh 1 . Together these programs replace rlogin and rsh, and provide secure encrypted communications between two untrusted hosts over an insecure network. .Pp .Nm listens for connections from clients. It is normally started at boot from .Pa /etc/rc.d/sshd . It forks a new daemon for each incoming connection. The forked daemons handle key exchange, encryption, authentication, command execution, and data exchange. .Pp .Nm can be configured using command-line options or a configuration file (by default .Xr sshd_config 5 ) ; command-line options override values specified in the configuration file. .Nm rereads its configuration file when it receives a hangup signal, .Dv SIGHUP , by executing itself with the name and options it was started with, e.g.\& .Pa /usr/sbin/sshd . .Pp The options are as follows: .Bl -tag -width Ds .It Fl 4 Forces .Nm to use IPv4 addresses only. .It Fl 6 Forces .Nm to use IPv6 addresses only. .It Fl C Ar connection_spec Specify the connection parameters to use for the .Fl T extended test mode. If provided, any .Cm Match directives in the configuration file that would apply to the specified user, host, and address will be set before the configuration is written to standard output. The connection parameters are supplied as keyword=value pairs. The keywords are .Dq user , .Dq host , .Dq laddr , .Dq lport , and .Dq addr . All are required and may be supplied in any order, either with multiple .Fl C options or as a comma-separated list. .It Fl c Ar host_certificate_file Specifies a path to a certificate file to identify .Nm during key exchange. The certificate file must match a host key file specified using the .Fl h option or the .Cm HostKey configuration directive. .It Fl D When this option is specified, .Nm will not detach and does not become a daemon. This allows easy monitoring of .Nm sshd . .It Fl d Debug mode. The server sends verbose debug output to standard error, and does not put itself in the background. The server also will not fork and will only process one connection. This option is only intended for debugging for the server. Multiple .Fl d options increase the debugging level. Maximum is 3. .It Fl E Ar log_file Append debug logs to .Ar log_file instead of the system log. .It Fl e Write debug logs to standard error instead of the system log. .It Fl f Ar config_file Specifies the name of the configuration file. The default is .Pa /etc/ssh/sshd_config . .Nm refuses to start if there is no configuration file. .It Fl g Ar login_grace_time Gives the grace time for clients to authenticate themselves (default 120 seconds). If the client fails to authenticate the user within this many seconds, the server disconnects and exits. A value of zero indicates no limit. .It Fl h Ar host_key_file Specifies a file from which a host key is read. This option must be given if .Nm is not run as root (as the normal host key files are normally not readable by anyone but root). The default is .Pa /etc/ssh/ssh_host_dsa_key , .Pa /etc/ssh/ssh_host_ecdsa_key , .Pa /etc/ssh/ssh_host_ed25519_key and .Pa /etc/ssh/ssh_host_rsa_key . It is possible to have multiple host key files for the different host key algorithms. .It Fl i Specifies that .Nm is being run from .Xr inetd 8 . .It Fl o Ar option Can be used to give options in the format used in the configuration file. This is useful for specifying options for which there is no separate command-line flag. For full details of the options, and their values, see .Xr sshd_config 5 . .It Fl p Ar port Specifies the port on which the server listens for connections (default 22). Multiple port options are permitted. Ports specified in the configuration file with the .Cm Port option are ignored when a command-line port is specified. Ports specified using the .Cm ListenAddress option override command-line ports. .It Fl q Quiet mode. Nothing is sent to the system log. Normally the beginning, authentication, and termination of each connection is logged. .It Fl T Extended test mode. Check the validity of the configuration file, output the effective configuration to stdout and then exit. Optionally, .Cm Match rules may be applied by specifying the connection parameters using one or more .Fl C options. .It Fl t Test mode. Only check the validity of the configuration file and sanity of the keys. This is useful for updating .Nm reliably as configuration options may change. .It Fl u Ar len This option is used to specify the size of the field in the .Li utmp structure that holds the remote host name. If the resolved host name is longer than .Ar len , the dotted decimal value will be used instead. This allows hosts with very long host names that overflow this field to still be uniquely identified. Specifying .Fl u0 indicates that only dotted decimal addresses should be put into the .Pa utmp file. .Fl u0 may also be used to prevent .Nm from making DNS requests unless the authentication mechanism or configuration requires it. Authentication mechanisms that may require DNS include .Cm HostbasedAuthentication and using a .Cm from="pattern-list" option in a key file. Configuration options that require DNS include using a USER@HOST pattern in .Cm AllowUsers or .Cm DenyUsers . .El .Sh AUTHENTICATION The OpenSSH SSH daemon supports SSH protocol 2 only. Each host has a host-specific key, used to identify the host. Whenever a client connects, the daemon responds with its public host key. The client compares the host key against its own database to verify that it has not changed. Forward security is provided through a Diffie-Hellman key agreement. This key agreement results in a shared session key. The rest of the session is encrypted using a symmetric cipher, currently 128-bit AES, Blowfish, 3DES, CAST128, Arcfour, 192-bit AES, or 256-bit AES. The client selects the encryption algorithm to use from those offered by the server. Additionally, session integrity is provided through a cryptographic message authentication code (hmac-md5, hmac-sha1, umac-64, umac-128, hmac-ripemd160, hmac-sha2-256 or hmac-sha2-512). .Pp Finally, the server and the client enter an authentication dialog. The client tries to authenticate itself using host-based authentication, public key authentication, challenge-response authentication, or password authentication. .Pp Regardless of the authentication type, the account is checked to ensure that it is accessible. An account is not accessible if it is locked, listed in .Cm DenyUsers or its group is listed in .Cm DenyGroups \&. The definition of a locked account is system dependant. Some platforms have their own account database (eg AIX) and some modify the passwd field ( .Ql \&*LK\&* on Solaris and UnixWare, .Ql \&* on HP-UX, containing .Ql Nologin on Tru64, a leading .Ql \&*LOCKED\&* on FreeBSD and a leading .Ql \&! on most Linuxes). If there is a requirement to disable password authentication for the account while allowing still public-key, then the passwd field should be set to something other than these values (eg .Ql NP or .Ql \&*NP\&* ). .Pp If the client successfully authenticates itself, a dialog for preparing the session is entered. At this time the client may request things like allocating a pseudo-tty, forwarding X11 connections, forwarding TCP connections, or forwarding the authentication agent connection over the secure channel. .Pp After this, the client either requests a shell or execution of a command. The sides then enter session mode. In this mode, either side may send data at any time, and such data is forwarded to/from the shell or command on the server side, and the user terminal in the client side. .Pp When the user program terminates and all forwarded X11 and other connections have been closed, the server sends command exit status to the client, and both sides exit. .Sh LOGIN PROCESS When a user successfully logs in, .Nm does the following: .Bl -enum -offset indent .It If the login is on a tty, and no command has been specified, prints last login time and .Pa /etc/motd (unless prevented in the configuration file or by .Pa ~/.hushlogin ; see the .Sx FILES section). .It If the login is on a tty, records login time. .It Checks .Pa /etc/nologin and .Pa /var/run/nologin ; if one exists, it prints the contents and quits (unless root). .It Changes to run with normal user privileges. .It Sets up basic environment. .It Reads the file .Pa ~/.ssh/environment , if it exists, and users are allowed to change their environment. See the .Cm PermitUserEnvironment option in .Xr sshd_config 5 . .It Changes to user's home directory. .It If .Pa ~/.ssh/rc exists and the .Xr sshd_config 5 .Cm PermitUserRC option is set, runs it; else if .Pa /etc/ssh/sshrc exists, runs it; otherwise runs .Xr xauth 1 . The .Dq rc files are given the X11 authentication protocol and cookie in standard input. See .Sx SSHRC , below. .It Runs user's shell or command. All commands are run under the user's login shell as specified in the system password database. .El .Sh SSHRC If the file .Pa ~/.ssh/rc exists, .Xr sh 1 runs it after reading the environment files but before starting the user's shell or command. It must not produce any output on stdout; stderr must be used instead. If X11 forwarding is in use, it will receive the "proto cookie" pair in its standard input (and .Ev DISPLAY in its environment). The script must call .Xr xauth 1 because .Nm will not run xauth automatically to add X11 cookies. .Pp The primary purpose of this file is to run any initialization routines which may be needed before the user's home directory becomes accessible; AFS is a particular example of such an environment. .Pp This file will probably contain some initialization code followed by something similar to: .Bd -literal -offset 3n if read proto cookie && [ -n "$DISPLAY" ]; then if [ `echo $DISPLAY | cut -c1-10` = 'localhost:' ]; then # X11UseLocalhost=yes echo add unix:`echo $DISPLAY | cut -c11-` $proto $cookie else # X11UseLocalhost=no echo add $DISPLAY $proto $cookie fi | xauth -q - fi .Ed .Pp If this file does not exist, .Pa /etc/ssh/sshrc is run, and if that does not exist either, xauth is used to add the cookie. .Sh AUTHORIZED_KEYS FILE FORMAT .Cm AuthorizedKeysFile specifies the files containing public keys for public key authentication; if this option is not specified, the default is .Pa ~/.ssh/authorized_keys and .Pa ~/.ssh/authorized_keys2 . Each line of the file contains one key (empty lines and lines starting with a .Ql # are ignored as comments). Public keys consist of the following space-separated fields: options, keytype, base64-encoded key, comment. The options field is optional. The keytype is .Dq ecdsa-sha2-nistp256 , .Dq ecdsa-sha2-nistp384 , .Dq ecdsa-sha2-nistp521 , .Dq ssh-ed25519 , .Dq ssh-dss or .Dq ssh-rsa ; the comment field is not used for anything (but may be convenient for the user to identify the key). .Pp Note that lines in this file can be several hundred bytes long (because of the size of the public key encoding) up to a limit of 8 kilobytes, which permits DSA keys up to 8 kilobits and RSA keys up to 16 kilobits. You don't want to type them in; instead, copy the .Pa id_dsa.pub , .Pa id_ecdsa.pub , .Pa id_ed25519.pub , or the .Pa id_rsa.pub file and edit it. .Pp .Nm enforces a minimum RSA key modulus size of 768 bits. .Pp The options (if present) consist of comma-separated option specifications. No spaces are permitted, except within double quotes. The following option specifications are supported (note that option keywords are case-insensitive): .Bl -tag -width Ds .It Cm agent-forwarding Enable authentication agent forwarding previously disabled by the .Cm restrict option. .It Cm cert-authority Specifies that the listed key is a certification authority (CA) that is trusted to validate signed certificates for user authentication. .Pp Certificates may encode access restrictions similar to these key options. If both certificate restrictions and key options are present, the most restrictive union of the two is applied. .It Cm command="command" Specifies that the command is executed whenever this key is used for authentication. The command supplied by the user (if any) is ignored. The command is run on a pty if the client requests a pty; otherwise it is run without a tty. If an 8-bit clean channel is required, one must not request a pty or should specify .Cm no-pty . A quote may be included in the command by quoting it with a backslash. .Pp This option might be useful to restrict certain public keys to perform just a specific operation. An example might be a key that permits remote backups but nothing else. Note that the client may specify TCP and/or X11 forwarding unless they are explicitly prohibited, e.g. using the .Cm restrict key option. .Pp The command originally supplied by the client is available in the .Ev SSH_ORIGINAL_COMMAND environment variable. Note that this option applies to shell, command or subsystem execution. Also note that this command may be superseded by a .Xr sshd_config 5 .Cm ForceCommand directive. .Pp If a command is specified and a forced-command is embedded in a certificate used for authentication, then the certificate will be accepted only if the two commands are identical. .It Cm environment="NAME=value" Specifies that the string is to be added to the environment when logging in using this key. Environment variables set this way override other default environment values. Multiple options of this type are permitted. Environment processing is disabled by default and is controlled via the .Cm PermitUserEnvironment option. .It Cm from="pattern-list" Specifies that in addition to public key authentication, either the canonical name of the remote host or its IP address must be present in the comma-separated list of patterns. See PATTERNS in .Xr ssh_config 5 for more information on patterns. .Pp In addition to the wildcard matching that may be applied to hostnames or addresses, a .Cm from stanza may match IP addresses using CIDR address/masklen notation. .Pp The purpose of this option is to optionally increase security: public key authentication by itself does not trust the network or name servers or anything (but the key); however, if somebody somehow steals the key, the key permits an intruder to log in from anywhere in the world. This additional option makes using a stolen key more difficult (name servers and/or routers would have to be compromised in addition to just the key). .It Cm no-agent-forwarding Forbids authentication agent forwarding when this key is used for authentication. .It Cm no-port-forwarding Forbids TCP forwarding when this key is used for authentication. Any port forward requests by the client will return an error. This might be used, e.g. in connection with the .Cm command option. .It Cm no-pty Prevents tty allocation (a request to allocate a pty will fail). .It Cm no-user-rc Disables execution of .Pa ~/.ssh/rc . .It Cm no-X11-forwarding Forbids X11 forwarding when this key is used for authentication. Any X11 forward requests by the client will return an error. .It Cm permitopen="host:port" Limit local port forwarding with .Xr ssh 1 .Fl L such that it may only connect to the specified host and port. IPv6 addresses can be specified by enclosing the address in square brackets. Multiple .Cm permitopen options may be applied separated by commas. No pattern matching is performed on the specified hostnames, they must be literal domains or addresses. A port specification of .Cm * matches any port. .It Cm port-forwarding Enable port forwarding previously disabled by the .Cm restrict .It Cm principals="principals" On a .Cm cert-authority line, specifies allowed principals for certificate authentication as a comma-separated list. At least one name from the list must appear in the certificate's list of principals for the certificate to be accepted. This option is ignored for keys that are not marked as trusted certificate signers using the .Cm cert-authority option. .It Cm pty Permits tty allocation previously disabled by the .Cm restrict option. .It Cm restrict Enable all restrictions, i.e. disable port, agent and X11 forwarding, as well as disabling PTY allocation and execution of .Pa ~/.ssh/rc . If any future restriction capabilities are added to authorized_keys files they will be included in this set. .It Cm tunnel="n" Force a .Xr tun 4 device on the server. Without this option, the next available device will be used if the client requests a tunnel. .It Cm user-rc Enables execution of .Pa ~/.ssh/rc previously disabled by the .Cm restrict option. .It Cm X11-forwarding Permits X11 forwarding previously disabled by the .Cm restrict option. .El .Pp An example authorized_keys file: .Bd -literal -offset 3n # Comments allowed at start of line ssh-rsa AAAAB3Nza...LiPk== user@example.net from="*.sales.example.net,!pc.sales.example.net" ssh-rsa AAAAB2...19Q== john@example.net command="dump /home",no-pty,no-port-forwarding ssh-dss AAAAC3...51R== example.net permitopen="192.0.2.1:80",permitopen="192.0.2.2:25" ssh-dss AAAAB5...21S== tunnel="0",command="sh /etc/netstart tun0" ssh-rsa AAAA...== jane@example.net restrict,command="uptime" ssh-rsa AAAA1C8...32Tv== user@example.net restrict,pty,command="nethack" ssh-rsa AAAA1f8...IrrC5== user@example.net .Ed .Sh SSH_KNOWN_HOSTS FILE FORMAT The .Pa /etc/ssh/ssh_known_hosts and .Pa ~/.ssh/known_hosts files contain host public keys for all known hosts. The global file should be prepared by the administrator (optional), and the per-user file is -maintained automatically: whenever the user connects from an unknown host, +maintained automatically: whenever the user connects to an unknown host, its key is added to the per-user file. .Pp Each line in these files contains the following fields: markers (optional), hostnames, keytype, base64-encoded key, comment. The fields are separated by spaces. .Pp The marker is optional, but if it is present then it must be one of .Dq @cert-authority , to indicate that the line contains a certification authority (CA) key, or .Dq @revoked , to indicate that the key contained on the line is revoked and must not ever be accepted. Only one marker should be used on a key line. .Pp Hostnames is a comma-separated list of patterns .Pf ( Ql * and .Ql \&? act as wildcards); each pattern in turn is matched against the canonical host name (when authenticating a client) or against the user-supplied name (when authenticating a server). A pattern may also be preceded by .Ql \&! to indicate negation: if the host name matches a negated pattern, it is not accepted (by that line) even if it matched another pattern on the line. A hostname or address may optionally be enclosed within .Ql \&[ and .Ql \&] brackets then followed by .Ql \&: and a non-standard port number. .Pp Alternately, hostnames may be stored in a hashed form which hides host names and addresses should the file's contents be disclosed. Hashed hostnames start with a .Ql | character. Only one hashed hostname may appear on a single line and none of the above negation or wildcard operators may be applied. .Pp The keytype and base64-encoded key are taken directly from the host key; they can be obtained, for example, from .Pa /etc/ssh/ssh_host_rsa_key.pub . The optional comment field continues to the end of the line, and is not used. .Pp Lines starting with .Ql # and empty lines are ignored as comments. .Pp When performing host authentication, authentication is accepted if any matching line has the proper key; either one that matches exactly or, if the server has presented a certificate for authentication, the key of the certification authority that signed the certificate. For a key to be trusted as a certification authority, it must use the .Dq @cert-authority marker described above. .Pp The known hosts file also provides a facility to mark keys as revoked, for example when it is known that the associated private key has been stolen. Revoked keys are specified by including the .Dq @revoked marker at the beginning of the key line, and are never accepted for authentication or as certification authorities, but instead will produce a warning from .Xr ssh 1 when they are encountered. .Pp It is permissible (but not recommended) to have several lines or different host keys for the same names. This will inevitably happen when short forms of host names from different domains are put in the file. It is possible that the files contain conflicting information; authentication is accepted if valid information can be found from either file. .Pp Note that the lines in these files are typically hundreds of characters long, and you definitely don't want to type in the host keys by hand. Rather, generate them by a script, .Xr ssh-keyscan 1 or by taking, for example, .Pa /etc/ssh/ssh_host_rsa_key.pub and adding the host names at the front. .Xr ssh-keygen 1 also offers some basic automated editing for .Pa ~/.ssh/known_hosts including removing hosts matching a host name and converting all host names to their hashed representations. .Pp An example ssh_known_hosts file: .Bd -literal -offset 3n # Comments allowed at start of line closenet,...,192.0.2.53 1024 37 159...93 closenet.example.net cvs.example.net,192.0.2.10 ssh-rsa AAAA1234.....= # A hashed hostname |1|JfKTdBh7rNbXkVAQCRp4OQoPfmI=|USECr3SWf1JUPsms5AqfD5QfxkM= ssh-rsa AAAA1234.....= # A revoked key @revoked * ssh-rsa AAAAB5W... # A CA key, accepted for any host in *.mydomain.com or *.mydomain.org @cert-authority *.mydomain.org,*.mydomain.com ssh-rsa AAAAB5W... .Ed .Sh FILES .Bl -tag -width Ds -compact .It Pa ~/.hushlogin This file is used to suppress printing the last login time and .Pa /etc/motd , if .Cm PrintLastLog and .Cm PrintMotd , respectively, are enabled. It does not suppress printing of the banner specified by .Cm Banner . .Pp .It Pa ~/.rhosts This file is used for host-based authentication (see .Xr ssh 1 for more information). On some machines this file may need to be world-readable if the user's home directory is on an NFS partition, because .Nm reads it as root. Additionally, this file must be owned by the user, and must not have write permissions for anyone else. The recommended permission for most machines is read/write for the user, and not accessible by others. .Pp .It Pa ~/.shosts This file is used in exactly the same way as .Pa .rhosts , but allows host-based authentication without permitting login with rlogin/rsh. .Pp .It Pa ~/.ssh/ This directory is the default location for all user-specific configuration and authentication information. There is no general requirement to keep the entire contents of this directory secret, but the recommended permissions are read/write/execute for the user, and not accessible by others. .Pp .It Pa ~/.ssh/authorized_keys Lists the public keys (DSA, ECDSA, Ed25519, RSA) that can be used for logging in as this user. The format of this file is described above. The content of the file is not highly sensitive, but the recommended permissions are read/write for the user, and not accessible by others. .Pp If this file, the .Pa ~/.ssh directory, or the user's home directory are writable by other users, then the file could be modified or replaced by unauthorized users. In this case, .Nm will not allow it to be used unless the .Cm StrictModes option has been set to .Dq no . .Pp .It Pa ~/.ssh/environment This file is read into the environment at login (if it exists). It can only contain empty lines, comment lines (that start with .Ql # ) , and assignment lines of the form name=value. The file should be writable only by the user; it need not be readable by anyone else. Environment processing is disabled by default and is controlled via the .Cm PermitUserEnvironment option. .Pp .It Pa ~/.ssh/known_hosts Contains a list of host keys for all hosts the user has logged into that are not already in the systemwide list of known host keys. The format of this file is described above. This file should be writable only by root/the owner and can, but need not be, world-readable. .Pp .It Pa ~/.ssh/rc Contains initialization routines to be run before the user's home directory becomes accessible. This file should be writable only by the user, and need not be readable by anyone else. .Pp .It Pa /etc/hosts.allow .It Pa /etc/hosts.deny Access controls that should be enforced by tcp-wrappers are defined here. Further details are described in .Xr hosts_access 5 . .Pp .It Pa /etc/hosts.equiv This file is for host-based authentication (see .Xr ssh 1 ) . It should only be writable by root. .Pp .It Pa /etc/moduli Contains Diffie-Hellman groups used for the "Diffie-Hellman Group Exchange" key exchange method. The file format is described in .Xr moduli 5 . If no usable groups are found in this file then fixed internal groups will be used. .Pp .It Pa /etc/motd See .Xr motd 5 . .Pp .It Pa /etc/nologin If this file exists, .Nm refuses to let anyone except root log in. The contents of the file are displayed to anyone trying to log in, and non-root connections are refused. The file should be world-readable. .Pp .It Pa /etc/shosts.equiv This file is used in exactly the same way as .Pa hosts.equiv , but allows host-based authentication without permitting login with rlogin/rsh. .Pp .It Pa /etc/ssh/ssh_host_dsa_key .It Pa /etc/ssh/ssh_host_ecdsa_key .It Pa /etc/ssh/ssh_host_ed25519_key .It Pa /etc/ssh/ssh_host_rsa_key These files contain the private parts of the host keys. These files should only be owned by root, readable only by root, and not accessible to others. Note that .Nm does not start if these files are group/world-accessible. .Pp .It Pa /etc/ssh/ssh_host_dsa_key.pub .It Pa /etc/ssh/ssh_host_ecdsa_key.pub .It Pa /etc/ssh/ssh_host_ed25519_key.pub .It Pa /etc/ssh/ssh_host_rsa_key.pub These files contain the public parts of the host keys. These files should be world-readable but writable only by root. Their contents should match the respective private parts. These files are not really used for anything; they are provided for the convenience of the user so their contents can be copied to known hosts files. These files are created using .Xr ssh-keygen 1 . .Pp .It Pa /etc/ssh/ssh_known_hosts Systemwide list of known host keys. This file should be prepared by the system administrator to contain the public host keys of all machines in the organization. The format of this file is described above. This file should be writable only by root/the owner and should be world-readable. .Pp .It Pa /etc/ssh/sshd_config Contains configuration data for .Nm sshd . The file format and configuration options are described in .Xr sshd_config 5 . .Pp .It Pa /etc/ssh/sshrc Similar to .Pa ~/.ssh/rc , it can be used to specify machine-specific login-time initializations globally. This file should be writable only by root, and should be world-readable. .Pp .It Pa /var/empty .Xr chroot 2 directory used by .Nm during privilege separation in the pre-authentication phase. The directory should not contain any files and must be owned by root and not group or world-writable. .Pp .It Pa /var/run/sshd.pid Contains the process ID of the .Nm listening for connections (if there are several daemons running concurrently for different ports, this contains the process ID of the one started last). The content of this file is not sensitive; it can be world-readable. .El .Sh SEE ALSO .Xr scp 1 , .Xr sftp 1 , .Xr ssh 1 , .Xr ssh-add 1 , .Xr ssh-agent 1 , .Xr ssh-keygen 1 , .Xr ssh-keyscan 1 , .Xr chroot 2 , .Xr hosts_access 5 , .Xr login.conf 5 , .Xr moduli 5 , .Xr sshd_config 5 , .Xr inetd 8 , .Xr sftp-server 8 .Sh AUTHORS OpenSSH is a derivative of the original and free ssh 1.2.12 release by Tatu Ylonen. Aaron Campbell, Bob Beck, Markus Friedl, Niels Provos, Theo de Raadt and Dug Song removed many bugs, re-added newer features and created OpenSSH. Markus Friedl contributed the support for SSH protocol versions 1.5 and 2.0. Niels Provos and Markus Friedl contributed support for privilege separation. diff --git a/crypto/openssh/sshd.c b/crypto/openssh/sshd.c index 50d3701e78b2..ac494844430d 100644 --- a/crypto/openssh/sshd.c +++ b/crypto/openssh/sshd.c @@ -1,2296 +1,2310 @@ -/* $OpenBSD: sshd.c,v 1.480 2016/12/09 03:04:29 djm Exp $ */ +/* $OpenBSD: sshd.c,v 1.485 2017/03/15 03:52:30 deraadt Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland * All rights reserved * This program is the ssh daemon. It listens for connections from clients, * and performs authentication, executes use commands or shell, and forwards * information to/from the application to the user client over an encrypted * connection. This can also handle forwarding of X11, TCP/IP, and * authentication agent connections. * * As far as I am concerned, the code I have written for this software * can be used freely for any purpose. Any derived versions of this * software must be clearly marked as such, and if the derived work is * incompatible with the protocol description in the RFC file, it must be * called by a name other than "ssh" or "Secure Shell". * * SSH2 implementation: * Privilege Separation: * * Copyright (c) 2000, 2001, 2002 Markus Friedl. All rights reserved. * Copyright (c) 2002 Niels Provos. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" __RCSID("$FreeBSD$"); #include #include #include #include #ifdef HAVE_SYS_STAT_H # include #endif #ifdef HAVE_SYS_TIME_H # include #endif #include "openbsd-compat/sys-tree.h" #include "openbsd-compat/sys-queue.h" #include #include #include #include #ifdef HAVE_PATHS_H #include #endif #include #include #include #include #include #include #include #include #include #ifdef WITH_OPENSSL #include #include #include #include "openbsd-compat/openssl-compat.h" #endif #ifdef HAVE_SECUREWARE #include #include #endif #ifdef __FreeBSD__ #include #if defined(GSSAPI) && defined(HAVE_GSSAPI_GSSAPI_H) #include #elif defined(GSSAPI) && defined(HAVE_GSSAPI_H) #include #endif #endif #include "xmalloc.h" #include "ssh.h" #include "ssh2.h" #include "rsa.h" #include "sshpty.h" #include "packet.h" #include "log.h" #include "buffer.h" #include "misc.h" #include "match.h" #include "servconf.h" #include "uidswap.h" #include "compat.h" #include "cipher.h" #include "digest.h" #include "key.h" #include "kex.h" #include "myproposal.h" #include "authfile.h" #include "pathnames.h" #include "atomicio.h" #include "canohost.h" #include "hostfile.h" #include "auth.h" #include "authfd.h" #include "msg.h" #include "dispatch.h" #include "channels.h" #include "session.h" #include "monitor.h" #ifdef GSSAPI #include "ssh-gss.h" #endif #include "monitor_wrap.h" #include "ssh-sandbox.h" #include "version.h" #include "ssherr.h" #include "blacklist_client.h" #ifdef LIBWRAP #include #include int allow_severity; int deny_severity; #endif /* LIBWRAP */ /* Re-exec fds */ #define REEXEC_DEVCRYPTO_RESERVED_FD (STDERR_FILENO + 1) #define REEXEC_STARTUP_PIPE_FD (STDERR_FILENO + 2) #define REEXEC_CONFIG_PASS_FD (STDERR_FILENO + 3) #define REEXEC_MIN_FREE_FD (STDERR_FILENO + 4) extern char *__progname; /* Server configuration options. */ ServerOptions options; /* Name of the server configuration file. */ char *config_file_name = _PATH_SERVER_CONFIG_FILE; /* * Debug mode flag. This can be set on the command line. If debug * mode is enabled, extra debugging output will be sent to the system * log, the daemon will not go to background, and will exit after processing * the first connection. */ int debug_flag = 0; /* Flag indicating that the daemon should only test the configuration and keys. */ int test_flag = 0; /* Flag indicating that the daemon is being started from inetd. */ int inetd_flag = 0; /* Flag indicating that sshd should not detach and become a daemon. */ int no_daemon_flag = 0; /* debug goes to stderr unless inetd_flag is set */ int log_stderr = 0; /* Saved arguments to main(). */ char **saved_argv; int saved_argc; /* re-exec */ int rexeced_flag = 0; int rexec_flag = 1; int rexec_argc = 0; char **rexec_argv; /* * The sockets that the server is listening; this is used in the SIGHUP * signal handler. */ #define MAX_LISTEN_SOCKS 16 int listen_socks[MAX_LISTEN_SOCKS]; int num_listen_socks = 0; /* * the client's version string, passed by sshd2 in compat mode. if != NULL, * sshd will skip the version-number exchange */ char *client_version_string = NULL; char *server_version_string = NULL; /* Daemon's agent connection */ int auth_sock = -1; int have_agent = 0; /* * Any really sensitive data in the application is contained in this * structure. The idea is that this structure could be locked into memory so * that the pages do not get written into swap. However, there are some * problems. The private key contains BIGNUMs, and we do not (in principle) * have access to the internals of them, and locking just the structure is * not very useful. Currently, memory locking is not implemented. */ struct { Key **host_keys; /* all private host keys */ Key **host_pubkeys; /* all public host keys */ Key **host_certificates; /* all public host certificates */ int have_ssh2_key; } sensitive_data; /* This is set to true when a signal is received. */ static volatile sig_atomic_t received_sighup = 0; static volatile sig_atomic_t received_sigterm = 0; /* session identifier, used by RSA-auth */ u_char session_id[16]; /* same for ssh2 */ u_char *session_id2 = NULL; u_int session_id2_len = 0; /* record remote hostname or ip */ u_int utmp_len = HOST_NAME_MAX+1; /* options.max_startup sized array of fd ints */ int *startup_pipes = NULL; int startup_pipe; /* in child */ /* variables used for privilege separation */ int use_privsep = -1; struct monitor *pmonitor = NULL; int privsep_is_preauth = 1; /* global authentication context */ Authctxt *the_authctxt = NULL; /* sshd_config buffer */ Buffer cfg; /* message to be displayed after login */ Buffer loginmsg; /* Unprivileged user */ struct passwd *privsep_pw = NULL; /* Prototypes for various functions defined later in this file. */ void destroy_sensitive_data(void); void demote_sensitive_data(void); static void do_ssh2_kex(void); /* * Close all listening sockets */ static void close_listen_socks(void) { int i; for (i = 0; i < num_listen_socks; i++) close(listen_socks[i]); num_listen_socks = -1; } static void close_startup_pipes(void) { int i; if (startup_pipes) for (i = 0; i < options.max_startups; i++) if (startup_pipes[i] != -1) close(startup_pipes[i]); } /* * Signal handler for SIGHUP. Sshd execs itself when it receives SIGHUP; * the effect is to reread the configuration file (and to regenerate * the server key). */ /*ARGSUSED*/ static void sighup_handler(int sig) { int save_errno = errno; received_sighup = 1; signal(SIGHUP, sighup_handler); errno = save_errno; } /* * Called from the main program after receiving SIGHUP. * Restarts the server. */ static void sighup_restart(void) { logit("Received SIGHUP; restarting."); if (options.pid_file != NULL) unlink(options.pid_file); platform_pre_restart(); close_listen_socks(); close_startup_pipes(); alarm(0); /* alarm timer persists across exec */ signal(SIGHUP, SIG_IGN); /* will be restored after exec */ execv(saved_argv[0], saved_argv); logit("RESTART FAILED: av[0]='%.100s', error: %.100s.", saved_argv[0], strerror(errno)); exit(1); } /* * Generic signal handler for terminating signals in the master daemon. */ /*ARGSUSED*/ static void sigterm_handler(int sig) { received_sigterm = sig; } /* * SIGCHLD handler. This is called whenever a child dies. This will then * reap any zombies left by exited children. */ /*ARGSUSED*/ static void main_sigchld_handler(int sig) { int save_errno = errno; pid_t pid; int status; while ((pid = waitpid(-1, &status, WNOHANG)) > 0 || (pid < 0 && errno == EINTR)) ; signal(SIGCHLD, main_sigchld_handler); errno = save_errno; } /* * Signal handler for the alarm after the login grace period has expired. */ /*ARGSUSED*/ static void grace_alarm_handler(int sig) { if (use_privsep && pmonitor != NULL && pmonitor->m_pid > 0) kill(pmonitor->m_pid, SIGALRM); /* * Try to kill any processes that we have spawned, E.g. authorized * keys command helpers. */ if (getpgid(0) == getpid()) { signal(SIGTERM, SIG_IGN); kill(0, SIGTERM); } BLACKLIST_NOTIFY(BLACKLIST_AUTH_FAIL, "ssh"); /* Log error and exit. */ sigdie("Timeout before authentication for %s port %d", ssh_remote_ipaddr(active_state), ssh_remote_port(active_state)); } static void sshd_exchange_identification(struct ssh *ssh, int sock_in, int sock_out) { u_int i; int remote_major, remote_minor; - char *s, *newline = "\n"; + char *s; char buf[256]; /* Must not be larger than remote_version. */ char remote_version[256]; /* Must be at least as big as buf. */ - xasprintf(&server_version_string, "SSH-%d.%d-%.100s%s%s%s", + xasprintf(&server_version_string, "SSH-%d.%d-%.100s%s%s\r\n", PROTOCOL_MAJOR_2, PROTOCOL_MINOR_2, SSH_VERSION, *options.version_addendum == '\0' ? "" : " ", - options.version_addendum, newline); + options.version_addendum); /* Send our protocol version identification. */ if (atomicio(vwrite, sock_out, server_version_string, strlen(server_version_string)) != strlen(server_version_string)) { logit("Could not write ident string to %s port %d", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); cleanup_exit(255); } /* Read other sides version identification. */ memset(buf, 0, sizeof(buf)); for (i = 0; i < sizeof(buf) - 1; i++) { if (atomicio(read, sock_in, &buf[i], 1) != 1) { logit("Did not receive identification string " "from %s port %d", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); cleanup_exit(255); } if (buf[i] == '\r') { buf[i] = 0; /* Kludge for F-Secure Macintosh < 1.0.2 */ if (i == 12 && strncmp(buf, "SSH-1.5-W1.0", 12) == 0) break; continue; } if (buf[i] == '\n') { buf[i] = 0; break; } } buf[sizeof(buf) - 1] = 0; client_version_string = xstrdup(buf); /* * Check that the versions match. In future this might accept * several versions and set appropriate flags to handle them. */ if (sscanf(client_version_string, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor, remote_version) != 3) { s = "Protocol mismatch.\n"; (void) atomicio(vwrite, sock_out, s, strlen(s)); logit("Bad protocol version identification '%.100s' " "from %s port %d", client_version_string, ssh_remote_ipaddr(ssh), ssh_remote_port(ssh)); close(sock_in); close(sock_out); cleanup_exit(255); } debug("Client protocol version %d.%d; client software version %.100s", remote_major, remote_minor, remote_version); ssh->compat = compat_datafellows(remote_version); if ((ssh->compat & SSH_BUG_PROBE) != 0) { logit("probed from %s port %d with %s. Don't panic.", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), client_version_string); cleanup_exit(255); } if ((ssh->compat & SSH_BUG_SCANNER) != 0) { logit("scanned from %s port %d with %s. Don't panic.", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), client_version_string); cleanup_exit(255); } if ((ssh->compat & SSH_BUG_RSASIGMD5) != 0) { logit("Client version \"%.100s\" uses unsafe RSA signature " "scheme; disabling use of RSA keys", remote_version); } if ((ssh->compat & SSH_BUG_DERIVEKEY) != 0) { fatal("Client version \"%.100s\" uses unsafe key agreement; " "refusing connection", remote_version); } chop(server_version_string); debug("Local version string %.200s", server_version_string); if (remote_major == 2 || (remote_major == 1 && remote_minor == 99)) { enable_compat20(); } else { s = "Protocol major versions differ.\n"; (void) atomicio(vwrite, sock_out, s, strlen(s)); close(sock_in); close(sock_out); logit("Protocol major versions differ for %s port %d: " "%.200s vs. %.200s", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), server_version_string, client_version_string); cleanup_exit(255); } } /* Destroy the host and server keys. They will no longer be needed. */ void destroy_sensitive_data(void) { int i; for (i = 0; i < options.num_host_key_files; i++) { if (sensitive_data.host_keys[i]) { key_free(sensitive_data.host_keys[i]); sensitive_data.host_keys[i] = NULL; } if (sensitive_data.host_certificates[i]) { key_free(sensitive_data.host_certificates[i]); sensitive_data.host_certificates[i] = NULL; } } } /* Demote private to public keys for network child */ void demote_sensitive_data(void) { Key *tmp; int i; for (i = 0; i < options.num_host_key_files; i++) { if (sensitive_data.host_keys[i]) { tmp = key_demote(sensitive_data.host_keys[i]); key_free(sensitive_data.host_keys[i]); sensitive_data.host_keys[i] = tmp; } /* Certs do not need demotion */ } } static void reseed_prngs(void) { u_int32_t rnd[256]; #ifdef WITH_OPENSSL RAND_poll(); #endif arc4random_stir(); /* noop on recent arc4random() implementations */ arc4random_buf(rnd, sizeof(rnd)); /* let arc4random notice PID change */ #ifdef WITH_OPENSSL RAND_seed(rnd, sizeof(rnd)); /* give libcrypto a chance to notice the PID change */ if ((RAND_bytes((u_char *)rnd, 1)) != 1) fatal("%s: RAND_bytes failed", __func__); #endif explicit_bzero(rnd, sizeof(rnd)); } static void privsep_preauth_child(void) { gid_t gidset[1]; /* Enable challenge-response authentication for privilege separation */ privsep_challenge_enable(); #ifdef GSSAPI /* Cache supported mechanism OIDs for later use */ if (options.gss_authentication) ssh_gssapi_prepare_supported_oids(); #endif reseed_prngs(); /* Demote the private keys to public keys. */ demote_sensitive_data(); /* Demote the child */ if (getuid() == 0 || geteuid() == 0) { /* Change our root directory */ if (chroot(_PATH_PRIVSEP_CHROOT_DIR) == -1) fatal("chroot(\"%s\"): %s", _PATH_PRIVSEP_CHROOT_DIR, strerror(errno)); if (chdir("/") == -1) fatal("chdir(\"/\"): %s", strerror(errno)); /* Drop our privileges */ debug3("privsep user:group %u:%u", (u_int)privsep_pw->pw_uid, (u_int)privsep_pw->pw_gid); gidset[0] = privsep_pw->pw_gid; if (setgroups(1, gidset) < 0) fatal("setgroups: %.100s", strerror(errno)); permanently_set_uid(privsep_pw); } } static int privsep_preauth(Authctxt *authctxt) { int status, r; pid_t pid; struct ssh_sandbox *box = NULL; /* Set up unprivileged child process to deal with network data */ pmonitor = monitor_init(); /* Store a pointer to the kex for later rekeying */ pmonitor->m_pkex = &active_state->kex; if (use_privsep == PRIVSEP_ON) box = ssh_sandbox_init(pmonitor); pid = fork(); if (pid == -1) { fatal("fork of unprivileged child failed"); } else if (pid != 0) { debug2("Network child is on pid %ld", (long)pid); pmonitor->m_pid = pid; if (have_agent) { r = ssh_get_authentication_socket(&auth_sock); if (r != 0) { error("Could not get agent socket: %s", ssh_err(r)); have_agent = 0; } } if (box != NULL) ssh_sandbox_parent_preauth(box, pid); monitor_child_preauth(authctxt, pmonitor); /* Wait for the child's exit status */ while (waitpid(pid, &status, 0) < 0) { if (errno == EINTR) continue; pmonitor->m_pid = -1; fatal("%s: waitpid: %s", __func__, strerror(errno)); } privsep_is_preauth = 0; pmonitor->m_pid = -1; if (WIFEXITED(status)) { if (WEXITSTATUS(status) != 0) fatal("%s: preauth child exited with status %d", __func__, WEXITSTATUS(status)); } else if (WIFSIGNALED(status)) fatal("%s: preauth child terminated by signal %d", __func__, WTERMSIG(status)); if (box != NULL) ssh_sandbox_parent_finish(box); return 1; } else { /* child */ close(pmonitor->m_sendfd); close(pmonitor->m_log_recvfd); /* Arrange for logging to be sent to the monitor */ set_log_handler(mm_log_handler, pmonitor); privsep_preauth_child(); setproctitle("%s", "[net]"); if (box != NULL) ssh_sandbox_child(box); return 0; } } static void privsep_postauth(Authctxt *authctxt) { #ifdef DISABLE_FD_PASSING if (1) { #else if (authctxt->pw->pw_uid == 0) { #endif /* File descriptor passing is broken or root login */ use_privsep = 0; goto skip; } /* New socket pair */ monitor_reinit(pmonitor); pmonitor->m_pid = fork(); if (pmonitor->m_pid == -1) fatal("fork of unprivileged child failed"); else if (pmonitor->m_pid != 0) { verbose("User child is on pid %ld", (long)pmonitor->m_pid); buffer_clear(&loginmsg); monitor_child_postauth(pmonitor); /* NEVERREACHED */ exit(0); } /* child */ close(pmonitor->m_sendfd); pmonitor->m_sendfd = -1; /* Demote the private keys to public keys. */ demote_sensitive_data(); reseed_prngs(); /* Drop privileges */ do_setusercontext(authctxt->pw); skip: /* It is safe now to apply the key state */ monitor_apply_keystate(pmonitor); /* * Tell the packet layer that authentication was successful, since * this information is not part of the key state. */ packet_set_authenticated(); } static char * list_hostkey_types(void) { Buffer b; const char *p; char *ret; int i; Key *key; buffer_init(&b); for (i = 0; i < options.num_host_key_files; i++) { key = sensitive_data.host_keys[i]; if (key == NULL) key = sensitive_data.host_pubkeys[i]; if (key == NULL) continue; /* Check that the key is accepted in HostkeyAlgorithms */ if (match_pattern_list(sshkey_ssh_name(key), options.hostkeyalgorithms, 0) != 1) { debug3("%s: %s key not permitted by HostkeyAlgorithms", __func__, sshkey_ssh_name(key)); continue; } switch (key->type) { case KEY_RSA: case KEY_DSA: case KEY_ECDSA: case KEY_ED25519: if (buffer_len(&b) > 0) buffer_append(&b, ",", 1); p = key_ssh_name(key); buffer_append(&b, p, strlen(p)); /* for RSA we also support SHA2 signatures */ if (key->type == KEY_RSA) { p = ",rsa-sha2-512,rsa-sha2-256"; buffer_append(&b, p, strlen(p)); } break; } /* If the private key has a cert peer, then list that too */ key = sensitive_data.host_certificates[i]; if (key == NULL) continue; switch (key->type) { case KEY_RSA_CERT: case KEY_DSA_CERT: case KEY_ECDSA_CERT: case KEY_ED25519_CERT: if (buffer_len(&b) > 0) buffer_append(&b, ",", 1); p = key_ssh_name(key); buffer_append(&b, p, strlen(p)); break; } } if ((ret = sshbuf_dup_string(&b)) == NULL) fatal("%s: sshbuf_dup_string failed", __func__); buffer_free(&b); debug("list_hostkey_types: %s", ret); return ret; } static Key * get_hostkey_by_type(int type, int nid, int need_private, struct ssh *ssh) { int i; Key *key; for (i = 0; i < options.num_host_key_files; i++) { switch (type) { case KEY_RSA_CERT: case KEY_DSA_CERT: case KEY_ECDSA_CERT: case KEY_ED25519_CERT: key = sensitive_data.host_certificates[i]; break; default: key = sensitive_data.host_keys[i]; if (key == NULL && !need_private) key = sensitive_data.host_pubkeys[i]; break; } if (key != NULL && key->type == type && (key->type != KEY_ECDSA || key->ecdsa_nid == nid)) return need_private ? sensitive_data.host_keys[i] : key; } return NULL; } Key * get_hostkey_public_by_type(int type, int nid, struct ssh *ssh) { return get_hostkey_by_type(type, nid, 0, ssh); } Key * get_hostkey_private_by_type(int type, int nid, struct ssh *ssh) { return get_hostkey_by_type(type, nid, 1, ssh); } Key * get_hostkey_by_index(int ind) { if (ind < 0 || ind >= options.num_host_key_files) return (NULL); return (sensitive_data.host_keys[ind]); } Key * get_hostkey_public_by_index(int ind, struct ssh *ssh) { if (ind < 0 || ind >= options.num_host_key_files) return (NULL); return (sensitive_data.host_pubkeys[ind]); } int get_hostkey_index(Key *key, int compare, struct ssh *ssh) { int i; for (i = 0; i < options.num_host_key_files; i++) { if (key_is_cert(key)) { if (key == sensitive_data.host_certificates[i] || (compare && sensitive_data.host_certificates[i] && sshkey_equal(key, sensitive_data.host_certificates[i]))) return (i); } else { if (key == sensitive_data.host_keys[i] || (compare && sensitive_data.host_keys[i] && sshkey_equal(key, sensitive_data.host_keys[i]))) return (i); if (key == sensitive_data.host_pubkeys[i] || (compare && sensitive_data.host_pubkeys[i] && sshkey_equal(key, sensitive_data.host_pubkeys[i]))) return (i); } } return (-1); } /* Inform the client of all hostkeys */ static void notify_hostkeys(struct ssh *ssh) { struct sshbuf *buf; struct sshkey *key; int i, nkeys, r; char *fp; /* Some clients cannot cope with the hostkeys message, skip those. */ if (datafellows & SSH_BUG_HOSTKEYS) return; if ((buf = sshbuf_new()) == NULL) fatal("%s: sshbuf_new", __func__); for (i = nkeys = 0; i < options.num_host_key_files; i++) { key = get_hostkey_public_by_index(i, ssh); if (key == NULL || key->type == KEY_UNSPEC || sshkey_is_cert(key)) continue; fp = sshkey_fingerprint(key, options.fingerprint_hash, SSH_FP_DEFAULT); debug3("%s: key %d: %s %s", __func__, i, sshkey_ssh_name(key), fp); free(fp); if (nkeys == 0) { packet_start(SSH2_MSG_GLOBAL_REQUEST); packet_put_cstring("hostkeys-00@openssh.com"); packet_put_char(0); /* want-reply */ } sshbuf_reset(buf); if ((r = sshkey_putb(key, buf)) != 0) fatal("%s: couldn't put hostkey %d: %s", __func__, i, ssh_err(r)); packet_put_string(sshbuf_ptr(buf), sshbuf_len(buf)); nkeys++; } debug3("%s: sent %d hostkeys", __func__, nkeys); if (nkeys == 0) fatal("%s: no hostkeys", __func__); packet_send(); sshbuf_free(buf); } /* * returns 1 if connection should be dropped, 0 otherwise. * dropping starts at connection #max_startups_begin with a probability * of (max_startups_rate/100). the probability increases linearly until * all connections are dropped for startups > max_startups */ static int drop_connection(int startups) { int p, r; if (startups < options.max_startups_begin) return 0; if (startups >= options.max_startups) return 1; if (options.max_startups_rate == 100) return 1; p = 100 - options.max_startups_rate; p *= startups - options.max_startups_begin; p /= options.max_startups - options.max_startups_begin; p += options.max_startups_rate; r = arc4random_uniform(100); debug("drop_connection: p %d, r %d", p, r); return (r < p) ? 1 : 0; } static void usage(void) { if (options.version_addendum && *options.version_addendum != '\0') fprintf(stderr, "%s %s, %s\n", SSH_RELEASE, options.version_addendum, OPENSSL_VERSION); else fprintf(stderr, "%s, %s\n", SSH_RELEASE, OPENSSL_VERSION); fprintf(stderr, "usage: sshd [-46DdeiqTt] [-C connection_spec] [-c host_cert_file]\n" " [-E log_file] [-f config_file] [-g login_grace_time]\n" " [-h host_key_file] [-o option] [-p port] [-u len]\n" ); exit(1); } static void send_rexec_state(int fd, struct sshbuf *conf) { struct sshbuf *m; int r; debug3("%s: entering fd = %d config len %zu", __func__, fd, sshbuf_len(conf)); /* * Protocol from reexec master to child: * string configuration * string rngseed (only if OpenSSL is not self-seeded) */ if ((m = sshbuf_new()) == NULL) fatal("%s: sshbuf_new failed", __func__); if ((r = sshbuf_put_stringb(m, conf)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); #if defined(WITH_OPENSSL) && !defined(OPENSSL_PRNG_ONLY) rexec_send_rng_seed(m); #endif if (ssh_msg_send(fd, 0, m) == -1) fatal("%s: ssh_msg_send failed", __func__); sshbuf_free(m); debug3("%s: done", __func__); } static void recv_rexec_state(int fd, Buffer *conf) { Buffer m; char *cp; u_int len; debug3("%s: entering fd = %d", __func__, fd); buffer_init(&m); if (ssh_msg_recv(fd, &m) == -1) fatal("%s: ssh_msg_recv failed", __func__); if (buffer_get_char(&m) != 0) fatal("%s: rexec version mismatch", __func__); cp = buffer_get_string(&m, &len); if (conf != NULL) buffer_append(conf, cp, len); free(cp); #if defined(WITH_OPENSSL) && !defined(OPENSSL_PRNG_ONLY) rexec_recv_rng_seed(&m); #endif buffer_free(&m); debug3("%s: done", __func__); } /* Accept a connection from inetd */ static void server_accept_inetd(int *sock_in, int *sock_out) { int fd; startup_pipe = -1; if (rexeced_flag) { close(REEXEC_CONFIG_PASS_FD); *sock_in = *sock_out = dup(STDIN_FILENO); if (!debug_flag) { startup_pipe = dup(REEXEC_STARTUP_PIPE_FD); close(REEXEC_STARTUP_PIPE_FD); } } else { *sock_in = dup(STDIN_FILENO); *sock_out = dup(STDOUT_FILENO); } /* * We intentionally do not close the descriptors 0, 1, and 2 * as our code for setting the descriptors won't work if * ttyfd happens to be one of those. */ if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); if (!log_stderr) dup2(fd, STDERR_FILENO); if (fd > (log_stderr ? STDERR_FILENO : STDOUT_FILENO)) close(fd); } debug("inetd sockets after dupping: %d, %d", *sock_in, *sock_out); } /* * Listen for TCP connections */ static void server_listen(void) { int ret, listen_sock, on = 1; struct addrinfo *ai; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; int socksize; socklen_t len; for (ai = options.listen_addrs; ai; ai = ai->ai_next) { if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6) continue; if (num_listen_socks >= MAX_LISTEN_SOCKS) fatal("Too many listen sockets. " "Enlarge MAX_LISTEN_SOCKS"); if ((ret = getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop), strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV)) != 0) { error("getnameinfo failed: %.100s", ssh_gai_strerror(ret)); continue; } /* Create socket for listening. */ listen_sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if (listen_sock < 0) { /* kernel may not support ipv6 */ verbose("socket: %.100s", strerror(errno)); continue; } if (set_nonblock(listen_sock) == -1) { close(listen_sock); continue; } + if (fcntl(listen_sock, F_SETFD, FD_CLOEXEC) == -1) { + verbose("socket: CLOEXEC: %s", strerror(errno)); + close(listen_sock); + continue; + } /* * Set socket options. * Allow local port reuse in TIME_WAIT. */ if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) error("setsockopt SO_REUSEADDR: %s", strerror(errno)); /* Only communicate in IPv6 over AF_INET6 sockets. */ if (ai->ai_family == AF_INET6) sock_set_v6only(listen_sock); debug("Bind to port %s on %s.", strport, ntop); len = sizeof(socksize); getsockopt(listen_sock, SOL_SOCKET, SO_RCVBUF, &socksize, &len); debug("Server TCP RWIN socket size: %d", socksize); /* Bind the socket to the desired port. */ if (bind(listen_sock, ai->ai_addr, ai->ai_addrlen) < 0) { error("Bind to port %s on %s failed: %.200s.", strport, ntop, strerror(errno)); close(listen_sock); continue; } listen_socks[num_listen_socks] = listen_sock; num_listen_socks++; /* Start listening on the port. */ if (listen(listen_sock, SSH_LISTEN_BACKLOG) < 0) fatal("listen on [%s]:%s: %.100s", ntop, strport, strerror(errno)); logit("Server listening on %s port %s.", ntop, strport); } freeaddrinfo(options.listen_addrs); if (!num_listen_socks) fatal("Cannot bind any address."); } /* * The main TCP accept loop. Note that, for the non-debug case, returns * from this function are in a forked subprocess. */ static void server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) { fd_set *fdset; int i, j, ret, maxfd; int startups = 0; int startup_p[2] = { -1 , -1 }; struct sockaddr_storage from; socklen_t fromlen; pid_t pid; u_char rnd[256]; /* setup fd set for accept */ fdset = NULL; maxfd = 0; for (i = 0; i < num_listen_socks; i++) if (listen_socks[i] > maxfd) maxfd = listen_socks[i]; /* pipes connected to unauthenticated childs */ startup_pipes = xcalloc(options.max_startups, sizeof(int)); for (i = 0; i < options.max_startups; i++) startup_pipes[i] = -1; /* * Stay listening for connections until the system crashes or * the daemon is killed with a signal. */ for (;;) { if (received_sighup) sighup_restart(); free(fdset); fdset = xcalloc(howmany(maxfd + 1, NFDBITS), sizeof(fd_mask)); for (i = 0; i < num_listen_socks; i++) FD_SET(listen_socks[i], fdset); for (i = 0; i < options.max_startups; i++) if (startup_pipes[i] != -1) FD_SET(startup_pipes[i], fdset); /* Wait in select until there is a connection. */ ret = select(maxfd+1, fdset, NULL, NULL, NULL); if (ret < 0 && errno != EINTR) error("select: %.100s", strerror(errno)); if (received_sigterm) { logit("Received signal %d; terminating.", (int) received_sigterm); close_listen_socks(); if (options.pid_file != NULL) unlink(options.pid_file); exit(received_sigterm == SIGTERM ? 0 : 255); } if (ret < 0) continue; for (i = 0; i < options.max_startups; i++) if (startup_pipes[i] != -1 && FD_ISSET(startup_pipes[i], fdset)) { /* * the read end of the pipe is ready * if the child has closed the pipe * after successful authentication * or if the child has died */ close(startup_pipes[i]); startup_pipes[i] = -1; startups--; } for (i = 0; i < num_listen_socks; i++) { if (!FD_ISSET(listen_socks[i], fdset)) continue; fromlen = sizeof(from); *newsock = accept(listen_socks[i], (struct sockaddr *)&from, &fromlen); if (*newsock < 0) { if (errno != EINTR && errno != EWOULDBLOCK && errno != ECONNABORTED && errno != EAGAIN) error("accept: %.100s", strerror(errno)); if (errno == EMFILE || errno == ENFILE) usleep(100 * 1000); continue; } if (unset_nonblock(*newsock) == -1) { close(*newsock); continue; } if (drop_connection(startups) == 1) { char *laddr = get_local_ipaddr(*newsock); char *raddr = get_peer_ipaddr(*newsock); verbose("drop connection #%d from [%s]:%d " "on [%s]:%d past MaxStartups", startups, raddr, get_peer_port(*newsock), laddr, get_local_port(*newsock)); free(laddr); free(raddr); close(*newsock); continue; } if (pipe(startup_p) == -1) { close(*newsock); continue; } if (rexec_flag && socketpair(AF_UNIX, SOCK_STREAM, 0, config_s) == -1) { error("reexec socketpair: %s", strerror(errno)); close(*newsock); close(startup_p[0]); close(startup_p[1]); continue; } for (j = 0; j < options.max_startups; j++) if (startup_pipes[j] == -1) { startup_pipes[j] = startup_p[0]; if (maxfd < startup_p[0]) maxfd = startup_p[0]; startups++; break; } /* * Got connection. Fork a child to handle it, unless * we are in debugging mode. */ if (debug_flag) { /* * In debugging mode. Close the listening * socket, and start processing the * connection without forking. */ debug("Server will not fork when running in debugging mode."); close_listen_socks(); *sock_in = *newsock; *sock_out = *newsock; close(startup_p[0]); close(startup_p[1]); startup_pipe = -1; pid = getpid(); if (rexec_flag) { send_rexec_state(config_s[0], &cfg); close(config_s[0]); } break; } /* * Normal production daemon. Fork, and have * the child process the connection. The * parent continues listening. */ platform_pre_fork(); if ((pid = fork()) == 0) { /* * Child. Close the listening and * max_startup sockets. Start using * the accepted socket. Reinitialize * logging (since our pid has changed). * We break out of the loop to handle * the connection. */ platform_post_fork_child(); startup_pipe = startup_p[1]; close_startup_pipes(); close_listen_socks(); *sock_in = *newsock; *sock_out = *newsock; log_init(__progname, options.log_level, options.log_facility, log_stderr); if (rexec_flag) close(config_s[0]); break; } /* Parent. Stay in the loop. */ platform_post_fork_parent(pid); if (pid < 0) error("fork: %.100s", strerror(errno)); else debug("Forked child %ld.", (long)pid); close(startup_p[1]); if (rexec_flag) { send_rexec_state(config_s[0], &cfg); close(config_s[0]); close(config_s[1]); } close(*newsock); /* * Ensure that our random state differs * from that of the child */ arc4random_stir(); arc4random_buf(rnd, sizeof(rnd)); #ifdef WITH_OPENSSL RAND_seed(rnd, sizeof(rnd)); if ((RAND_bytes((u_char *)rnd, 1)) != 1) fatal("%s: RAND_bytes failed", __func__); #endif explicit_bzero(rnd, sizeof(rnd)); } /* child process check (or debug mode) */ if (num_listen_socks < 0) break; } } /* * If IP options are supported, make sure there are none (log and * return an error if any are found). Basically we are worried about * source routing; it can be used to pretend you are somebody * (ip-address) you are not. That itself may be "almost acceptable" * under certain circumstances, but rhosts autentication is useless * if source routing is accepted. Notice also that if we just dropped * source routing here, the other side could use IP spoofing to do * rest of the interaction and could still bypass security. So we * exit here if we detect any IP options. */ static void check_ip_options(struct ssh *ssh) { #ifdef IP_OPTIONS int sock_in = ssh_packet_get_connection_in(ssh); struct sockaddr_storage from; u_char opts[200]; socklen_t i, option_size = sizeof(opts), fromlen = sizeof(from); char text[sizeof(opts) * 3 + 1]; memset(&from, 0, sizeof(from)); if (getpeername(sock_in, (struct sockaddr *)&from, &fromlen) < 0) return; if (from.ss_family != AF_INET) return; /* XXX IPv6 options? */ if (getsockopt(sock_in, IPPROTO_IP, IP_OPTIONS, opts, &option_size) >= 0 && option_size != 0) { text[0] = '\0'; for (i = 0; i < option_size; i++) snprintf(text + i*3, sizeof(text) - i*3, " %2.2x", opts[i]); fatal("Connection from %.100s port %d with IP opts: %.800s", ssh_remote_ipaddr(ssh), ssh_remote_port(ssh), text); } return; #endif /* IP_OPTIONS */ } /* * Main program for the daemon. */ int main(int ac, char **av) { struct ssh *ssh = NULL; extern char *optarg; extern int optind; int r, opt, i, j, on = 1, already_daemon; int sock_in = -1, sock_out = -1, newsock = -1; const char *remote_ip; int remote_port; char *fp, *line, *laddr, *logfile = NULL; int config_s[2] = { -1 , -1 }; u_int n; u_int64_t ibytes, obytes; mode_t new_umask; Key *key; Key *pubkey; int keytype; Authctxt *authctxt; struct connection_info *connection_info = get_connection_info(0, 0); ssh_malloc_init(); /* must be called before any mallocs */ #ifdef HAVE_SECUREWARE (void)set_auth_parameters(ac, av); #endif __progname = ssh_get_progname(av[0]); /* Save argv. Duplicate so setproctitle emulation doesn't clobber it */ saved_argc = ac; rexec_argc = ac; saved_argv = xcalloc(ac + 1, sizeof(*saved_argv)); for (i = 0; i < ac; i++) saved_argv[i] = xstrdup(av[i]); saved_argv[i] = NULL; #ifndef HAVE_SETPROCTITLE /* Prepare for later setproctitle emulation */ compat_init_setproctitle(ac, av); av = saved_argv; #endif if (geteuid() == 0 && setgroups(0, NULL) == -1) debug("setgroups(): %.200s", strerror(errno)); /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); /* Initialize configuration options to their default values. */ initialize_server_options(&options); /* Parse command-line arguments. */ while ((opt = getopt(ac, av, "C:E:b:c:f:g:h:k:o:p:u:46DQRTdeiqrt")) != -1) { switch (opt) { case '4': options.address_family = AF_INET; break; case '6': options.address_family = AF_INET6; break; case 'f': config_file_name = optarg; break; case 'c': if (options.num_host_cert_files >= MAX_HOSTCERTS) { fprintf(stderr, "too many host certificates.\n"); exit(1); } options.host_cert_files[options.num_host_cert_files++] = derelativise_path(optarg); break; case 'd': if (debug_flag == 0) { debug_flag = 1; options.log_level = SYSLOG_LEVEL_DEBUG1; } else if (options.log_level < SYSLOG_LEVEL_DEBUG3) options.log_level++; break; case 'D': no_daemon_flag = 1; break; case 'E': logfile = optarg; /* FALLTHROUGH */ case 'e': log_stderr = 1; break; case 'i': inetd_flag = 1; break; case 'r': rexec_flag = 0; break; case 'R': rexeced_flag = 1; inetd_flag = 1; break; case 'Q': /* ignored */ break; case 'q': options.log_level = SYSLOG_LEVEL_QUIET; break; case 'b': /* protocol 1, ignored */ break; case 'p': options.ports_from_cmdline = 1; if (options.num_ports >= MAX_PORTS) { fprintf(stderr, "too many ports.\n"); exit(1); } options.ports[options.num_ports++] = a2port(optarg); if (options.ports[options.num_ports-1] <= 0) { fprintf(stderr, "Bad port number.\n"); exit(1); } break; case 'g': if ((options.login_grace_time = convtime(optarg)) == -1) { fprintf(stderr, "Invalid login grace time.\n"); exit(1); } break; case 'k': /* protocol 1, ignored */ break; case 'h': if (options.num_host_key_files >= MAX_HOSTKEYS) { fprintf(stderr, "too many host keys.\n"); exit(1); } options.host_key_files[options.num_host_key_files++] = derelativise_path(optarg); break; case 't': test_flag = 1; break; case 'T': test_flag = 2; break; case 'C': if (parse_server_match_testspec(connection_info, optarg) == -1) exit(1); break; case 'u': utmp_len = (u_int)strtonum(optarg, 0, HOST_NAME_MAX+1+1, NULL); if (utmp_len > HOST_NAME_MAX+1) { fprintf(stderr, "Invalid utmp length.\n"); exit(1); } break; case 'o': line = xstrdup(optarg); if (process_server_config_line(&options, line, "command-line", 0, NULL, NULL) != 0) exit(1); free(line); break; case '?': default: usage(); break; } } if (rexeced_flag || inetd_flag) rexec_flag = 0; if (!test_flag && (rexec_flag && (av[0] == NULL || *av[0] != '/'))) fatal("sshd re-exec requires execution with an absolute path"); if (rexeced_flag) closefrom(REEXEC_MIN_FREE_FD); else closefrom(REEXEC_DEVCRYPTO_RESERVED_FD); #ifdef WITH_OPENSSL OpenSSL_add_all_algorithms(); #endif /* If requested, redirect the logs to the specified logfile. */ if (logfile != NULL) log_redirect_stderr_to(logfile); /* * Force logging to stderr until we have loaded the private host * key (unless started from inetd) */ log_init(__progname, options.log_level == SYSLOG_LEVEL_NOT_SET ? SYSLOG_LEVEL_INFO : options.log_level, options.log_facility == SYSLOG_FACILITY_NOT_SET ? SYSLOG_FACILITY_AUTH : options.log_facility, log_stderr || !inetd_flag); /* * Unset KRB5CCNAME, otherwise the user's session may inherit it from * root's environment */ if (getenv("KRB5CCNAME") != NULL) (void) unsetenv("KRB5CCNAME"); #ifdef _UNICOS /* Cray can define user privs drop all privs now! * Not needed on PRIV_SU systems! */ drop_cray_privs(); #endif sensitive_data.have_ssh2_key = 0; /* * If we're doing an extended config test, make sure we have all of * the parameters we need. If we're not doing an extended test, * do not silently ignore connection test params. */ if (test_flag >= 2 && server_match_spec_complete(connection_info) == 0) fatal("user, host and addr are all required when testing " "Match configs"); if (test_flag < 2 && server_match_spec_complete(connection_info) >= 0) fatal("Config test connection parameter (-C) provided without " "test mode (-T)"); /* Fetch our configuration */ buffer_init(&cfg); if (rexeced_flag) recv_rexec_state(REEXEC_CONFIG_PASS_FD, &cfg); else if (strcasecmp(config_file_name, "none") != 0) load_server_config(config_file_name, &cfg); parse_server_config(&options, rexeced_flag ? "rexec" : config_file_name, &cfg, NULL); seed_rng(); /* Fill in default values for those options not explicitly set. */ fill_default_server_options(&options); /* challenge-response is implemented via keyboard interactive */ if (options.challenge_response_authentication) options.kbd_interactive_authentication = 1; /* Check that options are sensible */ if (options.authorized_keys_command_user == NULL && (options.authorized_keys_command != NULL && strcasecmp(options.authorized_keys_command, "none") != 0)) fatal("AuthorizedKeysCommand set without " "AuthorizedKeysCommandUser"); if (options.authorized_principals_command_user == NULL && (options.authorized_principals_command != NULL && strcasecmp(options.authorized_principals_command, "none") != 0)) fatal("AuthorizedPrincipalsCommand set without " "AuthorizedPrincipalsCommandUser"); /* * Check whether there is any path through configured auth methods. * Unfortunately it is not possible to verify this generally before * daemonisation in the presence of Match block, but this catches * and warns for trivial misconfigurations that could break login. */ if (options.num_auth_methods != 0) { for (n = 0; n < options.num_auth_methods; n++) { if (auth2_methods_valid(options.auth_methods[n], 1) == 0) break; } if (n >= options.num_auth_methods) fatal("AuthenticationMethods cannot be satisfied by " "enabled authentication methods"); } /* set default channel AF */ channel_set_af(options.address_family); /* Check that there are no remaining arguments. */ if (optind < ac) { fprintf(stderr, "Extra argument %s.\n", av[optind]); exit(1); } debug("sshd version %s, %s", SSH_VERSION, #ifdef WITH_OPENSSL SSLeay_version(SSLEAY_VERSION) #else "without OpenSSL" #endif ); /* Store privilege separation user for later use if required. */ if ((privsep_pw = getpwnam(SSH_PRIVSEP_USER)) == NULL) { if (use_privsep || options.kerberos_authentication) fatal("Privilege separation user %s does not exist", SSH_PRIVSEP_USER); } else { explicit_bzero(privsep_pw->pw_passwd, strlen(privsep_pw->pw_passwd)); privsep_pw = pwcopy(privsep_pw); free(privsep_pw->pw_passwd); privsep_pw->pw_passwd = xstrdup("*"); } endpwent(); /* load host keys */ sensitive_data.host_keys = xcalloc(options.num_host_key_files, sizeof(Key *)); sensitive_data.host_pubkeys = xcalloc(options.num_host_key_files, sizeof(Key *)); if (options.host_key_agent) { if (strcmp(options.host_key_agent, SSH_AUTHSOCKET_ENV_NAME)) setenv(SSH_AUTHSOCKET_ENV_NAME, options.host_key_agent, 1); if ((r = ssh_get_authentication_socket(NULL)) == 0) have_agent = 1; else error("Could not connect to agent \"%s\": %s", options.host_key_agent, ssh_err(r)); } for (i = 0; i < options.num_host_key_files; i++) { if (options.host_key_files[i] == NULL) continue; key = key_load_private(options.host_key_files[i], "", NULL); pubkey = key_load_public(options.host_key_files[i], NULL); + + if ((pubkey != NULL && pubkey->type == KEY_RSA1) || + (key != NULL && key->type == KEY_RSA1)) { + verbose("Ignoring RSA1 key %s", + options.host_key_files[i]); + key_free(key); + key_free(pubkey); + continue; + } if (pubkey == NULL && key != NULL) pubkey = key_demote(key); sensitive_data.host_keys[i] = key; sensitive_data.host_pubkeys[i] = pubkey; if (key == NULL && pubkey != NULL && have_agent) { debug("will rely on agent for hostkey %s", options.host_key_files[i]); keytype = pubkey->type; } else if (key != NULL) { keytype = key->type; } else { error("Could not load host key: %s", options.host_key_files[i]); sensitive_data.host_keys[i] = NULL; sensitive_data.host_pubkeys[i] = NULL; continue; } switch (keytype) { case KEY_RSA: case KEY_DSA: case KEY_ECDSA: case KEY_ED25519: if (have_agent || key != NULL) sensitive_data.have_ssh2_key = 1; break; } if ((fp = sshkey_fingerprint(pubkey, options.fingerprint_hash, SSH_FP_DEFAULT)) == NULL) fatal("sshkey_fingerprint failed"); debug("%s host key #%d: %s %s", key ? "private" : "agent", i, sshkey_ssh_name(pubkey), fp); free(fp); } if (!sensitive_data.have_ssh2_key) { logit("sshd: no hostkeys available -- exiting."); exit(1); } /* * Load certificates. They are stored in an array at identical * indices to the public keys that they relate to. */ sensitive_data.host_certificates = xcalloc(options.num_host_key_files, sizeof(Key *)); for (i = 0; i < options.num_host_key_files; i++) sensitive_data.host_certificates[i] = NULL; for (i = 0; i < options.num_host_cert_files; i++) { if (options.host_cert_files[i] == NULL) continue; key = key_load_public(options.host_cert_files[i], NULL); if (key == NULL) { error("Could not load host certificate: %s", options.host_cert_files[i]); continue; } if (!key_is_cert(key)) { error("Certificate file is not a certificate: %s", options.host_cert_files[i]); key_free(key); continue; } /* Find matching private key */ for (j = 0; j < options.num_host_key_files; j++) { if (key_equal_public(key, sensitive_data.host_keys[j])) { sensitive_data.host_certificates[j] = key; break; } } if (j >= options.num_host_key_files) { error("No matching private key for certificate: %s", options.host_cert_files[i]); key_free(key); continue; } sensitive_data.host_certificates[j] = key; debug("host certificate: #%d type %d %s", j, key->type, key_type(key)); } if (use_privsep) { struct stat st; if ((stat(_PATH_PRIVSEP_CHROOT_DIR, &st) == -1) || (S_ISDIR(st.st_mode) == 0)) fatal("Missing privilege separation directory: %s", _PATH_PRIVSEP_CHROOT_DIR); #ifdef HAVE_CYGWIN if (check_ntsec(_PATH_PRIVSEP_CHROOT_DIR) && (st.st_uid != getuid () || (st.st_mode & (S_IWGRP|S_IWOTH)) != 0)) #else if (st.st_uid != 0 || (st.st_mode & (S_IWGRP|S_IWOTH)) != 0) #endif fatal("%s must be owned by root and not group or " "world-writable.", _PATH_PRIVSEP_CHROOT_DIR); } if (test_flag > 1) { if (server_match_spec_complete(connection_info) == 1) parse_server_match_config(&options, connection_info); dump_config(&options); } /* Configuration looks good, so exit if in test mode. */ if (test_flag) exit(0); /* * Clear out any supplemental groups we may have inherited. This * prevents inadvertent creation of files with bad modes (in the * portable version at least, it's certainly possible for PAM * to create a file, and we can't control the code in every * module which might be used). */ if (setgroups(0, NULL) < 0) debug("setgroups() failed: %.200s", strerror(errno)); if (rexec_flag) { rexec_argv = xcalloc(rexec_argc + 2, sizeof(char *)); for (i = 0; i < rexec_argc; i++) { debug("rexec_argv[%d]='%s'", i, saved_argv[i]); rexec_argv[i] = saved_argv[i]; } rexec_argv[rexec_argc] = "-R"; rexec_argv[rexec_argc + 1] = NULL; } /* Ensure that umask disallows at least group and world write */ new_umask = umask(0077) | 0022; (void) umask(new_umask); /* Initialize the log (it is reinitialized below in case we forked). */ if (debug_flag && (!inetd_flag || rexeced_flag)) log_stderr = 1; log_init(__progname, options.log_level, options.log_facility, log_stderr); /* * If not in debugging mode, not started from inetd and not already * daemonized (eg re-exec via SIGHUP), disconnect from the controlling * terminal, and fork. The original process exits. */ already_daemon = daemonized(); if (!(debug_flag || inetd_flag || no_daemon_flag || already_daemon)) { if (daemon(0, 0) < 0) fatal("daemon() failed: %.200s", strerror(errno)); disconnect_controlling_tty(); } /* Reinitialize the log (because of the fork above). */ log_init(__progname, options.log_level, options.log_facility, log_stderr); /* Avoid killing the process in high-pressure swapping environments. */ if (!inetd_flag && madvise(NULL, 0, MADV_PROTECT) != 0) debug("madvise(): %.200s", strerror(errno)); /* Chdir to the root directory so that the current disk can be unmounted if desired. */ if (chdir("/") == -1) error("chdir(\"/\"): %s", strerror(errno)); /* ignore SIGPIPE */ signal(SIGPIPE, SIG_IGN); /* Get a connection, either from inetd or a listening TCP socket */ if (inetd_flag) { server_accept_inetd(&sock_in, &sock_out); } else { platform_pre_listen(); server_listen(); signal(SIGHUP, sighup_handler); signal(SIGCHLD, main_sigchld_handler); signal(SIGTERM, sigterm_handler); signal(SIGQUIT, sigterm_handler); /* * Write out the pid file after the sigterm handler * is setup and the listen sockets are bound */ if (options.pid_file != NULL && !debug_flag) { FILE *f = fopen(options.pid_file, "w"); if (f == NULL) { error("Couldn't create pid file \"%s\": %s", options.pid_file, strerror(errno)); } else { fprintf(f, "%ld\n", (long) getpid()); fclose(f); } } /* Accept a connection and return in a forked child */ server_accept_loop(&sock_in, &sock_out, &newsock, config_s); } /* This is the child processing a new connection. */ setproctitle("%s", "[accepted]"); /* * Create a new session and process group since the 4.4BSD * setlogin() affects the entire process group. We don't * want the child to be able to affect the parent. */ #if !defined(SSHD_ACQUIRES_CTTY) /* * If setsid is called, on some platforms sshd will later acquire a * controlling terminal which will result in "could not set * controlling tty" errors. */ if (!debug_flag && !inetd_flag && setsid() < 0) error("setsid: %.100s", strerror(errno)); #endif if (rexec_flag) { int fd; debug("rexec start in %d out %d newsock %d pipe %d sock %d", sock_in, sock_out, newsock, startup_pipe, config_s[0]); dup2(newsock, STDIN_FILENO); dup2(STDIN_FILENO, STDOUT_FILENO); if (startup_pipe == -1) close(REEXEC_STARTUP_PIPE_FD); else if (startup_pipe != REEXEC_STARTUP_PIPE_FD) { dup2(startup_pipe, REEXEC_STARTUP_PIPE_FD); close(startup_pipe); startup_pipe = REEXEC_STARTUP_PIPE_FD; } dup2(config_s[1], REEXEC_CONFIG_PASS_FD); close(config_s[1]); execv(rexec_argv[0], rexec_argv); /* Reexec has failed, fall back and continue */ error("rexec of %s failed: %s", rexec_argv[0], strerror(errno)); recv_rexec_state(REEXEC_CONFIG_PASS_FD, NULL); log_init(__progname, options.log_level, options.log_facility, log_stderr); /* Clean up fds */ close(REEXEC_CONFIG_PASS_FD); newsock = sock_out = sock_in = dup(STDIN_FILENO); if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) { dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); if (fd > STDERR_FILENO) close(fd); } debug("rexec cleanup in %d out %d newsock %d pipe %d sock %d", sock_in, sock_out, newsock, startup_pipe, config_s[0]); } /* Executed child processes don't need these. */ fcntl(sock_out, F_SETFD, FD_CLOEXEC); fcntl(sock_in, F_SETFD, FD_CLOEXEC); /* * Disable the key regeneration alarm. We will not regenerate the * key since we are no longer in a position to give it to anyone. We * will not restart on SIGHUP since it no longer makes sense. */ alarm(0); signal(SIGALRM, SIG_DFL); signal(SIGHUP, SIG_DFL); signal(SIGTERM, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGCHLD, SIG_DFL); signal(SIGINT, SIG_DFL); #ifdef __FreeBSD__ /* * Initialize the resolver. This may not happen automatically * before privsep chroot(). */ if ((_res.options & RES_INIT) == 0) { debug("res_init()"); res_init(); } #ifdef GSSAPI /* * Force GSS-API to parse its configuration and load any * mechanism plugins. */ { gss_OID_set mechs; OM_uint32 minor_status; gss_indicate_mechs(&minor_status, &mechs); gss_release_oid_set(&minor_status, &mechs); } #endif #endif /* * Register our connection. This turns encryption off because we do * not have a key. */ packet_set_connection(sock_in, sock_out); packet_set_server(); ssh = active_state; /* XXX */ check_ip_options(ssh); /* Set SO_KEEPALIVE if requested. */ if (options.tcp_keep_alive && packet_connection_is_on_socket() && setsockopt(sock_in, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)) < 0) error("setsockopt SO_KEEPALIVE: %.100s", strerror(errno)); if ((remote_port = ssh_remote_port(ssh)) < 0) { debug("ssh_remote_port failed"); cleanup_exit(255); } /* * The rest of the code depends on the fact that * ssh_remote_ipaddr() caches the remote ip, even if * the socket goes away. */ remote_ip = ssh_remote_ipaddr(ssh); #ifdef SSH_AUDIT_EVENTS audit_connection_from(remote_ip, remote_port); #endif #ifdef LIBWRAP allow_severity = options.log_facility|LOG_INFO; deny_severity = options.log_facility|LOG_WARNING; /* Check whether logins are denied from this host. */ if (packet_connection_is_on_socket()) { struct request_info req; request_init(&req, RQ_DAEMON, __progname, RQ_FILE, sock_in, 0); fromhost(&req); if (!hosts_access(&req)) { debug("Connection refused by tcp wrapper"); refuse(&req); /* NOTREACHED */ fatal("libwrap refuse returns"); } } #endif /* LIBWRAP */ /* Log the connection. */ laddr = get_local_ipaddr(sock_in); verbose("Connection from %s port %d on %s port %d", remote_ip, remote_port, laddr, ssh_local_port(ssh)); free(laddr); /* * We don't want to listen forever unless the other side * successfully authenticates itself. So we set up an alarm which is * cleared after successful authentication. A limit of zero * indicates no limit. Note that we don't set the alarm in debugging * mode; it is just annoying to have the server exit just when you * are about to discover the bug. */ signal(SIGALRM, grace_alarm_handler); if (!debug_flag) alarm(options.login_grace_time); sshd_exchange_identification(ssh, sock_in, sock_out); packet_set_nonblocking(); /* allocate authentication context */ authctxt = xcalloc(1, sizeof(*authctxt)); authctxt->loginmsg = &loginmsg; /* XXX global for cleanup, access from other modules */ the_authctxt = authctxt; /* prepare buffer to collect messages to display to user after login */ buffer_init(&loginmsg); auth_debug_reset(); BLACKLIST_INIT(); if (use_privsep) { if (privsep_preauth(authctxt) == 1) goto authenticated; } else if (have_agent) { if ((r = ssh_get_authentication_socket(&auth_sock)) != 0) { error("Unable to get agent socket: %s", ssh_err(r)); have_agent = 0; } } /* perform the key exchange */ /* authenticate user and start session */ do_ssh2_kex(); do_authentication2(authctxt); /* * If we use privilege separation, the unprivileged child transfers * the current keystate and exits */ if (use_privsep) { mm_send_keystate(pmonitor); exit(0); } authenticated: /* * Cancel the alarm we set to limit the time taken for * authentication. */ alarm(0); signal(SIGALRM, SIG_DFL); authctxt->authenticated = 1; if (startup_pipe != -1) { close(startup_pipe); startup_pipe = -1; } #ifdef SSH_AUDIT_EVENTS audit_event(SSH_AUTH_SUCCESS); #endif #ifdef GSSAPI if (options.gss_authentication) { temporarily_use_uid(authctxt->pw); ssh_gssapi_storecreds(); restore_uid(); } #endif #ifdef USE_PAM if (options.use_pam) { do_pam_setcred(1); do_pam_session(); } #endif /* * In privilege separation, we fork another child and prepare * file descriptor passing. */ if (use_privsep) { privsep_postauth(authctxt); /* the monitor process [priv] will not return */ } packet_set_timeout(options.client_alive_interval, options.client_alive_count_max); /* Try to send all our hostkeys to the client */ notify_hostkeys(active_state); /* Start session. */ do_authenticated(authctxt); /* The connection has been terminated. */ packet_get_bytes(&ibytes, &obytes); verbose("Transferred: sent %llu, received %llu bytes", (unsigned long long)obytes, (unsigned long long)ibytes); verbose("Closing connection to %.500s port %d", remote_ip, remote_port); #ifdef USE_PAM if (options.use_pam) finish_pam(); #endif /* USE_PAM */ #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_CONNECTION_CLOSE)); #endif packet_close(); if (use_privsep) mm_terminate(); exit(0); } int sshd_hostkey_sign(Key *privkey, Key *pubkey, u_char **signature, size_t *slen, const u_char *data, size_t dlen, const char *alg, u_int flag) { int r; u_int xxx_slen, xxx_dlen = dlen; if (privkey) { if (PRIVSEP(key_sign(privkey, signature, &xxx_slen, data, xxx_dlen, alg) < 0)) fatal("%s: key_sign failed", __func__); if (slen) *slen = xxx_slen; } else if (use_privsep) { if (mm_key_sign(pubkey, signature, &xxx_slen, data, xxx_dlen, alg) < 0) fatal("%s: pubkey_sign failed", __func__); if (slen) *slen = xxx_slen; } else { if ((r = ssh_agent_sign(auth_sock, pubkey, signature, slen, data, dlen, alg, datafellows)) != 0) fatal("%s: ssh_agent_sign failed: %s", __func__, ssh_err(r)); } return 0; } /* SSH2 key exchange */ static void do_ssh2_kex(void) { char *myproposal[PROPOSAL_MAX] = { KEX_SERVER }; struct kex *kex; int r; myproposal[PROPOSAL_KEX_ALGS] = compat_kex_proposal( options.kex_algorithms); myproposal[PROPOSAL_ENC_ALGS_CTOS] = compat_cipher_proposal( options.ciphers); myproposal[PROPOSAL_ENC_ALGS_STOC] = compat_cipher_proposal( options.ciphers); myproposal[PROPOSAL_MAC_ALGS_CTOS] = myproposal[PROPOSAL_MAC_ALGS_STOC] = options.macs; if (options.compression == COMP_NONE) { myproposal[PROPOSAL_COMP_ALGS_CTOS] = myproposal[PROPOSAL_COMP_ALGS_STOC] = "none"; } if (options.rekey_limit || options.rekey_interval) packet_set_rekey_limits(options.rekey_limit, - (time_t)options.rekey_interval); + options.rekey_interval); myproposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = compat_pkalg_proposal( list_hostkey_types()); /* start key exchange */ if ((r = kex_setup(active_state, myproposal)) != 0) fatal("kex_setup: %s", ssh_err(r)); kex = active_state->kex; #ifdef WITH_OPENSSL kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; kex->kex[KEX_DH_GRP14_SHA1] = kexdh_server; kex->kex[KEX_DH_GRP14_SHA256] = kexdh_server; kex->kex[KEX_DH_GRP16_SHA512] = kexdh_server; kex->kex[KEX_DH_GRP18_SHA512] = kexdh_server; kex->kex[KEX_DH_GEX_SHA1] = kexgex_server; kex->kex[KEX_DH_GEX_SHA256] = kexgex_server; # ifdef OPENSSL_HAS_ECC kex->kex[KEX_ECDH_SHA2] = kexecdh_server; # endif #endif kex->kex[KEX_C25519_SHA256] = kexc25519_server; kex->server = 1; kex->client_version_string=client_version_string; kex->server_version_string=server_version_string; kex->load_host_public_key=&get_hostkey_public_by_type; kex->load_host_private_key=&get_hostkey_private_by_type; kex->host_key_index=&get_hostkey_index; kex->sign = sshd_hostkey_sign; dispatch_run(DISPATCH_BLOCK, &kex->done, active_state); session_id2 = kex->session_id; session_id2_len = kex->session_id_len; #ifdef DEBUG_KEXDH /* send 1st encrypted/maced/compressed message */ packet_start(SSH2_MSG_IGNORE); packet_put_cstring("markus"); packet_send(); packet_write_wait(); #endif debug("KEX done"); } /* server specific fatal cleanup */ void cleanup_exit(int i) { if (the_authctxt) { do_cleanup(the_authctxt); if (use_privsep && privsep_is_preauth && pmonitor != NULL && pmonitor->m_pid > 1) { debug("Killing privsep child %d", pmonitor->m_pid); if (kill(pmonitor->m_pid, SIGKILL) != 0 && errno != ESRCH) error("%s: kill(%d): %s", __func__, pmonitor->m_pid, strerror(errno)); } } #ifdef SSH_AUDIT_EVENTS /* done after do_cleanup so it can cancel the PAM auth 'thread' */ if (!use_privsep || mm_is_monitor()) audit_event(SSH_CONNECTION_ABANDON); #endif _exit(i); } diff --git a/crypto/openssh/sshd_config b/crypto/openssh/sshd_config index e8edb03b7ae8..6cf594dcdf1d 100644 --- a/crypto/openssh/sshd_config +++ b/crypto/openssh/sshd_config @@ -1,122 +1,122 @@ -# $OpenBSD: sshd_config,v 1.100 2016/08/15 12:32:04 naddy Exp $ +# $OpenBSD: sshd_config,v 1.101 2017/03/14 07:19:07 djm Exp $ # $FreeBSD$ # This is the sshd server system-wide configuration file. See # sshd_config(5) for more information. # This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin # The strategy used for options in the default sshd_config shipped with # OpenSSH is to specify options with their default value where # possible, but leave them commented. Uncommented options override the # default value. # Note that some of FreeBSD's defaults differ from OpenBSD's, and # FreeBSD has a few additional options. #Port 22 #AddressFamily any #ListenAddress 0.0.0.0 #ListenAddress :: #HostKey /etc/ssh/ssh_host_rsa_key #HostKey /etc/ssh/ssh_host_dsa_key #HostKey /etc/ssh/ssh_host_ecdsa_key #HostKey /etc/ssh/ssh_host_ed25519_key # Ciphers and keying #RekeyLimit default none # Logging #SyslogFacility AUTH #LogLevel INFO # Authentication: #LoginGraceTime 2m #PermitRootLogin no #StrictModes yes #MaxAuthTries 6 #MaxSessions 10 #PubkeyAuthentication yes # The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2 -#AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2 +# but this is overridden so installations will only check .ssh/authorized_keys +AuthorizedKeysFile .ssh/authorized_keys #AuthorizedPrincipalsFile none #AuthorizedKeysCommand none #AuthorizedKeysCommandUser nobody # For this to work you will also need host keys in /etc/ssh/ssh_known_hosts #HostbasedAuthentication no # Change to yes if you don't trust ~/.ssh/known_hosts for # HostbasedAuthentication #IgnoreUserKnownHosts no # Don't read the user's ~/.rhosts and ~/.shosts files #IgnoreRhosts yes # Change to yes to enable built-in password authentication. #PasswordAuthentication no #PermitEmptyPasswords no # Change to no to disable PAM authentication #ChallengeResponseAuthentication yes # Kerberos options #KerberosAuthentication no #KerberosOrLocalPasswd yes #KerberosTicketCleanup yes #KerberosGetAFSToken no # GSSAPI options #GSSAPIAuthentication no #GSSAPICleanupCredentials yes # Set this to 'no' to disable PAM authentication, account processing, # and session processing. If this is enabled, PAM authentication will # be allowed through the ChallengeResponseAuthentication and # PasswordAuthentication. Depending on your PAM configuration, # PAM authentication via ChallengeResponseAuthentication may bypass # the setting of "PermitRootLogin without-password". # If you just want the PAM account and session checks to run without # PAM authentication, then enable this but set PasswordAuthentication # and ChallengeResponseAuthentication to 'no'. #UsePAM yes #AllowAgentForwarding yes #AllowTcpForwarding yes #GatewayPorts no #X11Forwarding yes #X11DisplayOffset 10 #X11UseLocalhost yes #PermitTTY yes #PrintMotd yes #PrintLastLog yes #TCPKeepAlive yes -#UsePrivilegeSeparation sandbox #PermitUserEnvironment no #Compression delayed #ClientAliveInterval 0 #ClientAliveCountMax 3 #UseDNS yes #PidFile /var/run/sshd.pid #MaxStartups 10:30:100 #PermitTunnel no #ChrootDirectory none #UseBlacklist no -#VersionAddendum FreeBSD-20170305 +#VersionAddendum FreeBSD-20170804 # no default banner path #Banner none # override default of no subsystems Subsystem sftp /usr/libexec/sftp-server # Example of overriding settings on a per-user basis #Match User anoncvs # X11Forwarding no # AllowTcpForwarding no # PermitTTY no # ForceCommand cvs server diff --git a/crypto/openssh/sshd_config.5 b/crypto/openssh/sshd_config.5 index 5d934f41e9db..73fa9aa61e13 100644 --- a/crypto/openssh/sshd_config.5 +++ b/crypto/openssh/sshd_config.5 @@ -1,1712 +1,1710 @@ .\" .\" Author: Tatu Ylonen .\" Copyright (c) 1995 Tatu Ylonen , Espoo, Finland .\" All rights reserved .\" .\" As far as I am concerned, the code I have written for this software .\" can be used freely for any purpose. Any derived versions of this .\" software must be clearly marked as such, and if the derived work is .\" incompatible with the protocol description in the RFC file, it must be .\" called by a name other than "ssh" or "Secure Shell". .\" .\" Copyright (c) 1999,2000 Markus Friedl. All rights reserved. .\" Copyright (c) 1999 Aaron Campbell. All rights reserved. .\" Copyright (c) 1999 Theo de Raadt. All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.\" $OpenBSD: sshd_config.5,v 1.239 2016/11/30 03:00:05 djm Exp $ +.\" $OpenBSD: sshd_config.5,v 1.243 2017/03/14 07:19:07 djm Exp $ .\" $FreeBSD$ -.Dd $Mdocdate: November 30 2016 $ +.Dd $Mdocdate: March 14 2017 $ .Dt SSHD_CONFIG 5 .Os .Sh NAME .Nm sshd_config .Nd OpenSSH SSH daemon configuration file .Sh SYNOPSIS .Nm /etc/ssh/sshd_config .Sh DESCRIPTION .Xr sshd 8 reads configuration data from .Pa /etc/ssh/sshd_config (or the file specified with .Fl f on the command line). The file contains keyword-argument pairs, one per line. Lines starting with .Ql # and empty lines are interpreted as comments. Arguments may optionally be enclosed in double quotes .Pq \&" in order to represent arguments containing spaces. .Pp The possible keywords and their meanings are as follows (note that keywords are case-insensitive and arguments are case-sensitive): .Bl -tag -width Ds .It Cm AcceptEnv Specifies what environment variables sent by the client will be copied into the session's .Xr environ 7 . See .Cm SendEnv in .Xr ssh_config 5 for how to configure the client. The .Ev TERM environment variable is always sent whenever the client requests a pseudo-terminal as it is required by the protocol. Variables are specified by name, which may contain the wildcard characters .Ql * and .Ql \&? . Multiple environment variables may be separated by whitespace or spread across multiple .Cm AcceptEnv directives. Be warned that some environment variables could be used to bypass restricted user environments. For this reason, care should be taken in the use of this directive. The default is not to accept any environment variables. .It Cm AddressFamily Specifies which address family should be used by .Xr sshd 8 . Valid arguments are .Cm any (the default), .Cm inet (use IPv4 only), or .Cm inet6 (use IPv6 only). .It Cm AllowAgentForwarding Specifies whether .Xr ssh-agent 1 forwarding is permitted. The default is .Cm yes . Note that disabling agent forwarding does not improve security unless users are also denied shell access, as they can always install their own forwarders. .It Cm AllowGroups This keyword can be followed by a list of group name patterns, separated by spaces. If specified, login is allowed only for users whose primary group or supplementary group list matches one of the patterns. Only group names are valid; a numerical group ID is not recognized. By default, login is allowed for all groups. The allow/deny directives are processed in the following order: .Cm DenyUsers , .Cm AllowUsers , .Cm DenyGroups , and finally .Cm AllowGroups . .Pp See PATTERNS in .Xr ssh_config 5 for more information on patterns. .It Cm AllowStreamLocalForwarding Specifies whether StreamLocal (Unix-domain socket) forwarding is permitted. The available options are .Cm yes (the default) or .Cm all to allow StreamLocal forwarding, .Cm no to prevent all StreamLocal forwarding, .Cm local to allow local (from the perspective of .Xr ssh 1 ) forwarding only or .Cm remote to allow remote forwarding only. Note that disabling StreamLocal forwarding does not improve security unless users are also denied shell access, as they can always install their own forwarders. .It Cm AllowTcpForwarding Specifies whether TCP forwarding is permitted. The available options are .Cm yes (the default) or .Cm all to allow TCP forwarding, .Cm no to prevent all TCP forwarding, .Cm local to allow local (from the perspective of .Xr ssh 1 ) forwarding only or .Cm remote to allow remote forwarding only. Note that disabling TCP forwarding does not improve security unless users are also denied shell access, as they can always install their own forwarders. .It Cm AllowUsers This keyword can be followed by a list of user name patterns, separated by spaces. If specified, login is allowed only for user names that match one of the patterns. Only user names are valid; a numerical user ID is not recognized. By default, login is allowed for all users. If the pattern takes the form USER@HOST then USER and HOST are separately checked, restricting logins to particular users from particular hosts. HOST criteria may additionally contain addresses to match in CIDR address/masklen format. The allow/deny directives are processed in the following order: .Cm DenyUsers , .Cm AllowUsers , .Cm DenyGroups , and finally .Cm AllowGroups . .Pp See PATTERNS in .Xr ssh_config 5 for more information on patterns. .It Cm AuthenticationMethods Specifies the authentication methods that must be successfully completed for a user to be granted access. This option must be followed by one or more comma-separated lists of authentication method names, or by the single string .Cm any to indicate the default behaviour of accepting any single authentication method. If the default is overridden, then successful authentication requires completion of every method in at least one of these lists. .Pp For example, .Qq publickey,password publickey,keyboard-interactive would require the user to complete public key authentication, followed by either password or keyboard interactive authentication. Only methods that are next in one or more lists are offered at each stage, so for this example it would not be possible to attempt password or keyboard-interactive authentication before public key. .Pp For keyboard interactive authentication it is also possible to restrict authentication to a specific device by appending a colon followed by the device identifier .Cm bsdauth , .Cm pam , or .Cm skey , depending on the server configuration. For example, .Qq keyboard-interactive:bsdauth would restrict keyboard interactive authentication to the .Cm bsdauth device. .Pp If the publickey method is listed more than once, .Xr sshd 8 verifies that keys that have been used successfully are not reused for subsequent authentications. For example, .Qq publickey,publickey requires successful authentication using two different public keys. .Pp Note that each authentication method listed should also be explicitly enabled in the configuration. .It Cm AuthorizedKeysCommand Specifies a program to be used to look up the user's public keys. The program must be owned by root, not writable by group or others and specified by an absolute path. Arguments to .Cm AuthorizedKeysCommand accept the tokens described in the .Sx TOKENS section. If no arguments are specified then the username of the target user is used. .Pp The program should produce on standard output zero or more lines of authorized_keys output (see .Sx AUTHORIZED_KEYS in .Xr sshd 8 ) . If a key supplied by .Cm AuthorizedKeysCommand does not successfully authenticate and authorize the user then public key authentication continues using the usual .Cm AuthorizedKeysFile files. By default, no .Cm AuthorizedKeysCommand is run. .It Cm AuthorizedKeysCommandUser Specifies the user under whose account the .Cm AuthorizedKeysCommand is run. It is recommended to use a dedicated user that has no other role on the host than running authorized keys commands. If .Cm AuthorizedKeysCommand is specified but .Cm AuthorizedKeysCommandUser is not, then .Xr sshd 8 will refuse to start. .It Cm AuthorizedKeysFile Specifies the file that contains the public keys used for user authentication. The format is described in the .Sx AUTHORIZED_KEYS FILE FORMAT section of .Xr sshd 8 . Arguments to .Cm AuthorizedKeysFile accept the tokens described in the .Sx TOKENS section. After expansion, .Cm AuthorizedKeysFile is taken to be an absolute path or one relative to the user's home directory. Multiple files may be listed, separated by whitespace. Alternately this option may be set to .Cm none to skip checking for user keys in files. The default is .Qq .ssh/authorized_keys .ssh/authorized_keys2 . .It Cm AuthorizedPrincipalsCommand Specifies a program to be used to generate the list of allowed certificate principals as per .Cm AuthorizedPrincipalsFile . The program must be owned by root, not writable by group or others and specified by an absolute path. Arguments to .Cm AuthorizedPrincipalsCommand accept the tokens described in the .Sx TOKENS section. If no arguments are specified then the username of the target user is used. .Pp The program should produce on standard output zero or more lines of .Cm AuthorizedPrincipalsFile output. If either .Cm AuthorizedPrincipalsCommand or .Cm AuthorizedPrincipalsFile is specified, then certificates offered by the client for authentication must contain a principal that is listed. By default, no .Cm AuthorizedPrincipalsCommand is run. .It Cm AuthorizedPrincipalsCommandUser Specifies the user under whose account the .Cm AuthorizedPrincipalsCommand is run. It is recommended to use a dedicated user that has no other role on the host than running authorized principals commands. If .Cm AuthorizedPrincipalsCommand is specified but .Cm AuthorizedPrincipalsCommandUser is not, then .Xr sshd 8 will refuse to start. .It Cm AuthorizedPrincipalsFile Specifies a file that lists principal names that are accepted for certificate authentication. When using certificates signed by a key listed in .Cm TrustedUserCAKeys , this file lists names, one of which must appear in the certificate for it to be accepted for authentication. Names are listed one per line preceded by key options (as described in .Sx AUTHORIZED_KEYS FILE FORMAT in .Xr sshd 8 ) . Empty lines and comments starting with .Ql # are ignored. .Pp Arguments to .Cm AuthorizedPrincipalsFile accept the tokens described in the .Sx TOKENS section. After expansion, .Cm AuthorizedPrincipalsFile is taken to be an absolute path or one relative to the user's home directory. The default is .Cm none , i.e. not to use a principals file \(en in this case, the username of the user must appear in a certificate's principals list for it to be accepted. .Pp Note that .Cm AuthorizedPrincipalsFile is only used when authentication proceeds using a CA listed in .Cm TrustedUserCAKeys and is not consulted for certification authorities trusted via .Pa ~/.ssh/authorized_keys , though the .Cm principals= key option offers a similar facility (see .Xr sshd 8 for details). .It Cm Banner The contents of the specified file are sent to the remote user before authentication is allowed. If the argument is .Cm none then no banner is displayed. By default, no banner is displayed. .It Cm ChallengeResponseAuthentication Specifies whether challenge-response authentication is allowed (e.g. via PAM or through authentication styles supported in .Xr login.conf 5 ) The default is .Cm yes . .It Cm ChrootDirectory Specifies the pathname of a directory to .Xr chroot 2 to after authentication. At session startup .Xr sshd 8 checks that all components of the pathname are root-owned directories which are not writable by any other user or group. After the chroot, .Xr sshd 8 changes the working directory to the user's home directory. Arguments to .Cm ChrootDirectory accept the tokens described in the .Sx TOKENS section. .Pp The .Cm ChrootDirectory must contain the necessary files and directories to support the user's session. For an interactive session this requires at least a shell, typically .Xr sh 1 , and basic .Pa /dev nodes such as .Xr null 4 , .Xr zero 4 , .Xr stdin 4 , .Xr stdout 4 , .Xr stderr 4 , and .Xr tty 4 devices. For file transfer sessions using SFTP no additional configuration of the environment is necessary if the in-process sftp-server is used, though sessions which use logging may require .Pa /dev/log inside the chroot directory on some operating systems (see .Xr sftp-server 8 for details). .Pp For safety, it is very important that the directory hierarchy be prevented from modification by other processes on the system (especially those outside the jail). Misconfiguration can lead to unsafe environments which .Xr sshd 8 cannot detect. .Pp The default is .Cm none , indicating not to .Xr chroot 2 . .It Cm Ciphers Specifies the ciphers allowed. Multiple ciphers must be comma-separated. If the specified value begins with a .Sq + character, then the specified ciphers will be appended to the default set instead of replacing them. +If the specified value begins with a +.Sq - +character, then the specified ciphers (including wildcards) will be removed +from the default set instead of replacing them. .Pp The supported ciphers are: .Pp .Bl -item -compact -offset indent .It 3des-cbc .It aes128-cbc .It aes192-cbc .It aes256-cbc .It aes128-ctr .It aes192-ctr .It aes256-ctr .It aes128-gcm@openssh.com .It aes256-gcm@openssh.com .It arcfour .It arcfour128 .It arcfour256 .It blowfish-cbc .It cast128-cbc .It chacha20-poly1305@openssh.com .El .Pp The default is: .Bd -literal -offset indent chacha20-poly1305@openssh.com, aes128-ctr,aes192-ctr,aes256-ctr, aes128-gcm@openssh.com,aes256-gcm@openssh.com, aes128-cbc,aes192-cbc,aes256-cbc .Ed .Pp The list of available ciphers may also be obtained using .Qq ssh -Q cipher . .It Cm ClientAliveCountMax Sets the number of client alive messages which may be sent without .Xr sshd 8 receiving any messages back from the client. If this threshold is reached while client alive messages are being sent, sshd will disconnect the client, terminating the session. It is important to note that the use of client alive messages is very different from .Cm TCPKeepAlive . The client alive messages are sent through the encrypted channel and therefore will not be spoofable. The TCP keepalive option enabled by .Cm TCPKeepAlive is spoofable. The client alive mechanism is valuable when the client or server depend on knowing when a connection has become inactive. .Pp The default value is 3. If .Cm ClientAliveInterval is set to 15, and .Cm ClientAliveCountMax is left at the default, unresponsive SSH clients will be disconnected after approximately 45 seconds. .It Cm ClientAliveInterval Sets a timeout interval in seconds after which if no data has been received from the client, .Xr sshd 8 will send a message through the encrypted channel to request a response from the client. The default is 0, indicating that these messages will not be sent to the client. .It Cm Compression Specifies whether compression is enabled after the user has authenticated successfully. The argument must be .Cm yes , .Cm delayed (a legacy synonym for .Cm yes ) or .Cm no . The default is .Cm yes . .It Cm DenyGroups This keyword can be followed by a list of group name patterns, separated by spaces. Login is disallowed for users whose primary group or supplementary group list matches one of the patterns. Only group names are valid; a numerical group ID is not recognized. By default, login is allowed for all groups. The allow/deny directives are processed in the following order: .Cm DenyUsers , .Cm AllowUsers , .Cm DenyGroups , and finally .Cm AllowGroups . .Pp See PATTERNS in .Xr ssh_config 5 for more information on patterns. .It Cm DenyUsers This keyword can be followed by a list of user name patterns, separated by spaces. Login is disallowed for user names that match one of the patterns. Only user names are valid; a numerical user ID is not recognized. By default, login is allowed for all users. If the pattern takes the form USER@HOST then USER and HOST are separately checked, restricting logins to particular users from particular hosts. HOST criteria may additionally contain addresses to match in CIDR address/masklen format. The allow/deny directives are processed in the following order: .Cm DenyUsers , .Cm AllowUsers , .Cm DenyGroups , and finally .Cm AllowGroups . .Pp See PATTERNS in .Xr ssh_config 5 for more information on patterns. .It Cm DisableForwarding Disables all forwarding features, including X11, .Xr ssh-agent 1 , TCP and StreamLocal. This option overrides all other forwarding-related options and may simplify restricted configurations. .It Cm FingerprintHash Specifies the hash algorithm used when logging key fingerprints. Valid options are: .Cm md5 and .Cm sha256 . The default is .Cm sha256 . .It Cm ForceCommand Forces the execution of the command specified by .Cm ForceCommand , ignoring any command supplied by the client and .Pa ~/.ssh/rc if present. The command is invoked by using the user's login shell with the -c option. This applies to shell, command, or subsystem execution. It is most useful inside a .Cm Match block. The command originally supplied by the client is available in the .Ev SSH_ORIGINAL_COMMAND environment variable. Specifying a command of .Cm internal-sftp will force the use of an in-process SFTP server that requires no support files when used with .Cm ChrootDirectory . The default is .Cm none . .It Cm GatewayPorts Specifies whether remote hosts are allowed to connect to ports forwarded for the client. By default, .Xr sshd 8 binds remote port forwardings to the loopback address. This prevents other remote hosts from connecting to forwarded ports. .Cm GatewayPorts can be used to specify that sshd should allow remote port forwardings to bind to non-loopback addresses, thus allowing other hosts to connect. The argument may be .Cm no to force remote port forwardings to be available to the local host only, .Cm yes to force remote port forwardings to bind to the wildcard address, or .Cm clientspecified to allow the client to select the address to which the forwarding is bound. The default is .Cm no . .It Cm GSSAPIAuthentication Specifies whether user authentication based on GSSAPI is allowed. The default is .Cm no . .It Cm GSSAPICleanupCredentials Specifies whether to automatically destroy the user's credentials cache on logout. The default is .Cm yes . .It Cm GSSAPIStrictAcceptorCheck Determines whether to be strict about the identity of the GSSAPI acceptor a client authenticates against. If set to .Cm yes then the client must authenticate against the host service on the current hostname. If set to .Cm no then the client may authenticate against any service key stored in the machine's default store. This facility is provided to assist with operation on multi homed machines. The default is .Cm yes . .It Cm HostbasedAcceptedKeyTypes Specifies the key types that will be accepted for hostbased authentication as a comma-separated pattern list. Alternately if the specified value begins with a .Sq + character, then the specified key types will be appended to the default set instead of replacing them. +If the specified value begins with a +.Sq - +character, then the specified key types (including wildcards) will be removed +from the default set instead of replacing them. The default for this option is: .Bd -literal -offset 3n ecdsa-sha2-nistp256-cert-v01@openssh.com, ecdsa-sha2-nistp384-cert-v01@openssh.com, ecdsa-sha2-nistp521-cert-v01@openssh.com, ssh-ed25519-cert-v01@openssh.com, ssh-rsa-cert-v01@openssh.com, ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, ssh-ed25519,ssh-rsa .Ed .Pp The list of available key types may also be obtained using .Qq ssh -Q key . .It Cm HostbasedAuthentication Specifies whether rhosts or /etc/hosts.equiv authentication together with successful public key client host authentication is allowed (host-based authentication). The default is .Cm no . .It Cm HostbasedUsesNameFromPacketOnly Specifies whether or not the server will attempt to perform a reverse name lookup when matching the name in the .Pa ~/.shosts , .Pa ~/.rhosts , and .Pa /etc/hosts.equiv files during .Cm HostbasedAuthentication . A setting of .Cm yes means that .Xr sshd 8 uses the name supplied by the client rather than attempting to resolve the name from the TCP connection itself. The default is .Cm no . .It Cm HostCertificate Specifies a file containing a public host certificate. The certificate's public key must match a private host key already specified by .Cm HostKey . The default behaviour of .Xr sshd 8 is not to load any certificates. .It Cm HostKey Specifies a file containing a private host key used by SSH. The defaults are .Pa /etc/ssh/ssh_host_dsa_key , .Pa /etc/ssh/ssh_host_ecdsa_key , .Pa /etc/ssh/ssh_host_ed25519_key and .Pa /etc/ssh/ssh_host_rsa_key . .Pp Note that .Xr sshd 8 will refuse to use a file if it is group/world-accessible and that the .Cm HostKeyAlgorithms option restricts which of the keys are actually used by .Xr sshd 8 . .Pp It is possible to have multiple host key files. It is also possible to specify public host key files instead. In this case operations on the private key will be delegated to an .Xr ssh-agent 1 . .It Cm HostKeyAgent Identifies the UNIX-domain socket used to communicate with an agent that has access to the private host keys. If the string .Qq SSH_AUTH_SOCK is specified, the location of the socket will be read from the .Ev SSH_AUTH_SOCK environment variable. .It Cm HostKeyAlgorithms Specifies the host key algorithms that the server offers. The default for this option is: .Bd -literal -offset 3n ecdsa-sha2-nistp256-cert-v01@openssh.com, ecdsa-sha2-nistp384-cert-v01@openssh.com, ecdsa-sha2-nistp521-cert-v01@openssh.com, ssh-ed25519-cert-v01@openssh.com, ssh-rsa-cert-v01@openssh.com, ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, ssh-ed25519,ssh-rsa .Ed .Pp The list of available key types may also be obtained using .Qq ssh -Q key . .It Cm IgnoreRhosts Specifies that .Pa .rhosts and .Pa .shosts files will not be used in .Cm HostbasedAuthentication . .Pp .Pa /etc/hosts.equiv and .Pa /etc/ssh/shosts.equiv are still used. The default is .Cm yes . .It Cm IgnoreUserKnownHosts Specifies whether .Xr sshd 8 should ignore the user's .Pa ~/.ssh/known_hosts during .Cm HostbasedAuthentication . The default is .Cm no . .It Cm IPQoS Specifies the IPv4 type-of-service or DSCP class for the connection. Accepted values are .Cm af11 , .Cm af12 , .Cm af13 , .Cm af21 , .Cm af22 , .Cm af23 , .Cm af31 , .Cm af32 , .Cm af33 , .Cm af41 , .Cm af42 , .Cm af43 , .Cm cs0 , .Cm cs1 , .Cm cs2 , .Cm cs3 , .Cm cs4 , .Cm cs5 , .Cm cs6 , .Cm cs7 , .Cm ef , .Cm lowdelay , .Cm throughput , .Cm reliability , or a numeric value. This option may take one or two arguments, separated by whitespace. If one argument is specified, it is used as the packet class unconditionally. If two values are specified, the first is automatically selected for interactive sessions and the second for non-interactive sessions. The default is .Cm lowdelay for interactive sessions and .Cm throughput for non-interactive sessions. .It Cm KbdInteractiveAuthentication Specifies whether to allow keyboard-interactive authentication. The argument to this keyword must be .Cm yes or .Cm no . The default is to use whatever value .Cm ChallengeResponseAuthentication is set to (by default .Cm yes ) . .It Cm KerberosAuthentication Specifies whether the password provided by the user for .Cm PasswordAuthentication will be validated through the Kerberos KDC. To use this option, the server needs a Kerberos servtab which allows the verification of the KDC's identity. The default is .Cm no . .It Cm KerberosGetAFSToken If AFS is active and the user has a Kerberos 5 TGT, attempt to acquire an AFS token before accessing the user's home directory. The default is .Cm no . .It Cm KerberosOrLocalPasswd If password authentication through Kerberos fails then the password will be validated via any additional local mechanism such as .Pa /etc/passwd . The default is .Cm yes . .It Cm KerberosTicketCleanup Specifies whether to automatically destroy the user's ticket cache file on logout. The default is .Cm yes . .It Cm KexAlgorithms Specifies the available KEX (Key Exchange) algorithms. Multiple algorithms must be comma-separated. Alternately if the specified value begins with a .Sq + character, then the specified methods will be appended to the default set instead of replacing them. +If the specified value begins with a +.Sq - +character, then the specified methods (including wildcards) will be removed +from the default set instead of replacing them. The supported algorithms are: .Pp .Bl -item -compact -offset indent .It curve25519-sha256 .It curve25519-sha256@libssh.org .It diffie-hellman-group1-sha1 .It diffie-hellman-group14-sha1 .It diffie-hellman-group-exchange-sha1 .It diffie-hellman-group-exchange-sha256 .It ecdh-sha2-nistp256 .It ecdh-sha2-nistp384 .It ecdh-sha2-nistp521 .El .Pp The default is: .Bd -literal -offset indent curve25519-sha256,curve25519-sha256@libssh.org, ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521, diffie-hellman-group-exchange-sha256, diffie-hellman-group14-sha1 .Ed .Pp The list of available key exchange algorithms may also be obtained using .Qq ssh -Q kex . .It Cm ListenAddress Specifies the local addresses .Xr sshd 8 should listen on. The following forms may be used: .Pp .Bl -item -offset indent -compact .It .Cm ListenAddress .Sm off .Ar host | Ar IPv4_addr | Ar IPv6_addr .Sm on .It .Cm ListenAddress .Sm off .Ar host | Ar IPv4_addr : Ar port .Sm on .It .Cm ListenAddress .Sm off .Oo .Ar host | Ar IPv6_addr Oc : Ar port .Sm on .El .Pp If .Ar port is not specified, sshd will listen on the address and all .Cm Port options specified. The default is to listen on all local addresses. Multiple .Cm ListenAddress options are permitted. .It Cm LoginGraceTime The server disconnects after this time if the user has not successfully logged in. If the value is 0, there is no time limit. The default is 120 seconds. .It Cm LogLevel Gives the verbosity level that is used when logging messages from .Xr sshd 8 . The possible values are: QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3. The default is INFO. DEBUG and DEBUG1 are equivalent. DEBUG2 and DEBUG3 each specify higher levels of debugging output. Logging with a DEBUG level violates the privacy of users and is not recommended. .It Cm MACs Specifies the available MAC (message authentication code) algorithms. The MAC algorithm is used for data integrity protection. Multiple algorithms must be comma-separated. If the specified value begins with a .Sq + character, then the specified algorithms will be appended to the default set instead of replacing them. +If the specified value begins with a +.Sq - +character, then the specified algorithms (including wildcards) will be removed +from the default set instead of replacing them. .Pp The algorithms that contain .Qq -etm calculate the MAC after encryption (encrypt-then-mac). These are considered safer and their use recommended. The supported MACs are: .Pp .Bl -item -compact -offset indent .It hmac-md5 .It hmac-md5-96 .It hmac-ripemd160 .It hmac-sha1 .It hmac-sha1-96 .It hmac-sha2-256 .It hmac-sha2-512 .It umac-64@openssh.com .It umac-128@openssh.com .It hmac-md5-etm@openssh.com .It hmac-md5-96-etm@openssh.com .It hmac-ripemd160-etm@openssh.com .It hmac-sha1-etm@openssh.com .It hmac-sha1-96-etm@openssh.com .It hmac-sha2-256-etm@openssh.com .It hmac-sha2-512-etm@openssh.com .It umac-64-etm@openssh.com .It umac-128-etm@openssh.com .El .Pp The default is: .Bd -literal -offset indent umac-64-etm@openssh.com,umac-128-etm@openssh.com, hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com, hmac-sha1-etm@openssh.com, umac-64@openssh.com,umac-128@openssh.com, hmac-sha2-256,hmac-sha2-512,hmac-sha1 .Ed .Pp The list of available MAC algorithms may also be obtained using .Qq ssh -Q mac . .It Cm Match Introduces a conditional block. If all of the criteria on the .Cm Match line are satisfied, the keywords on the following lines override those set in the global section of the config file, until either another .Cm Match line or the end of the file. If a keyword appears in multiple .Cm Match blocks that are satisfied, only the first instance of the keyword is applied. .Pp The arguments to .Cm Match are one or more criteria-pattern pairs or the single token .Cm All which matches all criteria. The available criteria are .Cm User , .Cm Group , .Cm Host , .Cm LocalAddress , .Cm LocalPort , and .Cm Address . The match patterns may consist of single entries or comma-separated lists and may use the wildcard and negation operators described in the .Sx PATTERNS section of .Xr ssh_config 5 . .Pp The patterns in an .Cm Address criteria may additionally contain addresses to match in CIDR address/masklen format, such as 192.0.2.0/24 or 2001:db8::/32. Note that the mask length provided must be consistent with the address - it is an error to specify a mask length that is too long for the address or one with bits set in this host portion of the address. For example, 192.0.2.0/33 and 192.0.2.0/8, respectively. .Pp Only a subset of keywords may be used on the lines following a .Cm Match keyword. Available keywords are .Cm AcceptEnv , .Cm AllowAgentForwarding , .Cm AllowGroups , .Cm AllowStreamLocalForwarding , .Cm AllowTcpForwarding , .Cm AllowUsers , .Cm AuthenticationMethods , .Cm AuthorizedKeysCommand , .Cm AuthorizedKeysCommandUser , .Cm AuthorizedKeysFile , .Cm AuthorizedPrincipalsCommand , .Cm AuthorizedPrincipalsCommandUser , .Cm AuthorizedPrincipalsFile , .Cm Banner , .Cm ChrootDirectory , .Cm ClientAliveCountMax , .Cm ClientAliveInterval , .Cm DenyGroups , .Cm DenyUsers , .Cm ForceCommand , .Cm GatewayPorts , .Cm GSSAPIAuthentication , .Cm HostbasedAcceptedKeyTypes , .Cm HostbasedAuthentication , .Cm HostbasedUsesNameFromPacketOnly , .Cm IPQoS , .Cm KbdInteractiveAuthentication , .Cm KerberosAuthentication , .Cm MaxAuthTries , .Cm MaxSessions , .Cm PasswordAuthentication , .Cm PermitEmptyPasswords , .Cm PermitOpen , .Cm PermitRootLogin , .Cm PermitTTY , .Cm PermitTunnel , .Cm PermitUserRC , .Cm PubkeyAcceptedKeyTypes , .Cm PubkeyAuthentication , .Cm RekeyLimit , .Cm RevokedKeys , .Cm StreamLocalBindMask , .Cm StreamLocalBindUnlink , .Cm TrustedUserCAKeys , .Cm X11DisplayOffset , .Cm X11Forwarding and .Cm X11UseLocalHost . .It Cm MaxAuthTries Specifies the maximum number of authentication attempts permitted per connection. Once the number of failures reaches half this value, additional failures are logged. The default is 6. .It Cm MaxSessions Specifies the maximum number of open shell, login or subsystem (e.g. sftp) sessions permitted per network connection. Multiple sessions may be established by clients that support connection multiplexing. Setting .Cm MaxSessions to 1 will effectively disable session multiplexing, whereas setting it to 0 will prevent all shell, login and subsystem sessions while still permitting forwarding. The default is 10. .It Cm MaxStartups Specifies the maximum number of concurrent unauthenticated connections to the SSH daemon. Additional connections will be dropped until authentication succeeds or the .Cm LoginGraceTime expires for a connection. The default is 10:30:100. .Pp Alternatively, random early drop can be enabled by specifying the three colon separated values start:rate:full (e.g. "10:30:60"). .Xr sshd 8 will refuse connection attempts with a probability of rate/100 (30%) if there are currently start (10) unauthenticated connections. The probability increases linearly and all connection attempts are refused if the number of unauthenticated connections reaches full (60). .It Cm PasswordAuthentication Specifies whether password authentication is allowed. See also .Cm UsePAM . The default is .Cm no . .It Cm PermitEmptyPasswords When password authentication is allowed, it specifies whether the server allows login to accounts with empty password strings. The default is .Cm no . .It Cm PermitOpen Specifies the destinations to which TCP port forwarding is permitted. The forwarding specification must be one of the following forms: .Pp .Bl -item -offset indent -compact .It .Cm PermitOpen .Sm off .Ar host : port .Sm on .It .Cm PermitOpen .Sm off .Ar IPv4_addr : port .Sm on .It .Cm PermitOpen .Sm off .Ar \&[ IPv6_addr \&] : port .Sm on .El .Pp Multiple forwards may be specified by separating them with whitespace. An argument of .Cm any can be used to remove all restrictions and permit any forwarding requests. An argument of .Cm none can be used to prohibit all forwarding requests. The wildcard .Sq * can be used for host or port to allow all hosts or ports, respectively. By default all port forwarding requests are permitted. .It Cm PermitRootLogin Specifies whether root can log in using .Xr ssh 1 . The argument must be .Cm yes , .Cm prohibit-password , .Cm without-password , .Cm forced-commands-only , or .Cm no . The default is .Cm no . Note that if .Cm ChallengeResponseAuthentication and .Cm UsePAM are both .Cm yes , this setting may be overridden by the PAM policy. .Pp If this option is set to .Cm prohibit-password or .Cm without-password , password and keyboard-interactive authentication are disabled for root. .Pp If this option is set to .Cm forced-commands-only , root login with public key authentication will be allowed, but only if the .Ar command option has been specified (which may be useful for taking remote backups even if root login is normally not allowed). All other authentication methods are disabled for root. .Pp If this option is set to .Cm no , root is not allowed to log in. .It Cm PermitTTY Specifies whether .Xr pty 4 allocation is permitted. The default is .Cm yes . .It Cm PermitTunnel Specifies whether .Xr tun 4 device forwarding is allowed. The argument must be .Cm yes , .Cm point-to-point (layer 3), .Cm ethernet (layer 2), or .Cm no . Specifying .Cm yes permits both .Cm point-to-point and .Cm ethernet . The default is .Cm no . .Pp Independent of this setting, the permissions of the selected .Xr tun 4 device must allow access to the user. .It Cm PermitUserEnvironment Specifies whether .Pa ~/.ssh/environment and .Cm environment= options in .Pa ~/.ssh/authorized_keys are processed by .Xr sshd 8 . The default is .Cm no . Enabling environment processing may enable users to bypass access restrictions in some configurations using mechanisms such as .Ev LD_PRELOAD . .It Cm PermitUserRC Specifies whether any .Pa ~/.ssh/rc file is executed. The default is .Cm yes . .It Cm PidFile Specifies the file that contains the process ID of the SSH daemon, or .Cm none to not write one. The default is .Pa /var/run/sshd.pid . .It Cm Port Specifies the port number that .Xr sshd 8 listens on. The default is 22. Multiple options of this type are permitted. See also .Cm ListenAddress . .It Cm PrintLastLog Specifies whether .Xr sshd 8 should print the date and time of the last user login when a user logs in interactively. The default is .Cm yes . .It Cm PrintMotd Specifies whether .Xr sshd 8 should print .Pa /etc/motd when a user logs in interactively. (On some systems it is also printed by the shell, .Pa /etc/profile , or equivalent.) The default is .Cm yes . .It Cm PubkeyAcceptedKeyTypes Specifies the key types that will be accepted for public key authentication as a comma-separated pattern list. Alternately if the specified value begins with a .Sq + character, then the specified key types will be appended to the default set instead of replacing them. +If the specified value begins with a +.Sq - +character, then the specified key types (including wildcards) will be removed +from the default set instead of replacing them. The default for this option is: .Bd -literal -offset 3n ecdsa-sha2-nistp256-cert-v01@openssh.com, ecdsa-sha2-nistp384-cert-v01@openssh.com, ecdsa-sha2-nistp521-cert-v01@openssh.com, ssh-ed25519-cert-v01@openssh.com, ssh-rsa-cert-v01@openssh.com, ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, ssh-ed25519,ssh-rsa .Ed .Pp The list of available key types may also be obtained using .Qq ssh -Q key . .It Cm PubkeyAuthentication Specifies whether public key authentication is allowed. The default is .Cm yes . .It Cm RekeyLimit Specifies the maximum amount of data that may be transmitted before the session key is renegotiated, optionally followed a maximum amount of time that may pass before the session key is renegotiated. The first argument is specified in bytes and may have a suffix of .Sq K , .Sq M , or .Sq G to indicate Kilobytes, Megabytes, or Gigabytes, respectively. The default is between .Sq 1G and .Sq 4G , depending on the cipher. The optional second value is specified in seconds and may use any of the units documented in the .Sx TIME FORMATS section. The default value for .Cm RekeyLimit is .Cm default none , which means that rekeying is performed after the cipher's default amount of data has been sent or received and no time based rekeying is done. .It Cm RevokedKeys Specifies revoked public keys file, or .Cm none to not use one. Keys listed in this file will be refused for public key authentication. Note that if this file is not readable, then public key authentication will be refused for all users. Keys may be specified as a text file, listing one public key per line, or as an OpenSSH Key Revocation List (KRL) as generated by .Xr ssh-keygen 1 . For more information on KRLs, see the KEY REVOCATION LISTS section in .Xr ssh-keygen 1 . .It Cm StreamLocalBindMask Sets the octal file creation mode mask .Pq umask used when creating a Unix-domain socket file for local or remote port forwarding. This option is only used for port forwarding to a Unix-domain socket file. .Pp The default value is 0177, which creates a Unix-domain socket file that is readable and writable only by the owner. Note that not all operating systems honor the file mode on Unix-domain socket files. .It Cm StreamLocalBindUnlink Specifies whether to remove an existing Unix-domain socket file for local or remote port forwarding before creating a new one. If the socket file already exists and .Cm StreamLocalBindUnlink is not enabled, .Nm sshd will be unable to forward the port to the Unix-domain socket file. This option is only used for port forwarding to a Unix-domain socket file. .Pp The argument must be .Cm yes or .Cm no . The default is .Cm no . .It Cm StrictModes Specifies whether .Xr sshd 8 should check file modes and ownership of the user's files and home directory before accepting login. This is normally desirable because novices sometimes accidentally leave their directory or files world-writable. The default is .Cm yes . Note that this does not apply to .Cm ChrootDirectory , whose permissions and ownership are checked unconditionally. .It Cm Subsystem Configures an external subsystem (e.g. file transfer daemon). Arguments should be a subsystem name and a command (with optional arguments) to execute upon subsystem request. .Pp The command .Cm sftp-server implements the SFTP file transfer subsystem. .Pp Alternately the name .Cm internal-sftp implements an in-process SFTP server. This may simplify configurations using .Cm ChrootDirectory to force a different filesystem root on clients. .Pp By default no subsystems are defined. .It Cm SyslogFacility Gives the facility code that is used when logging messages from .Xr sshd 8 . The possible values are: DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. The default is AUTH. .It Cm TCPKeepAlive Specifies whether the system should send TCP keepalive messages to the other side. If they are sent, death of the connection or crash of one of the machines will be properly noticed. However, this means that connections will die if the route is down temporarily, and some people find it annoying. On the other hand, if TCP keepalives are not sent, sessions may hang indefinitely on the server, leaving .Qq ghost users and consuming server resources. .Pp The default is .Cm yes (to send TCP keepalive messages), and the server will notice if the network goes down or the client host crashes. This avoids infinitely hanging sessions. .Pp To disable TCP keepalive messages, the value should be set to .Cm no . .It Cm TrustedUserCAKeys Specifies a file containing public keys of certificate authorities that are trusted to sign user certificates for authentication, or .Cm none to not use one. Keys are listed one per line; empty lines and comments starting with .Ql # are allowed. If a certificate is presented for authentication and has its signing CA key listed in this file, then it may be used for authentication for any user listed in the certificate's principals list. Note that certificates that lack a list of principals will not be permitted for authentication using .Cm TrustedUserCAKeys . For more details on certificates, see the CERTIFICATES section in .Xr ssh-keygen 1 . .It Cm UseBlacklist Specifies whether .Xr sshd 8 attempts to send authentication success and failure messages to the .Xr blacklistd 8 daemon. The default is .Cm no . .It Cm UseDNS Specifies whether .Xr sshd 8 should look up the remote host name, and to check that the resolved host name for the remote IP address maps back to the very same IP address. .Pp If this option is set to .Cm no , then only addresses and not host names may be used in .Pa ~/.ssh/authorized_keys .Cm from and .Nm .Cm Match .Cm Host directives. The default is .Dq yes . .It Cm UsePAM Enables the Pluggable Authentication Module interface. If set to .Cm yes this will enable PAM authentication using .Cm ChallengeResponseAuthentication and .Cm PasswordAuthentication in addition to PAM account and session module processing for all authentication types. .Pp Because PAM challenge-response authentication usually serves an equivalent role to password authentication, you should disable either .Cm PasswordAuthentication or .Cm ChallengeResponseAuthentication. .Pp If .Cm UsePAM is enabled, you will not be able to run .Xr sshd 8 as a non-root user. The default is .Cm yes . -.It Cm UsePrivilegeSeparation -Specifies whether -.Xr sshd 8 -separates privileges by creating an unprivileged child process -to deal with incoming network traffic. -After successful authentication, another process will be created that has -the privilege of the authenticated user. -The goal of privilege separation is to prevent privilege -escalation by containing any corruption within the unprivileged processes. -The argument must be -.Cm yes , -.Cm no , -or -.Cm sandbox . -If -.Cm UsePrivilegeSeparation -is set to -.Cm sandbox -then the pre-authentication unprivileged process is subject to additional -restrictions. -The default is -.Cm sandbox . .It Cm VersionAddendum Optionally specifies additional text to append to the SSH protocol banner sent by the server upon connection. The default is -.Qq FreeBSD-20170305 . +.Qq FreeBSD-20170804 . The value .Cm none may be used to disable this. .It Cm X11DisplayOffset Specifies the first display number available for .Xr sshd 8 Ns 's X11 forwarding. This prevents sshd from interfering with real X11 servers. The default is 10. .It Cm X11Forwarding Specifies whether X11 forwarding is permitted. The argument must be .Cm yes or .Cm no . The default is .Cm yes . .Pp When X11 forwarding is enabled, there may be additional exposure to the server and to client displays if the .Xr sshd 8 proxy display is configured to listen on the wildcard address (see .Cm X11UseLocalhost ) , though this is not the default. Additionally, the authentication spoofing and authentication data verification and substitution occur on the client side. The security risk of using X11 forwarding is that the client's X11 display server may be exposed to attack when the SSH client requests forwarding (see the warnings for .Cm ForwardX11 in .Xr ssh_config 5 ) . A system administrator may have a stance in which they want to protect clients that may expose themselves to attack by unwittingly requesting X11 forwarding, which can warrant a .Cm no setting. .Pp Note that disabling X11 forwarding does not prevent users from forwarding X11 traffic, as users can always install their own forwarders. .It Cm X11UseLocalhost Specifies whether .Xr sshd 8 should bind the X11 forwarding server to the loopback address or to the wildcard address. By default, sshd binds the forwarding server to the loopback address and sets the hostname part of the .Ev DISPLAY environment variable to .Cm localhost . This prevents remote hosts from connecting to the proxy display. However, some older X11 clients may not function with this configuration. .Cm X11UseLocalhost may be set to .Cm no to specify that the forwarding server should be bound to the wildcard address. The argument must be .Cm yes or .Cm no . The default is .Cm yes . .It Cm XAuthLocation Specifies the full pathname of the .Xr xauth 1 program, or .Cm none to not use one. The default is .Pa /usr/local/bin/xauth . .El .Sh TIME FORMATS .Xr sshd 8 command-line arguments and configuration file options that specify time may be expressed using a sequence of the form: .Sm off .Ar time Op Ar qualifier , .Sm on where .Ar time is a positive integer value and .Ar qualifier is one of the following: .Pp .Bl -tag -width Ds -compact -offset indent .It Aq Cm none seconds .It Cm s | Cm S seconds .It Cm m | Cm M minutes .It Cm h | Cm H hours .It Cm d | Cm D days .It Cm w | Cm W weeks .El .Pp Each member of the sequence is added together to calculate the total time value. .Pp Time format examples: .Pp .Bl -tag -width Ds -compact -offset indent .It 600 600 seconds (10 minutes) .It 10m 10 minutes .It 1h30m 1 hour 30 minutes (90 minutes) .El .Sh TOKENS Arguments to some keywords can make use of tokens, which are expanded at runtime: .Pp .Bl -tag -width XXXX -offset indent -compact .It %% A literal .Sq % . .It %F The fingerprint of the CA key. .It %f The fingerprint of the key or certificate. .It %h The home directory of the user. .It %i The key ID in the certificate. .It %K The base64-encoded CA key. .It %k The base64-encoded key or certificate for authentication. .It %s The serial number of the certificate. .It \&%T The type of the CA key. .It %t The key or certificate type. .It %u The username. .El .Pp .Cm AuthorizedKeysCommand -accepts the tokens %%, %f, %h, %t, and %u. +accepts the tokens %%, %f, %h, %k, %t, and %u. .Pp .Cm AuthorizedKeysFile accepts the tokens %%, %h, and %u. .Pp .Cm AuthorizedPrincipalsCommand -accepts the tokens %%, %F, %f, %K, %k, %h, %i, %s, %T, %t, and %u. +accepts the tokens %%, %F, %f, %h, %i, %K, %k, %s, %T, %t, and %u. .Pp .Cm AuthorizedPrincipalsFile accepts the tokens %%, %h, and %u. .Pp .Cm ChrootDirectory accepts the tokens %%, %h, and %u. .Sh FILES .Bl -tag -width Ds .It Pa /etc/ssh/sshd_config Contains configuration data for .Xr sshd 8 . This file should be writable by root only, but it is recommended (though not necessary) that it be world-readable. .El .Sh SEE ALSO .Xr sftp-server 8 , .Xr sshd 8 .Sh AUTHORS .An -nosplit OpenSSH is a derivative of the original and free ssh 1.2.12 release by .An Tatu Ylonen . .An Aaron Campbell , Bob Beck , Markus Friedl , Niels Provos , .An Theo de Raadt and .An Dug Song removed many bugs, re-added newer features and created OpenSSH. .An Markus Friedl contributed the support for SSH protocol versions 1.5 and 2.0. .An Niels Provos and .An Markus Friedl contributed support for privilege separation. diff --git a/crypto/openssh/sshkey.c b/crypto/openssh/sshkey.c index c01da6c39b2e..53a7674b5e74 100644 --- a/crypto/openssh/sshkey.c +++ b/crypto/openssh/sshkey.c @@ -1,3917 +1,3967 @@ -/* $OpenBSD: sshkey.c,v 1.41 2016/10/24 01:09:17 dtucker Exp $ */ +/* $OpenBSD: sshkey.c,v 1.45 2017/03/10 04:07:20 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * Copyright (c) 2008 Alexander von Gernler. All rights reserved. * Copyright (c) 2010,2011 Damien Miller. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" #include #include #ifdef WITH_OPENSSL #include #include #include #endif #include "crypto_api.h" #include #include #include #include #include #ifdef HAVE_UTIL_H #include #endif /* HAVE_UTIL_H */ #include "ssh2.h" #include "ssherr.h" #include "misc.h" #include "sshbuf.h" #include "rsa.h" #include "cipher.h" #include "digest.h" #define SSHKEY_INTERNAL #include "sshkey.h" #include "match.h" /* openssh private key file format */ #define MARK_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----\n" #define MARK_END "-----END OPENSSH PRIVATE KEY-----\n" #define MARK_BEGIN_LEN (sizeof(MARK_BEGIN) - 1) #define MARK_END_LEN (sizeof(MARK_END) - 1) #define KDFNAME "bcrypt" #define AUTH_MAGIC "openssh-key-v1" #define SALT_LEN 16 #define DEFAULT_CIPHERNAME "aes256-cbc" #define DEFAULT_ROUNDS 16 /* Version identification string for SSH v1 identity files. */ #define LEGACY_BEGIN "SSH PRIVATE KEY FILE FORMAT 1.1\n" static int sshkey_from_blob_internal(struct sshbuf *buf, struct sshkey **keyp, int allow_cert); /* Supported key types */ struct keytype { const char *name; const char *shortname; int type; int nid; int cert; int sigonly; }; static const struct keytype keytypes[] = { { "ssh-ed25519", "ED25519", KEY_ED25519, 0, 0, 0 }, { "ssh-ed25519-cert-v01@openssh.com", "ED25519-CERT", KEY_ED25519_CERT, 0, 1, 0 }, #ifdef WITH_OPENSSL +# ifdef WITH_SSH1 { NULL, "RSA1", KEY_RSA1, 0, 0, 0 }, +# endif { "ssh-rsa", "RSA", KEY_RSA, 0, 0, 0 }, { "rsa-sha2-256", "RSA", KEY_RSA, 0, 0, 1 }, { "rsa-sha2-512", "RSA", KEY_RSA, 0, 0, 1 }, { "ssh-dss", "DSA", KEY_DSA, 0, 0, 0 }, # ifdef OPENSSL_HAS_ECC { "ecdsa-sha2-nistp256", "ECDSA", KEY_ECDSA, NID_X9_62_prime256v1, 0, 0 }, { "ecdsa-sha2-nistp384", "ECDSA", KEY_ECDSA, NID_secp384r1, 0, 0 }, # ifdef OPENSSL_HAS_NISTP521 { "ecdsa-sha2-nistp521", "ECDSA", KEY_ECDSA, NID_secp521r1, 0, 0 }, # endif /* OPENSSL_HAS_NISTP521 */ # endif /* OPENSSL_HAS_ECC */ { "ssh-rsa-cert-v01@openssh.com", "RSA-CERT", KEY_RSA_CERT, 0, 1, 0 }, { "ssh-dss-cert-v01@openssh.com", "DSA-CERT", KEY_DSA_CERT, 0, 1, 0 }, # ifdef OPENSSL_HAS_ECC { "ecdsa-sha2-nistp256-cert-v01@openssh.com", "ECDSA-CERT", KEY_ECDSA_CERT, NID_X9_62_prime256v1, 1, 0 }, { "ecdsa-sha2-nistp384-cert-v01@openssh.com", "ECDSA-CERT", KEY_ECDSA_CERT, NID_secp384r1, 1, 0 }, # ifdef OPENSSL_HAS_NISTP521 { "ecdsa-sha2-nistp521-cert-v01@openssh.com", "ECDSA-CERT", KEY_ECDSA_CERT, NID_secp521r1, 1, 0 }, # endif /* OPENSSL_HAS_NISTP521 */ # endif /* OPENSSL_HAS_ECC */ #endif /* WITH_OPENSSL */ { NULL, NULL, -1, -1, 0, 0 } }; const char * sshkey_type(const struct sshkey *k) { const struct keytype *kt; for (kt = keytypes; kt->type != -1; kt++) { if (kt->type == k->type) return kt->shortname; } return "unknown"; } static const char * sshkey_ssh_name_from_type_nid(int type, int nid) { const struct keytype *kt; for (kt = keytypes; kt->type != -1; kt++) { if (kt->type == type && (kt->nid == 0 || kt->nid == nid)) return kt->name; } return "ssh-unknown"; } int sshkey_type_is_cert(int type) { const struct keytype *kt; for (kt = keytypes; kt->type != -1; kt++) { if (kt->type == type) return kt->cert; } return 0; } const char * sshkey_ssh_name(const struct sshkey *k) { return sshkey_ssh_name_from_type_nid(k->type, k->ecdsa_nid); } const char * sshkey_ssh_name_plain(const struct sshkey *k) { return sshkey_ssh_name_from_type_nid(sshkey_type_plain(k->type), k->ecdsa_nid); } int sshkey_type_from_name(const char *name) { const struct keytype *kt; for (kt = keytypes; kt->type != -1; kt++) { /* Only allow shortname matches for plain key types */ if ((kt->name != NULL && strcmp(name, kt->name) == 0) || (!kt->cert && strcasecmp(kt->shortname, name) == 0)) return kt->type; } return KEY_UNSPEC; } int sshkey_ecdsa_nid_from_name(const char *name) { const struct keytype *kt; for (kt = keytypes; kt->type != -1; kt++) { if (kt->type != KEY_ECDSA && kt->type != KEY_ECDSA_CERT) continue; if (kt->name != NULL && strcmp(name, kt->name) == 0) return kt->nid; } return -1; } char * -sshkey_alg_list(int certs_only, int plain_only, char sep) +sshkey_alg_list(int certs_only, int plain_only, int include_sigonly, char sep) { char *tmp, *ret = NULL; size_t nlen, rlen = 0; const struct keytype *kt; for (kt = keytypes; kt->type != -1; kt++) { - if (kt->name == NULL || kt->sigonly) + if (kt->name == NULL) + continue; + if (!include_sigonly && kt->sigonly) continue; if ((certs_only && !kt->cert) || (plain_only && kt->cert)) continue; if (ret != NULL) ret[rlen++] = sep; nlen = strlen(kt->name); if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) { free(ret); return NULL; } ret = tmp; memcpy(ret + rlen, kt->name, nlen + 1); rlen += nlen; } return ret; } int sshkey_names_valid2(const char *names, int allow_wildcard) { char *s, *cp, *p; const struct keytype *kt; int type; if (names == NULL || strcmp(names, "") == 0) return 0; if ((s = cp = strdup(names)) == NULL) return 0; for ((p = strsep(&cp, ",")); p && *p != '\0'; (p = strsep(&cp, ","))) { type = sshkey_type_from_name(p); if (type == KEY_RSA1) { free(s); return 0; } if (type == KEY_UNSPEC) { if (allow_wildcard) { /* * Try matching key types against the string. * If any has a positive or negative match then * the component is accepted. */ for (kt = keytypes; kt->type != -1; kt++) { if (kt->type == KEY_RSA1) continue; if (match_pattern_list(kt->name, p, 0) != 0) break; } if (kt->type != -1) continue; } free(s); return 0; } } free(s); return 1; } u_int sshkey_size(const struct sshkey *k) { switch (k->type) { #ifdef WITH_OPENSSL case KEY_RSA1: case KEY_RSA: case KEY_RSA_CERT: return BN_num_bits(k->rsa->n); case KEY_DSA: case KEY_DSA_CERT: return BN_num_bits(k->dsa->p); case KEY_ECDSA: case KEY_ECDSA_CERT: return sshkey_curve_nid_to_bits(k->ecdsa_nid); #endif /* WITH_OPENSSL */ case KEY_ED25519: case KEY_ED25519_CERT: return 256; /* XXX */ } return 0; } static int sshkey_type_is_valid_ca(int type) { switch (type) { case KEY_RSA: case KEY_DSA: case KEY_ECDSA: case KEY_ED25519: return 1; default: return 0; } } int sshkey_is_cert(const struct sshkey *k) { if (k == NULL) return 0; return sshkey_type_is_cert(k->type); } /* Return the cert-less equivalent to a certified key type */ int sshkey_type_plain(int type) { switch (type) { case KEY_RSA_CERT: return KEY_RSA; case KEY_DSA_CERT: return KEY_DSA; case KEY_ECDSA_CERT: return KEY_ECDSA; case KEY_ED25519_CERT: return KEY_ED25519; default: return type; } } #ifdef WITH_OPENSSL /* XXX: these are really begging for a table-driven approach */ int sshkey_curve_name_to_nid(const char *name) { if (strcmp(name, "nistp256") == 0) return NID_X9_62_prime256v1; else if (strcmp(name, "nistp384") == 0) return NID_secp384r1; # ifdef OPENSSL_HAS_NISTP521 else if (strcmp(name, "nistp521") == 0) return NID_secp521r1; # endif /* OPENSSL_HAS_NISTP521 */ else return -1; } u_int sshkey_curve_nid_to_bits(int nid) { switch (nid) { case NID_X9_62_prime256v1: return 256; case NID_secp384r1: return 384; # ifdef OPENSSL_HAS_NISTP521 case NID_secp521r1: return 521; # endif /* OPENSSL_HAS_NISTP521 */ default: return 0; } } int sshkey_ecdsa_bits_to_nid(int bits) { switch (bits) { case 256: return NID_X9_62_prime256v1; case 384: return NID_secp384r1; # ifdef OPENSSL_HAS_NISTP521 case 521: return NID_secp521r1; # endif /* OPENSSL_HAS_NISTP521 */ default: return -1; } } const char * sshkey_curve_nid_to_name(int nid) { switch (nid) { case NID_X9_62_prime256v1: return "nistp256"; case NID_secp384r1: return "nistp384"; # ifdef OPENSSL_HAS_NISTP521 case NID_secp521r1: return "nistp521"; # endif /* OPENSSL_HAS_NISTP521 */ default: return NULL; } } int sshkey_ec_nid_to_hash_alg(int nid) { int kbits = sshkey_curve_nid_to_bits(nid); if (kbits <= 0) return -1; /* RFC5656 section 6.2.1 */ if (kbits <= 256) return SSH_DIGEST_SHA256; else if (kbits <= 384) return SSH_DIGEST_SHA384; else return SSH_DIGEST_SHA512; } #endif /* WITH_OPENSSL */ static void cert_free(struct sshkey_cert *cert) { u_int i; if (cert == NULL) return; sshbuf_free(cert->certblob); sshbuf_free(cert->critical); sshbuf_free(cert->extensions); free(cert->key_id); for (i = 0; i < cert->nprincipals; i++) free(cert->principals[i]); free(cert->principals); sshkey_free(cert->signature_key); explicit_bzero(cert, sizeof(*cert)); free(cert); } static struct sshkey_cert * cert_new(void) { struct sshkey_cert *cert; if ((cert = calloc(1, sizeof(*cert))) == NULL) return NULL; if ((cert->certblob = sshbuf_new()) == NULL || (cert->critical = sshbuf_new()) == NULL || (cert->extensions = sshbuf_new()) == NULL) { cert_free(cert); return NULL; } cert->key_id = NULL; cert->principals = NULL; cert->signature_key = NULL; return cert; } struct sshkey * sshkey_new(int type) { struct sshkey *k; #ifdef WITH_OPENSSL RSA *rsa; DSA *dsa; #endif /* WITH_OPENSSL */ if ((k = calloc(1, sizeof(*k))) == NULL) return NULL; k->type = type; k->ecdsa = NULL; k->ecdsa_nid = -1; k->dsa = NULL; k->rsa = NULL; k->cert = NULL; k->ed25519_sk = NULL; k->ed25519_pk = NULL; switch (k->type) { #ifdef WITH_OPENSSL case KEY_RSA1: case KEY_RSA: case KEY_RSA_CERT: if ((rsa = RSA_new()) == NULL || (rsa->n = BN_new()) == NULL || (rsa->e = BN_new()) == NULL) { if (rsa != NULL) RSA_free(rsa); free(k); return NULL; } k->rsa = rsa; break; case KEY_DSA: case KEY_DSA_CERT: if ((dsa = DSA_new()) == NULL || (dsa->p = BN_new()) == NULL || (dsa->q = BN_new()) == NULL || (dsa->g = BN_new()) == NULL || (dsa->pub_key = BN_new()) == NULL) { if (dsa != NULL) DSA_free(dsa); free(k); return NULL; } k->dsa = dsa; break; case KEY_ECDSA: case KEY_ECDSA_CERT: /* Cannot do anything until we know the group */ break; #endif /* WITH_OPENSSL */ case KEY_ED25519: case KEY_ED25519_CERT: /* no need to prealloc */ break; case KEY_UNSPEC: break; default: free(k); return NULL; } if (sshkey_is_cert(k)) { if ((k->cert = cert_new()) == NULL) { sshkey_free(k); return NULL; } } return k; } int sshkey_add_private(struct sshkey *k) { switch (k->type) { #ifdef WITH_OPENSSL case KEY_RSA1: case KEY_RSA: case KEY_RSA_CERT: #define bn_maybe_alloc_failed(p) (p == NULL && (p = BN_new()) == NULL) if (bn_maybe_alloc_failed(k->rsa->d) || bn_maybe_alloc_failed(k->rsa->iqmp) || bn_maybe_alloc_failed(k->rsa->q) || bn_maybe_alloc_failed(k->rsa->p) || bn_maybe_alloc_failed(k->rsa->dmq1) || bn_maybe_alloc_failed(k->rsa->dmp1)) return SSH_ERR_ALLOC_FAIL; break; case KEY_DSA: case KEY_DSA_CERT: if (bn_maybe_alloc_failed(k->dsa->priv_key)) return SSH_ERR_ALLOC_FAIL; break; #undef bn_maybe_alloc_failed case KEY_ECDSA: case KEY_ECDSA_CERT: /* Cannot do anything until we know the group */ break; #endif /* WITH_OPENSSL */ case KEY_ED25519: case KEY_ED25519_CERT: /* no need to prealloc */ break; case KEY_UNSPEC: break; default: return SSH_ERR_INVALID_ARGUMENT; } return 0; } struct sshkey * sshkey_new_private(int type) { struct sshkey *k = sshkey_new(type); if (k == NULL) return NULL; if (sshkey_add_private(k) != 0) { sshkey_free(k); return NULL; } return k; } void sshkey_free(struct sshkey *k) { if (k == NULL) return; switch (k->type) { #ifdef WITH_OPENSSL case KEY_RSA1: case KEY_RSA: case KEY_RSA_CERT: if (k->rsa != NULL) RSA_free(k->rsa); k->rsa = NULL; break; case KEY_DSA: case KEY_DSA_CERT: if (k->dsa != NULL) DSA_free(k->dsa); k->dsa = NULL; break; # ifdef OPENSSL_HAS_ECC case KEY_ECDSA: case KEY_ECDSA_CERT: if (k->ecdsa != NULL) EC_KEY_free(k->ecdsa); k->ecdsa = NULL; break; # endif /* OPENSSL_HAS_ECC */ #endif /* WITH_OPENSSL */ case KEY_ED25519: case KEY_ED25519_CERT: if (k->ed25519_pk) { explicit_bzero(k->ed25519_pk, ED25519_PK_SZ); free(k->ed25519_pk); k->ed25519_pk = NULL; } if (k->ed25519_sk) { explicit_bzero(k->ed25519_sk, ED25519_SK_SZ); free(k->ed25519_sk); k->ed25519_sk = NULL; } break; case KEY_UNSPEC: break; default: break; } if (sshkey_is_cert(k)) cert_free(k->cert); explicit_bzero(k, sizeof(*k)); free(k); } static int cert_compare(struct sshkey_cert *a, struct sshkey_cert *b) { if (a == NULL && b == NULL) return 1; if (a == NULL || b == NULL) return 0; if (sshbuf_len(a->certblob) != sshbuf_len(b->certblob)) return 0; if (timingsafe_bcmp(sshbuf_ptr(a->certblob), sshbuf_ptr(b->certblob), sshbuf_len(a->certblob)) != 0) return 0; return 1; } /* * Compare public portions of key only, allowing comparisons between * certificates and plain keys too. */ int sshkey_equal_public(const struct sshkey *a, const struct sshkey *b) { #if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) BN_CTX *bnctx; #endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */ if (a == NULL || b == NULL || sshkey_type_plain(a->type) != sshkey_type_plain(b->type)) return 0; switch (a->type) { #ifdef WITH_OPENSSL case KEY_RSA1: case KEY_RSA_CERT: case KEY_RSA: return a->rsa != NULL && b->rsa != NULL && BN_cmp(a->rsa->e, b->rsa->e) == 0 && BN_cmp(a->rsa->n, b->rsa->n) == 0; case KEY_DSA_CERT: case KEY_DSA: return a->dsa != NULL && b->dsa != NULL && BN_cmp(a->dsa->p, b->dsa->p) == 0 && BN_cmp(a->dsa->q, b->dsa->q) == 0 && BN_cmp(a->dsa->g, b->dsa->g) == 0 && BN_cmp(a->dsa->pub_key, b->dsa->pub_key) == 0; # ifdef OPENSSL_HAS_ECC case KEY_ECDSA_CERT: case KEY_ECDSA: if (a->ecdsa == NULL || b->ecdsa == NULL || EC_KEY_get0_public_key(a->ecdsa) == NULL || EC_KEY_get0_public_key(b->ecdsa) == NULL) return 0; if ((bnctx = BN_CTX_new()) == NULL) return 0; if (EC_GROUP_cmp(EC_KEY_get0_group(a->ecdsa), EC_KEY_get0_group(b->ecdsa), bnctx) != 0 || EC_POINT_cmp(EC_KEY_get0_group(a->ecdsa), EC_KEY_get0_public_key(a->ecdsa), EC_KEY_get0_public_key(b->ecdsa), bnctx) != 0) { BN_CTX_free(bnctx); return 0; } BN_CTX_free(bnctx); return 1; # endif /* OPENSSL_HAS_ECC */ #endif /* WITH_OPENSSL */ case KEY_ED25519: case KEY_ED25519_CERT: return a->ed25519_pk != NULL && b->ed25519_pk != NULL && memcmp(a->ed25519_pk, b->ed25519_pk, ED25519_PK_SZ) == 0; default: return 0; } /* NOTREACHED */ } int sshkey_equal(const struct sshkey *a, const struct sshkey *b) { if (a == NULL || b == NULL || a->type != b->type) return 0; if (sshkey_is_cert(a)) { if (!cert_compare(a->cert, b->cert)) return 0; } return sshkey_equal_public(a, b); } static int to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain) { int type, ret = SSH_ERR_INTERNAL_ERROR; const char *typename; if (key == NULL) return SSH_ERR_INVALID_ARGUMENT; if (sshkey_is_cert(key)) { if (key->cert == NULL) return SSH_ERR_EXPECTED_CERT; if (sshbuf_len(key->cert->certblob) == 0) return SSH_ERR_KEY_LACKS_CERTBLOB; } type = force_plain ? sshkey_type_plain(key->type) : key->type; typename = sshkey_ssh_name_from_type_nid(type, key->ecdsa_nid); switch (type) { #ifdef WITH_OPENSSL case KEY_DSA_CERT: case KEY_ECDSA_CERT: case KEY_RSA_CERT: #endif /* WITH_OPENSSL */ case KEY_ED25519_CERT: /* Use the existing blob */ /* XXX modified flag? */ if ((ret = sshbuf_putb(b, key->cert->certblob)) != 0) return ret; break; #ifdef WITH_OPENSSL case KEY_DSA: if (key->dsa == NULL) return SSH_ERR_INVALID_ARGUMENT; if ((ret = sshbuf_put_cstring(b, typename)) != 0 || (ret = sshbuf_put_bignum2(b, key->dsa->p)) != 0 || (ret = sshbuf_put_bignum2(b, key->dsa->q)) != 0 || (ret = sshbuf_put_bignum2(b, key->dsa->g)) != 0 || (ret = sshbuf_put_bignum2(b, key->dsa->pub_key)) != 0) return ret; break; # ifdef OPENSSL_HAS_ECC case KEY_ECDSA: if (key->ecdsa == NULL) return SSH_ERR_INVALID_ARGUMENT; if ((ret = sshbuf_put_cstring(b, typename)) != 0 || (ret = sshbuf_put_cstring(b, sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 || (ret = sshbuf_put_eckey(b, key->ecdsa)) != 0) return ret; break; # endif case KEY_RSA: if (key->rsa == NULL) return SSH_ERR_INVALID_ARGUMENT; if ((ret = sshbuf_put_cstring(b, typename)) != 0 || (ret = sshbuf_put_bignum2(b, key->rsa->e)) != 0 || (ret = sshbuf_put_bignum2(b, key->rsa->n)) != 0) return ret; break; #endif /* WITH_OPENSSL */ case KEY_ED25519: if (key->ed25519_pk == NULL) return SSH_ERR_INVALID_ARGUMENT; if ((ret = sshbuf_put_cstring(b, typename)) != 0 || (ret = sshbuf_put_string(b, key->ed25519_pk, ED25519_PK_SZ)) != 0) return ret; break; default: return SSH_ERR_KEY_TYPE_UNKNOWN; } return 0; } int sshkey_putb(const struct sshkey *key, struct sshbuf *b) { return to_blob_buf(key, b, 0); } int sshkey_puts(const struct sshkey *key, struct sshbuf *b) { struct sshbuf *tmp; int r; if ((tmp = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; r = to_blob_buf(key, tmp, 0); if (r == 0) r = sshbuf_put_stringb(b, tmp); sshbuf_free(tmp); return r; } int sshkey_putb_plain(const struct sshkey *key, struct sshbuf *b) { return to_blob_buf(key, b, 1); } static int to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp, int force_plain) { int ret = SSH_ERR_INTERNAL_ERROR; size_t len; struct sshbuf *b = NULL; if (lenp != NULL) *lenp = 0; if (blobp != NULL) *blobp = NULL; if ((b = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; if ((ret = to_blob_buf(key, b, force_plain)) != 0) goto out; len = sshbuf_len(b); if (lenp != NULL) *lenp = len; if (blobp != NULL) { if ((*blobp = malloc(len)) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; } memcpy(*blobp, sshbuf_ptr(b), len); } ret = 0; out: sshbuf_free(b); return ret; } int sshkey_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp) { return to_blob(key, blobp, lenp, 0); } int sshkey_plain_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp) { return to_blob(key, blobp, lenp, 1); } int sshkey_fingerprint_raw(const struct sshkey *k, int dgst_alg, u_char **retp, size_t *lenp) { u_char *blob = NULL, *ret = NULL; size_t blob_len = 0; int r = SSH_ERR_INTERNAL_ERROR; if (retp != NULL) *retp = NULL; if (lenp != NULL) *lenp = 0; if (ssh_digest_bytes(dgst_alg) == 0) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } if (k->type == KEY_RSA1) { #ifdef WITH_OPENSSL int nlen = BN_num_bytes(k->rsa->n); int elen = BN_num_bytes(k->rsa->e); if (nlen < 0 || elen < 0 || nlen >= INT_MAX - elen) { r = SSH_ERR_INVALID_FORMAT; goto out; } blob_len = nlen + elen; if ((blob = malloc(blob_len)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } BN_bn2bin(k->rsa->n, blob); BN_bn2bin(k->rsa->e, blob + nlen); #endif /* WITH_OPENSSL */ } else if ((r = to_blob(k, &blob, &blob_len, 1)) != 0) goto out; if ((ret = calloc(1, SSH_DIGEST_MAX_LENGTH)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = ssh_digest_memory(dgst_alg, blob, blob_len, ret, SSH_DIGEST_MAX_LENGTH)) != 0) goto out; /* success */ if (retp != NULL) { *retp = ret; ret = NULL; } if (lenp != NULL) *lenp = ssh_digest_bytes(dgst_alg); r = 0; out: free(ret); if (blob != NULL) { explicit_bzero(blob, blob_len); free(blob); } return r; } static char * fingerprint_b64(const char *alg, u_char *dgst_raw, size_t dgst_raw_len) { char *ret; size_t plen = strlen(alg) + 1; size_t rlen = ((dgst_raw_len + 2) / 3) * 4 + plen + 1; int r; if (dgst_raw_len > 65536 || (ret = calloc(1, rlen)) == NULL) return NULL; strlcpy(ret, alg, rlen); strlcat(ret, ":", rlen); if (dgst_raw_len == 0) return ret; if ((r = b64_ntop(dgst_raw, dgst_raw_len, ret + plen, rlen - plen)) == -1) { explicit_bzero(ret, rlen); free(ret); return NULL; } /* Trim padding characters from end */ ret[strcspn(ret, "=")] = '\0'; return ret; } static char * fingerprint_hex(const char *alg, u_char *dgst_raw, size_t dgst_raw_len) { char *retval, hex[5]; size_t i, rlen = dgst_raw_len * 3 + strlen(alg) + 2; if (dgst_raw_len > 65536 || (retval = calloc(1, rlen)) == NULL) return NULL; strlcpy(retval, alg, rlen); strlcat(retval, ":", rlen); for (i = 0; i < dgst_raw_len; i++) { snprintf(hex, sizeof(hex), "%s%02x", i > 0 ? ":" : "", dgst_raw[i]); strlcat(retval, hex, rlen); } return retval; } static char * fingerprint_bubblebabble(u_char *dgst_raw, size_t dgst_raw_len) { char vowels[] = { 'a', 'e', 'i', 'o', 'u', 'y' }; char consonants[] = { 'b', 'c', 'd', 'f', 'g', 'h', 'k', 'l', 'm', 'n', 'p', 'r', 's', 't', 'v', 'z', 'x' }; u_int i, j = 0, rounds, seed = 1; char *retval; rounds = (dgst_raw_len / 2) + 1; if ((retval = calloc(rounds, 6)) == NULL) return NULL; retval[j++] = 'x'; for (i = 0; i < rounds; i++) { u_int idx0, idx1, idx2, idx3, idx4; if ((i + 1 < rounds) || (dgst_raw_len % 2 != 0)) { idx0 = (((((u_int)(dgst_raw[2 * i])) >> 6) & 3) + seed) % 6; idx1 = (((u_int)(dgst_raw[2 * i])) >> 2) & 15; idx2 = ((((u_int)(dgst_raw[2 * i])) & 3) + (seed / 6)) % 6; retval[j++] = vowels[idx0]; retval[j++] = consonants[idx1]; retval[j++] = vowels[idx2]; if ((i + 1) < rounds) { idx3 = (((u_int)(dgst_raw[(2 * i) + 1])) >> 4) & 15; idx4 = (((u_int)(dgst_raw[(2 * i) + 1]))) & 15; retval[j++] = consonants[idx3]; retval[j++] = '-'; retval[j++] = consonants[idx4]; seed = ((seed * 5) + ((((u_int)(dgst_raw[2 * i])) * 7) + ((u_int)(dgst_raw[(2 * i) + 1])))) % 36; } } else { idx0 = seed % 6; idx1 = 16; idx2 = seed / 6; retval[j++] = vowels[idx0]; retval[j++] = consonants[idx1]; retval[j++] = vowels[idx2]; } } retval[j++] = 'x'; retval[j++] = '\0'; return retval; } /* * Draw an ASCII-Art representing the fingerprint so human brain can * profit from its built-in pattern recognition ability. * This technique is called "random art" and can be found in some * scientific publications like this original paper: * * "Hash Visualization: a New Technique to improve Real-World Security", * Perrig A. and Song D., 1999, International Workshop on Cryptographic * Techniques and E-Commerce (CrypTEC '99) * sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf * * The subject came up in a talk by Dan Kaminsky, too. * * If you see the picture is different, the key is different. * If the picture looks the same, you still know nothing. * * The algorithm used here is a worm crawling over a discrete plane, * leaving a trace (augmenting the field) everywhere it goes. * Movement is taken from dgst_raw 2bit-wise. Bumping into walls * makes the respective movement vector be ignored for this turn. * Graphs are not unambiguous, because circles in graphs can be * walked in either direction. */ /* * Field sizes for the random art. Have to be odd, so the starting point * can be in the exact middle of the picture, and FLDBASE should be >=8 . * Else pictures would be too dense, and drawing the frame would * fail, too, because the key type would not fit in anymore. */ #define FLDBASE 8 #define FLDSIZE_Y (FLDBASE + 1) #define FLDSIZE_X (FLDBASE * 2 + 1) static char * fingerprint_randomart(const char *alg, u_char *dgst_raw, size_t dgst_raw_len, const struct sshkey *k) { /* * Chars to be used after each other every time the worm * intersects with itself. Matter of taste. */ char *augmentation_string = " .o+=*BOX@%&#/^SE"; char *retval, *p, title[FLDSIZE_X], hash[FLDSIZE_X]; u_char field[FLDSIZE_X][FLDSIZE_Y]; size_t i, tlen, hlen; u_int b; int x, y, r; size_t len = strlen(augmentation_string) - 1; if ((retval = calloc((FLDSIZE_X + 3), (FLDSIZE_Y + 2))) == NULL) return NULL; /* initialize field */ memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char)); x = FLDSIZE_X / 2; y = FLDSIZE_Y / 2; /* process raw key */ for (i = 0; i < dgst_raw_len; i++) { int input; /* each byte conveys four 2-bit move commands */ input = dgst_raw[i]; for (b = 0; b < 4; b++) { /* evaluate 2 bit, rest is shifted later */ x += (input & 0x1) ? 1 : -1; y += (input & 0x2) ? 1 : -1; /* assure we are still in bounds */ x = MAXIMUM(x, 0); y = MAXIMUM(y, 0); x = MINIMUM(x, FLDSIZE_X - 1); y = MINIMUM(y, FLDSIZE_Y - 1); /* augment the field */ if (field[x][y] < len - 2) field[x][y]++; input = input >> 2; } } /* mark starting point and end point*/ field[FLDSIZE_X / 2][FLDSIZE_Y / 2] = len - 1; field[x][y] = len; /* assemble title */ r = snprintf(title, sizeof(title), "[%s %u]", sshkey_type(k), sshkey_size(k)); /* If [type size] won't fit, then try [type]; fits "[ED25519-CERT]" */ if (r < 0 || r > (int)sizeof(title)) r = snprintf(title, sizeof(title), "[%s]", sshkey_type(k)); tlen = (r <= 0) ? 0 : strlen(title); /* assemble hash ID. */ r = snprintf(hash, sizeof(hash), "[%s]", alg); hlen = (r <= 0) ? 0 : strlen(hash); /* output upper border */ p = retval; *p++ = '+'; for (i = 0; i < (FLDSIZE_X - tlen) / 2; i++) *p++ = '-'; memcpy(p, title, tlen); p += tlen; for (i += tlen; i < FLDSIZE_X; i++) *p++ = '-'; *p++ = '+'; *p++ = '\n'; /* output content */ for (y = 0; y < FLDSIZE_Y; y++) { *p++ = '|'; for (x = 0; x < FLDSIZE_X; x++) *p++ = augmentation_string[MINIMUM(field[x][y], len)]; *p++ = '|'; *p++ = '\n'; } /* output lower border */ *p++ = '+'; for (i = 0; i < (FLDSIZE_X - hlen) / 2; i++) *p++ = '-'; memcpy(p, hash, hlen); p += hlen; for (i += hlen; i < FLDSIZE_X; i++) *p++ = '-'; *p++ = '+'; return retval; } char * sshkey_fingerprint(const struct sshkey *k, int dgst_alg, enum sshkey_fp_rep dgst_rep) { char *retval = NULL; u_char *dgst_raw; size_t dgst_raw_len; if (sshkey_fingerprint_raw(k, dgst_alg, &dgst_raw, &dgst_raw_len) != 0) return NULL; switch (dgst_rep) { case SSH_FP_DEFAULT: if (dgst_alg == SSH_DIGEST_MD5) { retval = fingerprint_hex(ssh_digest_alg_name(dgst_alg), dgst_raw, dgst_raw_len); } else { retval = fingerprint_b64(ssh_digest_alg_name(dgst_alg), dgst_raw, dgst_raw_len); } break; case SSH_FP_HEX: retval = fingerprint_hex(ssh_digest_alg_name(dgst_alg), dgst_raw, dgst_raw_len); break; case SSH_FP_BASE64: retval = fingerprint_b64(ssh_digest_alg_name(dgst_alg), dgst_raw, dgst_raw_len); break; case SSH_FP_BUBBLEBABBLE: retval = fingerprint_bubblebabble(dgst_raw, dgst_raw_len); break; case SSH_FP_RANDOMART: retval = fingerprint_randomart(ssh_digest_alg_name(dgst_alg), dgst_raw, dgst_raw_len, k); break; default: explicit_bzero(dgst_raw, dgst_raw_len); free(dgst_raw); return NULL; } explicit_bzero(dgst_raw, dgst_raw_len); free(dgst_raw); return retval; } #ifdef WITH_SSH1 /* * Reads a multiple-precision integer in decimal from the buffer, and advances * the pointer. The integer must already be initialized. This function is * permitted to modify the buffer. This leaves *cpp to point just beyond the * last processed character. */ static int read_decimal_bignum(char **cpp, BIGNUM *v) { char *cp; size_t e; int skip = 1; /* skip white space */ cp = *cpp; while (*cp == ' ' || *cp == '\t') cp++; e = strspn(cp, "0123456789"); if (e == 0) return SSH_ERR_INVALID_FORMAT; if (e > SSHBUF_MAX_BIGNUM * 3) return SSH_ERR_BIGNUM_TOO_LARGE; if (cp[e] == '\0') skip = 0; else if (strchr(" \t\r\n", cp[e]) == NULL) return SSH_ERR_INVALID_FORMAT; cp[e] = '\0'; if (BN_dec2bn(&v, cp) <= 0) return SSH_ERR_INVALID_FORMAT; *cpp = cp + e + skip; return 0; } #endif /* WITH_SSH1 */ /* returns 0 ok, and < 0 error */ int sshkey_read(struct sshkey *ret, char **cpp) { struct sshkey *k; int retval = SSH_ERR_INVALID_FORMAT; char *ep, *cp, *space; int r, type, curve_nid = -1; struct sshbuf *blob; #ifdef WITH_SSH1 u_long bits; #endif /* WITH_SSH1 */ + if (ret == NULL) + return SSH_ERR_INVALID_ARGUMENT; + cp = *cpp; switch (ret->type) { case KEY_RSA1: #ifdef WITH_SSH1 /* Get number of bits. */ bits = strtoul(cp, &ep, 10); if (*cp == '\0' || strchr(" \t\r\n", *ep) == NULL || bits == 0 || bits > SSHBUF_MAX_BIGNUM * 8) return SSH_ERR_INVALID_FORMAT; /* Bad bit count... */ /* Get public exponent, public modulus. */ if ((r = read_decimal_bignum(&ep, ret->rsa->e)) < 0) return r; if ((r = read_decimal_bignum(&ep, ret->rsa->n)) < 0) return r; /* validate the claimed number of bits */ if (BN_num_bits(ret->rsa->n) != (int)bits) return SSH_ERR_KEY_BITS_MISMATCH; *cpp = ep; retval = 0; #endif /* WITH_SSH1 */ break; case KEY_UNSPEC: case KEY_RSA: case KEY_DSA: case KEY_ECDSA: case KEY_ED25519: case KEY_DSA_CERT: case KEY_ECDSA_CERT: case KEY_RSA_CERT: case KEY_ED25519_CERT: space = strchr(cp, ' '); if (space == NULL) return SSH_ERR_INVALID_FORMAT; *space = '\0'; type = sshkey_type_from_name(cp); if (sshkey_type_plain(type) == KEY_ECDSA && (curve_nid = sshkey_ecdsa_nid_from_name(cp)) == -1) return SSH_ERR_EC_CURVE_INVALID; *space = ' '; if (type == KEY_UNSPEC) return SSH_ERR_INVALID_FORMAT; cp = space+1; if (*cp == '\0') return SSH_ERR_INVALID_FORMAT; if (ret->type != KEY_UNSPEC && ret->type != type) return SSH_ERR_KEY_TYPE_MISMATCH; if ((blob = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; /* trim comment */ space = strchr(cp, ' '); if (space) { /* advance 'space': skip whitespace */ *space++ = '\0'; while (*space == ' ' || *space == '\t') space++; ep = space; } else ep = cp + strlen(cp); if ((r = sshbuf_b64tod(blob, cp)) != 0) { sshbuf_free(blob); return r; } if ((r = sshkey_from_blob(sshbuf_ptr(blob), sshbuf_len(blob), &k)) != 0) { sshbuf_free(blob); return r; } sshbuf_free(blob); if (k->type != type) { sshkey_free(k); return SSH_ERR_KEY_TYPE_MISMATCH; } if (sshkey_type_plain(type) == KEY_ECDSA && curve_nid != k->ecdsa_nid) { sshkey_free(k); return SSH_ERR_EC_CURVE_MISMATCH; } ret->type = type; if (sshkey_is_cert(ret)) { if (!sshkey_is_cert(k)) { sshkey_free(k); return SSH_ERR_EXPECTED_CERT; } if (ret->cert != NULL) cert_free(ret->cert); ret->cert = k->cert; k->cert = NULL; } switch (sshkey_type_plain(ret->type)) { #ifdef WITH_OPENSSL case KEY_RSA: if (ret->rsa != NULL) RSA_free(ret->rsa); ret->rsa = k->rsa; k->rsa = NULL; #ifdef DEBUG_PK RSA_print_fp(stderr, ret->rsa, 8); #endif break; case KEY_DSA: if (ret->dsa != NULL) DSA_free(ret->dsa); ret->dsa = k->dsa; k->dsa = NULL; #ifdef DEBUG_PK DSA_print_fp(stderr, ret->dsa, 8); #endif break; # ifdef OPENSSL_HAS_ECC case KEY_ECDSA: if (ret->ecdsa != NULL) EC_KEY_free(ret->ecdsa); ret->ecdsa = k->ecdsa; ret->ecdsa_nid = k->ecdsa_nid; k->ecdsa = NULL; k->ecdsa_nid = -1; #ifdef DEBUG_PK sshkey_dump_ec_key(ret->ecdsa); #endif break; # endif /* OPENSSL_HAS_ECC */ #endif /* WITH_OPENSSL */ case KEY_ED25519: free(ret->ed25519_pk); ret->ed25519_pk = k->ed25519_pk; k->ed25519_pk = NULL; #ifdef DEBUG_PK /* XXX */ #endif break; } *cpp = ep; retval = 0; /*XXXX*/ sshkey_free(k); if (retval != 0) break; break; default: return SSH_ERR_INVALID_ARGUMENT; } return retval; } int sshkey_to_base64(const struct sshkey *key, char **b64p) { int r = SSH_ERR_INTERNAL_ERROR; struct sshbuf *b = NULL; char *uu = NULL; if (b64p != NULL) *b64p = NULL; if ((b = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; if ((r = sshkey_putb(key, b)) != 0) goto out; if ((uu = sshbuf_dtob64(b)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } /* Success */ if (b64p != NULL) { *b64p = uu; uu = NULL; } r = 0; out: sshbuf_free(b); free(uu); return r; } static int sshkey_format_rsa1(const struct sshkey *key, struct sshbuf *b) { int r = SSH_ERR_INTERNAL_ERROR; #ifdef WITH_SSH1 u_int bits = 0; char *dec_e = NULL, *dec_n = NULL; if (key->rsa == NULL || key->rsa->e == NULL || key->rsa->n == NULL) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } if ((dec_e = BN_bn2dec(key->rsa->e)) == NULL || (dec_n = BN_bn2dec(key->rsa->n)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } /* size of modulus 'n' */ if ((bits = BN_num_bits(key->rsa->n)) <= 0) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } if ((r = sshbuf_putf(b, "%u %s %s", bits, dec_e, dec_n)) != 0) goto out; /* Success */ r = 0; out: if (dec_e != NULL) OPENSSL_free(dec_e); if (dec_n != NULL) OPENSSL_free(dec_n); #endif /* WITH_SSH1 */ return r; } static int sshkey_format_text(const struct sshkey *key, struct sshbuf *b) { int r = SSH_ERR_INTERNAL_ERROR; char *uu = NULL; if (key->type == KEY_RSA1) { if ((r = sshkey_format_rsa1(key, b)) != 0) goto out; } else { /* Unsupported key types handled in sshkey_to_base64() */ if ((r = sshkey_to_base64(key, &uu)) != 0) goto out; if ((r = sshbuf_putf(b, "%s %s", sshkey_ssh_name(key), uu)) != 0) goto out; } r = 0; out: free(uu); return r; } int sshkey_write(const struct sshkey *key, FILE *f) { struct sshbuf *b = NULL; int r = SSH_ERR_INTERNAL_ERROR; if ((b = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; if ((r = sshkey_format_text(key, b)) != 0) goto out; if (fwrite(sshbuf_ptr(b), sshbuf_len(b), 1, f) != 1) { if (feof(f)) errno = EPIPE; r = SSH_ERR_SYSTEM_ERROR; goto out; } /* Success */ r = 0; out: sshbuf_free(b); return r; } const char * sshkey_cert_type(const struct sshkey *k) { switch (k->cert->type) { case SSH2_CERT_TYPE_USER: return "user"; case SSH2_CERT_TYPE_HOST: return "host"; default: return "unknown"; } } #ifdef WITH_OPENSSL static int rsa_generate_private_key(u_int bits, RSA **rsap) { RSA *private = NULL; BIGNUM *f4 = NULL; int ret = SSH_ERR_INTERNAL_ERROR; if (rsap == NULL || bits < SSH_RSA_MINIMUM_MODULUS_SIZE || bits > SSHBUF_MAX_BIGNUM * 8) return SSH_ERR_INVALID_ARGUMENT; *rsap = NULL; if ((private = RSA_new()) == NULL || (f4 = BN_new()) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; } if (!BN_set_word(f4, RSA_F4) || !RSA_generate_key_ex(private, bits, f4, NULL)) { ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; } *rsap = private; private = NULL; ret = 0; out: if (private != NULL) RSA_free(private); if (f4 != NULL) BN_free(f4); return ret; } static int dsa_generate_private_key(u_int bits, DSA **dsap) { DSA *private; int ret = SSH_ERR_INTERNAL_ERROR; if (dsap == NULL || bits != 1024) return SSH_ERR_INVALID_ARGUMENT; if ((private = DSA_new()) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; } *dsap = NULL; if (!DSA_generate_parameters_ex(private, bits, NULL, 0, NULL, NULL, NULL) || !DSA_generate_key(private)) { ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; } *dsap = private; private = NULL; ret = 0; out: if (private != NULL) DSA_free(private); return ret; } # ifdef OPENSSL_HAS_ECC int sshkey_ecdsa_key_to_nid(EC_KEY *k) { EC_GROUP *eg; int nids[] = { NID_X9_62_prime256v1, NID_secp384r1, # ifdef OPENSSL_HAS_NISTP521 NID_secp521r1, # endif /* OPENSSL_HAS_NISTP521 */ -1 }; int nid; u_int i; BN_CTX *bnctx; const EC_GROUP *g = EC_KEY_get0_group(k); /* * The group may be stored in a ASN.1 encoded private key in one of two * ways: as a "named group", which is reconstituted by ASN.1 object ID * or explicit group parameters encoded into the key blob. Only the * "named group" case sets the group NID for us, but we can figure * it out for the other case by comparing against all the groups that * are supported. */ if ((nid = EC_GROUP_get_curve_name(g)) > 0) return nid; if ((bnctx = BN_CTX_new()) == NULL) return -1; for (i = 0; nids[i] != -1; i++) { if ((eg = EC_GROUP_new_by_curve_name(nids[i])) == NULL) { BN_CTX_free(bnctx); return -1; } if (EC_GROUP_cmp(g, eg, bnctx) == 0) break; EC_GROUP_free(eg); } BN_CTX_free(bnctx); if (nids[i] != -1) { /* Use the group with the NID attached */ EC_GROUP_set_asn1_flag(eg, OPENSSL_EC_NAMED_CURVE); if (EC_KEY_set_group(k, eg) != 1) { EC_GROUP_free(eg); return -1; } } return nids[i]; } static int ecdsa_generate_private_key(u_int bits, int *nid, EC_KEY **ecdsap) { EC_KEY *private; int ret = SSH_ERR_INTERNAL_ERROR; if (nid == NULL || ecdsap == NULL || (*nid = sshkey_ecdsa_bits_to_nid(bits)) == -1) return SSH_ERR_INVALID_ARGUMENT; *ecdsap = NULL; if ((private = EC_KEY_new_by_curve_name(*nid)) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; } if (EC_KEY_generate_key(private) != 1) { ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; } EC_KEY_set_asn1_flag(private, OPENSSL_EC_NAMED_CURVE); *ecdsap = private; private = NULL; ret = 0; out: if (private != NULL) EC_KEY_free(private); return ret; } # endif /* OPENSSL_HAS_ECC */ #endif /* WITH_OPENSSL */ int sshkey_generate(int type, u_int bits, struct sshkey **keyp) { struct sshkey *k; int ret = SSH_ERR_INTERNAL_ERROR; if (keyp == NULL) return SSH_ERR_INVALID_ARGUMENT; *keyp = NULL; if ((k = sshkey_new(KEY_UNSPEC)) == NULL) return SSH_ERR_ALLOC_FAIL; switch (type) { case KEY_ED25519: if ((k->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL || (k->ed25519_sk = malloc(ED25519_SK_SZ)) == NULL) { ret = SSH_ERR_ALLOC_FAIL; break; } crypto_sign_ed25519_keypair(k->ed25519_pk, k->ed25519_sk); ret = 0; break; #ifdef WITH_OPENSSL case KEY_DSA: ret = dsa_generate_private_key(bits, &k->dsa); break; # ifdef OPENSSL_HAS_ECC case KEY_ECDSA: ret = ecdsa_generate_private_key(bits, &k->ecdsa_nid, &k->ecdsa); break; # endif /* OPENSSL_HAS_ECC */ case KEY_RSA: case KEY_RSA1: ret = rsa_generate_private_key(bits, &k->rsa); break; #endif /* WITH_OPENSSL */ default: ret = SSH_ERR_INVALID_ARGUMENT; } if (ret == 0) { k->type = type; *keyp = k; } else sshkey_free(k); return ret; } int sshkey_cert_copy(const struct sshkey *from_key, struct sshkey *to_key) { u_int i; const struct sshkey_cert *from; struct sshkey_cert *to; int ret = SSH_ERR_INTERNAL_ERROR; if (to_key->cert != NULL) { cert_free(to_key->cert); to_key->cert = NULL; } if ((from = from_key->cert) == NULL) return SSH_ERR_INVALID_ARGUMENT; if ((to = to_key->cert = cert_new()) == NULL) return SSH_ERR_ALLOC_FAIL; if ((ret = sshbuf_putb(to->certblob, from->certblob)) != 0 || (ret = sshbuf_putb(to->critical, from->critical)) != 0 || (ret = sshbuf_putb(to->extensions, from->extensions)) != 0) return ret; to->serial = from->serial; to->type = from->type; if (from->key_id == NULL) to->key_id = NULL; else if ((to->key_id = strdup(from->key_id)) == NULL) return SSH_ERR_ALLOC_FAIL; to->valid_after = from->valid_after; to->valid_before = from->valid_before; if (from->signature_key == NULL) to->signature_key = NULL; else if ((ret = sshkey_from_private(from->signature_key, &to->signature_key)) != 0) return ret; if (from->nprincipals > SSHKEY_CERT_MAX_PRINCIPALS) return SSH_ERR_INVALID_ARGUMENT; if (from->nprincipals > 0) { if ((to->principals = calloc(from->nprincipals, sizeof(*to->principals))) == NULL) return SSH_ERR_ALLOC_FAIL; for (i = 0; i < from->nprincipals; i++) { to->principals[i] = strdup(from->principals[i]); if (to->principals[i] == NULL) { to->nprincipals = i; return SSH_ERR_ALLOC_FAIL; } } } to->nprincipals = from->nprincipals; return 0; } int sshkey_from_private(const struct sshkey *k, struct sshkey **pkp) { struct sshkey *n = NULL; int ret = SSH_ERR_INTERNAL_ERROR; *pkp = NULL; switch (k->type) { #ifdef WITH_OPENSSL case KEY_DSA: case KEY_DSA_CERT: if ((n = sshkey_new(k->type)) == NULL) return SSH_ERR_ALLOC_FAIL; if ((BN_copy(n->dsa->p, k->dsa->p) == NULL) || (BN_copy(n->dsa->q, k->dsa->q) == NULL) || (BN_copy(n->dsa->g, k->dsa->g) == NULL) || (BN_copy(n->dsa->pub_key, k->dsa->pub_key) == NULL)) { sshkey_free(n); return SSH_ERR_ALLOC_FAIL; } break; # ifdef OPENSSL_HAS_ECC case KEY_ECDSA: case KEY_ECDSA_CERT: if ((n = sshkey_new(k->type)) == NULL) return SSH_ERR_ALLOC_FAIL; n->ecdsa_nid = k->ecdsa_nid; n->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid); if (n->ecdsa == NULL) { sshkey_free(n); return SSH_ERR_ALLOC_FAIL; } if (EC_KEY_set_public_key(n->ecdsa, EC_KEY_get0_public_key(k->ecdsa)) != 1) { sshkey_free(n); return SSH_ERR_LIBCRYPTO_ERROR; } break; # endif /* OPENSSL_HAS_ECC */ case KEY_RSA: case KEY_RSA1: case KEY_RSA_CERT: if ((n = sshkey_new(k->type)) == NULL) return SSH_ERR_ALLOC_FAIL; if ((BN_copy(n->rsa->n, k->rsa->n) == NULL) || (BN_copy(n->rsa->e, k->rsa->e) == NULL)) { sshkey_free(n); return SSH_ERR_ALLOC_FAIL; } break; #endif /* WITH_OPENSSL */ case KEY_ED25519: case KEY_ED25519_CERT: if ((n = sshkey_new(k->type)) == NULL) return SSH_ERR_ALLOC_FAIL; if (k->ed25519_pk != NULL) { if ((n->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) { sshkey_free(n); return SSH_ERR_ALLOC_FAIL; } memcpy(n->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ); } break; default: return SSH_ERR_KEY_TYPE_UNKNOWN; } if (sshkey_is_cert(k)) { if ((ret = sshkey_cert_copy(k, n)) != 0) { sshkey_free(n); return ret; } } *pkp = n; return 0; } static int cert_parse(struct sshbuf *b, struct sshkey *key, struct sshbuf *certbuf) { struct sshbuf *principals = NULL, *crit = NULL; struct sshbuf *exts = NULL, *ca = NULL; u_char *sig = NULL; size_t signed_len = 0, slen = 0, kidlen = 0; int ret = SSH_ERR_INTERNAL_ERROR; /* Copy the entire key blob for verification and later serialisation */ if ((ret = sshbuf_putb(key->cert->certblob, certbuf)) != 0) return ret; /* Parse body of certificate up to signature */ if ((ret = sshbuf_get_u64(b, &key->cert->serial)) != 0 || (ret = sshbuf_get_u32(b, &key->cert->type)) != 0 || (ret = sshbuf_get_cstring(b, &key->cert->key_id, &kidlen)) != 0 || (ret = sshbuf_froms(b, &principals)) != 0 || (ret = sshbuf_get_u64(b, &key->cert->valid_after)) != 0 || (ret = sshbuf_get_u64(b, &key->cert->valid_before)) != 0 || (ret = sshbuf_froms(b, &crit)) != 0 || (ret = sshbuf_froms(b, &exts)) != 0 || (ret = sshbuf_get_string_direct(b, NULL, NULL)) != 0 || (ret = sshbuf_froms(b, &ca)) != 0) { /* XXX debug print error for ret */ ret = SSH_ERR_INVALID_FORMAT; goto out; } /* Signature is left in the buffer so we can calculate this length */ signed_len = sshbuf_len(key->cert->certblob) - sshbuf_len(b); if ((ret = sshbuf_get_string(b, &sig, &slen)) != 0) { ret = SSH_ERR_INVALID_FORMAT; goto out; } if (key->cert->type != SSH2_CERT_TYPE_USER && key->cert->type != SSH2_CERT_TYPE_HOST) { ret = SSH_ERR_KEY_CERT_UNKNOWN_TYPE; goto out; } /* Parse principals section */ while (sshbuf_len(principals) > 0) { char *principal = NULL; char **oprincipals = NULL; if (key->cert->nprincipals >= SSHKEY_CERT_MAX_PRINCIPALS) { ret = SSH_ERR_INVALID_FORMAT; goto out; } if ((ret = sshbuf_get_cstring(principals, &principal, NULL)) != 0) { ret = SSH_ERR_INVALID_FORMAT; goto out; } oprincipals = key->cert->principals; key->cert->principals = reallocarray(key->cert->principals, key->cert->nprincipals + 1, sizeof(*key->cert->principals)); if (key->cert->principals == NULL) { free(principal); key->cert->principals = oprincipals; ret = SSH_ERR_ALLOC_FAIL; goto out; } key->cert->principals[key->cert->nprincipals++] = principal; } /* * Stash a copies of the critical options and extensions sections * for later use. */ if ((ret = sshbuf_putb(key->cert->critical, crit)) != 0 || (exts != NULL && (ret = sshbuf_putb(key->cert->extensions, exts)) != 0)) goto out; /* * Validate critical options and extensions sections format. */ while (sshbuf_len(crit) != 0) { if ((ret = sshbuf_get_string_direct(crit, NULL, NULL)) != 0 || (ret = sshbuf_get_string_direct(crit, NULL, NULL)) != 0) { sshbuf_reset(key->cert->critical); ret = SSH_ERR_INVALID_FORMAT; goto out; } } while (exts != NULL && sshbuf_len(exts) != 0) { if ((ret = sshbuf_get_string_direct(exts, NULL, NULL)) != 0 || (ret = sshbuf_get_string_direct(exts, NULL, NULL)) != 0) { sshbuf_reset(key->cert->extensions); ret = SSH_ERR_INVALID_FORMAT; goto out; } } /* Parse CA key and check signature */ if (sshkey_from_blob_internal(ca, &key->cert->signature_key, 0) != 0) { ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; goto out; } if (!sshkey_type_is_valid_ca(key->cert->signature_key->type)) { ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; goto out; } if ((ret = sshkey_verify(key->cert->signature_key, sig, slen, sshbuf_ptr(key->cert->certblob), signed_len, 0)) != 0) goto out; /* Success */ ret = 0; out: sshbuf_free(ca); sshbuf_free(crit); sshbuf_free(exts); sshbuf_free(principals); free(sig); return ret; } static int sshkey_from_blob_internal(struct sshbuf *b, struct sshkey **keyp, int allow_cert) { int type, ret = SSH_ERR_INTERNAL_ERROR; char *ktype = NULL, *curve = NULL; struct sshkey *key = NULL; size_t len; u_char *pk = NULL; struct sshbuf *copy; #if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) EC_POINT *q = NULL; #endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */ #ifdef DEBUG_PK /* XXX */ sshbuf_dump(b, stderr); #endif if (keyp != NULL) *keyp = NULL; if ((copy = sshbuf_fromb(b)) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; } if (sshbuf_get_cstring(b, &ktype, NULL) != 0) { ret = SSH_ERR_INVALID_FORMAT; goto out; } type = sshkey_type_from_name(ktype); if (!allow_cert && sshkey_type_is_cert(type)) { ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; goto out; } switch (type) { #ifdef WITH_OPENSSL case KEY_RSA_CERT: /* Skip nonce */ if (sshbuf_get_string_direct(b, NULL, NULL) != 0) { ret = SSH_ERR_INVALID_FORMAT; goto out; } /* FALLTHROUGH */ case KEY_RSA: if ((key = sshkey_new(type)) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; } if (sshbuf_get_bignum2(b, key->rsa->e) != 0 || sshbuf_get_bignum2(b, key->rsa->n) != 0) { ret = SSH_ERR_INVALID_FORMAT; goto out; } #ifdef DEBUG_PK RSA_print_fp(stderr, key->rsa, 8); #endif break; case KEY_DSA_CERT: /* Skip nonce */ if (sshbuf_get_string_direct(b, NULL, NULL) != 0) { ret = SSH_ERR_INVALID_FORMAT; goto out; } /* FALLTHROUGH */ case KEY_DSA: if ((key = sshkey_new(type)) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; } if (sshbuf_get_bignum2(b, key->dsa->p) != 0 || sshbuf_get_bignum2(b, key->dsa->q) != 0 || sshbuf_get_bignum2(b, key->dsa->g) != 0 || sshbuf_get_bignum2(b, key->dsa->pub_key) != 0) { ret = SSH_ERR_INVALID_FORMAT; goto out; } #ifdef DEBUG_PK DSA_print_fp(stderr, key->dsa, 8); #endif break; case KEY_ECDSA_CERT: /* Skip nonce */ if (sshbuf_get_string_direct(b, NULL, NULL) != 0) { ret = SSH_ERR_INVALID_FORMAT; goto out; } /* FALLTHROUGH */ # ifdef OPENSSL_HAS_ECC case KEY_ECDSA: if ((key = sshkey_new(type)) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; } key->ecdsa_nid = sshkey_ecdsa_nid_from_name(ktype); if (sshbuf_get_cstring(b, &curve, NULL) != 0) { ret = SSH_ERR_INVALID_FORMAT; goto out; } if (key->ecdsa_nid != sshkey_curve_name_to_nid(curve)) { ret = SSH_ERR_EC_CURVE_MISMATCH; goto out; } if (key->ecdsa != NULL) EC_KEY_free(key->ecdsa); if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL) { ret = SSH_ERR_EC_CURVE_INVALID; goto out; } if ((q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; } if (sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa)) != 0) { ret = SSH_ERR_INVALID_FORMAT; goto out; } if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa), q) != 0) { ret = SSH_ERR_KEY_INVALID_EC_VALUE; goto out; } if (EC_KEY_set_public_key(key->ecdsa, q) != 1) { /* XXX assume it is a allocation error */ ret = SSH_ERR_ALLOC_FAIL; goto out; } #ifdef DEBUG_PK sshkey_dump_ec_point(EC_KEY_get0_group(key->ecdsa), q); #endif break; # endif /* OPENSSL_HAS_ECC */ #endif /* WITH_OPENSSL */ case KEY_ED25519_CERT: /* Skip nonce */ if (sshbuf_get_string_direct(b, NULL, NULL) != 0) { ret = SSH_ERR_INVALID_FORMAT; goto out; } /* FALLTHROUGH */ case KEY_ED25519: if ((ret = sshbuf_get_string(b, &pk, &len)) != 0) goto out; if (len != ED25519_PK_SZ) { ret = SSH_ERR_INVALID_FORMAT; goto out; } if ((key = sshkey_new(type)) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; } key->ed25519_pk = pk; pk = NULL; break; case KEY_UNSPEC: if ((key = sshkey_new(type)) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; } break; default: ret = SSH_ERR_KEY_TYPE_UNKNOWN; goto out; } /* Parse certificate potion */ if (sshkey_is_cert(key) && (ret = cert_parse(b, key, copy)) != 0) goto out; if (key != NULL && sshbuf_len(b) != 0) { ret = SSH_ERR_INVALID_FORMAT; goto out; } ret = 0; if (keyp != NULL) { *keyp = key; key = NULL; } out: sshbuf_free(copy); sshkey_free(key); free(ktype); free(curve); free(pk); #if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) if (q != NULL) EC_POINT_free(q); #endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */ return ret; } int sshkey_from_blob(const u_char *blob, size_t blen, struct sshkey **keyp) { struct sshbuf *b; int r; if ((b = sshbuf_from(blob, blen)) == NULL) return SSH_ERR_ALLOC_FAIL; r = sshkey_from_blob_internal(b, keyp, 1); sshbuf_free(b); return r; } int sshkey_fromb(struct sshbuf *b, struct sshkey **keyp) { return sshkey_from_blob_internal(b, keyp, 1); } int sshkey_froms(struct sshbuf *buf, struct sshkey **keyp) { struct sshbuf *b; int r; if ((r = sshbuf_froms(buf, &b)) != 0) return r; r = sshkey_from_blob_internal(b, keyp, 1); sshbuf_free(b); return r; } int sshkey_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, const char *alg, u_int compat) { if (sigp != NULL) *sigp = NULL; if (lenp != NULL) *lenp = 0; if (datalen > SSH_KEY_MAX_SIGN_DATA_SIZE) return SSH_ERR_INVALID_ARGUMENT; switch (key->type) { #ifdef WITH_OPENSSL case KEY_DSA_CERT: case KEY_DSA: return ssh_dss_sign(key, sigp, lenp, data, datalen, compat); # ifdef OPENSSL_HAS_ECC case KEY_ECDSA_CERT: case KEY_ECDSA: return ssh_ecdsa_sign(key, sigp, lenp, data, datalen, compat); # endif /* OPENSSL_HAS_ECC */ case KEY_RSA_CERT: case KEY_RSA: return ssh_rsa_sign(key, sigp, lenp, data, datalen, alg); #endif /* WITH_OPENSSL */ case KEY_ED25519: case KEY_ED25519_CERT: return ssh_ed25519_sign(key, sigp, lenp, data, datalen, compat); default: return SSH_ERR_KEY_TYPE_UNKNOWN; } } /* * ssh_key_verify returns 0 for a correct signature and < 0 on error. */ int sshkey_verify(const struct sshkey *key, const u_char *sig, size_t siglen, const u_char *data, size_t dlen, u_int compat) { if (siglen == 0 || dlen > SSH_KEY_MAX_SIGN_DATA_SIZE) return SSH_ERR_INVALID_ARGUMENT; switch (key->type) { #ifdef WITH_OPENSSL case KEY_DSA_CERT: case KEY_DSA: return ssh_dss_verify(key, sig, siglen, data, dlen, compat); # ifdef OPENSSL_HAS_ECC case KEY_ECDSA_CERT: case KEY_ECDSA: return ssh_ecdsa_verify(key, sig, siglen, data, dlen, compat); # endif /* OPENSSL_HAS_ECC */ case KEY_RSA_CERT: case KEY_RSA: return ssh_rsa_verify(key, sig, siglen, data, dlen); #endif /* WITH_OPENSSL */ case KEY_ED25519: case KEY_ED25519_CERT: return ssh_ed25519_verify(key, sig, siglen, data, dlen, compat); default: return SSH_ERR_KEY_TYPE_UNKNOWN; } } /* Converts a private to a public key */ int sshkey_demote(const struct sshkey *k, struct sshkey **dkp) { struct sshkey *pk; int ret = SSH_ERR_INTERNAL_ERROR; *dkp = NULL; if ((pk = calloc(1, sizeof(*pk))) == NULL) return SSH_ERR_ALLOC_FAIL; pk->type = k->type; pk->flags = k->flags; pk->ecdsa_nid = k->ecdsa_nid; pk->dsa = NULL; pk->ecdsa = NULL; pk->rsa = NULL; pk->ed25519_pk = NULL; pk->ed25519_sk = NULL; switch (k->type) { #ifdef WITH_OPENSSL case KEY_RSA_CERT: if ((ret = sshkey_cert_copy(k, pk)) != 0) goto fail; /* FALLTHROUGH */ case KEY_RSA1: case KEY_RSA: if ((pk->rsa = RSA_new()) == NULL || (pk->rsa->e = BN_dup(k->rsa->e)) == NULL || (pk->rsa->n = BN_dup(k->rsa->n)) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto fail; } break; case KEY_DSA_CERT: if ((ret = sshkey_cert_copy(k, pk)) != 0) goto fail; /* FALLTHROUGH */ case KEY_DSA: if ((pk->dsa = DSA_new()) == NULL || (pk->dsa->p = BN_dup(k->dsa->p)) == NULL || (pk->dsa->q = BN_dup(k->dsa->q)) == NULL || (pk->dsa->g = BN_dup(k->dsa->g)) == NULL || (pk->dsa->pub_key = BN_dup(k->dsa->pub_key)) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto fail; } break; case KEY_ECDSA_CERT: if ((ret = sshkey_cert_copy(k, pk)) != 0) goto fail; /* FALLTHROUGH */ # ifdef OPENSSL_HAS_ECC case KEY_ECDSA: pk->ecdsa = EC_KEY_new_by_curve_name(pk->ecdsa_nid); if (pk->ecdsa == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto fail; } if (EC_KEY_set_public_key(pk->ecdsa, EC_KEY_get0_public_key(k->ecdsa)) != 1) { ret = SSH_ERR_LIBCRYPTO_ERROR; goto fail; } break; # endif /* OPENSSL_HAS_ECC */ #endif /* WITH_OPENSSL */ case KEY_ED25519_CERT: if ((ret = sshkey_cert_copy(k, pk)) != 0) goto fail; /* FALLTHROUGH */ case KEY_ED25519: if (k->ed25519_pk != NULL) { if ((pk->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto fail; } memcpy(pk->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ); } break; default: ret = SSH_ERR_KEY_TYPE_UNKNOWN; fail: sshkey_free(pk); return ret; } *dkp = pk; return 0; } /* Convert a plain key to their _CERT equivalent */ int sshkey_to_certified(struct sshkey *k) { int newtype; switch (k->type) { #ifdef WITH_OPENSSL case KEY_RSA: newtype = KEY_RSA_CERT; break; case KEY_DSA: newtype = KEY_DSA_CERT; break; case KEY_ECDSA: newtype = KEY_ECDSA_CERT; break; #endif /* WITH_OPENSSL */ case KEY_ED25519: newtype = KEY_ED25519_CERT; break; default: return SSH_ERR_INVALID_ARGUMENT; } if ((k->cert = cert_new()) == NULL) return SSH_ERR_ALLOC_FAIL; k->type = newtype; return 0; } /* Convert a certificate to its raw key equivalent */ int sshkey_drop_cert(struct sshkey *k) { if (!sshkey_type_is_cert(k->type)) return SSH_ERR_KEY_TYPE_UNKNOWN; cert_free(k->cert); k->cert = NULL; k->type = sshkey_type_plain(k->type); return 0; } /* Sign a certified key, (re-)generating the signed certblob. */ int sshkey_certify(struct sshkey *k, struct sshkey *ca, const char *alg) { struct sshbuf *principals = NULL; u_char *ca_blob = NULL, *sig_blob = NULL, nonce[32]; size_t i, ca_len, sig_len; int ret = SSH_ERR_INTERNAL_ERROR; struct sshbuf *cert; if (k == NULL || k->cert == NULL || k->cert->certblob == NULL || ca == NULL) return SSH_ERR_INVALID_ARGUMENT; if (!sshkey_is_cert(k)) return SSH_ERR_KEY_TYPE_UNKNOWN; if (!sshkey_type_is_valid_ca(ca->type)) return SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; if ((ret = sshkey_to_blob(ca, &ca_blob, &ca_len)) != 0) return SSH_ERR_KEY_CERT_INVALID_SIGN_KEY; cert = k->cert->certblob; /* for readability */ sshbuf_reset(cert); if ((ret = sshbuf_put_cstring(cert, sshkey_ssh_name(k))) != 0) goto out; /* -v01 certs put nonce first */ arc4random_buf(&nonce, sizeof(nonce)); if ((ret = sshbuf_put_string(cert, nonce, sizeof(nonce))) != 0) goto out; /* XXX this substantially duplicates to_blob(); refactor */ switch (k->type) { #ifdef WITH_OPENSSL case KEY_DSA_CERT: if ((ret = sshbuf_put_bignum2(cert, k->dsa->p)) != 0 || (ret = sshbuf_put_bignum2(cert, k->dsa->q)) != 0 || (ret = sshbuf_put_bignum2(cert, k->dsa->g)) != 0 || (ret = sshbuf_put_bignum2(cert, k->dsa->pub_key)) != 0) goto out; break; # ifdef OPENSSL_HAS_ECC case KEY_ECDSA_CERT: if ((ret = sshbuf_put_cstring(cert, sshkey_curve_nid_to_name(k->ecdsa_nid))) != 0 || (ret = sshbuf_put_ec(cert, EC_KEY_get0_public_key(k->ecdsa), EC_KEY_get0_group(k->ecdsa))) != 0) goto out; break; # endif /* OPENSSL_HAS_ECC */ case KEY_RSA_CERT: if ((ret = sshbuf_put_bignum2(cert, k->rsa->e)) != 0 || (ret = sshbuf_put_bignum2(cert, k->rsa->n)) != 0) goto out; break; #endif /* WITH_OPENSSL */ case KEY_ED25519_CERT: if ((ret = sshbuf_put_string(cert, k->ed25519_pk, ED25519_PK_SZ)) != 0) goto out; break; default: ret = SSH_ERR_INVALID_ARGUMENT; goto out; } if ((ret = sshbuf_put_u64(cert, k->cert->serial)) != 0 || (ret = sshbuf_put_u32(cert, k->cert->type)) != 0 || (ret = sshbuf_put_cstring(cert, k->cert->key_id)) != 0) goto out; if ((principals = sshbuf_new()) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; } for (i = 0; i < k->cert->nprincipals; i++) { if ((ret = sshbuf_put_cstring(principals, k->cert->principals[i])) != 0) goto out; } if ((ret = sshbuf_put_stringb(cert, principals)) != 0 || (ret = sshbuf_put_u64(cert, k->cert->valid_after)) != 0 || (ret = sshbuf_put_u64(cert, k->cert->valid_before)) != 0 || (ret = sshbuf_put_stringb(cert, k->cert->critical)) != 0 || (ret = sshbuf_put_stringb(cert, k->cert->extensions)) != 0 || (ret = sshbuf_put_string(cert, NULL, 0)) != 0 || /* Reserved */ (ret = sshbuf_put_string(cert, ca_blob, ca_len)) != 0) goto out; /* Sign the whole mess */ if ((ret = sshkey_sign(ca, &sig_blob, &sig_len, sshbuf_ptr(cert), sshbuf_len(cert), alg, 0)) != 0) goto out; /* Append signature and we are done */ if ((ret = sshbuf_put_string(cert, sig_blob, sig_len)) != 0) goto out; ret = 0; out: if (ret != 0) sshbuf_reset(cert); free(sig_blob); free(ca_blob); sshbuf_free(principals); return ret; } int sshkey_cert_check_authority(const struct sshkey *k, int want_host, int require_principal, const char *name, const char **reason) { u_int i, principal_matches; time_t now = time(NULL); if (reason != NULL) *reason = NULL; if (want_host) { if (k->cert->type != SSH2_CERT_TYPE_HOST) { *reason = "Certificate invalid: not a host certificate"; return SSH_ERR_KEY_CERT_INVALID; } } else { if (k->cert->type != SSH2_CERT_TYPE_USER) { *reason = "Certificate invalid: not a user certificate"; return SSH_ERR_KEY_CERT_INVALID; } } if (now < 0) { /* yikes - system clock before epoch! */ *reason = "Certificate invalid: not yet valid"; return SSH_ERR_KEY_CERT_INVALID; } if ((u_int64_t)now < k->cert->valid_after) { *reason = "Certificate invalid: not yet valid"; return SSH_ERR_KEY_CERT_INVALID; } if ((u_int64_t)now >= k->cert->valid_before) { *reason = "Certificate invalid: expired"; return SSH_ERR_KEY_CERT_INVALID; } if (k->cert->nprincipals == 0) { if (require_principal) { *reason = "Certificate lacks principal list"; return SSH_ERR_KEY_CERT_INVALID; } } else if (name != NULL) { principal_matches = 0; for (i = 0; i < k->cert->nprincipals; i++) { if (strcmp(name, k->cert->principals[i]) == 0) { principal_matches = 1; break; } } if (!principal_matches) { *reason = "Certificate invalid: name is not a listed " "principal"; return SSH_ERR_KEY_CERT_INVALID; } } return 0; } size_t sshkey_format_cert_validity(const struct sshkey_cert *cert, char *s, size_t l) { char from[32], to[32], ret[64]; time_t tt; struct tm *tm; *from = *to = '\0'; if (cert->valid_after == 0 && cert->valid_before == 0xffffffffffffffffULL) return strlcpy(s, "forever", l); if (cert->valid_after != 0) { /* XXX revisit INT_MAX in 2038 :) */ tt = cert->valid_after > INT_MAX ? INT_MAX : cert->valid_after; tm = localtime(&tt); strftime(from, sizeof(from), "%Y-%m-%dT%H:%M:%S", tm); } if (cert->valid_before != 0xffffffffffffffffULL) { /* XXX revisit INT_MAX in 2038 :) */ tt = cert->valid_before > INT_MAX ? INT_MAX : cert->valid_before; tm = localtime(&tt); strftime(to, sizeof(to), "%Y-%m-%dT%H:%M:%S", tm); } if (cert->valid_after == 0) snprintf(ret, sizeof(ret), "before %s", to); else if (cert->valid_before == 0xffffffffffffffffULL) snprintf(ret, sizeof(ret), "after %s", from); else snprintf(ret, sizeof(ret), "from %s to %s", from, to); return strlcpy(s, ret, l); } int sshkey_private_serialize(const struct sshkey *key, struct sshbuf *b) { int r = SSH_ERR_INTERNAL_ERROR; if ((r = sshbuf_put_cstring(b, sshkey_ssh_name(key))) != 0) goto out; switch (key->type) { #ifdef WITH_OPENSSL case KEY_RSA: if ((r = sshbuf_put_bignum2(b, key->rsa->n)) != 0 || (r = sshbuf_put_bignum2(b, key->rsa->e)) != 0 || (r = sshbuf_put_bignum2(b, key->rsa->d)) != 0 || (r = sshbuf_put_bignum2(b, key->rsa->iqmp)) != 0 || (r = sshbuf_put_bignum2(b, key->rsa->p)) != 0 || (r = sshbuf_put_bignum2(b, key->rsa->q)) != 0) goto out; break; case KEY_RSA_CERT: if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || (r = sshbuf_put_bignum2(b, key->rsa->d)) != 0 || (r = sshbuf_put_bignum2(b, key->rsa->iqmp)) != 0 || (r = sshbuf_put_bignum2(b, key->rsa->p)) != 0 || (r = sshbuf_put_bignum2(b, key->rsa->q)) != 0) goto out; break; case KEY_DSA: if ((r = sshbuf_put_bignum2(b, key->dsa->p)) != 0 || (r = sshbuf_put_bignum2(b, key->dsa->q)) != 0 || (r = sshbuf_put_bignum2(b, key->dsa->g)) != 0 || (r = sshbuf_put_bignum2(b, key->dsa->pub_key)) != 0 || (r = sshbuf_put_bignum2(b, key->dsa->priv_key)) != 0) goto out; break; case KEY_DSA_CERT: if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || (r = sshbuf_put_bignum2(b, key->dsa->priv_key)) != 0) goto out; break; # ifdef OPENSSL_HAS_ECC case KEY_ECDSA: if ((r = sshbuf_put_cstring(b, sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 || (r = sshbuf_put_eckey(b, key->ecdsa)) != 0 || (r = sshbuf_put_bignum2(b, EC_KEY_get0_private_key(key->ecdsa))) != 0) goto out; break; case KEY_ECDSA_CERT: if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || (r = sshbuf_put_bignum2(b, EC_KEY_get0_private_key(key->ecdsa))) != 0) goto out; break; # endif /* OPENSSL_HAS_ECC */ #endif /* WITH_OPENSSL */ case KEY_ED25519: if ((r = sshbuf_put_string(b, key->ed25519_pk, ED25519_PK_SZ)) != 0 || (r = sshbuf_put_string(b, key->ed25519_sk, ED25519_SK_SZ)) != 0) goto out; break; case KEY_ED25519_CERT: if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 || (r = sshbuf_put_string(b, key->ed25519_pk, ED25519_PK_SZ)) != 0 || (r = sshbuf_put_string(b, key->ed25519_sk, ED25519_SK_SZ)) != 0) goto out; break; default: r = SSH_ERR_INVALID_ARGUMENT; goto out; } /* success */ r = 0; out: return r; } int sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp) { char *tname = NULL, *curve = NULL; struct sshkey *k = NULL; size_t pklen = 0, sklen = 0; int type, r = SSH_ERR_INTERNAL_ERROR; u_char *ed25519_pk = NULL, *ed25519_sk = NULL; #ifdef WITH_OPENSSL BIGNUM *exponent = NULL; #endif /* WITH_OPENSSL */ if (kp != NULL) *kp = NULL; if ((r = sshbuf_get_cstring(buf, &tname, NULL)) != 0) goto out; type = sshkey_type_from_name(tname); switch (type) { #ifdef WITH_OPENSSL case KEY_DSA: if ((k = sshkey_new_private(type)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = sshbuf_get_bignum2(buf, k->dsa->p)) != 0 || (r = sshbuf_get_bignum2(buf, k->dsa->q)) != 0 || (r = sshbuf_get_bignum2(buf, k->dsa->g)) != 0 || (r = sshbuf_get_bignum2(buf, k->dsa->pub_key)) != 0 || (r = sshbuf_get_bignum2(buf, k->dsa->priv_key)) != 0) goto out; break; case KEY_DSA_CERT: if ((r = sshkey_froms(buf, &k)) != 0 || (r = sshkey_add_private(k)) != 0 || (r = sshbuf_get_bignum2(buf, k->dsa->priv_key)) != 0) goto out; break; # ifdef OPENSSL_HAS_ECC case KEY_ECDSA: if ((k = sshkey_new_private(type)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((k->ecdsa_nid = sshkey_ecdsa_nid_from_name(tname)) == -1) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } if ((r = sshbuf_get_cstring(buf, &curve, NULL)) != 0) goto out; if (k->ecdsa_nid != sshkey_curve_name_to_nid(curve)) { r = SSH_ERR_EC_CURVE_MISMATCH; goto out; } k->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid); if (k->ecdsa == NULL || (exponent = BN_new()) == NULL) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } if ((r = sshbuf_get_eckey(buf, k->ecdsa)) != 0 || (r = sshbuf_get_bignum2(buf, exponent))) goto out; if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } if ((r = sshkey_ec_validate_public(EC_KEY_get0_group(k->ecdsa), EC_KEY_get0_public_key(k->ecdsa))) != 0 || (r = sshkey_ec_validate_private(k->ecdsa)) != 0) goto out; break; case KEY_ECDSA_CERT: if ((exponent = BN_new()) == NULL) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } if ((r = sshkey_froms(buf, &k)) != 0 || (r = sshkey_add_private(k)) != 0 || (r = sshbuf_get_bignum2(buf, exponent)) != 0) goto out; if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } if ((r = sshkey_ec_validate_public(EC_KEY_get0_group(k->ecdsa), EC_KEY_get0_public_key(k->ecdsa))) != 0 || (r = sshkey_ec_validate_private(k->ecdsa)) != 0) goto out; break; # endif /* OPENSSL_HAS_ECC */ case KEY_RSA: if ((k = sshkey_new_private(type)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = sshbuf_get_bignum2(buf, k->rsa->n)) != 0 || (r = sshbuf_get_bignum2(buf, k->rsa->e)) != 0 || (r = sshbuf_get_bignum2(buf, k->rsa->d)) != 0 || (r = sshbuf_get_bignum2(buf, k->rsa->iqmp)) != 0 || (r = sshbuf_get_bignum2(buf, k->rsa->p)) != 0 || (r = sshbuf_get_bignum2(buf, k->rsa->q)) != 0 || (r = rsa_generate_additional_parameters(k->rsa)) != 0) goto out; break; case KEY_RSA_CERT: if ((r = sshkey_froms(buf, &k)) != 0 || (r = sshkey_add_private(k)) != 0 || (r = sshbuf_get_bignum2(buf, k->rsa->d)) != 0 || (r = sshbuf_get_bignum2(buf, k->rsa->iqmp)) != 0 || (r = sshbuf_get_bignum2(buf, k->rsa->p)) != 0 || (r = sshbuf_get_bignum2(buf, k->rsa->q)) != 0 || (r = rsa_generate_additional_parameters(k->rsa)) != 0) goto out; break; #endif /* WITH_OPENSSL */ case KEY_ED25519: if ((k = sshkey_new_private(type)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = sshbuf_get_string(buf, &ed25519_pk, &pklen)) != 0 || (r = sshbuf_get_string(buf, &ed25519_sk, &sklen)) != 0) goto out; if (pklen != ED25519_PK_SZ || sklen != ED25519_SK_SZ) { r = SSH_ERR_INVALID_FORMAT; goto out; } k->ed25519_pk = ed25519_pk; k->ed25519_sk = ed25519_sk; ed25519_pk = ed25519_sk = NULL; break; case KEY_ED25519_CERT: if ((r = sshkey_froms(buf, &k)) != 0 || (r = sshkey_add_private(k)) != 0 || (r = sshbuf_get_string(buf, &ed25519_pk, &pklen)) != 0 || (r = sshbuf_get_string(buf, &ed25519_sk, &sklen)) != 0) goto out; if (pklen != ED25519_PK_SZ || sklen != ED25519_SK_SZ) { r = SSH_ERR_INVALID_FORMAT; goto out; } k->ed25519_pk = ed25519_pk; k->ed25519_sk = ed25519_sk; ed25519_pk = ed25519_sk = NULL; break; default: r = SSH_ERR_KEY_TYPE_UNKNOWN; goto out; } #ifdef WITH_OPENSSL /* enable blinding */ switch (k->type) { case KEY_RSA: case KEY_RSA_CERT: case KEY_RSA1: if (RSA_blinding_on(k->rsa, NULL) != 1) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } break; } #endif /* WITH_OPENSSL */ /* success */ r = 0; if (kp != NULL) { *kp = k; k = NULL; } out: free(tname); free(curve); #ifdef WITH_OPENSSL if (exponent != NULL) BN_clear_free(exponent); #endif /* WITH_OPENSSL */ sshkey_free(k); if (ed25519_pk != NULL) { explicit_bzero(ed25519_pk, pklen); free(ed25519_pk); } if (ed25519_sk != NULL) { explicit_bzero(ed25519_sk, sklen); free(ed25519_sk); } return r; } #if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC) int sshkey_ec_validate_public(const EC_GROUP *group, const EC_POINT *public) { BN_CTX *bnctx; EC_POINT *nq = NULL; BIGNUM *order, *x, *y, *tmp; int ret = SSH_ERR_KEY_INVALID_EC_VALUE; /* * NB. This assumes OpenSSL has already verified that the public * point lies on the curve. This is done by EC_POINT_oct2point() * implicitly calling EC_POINT_is_on_curve(). If this code is ever * reachable with public points not unmarshalled using * EC_POINT_oct2point then the caller will need to explicitly check. */ if ((bnctx = BN_CTX_new()) == NULL) return SSH_ERR_ALLOC_FAIL; BN_CTX_start(bnctx); /* * We shouldn't ever hit this case because bignum_get_ecpoint() * refuses to load GF2m points. */ if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) != NID_X9_62_prime_field) goto out; /* Q != infinity */ if (EC_POINT_is_at_infinity(group, public)) goto out; if ((x = BN_CTX_get(bnctx)) == NULL || (y = BN_CTX_get(bnctx)) == NULL || (order = BN_CTX_get(bnctx)) == NULL || (tmp = BN_CTX_get(bnctx)) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; } /* log2(x) > log2(order)/2, log2(y) > log2(order)/2 */ if (EC_GROUP_get_order(group, order, bnctx) != 1 || EC_POINT_get_affine_coordinates_GFp(group, public, x, y, bnctx) != 1) { ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; } if (BN_num_bits(x) <= BN_num_bits(order) / 2 || BN_num_bits(y) <= BN_num_bits(order) / 2) goto out; /* nQ == infinity (n == order of subgroup) */ if ((nq = EC_POINT_new(group)) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; } if (EC_POINT_mul(group, nq, NULL, public, order, bnctx) != 1) { ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; } if (EC_POINT_is_at_infinity(group, nq) != 1) goto out; /* x < order - 1, y < order - 1 */ if (!BN_sub(tmp, order, BN_value_one())) { ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; } if (BN_cmp(x, tmp) >= 0 || BN_cmp(y, tmp) >= 0) goto out; ret = 0; out: BN_CTX_free(bnctx); if (nq != NULL) EC_POINT_free(nq); return ret; } int sshkey_ec_validate_private(const EC_KEY *key) { BN_CTX *bnctx; BIGNUM *order, *tmp; int ret = SSH_ERR_KEY_INVALID_EC_VALUE; if ((bnctx = BN_CTX_new()) == NULL) return SSH_ERR_ALLOC_FAIL; BN_CTX_start(bnctx); if ((order = BN_CTX_get(bnctx)) == NULL || (tmp = BN_CTX_get(bnctx)) == NULL) { ret = SSH_ERR_ALLOC_FAIL; goto out; } /* log2(private) > log2(order)/2 */ if (EC_GROUP_get_order(EC_KEY_get0_group(key), order, bnctx) != 1) { ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; } if (BN_num_bits(EC_KEY_get0_private_key(key)) <= BN_num_bits(order) / 2) goto out; /* private < order - 1 */ if (!BN_sub(tmp, order, BN_value_one())) { ret = SSH_ERR_LIBCRYPTO_ERROR; goto out; } if (BN_cmp(EC_KEY_get0_private_key(key), tmp) >= 0) goto out; ret = 0; out: BN_CTX_free(bnctx); return ret; } void sshkey_dump_ec_point(const EC_GROUP *group, const EC_POINT *point) { BIGNUM *x, *y; BN_CTX *bnctx; if (point == NULL) { fputs("point=(NULL)\n", stderr); return; } if ((bnctx = BN_CTX_new()) == NULL) { fprintf(stderr, "%s: BN_CTX_new failed\n", __func__); return; } BN_CTX_start(bnctx); if ((x = BN_CTX_get(bnctx)) == NULL || (y = BN_CTX_get(bnctx)) == NULL) { fprintf(stderr, "%s: BN_CTX_get failed\n", __func__); return; } if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) != NID_X9_62_prime_field) { fprintf(stderr, "%s: group is not a prime field\n", __func__); return; } if (EC_POINT_get_affine_coordinates_GFp(group, point, x, y, bnctx) != 1) { fprintf(stderr, "%s: EC_POINT_get_affine_coordinates_GFp\n", __func__); return; } fputs("x=", stderr); BN_print_fp(stderr, x); fputs("\ny=", stderr); BN_print_fp(stderr, y); fputs("\n", stderr); BN_CTX_free(bnctx); } void sshkey_dump_ec_key(const EC_KEY *key) { const BIGNUM *exponent; sshkey_dump_ec_point(EC_KEY_get0_group(key), EC_KEY_get0_public_key(key)); fputs("exponent=", stderr); if ((exponent = EC_KEY_get0_private_key(key)) == NULL) fputs("(NULL)", stderr); else BN_print_fp(stderr, EC_KEY_get0_private_key(key)); fputs("\n", stderr); } #endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */ static int sshkey_private_to_blob2(const struct sshkey *prv, struct sshbuf *blob, const char *passphrase, const char *comment, const char *ciphername, int rounds) { u_char *cp, *key = NULL, *pubkeyblob = NULL; u_char salt[SALT_LEN]; char *b64 = NULL; size_t i, pubkeylen, keylen, ivlen, blocksize, authlen; u_int check; int r = SSH_ERR_INTERNAL_ERROR; struct sshcipher_ctx *ciphercontext = NULL; const struct sshcipher *cipher; const char *kdfname = KDFNAME; struct sshbuf *encoded = NULL, *encrypted = NULL, *kdf = NULL; if (rounds <= 0) rounds = DEFAULT_ROUNDS; if (passphrase == NULL || !strlen(passphrase)) { ciphername = "none"; kdfname = "none"; } else if (ciphername == NULL) ciphername = DEFAULT_CIPHERNAME; else if (cipher_number(ciphername) != SSH_CIPHER_SSH2) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } if ((cipher = cipher_by_name(ciphername)) == NULL) { r = SSH_ERR_INTERNAL_ERROR; goto out; } if ((kdf = sshbuf_new()) == NULL || (encoded = sshbuf_new()) == NULL || (encrypted = sshbuf_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } blocksize = cipher_blocksize(cipher); keylen = cipher_keylen(cipher); ivlen = cipher_ivlen(cipher); authlen = cipher_authlen(cipher); if ((key = calloc(1, keylen + ivlen)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if (strcmp(kdfname, "bcrypt") == 0) { arc4random_buf(salt, SALT_LEN); if (bcrypt_pbkdf(passphrase, strlen(passphrase), salt, SALT_LEN, key, keylen + ivlen, rounds) < 0) { r = SSH_ERR_INVALID_ARGUMENT; goto out; } if ((r = sshbuf_put_string(kdf, salt, SALT_LEN)) != 0 || (r = sshbuf_put_u32(kdf, rounds)) != 0) goto out; } else if (strcmp(kdfname, "none") != 0) { /* Unsupported KDF type */ r = SSH_ERR_KEY_UNKNOWN_CIPHER; goto out; } if ((r = cipher_init(&ciphercontext, cipher, key, keylen, key + keylen, ivlen, 1)) != 0) goto out; if ((r = sshbuf_put(encoded, AUTH_MAGIC, sizeof(AUTH_MAGIC))) != 0 || (r = sshbuf_put_cstring(encoded, ciphername)) != 0 || (r = sshbuf_put_cstring(encoded, kdfname)) != 0 || (r = sshbuf_put_stringb(encoded, kdf)) != 0 || (r = sshbuf_put_u32(encoded, 1)) != 0 || /* number of keys */ (r = sshkey_to_blob(prv, &pubkeyblob, &pubkeylen)) != 0 || (r = sshbuf_put_string(encoded, pubkeyblob, pubkeylen)) != 0) goto out; /* set up the buffer that will be encrypted */ /* Random check bytes */ check = arc4random(); if ((r = sshbuf_put_u32(encrypted, check)) != 0 || (r = sshbuf_put_u32(encrypted, check)) != 0) goto out; /* append private key and comment*/ if ((r = sshkey_private_serialize(prv, encrypted)) != 0 || (r = sshbuf_put_cstring(encrypted, comment)) != 0) goto out; /* padding */ i = 0; while (sshbuf_len(encrypted) % blocksize) { if ((r = sshbuf_put_u8(encrypted, ++i & 0xff)) != 0) goto out; } /* length in destination buffer */ if ((r = sshbuf_put_u32(encoded, sshbuf_len(encrypted))) != 0) goto out; /* encrypt */ if ((r = sshbuf_reserve(encoded, sshbuf_len(encrypted) + authlen, &cp)) != 0) goto out; if ((r = cipher_crypt(ciphercontext, 0, cp, sshbuf_ptr(encrypted), sshbuf_len(encrypted), 0, authlen)) != 0) goto out; /* uuencode */ if ((b64 = sshbuf_dtob64(encoded)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } sshbuf_reset(blob); if ((r = sshbuf_put(blob, MARK_BEGIN, MARK_BEGIN_LEN)) != 0) goto out; for (i = 0; i < strlen(b64); i++) { if ((r = sshbuf_put_u8(blob, b64[i])) != 0) goto out; /* insert line breaks */ if (i % 70 == 69 && (r = sshbuf_put_u8(blob, '\n')) != 0) goto out; } if (i % 70 != 69 && (r = sshbuf_put_u8(blob, '\n')) != 0) goto out; if ((r = sshbuf_put(blob, MARK_END, MARK_END_LEN)) != 0) goto out; /* success */ r = 0; out: sshbuf_free(kdf); sshbuf_free(encoded); sshbuf_free(encrypted); cipher_free(ciphercontext); explicit_bzero(salt, sizeof(salt)); if (key != NULL) { explicit_bzero(key, keylen + ivlen); free(key); } if (pubkeyblob != NULL) { explicit_bzero(pubkeyblob, pubkeylen); free(pubkeyblob); } if (b64 != NULL) { explicit_bzero(b64, strlen(b64)); free(b64); } return r; } static int sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase, struct sshkey **keyp, char **commentp) { char *comment = NULL, *ciphername = NULL, *kdfname = NULL; const struct sshcipher *cipher = NULL; const u_char *cp; int r = SSH_ERR_INTERNAL_ERROR; size_t encoded_len; size_t i, keylen = 0, ivlen = 0, authlen = 0, slen = 0; struct sshbuf *encoded = NULL, *decoded = NULL; struct sshbuf *kdf = NULL, *decrypted = NULL; struct sshcipher_ctx *ciphercontext = NULL; struct sshkey *k = NULL; u_char *key = NULL, *salt = NULL, *dp, pad, last; u_int blocksize, rounds, nkeys, encrypted_len, check1, check2; if (keyp != NULL) *keyp = NULL; if (commentp != NULL) *commentp = NULL; if ((encoded = sshbuf_new()) == NULL || (decoded = sshbuf_new()) == NULL || (decrypted = sshbuf_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } /* check preamble */ cp = sshbuf_ptr(blob); encoded_len = sshbuf_len(blob); if (encoded_len < (MARK_BEGIN_LEN + MARK_END_LEN) || memcmp(cp, MARK_BEGIN, MARK_BEGIN_LEN) != 0) { r = SSH_ERR_INVALID_FORMAT; goto out; } cp += MARK_BEGIN_LEN; encoded_len -= MARK_BEGIN_LEN; /* Look for end marker, removing whitespace as we go */ while (encoded_len > 0) { if (*cp != '\n' && *cp != '\r') { if ((r = sshbuf_put_u8(encoded, *cp)) != 0) goto out; } last = *cp; encoded_len--; cp++; if (last == '\n') { if (encoded_len >= MARK_END_LEN && memcmp(cp, MARK_END, MARK_END_LEN) == 0) { /* \0 terminate */ if ((r = sshbuf_put_u8(encoded, 0)) != 0) goto out; break; } } } if (encoded_len == 0) { r = SSH_ERR_INVALID_FORMAT; goto out; } /* decode base64 */ if ((r = sshbuf_b64tod(decoded, (char *)sshbuf_ptr(encoded))) != 0) goto out; /* check magic */ if (sshbuf_len(decoded) < sizeof(AUTH_MAGIC) || memcmp(sshbuf_ptr(decoded), AUTH_MAGIC, sizeof(AUTH_MAGIC))) { r = SSH_ERR_INVALID_FORMAT; goto out; } /* parse public portion of key */ if ((r = sshbuf_consume(decoded, sizeof(AUTH_MAGIC))) != 0 || (r = sshbuf_get_cstring(decoded, &ciphername, NULL)) != 0 || (r = sshbuf_get_cstring(decoded, &kdfname, NULL)) != 0 || (r = sshbuf_froms(decoded, &kdf)) != 0 || (r = sshbuf_get_u32(decoded, &nkeys)) != 0 || (r = sshbuf_skip_string(decoded)) != 0 || /* pubkey */ (r = sshbuf_get_u32(decoded, &encrypted_len)) != 0) goto out; if ((cipher = cipher_by_name(ciphername)) == NULL) { r = SSH_ERR_KEY_UNKNOWN_CIPHER; goto out; } if ((passphrase == NULL || strlen(passphrase) == 0) && strcmp(ciphername, "none") != 0) { /* passphrase required */ r = SSH_ERR_KEY_WRONG_PASSPHRASE; goto out; } if (strcmp(kdfname, "none") != 0 && strcmp(kdfname, "bcrypt") != 0) { r = SSH_ERR_KEY_UNKNOWN_CIPHER; goto out; } if (!strcmp(kdfname, "none") && strcmp(ciphername, "none") != 0) { r = SSH_ERR_INVALID_FORMAT; goto out; } if (nkeys != 1) { /* XXX only one key supported */ r = SSH_ERR_INVALID_FORMAT; goto out; } /* check size of encrypted key blob */ blocksize = cipher_blocksize(cipher); if (encrypted_len < blocksize || (encrypted_len % blocksize) != 0) { r = SSH_ERR_INVALID_FORMAT; goto out; } /* setup key */ keylen = cipher_keylen(cipher); ivlen = cipher_ivlen(cipher); authlen = cipher_authlen(cipher); if ((key = calloc(1, keylen + ivlen)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if (strcmp(kdfname, "bcrypt") == 0) { if ((r = sshbuf_get_string(kdf, &salt, &slen)) != 0 || (r = sshbuf_get_u32(kdf, &rounds)) != 0) goto out; if (bcrypt_pbkdf(passphrase, strlen(passphrase), salt, slen, key, keylen + ivlen, rounds) < 0) { r = SSH_ERR_INVALID_FORMAT; goto out; } } /* check that an appropriate amount of auth data is present */ if (sshbuf_len(decoded) < encrypted_len + authlen) { r = SSH_ERR_INVALID_FORMAT; goto out; } /* decrypt private portion of key */ if ((r = sshbuf_reserve(decrypted, encrypted_len, &dp)) != 0 || (r = cipher_init(&ciphercontext, cipher, key, keylen, key + keylen, ivlen, 0)) != 0) goto out; if ((r = cipher_crypt(ciphercontext, 0, dp, sshbuf_ptr(decoded), encrypted_len, 0, authlen)) != 0) { /* an integrity error here indicates an incorrect passphrase */ if (r == SSH_ERR_MAC_INVALID) r = SSH_ERR_KEY_WRONG_PASSPHRASE; goto out; } if ((r = sshbuf_consume(decoded, encrypted_len + authlen)) != 0) goto out; /* there should be no trailing data */ if (sshbuf_len(decoded) != 0) { r = SSH_ERR_INVALID_FORMAT; goto out; } /* check check bytes */ if ((r = sshbuf_get_u32(decrypted, &check1)) != 0 || (r = sshbuf_get_u32(decrypted, &check2)) != 0) goto out; if (check1 != check2) { r = SSH_ERR_KEY_WRONG_PASSPHRASE; goto out; } /* Load the private key and comment */ if ((r = sshkey_private_deserialize(decrypted, &k)) != 0 || (r = sshbuf_get_cstring(decrypted, &comment, NULL)) != 0) goto out; /* Check deterministic padding */ i = 0; while (sshbuf_len(decrypted)) { if ((r = sshbuf_get_u8(decrypted, &pad)) != 0) goto out; if (pad != (++i & 0xff)) { r = SSH_ERR_INVALID_FORMAT; goto out; } } /* XXX decode pubkey and check against private */ /* success */ r = 0; if (keyp != NULL) { *keyp = k; k = NULL; } if (commentp != NULL) { *commentp = comment; comment = NULL; } out: pad = 0; cipher_free(ciphercontext); free(ciphername); free(kdfname); free(comment); if (salt != NULL) { explicit_bzero(salt, slen); free(salt); } if (key != NULL) { explicit_bzero(key, keylen + ivlen); free(key); } sshbuf_free(encoded); sshbuf_free(decoded); sshbuf_free(kdf); sshbuf_free(decrypted); sshkey_free(k); return r; } #if WITH_SSH1 /* * Serialises the authentication (private) key to a blob, encrypting it with * passphrase. The identification of the blob (lowest 64 bits of n) will * precede the key to provide identification of the key without needing a * passphrase. */ static int sshkey_private_rsa1_to_blob(struct sshkey *key, struct sshbuf *blob, const char *passphrase, const char *comment) { struct sshbuf *buffer = NULL, *encrypted = NULL; u_char buf[8]; int r, cipher_num; struct sshcipher_ctx *ciphercontext = NULL; const struct sshcipher *cipher; u_char *cp; /* * If the passphrase is empty, use SSH_CIPHER_NONE to ease converting * to another cipher; otherwise use SSH_AUTHFILE_CIPHER. */ cipher_num = (strcmp(passphrase, "") == 0) ? SSH_CIPHER_NONE : SSH_CIPHER_3DES; if ((cipher = cipher_by_number(cipher_num)) == NULL) return SSH_ERR_INTERNAL_ERROR; /* This buffer is used to build the secret part of the private key. */ if ((buffer = sshbuf_new()) == NULL) return SSH_ERR_ALLOC_FAIL; /* Put checkbytes for checking passphrase validity. */ if ((r = sshbuf_reserve(buffer, 4, &cp)) != 0) goto out; arc4random_buf(cp, 2); memcpy(cp + 2, cp, 2); /* * Store the private key (n and e will not be stored because they * will be stored in plain text, and storing them also in encrypted * format would just give known plaintext). * Note: q and p are stored in reverse order to SSL. */ if ((r = sshbuf_put_bignum1(buffer, key->rsa->d)) != 0 || (r = sshbuf_put_bignum1(buffer, key->rsa->iqmp)) != 0 || (r = sshbuf_put_bignum1(buffer, key->rsa->q)) != 0 || (r = sshbuf_put_bignum1(buffer, key->rsa->p)) != 0) goto out; /* Pad the part to be encrypted to a size that is a multiple of 8. */ explicit_bzero(buf, 8); if ((r = sshbuf_put(buffer, buf, 8 - (sshbuf_len(buffer) % 8))) != 0) goto out; /* This buffer will be used to contain the data in the file. */ if ((encrypted = sshbuf_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } /* First store keyfile id string. */ if ((r = sshbuf_put(encrypted, LEGACY_BEGIN, sizeof(LEGACY_BEGIN))) != 0) goto out; /* Store cipher type and "reserved" field. */ if ((r = sshbuf_put_u8(encrypted, cipher_num)) != 0 || (r = sshbuf_put_u32(encrypted, 0)) != 0) goto out; /* Store public key. This will be in plain text. */ if ((r = sshbuf_put_u32(encrypted, BN_num_bits(key->rsa->n))) != 0 || (r = sshbuf_put_bignum1(encrypted, key->rsa->n)) != 0 || (r = sshbuf_put_bignum1(encrypted, key->rsa->e)) != 0 || (r = sshbuf_put_cstring(encrypted, comment)) != 0) goto out; /* Allocate space for the private part of the key in the buffer. */ if ((r = sshbuf_reserve(encrypted, sshbuf_len(buffer), &cp)) != 0) goto out; if ((r = cipher_set_key_string(&ciphercontext, cipher, passphrase, CIPHER_ENCRYPT)) != 0) goto out; if ((r = cipher_crypt(ciphercontext, 0, cp, sshbuf_ptr(buffer), sshbuf_len(buffer), 0, 0)) != 0) goto out; r = sshbuf_putb(blob, encrypted); out: cipher_free(ciphercontext); explicit_bzero(buf, sizeof(buf)); sshbuf_free(buffer); sshbuf_free(encrypted); return r; } #endif /* WITH_SSH1 */ #ifdef WITH_OPENSSL /* convert SSH v2 key in OpenSSL PEM format */ static int sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *blob, const char *_passphrase, const char *comment) { int success, r; int blen, len = strlen(_passphrase); u_char *passphrase = (len > 0) ? (u_char *)_passphrase : NULL; #if (OPENSSL_VERSION_NUMBER < 0x00907000L) const EVP_CIPHER *cipher = (len > 0) ? EVP_des_ede3_cbc() : NULL; #else const EVP_CIPHER *cipher = (len > 0) ? EVP_aes_128_cbc() : NULL; #endif const u_char *bptr; BIO *bio = NULL; if (len > 0 && len <= 4) return SSH_ERR_PASSPHRASE_TOO_SHORT; if ((bio = BIO_new(BIO_s_mem())) == NULL) return SSH_ERR_ALLOC_FAIL; switch (key->type) { case KEY_DSA: success = PEM_write_bio_DSAPrivateKey(bio, key->dsa, cipher, passphrase, len, NULL, NULL); break; #ifdef OPENSSL_HAS_ECC case KEY_ECDSA: success = PEM_write_bio_ECPrivateKey(bio, key->ecdsa, cipher, passphrase, len, NULL, NULL); break; #endif case KEY_RSA: success = PEM_write_bio_RSAPrivateKey(bio, key->rsa, cipher, passphrase, len, NULL, NULL); break; default: success = 0; break; } if (success == 0) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } if ((blen = BIO_get_mem_data(bio, &bptr)) <= 0) { r = SSH_ERR_INTERNAL_ERROR; goto out; } if ((r = sshbuf_put(blob, bptr, blen)) != 0) goto out; r = 0; out: BIO_free(bio); return r; } #endif /* WITH_OPENSSL */ /* Serialise "key" to buffer "blob" */ int sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob, const char *passphrase, const char *comment, int force_new_format, const char *new_format_cipher, int new_format_rounds) { switch (key->type) { #ifdef WITH_SSH1 case KEY_RSA1: return sshkey_private_rsa1_to_blob(key, blob, passphrase, comment); #endif /* WITH_SSH1 */ #ifdef WITH_OPENSSL case KEY_DSA: case KEY_ECDSA: case KEY_RSA: if (force_new_format) { return sshkey_private_to_blob2(key, blob, passphrase, comment, new_format_cipher, new_format_rounds); } return sshkey_private_pem_to_blob(key, blob, passphrase, comment); #endif /* WITH_OPENSSL */ case KEY_ED25519: return sshkey_private_to_blob2(key, blob, passphrase, comment, new_format_cipher, new_format_rounds); default: return SSH_ERR_KEY_TYPE_UNKNOWN; } } #ifdef WITH_SSH1 /* * Parse the public, unencrypted portion of a RSA1 key. */ int sshkey_parse_public_rsa1_fileblob(struct sshbuf *blob, struct sshkey **keyp, char **commentp) { int r; struct sshkey *pub = NULL; struct sshbuf *copy = NULL; if (keyp != NULL) *keyp = NULL; if (commentp != NULL) *commentp = NULL; /* Check that it is at least big enough to contain the ID string. */ if (sshbuf_len(blob) < sizeof(LEGACY_BEGIN)) return SSH_ERR_INVALID_FORMAT; /* * Make sure it begins with the id string. Consume the id string * from the buffer. */ if (memcmp(sshbuf_ptr(blob), LEGACY_BEGIN, sizeof(LEGACY_BEGIN)) != 0) return SSH_ERR_INVALID_FORMAT; /* Make a working copy of the keyblob and skip past the magic */ if ((copy = sshbuf_fromb(blob)) == NULL) return SSH_ERR_ALLOC_FAIL; if ((r = sshbuf_consume(copy, sizeof(LEGACY_BEGIN))) != 0) goto out; /* Skip cipher type, reserved data and key bits. */ if ((r = sshbuf_get_u8(copy, NULL)) != 0 || /* cipher type */ (r = sshbuf_get_u32(copy, NULL)) != 0 || /* reserved */ (r = sshbuf_get_u32(copy, NULL)) != 0) /* key bits */ goto out; /* Read the public key from the buffer. */ if ((pub = sshkey_new(KEY_RSA1)) == NULL || (r = sshbuf_get_bignum1(copy, pub->rsa->n)) != 0 || (r = sshbuf_get_bignum1(copy, pub->rsa->e)) != 0) goto out; /* Finally, the comment */ if ((r = sshbuf_get_string(copy, (u_char**)commentp, NULL)) != 0) goto out; /* The encrypted private part is not parsed by this function. */ r = 0; if (keyp != NULL) { *keyp = pub; pub = NULL; } out: sshbuf_free(copy); sshkey_free(pub); return r; } static int sshkey_parse_private_rsa1(struct sshbuf *blob, const char *passphrase, struct sshkey **keyp, char **commentp) { int r; u_int16_t check1, check2; u_int8_t cipher_type; struct sshbuf *decrypted = NULL, *copy = NULL; u_char *cp; char *comment = NULL; struct sshcipher_ctx *ciphercontext = NULL; const struct sshcipher *cipher; struct sshkey *prv = NULL; if (keyp != NULL) *keyp = NULL; if (commentp != NULL) *commentp = NULL; /* Check that it is at least big enough to contain the ID string. */ if (sshbuf_len(blob) < sizeof(LEGACY_BEGIN)) return SSH_ERR_INVALID_FORMAT; /* * Make sure it begins with the id string. Consume the id string * from the buffer. */ if (memcmp(sshbuf_ptr(blob), LEGACY_BEGIN, sizeof(LEGACY_BEGIN)) != 0) return SSH_ERR_INVALID_FORMAT; if ((prv = sshkey_new_private(KEY_RSA1)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((copy = sshbuf_fromb(blob)) == NULL || (decrypted = sshbuf_new()) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((r = sshbuf_consume(copy, sizeof(LEGACY_BEGIN))) != 0) goto out; /* Read cipher type. */ if ((r = sshbuf_get_u8(copy, &cipher_type)) != 0 || (r = sshbuf_get_u32(copy, NULL)) != 0) /* reserved */ goto out; /* Read the public key and comment from the buffer. */ if ((r = sshbuf_get_u32(copy, NULL)) != 0 || /* key bits */ (r = sshbuf_get_bignum1(copy, prv->rsa->n)) != 0 || (r = sshbuf_get_bignum1(copy, prv->rsa->e)) != 0 || (r = sshbuf_get_cstring(copy, &comment, NULL)) != 0) goto out; /* Check that it is a supported cipher. */ cipher = cipher_by_number(cipher_type); if (cipher == NULL) { r = SSH_ERR_KEY_UNKNOWN_CIPHER; goto out; } /* Initialize space for decrypted data. */ if ((r = sshbuf_reserve(decrypted, sshbuf_len(copy), &cp)) != 0) goto out; /* Rest of the buffer is encrypted. Decrypt it using the passphrase. */ if ((r = cipher_set_key_string(&ciphercontext, cipher, passphrase, CIPHER_DECRYPT)) != 0) goto out; if ((r = cipher_crypt(ciphercontext, 0, cp, sshbuf_ptr(copy), sshbuf_len(copy), 0, 0)) != 0) goto out; if ((r = sshbuf_get_u16(decrypted, &check1)) != 0 || (r = sshbuf_get_u16(decrypted, &check2)) != 0) goto out; if (check1 != check2) { r = SSH_ERR_KEY_WRONG_PASSPHRASE; goto out; } /* Read the rest of the private key. */ if ((r = sshbuf_get_bignum1(decrypted, prv->rsa->d)) != 0 || (r = sshbuf_get_bignum1(decrypted, prv->rsa->iqmp)) != 0 || (r = sshbuf_get_bignum1(decrypted, prv->rsa->q)) != 0 || (r = sshbuf_get_bignum1(decrypted, prv->rsa->p)) != 0) goto out; /* calculate p-1 and q-1 */ if ((r = rsa_generate_additional_parameters(prv->rsa)) != 0) goto out; /* enable blinding */ if (RSA_blinding_on(prv->rsa, NULL) != 1) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } r = 0; if (keyp != NULL) { *keyp = prv; prv = NULL; } if (commentp != NULL) { *commentp = comment; comment = NULL; } out: cipher_free(ciphercontext); free(comment); sshkey_free(prv); sshbuf_free(copy); sshbuf_free(decrypted); return r; } #endif /* WITH_SSH1 */ #ifdef WITH_OPENSSL static int sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type, const char *passphrase, struct sshkey **keyp) { EVP_PKEY *pk = NULL; struct sshkey *prv = NULL; BIO *bio = NULL; int r; if (keyp != NULL) *keyp = NULL; if ((bio = BIO_new(BIO_s_mem())) == NULL || sshbuf_len(blob) > INT_MAX) return SSH_ERR_ALLOC_FAIL; if (BIO_write(bio, sshbuf_ptr(blob), sshbuf_len(blob)) != (int)sshbuf_len(blob)) { r = SSH_ERR_ALLOC_FAIL; goto out; } if ((pk = PEM_read_bio_PrivateKey(bio, NULL, NULL, (char *)passphrase)) == NULL) { - r = SSH_ERR_KEY_WRONG_PASSPHRASE; + unsigned long pem_err = ERR_peek_last_error(); + int pem_reason = ERR_GET_REASON(pem_err); + + /* + * Translate OpenSSL error codes to determine whether + * passphrase is required/incorrect. + */ + switch (ERR_GET_LIB(pem_err)) { + case ERR_LIB_PEM: + switch (pem_reason) { + case PEM_R_BAD_PASSWORD_READ: + case PEM_R_PROBLEMS_GETTING_PASSWORD: + case PEM_R_BAD_DECRYPT: + r = SSH_ERR_KEY_WRONG_PASSPHRASE; + goto out; + default: + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + case ERR_LIB_EVP: + switch (pem_reason) { + case EVP_R_BAD_DECRYPT: + r = SSH_ERR_KEY_WRONG_PASSPHRASE; + goto out; + case EVP_R_BN_DECODE_ERROR: + case EVP_R_DECODE_ERROR: +#ifdef EVP_R_PRIVATE_KEY_DECODE_ERROR + case EVP_R_PRIVATE_KEY_DECODE_ERROR: +#endif + r = SSH_ERR_INVALID_FORMAT; + goto out; + default: + r = SSH_ERR_LIBCRYPTO_ERROR; + goto out; + } + case ERR_LIB_ASN1: + r = SSH_ERR_INVALID_FORMAT; + goto out; + } + r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } if (pk->type == EVP_PKEY_RSA && (type == KEY_UNSPEC || type == KEY_RSA)) { if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } prv->rsa = EVP_PKEY_get1_RSA(pk); prv->type = KEY_RSA; #ifdef DEBUG_PK RSA_print_fp(stderr, prv->rsa, 8); #endif if (RSA_blinding_on(prv->rsa, NULL) != 1) { r = SSH_ERR_LIBCRYPTO_ERROR; goto out; } } else if (pk->type == EVP_PKEY_DSA && (type == KEY_UNSPEC || type == KEY_DSA)) { if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } prv->dsa = EVP_PKEY_get1_DSA(pk); prv->type = KEY_DSA; #ifdef DEBUG_PK DSA_print_fp(stderr, prv->dsa, 8); #endif #ifdef OPENSSL_HAS_ECC } else if (pk->type == EVP_PKEY_EC && (type == KEY_UNSPEC || type == KEY_ECDSA)) { if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) { r = SSH_ERR_ALLOC_FAIL; goto out; } prv->ecdsa = EVP_PKEY_get1_EC_KEY(pk); prv->type = KEY_ECDSA; prv->ecdsa_nid = sshkey_ecdsa_key_to_nid(prv->ecdsa); if (prv->ecdsa_nid == -1 || sshkey_curve_nid_to_name(prv->ecdsa_nid) == NULL || sshkey_ec_validate_public(EC_KEY_get0_group(prv->ecdsa), EC_KEY_get0_public_key(prv->ecdsa)) != 0 || sshkey_ec_validate_private(prv->ecdsa) != 0) { r = SSH_ERR_INVALID_FORMAT; goto out; } # ifdef DEBUG_PK if (prv != NULL && prv->ecdsa != NULL) sshkey_dump_ec_key(prv->ecdsa); # endif #endif /* OPENSSL_HAS_ECC */ } else { r = SSH_ERR_INVALID_FORMAT; goto out; } r = 0; if (keyp != NULL) { *keyp = prv; prv = NULL; } out: BIO_free(bio); if (pk != NULL) EVP_PKEY_free(pk); sshkey_free(prv); return r; } #endif /* WITH_OPENSSL */ int sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type, const char *passphrase, struct sshkey **keyp, char **commentp) { + int r = SSH_ERR_INTERNAL_ERROR; + if (keyp != NULL) *keyp = NULL; if (commentp != NULL) *commentp = NULL; switch (type) { #ifdef WITH_SSH1 case KEY_RSA1: return sshkey_parse_private_rsa1(blob, passphrase, keyp, commentp); #endif /* WITH_SSH1 */ #ifdef WITH_OPENSSL case KEY_DSA: case KEY_ECDSA: case KEY_RSA: return sshkey_parse_private_pem_fileblob(blob, type, passphrase, keyp); #endif /* WITH_OPENSSL */ case KEY_ED25519: return sshkey_parse_private2(blob, type, passphrase, keyp, commentp); case KEY_UNSPEC: - if (sshkey_parse_private2(blob, type, passphrase, keyp, - commentp) == 0) - return 0; + r = sshkey_parse_private2(blob, type, passphrase, keyp, + commentp); + /* Do not fallback to PEM parser if only passphrase is wrong. */ + if (r == 0 || r == SSH_ERR_KEY_WRONG_PASSPHRASE) + return r; #ifdef WITH_OPENSSL return sshkey_parse_private_pem_fileblob(blob, type, passphrase, keyp); #else return SSH_ERR_INVALID_FORMAT; #endif /* WITH_OPENSSL */ default: return SSH_ERR_KEY_TYPE_UNKNOWN; } } int sshkey_parse_private_fileblob(struct sshbuf *buffer, const char *passphrase, struct sshkey **keyp, char **commentp) { if (keyp != NULL) *keyp = NULL; if (commentp != NULL) *commentp = NULL; #ifdef WITH_SSH1 /* it's a SSH v1 key if the public key part is readable */ if (sshkey_parse_public_rsa1_fileblob(buffer, NULL, NULL) == 0) { return sshkey_parse_private_fileblob_type(buffer, KEY_RSA1, passphrase, keyp, commentp); } #endif /* WITH_SSH1 */ return sshkey_parse_private_fileblob_type(buffer, KEY_UNSPEC, passphrase, keyp, commentp); } diff --git a/crypto/openssh/sshkey.h b/crypto/openssh/sshkey.h index f39363842df8..1b9e42f45be0 100644 --- a/crypto/openssh/sshkey.h +++ b/crypto/openssh/sshkey.h @@ -1,230 +1,230 @@ -/* $OpenBSD: sshkey.h,v 1.14 2016/09/12 23:31:27 djm Exp $ */ +/* $OpenBSD: sshkey.h,v 1.15 2017/03/10 04:07:20 djm Exp $ */ /* * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef SSHKEY_H #define SSHKEY_H #include #ifdef WITH_OPENSSL #include #include # ifdef OPENSSL_HAS_ECC # include # else /* OPENSSL_HAS_ECC */ # define EC_KEY void # define EC_GROUP void # define EC_POINT void # endif /* OPENSSL_HAS_ECC */ #else /* WITH_OPENSSL */ # define RSA void # define DSA void # define EC_KEY void # define EC_GROUP void # define EC_POINT void #endif /* WITH_OPENSSL */ #define SSH_RSA_MINIMUM_MODULUS_SIZE 768 #define SSH_KEY_MAX_SIGN_DATA_SIZE (1 << 20) struct sshbuf; /* Key types */ enum sshkey_types { KEY_RSA1, KEY_RSA, KEY_DSA, KEY_ECDSA, KEY_ED25519, KEY_RSA_CERT, KEY_DSA_CERT, KEY_ECDSA_CERT, KEY_ED25519_CERT, KEY_UNSPEC }; /* Default fingerprint hash */ #define SSH_FP_HASH_DEFAULT SSH_DIGEST_SHA256 /* Fingerprint representation formats */ enum sshkey_fp_rep { SSH_FP_DEFAULT = 0, SSH_FP_HEX, SSH_FP_BASE64, SSH_FP_BUBBLEBABBLE, SSH_FP_RANDOMART }; /* key is stored in external hardware */ #define SSHKEY_FLAG_EXT 0x0001 #define SSHKEY_CERT_MAX_PRINCIPALS 256 /* XXX opaquify? */ struct sshkey_cert { struct sshbuf *certblob; /* Kept around for use on wire */ u_int type; /* SSH2_CERT_TYPE_USER or SSH2_CERT_TYPE_HOST */ u_int64_t serial; char *key_id; u_int nprincipals; char **principals; u_int64_t valid_after, valid_before; struct sshbuf *critical; struct sshbuf *extensions; struct sshkey *signature_key; }; /* XXX opaquify? */ struct sshkey { int type; int flags; RSA *rsa; DSA *dsa; int ecdsa_nid; /* NID of curve */ EC_KEY *ecdsa; u_char *ed25519_sk; u_char *ed25519_pk; struct sshkey_cert *cert; }; #define ED25519_SK_SZ crypto_sign_ed25519_SECRETKEYBYTES #define ED25519_PK_SZ crypto_sign_ed25519_PUBLICKEYBYTES struct sshkey *sshkey_new(int); int sshkey_add_private(struct sshkey *); struct sshkey *sshkey_new_private(int); void sshkey_free(struct sshkey *); int sshkey_demote(const struct sshkey *, struct sshkey **); int sshkey_equal_public(const struct sshkey *, const struct sshkey *); int sshkey_equal(const struct sshkey *, const struct sshkey *); char *sshkey_fingerprint(const struct sshkey *, int, enum sshkey_fp_rep); int sshkey_fingerprint_raw(const struct sshkey *k, int, u_char **retp, size_t *lenp); const char *sshkey_type(const struct sshkey *); const char *sshkey_cert_type(const struct sshkey *); int sshkey_write(const struct sshkey *, FILE *); int sshkey_read(struct sshkey *, char **); u_int sshkey_size(const struct sshkey *); int sshkey_generate(int type, u_int bits, struct sshkey **keyp); int sshkey_from_private(const struct sshkey *, struct sshkey **); int sshkey_type_from_name(const char *); int sshkey_is_cert(const struct sshkey *); int sshkey_type_is_cert(int); int sshkey_type_plain(int); int sshkey_to_certified(struct sshkey *); int sshkey_drop_cert(struct sshkey *); int sshkey_certify(struct sshkey *, struct sshkey *, const char *); int sshkey_cert_copy(const struct sshkey *, struct sshkey *); int sshkey_cert_check_authority(const struct sshkey *, int, int, const char *, const char **); size_t sshkey_format_cert_validity(const struct sshkey_cert *, char *, size_t) __attribute__((__bounded__(__string__, 2, 3))); int sshkey_ecdsa_nid_from_name(const char *); int sshkey_curve_name_to_nid(const char *); const char * sshkey_curve_nid_to_name(int); u_int sshkey_curve_nid_to_bits(int); int sshkey_ecdsa_bits_to_nid(int); int sshkey_ecdsa_key_to_nid(EC_KEY *); int sshkey_ec_nid_to_hash_alg(int nid); int sshkey_ec_validate_public(const EC_GROUP *, const EC_POINT *); int sshkey_ec_validate_private(const EC_KEY *); const char *sshkey_ssh_name(const struct sshkey *); const char *sshkey_ssh_name_plain(const struct sshkey *); int sshkey_names_valid2(const char *, int); -char *sshkey_alg_list(int, int, char); +char *sshkey_alg_list(int, int, int, char); int sshkey_from_blob(const u_char *, size_t, struct sshkey **); int sshkey_fromb(struct sshbuf *, struct sshkey **); int sshkey_froms(struct sshbuf *, struct sshkey **); int sshkey_to_blob(const struct sshkey *, u_char **, size_t *); int sshkey_to_base64(const struct sshkey *, char **); int sshkey_putb(const struct sshkey *, struct sshbuf *); int sshkey_puts(const struct sshkey *, struct sshbuf *); int sshkey_plain_to_blob(const struct sshkey *, u_char **, size_t *); int sshkey_putb_plain(const struct sshkey *, struct sshbuf *); int sshkey_sign(const struct sshkey *, u_char **, size_t *, const u_char *, size_t, const char *, u_int); int sshkey_verify(const struct sshkey *, const u_char *, size_t, const u_char *, size_t, u_int); /* for debug */ void sshkey_dump_ec_point(const EC_GROUP *, const EC_POINT *); void sshkey_dump_ec_key(const EC_KEY *); /* private key parsing and serialisation */ int sshkey_private_serialize(const struct sshkey *key, struct sshbuf *buf); int sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **keyp); /* private key file format parsing and serialisation */ int sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob, const char *passphrase, const char *comment, int force_new_format, const char *new_format_cipher, int new_format_rounds); int sshkey_parse_public_rsa1_fileblob(struct sshbuf *blob, struct sshkey **keyp, char **commentp); int sshkey_parse_private_fileblob(struct sshbuf *buffer, const char *passphrase, struct sshkey **keyp, char **commentp); int sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type, const char *passphrase, struct sshkey **keyp, char **commentp); #ifdef SSHKEY_INTERNAL int ssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, const char *ident); int ssh_rsa_verify(const struct sshkey *key, const u_char *sig, size_t siglen, const u_char *data, size_t datalen); int ssh_dss_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, u_int compat); int ssh_dss_verify(const struct sshkey *key, const u_char *signature, size_t signaturelen, const u_char *data, size_t datalen, u_int compat); int ssh_ecdsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, u_int compat); int ssh_ecdsa_verify(const struct sshkey *key, const u_char *signature, size_t signaturelen, const u_char *data, size_t datalen, u_int compat); int ssh_ed25519_sign(const struct sshkey *key, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, u_int compat); int ssh_ed25519_verify(const struct sshkey *key, const u_char *signature, size_t signaturelen, const u_char *data, size_t datalen, u_int compat); #endif #if !defined(WITH_OPENSSL) # undef RSA # undef DSA # undef EC_KEY # undef EC_GROUP # undef EC_POINT #elif !defined(OPENSSL_HAS_ECC) # undef EC_KEY # undef EC_GROUP # undef EC_POINT #endif #endif /* SSHKEY_H */ diff --git a/crypto/openssh/utf8.c b/crypto/openssh/utf8.c index 87fa9e89a2b4..dead79b8a252 100644 --- a/crypto/openssh/utf8.c +++ b/crypto/openssh/utf8.c @@ -1,332 +1,334 @@ -/* $OpenBSD: utf8.c,v 1.3 2016/05/30 12:57:21 schwarze Exp $ */ +/* $OpenBSD: utf8.c,v 1.5 2017/02/19 00:10:57 djm Exp $ */ /* * Copyright (c) 2016 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Utility functions for multibyte-character handling, * in particular to sanitize untrusted strings for terminal output. */ #include "includes.h" #include #ifdef HAVE_LANGINFO_H # include #endif #include #include #include #include #include #include #if defined(HAVE_STRNVIS) && defined(HAVE_VIS_H) && !defined(BROKEN_STRNVIS) # include #endif #ifdef HAVE_WCHAR_H # include #endif #include "utf8.h" static int dangerous_locale(void); static int grow_dst(char **, size_t *, size_t, char **, size_t); static int vasnmprintf(char **, size_t, int *, const char *, va_list); /* * For US-ASCII and UTF-8 encodings, we can safely recover from * encoding errors and from non-printable characters. For any * other encodings, err to the side of caution and abort parsing: * For state-dependent encodings, recovery is impossible. * For arbitrary encodings, replacement of non-printable * characters would be non-trivial and too fragile. */ static int dangerous_locale(void) { char *loc; loc = nl_langinfo(CODESET); - return strcmp(loc, "US-ASCII") && strcmp(loc, "UTF-8"); + return strcmp(loc, "US-ASCII") != 0 && strcmp(loc, "UTF-8") != 0 && + strcmp(loc, "ANSI_X3.4-1968") != 0 && strcmp(loc, "646") != 0; } static int grow_dst(char **dst, size_t *sz, size_t maxsz, char **dp, size_t need) { char *tp; size_t tsz; if (*dp + need < *dst + *sz) return 0; tsz = *sz + 128; if (tsz > maxsz) tsz = maxsz; if ((tp = realloc(*dst, tsz)) == NULL) return -1; *dp = tp + (*dp - *dst); *dst = tp; *sz = tsz; return 0; } /* * The following two functions limit the number of bytes written, * including the terminating '\0', to sz. Unless wp is NULL, * they limit the number of display columns occupied to *wp. * Whichever is reached first terminates the output string. * To stay close to the standard interfaces, they return the number of * non-NUL bytes that would have been written if both were unlimited. * If wp is NULL, newline, carriage return, and tab are allowed; * otherwise, the actual number of columns occupied by what was * written is returned in *wp. */ static int vasnmprintf(char **str, size_t maxsz, int *wp, const char *fmt, va_list ap) { char *src; /* Source string returned from vasprintf. */ char *sp; /* Pointer into src. */ char *dst; /* Destination string to be returned. */ char *dp; /* Pointer into dst. */ char *tp; /* Temporary pointer for dst. */ size_t sz; /* Number of bytes allocated for dst. */ wchar_t wc; /* Wide character at sp. */ int len; /* Number of bytes in the character at sp. */ int ret; /* Number of bytes needed to format src. */ int width; /* Display width of the character wc. */ int total_width, max_width, print; src = NULL; if ((ret = vasprintf(&src, fmt, ap)) <= 0) goto fail; sz = strlen(src) + 1; if ((dst = malloc(sz)) == NULL) { free(src); + ret = -1; goto fail; } if (maxsz > INT_MAX) maxsz = INT_MAX; sp = src; dp = dst; ret = 0; print = 1; total_width = 0; max_width = wp == NULL ? INT_MAX : *wp; while (*sp != '\0') { if ((len = mbtowc(&wc, sp, MB_CUR_MAX)) == -1) { (void)mbtowc(NULL, NULL, MB_CUR_MAX); if (dangerous_locale()) { ret = -1; break; } len = 1; width = -1; } else if (wp == NULL && (wc == L'\n' || wc == L'\r' || wc == L'\t')) { /* * Don't use width uninitialized; the actual * value doesn't matter because total_width * is only returned for wp != NULL. */ width = 0; } else if ((width = wcwidth(wc)) == -1 && dangerous_locale()) { ret = -1; break; } /* Valid, printable character. */ if (width >= 0) { if (print && (dp - dst >= (int)maxsz - len || total_width > max_width - width)) print = 0; if (print) { if (grow_dst(&dst, &sz, maxsz, &dp, len) == -1) { ret = -1; break; } total_width += width; memcpy(dp, sp, len); dp += len; } sp += len; if (ret >= 0) ret += len; continue; } /* Escaping required. */ while (len > 0) { if (print && (dp - dst >= (int)maxsz - 4 || total_width > max_width - 4)) print = 0; if (print) { if (grow_dst(&dst, &sz, maxsz, &dp, 4) == -1) { ret = -1; break; } tp = vis(dp, *sp, VIS_OCTAL | VIS_ALL, 0); width = tp - dp; total_width += width; dp = tp; } else width = 4; len--; sp++; if (ret >= 0) ret += width; } if (len > 0) break; } free(src); *dp = '\0'; *str = dst; if (wp != NULL) *wp = total_width; /* * If the string was truncated by the width limit but * would have fit into the size limit, the only sane way * to report the problem is using the return value, such * that the usual idiom "if (ret < 0 || ret >= sz) error" * works as expected. */ if (ret < (int)maxsz && !print) ret = -1; return ret; fail: if (wp != NULL) *wp = 0; if (ret == 0) { *str = src; return 0; } else { *str = NULL; return -1; } } int snmprintf(char *str, size_t sz, int *wp, const char *fmt, ...) { va_list ap; char *cp; int ret; va_start(ap, fmt); ret = vasnmprintf(&cp, sz, wp, fmt, ap); va_end(ap); if (cp != NULL) { (void)strlcpy(str, cp, sz); free(cp); } else *str = '\0'; return ret; } /* * To stay close to the standard interfaces, the following functions * return the number of non-NUL bytes written. */ int vfmprintf(FILE *stream, const char *fmt, va_list ap) { char *str; int ret; if ((ret = vasnmprintf(&str, INT_MAX, NULL, fmt, ap)) < 0) return -1; if (fputs(str, stream) == EOF) ret = -1; free(str); return ret; } int fmprintf(FILE *stream, const char *fmt, ...) { va_list ap; int ret; va_start(ap, fmt); ret = vfmprintf(stream, fmt, ap); va_end(ap); return ret; } int mprintf(const char *fmt, ...) { va_list ap; int ret; va_start(ap, fmt); ret = vfmprintf(stdout, fmt, ap); va_end(ap); return ret; } /* * Set up libc for multibyte output in the user's chosen locale. * * XXX: we are known to have problems with Turkish (i/I confusion) so we * deliberately fall back to the C locale for now. Longer term we should * always prefer to select C.[encoding] if possible, but there's no * standardisation in locales between systems, so we'll need to survey * what's out there first. */ void msetlocale(void) { const char *vars[] = { "LC_ALL", "LC_CTYPE", "LANG", NULL }; char *cp; int i; /* * We can't yet cope with dotless/dotted I in Turkish locales, * so fall back to the C locale for these. */ for (i = 0; vars[i] != NULL; i++) { if ((cp = getenv(vars[i])) == NULL) continue; if (strncasecmp(cp, "TR", 2) != 0) break; /* * If we're in a UTF-8 locale then prefer to use * the C.UTF-8 locale (or equivalent) if it exists. */ if ((strcasestr(cp, "UTF-8") != NULL || strcasestr(cp, "UTF8") != NULL) && (setlocale(LC_CTYPE, "C.UTF-8") != NULL || setlocale(LC_CTYPE, "POSIX.UTF-8") != NULL)) return; setlocale(LC_CTYPE, "C"); return; } /* We can handle this locale */ setlocale(LC_CTYPE, ""); } diff --git a/crypto/openssh/version.h b/crypto/openssh/version.h index d1586077efd6..5432964dc2d8 100644 --- a/crypto/openssh/version.h +++ b/crypto/openssh/version.h @@ -1,15 +1,15 @@ -/* $OpenBSD: version.h,v 1.78 2016/12/19 04:55:51 djm Exp $ */ +/* $OpenBSD: version.h,v 1.79 2017/03/20 01:18:59 djm Exp $ */ /* $FreeBSD$ */ -#define SSH_VERSION "OpenSSH_7.4" +#define SSH_VERSION "OpenSSH_7.5" #define SSH_PORTABLE "p1" #define SSH_RELEASE SSH_VERSION SSH_PORTABLE -#define SSH_VERSION_FREEBSD "FreeBSD-20170305" +#define SSH_VERSION_FREEBSD "FreeBSD-20170804" #ifdef WITH_OPENSSL #define OPENSSL_VERSION SSLeay_version(SSLEAY_VERSION) #else #define OPENSSL_VERSION "without OpenSSL" #endif